]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/contrib/ipfilter/netinet/ip_nat6.c
MFC 57785538c6e0d7e8ca0f161ab95bae10fd304047 and
[FreeBSD/FreeBSD.git] / sys / contrib / ipfilter / netinet / ip_nat6.c
1 /*
2  * Copyright (C) 2012 by Darren Reed.
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  */
6 #if defined(KERNEL) || defined(_KERNEL)
7 # undef KERNEL
8 # undef _KERNEL
9 # define        KERNEL  1
10 # define        _KERNEL 1
11 #endif
12 #include <sys/errno.h>
13 #include <sys/types.h>
14 #include <sys/param.h>
15 #include <sys/time.h>
16 #include <sys/file.h>
17 #if defined(_KERNEL) && defined(__NetBSD_Version__) && \
18     (__NetBSD_Version__ >= 399002000)
19 # include <sys/kauth.h>
20 #endif
21 #if !defined(_KERNEL)
22 # include <stdio.h>
23 # include <string.h>
24 # include <stdlib.h>
25 # define _KERNEL
26 # ifdef ipf_nat6__OpenBSD__
27 struct file;
28 # endif
29 # include <sys/uio.h>
30 # undef _KERNEL
31 #endif
32 #if defined(_KERNEL) && defined(__FreeBSD__)
33 # include <sys/filio.h>
34 # include <sys/fcntl.h>
35 #else
36 # include <sys/ioctl.h>
37 #endif
38 # include <sys/fcntl.h>
39 # include <sys/protosw.h>
40 #include <sys/socket.h>
41 #if defined(_KERNEL)
42 # include <sys/systm.h>
43 # if !defined(__SVR4)
44 #  include <sys/mbuf.h>
45 # endif
46 #endif
47 #if defined(__SVR4)
48 # include <sys/filio.h>
49 # include <sys/byteorder.h>
50 # ifdef _KERNEL
51 #  include <sys/dditypes.h>
52 # endif
53 # include <sys/stream.h>
54 # include <sys/kmem.h>
55 #endif
56 #if defined(__FreeBSD__)
57 # include <sys/queue.h>
58 #endif
59 #include <net/if.h>
60 #if defined(__FreeBSD__)
61 # include <net/if_var.h>
62 #endif
63 #ifdef sun
64 # include <net/af.h>
65 #endif
66 #include <net/route.h>
67 #include <netinet/in.h>
68 #include <netinet/in_systm.h>
69 #include <netinet/ip.h>
70
71 #ifdef RFC1825
72 # include <vpn/md5.h>
73 # include <vpn/ipsec.h>
74 extern struct ifnet vpnif;
75 #endif
76
77 # include <netinet/ip_var.h>
78 #include <netinet/tcp.h>
79 #include <netinet/udp.h>
80 #include <netinet/ip_icmp.h>
81 #include "netinet/ip_compat.h"
82 #include <netinet/tcpip.h>
83 #include "netinet/ip_fil.h"
84 #include "netinet/ip_nat.h"
85 #include "netinet/ip_frag.h"
86 #include "netinet/ip_state.h"
87 #include "netinet/ip_proxy.h"
88 #include "netinet/ip_lookup.h"
89 #include "netinet/ip_dstlist.h"
90 #include "netinet/ip_sync.h"
91 #if defined(__FreeBSD__)
92 # include <sys/malloc.h>
93 #endif
94 #ifdef HAS_SYS_MD5_H
95 # include <sys/md5.h>
96 #else
97 # include "md5.h"
98 #endif
99 /* END OF INCLUDES */
100
101 #undef  SOCKADDR_IN
102 #define SOCKADDR_IN     struct sockaddr_in
103
104 #if !defined(lint)
105 static const char rcsid[] = "@(#)$Id: ip_nat6.c,v 1.22.2.20 2012/07/22 08:04:23 darren_r Exp $";
106 #endif
107
108 #ifdef USE_INET6
109 static struct hostmap *ipf_nat6_hostmap(ipf_nat_softc_t *, ipnat_t *,
110                                              i6addr_t *, i6addr_t *,
111                                              i6addr_t *, u_32_t);
112 static int ipf_nat6_match(fr_info_t *, ipnat_t *);
113 static void ipf_nat6_tabmove(ipf_nat_softc_t *, nat_t *);
114 static int ipf_nat6_decap(fr_info_t *, nat_t *);
115 static int ipf_nat6_nextaddr(fr_info_t *, nat_addr_t *, i6addr_t *,
116                                   i6addr_t *);
117 static int ipf_nat6_icmpquerytype(int);
118 static int ipf_nat6_out(fr_info_t *, nat_t *, int, u_32_t);
119 static int ipf_nat6_in(fr_info_t *, nat_t *, int, u_32_t);
120 static int ipf_nat6_builddivertmp(ipf_nat_softc_t *, ipnat_t *);
121 static int ipf_nat6_nextaddrinit(ipf_main_softc_t *, char *,
122                                       nat_addr_t *, int, void *);
123 static int ipf_nat6_insert(ipf_main_softc_t *, ipf_nat_softc_t *,
124                                 nat_t *);
125
126
127 #define NINCLSIDE6(y,x) ATOMIC_INCL(softn->ipf_nat_stats.ns_side6[y].x)
128 #define NBUMPSIDE(y,x)  softn->ipf_nat_stats.ns_side[y].x++
129 #define NBUMPSIDE6(y,x) softn->ipf_nat_stats.ns_side6[y].x++
130 #define NBUMPSIDE6D(y,x) \
131                         do { \
132                                 softn->ipf_nat_stats.ns_side6[y].x++; \
133                                 DT(x); \
134                         } while (0)
135 #define NBUMPSIDE6DX(y,x,z) \
136                         do { \
137                                 softn->ipf_nat_stats.ns_side6[y].x++; \
138                                 DT(z); \
139                         } while (0)
140
141
142 /* ------------------------------------------------------------------------ */
143 /* Function:    ipf_nat6_ruleaddrinit                                       */
144 /* Returns:     int   - 0 == success, else failure                          */
145 /* Parameters:  in(I) - NAT rule that requires address fields to be init'd  */
146 /*                                                                          */
147 /* For each of the source/destination address fields in a NAT rule, call    */
148 /* ipf_nat6_nextaddrinit() to prepare the structure for active duty.  Other */
149 /* IPv6 specific actions can also be taken care of here.                    */
150 /* ------------------------------------------------------------------------ */
151 int
152 ipf_nat6_ruleaddrinit(softc, softn, n)
153         ipf_main_softc_t *softc;
154         ipf_nat_softc_t *softn;
155         ipnat_t *n;
156 {
157         int idx, error;
158
159         if (n->in_redir == NAT_BIMAP) {
160                 n->in_ndstip6 = n->in_osrcip6;
161                 n->in_ndstmsk6 = n->in_osrcmsk6;
162                 n->in_odstip6 = n->in_nsrcip6;
163                 n->in_odstmsk6 = n->in_nsrcmsk6;
164
165         }
166
167         if (n->in_redir & NAT_REDIRECT)
168                 idx = 1;
169         else
170                 idx = 0;
171         /*
172          * Initialise all of the address fields.
173          */
174         error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_osrc, 1,
175                                       n->in_ifps[idx]);
176         if (error != 0)
177                 return error;
178
179         error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_odst, 1,
180                                       n->in_ifps[idx]);
181         if (error != 0)
182                 return error;
183
184         error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1,
185                                       n->in_ifps[idx]);
186         if (error != 0)
187                 return error;
188
189         error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_ndst, 1,
190                                       n->in_ifps[idx]);
191         if (error != 0)
192                 return error;
193
194         if (n->in_redir & NAT_DIVERTUDP)
195                 ipf_nat6_builddivertmp(softn, n);
196         return 0;
197 }
198
199
200 /* ------------------------------------------------------------------------ */
201 /* Function:    ipf_nat6_addrdr                                             */
202 /* Returns:     Nil                                                         */
203 /* Parameters:  n(I) - pointer to NAT rule to add                           */
204 /*                                                                          */
205 /* Adds a redirect rule to the hash table of redirect rules and the list of */
206 /* loaded NAT rules.  Updates the bitmask indicating which netmasks are in  */
207 /* use by redirect rules.                                                   */
208 /* ------------------------------------------------------------------------ */
209 void
210 ipf_nat6_addrdr(softn, n)
211         ipf_nat_softc_t *softn;
212         ipnat_t *n;
213 {
214         i6addr_t *mask;
215         ipnat_t **np;
216         i6addr_t j;
217         u_int hv;
218         int k;
219
220         if ((n->in_redir & NAT_BIMAP) == NAT_BIMAP) {
221                 k = count6bits(n->in_nsrcmsk6.i6);
222                 mask = &n->in_nsrcmsk6;
223                 IP6_AND(&n->in_odstip6, &n->in_odstmsk6, &j);
224                 hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_rdrrules_sz);
225
226         } else if (n->in_odstatype == FRI_NORMAL) {
227                 k = count6bits(n->in_odstmsk6.i6);
228                 mask = &n->in_odstmsk6;
229                 IP6_AND(&n->in_odstip6, &n->in_odstmsk6, &j);
230                 hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_rdrrules_sz);
231         } else {
232                 k = 0;
233                 hv = 0;
234                 mask = NULL;
235         }
236         ipf_inet6_mask_add(k, mask, &softn->ipf_nat6_rdr_mask);
237
238         np = softn->ipf_nat_rdr_rules + hv;
239         while (*np != NULL)
240                 np = &(*np)->in_rnext;
241         n->in_rnext = NULL;
242         n->in_prnext = np;
243         n->in_hv[0] = hv;
244         n->in_use++;
245         *np = n;
246 }
247
248
249 /* ------------------------------------------------------------------------ */
250 /* Function:    ipf_nat6_addmap                                             */
251 /* Returns:     Nil                                                         */
252 /* Parameters:  n(I) - pointer to NAT rule to add                           */
253 /*                                                                          */
254 /* Adds a NAT map rule to the hash table of rules and the list of  loaded   */
255 /* NAT rules.  Updates the bitmask indicating which netmasks are in use by  */
256 /* redirect rules.                                                          */
257 /* ------------------------------------------------------------------------ */
258 void
259 ipf_nat6_addmap(softn, n)
260         ipf_nat_softc_t *softn;
261         ipnat_t *n;
262 {
263         i6addr_t *mask;
264         ipnat_t **np;
265         i6addr_t j;
266         u_int hv;
267         int k;
268
269         if (n->in_osrcatype == FRI_NORMAL) {
270                 k = count6bits(n->in_osrcmsk6.i6);
271                 mask = &n->in_osrcmsk6;
272                 IP6_AND(&n->in_osrcip6, &n->in_osrcmsk6, &j);
273                 hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_maprules_sz);
274         } else {
275                 k = 0;
276                 hv = 0;
277                 mask = NULL;
278         }
279         ipf_inet6_mask_add(k, mask, &softn->ipf_nat6_map_mask);
280
281         np = softn->ipf_nat_map_rules + hv;
282         while (*np != NULL)
283                 np = &(*np)->in_mnext;
284         n->in_mnext = NULL;
285         n->in_pmnext = np;
286         n->in_hv[1] = hv;
287         n->in_use++;
288         *np = n;
289 }
290
291
292 /* ------------------------------------------------------------------------ */
293 /* Function:    ipf_nat6_del_rdr                                             */
294 /* Returns:     Nil                                                         */
295 /* Parameters:  n(I) - pointer to NAT rule to delete                        */
296 /*                                                                          */
297 /* Removes a NAT rdr rule from the hash table of NAT rdr rules.             */
298 /* ------------------------------------------------------------------------ */
299 void
300 ipf_nat6_delrdr(softn, n)
301         ipf_nat_softc_t *softn;
302         ipnat_t *n;
303 {
304         i6addr_t *mask;
305         int k;
306
307         if ((n->in_redir & NAT_BIMAP) == NAT_BIMAP) {
308                 k = count6bits(n->in_nsrcmsk6.i6);
309                 mask = &n->in_nsrcmsk6;
310         } else if (n->in_odstatype == FRI_NORMAL) {
311                 k = count6bits(n->in_odstmsk6.i6);
312                 mask = &n->in_odstmsk6;
313         } else {
314                 k = 0;
315                 mask = NULL;
316         }
317         ipf_inet6_mask_del(k, mask, &softn->ipf_nat6_rdr_mask);
318
319         if (n->in_rnext != NULL)
320                 n->in_rnext->in_prnext = n->in_prnext;
321         *n->in_prnext = n->in_rnext;
322         n->in_use--;
323 }
324                                         
325                        
326 /* ------------------------------------------------------------------------ */
327 /* Function:    ipf_nat6_delmap                                             */
328 /* Returns:     Nil                                                         */
329 /* Parameters:  n(I) - pointer to NAT rule to delete                        */
330 /*                                                                          */
331 /* Removes a NAT map rule from the hash table of NAT map rules.             */
332 /* ------------------------------------------------------------------------ */
333 void
334 ipf_nat6_delmap(softn, n)
335         ipf_nat_softc_t *softn;
336         ipnat_t *n;
337 {
338         i6addr_t *mask;
339         int k;
340
341         if (n->in_osrcatype == FRI_NORMAL) {
342                 k = count6bits(n->in_osrcmsk6.i6);
343                 mask = &n->in_osrcmsk6;
344         } else {
345                 k = 0;
346                 mask = NULL;
347         }
348         ipf_inet6_mask_del(k, mask, &softn->ipf_nat6_map_mask);
349
350         if (n->in_mnext != NULL)
351                 n->in_mnext->in_pmnext = n->in_pmnext;
352         *n->in_pmnext = n->in_mnext;
353         n->in_use--;
354 }
355
356
357 /* ------------------------------------------------------------------------ */
358 /* Function:    ipf_nat6_hostmap                                            */
359 /* Returns:     struct hostmap* - NULL if no hostmap could be created,      */
360 /*                                else a pointer to the hostmapping to use  */
361 /* Parameters:  np(I)   - pointer to NAT rule                               */
362 /*              real(I) - real IP address                                   */
363 /*              map(I)  - mapped IP address                                 */
364 /*              port(I) - destination port number                           */
365 /* Write Locks: ipf_nat                                                     */
366 /*                                                                          */
367 /* Check if an ip address has already been allocated for a given mapping    */
368 /* that is not doing port based translation.  If is not yet allocated, then */
369 /* create a new entry if a non-NULL NAT rule pointer has been supplied.     */
370 /* ------------------------------------------------------------------------ */
371 static struct hostmap *
372 ipf_nat6_hostmap(softn, np, src, dst, map, port)
373         ipf_nat_softc_t *softn;
374         ipnat_t *np;
375         i6addr_t *src, *dst, *map;
376         u_32_t port;
377 {
378         hostmap_t *hm;
379         u_int hv;
380
381         hv = (src->i6[3] ^ dst->i6[3]);
382         hv += (src->i6[2] ^ dst->i6[2]);
383         hv += (src->i6[1] ^ dst->i6[1]);
384         hv += (src->i6[0] ^ dst->i6[0]);
385         hv += src->i6[3];
386         hv += src->i6[2];
387         hv += src->i6[1];
388         hv += src->i6[0];
389         hv += dst->i6[3];
390         hv += dst->i6[2];
391         hv += dst->i6[1];
392         hv += dst->i6[0];
393         hv %= softn->ipf_nat_hostmap_sz;
394         for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_next)
395                 if (IP6_EQ(&hm->hm_osrc6, src) &&
396                     IP6_EQ(&hm->hm_odst6, dst) &&
397                     ((np == NULL) || (np == hm->hm_ipnat)) &&
398                     ((port == 0) || (port == hm->hm_port))) {
399                         softn->ipf_nat_stats.ns_hm_addref++;
400                         hm->hm_ref++;
401                         return hm;
402                 }
403
404         if (np == NULL) {
405                 softn->ipf_nat_stats.ns_hm_nullnp++;
406                 return NULL;
407         }
408
409         KMALLOC(hm, hostmap_t *);
410         if (hm) {
411                 hm->hm_next = softn->ipf_hm_maplist;
412                 hm->hm_pnext = &softn->ipf_hm_maplist;
413                 if (softn->ipf_hm_maplist != NULL)
414                         softn->ipf_hm_maplist->hm_pnext = &hm->hm_next;
415                 softn->ipf_hm_maplist = hm;
416                 hm->hm_hnext = softn->ipf_hm_maptable[hv];
417                 hm->hm_phnext = softn->ipf_hm_maptable + hv;
418                 if (softn->ipf_hm_maptable[hv] != NULL)
419                         softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext;
420                 softn->ipf_hm_maptable[hv] = hm;
421                 hm->hm_ipnat = np;
422                 np->in_use++;
423                 hm->hm_osrcip6 = *src;
424                 hm->hm_odstip6 = *dst;
425                 hm->hm_nsrcip6 = *map;
426                 hm->hm_ndstip6.i6[0] = 0;
427                 hm->hm_ndstip6.i6[1] = 0;
428                 hm->hm_ndstip6.i6[2] = 0;
429                 hm->hm_ndstip6.i6[3] = 0;
430                 hm->hm_ref = 1;
431                 hm->hm_port = port;
432                 hm->hm_hv = hv;
433                 hm->hm_v = 6;
434                 softn->ipf_nat_stats.ns_hm_new++;
435         } else {
436                 softn->ipf_nat_stats.ns_hm_newfail++;
437         }
438         return hm;
439 }
440
441
442 /* ------------------------------------------------------------------------ */
443 /* Function:    ipf_nat6_newmap                                             */
444 /* Returns:     int - -1 == error, 0 == success                             */
445 /* Parameters:  fin(I) - pointer to packet information                      */
446 /*              nat(I) - pointer to NAT entry                               */
447 /*              ni(I)  - pointer to structure with misc. information needed */
448 /*                       to create new NAT entry.                           */
449 /*                                                                          */
450 /* Given an empty NAT structure, populate it with new information about a   */
451 /* new NAT session, as defined by the matching NAT rule.                    */
452 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
453 /* to the new IP address for the translation.                               */
454 /* ------------------------------------------------------------------------ */
455 int
456 ipf_nat6_newmap(fin, nat, ni)
457         fr_info_t *fin;
458         nat_t *nat;
459         natinfo_t *ni;
460 {
461         ipf_main_softc_t *softc = fin->fin_main_soft;
462         ipf_nat_softc_t *softn = softc->ipf_nat_soft;
463         u_short st_port, dport, sport, port, sp, dp;
464         i6addr_t in, st_ip;
465         hostmap_t *hm;
466         u_32_t flags;
467         ipnat_t *np;
468         nat_t *natl;
469         int l;
470
471         /*
472          * If it's an outbound packet which doesn't match any existing
473          * record, then create a new port
474          */
475         l = 0;
476         hm = NULL;
477         np = ni->nai_np;
478         st_ip = np->in_snip6;
479         st_port = np->in_spnext;
480         flags = nat->nat_flags;
481
482         if (flags & IPN_ICMPQUERY) {
483                 sport = fin->fin_data[1];
484                 dport = 0;
485         } else {
486                 sport = htons(fin->fin_data[0]);
487                 dport = htons(fin->fin_data[1]);
488         }
489
490         /*
491          * Do a loop until we either run out of entries to try or we find
492          * a NAT mapping that isn't currently being used.  This is done
493          * because the change to the source is not (usually) being fixed.
494          */
495         do {
496                 port = 0;
497                 in = np->in_nsrc.na_nextaddr;
498                 if (l == 0) {
499                         /*
500                          * Check to see if there is an existing NAT
501                          * setup for this IP address pair.
502                          */
503                         hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6,
504                                               &fin->fin_dst6, &in, 0);
505                         if (hm != NULL)
506                                 in = hm->hm_nsrcip6;
507                 } else if ((l == 1) && (hm != NULL)) {
508                         ipf_nat_hostmapdel(softc, &hm);
509                 }
510
511                 nat->nat_hm = hm;
512
513                 if (IP6_ISONES(&np->in_nsrcmsk6) && (np->in_spnext == 0)) {
514                         if (l > 0) {
515                                 NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_1);
516                                 return -1;
517                         }
518                 }
519
520                 if ((np->in_redir == NAT_BIMAP) &&
521                     IP6_EQ(&np->in_osrcmsk6, &np->in_nsrcmsk6)) {
522                         i6addr_t temp;
523                         /*
524                          * map the address block in a 1:1 fashion
525                          */
526                         temp.i6[0] = fin->fin_src6.i6[0] &
527                                      ~np->in_osrcmsk6.i6[0];
528                         temp.i6[1] = fin->fin_src6.i6[1] &
529                                      ~np->in_osrcmsk6.i6[1];
530                         temp.i6[2] = fin->fin_src6.i6[2] &
531                                      ~np->in_osrcmsk6.i6[0];
532                         temp.i6[3] = fin->fin_src6.i6[3] &
533                                      ~np->in_osrcmsk6.i6[3];
534                         in = np->in_nsrcip6;
535                         IP6_MERGE(&in, &temp, &np->in_osrc);
536
537 #ifdef NEED_128BIT_MATH
538                 } else if (np->in_redir & NAT_MAPBLK) {
539                         if ((l >= np->in_ppip) || ((l > 0) &&
540                              !(flags & IPN_TCPUDP))) {
541                                 NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_2);
542                                 return -1;
543                         }
544                         /*
545                          * map-block - Calculate destination address.
546                          */
547                         IP6_MASK(&in, &fin->fin_src6, &np->in_osrcmsk6);
548                         in = ntohl(in);
549                         inb = in;
550                         in.s_addr /= np->in_ippip;
551                         in.s_addr &= ntohl(~np->in_nsrcmsk6);
552                         in.s_addr += ntohl(np->in_nsrcaddr6);
553                         /*
554                          * Calculate destination port.
555                          */
556                         if ((flags & IPN_TCPUDP) &&
557                             (np->in_ppip != 0)) {
558                                 port = ntohs(sport) + l;
559                                 port %= np->in_ppip;
560                                 port += np->in_ppip *
561                                         (inb.s_addr % np->in_ippip);
562                                 port += MAPBLK_MINPORT;
563                                 port = htons(port);
564                         }
565 #endif
566
567                 } else if (IP6_ISZERO(&np->in_nsrcaddr) &&
568                            IP6_ISONES(&np->in_nsrcmsk)) {
569                         /*
570                          * 0/32 - use the interface's IP address.
571                          */
572                         if ((l > 0) ||
573                             ipf_ifpaddr(softc, 6, FRI_NORMAL, fin->fin_ifp,
574                                        &in, NULL) == -1) {
575                                 NBUMPSIDE6DX(1, ns_new_ifpaddr,
576                                              ns_new_ifpaddr_1);
577                                 return -1;
578                         }
579
580                 } else if (IP6_ISZERO(&np->in_nsrcip6) &&
581                            IP6_ISZERO(&np->in_nsrcmsk6)) {
582                         /*
583                          * 0/0 - use the original source address/port.
584                          */
585                         if (l > 0) {
586                                 NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_3);
587                                 return -1;
588                         }
589                         in = fin->fin_src6;
590
591                 } else if (!IP6_ISONES(&np->in_nsrcmsk6) &&
592                            (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) {
593                         IP6_INC(&np->in_snip6);
594                 }
595
596                 natl = NULL;
597
598                 if ((flags & IPN_TCPUDP) &&
599                     ((np->in_redir & NAT_MAPBLK) == 0) &&
600                     (np->in_flags & IPN_AUTOPORTMAP)) {
601 #ifdef NEED_128BIT_MATH
602                         /*
603                          * "ports auto" (without map-block)
604                          */
605                         if ((l > 0) && (l % np->in_ppip == 0)) {
606                                 if ((l > np->in_ppip) &&
607                                     !IP6_ISONES(&np->in_nsrcmsk)) {
608                                         IP6_INC(&np->in_snip6)
609                                 }
610                         }
611                         if (np->in_ppip != 0) {
612                                 port = ntohs(sport);
613                                 port += (l % np->in_ppip);
614                                 port %= np->in_ppip;
615                                 port += np->in_ppip *
616                                         (ntohl(fin->fin_src6) %
617                                          np->in_ippip);
618                                 port += MAPBLK_MINPORT;
619                                 port = htons(port);
620                         }
621 #endif
622
623                 } else if (((np->in_redir & NAT_MAPBLK) == 0) &&
624                            (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) {
625                         /*
626                          * Standard port translation.  Select next port.
627                          */
628                         if (np->in_flags & IPN_SEQUENTIAL) {
629                                 port = np->in_spnext;
630                         } else {
631                                 port = ipf_random() % (np->in_spmax -
632                                                        np->in_spmin + 1);
633                                 port += np->in_spmin;
634                         }
635                         port = htons(port);
636                         np->in_spnext++;
637
638                         if (np->in_spnext > np->in_spmax) {
639                                 np->in_spnext = np->in_spmin;
640                                 if (!IP6_ISONES(&np->in_nsrcmsk6)) {
641                                         IP6_INC(&np->in_snip6);
642                                 }
643                         }
644                 }
645
646                 if (np->in_flags & IPN_SIPRANGE) {
647                         if (IP6_GT(&np->in_snip, &np->in_nsrcmsk))
648                                 np->in_snip6 = np->in_nsrcip6;
649                 } else {
650                         i6addr_t a1, a2;
651
652                         a1 = np->in_snip6;
653                         IP6_INC(&a1);
654                         IP6_AND(&a1, &np->in_nsrcmsk6, &a2);
655
656                         if (!IP6_ISONES(&np->in_nsrcmsk6) &&
657                             IP6_GT(&a2, &np->in_nsrcip6)) {
658                                 IP6_ADD(&np->in_nsrcip6, 1, &np->in_snip6);
659                         }
660                 }
661
662                 if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY)))
663                         port = sport;
664
665                 /*
666                  * Here we do a lookup of the connection as seen from
667                  * the outside.  If an IP# pair already exists, try
668                  * again.  So if you have A->B becomes C->B, you can
669                  * also have D->E become C->E but not D->B causing
670                  * another C->B.  Also take protocol and ports into
671                  * account when determining whether a pre-existing
672                  * NAT setup will cause an external conflict where
673                  * this is appropriate.
674                  */
675                 sp = fin->fin_data[0];
676                 dp = fin->fin_data[1];
677                 fin->fin_data[0] = fin->fin_data[1];
678                 fin->fin_data[1] = ntohs(port);
679                 natl = ipf_nat6_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
680                                          (u_int)fin->fin_p, &fin->fin_dst6.in6,
681                                          &in.in6);
682                 fin->fin_data[0] = sp;
683                 fin->fin_data[1] = dp;
684
685                 /*
686                  * Has the search wrapped around and come back to the
687                  * start ?
688                  */
689                 if ((natl != NULL) &&
690                     (np->in_spnext != 0) && (st_port == np->in_spnext) &&
691                     (!IP6_ISZERO(&np->in_snip6) &&
692                      IP6_EQ(&st_ip, &np->in_snip6))) {
693                         NBUMPSIDE6D(1, ns_wrap);
694                         return -1;
695                 }
696                 l++;
697         } while (natl != NULL);
698
699         /* Setup the NAT table */
700         nat->nat_osrc6 = fin->fin_src6;
701         nat->nat_nsrc6 = in;
702         nat->nat_odst6 = fin->fin_dst6;
703         nat->nat_ndst6 = fin->fin_dst6;
704         if (nat->nat_hm == NULL)
705                 nat->nat_hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6,
706                                                &fin->fin_dst6,
707                                                &nat->nat_nsrc6, 0);
708
709         if (flags & IPN_TCPUDP) {
710                 nat->nat_osport = sport;
711                 nat->nat_nsport = port; /* sport */
712                 nat->nat_odport = dport;
713                 nat->nat_ndport = dport;
714                 ((tcphdr_t *)fin->fin_dp)->th_sport = port;
715         } else if (flags & IPN_ICMPQUERY) {
716                 nat->nat_oicmpid = fin->fin_data[1];
717                 ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = port;
718                 nat->nat_nicmpid = port;
719         }
720         return 0;
721 }
722
723
724 /* ------------------------------------------------------------------------ */
725 /* Function:    ipf_nat6_newrdr                                             */
726 /* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
727 /*                    allow rule to be moved if IPN_ROUNDR is set.          */
728 /* Parameters:  fin(I) - pointer to packet information                      */
729 /*              nat(I) - pointer to NAT entry                               */
730 /*              ni(I)  - pointer to structure with misc. information needed */
731 /*                       to create new NAT entry.                           */
732 /*                                                                          */
733 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
734 /* to the new IP address for the translation.                               */
735 /* ------------------------------------------------------------------------ */
736 int
737 ipf_nat6_newrdr(fin, nat, ni)
738         fr_info_t *fin;
739         nat_t *nat;
740         natinfo_t *ni;
741 {
742         ipf_main_softc_t *softc = fin->fin_main_soft;
743         ipf_nat_softc_t *softn = softc->ipf_nat_soft;
744         u_short nport, dport, sport;
745         u_short sp, dp;
746         hostmap_t *hm;
747         u_32_t flags;
748         i6addr_t in;
749         ipnat_t *np;
750         nat_t *natl;
751         int move;
752
753         move = 1;
754         hm = NULL;
755         in.i6[0] = 0;
756         in.i6[1] = 0;
757         in.i6[2] = 0;
758         in.i6[3] = 0;
759         np = ni->nai_np;
760         flags = nat->nat_flags;
761
762         if (flags & IPN_ICMPQUERY) {
763                 dport = fin->fin_data[1];
764                 sport = 0;
765         } else {
766                 sport = htons(fin->fin_data[0]);
767                 dport = htons(fin->fin_data[1]);
768         }
769
770         /* TRACE sport, dport */
771
772
773         /*
774          * If the matching rule has IPN_STICKY set, then we want to have the
775          * same rule kick in as before.  Why would this happen?  If you have
776          * a collection of rdr rules with "round-robin sticky", the current
777          * packet might match a different one to the previous connection but
778          * we want the same destination to be used.
779          */
780         if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) &&
781             ((np->in_flags & IPN_STICKY) != 0)) {
782                 hm = ipf_nat6_hostmap(softn, NULL, &fin->fin_src6,
783                                       &fin->fin_dst6, &in, (u_32_t)dport);
784                 if (hm != NULL) {
785                         in = hm->hm_ndstip6;
786                         np = hm->hm_ipnat;
787                         ni->nai_np = np;
788                         move = 0;
789                 }
790         }
791
792         /*
793          * Otherwise, it's an inbound packet. Most likely, we don't
794          * want to rewrite source ports and source addresses. Instead,
795          * we want to rewrite to a fixed internal address and fixed
796          * internal port.
797          */
798         if (np->in_flags & IPN_SPLIT) {
799                 in = np->in_dnip6;
800
801                 if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) {
802                         hm = ipf_nat6_hostmap(softn, NULL, &fin->fin_src6,
803                                               &fin->fin_dst6, &in,
804                                               (u_32_t)dport);
805                         if (hm != NULL) {
806                                 in = hm->hm_ndstip6;
807                                 move = 0;
808                         }
809                 }
810
811                 if (hm == NULL || hm->hm_ref == 1) {
812                         if (IP6_EQ(&np->in_ndstip6, &in)) {
813                                 np->in_dnip6 = np->in_ndstmsk6;
814                                 move = 0;
815                         } else {
816                                 np->in_dnip6 = np->in_ndstip6;
817                         }
818                 }
819
820         } else if (IP6_ISZERO(&np->in_ndstaddr) &&
821                    IP6_ISONES(&np->in_ndstmsk)) {
822                 /*
823                  * 0/32 - use the interface's IP address.
824                  */
825                 if (ipf_ifpaddr(softc, 6, FRI_NORMAL, fin->fin_ifp,
826                                &in, NULL) == -1) {
827                         NBUMPSIDE6DX(0, ns_new_ifpaddr, ns_new_ifpaddr_2);
828                         return -1;
829                 }
830
831         } else if (IP6_ISZERO(&np->in_ndstip6) &&
832                    IP6_ISZERO(&np->in_ndstmsk6)) {
833                 /*
834                  * 0/0 - use the original destination address/port.
835                  */
836                 in = fin->fin_dst6;
837
838         } else if (np->in_redir == NAT_BIMAP &&
839                    IP6_EQ(&np->in_ndstmsk6, &np->in_odstmsk6)) {
840                 i6addr_t temp;
841                 /*
842                  * map the address block in a 1:1 fashion
843                  */
844                 temp.i6[0] = fin->fin_dst6.i6[0] & ~np->in_osrcmsk6.i6[0];
845                 temp.i6[1] = fin->fin_dst6.i6[1] & ~np->in_osrcmsk6.i6[1];
846                 temp.i6[2] = fin->fin_dst6.i6[2] & ~np->in_osrcmsk6.i6[0];
847                 temp.i6[3] = fin->fin_dst6.i6[3] & ~np->in_osrcmsk6.i6[3];
848                 in = np->in_ndstip6;
849                 IP6_MERGE(&in, &temp, &np->in_ndstmsk6);
850         } else {
851                 in = np->in_ndstip6;
852         }
853
854         if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0))
855                 nport = dport;
856         else {
857                 /*
858                  * Whilst not optimized for the case where
859                  * pmin == pmax, the gain is not significant.
860                  */
861                 if (((np->in_flags & IPN_FIXEDDPORT) == 0) &&
862                     (np->in_odport != np->in_dtop)) {
863                         nport = ntohs(dport) - np->in_odport + np->in_dpmax;
864                         nport = htons(nport);
865                 } else {
866                         nport = htons(np->in_dpnext);
867                         np->in_dpnext++;
868                         if (np->in_dpnext > np->in_dpmax)
869                                 np->in_dpnext = np->in_dpmin;
870                 }
871         }
872
873         /*
874          * When the redirect-to address is set to 0.0.0.0, just
875          * assume a blank `forwarding' of the packet.  We don't
876          * setup any translation for this either.
877          */
878         if (IP6_ISZERO(&in)) {
879                 if (nport == dport) {
880                         NBUMPSIDE6D(0, ns_xlate_null);
881                         return -1;
882                 }
883                 in = fin->fin_dst6;
884         }
885
886         /*
887          * Check to see if this redirect mapping already exists and if
888          * it does, return "failure" (allowing it to be created will just
889          * cause one or both of these "connections" to stop working.)
890          */
891         sp = fin->fin_data[0];
892         dp = fin->fin_data[1];
893         fin->fin_data[1] = fin->fin_data[0];
894         fin->fin_data[0] = ntohs(nport);
895         natl = ipf_nat6_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
896                                   (u_int)fin->fin_p, &in.in6,
897                                   &fin->fin_src6.in6);
898         fin->fin_data[0] = sp;
899         fin->fin_data[1] = dp;
900         if (natl != NULL) {
901                 NBUMPSIDE6D(0, ns_xlate_exists);
902                 return -1;
903         }
904
905         nat->nat_ndst6 = in;
906         nat->nat_odst6 = fin->fin_dst6;
907         nat->nat_nsrc6 = fin->fin_src6;
908         nat->nat_osrc6 = fin->fin_src6;
909         if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0))
910                 nat->nat_hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6,
911                                                &fin->fin_dst6, &in,
912                                                (u_32_t)dport);
913
914         if (flags & IPN_TCPUDP) {
915                 nat->nat_odport = dport;
916                 nat->nat_ndport = nport;
917                 nat->nat_osport = sport;
918                 nat->nat_nsport = sport;
919                 ((tcphdr_t *)fin->fin_dp)->th_dport = nport;
920         } else if (flags & IPN_ICMPQUERY) {
921                 nat->nat_oicmpid = fin->fin_data[1];
922                 ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = nport;
923                 nat->nat_nicmpid = nport;
924         }
925
926         return move;
927 }
928
929 /* ------------------------------------------------------------------------ */
930 /* Function:    ipf_nat6_add                                                */
931 /* Returns:     nat6_t*      - NULL == failure to create new NAT structure, */
932 /*                             else pointer to new NAT structure            */
933 /* Parameters:  fin(I)       - pointer to packet information                */
934 /*              np(I)        - pointer to NAT rule                          */
935 /*              natsave(I)   - pointer to where to store NAT struct pointer */
936 /*              flags(I)     - flags describing the current packet          */
937 /*              direction(I) - direction of packet (in/out)                 */
938 /* Write Lock:  ipf_nat                                                     */
939 /*                                                                          */
940 /* Attempts to create a new NAT entry.  Does not actually change the packet */
941 /* in any way.                                                              */
942 /*                                                                          */
943 /* This fucntion is in three main parts: (1) deal with creating a new NAT   */
944 /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with     */
945 /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */
946 /* and (3) building that structure and putting it into the NAT table(s).    */
947 /*                                                                          */
948 /* NOTE: natsave should NOT be used top point back to an ipstate_t struct   */
949 /*       as it can result in memory being corrupted.                        */
950 /* ------------------------------------------------------------------------ */
951 nat_t *
952 ipf_nat6_add(fin, np, natsave, flags, direction)
953         fr_info_t *fin;
954         ipnat_t *np;
955         nat_t **natsave;
956         u_int flags;
957         int direction;
958 {
959         ipf_main_softc_t *softc = fin->fin_main_soft;
960         ipf_nat_softc_t *softn = softc->ipf_nat_soft;
961         hostmap_t *hm = NULL;
962         nat_t *nat, *natl;
963         natstat_t *nsp;
964         u_int nflags;
965         natinfo_t ni;
966         int move;
967 #if SOLARIS && defined(_KERNEL) && defined(ICK_M_CTL_MAGIC)
968         qpktinfo_t *qpi = fin->fin_qpi;
969 #endif
970
971         nsp = &softn->ipf_nat_stats;
972
973         if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) >
974             softn->ipf_nat_table_wm_high) {
975                 softn->ipf_nat_doflush = 1;
976         }
977
978         if (nsp->ns_active >= softn->ipf_nat_table_max) {
979                 NBUMPSIDE6(fin->fin_out, ns_table_max);
980                 return NULL;
981         }
982
983         move = 1;
984         nflags = np->in_flags & flags;
985         nflags &= NAT_FROMRULE;
986
987         ni.nai_np = np;
988         ni.nai_dport = 0;
989         ni.nai_sport = 0;
990
991         /* Give me a new nat */
992         KMALLOC(nat, nat_t *);
993         if (nat == NULL) {
994                 NBUMPSIDE6(fin->fin_out, ns_memfail);
995                 /*
996                  * Try to automatically tune the max # of entries in the
997                  * table allowed to be less than what will cause kmem_alloc()
998                  * to fail and try to eliminate panics due to out of memory
999                  * conditions arising.
1000                  */
1001                 if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) &&
1002                     (nsp->ns_active > 100)) {
1003                         softn->ipf_nat_table_max = nsp->ns_active - 100;
1004                         printf("table_max reduced to %d\n",
1005                                 softn->ipf_nat_table_max);
1006                 }
1007                 return NULL;
1008         }
1009
1010         if (flags & IPN_ICMPQUERY) {
1011                 /*
1012                  * In the ICMP query NAT code, we translate the ICMP id fields
1013                  * to make them unique. This is indepedent of the ICMP type
1014                  * (e.g. in the unlikely event that a host sends an echo and
1015                  * an tstamp request with the same id, both packets will have
1016                  * their ip address/id field changed in the same way).
1017                  */
1018                 /* The icmp6_id field is used by the sender to identify the
1019                  * process making the icmp request. (the receiver justs
1020                  * copies it back in its response). So, it closely matches
1021                  * the concept of source port. We overlay sport, so we can
1022                  * maximally reuse the existing code.
1023                  */
1024                 ni.nai_sport = fin->fin_data[1];
1025                 ni.nai_dport = 0;
1026         }
1027
1028         bzero((char *)nat, sizeof(*nat));
1029         nat->nat_flags = flags;
1030         nat->nat_redir = np->in_redir;
1031         nat->nat_dir = direction;
1032         nat->nat_pr[0] = fin->fin_p;
1033         nat->nat_pr[1] = fin->fin_p;
1034
1035         /*
1036          * Search the current table for a match and create a new mapping
1037          * if there is none found.
1038          */
1039         if (np->in_redir & NAT_DIVERTUDP) {
1040                 move = ipf_nat6_newdivert(fin, nat, &ni);
1041
1042         } else if (np->in_redir & NAT_REWRITE) {
1043                 move = ipf_nat6_newrewrite(fin, nat, &ni);
1044
1045         } else if (direction == NAT_OUTBOUND) {
1046                 /*
1047                  * We can now arrange to call this for the same connection
1048                  * because ipf_nat6_new doesn't protect the code path into
1049                  * this function.
1050                  */
1051                 natl = ipf_nat6_outlookup(fin, nflags, (u_int)fin->fin_p,
1052                                           &fin->fin_src6.in6,
1053                                           &fin->fin_dst6.in6);
1054                 if (natl != NULL) {
1055                         KFREE(nat);
1056                         nat = natl;
1057                         goto done;
1058                 }
1059
1060                 move = ipf_nat6_newmap(fin, nat, &ni);
1061         } else {
1062                 /*
1063                  * NAT_INBOUND is used for redirects rules
1064                  */
1065                 natl = ipf_nat6_inlookup(fin, nflags, (u_int)fin->fin_p,
1066                                          &fin->fin_src6.in6,
1067                                          &fin->fin_dst6.in6);
1068                 if (natl != NULL) {
1069                         KFREE(nat);
1070                         nat = natl;
1071                         goto done;
1072                 }
1073
1074                 move = ipf_nat6_newrdr(fin, nat, &ni);
1075         }
1076         if (move == -1)
1077                 goto badnat;
1078
1079         np = ni.nai_np;
1080
1081         nat->nat_mssclamp = np->in_mssclamp;
1082         nat->nat_me = natsave;
1083         nat->nat_fr = fin->fin_fr;
1084         nat->nat_rev = fin->fin_rev;
1085         nat->nat_ptr = np;
1086         nat->nat_dlocal = np->in_dlocal;
1087
1088         if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) {
1089                 if (ipf_proxy_new(fin, nat) == -1) {
1090                         NBUMPSIDE6D(fin->fin_out, ns_appr_fail);
1091                         goto badnat;
1092                 }
1093         }
1094
1095         nat->nat_ifps[0] = np->in_ifps[0];
1096         if (np->in_ifps[0] != NULL) {
1097                 COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]);
1098         }
1099
1100         nat->nat_ifps[1] = np->in_ifps[1];
1101         if (np->in_ifps[1] != NULL) {
1102                 COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]);
1103         }
1104
1105         if (ipf_nat6_finalise(fin, nat) == -1) {
1106                 goto badnat;
1107         }
1108
1109         np->in_use++;
1110
1111         if ((move == 1) && (np->in_flags & IPN_ROUNDR)) {
1112                 if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) {
1113                         ipf_nat6_delrdr(softn, np);
1114                         ipf_nat6_addrdr(softn, np);
1115                 } else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) {
1116                         ipf_nat6_delmap(softn, np);
1117                         ipf_nat6_addmap(softn, np);
1118                 }
1119         }
1120
1121         if (flags & SI_WILDP)
1122                 nsp->ns_wilds++;
1123         softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]++;
1124
1125         goto done;
1126 badnat:
1127         NBUMPSIDE6(fin->fin_out, ns_badnatnew);
1128         if ((hm = nat->nat_hm) != NULL)
1129                 ipf_nat_hostmapdel(softc, &hm);
1130         KFREE(nat);
1131         nat = NULL;
1132 done:
1133         if (nat != NULL && np != NULL)
1134                 np->in_hits++;
1135         if (natsave != NULL)
1136                 *natsave = nat;
1137         return nat;
1138 }
1139
1140
1141 /* ------------------------------------------------------------------------ */
1142 /* Function:    ipf_nat6_finalise                                           */
1143 /* Returns:     int - 0 == sucess, -1 == failure                            */
1144 /* Parameters:  fin(I) - pointer to packet information                      */
1145 /*              nat(I) - pointer to NAT entry                               */
1146 /* Write Lock:  ipf_nat                                                     */
1147 /*                                                                          */
1148 /* This is the tail end of constructing a new NAT entry and is the same     */
1149 /* for both IPv4 and IPv6.                                                  */
1150 /* ------------------------------------------------------------------------ */
1151 /*ARGSUSED*/
1152 int
1153 ipf_nat6_finalise(fin, nat)
1154         fr_info_t *fin;
1155         nat_t *nat;
1156 {
1157         ipf_main_softc_t *softc = fin->fin_main_soft;
1158         ipf_nat_softc_t *softn = softc->ipf_nat_soft;
1159         u_32_t sum1, sum2, sumd;
1160         frentry_t *fr;
1161         u_32_t flags;
1162
1163         flags = nat->nat_flags;
1164
1165         switch (fin->fin_p)
1166         {
1167         case IPPROTO_ICMPV6 :
1168                 sum1 = LONG_SUM6(&nat->nat_osrc6);
1169                 sum1 += ntohs(nat->nat_oicmpid);
1170                 sum2 = LONG_SUM6(&nat->nat_nsrc6);
1171                 sum2 += ntohs(nat->nat_nicmpid);
1172                 CALC_SUMD(sum1, sum2, sumd);
1173                 nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
1174
1175                 sum1 = LONG_SUM6(&nat->nat_odst6);
1176                 sum2 = LONG_SUM6(&nat->nat_ndst6);
1177                 CALC_SUMD(sum1, sum2, sumd);
1178                 nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16);
1179                 break;
1180
1181         case IPPROTO_TCP :
1182         case IPPROTO_UDP :
1183                 sum1 = LONG_SUM6(&nat->nat_osrc6);
1184                 sum1 += ntohs(nat->nat_osport);
1185                 sum2 = LONG_SUM6(&nat->nat_nsrc6);
1186                 sum2 += ntohs(nat->nat_nsport);
1187                 CALC_SUMD(sum1, sum2, sumd);
1188                 nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
1189
1190                 sum1 = LONG_SUM6(&nat->nat_odst6);
1191                 sum1 += ntohs(nat->nat_odport);
1192                 sum2 = LONG_SUM6(&nat->nat_ndst6);
1193                 sum2 += ntohs(nat->nat_ndport);
1194                 CALC_SUMD(sum1, sum2, sumd);
1195                 nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16);
1196                 break;
1197
1198         default :
1199                 sum1 = LONG_SUM6(&nat->nat_osrc6);
1200                 sum2 = LONG_SUM6(&nat->nat_nsrc6);
1201                 CALC_SUMD(sum1, sum2, sumd);
1202                 nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
1203
1204                 sum1 = LONG_SUM6(&nat->nat_odst6);
1205                 sum2 = LONG_SUM6(&nat->nat_ndst6);
1206                 CALC_SUMD(sum1, sum2, sumd);
1207                 nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16);
1208                 break;
1209         }
1210
1211         /*
1212          * Compute the partial checksum, just in case.
1213          * This is only ever placed into outbound packets so care needs
1214          * to be taken over which pair of addresses are used.
1215          */
1216         if (nat->nat_dir == NAT_OUTBOUND) {
1217                 sum1 = LONG_SUM6(&nat->nat_nsrc6);
1218                 sum1 += LONG_SUM6(&nat->nat_ndst6);
1219         } else {
1220                 sum1 = LONG_SUM6(&nat->nat_osrc6);
1221                 sum1 += LONG_SUM6(&nat->nat_odst6);
1222         }
1223         sum1 += nat->nat_pr[1];
1224         nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16);
1225
1226         if ((nat->nat_flags & SI_CLONE) == 0)
1227                 nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat);
1228
1229         if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) {
1230                 nat->nat_mtu[0] = GETIFMTU_6(nat->nat_ifps[0]);
1231         }
1232
1233         if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) {
1234                 nat->nat_mtu[1] = GETIFMTU_6(nat->nat_ifps[1]);
1235         }
1236
1237         nat->nat_v[0] = 6;
1238         nat->nat_v[1] = 6;
1239
1240         if (ipf_nat6_insert(softc, softn, nat) == 0) {
1241                 if (softn->ipf_nat_logging)
1242                         ipf_nat_log(softc, softn, nat, NL_NEW);
1243                 fr = nat->nat_fr;
1244                 if (fr != NULL) {
1245                         MUTEX_ENTER(&fr->fr_lock);
1246                         fr->fr_ref++;
1247                         MUTEX_EXIT(&fr->fr_lock);
1248                 }
1249                 return 0;
1250         }
1251
1252         NBUMPSIDE6D(fin->fin_out, ns_unfinalised);
1253         /*
1254          * nat6_insert failed, so cleanup time...
1255          */
1256         if (nat->nat_sync != NULL)
1257                 ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync);
1258         return -1;
1259 }
1260
1261
1262 /* ------------------------------------------------------------------------ */
1263 /* Function:    ipf_nat6_insert                                             */
1264 /* Returns:     int - 0 == sucess, -1 == failure                            */
1265 /* Parameters:  softc(I) - pointer to soft context main structure           */
1266 /*              softn(I) - pointer to NAT context structure                 */
1267 /*              nat(I) - pointer to NAT structure                           */
1268 /* Write Lock:  ipf_nat                                                     */
1269 /*                                                                          */
1270 /* Insert a NAT entry into the hash tables for searching and add it to the  */
1271 /* list of active NAT entries.  Adjust global counters when complete.       */
1272 /* ------------------------------------------------------------------------ */
1273 static int
1274 ipf_nat6_insert(softc, softn, nat)
1275         ipf_main_softc_t *softc;
1276         ipf_nat_softc_t *softn;
1277         nat_t *nat;
1278 {
1279         u_int hv1, hv2;
1280         u_32_t sp, dp;
1281         ipnat_t *in;
1282
1283         /*
1284          * Try and return an error as early as possible, so calculate the hash
1285          * entry numbers first and then proceed.
1286          */
1287         if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) {
1288                 if ((nat->nat_flags & IPN_TCPUDP) != 0) {
1289                         sp = nat->nat_osport;
1290                         dp = nat->nat_odport;
1291                 } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
1292                         sp = 0;
1293                         dp = nat->nat_oicmpid;
1294                 } else {
1295                         sp = 0;
1296                         dp = 0;
1297                 }
1298                 hv1 = NAT_HASH_FN6(&nat->nat_osrc6, sp, 0xffffffff);
1299                 hv1 = NAT_HASH_FN6(&nat->nat_odst6, hv1 + dp,
1300                                    softn->ipf_nat_table_sz);
1301
1302                 /*
1303                  * TRACE nat6_osrc6, nat6_osport, nat6_odst6,
1304                  * nat6_odport, hv1
1305                  */
1306
1307                 if ((nat->nat_flags & IPN_TCPUDP) != 0) {
1308                         sp = nat->nat_nsport;
1309                         dp = nat->nat_ndport;
1310                 } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
1311                         sp = 0;
1312                         dp = nat->nat_nicmpid;
1313                 } else {
1314                         sp = 0;
1315                         dp = 0;
1316                 }
1317                 hv2 = NAT_HASH_FN6(&nat->nat_nsrc6, sp, 0xffffffff);
1318                 hv2 = NAT_HASH_FN6(&nat->nat_ndst6, hv2 + dp,
1319                                    softn->ipf_nat_table_sz);
1320                 /*
1321                  * TRACE nat6_nsrcaddr, nat6_nsport, nat6_ndstaddr,
1322                  * nat6_ndport, hv1
1323                  */
1324         } else {
1325                 hv1 = NAT_HASH_FN6(&nat->nat_osrc6, 0, 0xffffffff);
1326                 hv1 = NAT_HASH_FN6(&nat->nat_odst6, hv1,
1327                                    softn->ipf_nat_table_sz);
1328                 /* TRACE nat6_osrcip6, nat6_odstip6, hv1 */
1329
1330                 hv2 = NAT_HASH_FN6(&nat->nat_nsrc6, 0, 0xffffffff);
1331                 hv2 = NAT_HASH_FN6(&nat->nat_ndst6, hv2,
1332                                    softn->ipf_nat_table_sz);
1333                 /* TRACE nat6_nsrcip6, nat6_ndstip6, hv2 */
1334         }
1335
1336         nat->nat_hv[0] = hv1;
1337         nat->nat_hv[1] = hv2;
1338
1339         MUTEX_INIT(&nat->nat_lock, "nat entry lock");
1340
1341         in = nat->nat_ptr;
1342         nat->nat_ref = nat->nat_me ? 2 : 1;
1343
1344         nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0';
1345         nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0],
1346                                           nat->nat_v[0]);
1347
1348         if (nat->nat_ifnames[1][0] != '\0') {
1349                 nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
1350                 nat->nat_ifps[1] = ipf_resolvenic(softc, nat->nat_ifnames[1],
1351                                                   nat->nat_v[1]);
1352         } else if (in->in_ifnames[1] != -1) {
1353                 char *name;
1354
1355                 name = in->in_names + in->in_ifnames[1];
1356                 if (name[1] != '\0' && name[0] != '-' && name[0] != '*') {
1357                         (void) strncpy(nat->nat_ifnames[1],
1358                                        nat->nat_ifnames[0], LIFNAMSIZ);
1359                         nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
1360                         nat->nat_ifps[1] = nat->nat_ifps[0];
1361                 }
1362         }
1363         if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) {
1364                 nat->nat_mtu[0] = GETIFMTU_6(nat->nat_ifps[0]);
1365         }
1366         if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) {
1367                 nat->nat_mtu[1] = GETIFMTU_6(nat->nat_ifps[1]);
1368         }
1369
1370         return ipf_nat_hashtab_add(softc, softn, nat);
1371 }
1372
1373
1374 /* ------------------------------------------------------------------------ */
1375 /* Function:    ipf_nat6_icmperrorlookup                                    */
1376 /* Returns:     nat6_t* - point to matching NAT structure                    */
1377 /* Parameters:  fin(I) - pointer to packet information                      */
1378 /*              dir(I) - direction of packet (in/out)                       */
1379 /*                                                                          */
1380 /* Check if the ICMP error message is related to an existing TCP, UDP or    */
1381 /* ICMP query nat entry.  It is assumed that the packet is already of the   */
1382 /* the required length.                                                     */
1383 /* ------------------------------------------------------------------------ */
1384 nat_t *
1385 ipf_nat6_icmperrorlookup(fin, dir)
1386         fr_info_t *fin;
1387         int dir;
1388 {
1389         ipf_main_softc_t *softc = fin->fin_main_soft;
1390         ipf_nat_softc_t *softn = softc->ipf_nat_soft;
1391         struct icmp6_hdr *icmp6, *orgicmp;
1392         int flags = 0, type, minlen;
1393         nat_stat_side_t *nside;
1394         tcphdr_t *tcp = NULL;
1395         u_short data[2];
1396         ip6_t *oip6;
1397         nat_t *nat;
1398         u_int p;
1399
1400         minlen = 40;
1401         icmp6 = fin->fin_dp;
1402         type = icmp6->icmp6_type;
1403         nside = &softn->ipf_nat_stats.ns_side6[fin->fin_out];
1404         /*
1405          * Does it at least have the return (basic) IP header ?
1406          * Only a basic IP header (no options) should be with an ICMP error
1407          * header.  Also, if it's not an error type, then return.
1408          */
1409         if (!(fin->fin_flx & FI_ICMPERR)) {
1410                 ATOMIC_INCL(nside->ns_icmp_basic);
1411                 return NULL;
1412         }
1413
1414         /*
1415          * Check packet size
1416          */
1417         if (fin->fin_plen < ICMP6ERR_IPICMPHLEN) {
1418                 ATOMIC_INCL(nside->ns_icmp_size);
1419                 return NULL;
1420         }
1421         oip6 = (ip6_t *)((char *)fin->fin_dp + 8);
1422
1423         /*
1424          * Is the buffer big enough for all of it ?  It's the size of the IP
1425          * header claimed in the encapsulated part which is of concern.  It
1426          * may be too big to be in this buffer but not so big that it's
1427          * outside the ICMP packet, leading to TCP deref's causing problems.
1428          * This is possible because we don't know how big oip_hl is when we
1429          * do the pullup early in ipf_check() and thus can't gaurantee it is
1430          * all here now.
1431          */
1432 #ifdef  _KERNEL
1433         {
1434         mb_t *m;
1435
1436         m = fin->fin_m;
1437 # if SOLARIS
1438         if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN >
1439             (char *)m->b_wptr) {
1440                 ATOMIC_INCL(nside->ns_icmp_mbuf);
1441                 return NULL;
1442         }
1443 # else
1444         if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN >
1445             (char *)fin->fin_ip + M_LEN(m)) {
1446                 ATOMIC_INCL(nside->ns_icmp_mbuf);
1447                 return NULL;
1448         }
1449 # endif
1450         }
1451 #endif
1452
1453         if (IP6_NEQ(&fin->fin_dst6, &oip6->ip6_src)) {
1454                 ATOMIC_INCL(nside->ns_icmp_address);
1455                 return NULL;
1456         }
1457
1458         p = oip6->ip6_nxt;
1459         if (p == IPPROTO_TCP)
1460                 flags = IPN_TCP;
1461         else if (p == IPPROTO_UDP)
1462                 flags = IPN_UDP;
1463         else if (p == IPPROTO_ICMPV6) {
1464                 orgicmp = (struct icmp6_hdr *)(oip6 + 1);
1465
1466                 /* see if this is related to an ICMP query */
1467                 if (ipf_nat6_icmpquerytype(orgicmp->icmp6_type)) {
1468                         data[0] = fin->fin_data[0];
1469                         data[1] = fin->fin_data[1];
1470                         fin->fin_data[0] = 0;
1471                         fin->fin_data[1] = orgicmp->icmp6_id;
1472
1473                         flags = IPN_ICMPERR|IPN_ICMPQUERY;
1474                         /*
1475                          * NOTE : dir refers to the direction of the original
1476                          *        ip packet. By definition the icmp error
1477                          *        message flows in the opposite direction.
1478                          */
1479                         if (dir == NAT_INBOUND)
1480                                 nat = ipf_nat6_inlookup(fin, flags, p,
1481                                                         &oip6->ip6_dst,
1482                                                         &oip6->ip6_src);
1483                         else
1484                                 nat = ipf_nat6_outlookup(fin, flags, p,
1485                                                          &oip6->ip6_dst,
1486                                                          &oip6->ip6_src);
1487                         fin->fin_data[0] = data[0];
1488                         fin->fin_data[1] = data[1];
1489                         return nat;
1490                 }
1491         }
1492
1493         if (flags & IPN_TCPUDP) {
1494                 minlen += 8;            /* + 64bits of data to get ports */
1495                 /* TRACE (fin,minlen) */
1496                 if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) {
1497                         ATOMIC_INCL(nside->ns_icmp_short);
1498                         return NULL;
1499                 }
1500
1501                 data[0] = fin->fin_data[0];
1502                 data[1] = fin->fin_data[1];
1503                 tcp = (tcphdr_t *)(oip6 + 1);
1504                 fin->fin_data[0] = ntohs(tcp->th_dport);
1505                 fin->fin_data[1] = ntohs(tcp->th_sport);
1506
1507                 if (dir == NAT_INBOUND) {
1508                         nat = ipf_nat6_inlookup(fin, flags, p, &oip6->ip6_dst,
1509                                                 &oip6->ip6_src);
1510                 } else {
1511                         nat = ipf_nat6_outlookup(fin, flags, p, &oip6->ip6_dst,
1512                                                  &oip6->ip6_src);
1513                 }
1514                 fin->fin_data[0] = data[0];
1515                 fin->fin_data[1] = data[1];
1516                 return nat;
1517         }
1518         if (dir == NAT_INBOUND)
1519                 nat = ipf_nat6_inlookup(fin, 0, p, &oip6->ip6_dst,
1520                                         &oip6->ip6_src);
1521         else
1522                 nat = ipf_nat6_outlookup(fin, 0, p, &oip6->ip6_dst,
1523                                          &oip6->ip6_src);
1524
1525         return nat;
1526 }
1527
1528
1529 /* result = ip1 - ip2 */
1530 u_32_t
1531 ipf_nat6_ip6subtract(ip1, ip2)
1532         i6addr_t *ip1, *ip2;
1533 {
1534         i6addr_t l1, l2, d;
1535         u_short *s1, *s2, *ds;
1536         u_32_t r;
1537         int i, neg;
1538
1539         neg = 0;
1540         l1 = *ip1;
1541         l2 = *ip2;
1542         s1 = (u_short *)&l1;
1543         s2 = (u_short *)&l2;
1544         ds = (u_short *)&d;
1545
1546         for (i = 7; i > 0; i--) {
1547                 if (s1[i] > s2[i]) {
1548                         ds[i] = s2[i] + 0x10000 - s1[i];
1549                         s2[i - 1] += 0x10000;
1550                 } else {
1551                         ds[i] = s2[i] - s1[i];
1552                 }
1553         }
1554         if (s2[0] > s1[0]) {
1555                 ds[0] = s2[0] + 0x10000 - s1[0];
1556                 neg = 1;
1557         } else {
1558                 ds[0] = s2[0] - s1[0];
1559         }
1560
1561         for (i = 0, r = 0; i < 8; i++) {
1562                 r += ds[i];
1563         }
1564
1565         return r;
1566 }
1567
1568
1569 /* ------------------------------------------------------------------------ */
1570 /* Function:    ipf_nat6_icmperror                                          */
1571 /* Returns:     nat6_t* - point to matching NAT structure                    */
1572 /* Parameters:  fin(I)    - pointer to packet information                   */
1573 /*              nflags(I) - NAT flags for this packet                       */
1574 /*              dir(I)    - direction of packet (in/out)                    */
1575 /*                                                                          */
1576 /* Fix up an ICMP packet which is an error message for an existing NAT      */
1577 /* session.  This will correct both packet header data and checksums.       */
1578 /*                                                                          */
1579 /* This should *ONLY* be used for incoming ICMP error packets to make sure  */
1580 /* a NAT'd ICMP packet gets correctly recognised.                           */
1581 /* ------------------------------------------------------------------------ */
1582 nat_t *
1583 ipf_nat6_icmperror(fin, nflags, dir)
1584         fr_info_t *fin;
1585         u_int *nflags;
1586         int dir;
1587 {
1588         ipf_main_softc_t *softc = fin->fin_main_soft;
1589         ipf_nat_softc_t *softn = softc->ipf_nat_soft;
1590         u_32_t sum1, sum2, sumd, sumd2;
1591         i6addr_t a1, a2, a3, a4;
1592         struct icmp6_hdr *icmp6;
1593         int flags, dlen, odst;
1594         u_short *csump;
1595         tcphdr_t *tcp;
1596         ip6_t *oip6;
1597         nat_t *nat;
1598         void *dp;
1599
1600         if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
1601                 NBUMPSIDE6D(fin->fin_out, ns_icmp_short);
1602                 return NULL;
1603         }
1604
1605         /*
1606          * ipf_nat6_icmperrorlookup() will return NULL for `defective' packets.
1607          */
1608         if ((fin->fin_v != 6) || !(nat = ipf_nat6_icmperrorlookup(fin, dir))) {
1609                 NBUMPSIDE6D(fin->fin_out, ns_icmp_notfound);
1610                 return NULL;
1611         }
1612
1613         tcp = NULL;
1614         csump = NULL;
1615         flags = 0;
1616         sumd2 = 0;
1617         *nflags = IPN_ICMPERR;
1618         icmp6 = fin->fin_dp;
1619         oip6 = (ip6_t *)((u_char *)icmp6 + sizeof(*icmp6));
1620         dp = (u_char *)oip6 + sizeof(*oip6);
1621         if (oip6->ip6_nxt == IPPROTO_TCP) {
1622                 tcp = (tcphdr_t *)dp;
1623                 csump = (u_short *)&tcp->th_sum;
1624                 flags = IPN_TCP;
1625         } else if (oip6->ip6_nxt == IPPROTO_UDP) {
1626                 udphdr_t *udp;
1627
1628                 udp = (udphdr_t *)dp;
1629                 tcp = (tcphdr_t *)dp;
1630                 csump = (u_short *)&udp->uh_sum;
1631                 flags = IPN_UDP;
1632         } else if (oip6->ip6_nxt == IPPROTO_ICMPV6)
1633                 flags = IPN_ICMPQUERY;
1634         dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip);
1635
1636         /*
1637          * Need to adjust ICMP header to include the real IP#'s and
1638          * port #'s.  Only apply a checksum change relative to the
1639          * IP address change as it will be modified again in ipf_nat6_checkout
1640          * for both address and port.  Two checksum changes are
1641          * necessary for the two header address changes.  Be careful
1642          * to only modify the checksum once for the port # and twice
1643          * for the IP#.
1644          */
1645
1646         /*
1647          * Step 1
1648          * Fix the IP addresses in the offending IP packet. You also need
1649          * to adjust the IP header checksum of that offending IP packet.
1650          *
1651          * Normally, you would expect that the ICMP checksum of the
1652          * ICMP error message needs to be adjusted as well for the
1653          * IP address change in oip.
1654          * However, this is a NOP, because the ICMP checksum is
1655          * calculated over the complete ICMP packet, which includes the
1656          * changed oip IP addresses and oip6->ip6_sum. However, these
1657          * two changes cancel each other out (if the delta for
1658          * the IP address is x, then the delta for ip_sum is minus x),
1659          * so no change in the icmp_cksum is necessary.
1660          *
1661          * Inbound ICMP
1662          * ------------
1663          * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
1664          * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b)
1665          * - OIP_SRC(c)=nat6_newsrcip,          OIP_DST(b)=nat6_newdstip
1666          *=> OIP_SRC(c)=nat6_oldsrcip,          OIP_DST(b)=nat6_olddstip
1667          *
1668          * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
1669          * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a)
1670          * - OIP_SRC(b)=nat6_olddstip,          OIP_DST(a)=nat6_oldsrcip
1671          *=> OIP_SRC(b)=nat6_newdstip,          OIP_DST(a)=nat6_newsrcip
1672          *
1673          * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d
1674          * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d)
1675          * - OIP_SRC(c)=nat6_newsrcip,          OIP_DST(d)=nat6_newdstip
1676          *=> OIP_SRC(c)=nat6_oldsrcip,          OIP_DST(d)=nat6_olddstip
1677          *
1678          * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d
1679          * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a)
1680          * - OIP_SRC(b)=nat6_olddstip,          OIP_DST(a)=nat6_oldsrcip
1681          *=> OIP_SRC(b)=nat6_newdstip,          OIP_DST(a)=nat6_newsrcip
1682          *
1683          * Outbound ICMP
1684          * -------------
1685          * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
1686          * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a)
1687          * - OIP_SRC(b)=nat6_olddstip,          OIP_DST(a)=nat6_oldsrcip
1688          *=> OIP_SRC(b)=nat6_newdstip,          OIP_DST(a)=nat6_newsrcip
1689          *
1690          * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
1691          * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c)
1692          * - OIP_SRC(a)=nat6_newsrcip,          OIP_DST(c)=nat6_newdstip
1693          *=> OIP_SRC(a)=nat6_oldsrcip,          OIP_DST(c)=nat6_olddstip
1694          *
1695          * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d
1696          * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d)
1697          * - OIP_SRC(c)=nat6_olddstip,          OIP_DST(d)=nat6_oldsrcip
1698          *=> OIP_SRC(b)=nat6_newdstip,          OIP_DST(a)=nat6_newsrcip
1699          *
1700          * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d
1701          * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a)
1702          * - OIP_SRC(b)=nat6_newsrcip,          OIP_DST(a)=nat6_newdstip
1703          *=> OIP_SRC(a)=nat6_oldsrcip,          OIP_DST(c)=nat6_olddstip
1704          */
1705
1706         if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) ||
1707             ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) {
1708                 a1 = nat->nat_osrc6;
1709                 a4.in6 = oip6->ip6_src;
1710                 a3 = nat->nat_odst6;
1711                 a2.in6 = oip6->ip6_dst;
1712                 oip6->ip6_src = a1.in6;
1713                 oip6->ip6_dst = a3.in6;
1714                 odst = 1;
1715         } else {
1716                 a1 = nat->nat_ndst6;
1717                 a2.in6 = oip6->ip6_dst;
1718                 a3 = nat->nat_nsrc6;
1719                 a4.in6 = oip6->ip6_src;
1720                 oip6->ip6_dst = a3.in6;
1721                 oip6->ip6_src = a1.in6;
1722                 odst = 0;
1723         }
1724
1725         sumd = 0;
1726         if (IP6_NEQ(&a3, &a2) || IP6_NEQ(&a1, &a4)) {
1727                 if (IP6_GT(&a3, &a2)) {
1728                         sumd = ipf_nat6_ip6subtract(&a2, &a3);
1729                         sumd--;
1730                 } else {
1731                         sumd = ipf_nat6_ip6subtract(&a2, &a3);
1732                 }
1733                 if (IP6_GT(&a1, &a4)) {
1734                         sumd += ipf_nat6_ip6subtract(&a4, &a1);
1735                         sumd--;
1736                 } else {
1737                         sumd += ipf_nat6_ip6subtract(&a4, &a1);
1738                 }
1739                 sumd = ~sumd;
1740         }
1741
1742         sumd2 = sumd;
1743         sum1 = 0;
1744         sum2 = 0;
1745
1746         /*
1747          * Fix UDP pseudo header checksum to compensate for the
1748          * IP address change.
1749          */
1750         if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) {
1751                 u_32_t sum3, sum4;
1752                 /*
1753                  * Step 2 :
1754                  * For offending TCP/UDP IP packets, translate the ports as
1755                  * well, based on the NAT specification. Of course such
1756                  * a change may be reflected in the ICMP checksum as well.
1757                  *
1758                  * Since the port fields are part of the TCP/UDP checksum
1759                  * of the offending IP packet, you need to adjust that checksum
1760                  * as well... except that the change in the port numbers should
1761                  * be offset by the checksum change.  However, the TCP/UDP
1762                  * checksum will also need to change if there has been an
1763                  * IP address change.
1764                  */
1765                 if (odst == 1) {
1766                         sum1 = ntohs(nat->nat_osport);
1767                         sum4 = ntohs(tcp->th_sport);
1768                         sum3 = ntohs(nat->nat_odport);
1769                         sum2 = ntohs(tcp->th_dport);
1770
1771                         tcp->th_sport = htons(sum1);
1772                         tcp->th_dport = htons(sum3);
1773                 } else {
1774                         sum1 = ntohs(nat->nat_ndport);
1775                         sum2 = ntohs(tcp->th_dport);
1776                         sum3 = ntohs(nat->nat_nsport);
1777                         sum4 = ntohs(tcp->th_sport);
1778
1779                         tcp->th_dport = htons(sum3);
1780                         tcp->th_sport = htons(sum1);
1781                 }
1782                 sumd += sum1 - sum4;
1783                 sumd += sum3 - sum2;
1784
1785                 if (sumd != 0 || sumd2 != 0) {
1786                         /*
1787                          * At this point, sumd is the delta to apply to the
1788                          * TCP/UDP header, given the changes in both the IP
1789                          * address and the ports and sumd2 is the delta to
1790                          * apply to the ICMP header, given the IP address
1791                          * change delta that may need to be applied to the
1792                          * TCP/UDP checksum instead.
1793                          *
1794                          * If we will both the IP and TCP/UDP checksums
1795                          * then the ICMP checksum changes by the address
1796                          * delta applied to the TCP/UDP checksum.  If we
1797                          * do not change the TCP/UDP checksum them we
1798                          * apply the delta in ports to the ICMP checksum.
1799                          */
1800                         if (oip6->ip6_nxt == IPPROTO_UDP) {
1801                                 if ((dlen >= 8) && (*csump != 0)) {
1802                                         ipf_fix_datacksum(csump, sumd);
1803                                 } else {
1804                                         sumd2 = sum4 - sum1;
1805                                         if (sum1 > sum4)
1806                                                 sumd2--;
1807                                         sumd2 += sum2 - sum3;
1808                                         if (sum3 > sum2)
1809                                                 sumd2--;
1810                                 }
1811                         } else if (oip6->ip6_nxt == IPPROTO_TCP) {
1812                                 if (dlen >= 18) {
1813                                         ipf_fix_datacksum(csump, sumd);
1814                                 } else {
1815                                         sumd2 = sum4 - sum1;
1816                                         if (sum1 > sum4)
1817                                                 sumd2--;
1818                                         sumd2 += sum2 - sum3;
1819                                         if (sum3 > sum2)
1820                                                 sumd2--;
1821                                 }
1822                         }
1823                         if (sumd2 != 0) {
1824                                 sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
1825                                 sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
1826                                 sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
1827                                 ipf_fix_incksum(0, &icmp6->icmp6_cksum,
1828                                                 sumd2, 0);
1829                         }
1830                 }
1831         } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) {
1832                 struct icmp6_hdr *orgicmp;
1833
1834                 /*
1835                  * XXX - what if this is bogus hl and we go off the end ?
1836                  * In this case, ipf_nat6_icmperrorlookup() will have
1837                  * returned NULL.
1838                  */
1839                 orgicmp = (struct icmp6_hdr *)dp;
1840
1841                 if (odst == 1) {
1842                         if (orgicmp->icmp6_id != nat->nat_osport) {
1843
1844                                 /*
1845                                  * Fix ICMP checksum (of the offening ICMP
1846                                  * query packet) to compensate the change
1847                                  * in the ICMP id of the offending ICMP
1848                                  * packet.
1849                                  *
1850                                  * Since you modify orgicmp->icmp6_id with
1851                                  * a delta (say x) and you compensate that
1852                                  * in origicmp->icmp6_cksum with a delta
1853                                  * minus x, you don't have to adjust the
1854                                  * overall icmp->icmp6_cksum
1855                                  */
1856                                 sum1 = ntohs(orgicmp->icmp6_id);
1857                                 sum2 = ntohs(nat->nat_osport);
1858                                 CALC_SUMD(sum1, sum2, sumd);
1859                                 orgicmp->icmp6_id = nat->nat_oicmpid;
1860                                 ipf_fix_datacksum(&orgicmp->icmp6_cksum, sumd);
1861                         }
1862                 } /* nat6_dir == NAT_INBOUND is impossible for icmp queries */
1863         }
1864         return nat;
1865 }
1866
1867
1868 /*
1869  *       MAP-IN    MAP-OUT   RDR-IN   RDR-OUT
1870  * osrc    X       == src    == src      X
1871  * odst    X       == dst    == dst      X
1872  * nsrc  == dst      X         X      == dst
1873  * ndst  == src      X         X      == src
1874  * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND
1875  */
1876 /*
1877  * NB: these lookups don't lock access to the list, it assumed that it has
1878  * already been done!
1879  */
1880 /* ------------------------------------------------------------------------ */
1881 /* Function:    ipf_nat6_inlookup                                           */
1882 /* Returns:     nat6_t*   - NULL == no match,                               */
1883 /*                          else pointer to matching NAT entry              */
1884 /* Parameters:  fin(I)    - pointer to packet information                   */
1885 /*              flags(I)  - NAT flags for this packet                       */
1886 /*              p(I)      - protocol for this packet                        */
1887 /*              src(I)    - source IP address                               */
1888 /*              mapdst(I) - destination IP address                          */
1889 /*                                                                          */
1890 /* Lookup a nat entry based on the mapped destination ip address/port and   */
1891 /* real source address/port.  We use this lookup when receiving a packet,   */
1892 /* we're looking for a table entry, based on the destination address.       */
1893 /*                                                                          */
1894 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
1895 /*                                                                          */
1896 /* NOTE: IT IS ASSUMED THAT  IS ONLY HELD WITH A READ LOCK WHEN             */
1897 /*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
1898 /*                                                                          */
1899 /* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
1900 /*            the packet is of said protocol                                */
1901 /* ------------------------------------------------------------------------ */
1902 nat_t *
1903 ipf_nat6_inlookup(fin, flags, p, src, mapdst)
1904         fr_info_t *fin;
1905         u_int flags, p;
1906         struct in6_addr *src , *mapdst;
1907 {
1908         ipf_main_softc_t *softc = fin->fin_main_soft;
1909         ipf_nat_softc_t *softn = softc->ipf_nat_soft;
1910         u_short sport, dport;
1911         grehdr_t *gre;
1912         ipnat_t *ipn;
1913         u_int sflags;
1914         nat_t *nat;
1915         int nflags;
1916         i6addr_t dst;
1917         void *ifp;
1918         u_int hv;
1919
1920         ifp = fin->fin_ifp;
1921         sport = 0;
1922         dport = 0;
1923         gre = NULL;
1924         dst.in6 = *mapdst;
1925         sflags = flags & NAT_TCPUDPICMP;
1926
1927         switch (p)
1928         {
1929         case IPPROTO_TCP :
1930         case IPPROTO_UDP :
1931                 sport = htons(fin->fin_data[0]);
1932                 dport = htons(fin->fin_data[1]);
1933                 break;
1934         case IPPROTO_ICMPV6 :
1935                 if (flags & IPN_ICMPERR)
1936                         sport = fin->fin_data[1];
1937                 else
1938                         dport = fin->fin_data[1];
1939                 break;
1940         default :
1941                 break;
1942         }
1943
1944
1945         if ((flags & SI_WILDP) != 0)
1946                 goto find_in_wild_ports;
1947
1948         hv = NAT_HASH_FN6(&dst, dport, 0xffffffff);
1949         hv = NAT_HASH_FN6(src, hv + sport, softn->ipf_nat_table_sz);
1950         nat = softn->ipf_nat_table[1][hv];
1951         /* TRACE dst, dport, src, sport, hv, nat */
1952
1953         for (; nat; nat = nat->nat_hnext[1]) {
1954                 if (nat->nat_ifps[0] != NULL) {
1955                         if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
1956                                 continue;
1957                 }
1958
1959                 if (nat->nat_pr[0] != p)
1960                         continue;
1961
1962                 switch (nat->nat_dir)
1963                 {
1964                 case NAT_INBOUND :
1965                         if (nat->nat_v[0] != 6)
1966                                 continue;
1967                         if (IP6_NEQ(&nat->nat_osrc6, src) ||
1968                             IP6_NEQ(&nat->nat_odst6, &dst))
1969                                 continue;
1970                         if ((nat->nat_flags & IPN_TCPUDP) != 0) {
1971                                 if (nat->nat_osport != sport)
1972                                         continue;
1973                                 if (nat->nat_odport != dport)
1974                                         continue;
1975
1976                         } else if (p == IPPROTO_ICMPV6) {
1977                                 if (nat->nat_osport != dport) {
1978                                         continue;
1979                                 }
1980                         }
1981                         break;
1982                 case NAT_OUTBOUND :
1983                         if (nat->nat_v[1] != 6)
1984                                 continue;
1985                         if (IP6_NEQ(&nat->nat_ndst6, src) ||
1986                             IP6_NEQ(&nat->nat_nsrc6, &dst))
1987                                 continue;
1988                         if ((nat->nat_flags & IPN_TCPUDP) != 0) {
1989                                 if (nat->nat_ndport != sport)
1990                                         continue;
1991                                 if (nat->nat_nsport != dport)
1992                                         continue;
1993
1994                         } else if (p == IPPROTO_ICMPV6) {
1995                                 if (nat->nat_osport != dport) {
1996                                         continue;
1997                                 }
1998                         }
1999                         break;
2000                 }
2001
2002
2003                 if ((nat->nat_flags & IPN_TCPUDP) != 0) {
2004                         ipn = nat->nat_ptr;
2005 #ifdef IPF_V6_PROXIES
2006                         if ((ipn != NULL) && (nat->nat_aps != NULL))
2007                                 if (appr_match(fin, nat) != 0)
2008                                         continue;
2009 #endif
2010                 }
2011                 if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) {
2012                         nat->nat_ifps[0] = ifp;
2013                         nat->nat_mtu[0] = GETIFMTU_6(ifp);
2014                 }
2015                 return nat;
2016         }
2017
2018         /*
2019          * So if we didn't find it but there are wildcard members in the hash
2020          * table, go back and look for them.  We do this search and update here
2021          * because it is modifying the NAT table and we want to do this only
2022          * for the first packet that matches.  The exception, of course, is
2023          * for "dummy" (FI_IGNORE) lookups.
2024          */
2025 find_in_wild_ports:
2026         if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) {
2027                 NBUMPSIDE6DX(0, ns_lookup_miss, ns_lookup_miss_1);
2028                 return NULL;
2029         }
2030         if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) {
2031                 NBUMPSIDE6D(0, ns_lookup_nowild);
2032                 return NULL;
2033         }
2034
2035         RWLOCK_EXIT(&softc->ipf_nat);
2036
2037         hv = NAT_HASH_FN6(&dst, 0, 0xffffffff);
2038         hv = NAT_HASH_FN6(src, hv, softn->ipf_nat_table_sz);
2039         WRITE_ENTER(&softc->ipf_nat);
2040
2041         nat = softn->ipf_nat_table[1][hv];
2042         /* TRACE dst, src, hv, nat */
2043         for (; nat; nat = nat->nat_hnext[1]) {
2044                 if (nat->nat_ifps[0] != NULL) {
2045                         if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
2046                                 continue;
2047                 }
2048
2049                 if (nat->nat_pr[0] != fin->fin_p)
2050                         continue;
2051
2052                 switch (nat->nat_dir)
2053                 {
2054                 case NAT_INBOUND :
2055                         if (nat->nat_v[0] != 6)
2056                                 continue;
2057                         if (IP6_NEQ(&nat->nat_osrc6, src) ||
2058                             IP6_NEQ(&nat->nat_odst6, &dst))
2059                                 continue;
2060                         break;
2061                 case NAT_OUTBOUND :
2062                         if (nat->nat_v[1] != 6)
2063                                 continue;
2064                         if (IP6_NEQ(&nat->nat_ndst6, src) ||
2065                             IP6_NEQ(&nat->nat_nsrc6, &dst))
2066                                 continue;
2067                         break;
2068                 }
2069
2070                 nflags = nat->nat_flags;
2071                 if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
2072                         continue;
2073
2074                 if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags,
2075                                    NAT_INBOUND) == 1) {
2076                         if ((fin->fin_flx & FI_IGNORE) != 0)
2077                                 break;
2078                         if ((nflags & SI_CLONE) != 0) {
2079                                 nat = ipf_nat_clone(fin, nat);
2080                                 if (nat == NULL)
2081                                         break;
2082                         } else {
2083                                 MUTEX_ENTER(&softn->ipf_nat_new);
2084                                 softn->ipf_nat_stats.ns_wilds--;
2085                                 MUTEX_EXIT(&softn->ipf_nat_new);
2086                         }
2087
2088                         if (nat->nat_dir == NAT_INBOUND) {
2089                                 if (nat->nat_osport == 0) {
2090                                         nat->nat_osport = sport;
2091                                         nat->nat_nsport = sport;
2092                                 }
2093                                 if (nat->nat_odport == 0) {
2094                                         nat->nat_odport = dport;
2095                                         nat->nat_ndport = dport;
2096                                 }
2097                         } else {
2098                                 if (nat->nat_osport == 0) {
2099                                         nat->nat_osport = dport;
2100                                         nat->nat_nsport = dport;
2101                                 }
2102                                 if (nat->nat_odport == 0) {
2103                                         nat->nat_odport = sport;
2104                                         nat->nat_ndport = sport;
2105                                 }
2106                         }
2107                         if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) {
2108                                 nat->nat_ifps[0] = ifp;
2109                                 nat->nat_mtu[0] = GETIFMTU_6(ifp);
2110                         }
2111                         nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
2112                         ipf_nat6_tabmove(softn, nat);
2113                         break;
2114                 }
2115         }
2116
2117         MUTEX_DOWNGRADE(&softc->ipf_nat);
2118
2119         if (nat == NULL) {
2120                 NBUMPSIDE6DX(0, ns_lookup_miss, ns_lookup_miss_2);
2121         }
2122         return nat;
2123 }
2124
2125
2126 /* ------------------------------------------------------------------------ */
2127 /* Function:    ipf_nat6_tabmove                                            */
2128 /* Returns:     Nil                                                         */
2129 /* Parameters:  nat(I) - pointer to NAT structure                           */
2130 /* Write Lock:  ipf_nat                                                     */
2131 /*                                                                          */
2132 /* This function is only called for TCP/UDP NAT table entries where the     */
2133 /* original was placed in the table without hashing on the ports and we now */
2134 /* want to include hashing on port numbers.                                 */
2135 /* ------------------------------------------------------------------------ */
2136 static void
2137 ipf_nat6_tabmove(softn, nat)
2138         ipf_nat_softc_t *softn;
2139         nat_t *nat;
2140 {
2141         nat_t **natp;
2142         u_int hv0, hv1;
2143
2144         if (nat->nat_flags & SI_CLONE)
2145                 return;
2146
2147         /*
2148          * Remove the NAT entry from the old location
2149          */
2150         if (nat->nat_hnext[0])
2151                 nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
2152         *nat->nat_phnext[0] = nat->nat_hnext[0];
2153         softn->ipf_nat_stats.ns_side[0].ns_bucketlen[nat->nat_hv[0]]--;
2154
2155         if (nat->nat_hnext[1])
2156                 nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
2157         *nat->nat_phnext[1] = nat->nat_hnext[1];
2158         softn->ipf_nat_stats.ns_side[1].ns_bucketlen[nat->nat_hv[1]]--;
2159
2160         /*
2161          * Add into the NAT table in the new position
2162          */
2163         hv0 = NAT_HASH_FN6(&nat->nat_osrc6, nat->nat_osport, 0xffffffff);
2164         hv0 = NAT_HASH_FN6(&nat->nat_odst6, hv0 + nat->nat_odport,
2165                            softn->ipf_nat_table_sz);
2166         hv1 = NAT_HASH_FN6(&nat->nat_nsrc6, nat->nat_nsport, 0xffffffff);
2167         hv1 = NAT_HASH_FN6(&nat->nat_ndst6, hv1 + nat->nat_ndport,
2168                            softn->ipf_nat_table_sz);
2169
2170         if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) {
2171                 u_int swap;
2172
2173                 swap = hv0;
2174                 hv0 = hv1;
2175                 hv1 = swap;
2176         }
2177
2178         /* TRACE nat_osrc6, nat_osport, nat_odst6, nat_odport, hv0 */
2179         /* TRACE nat_nsrc6, nat_nsport, nat_ndst6, nat_ndport, hv1 */
2180
2181         nat->nat_hv[0] = hv0;
2182         natp = &softn->ipf_nat_table[0][hv0];
2183         if (*natp)
2184                 (*natp)->nat_phnext[0] = &nat->nat_hnext[0];
2185         nat->nat_phnext[0] = natp;
2186         nat->nat_hnext[0] = *natp;
2187         *natp = nat;
2188         softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]++;
2189
2190         nat->nat_hv[1] = hv1;
2191         natp = &softn->ipf_nat_table[1][hv1];
2192         if (*natp)
2193                 (*natp)->nat_phnext[1] = &nat->nat_hnext[1];
2194         nat->nat_phnext[1] = natp;
2195         nat->nat_hnext[1] = *natp;
2196         *natp = nat;
2197         softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]++;
2198 }
2199
2200
2201 /* ------------------------------------------------------------------------ */
2202 /* Function:    ipf_nat6_outlookup                                          */
2203 /* Returns:     nat6_t*  - NULL == no match,                                */
2204 /*                         else pointer to matching NAT entry               */
2205 /* Parameters:  fin(I)   - pointer to packet information                    */
2206 /*              flags(I) - NAT flags for this packet                        */
2207 /*              p(I)     - protocol for this packet                         */
2208 /*              src(I)   - source IP address                                */
2209 /*              dst(I)   - destination IP address                           */
2210 /*              rw(I)    - 1 == write lock on  held, 0 == read lock.        */
2211 /*                                                                          */
2212 /* Lookup a nat entry based on the source 'real' ip address/port and        */
2213 /* destination address/port.  We use this lookup when sending a packet out, */
2214 /* we're looking for a table entry, based on the source address.            */
2215 /*                                                                          */
2216 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
2217 /*                                                                          */
2218 /* NOTE: IT IS ASSUMED THAT  IS ONLY HELD WITH A READ LOCK WHEN             */
2219 /*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
2220 /*                                                                          */
2221 /* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
2222 /*            the packet is of said protocol                                */
2223 /* ------------------------------------------------------------------------ */
2224 nat_t *
2225 ipf_nat6_outlookup(fin, flags, p, src, dst)
2226         fr_info_t *fin;
2227         u_int flags, p;
2228         struct in6_addr *src , *dst;
2229 {
2230         ipf_main_softc_t *softc = fin->fin_main_soft;
2231         ipf_nat_softc_t *softn = softc->ipf_nat_soft;
2232         u_short sport, dport;
2233         u_int sflags;
2234         ipnat_t *ipn;
2235         nat_t *nat;
2236         void *ifp;
2237         u_int hv;
2238
2239         ifp = fin->fin_ifp;
2240         sflags = flags & IPN_TCPUDPICMP;
2241         sport = 0;
2242         dport = 0;
2243
2244         switch (p)
2245         {
2246         case IPPROTO_TCP :
2247         case IPPROTO_UDP :
2248                 sport = htons(fin->fin_data[0]);
2249                 dport = htons(fin->fin_data[1]);
2250                 break;
2251         case IPPROTO_ICMPV6 :
2252                 if (flags & IPN_ICMPERR)
2253                         sport = fin->fin_data[1];
2254                 else
2255                         dport = fin->fin_data[1];
2256                 break;
2257         default :
2258                 break;
2259         }
2260
2261         if ((flags & SI_WILDP) != 0)
2262                 goto find_out_wild_ports;
2263
2264         hv = NAT_HASH_FN6(src, sport, 0xffffffff);
2265         hv = NAT_HASH_FN6(dst, hv + dport, softn->ipf_nat_table_sz);
2266         nat = softn->ipf_nat_table[0][hv];
2267
2268         /* TRACE src, sport, dst, dport, hv, nat */
2269
2270         for (; nat; nat = nat->nat_hnext[0]) {
2271                 if (nat->nat_ifps[1] != NULL) {
2272                         if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
2273                                 continue;
2274                 }
2275
2276                 if (nat->nat_pr[1] != p)
2277                         continue;
2278
2279                 switch (nat->nat_dir)
2280                 {
2281                 case NAT_INBOUND :
2282                         if (nat->nat_v[1] != 6)
2283                                 continue;
2284                         if (IP6_NEQ(&nat->nat_ndst6, src) ||
2285                             IP6_NEQ(&nat->nat_nsrc6, dst))
2286                                 continue;
2287
2288                         if ((nat->nat_flags & IPN_TCPUDP) != 0) {
2289                                 if (nat->nat_ndport != sport)
2290                                         continue;
2291                                 if (nat->nat_nsport != dport)
2292                                         continue;
2293
2294                         } else if (p == IPPROTO_ICMPV6) {
2295                                 if (nat->nat_osport != dport) {
2296                                         continue;
2297                                 }
2298                         }
2299                         break;
2300                 case NAT_OUTBOUND :
2301                         if (nat->nat_v[0] != 6)
2302                                 continue;
2303                         if (IP6_NEQ(&nat->nat_osrc6, src) ||
2304                             IP6_NEQ(&nat->nat_odst6, dst))
2305                                 continue;
2306
2307                         if ((nat->nat_flags & IPN_TCPUDP) != 0) {
2308                                 if (nat->nat_odport != dport)
2309                                         continue;
2310                                 if (nat->nat_osport != sport)
2311                                         continue;
2312
2313                         } else if (p == IPPROTO_ICMPV6) {
2314                                 if (nat->nat_osport != dport) {
2315                                         continue;
2316                                 }
2317                         }
2318                         break;
2319                 }
2320
2321                 ipn = nat->nat_ptr;
2322 #ifdef IPF_V6_PROXIES
2323                 if ((ipn != NULL) && (nat->nat_aps != NULL))
2324                         if (appr_match(fin, nat) != 0)
2325                                 continue;
2326 #endif
2327
2328                 if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) {
2329                         nat->nat_ifps[1] = ifp;
2330                         nat->nat_mtu[1] = GETIFMTU_6(ifp);
2331                 }
2332                 return nat;
2333         }
2334
2335         /*
2336          * So if we didn't find it but there are wildcard members in the hash
2337          * table, go back and look for them.  We do this search and update here
2338          * because it is modifying the NAT table and we want to do this only
2339          * for the first packet that matches.  The exception, of course, is
2340          * for "dummy" (FI_IGNORE) lookups.
2341          */
2342 find_out_wild_ports:
2343         if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) {
2344                 NBUMPSIDE6DX(1, ns_lookup_miss, ns_lookup_miss_3);
2345                 return NULL;
2346         }
2347         if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) {
2348                 NBUMPSIDE6D(1, ns_lookup_nowild);
2349                 return NULL;
2350         }
2351
2352         RWLOCK_EXIT(&softc->ipf_nat);
2353
2354         hv = NAT_HASH_FN6(src, 0, 0xffffffff);
2355         hv = NAT_HASH_FN6(dst, hv, softn->ipf_nat_table_sz);
2356
2357         WRITE_ENTER(&softc->ipf_nat);
2358
2359         nat = softn->ipf_nat_table[0][hv];
2360         for (; nat; nat = nat->nat_hnext[0]) {
2361                 if (nat->nat_ifps[1] != NULL) {
2362                         if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
2363                                 continue;
2364                 }
2365
2366                 if (nat->nat_pr[1] != fin->fin_p)
2367                         continue;
2368
2369                 switch (nat->nat_dir)
2370                 {
2371                 case NAT_INBOUND :
2372                         if (nat->nat_v[1] != 6)
2373                                 continue;
2374                         if (IP6_NEQ(&nat->nat_ndst6, src) ||
2375                             IP6_NEQ(&nat->nat_nsrc6, dst))
2376                                 continue;
2377                         break;
2378                 case NAT_OUTBOUND :
2379                         if (nat->nat_v[0] != 6)
2380                         continue;
2381                         if (IP6_NEQ(&nat->nat_osrc6, src) ||
2382                             IP6_NEQ(&nat->nat_odst6, dst))
2383                                 continue;
2384                         break;
2385                 }
2386
2387                 if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP)))
2388                         continue;
2389
2390                 if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags,
2391                                    NAT_OUTBOUND) == 1) {
2392                         if ((fin->fin_flx & FI_IGNORE) != 0)
2393                                 break;
2394                         if ((nat->nat_flags & SI_CLONE) != 0) {
2395                                 nat = ipf_nat_clone(fin, nat);
2396                                 if (nat == NULL)
2397                                         break;
2398                         } else {
2399                                 MUTEX_ENTER(&softn->ipf_nat_new);
2400                                 softn->ipf_nat_stats.ns_wilds--;
2401                                 MUTEX_EXIT(&softn->ipf_nat_new);
2402                         }
2403
2404                         if (nat->nat_dir == NAT_OUTBOUND) {
2405                                 if (nat->nat_osport == 0) {
2406                                         nat->nat_osport = sport;
2407                                         nat->nat_nsport = sport;
2408                                 }
2409                                 if (nat->nat_odport == 0) {
2410                                         nat->nat_odport = dport;
2411                                         nat->nat_ndport = dport;
2412                                 }
2413                         } else {
2414                                 if (nat->nat_osport == 0) {
2415                                         nat->nat_osport = dport;
2416                                         nat->nat_nsport = dport;
2417                                 }
2418                                 if (nat->nat_odport == 0) {
2419                                         nat->nat_odport = sport;
2420                                         nat->nat_ndport = sport;
2421                                 }
2422                         }
2423                         if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) {
2424                                 nat->nat_ifps[1] = ifp;
2425                                 nat->nat_mtu[1] = GETIFMTU_6(ifp);
2426                         }
2427                         nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
2428                         ipf_nat6_tabmove(softn, nat);
2429                         break;
2430                 }
2431         }
2432
2433         MUTEX_DOWNGRADE(&softc->ipf_nat);
2434
2435         if (nat == NULL) {
2436                 NBUMPSIDE6DX(1, ns_lookup_miss, ns_lookup_miss_4);
2437         }
2438         return nat;
2439 }
2440
2441
2442 /* ------------------------------------------------------------------------ */
2443 /* Function:    ipf_nat6_lookupredir                                        */
2444 /* Returns:     nat6_t* - NULL == no match,                                 */
2445 /*                       else pointer to matching NAT entry                 */
2446 /* Parameters:  np(I) - pointer to description of packet to find NAT table  */
2447 /*                      entry for.                                          */
2448 /*                                                                          */
2449 /* Lookup the NAT tables to search for a matching redirect                  */
2450 /* The contents of natlookup_t should imitate those found in a packet that  */
2451 /* would be translated - ie a packet coming in for RDR or going out for MAP.*/
2452 /* We can do the lookup in one of two ways, imitating an inbound or         */
2453 /* outbound  packet.  By default we assume outbound, unless IPN_IN is set.  */
2454 /* For IN, the fields are set as follows:                                   */
2455 /*     nl_real* = source information                                        */
2456 /*     nl_out* = destination information (translated)                       */
2457 /* For an out packet, the fields are set like this:                         */
2458 /*     nl_in* = source information (untranslated)                           */
2459 /*     nl_out* = destination information (translated)                       */
2460 /* ------------------------------------------------------------------------ */
2461 nat_t *
2462 ipf_nat6_lookupredir(np)
2463         natlookup_t *np;
2464 {
2465         fr_info_t fi;
2466         nat_t *nat;
2467
2468         bzero((char *)&fi, sizeof(fi));
2469         if (np->nl_flags & IPN_IN) {
2470                 fi.fin_data[0] = ntohs(np->nl_realport);
2471                 fi.fin_data[1] = ntohs(np->nl_outport);
2472         } else {
2473                 fi.fin_data[0] = ntohs(np->nl_inport);
2474                 fi.fin_data[1] = ntohs(np->nl_outport);
2475         }
2476         if (np->nl_flags & IPN_TCP)
2477                 fi.fin_p = IPPROTO_TCP;
2478         else if (np->nl_flags & IPN_UDP)
2479                 fi.fin_p = IPPROTO_UDP;
2480         else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY))
2481                 fi.fin_p = IPPROTO_ICMPV6;
2482
2483         /*
2484          * We can do two sorts of lookups:
2485          * - IPN_IN: we have the `real' and `out' address, look for `in'.
2486          * - default: we have the `in' and `out' address, look for `real'.
2487          */
2488         if (np->nl_flags & IPN_IN) {
2489                 if ((nat = ipf_nat6_inlookup(&fi, np->nl_flags, fi.fin_p,
2490                                              &np->nl_realip6,
2491                                              &np->nl_outip6))) {
2492                         np->nl_inip6 = nat->nat_odst6.in6;
2493                         np->nl_inport = nat->nat_odport;
2494                 }
2495         } else {
2496                 /*
2497                  * If nl_inip is non null, this is a lookup based on the real
2498                  * ip address. Else, we use the fake.
2499                  */
2500                 if ((nat = ipf_nat6_outlookup(&fi, np->nl_flags, fi.fin_p,
2501                                               &np->nl_inip6, &np->nl_outip6))) {
2502
2503                         if ((np->nl_flags & IPN_FINDFORWARD) != 0) {
2504                                 fr_info_t fin;
2505                                 bzero((char *)&fin, sizeof(fin));
2506                                 fin.fin_p = nat->nat_pr[0];
2507                                 fin.fin_data[0] = ntohs(nat->nat_ndport);
2508                                 fin.fin_data[1] = ntohs(nat->nat_nsport);
2509                                 if (ipf_nat6_inlookup(&fin, np->nl_flags,
2510                                                      fin.fin_p,
2511                                                      &nat->nat_ndst6.in6,
2512                                                      &nat->nat_nsrc6.in6) !=
2513                                     NULL) {
2514                                         np->nl_flags &= ~IPN_FINDFORWARD;
2515                                 }
2516                         }
2517
2518                         np->nl_realip6 = nat->nat_odst6.in6;
2519                         np->nl_realport = nat->nat_odport;
2520                 }
2521         }
2522
2523         return nat;
2524 }
2525
2526
2527 /* ------------------------------------------------------------------------ */
2528 /* Function:    ipf_nat6_match                                              */
2529 /* Returns:     int - 0 == no match, 1 == match                             */
2530 /* Parameters:  fin(I)   - pointer to packet information                    */
2531 /*              np(I)    - pointer to NAT rule                              */
2532 /*                                                                          */
2533 /* Pull the matching of a packet against a NAT rule out of that complex     */
2534 /* loop inside ipf_nat6_checkin() and lay it out properly in its own        */
2535 /* function.                                                                */
2536 /* ------------------------------------------------------------------------ */
2537 static int
2538 ipf_nat6_match(fin, np)
2539         fr_info_t *fin;
2540         ipnat_t *np;
2541 {
2542         frtuc_t *ft;
2543         int match;
2544
2545         match = 0;
2546         switch (np->in_osrcatype)
2547         {
2548         case FRI_NORMAL :
2549                 match = IP6_MASKNEQ(&fin->fin_src6, &np->in_osrcmsk6,
2550                                     &np->in_osrcip6);
2551                 break;
2552         case FRI_LOOKUP :
2553                 match = (*np->in_osrcfunc)(fin->fin_main_soft, np->in_osrcptr,
2554                                            6, &fin->fin_src6, fin->fin_plen);
2555                 break;
2556         }
2557         match ^= ((np->in_flags & IPN_NOTSRC) != 0);
2558         if (match)
2559                 return 0;
2560
2561         match = 0;
2562         switch (np->in_odstatype)
2563         {
2564         case FRI_NORMAL :
2565                 match = IP6_MASKNEQ(&fin->fin_dst6, &np->in_odstmsk6,
2566                                     &np->in_odstip6);
2567                 break;
2568         case FRI_LOOKUP :
2569                 match = (*np->in_odstfunc)(fin->fin_main_soft, np->in_odstptr,
2570                                            6, &fin->fin_dst6, fin->fin_plen);
2571                 break;
2572         }
2573
2574         match ^= ((np->in_flags & IPN_NOTDST) != 0);
2575         if (match)
2576                 return 0;
2577
2578         ft = &np->in_tuc;
2579         if (!(fin->fin_flx & FI_TCPUDP) ||
2580             (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
2581                 if (ft->ftu_scmp || ft->ftu_dcmp)
2582                         return 0;
2583                 return 1;
2584         }
2585
2586         return ipf_tcpudpchk(&fin->fin_fi, ft);
2587 }
2588
2589
2590 /* ------------------------------------------------------------------------ */
2591 /* Function:    ipf_nat6_checkout                                           */
2592 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
2593 /*                     0 == no packet translation occurred,                 */
2594 /*                     1 == packet was successfully translated.             */
2595 /* Parameters:  fin(I)   - pointer to packet information                    */
2596 /*              passp(I) - pointer to filtering result flags                */
2597 /*                                                                          */
2598 /* Check to see if an outcoming packet should be changed.  ICMP packets are */
2599 /* first checked to see if they match an existing entry (if an error),      */
2600 /* otherwise a search of the current NAT table is made.  If neither results */
2601 /* in a match then a search for a matching NAT rule is made.  Create a new  */
2602 /* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
2603 /* packet header(s) as required.                                            */
2604 /* ------------------------------------------------------------------------ */
2605 int
2606 ipf_nat6_checkout(fin, passp)
2607         fr_info_t *fin;
2608         u_32_t *passp;
2609 {
2610         ipf_main_softc_t *softc = fin->fin_main_soft;
2611         ipf_nat_softc_t *softn = softc->ipf_nat_soft;
2612         struct icmp6_hdr *icmp6 = NULL;
2613         struct ifnet *ifp, *sifp;
2614         tcphdr_t *tcp = NULL;
2615         int rval, natfailed;
2616         ipnat_t *np = NULL;
2617         u_int nflags = 0;
2618         i6addr_t ipa, iph;
2619         int natadd = 1;
2620         frentry_t *fr;
2621         nat_t *nat;
2622
2623         if (softn->ipf_nat_stats.ns_rules == 0 || softn->ipf_nat_lock != 0)
2624                 return 0;
2625
2626         icmp6 = NULL;
2627         natfailed = 0;
2628         fr = fin->fin_fr;
2629         sifp = fin->fin_ifp;
2630         if (fr != NULL) {
2631                 ifp = fr->fr_tifs[fin->fin_rev].fd_ptr;
2632                 if ((ifp != NULL) && (ifp != (void *)-1))
2633                         fin->fin_ifp = ifp;
2634         }
2635         ifp = fin->fin_ifp;
2636
2637         if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
2638                 switch (fin->fin_p)
2639                 {
2640                 case IPPROTO_TCP :
2641                         nflags = IPN_TCP;
2642                         break;
2643                 case IPPROTO_UDP :
2644                         nflags = IPN_UDP;
2645                         break;
2646                 case IPPROTO_ICMPV6 :
2647                         icmp6 = fin->fin_dp;
2648
2649                         /*
2650                          * Apart from ECHO request and reply, all other
2651                          * informational messages should not be translated
2652                          * so as to keep IPv6 working.
2653                          */
2654                         if (icmp6->icmp6_type > ICMP6_ECHO_REPLY)
2655                                 return 0;
2656
2657                         /*
2658                          * This is an incoming packet, so the destination is
2659                          * the icmp6_id and the source port equals 0
2660                          */
2661                         if ((fin->fin_flx & FI_ICMPQUERY) != 0)
2662                                 nflags = IPN_ICMPQUERY;
2663                         break;
2664                 default :
2665                         break;
2666                 }
2667
2668                 if ((nflags & IPN_TCPUDP))
2669                         tcp = fin->fin_dp;
2670         }
2671
2672         ipa = fin->fin_src6;
2673
2674         READ_ENTER(&softc->ipf_nat);
2675
2676         if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) &&
2677             (nat = ipf_nat6_icmperror(fin, &nflags, NAT_OUTBOUND)))
2678                 /*EMPTY*/;
2679         else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin)))
2680                 natadd = 0;
2681         else if ((nat = ipf_nat6_outlookup(fin, nflags|NAT_SEARCH,
2682                                            (u_int)fin->fin_p,
2683                                            &fin->fin_src6.in6,
2684                                            &fin->fin_dst6.in6))) {
2685                 nflags = nat->nat_flags;
2686         } else if (fin->fin_off == 0) {
2687                 u_32_t hv, nmsk = 0;
2688                 i6addr_t *msk;
2689
2690                 /*
2691                  * If there is no current entry in the nat table for this IP#,
2692                  * create one for it (if there is a matching rule).
2693                  */
2694 maskloop:
2695                 msk = &softn->ipf_nat6_map_active_masks[nmsk];
2696                 IP6_AND(&ipa, msk, &iph);
2697                 hv = NAT_HASH_FN6(&iph, 0, softn->ipf_nat_maprules_sz);
2698                 for (np = softn->ipf_nat_map_rules[hv]; np; np = np->in_mnext) {
2699                         if ((np->in_ifps[1] && (np->in_ifps[1] != ifp)))
2700                                 continue;
2701                         if (np->in_v[0] != 6)
2702                                 continue;
2703                         if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p))
2704                                 continue;
2705                         if ((np->in_flags & IPN_RF) &&
2706                             !(np->in_flags & nflags))
2707                                 continue;
2708                         if (np->in_flags & IPN_FILTER) {
2709                                 switch (ipf_nat6_match(fin, np))
2710                                 {
2711                                 case 0 :
2712                                         continue;
2713                                 case -1 :
2714                                         rval = -1;
2715                                         goto outmatchfail;
2716                                 case 1 :
2717                                 default :
2718                                         break;
2719                                 }
2720                         } else if (!IP6_MASKEQ(&ipa, &np->in_osrcmsk,
2721                                                &np->in_osrcip6))
2722                                 continue;
2723
2724                         if ((fr != NULL) &&
2725                             !ipf_matchtag(&np->in_tag, &fr->fr_nattag))
2726                                 continue;
2727
2728 #ifdef IPF_V6_PROXIES
2729                         if (np->in_plabel != -1) {
2730                                 if (((np->in_flags & IPN_FILTER) == 0) &&
2731                                     (np->in_odport != fin->fin_data[1]))
2732                                         continue;
2733                                 if (appr_ok(fin, tcp, np) == 0)
2734                                         continue;
2735                         }
2736 #endif
2737
2738                         if (np->in_flags & IPN_NO) {
2739                                 np->in_hits++;
2740                                 break;
2741                         }
2742
2743                         MUTEX_ENTER(&softn->ipf_nat_new);
2744                         nat = ipf_nat6_add(fin, np, NULL, nflags, NAT_OUTBOUND);
2745                         MUTEX_EXIT(&softn->ipf_nat_new);
2746                         if (nat != NULL) {
2747                                 np->in_hits++;
2748                                 break;
2749                         }
2750                         natfailed = -1;
2751                 }
2752                 if ((np == NULL) && (nmsk < softn->ipf_nat6_map_max)) {
2753                         nmsk++;
2754                         goto maskloop;
2755                 }
2756         }
2757
2758         if (nat != NULL) {
2759                 rval = ipf_nat6_out(fin, nat, natadd, nflags);
2760                 if (rval == 1) {
2761                         MUTEX_ENTER(&nat->nat_lock);
2762                         ipf_nat_update(fin, nat);
2763                         nat->nat_bytes[1] += fin->fin_plen;
2764                         nat->nat_pkts[1]++;
2765                         MUTEX_EXIT(&nat->nat_lock);
2766                 }
2767         } else
2768                 rval = natfailed;
2769 outmatchfail:
2770         RWLOCK_EXIT(&softc->ipf_nat);
2771
2772         switch (rval)
2773         {
2774         case -1 :
2775                 if (passp != NULL) {
2776                         NBUMPSIDE6D(1, ns_drop);
2777                         *passp = FR_BLOCK;
2778                         fin->fin_reason = FRB_NATV6;
2779                 }
2780                 fin->fin_flx |= FI_BADNAT;
2781                 NBUMPSIDE6D(1, ns_badnat);
2782                 break;
2783         case 0 :
2784                 NBUMPSIDE6D(1, ns_ignored);
2785                 break;
2786         case 1 :
2787                 NBUMPSIDE6D(1, ns_translated);
2788                 break;
2789         }
2790         fin->fin_ifp = sifp;
2791         return rval;
2792 }
2793
2794 /* ------------------------------------------------------------------------ */
2795 /* Function:    ipf_nat6_out                                                */
2796 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
2797 /*                     1 == packet was successfully translated.             */
2798 /* Parameters:  fin(I)    - pointer to packet information                   */
2799 /*              nat(I)    - pointer to NAT structure                        */
2800 /*              natadd(I) - flag indicating if it is safe to add frag cache */
2801 /*              nflags(I) - NAT flags set for this packet                   */
2802 /*                                                                          */
2803 /* Translate a packet coming "out" on an interface.                         */
2804 /* ------------------------------------------------------------------------ */
2805 static int
2806 ipf_nat6_out(fin, nat, natadd, nflags)
2807         fr_info_t *fin;
2808         nat_t *nat;
2809         int natadd;
2810         u_32_t nflags;
2811 {
2812         ipf_main_softc_t *softc = fin->fin_main_soft;
2813         ipf_nat_softc_t *softn = softc->ipf_nat_soft;
2814         struct icmp6_hdr *icmp6;
2815         tcphdr_t *tcp;
2816         ipnat_t *np;
2817         int skip;
2818         int i;
2819
2820         tcp = NULL;
2821         icmp6 = NULL;
2822         np = nat->nat_ptr;
2823
2824         if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL))
2825                 (void) ipf_frag_natnew(softc, fin, 0, nat);
2826
2827         /*
2828          * Address assignment is after the checksum modification because
2829          * we are using the address in the packet for determining the
2830          * correct checksum offset (the ICMP error could be coming from
2831          * anyone...)
2832          */
2833         switch (nat->nat_dir)
2834         {
2835         case NAT_OUTBOUND :
2836                 fin->fin_ip6->ip6_src = nat->nat_nsrc6.in6;
2837                 fin->fin_src6 = nat->nat_nsrc6;
2838                 fin->fin_ip6->ip6_dst = nat->nat_ndst6.in6;
2839                 fin->fin_dst6 = nat->nat_ndst6;
2840                 break;
2841
2842         case NAT_INBOUND :
2843                 fin->fin_ip6->ip6_src = nat->nat_odst6.in6;
2844                 fin->fin_src6 = nat->nat_ndst6;
2845                 fin->fin_ip6->ip6_dst = nat->nat_osrc6.in6;
2846                 fin->fin_dst6 = nat->nat_nsrc6;
2847                 break;
2848
2849         case NAT_DIVERTIN :
2850             {
2851                 mb_t *m;
2852
2853                 skip = ipf_nat6_decap(fin, nat);
2854                 if (skip <= 0) {
2855                         NBUMPSIDE6D(1, ns_decap_fail);
2856                         return -1;
2857                 }
2858
2859                 m = fin->fin_m;
2860
2861 #if SOLARIS && defined(_KERNEL)
2862                 m->b_rptr += skip;
2863 #else
2864                 m->m_data += skip;
2865                 m->m_len -= skip;
2866
2867 # ifdef M_PKTHDR
2868                 if (m->m_flags & M_PKTHDR)
2869                         m->m_pkthdr.len -= skip;
2870 # endif
2871 #endif
2872
2873                 MUTEX_ENTER(&nat->nat_lock);
2874                 ipf_nat_update(fin, nat);
2875                 MUTEX_EXIT(&nat->nat_lock);
2876                 fin->fin_flx |= FI_NATED;
2877                 if (np != NULL && np->in_tag.ipt_num[0] != 0)
2878                         fin->fin_nattag = &np->in_tag;
2879                 return 1;
2880                 /* NOTREACHED */
2881             }
2882
2883         case NAT_DIVERTOUT :
2884             {
2885                 udphdr_t *uh;
2886                 ip6_t *ip6;
2887                 mb_t *m;
2888
2889                 m = M_DUP(np->in_divmp);
2890                 if (m == NULL) {
2891                         NBUMPSIDE6D(1, ns_divert_dup);
2892                         return -1;
2893                 }
2894
2895                 ip6 = MTOD(m, ip6_t *);
2896
2897                 ip6->ip6_plen = htons(fin->fin_plen + 8);
2898
2899                 uh = (udphdr_t *)(ip6 + 1);
2900                 uh->uh_ulen = htons(fin->fin_plen);
2901
2902                 PREP_MB_T(fin, m);
2903
2904                 fin->fin_ip6 = ip6;
2905                 fin->fin_plen += sizeof(ip6_t) + 8;     /* UDP + new IPv4 hdr */
2906                 fin->fin_dlen += sizeof(ip6_t) + 8;     /* UDP + old IPv4 hdr */
2907
2908                 nflags &= ~IPN_TCPUDPICMP;
2909
2910                 break;
2911             }
2912
2913         default :
2914                 break;
2915         }
2916
2917         if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
2918                 u_short *csump;
2919
2920                 if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) {
2921                         tcp = fin->fin_dp;
2922
2923                         switch (nat->nat_dir)
2924                         {
2925                         case NAT_OUTBOUND :
2926                                 tcp->th_sport = nat->nat_nsport;
2927                                 fin->fin_data[0] = ntohs(nat->nat_nsport);
2928                                 tcp->th_dport = nat->nat_ndport;
2929                                 fin->fin_data[1] = ntohs(nat->nat_ndport);
2930                                 break;
2931
2932                         case NAT_INBOUND :
2933                                 tcp->th_sport = nat->nat_odport;
2934                                 fin->fin_data[0] = ntohs(nat->nat_odport);
2935                                 tcp->th_dport = nat->nat_osport;
2936                                 fin->fin_data[1] = ntohs(nat->nat_osport);
2937                                 break;
2938                         }
2939                 }
2940
2941                 if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) {
2942                         icmp6 = fin->fin_dp;
2943                         icmp6->icmp6_id = nat->nat_nicmpid;
2944                 }
2945
2946                 csump = ipf_nat_proto(fin, nat, nflags);
2947
2948                 /*
2949                  * The above comments do not hold for layer 4 (or higher)
2950                  * checksums...
2951                  */
2952                 if (csump != NULL) {
2953                         if (nat->nat_dir == NAT_OUTBOUND)
2954                                 ipf_fix_outcksum(fin->fin_cksum, csump,
2955                                                  nat->nat_sumd[0],
2956                                                  nat->nat_sumd[1] +
2957                                                  fin->fin_dlen);
2958                         else
2959                                 ipf_fix_incksum(fin->fin_cksum, csump,
2960                                                 nat->nat_sumd[0],
2961                                                 nat->nat_sumd[1] +
2962                                                 fin->fin_dlen);
2963                 }
2964         }
2965
2966         ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync);
2967         /* ------------------------------------------------------------- */
2968         /* A few quick notes:                                            */
2969         /*      Following are test conditions prior to calling the       */
2970         /*      ipf_proxy_check routine.                                 */
2971         /*                                                               */
2972         /*      A NULL tcp indicates a non TCP/UDP packet.  When dealing */
2973         /*      with a redirect rule, we attempt to match the packet's   */
2974         /*      source port against in_dport, otherwise we'd compare the */
2975         /*      packet's destination.                                    */
2976         /* ------------------------------------------------------------- */
2977         if ((np != NULL) && (np->in_apr != NULL)) {
2978                 i = ipf_proxy_check(fin, nat);
2979                 if (i == 0) {
2980                         i = 1;
2981                 } else if (i == -1) {
2982                         NBUMPSIDE6D(1, ns_ipf_proxy_fail);
2983                 }
2984         } else {
2985                 i = 1;
2986         }
2987         fin->fin_flx |= FI_NATED;
2988         return i;
2989 }
2990
2991
2992 /* ------------------------------------------------------------------------ */
2993 /* Function:    ipf_nat6_checkin                                            */
2994 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
2995 /*                     0 == no packet translation occurred,                 */
2996 /*                     1 == packet was successfully translated.             */
2997 /* Parameters:  fin(I)   - pointer to packet information                    */
2998 /*              passp(I) - pointer to filtering result flags                */
2999 /*                                                                          */
3000 /* Check to see if an incoming packet should be changed.  ICMP packets are  */
3001 /* first checked to see if they match an existing entry (if an error),      */
3002 /* otherwise a search of the current NAT table is made.  If neither results */
3003 /* in a match then a search for a matching NAT rule is made.  Create a new  */
3004 /* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
3005 /* packet header(s) as required.                                            */
3006 /* ------------------------------------------------------------------------ */
3007 int
3008 ipf_nat6_checkin(fin, passp)
3009         fr_info_t *fin;
3010         u_32_t *passp;
3011 {
3012         ipf_main_softc_t *softc = fin->fin_main_soft;
3013         ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3014         struct icmp6_hdr *icmp6;
3015         u_int nflags, natadd;
3016         int rval, natfailed;
3017         struct ifnet *ifp;
3018         i6addr_t ipa, iph;
3019         tcphdr_t *tcp;
3020         u_short dport;
3021         ipnat_t *np;
3022         nat_t *nat;
3023
3024         if (softn->ipf_nat_stats.ns_rules == 0 || softn->ipf_nat_lock != 0)
3025                 return 0;
3026
3027         tcp = NULL;
3028         icmp6 = NULL;
3029         dport = 0;
3030         natadd = 1;
3031         nflags = 0;
3032         natfailed = 0;
3033         ifp = fin->fin_ifp;
3034
3035         if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
3036                 switch (fin->fin_p)
3037                 {
3038                 case IPPROTO_TCP :
3039                         nflags = IPN_TCP;
3040                         break;
3041                 case IPPROTO_UDP :
3042                         nflags = IPN_UDP;
3043                         break;
3044                 case IPPROTO_ICMPV6 :
3045                         icmp6 = fin->fin_dp;
3046
3047                         /*
3048                          * Apart from ECHO request and reply, all other
3049                          * informational messages should not be translated
3050                          * so as to keep IPv6 working.
3051                          */
3052                         if (icmp6->icmp6_type > ICMP6_ECHO_REPLY)
3053                                 return 0;
3054
3055                         /*
3056                          * This is an incoming packet, so the destination is
3057                          * the icmp6_id and the source port equals 0
3058                          */
3059                         if ((fin->fin_flx & FI_ICMPQUERY) != 0) {
3060                                 nflags = IPN_ICMPQUERY;
3061                                 dport = icmp6->icmp6_id;
3062                         } break;
3063                 default :
3064                         break;
3065                 }
3066
3067                 if ((nflags & IPN_TCPUDP)) {
3068                         tcp = fin->fin_dp;
3069                         dport = fin->fin_data[1];
3070                 }
3071         }
3072
3073         ipa = fin->fin_dst6;
3074
3075         READ_ENTER(&softc->ipf_nat);
3076
3077         if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) &&
3078             (nat = ipf_nat6_icmperror(fin, &nflags, NAT_INBOUND)))
3079                 /*EMPTY*/;
3080         else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin)))
3081                 natadd = 0;
3082         else if ((nat = ipf_nat6_inlookup(fin, nflags|NAT_SEARCH,
3083                                           (u_int)fin->fin_p,
3084                                           &fin->fin_src6.in6, &ipa.in6))) {
3085                 nflags = nat->nat_flags;
3086         } else if (fin->fin_off == 0) {
3087                 u_32_t hv, rmsk = 0;
3088                 i6addr_t *msk;
3089
3090                 /*
3091                  * If there is no current entry in the nat table for this IP#,
3092                  * create one for it (if there is a matching rule).
3093                  */
3094 maskloop:
3095                 msk = &softn->ipf_nat6_rdr_active_masks[rmsk];
3096                 IP6_AND(&ipa, msk, &iph);
3097                 hv = NAT_HASH_FN6(&iph, 0, softn->ipf_nat_rdrrules_sz);
3098                 for (np = softn->ipf_nat_rdr_rules[hv]; np; np = np->in_rnext) {
3099                         if (np->in_ifps[0] && (np->in_ifps[0] != ifp))
3100                                 continue;
3101                         if (np->in_v[0] != 6)
3102                                 continue;
3103                         if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p))
3104                                 continue;
3105                         if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
3106                                 continue;
3107                         if (np->in_flags & IPN_FILTER) {
3108                                 switch (ipf_nat6_match(fin, np))
3109                                 {
3110                                 case 0 :
3111                                         continue;
3112                                 case -1 :
3113                                         rval = -1;
3114                                         goto inmatchfail;
3115                                 case 1 :
3116                                 default :
3117                                         break;
3118                                 }
3119                         } else {
3120                                 if (!IP6_MASKEQ(&ipa, &np->in_odstmsk6,
3121                                                 &np->in_odstip6)) {
3122                                         continue;
3123                                 }
3124                                 if (np->in_odport &&
3125                                     ((np->in_dtop < dport) ||
3126                                      (dport < np->in_odport)))
3127                                         continue;
3128                         }
3129
3130 #ifdef IPF_V6_PROXIES
3131                         if (np->in_plabel != -1) {
3132                                 if (!appr_ok(fin, tcp, np)) {
3133                                         continue;
3134                                 }
3135                         }
3136 #endif
3137
3138                         if (np->in_flags & IPN_NO) {
3139                                 np->in_hits++;
3140                                 break;
3141                         }
3142
3143                         MUTEX_ENTER(&softn->ipf_nat_new);
3144                         nat = ipf_nat6_add(fin, np, NULL, nflags, NAT_INBOUND);
3145                         MUTEX_EXIT(&softn->ipf_nat_new);
3146                         if (nat != NULL) {
3147                                 np->in_hits++;
3148                                 break;
3149                         }
3150                         natfailed = -1;
3151                 }
3152
3153                 if ((np == NULL) && (rmsk < softn->ipf_nat6_rdr_max)) {
3154                         rmsk++;
3155                         goto maskloop;
3156                 }
3157         }
3158         if (nat != NULL) {
3159                 rval = ipf_nat6_in(fin, nat, natadd, nflags);
3160                 if (rval == 1) {
3161                         MUTEX_ENTER(&nat->nat_lock);
3162                         ipf_nat_update(fin, nat);
3163                         nat->nat_bytes[0] += fin->fin_plen;
3164                         nat->nat_pkts[0]++;
3165                         MUTEX_EXIT(&nat->nat_lock);
3166                 }
3167         } else
3168                 rval = natfailed;
3169 inmatchfail:
3170         RWLOCK_EXIT(&softc->ipf_nat);
3171
3172         switch (rval)
3173         {
3174         case -1 :
3175                 if (passp != NULL) {
3176                         NBUMPSIDE6D(0, ns_drop);
3177                         *passp = FR_BLOCK;
3178                         fin->fin_reason = FRB_NATV6;
3179                 }
3180                 fin->fin_flx |= FI_BADNAT;
3181                 NBUMPSIDE6D(0, ns_badnat);
3182                 break;
3183         case 0 :
3184                 NBUMPSIDE6D(0, ns_ignored);
3185                 break;
3186         case 1 :
3187                 NBUMPSIDE6D(0, ns_translated);
3188                 break;
3189         }
3190         return rval;
3191 }
3192
3193
3194 /* ------------------------------------------------------------------------ */
3195 /* Function:    ipf_nat6_in                                                 */
3196 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
3197 /*                     1 == packet was successfully translated.             */
3198 /* Parameters:  fin(I)    - pointer to packet information                   */
3199 /*              nat(I)    - pointer to NAT structure                        */
3200 /*              natadd(I) - flag indicating if it is safe to add frag cache */
3201 /*              nflags(I) - NAT flags set for this packet                   */
3202 /* Locks Held:   (READ)                                              */
3203 /*                                                                          */
3204 /* Translate a packet coming "in" on an interface.                          */
3205 /* ------------------------------------------------------------------------ */
3206 static int
3207 ipf_nat6_in(fin, nat, natadd, nflags)
3208         fr_info_t *fin;
3209         nat_t *nat;
3210         int natadd;
3211         u_32_t nflags;
3212 {
3213         ipf_main_softc_t *softc = fin->fin_main_soft;
3214         ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3215         struct icmp6_hdr *icmp6;
3216         u_short *csump;
3217         tcphdr_t *tcp;
3218         ipnat_t *np;
3219         int skip;
3220         int i;
3221
3222         tcp = NULL;
3223         csump = NULL;
3224         np = nat->nat_ptr;
3225         fin->fin_fr = nat->nat_fr;
3226
3227         if (np != NULL) {
3228                 if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
3229                         (void) ipf_frag_natnew(softc, fin, 0, nat);
3230
3231         /* ------------------------------------------------------------- */
3232         /* A few quick notes:                                            */
3233         /*      Following are test conditions prior to calling the       */
3234         /*      ipf_proxy_check routine.                                 */
3235         /*                                                               */
3236         /*      A NULL tcp indicates a non TCP/UDP packet.  When dealing */
3237         /*      with a map rule, we attempt to match the packet's        */
3238         /*      source port against in_dport, otherwise we'd compare the */
3239         /*      packet's destination.                                    */
3240         /* ------------------------------------------------------------- */
3241                 if (np->in_apr != NULL) {
3242                         i = ipf_proxy_check(fin, nat);
3243                         if (i == -1) {
3244                                 NBUMPSIDE6D(0, ns_ipf_proxy_fail);
3245                                 return -1;
3246                         }
3247                 }
3248         }
3249
3250         ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync);
3251
3252         /*
3253          * Fix up checksums, not by recalculating them, but
3254          * simply computing adjustments.
3255          * Why only do this for some platforms on inbound packets ?
3256          * Because for those that it is done, IP processing is yet to happen
3257          * and so the IPv4 header checksum has not yet been evaluated.
3258          * Perhaps it should always be done for the benefit of things like
3259          * fast forwarding (so that it doesn't need to be recomputed) but with
3260          * header checksum offloading, perhaps it is a moot point.
3261          */
3262
3263         switch (nat->nat_dir)
3264         {
3265         case NAT_INBOUND :
3266                 if ((fin->fin_flx & FI_ICMPERR) == 0) {
3267                         fin->fin_ip6->ip6_src = nat->nat_nsrc6.in6;
3268                         fin->fin_src6 = nat->nat_nsrc6;
3269                 }
3270                 fin->fin_ip6->ip6_dst = nat->nat_ndst6.in6;
3271                 fin->fin_dst6 = nat->nat_ndst6;
3272                 break;
3273
3274         case NAT_OUTBOUND :
3275                 if ((fin->fin_flx & FI_ICMPERR) == 0) {
3276                         fin->fin_ip6->ip6_src = nat->nat_odst6.in6;
3277                         fin->fin_src6 = nat->nat_odst6;
3278                 }
3279                 fin->fin_ip6->ip6_dst = nat->nat_osrc6.in6;
3280                 fin->fin_dst6 = nat->nat_osrc6;
3281                 break;
3282
3283         case NAT_DIVERTIN :
3284             {
3285                 udphdr_t *uh;
3286                 ip6_t *ip6;
3287                 mb_t *m;
3288
3289                 m = M_DUP(np->in_divmp);
3290                 if (m == NULL) {
3291                         NBUMPSIDE6D(0, ns_divert_dup);
3292                         return -1;
3293                 }
3294
3295                 ip6 = MTOD(m, ip6_t *);
3296                 ip6->ip6_plen = htons(fin->fin_plen + sizeof(udphdr_t));
3297
3298                 uh = (udphdr_t *)(ip6 + 1);
3299                 uh->uh_ulen = ntohs(fin->fin_plen);
3300
3301                 PREP_MB_T(fin, m);
3302
3303                 fin->fin_ip6 = ip6;
3304                 fin->fin_plen += sizeof(ip6_t) + 8;     /* UDP + new IPv6 hdr */
3305                 fin->fin_dlen += sizeof(ip6_t) + 8;     /* UDP + old IPv6 hdr */
3306
3307                 nflags &= ~IPN_TCPUDPICMP;
3308
3309                 break;
3310             }
3311
3312         case NAT_DIVERTOUT :
3313             {
3314                 mb_t *m;
3315
3316                 skip = ipf_nat6_decap(fin, nat);
3317                 if (skip <= 0) {
3318                         NBUMPSIDE6D(0, ns_decap_fail);
3319                         return -1;
3320                 }
3321
3322                 m = fin->fin_m;
3323
3324 #if SOLARIS && defined(_KERNEL)
3325                 m->b_rptr += skip;
3326 #else
3327                 m->m_data += skip;
3328                 m->m_len -= skip;
3329
3330 # ifdef M_PKTHDR
3331                 if (m->m_flags & M_PKTHDR)
3332                         m->m_pkthdr.len -= skip;
3333 # endif
3334 #endif
3335
3336                 ipf_nat_update(fin, nat);
3337                 fin->fin_flx |= FI_NATED;
3338                 if (np != NULL && np->in_tag.ipt_num[0] != 0)
3339                         fin->fin_nattag = &np->in_tag;
3340                 return 1;
3341                 /* NOTREACHED */
3342             }
3343         }
3344         if (nflags & IPN_TCPUDP)
3345                 tcp = fin->fin_dp;
3346
3347         if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
3348                 if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) {
3349                         switch (nat->nat_dir)
3350                         {
3351                         case NAT_INBOUND :
3352                                 tcp->th_sport = nat->nat_nsport;
3353                                 fin->fin_data[0] = ntohs(nat->nat_nsport);
3354                                 tcp->th_dport = nat->nat_ndport;
3355                                 fin->fin_data[1] = ntohs(nat->nat_ndport);
3356                                 break;
3357
3358                         case NAT_OUTBOUND :
3359                                 tcp->th_sport = nat->nat_odport;
3360                                 fin->fin_data[0] = ntohs(nat->nat_odport);
3361                                 tcp->th_dport = nat->nat_osport;
3362                                 fin->fin_data[1] = ntohs(nat->nat_osport);
3363                                 break;
3364                         }
3365                 }
3366
3367
3368                 if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) {
3369                         icmp6 = fin->fin_dp;
3370
3371                         icmp6->icmp6_id = nat->nat_nicmpid;
3372                 }
3373
3374                 csump = ipf_nat_proto(fin, nat, nflags);
3375         }
3376
3377         /*
3378          * The above comments do not hold for layer 4 (or higher) checksums...
3379          */
3380         if (csump != NULL) {
3381                 if (nat->nat_dir == NAT_OUTBOUND)
3382                         ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0);
3383                 else
3384                         ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0);
3385         }
3386         fin->fin_flx |= FI_NATED;
3387         if (np != NULL && np->in_tag.ipt_num[0] != 0)
3388                 fin->fin_nattag = &np->in_tag;
3389         return 1;
3390 }
3391
3392
3393 /* ------------------------------------------------------------------------ */
3394 /* Function:    ipf_nat6_newrewrite                                         */
3395 /* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
3396 /*                    allow rule to be moved if IPN_ROUNDR is set.          */
3397 /* Parameters:  fin(I) - pointer to packet information                      */
3398 /*              nat(I) - pointer to NAT entry                               */
3399 /*              ni(I)  - pointer to structure with misc. information needed */
3400 /*                       to create new NAT entry.                           */
3401 /* Write Lock:  ipf_nat                                                     */
3402 /*                                                                          */
3403 /* This function is responsible for setting up an active NAT session where  */
3404 /* we are changing both the source and destination parameters at the same   */
3405 /* time.  The loop in here works differently to elsewhere - each iteration  */
3406 /* is responsible for changing a single parameter that can be incremented.  */
3407 /* So one pass may increase the source IP#, next source port, next dest. IP#*/
3408 /* and the last destination port for a total of 4 iterations to try each.   */
3409 /* This is done to try and exhaustively use the translation space available.*/
3410 /* ------------------------------------------------------------------------ */
3411 int
3412 ipf_nat6_newrewrite(fin, nat, nai)
3413         fr_info_t *fin;
3414         nat_t *nat;
3415         natinfo_t *nai;
3416 {
3417         int src_search = 1;
3418         int dst_search = 1;
3419         fr_info_t frnat;
3420         u_32_t flags;
3421         u_short swap;
3422         ipnat_t *np;
3423         nat_t *natl;
3424         int l = 0;
3425         int changed;
3426
3427         natl = NULL;
3428         changed = -1;
3429         np = nai->nai_np;
3430         flags = nat->nat_flags;
3431         bcopy((char *)fin, (char *)&frnat, sizeof(*fin));
3432
3433         nat->nat_hm = NULL;
3434
3435         do {
3436                 changed = -1;
3437                 /* TRACE (l, src_search, dst_search, np) */
3438
3439                 if ((src_search == 0) && (np->in_spnext == 0) &&
3440                     (dst_search == 0) && (np->in_dpnext == 0)) {
3441                         if (l > 0)
3442                                 return -1;
3443                 }
3444
3445                 /*
3446                  * Find a new source address
3447                  */
3448                 if (ipf_nat6_nextaddr(fin, &np->in_nsrc, &frnat.fin_src6,
3449                                  &frnat.fin_src6) == -1) {
3450                         return -1;
3451                 }
3452
3453                 if (IP6_ISZERO(&np->in_nsrcip6) &&
3454                     IP6_ISONES(&np->in_nsrcmsk6)) {
3455                         src_search = 0;
3456                         if (np->in_stepnext == 0)
3457                                 np->in_stepnext = 1;
3458
3459                 } else if (IP6_ISZERO(&np->in_nsrcip6) &&
3460                            IP6_ISZERO(&np->in_nsrcmsk6)) {
3461                         src_search = 0;
3462                         if (np->in_stepnext == 0)
3463                                 np->in_stepnext = 1;
3464
3465                 } else if (IP6_ISONES(&np->in_nsrcmsk)) {
3466                         src_search = 0;
3467                         if (np->in_stepnext == 0)
3468                                 np->in_stepnext = 1;
3469
3470                 } else if (!IP6_ISONES(&np->in_nsrcmsk6)) {
3471                         if (np->in_stepnext == 0 && changed == -1) {
3472                                 IP6_INC(&np->in_snip);
3473                                 np->in_stepnext++;
3474                                 changed = 0;
3475                         }
3476                 }
3477
3478                 if ((flags & IPN_TCPUDPICMP) != 0) {
3479                         if (np->in_spnext != 0)
3480                                 frnat.fin_data[0] = np->in_spnext;
3481
3482                         /*
3483                          * Standard port translation.  Select next port.
3484                          */
3485                         if ((flags & IPN_FIXEDSPORT) != 0) {
3486                                 np->in_stepnext = 2;
3487                         } else if ((np->in_stepnext == 1) &&
3488                                    (changed == -1) && (natl != NULL)) {
3489                                 np->in_spnext++;
3490                                 np->in_stepnext++;
3491                                 changed = 1;
3492                                 if (np->in_spnext > np->in_spmax)
3493                                         np->in_spnext = np->in_spmin;
3494                         }
3495                 } else {
3496                         np->in_stepnext = 2;
3497                 }
3498                 np->in_stepnext &= 0x3;
3499
3500                 /*
3501                  * Find a new destination address
3502                  */
3503                 /* TRACE (fin, np, l, frnat) */
3504
3505                 if (ipf_nat6_nextaddr(fin, &np->in_ndst, &frnat.fin_dst6,
3506                                       &frnat.fin_dst6) == -1)
3507                         return -1;
3508
3509                 if (IP6_ISZERO(&np->in_ndstip6) &&
3510                     IP6_ISONES(&np->in_ndstmsk6)) {
3511                         dst_search = 0;
3512                         if (np->in_stepnext == 2)
3513                                 np->in_stepnext = 3;
3514
3515                 } else if (IP6_ISZERO(&np->in_ndstip6) &&
3516                            IP6_ISZERO(&np->in_ndstmsk6)) {
3517                         dst_search = 0;
3518                         if (np->in_stepnext == 2)
3519                                 np->in_stepnext = 3;
3520
3521                 } else if (IP6_ISONES(&np->in_ndstmsk6)) {
3522                         dst_search = 0;
3523                         if (np->in_stepnext == 2)
3524                                 np->in_stepnext = 3;
3525
3526                 } else if (!IP6_ISONES(&np->in_ndstmsk6)) {
3527                         if ((np->in_stepnext == 2) && (changed == -1) &&
3528                             (natl != NULL)) {
3529                                 changed = 2;
3530                                 np->in_stepnext++;
3531                                 IP6_INC(&np->in_dnip6);
3532                         }
3533                 }
3534
3535                 if ((flags & IPN_TCPUDPICMP) != 0) {
3536                         if (np->in_dpnext != 0)
3537                                 frnat.fin_data[1] = np->in_dpnext;
3538
3539                         /*
3540                          * Standard port translation.  Select next port.
3541                          */
3542                         if ((flags & IPN_FIXEDDPORT) != 0) {
3543                                 np->in_stepnext = 0;
3544                         } else if (np->in_stepnext == 3 && changed == -1) {
3545                                 np->in_dpnext++;
3546                                 np->in_stepnext++;
3547                                 changed = 3;
3548                                 if (np->in_dpnext > np->in_dpmax)
3549                                         np->in_dpnext = np->in_dpmin;
3550                         }
3551                 } else {
3552                         if (np->in_stepnext == 3)
3553                                 np->in_stepnext = 0;
3554                 }
3555
3556                 /* TRACE (frnat) */
3557
3558                 /*
3559                  * Here we do a lookup of the connection as seen from
3560                  * the outside.  If an IP# pair already exists, try
3561                  * again.  So if you have A->B becomes C->B, you can
3562                  * also have D->E become C->E but not D->B causing
3563                  * another C->B.  Also take protocol and ports into
3564                  * account when determining whether a pre-existing
3565                  * NAT setup will cause an external conflict where
3566                  * this is appropriate.
3567                  *
3568                  * fin_data[] is swapped around because we are doing a
3569                  * lookup of the packet is if it were moving in the opposite
3570                  * direction of the one we are working with now.
3571                  */
3572                 if (flags & IPN_TCPUDP) {
3573                         swap = frnat.fin_data[0];
3574                         frnat.fin_data[0] = frnat.fin_data[1];
3575                         frnat.fin_data[1] = swap;
3576                 }
3577                 if (fin->fin_out == 1) {
3578                         natl = ipf_nat6_inlookup(&frnat,
3579                                             flags & ~(SI_WILDP|NAT_SEARCH),
3580                                             (u_int)frnat.fin_p,
3581                                             &frnat.fin_dst6.in6,
3582                                             &frnat.fin_src6.in6);
3583
3584                 } else {
3585                         natl = ipf_nat6_outlookup(&frnat,
3586                                              flags & ~(SI_WILDP|NAT_SEARCH),
3587                                              (u_int)frnat.fin_p,
3588                                              &frnat.fin_dst6.in6,
3589                                              &frnat.fin_src6.in6);
3590                 }
3591                 if (flags & IPN_TCPUDP) {
3592                         swap = frnat.fin_data[0];
3593                         frnat.fin_data[0] = frnat.fin_data[1];
3594                         frnat.fin_data[1] = swap;
3595                 }
3596
3597                 /* TRACE natl, in_stepnext, l */
3598
3599                 if ((natl != NULL) && (l > 8))  /* XXX 8 is arbitrary */
3600                         return -1;
3601
3602                 np->in_stepnext &= 0x3;
3603
3604                 l++;
3605                 changed = -1;
3606         } while (natl != NULL);
3607         nat->nat_osrc6 = fin->fin_src6;
3608         nat->nat_odst6 = fin->fin_dst6;
3609         nat->nat_nsrc6 = frnat.fin_src6;
3610         nat->nat_ndst6 = frnat.fin_dst6;
3611
3612         if ((flags & IPN_TCPUDP) != 0) {
3613                 nat->nat_osport = htons(fin->fin_data[0]);
3614                 nat->nat_odport = htons(fin->fin_data[1]);
3615                 nat->nat_nsport = htons(frnat.fin_data[0]);
3616                 nat->nat_ndport = htons(frnat.fin_data[1]);
3617         } else if ((flags & IPN_ICMPQUERY) != 0) {
3618                 nat->nat_oicmpid = fin->fin_data[1];
3619                 nat->nat_nicmpid = frnat.fin_data[1];
3620         }
3621
3622         return 0;
3623 }
3624
3625
3626 /* ------------------------------------------------------------------------ */
3627 /* Function:    ipf_nat6_newdivert                                          */
3628 /* Returns:     int - -1 == error, 0 == success                             */
3629 /* Parameters:  fin(I) - pointer to packet information                      */
3630 /*              nat(I) - pointer to NAT entry                               */
3631 /*              ni(I)  - pointer to structure with misc. information needed */
3632 /*                       to create new NAT entry.                           */
3633 /* Write Lock:  ipf_nat                                                     */
3634 /*                                                                          */
3635 /* Create a new NAT divert session as defined by the NAT rule.  This is     */
3636 /* somewhat different to other NAT session creation routines because we     */
3637 /* do not iterate through either port numbers or IP addresses, searching    */
3638 /* for a unique mapping, however, a complimentary duplicate check is made.  */
3639 /* ------------------------------------------------------------------------ */
3640 int
3641 ipf_nat6_newdivert(fin, nat, nai)
3642         fr_info_t *fin;
3643         nat_t *nat;
3644         natinfo_t *nai;
3645 {
3646         ipf_main_softc_t *softc = fin->fin_main_soft;
3647         ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3648         fr_info_t frnat;
3649         ipnat_t *np;
3650         nat_t *natl;
3651         int p;
3652
3653         np = nai->nai_np;
3654         bcopy((char *)fin, (char *)&frnat, sizeof(*fin));
3655
3656         nat->nat_pr[0] = 0;
3657         nat->nat_osrc6 = fin->fin_src6;
3658         nat->nat_odst6 = fin->fin_dst6;
3659         nat->nat_osport = htons(fin->fin_data[0]);
3660         nat->nat_odport = htons(fin->fin_data[1]);
3661         frnat.fin_src6 = np->in_snip6;
3662         frnat.fin_dst6 = np->in_dnip6;
3663
3664         if (np->in_redir & NAT_DIVERTUDP) {
3665                 frnat.fin_data[0] = np->in_spnext;
3666                 frnat.fin_data[1] = np->in_dpnext;
3667                 frnat.fin_flx |= FI_TCPUDP;
3668                 p = IPPROTO_UDP;
3669         } else {
3670                 frnat.fin_flx &= ~FI_TCPUDP;
3671                 p = IPPROTO_IPIP;
3672         }
3673
3674         if (fin->fin_out == 1) {
3675                 natl = ipf_nat6_inlookup(&frnat, 0, p, &frnat.fin_dst6.in6,
3676                                          &frnat.fin_src6.in6);
3677
3678         } else {
3679                 natl = ipf_nat6_outlookup(&frnat, 0, p, &frnat.fin_dst6.in6,
3680                                           &frnat.fin_src6.in6);
3681         }
3682
3683         if (natl != NULL) {
3684                 NBUMPSIDE6D(fin->fin_out, ns_divert_exist);
3685                 return -1;
3686         }
3687
3688         nat->nat_nsrc6 = frnat.fin_src6;
3689         nat->nat_ndst6 = frnat.fin_dst6;
3690         if (np->in_redir & NAT_DIVERTUDP) {
3691                 nat->nat_nsport = htons(frnat.fin_data[0]);
3692                 nat->nat_ndport = htons(frnat.fin_data[1]);
3693         }
3694         nat->nat_pr[fin->fin_out] = fin->fin_p;
3695         nat->nat_pr[1 - fin->fin_out] = p;
3696
3697         if (np->in_redir & NAT_REDIRECT)
3698                 nat->nat_dir = NAT_DIVERTIN;
3699         else
3700                 nat->nat_dir = NAT_DIVERTOUT;
3701
3702         return 0;
3703 }
3704
3705
3706 /* ------------------------------------------------------------------------ */
3707 /* Function:    nat6_builddivertmp                                          */
3708 /* Returns:     int - -1 == error, 0 == success                             */
3709 /* Parameters:  np(I) - pointer to a NAT rule                               */
3710 /*                                                                          */
3711 /* For divert rules, a skeleton packet representing what will be prepended  */
3712 /* to the real packet is created.  Even though we don't have the full       */
3713 /* packet here, a checksum is calculated that we update later when we       */
3714 /* fill in the final details.  At present a 0 checksum for UDP is being set */
3715 /* here because it is expected that divert will be used for localhost.      */
3716 /* ------------------------------------------------------------------------ */
3717 static int
3718 ipf_nat6_builddivertmp(softn, np)
3719         ipf_nat_softc_t *softn;
3720         ipnat_t *np;
3721 {
3722         udphdr_t *uh;
3723         size_t len;
3724         ip6_t *ip6;
3725
3726         if ((np->in_redir & NAT_DIVERTUDP) != 0)
3727                 len = sizeof(ip6_t) + sizeof(udphdr_t);
3728         else
3729                 len = sizeof(ip6_t);
3730
3731         ALLOC_MB_T(np->in_divmp, len);
3732         if (np->in_divmp == NULL) {
3733                 ATOMIC_INCL(softn->ipf_nat_stats.ns_divert_build);
3734                 return -1;
3735         }
3736
3737         /*
3738          * First, the header to get the packet diverted to the new destination
3739          */
3740         ip6 = MTOD(np->in_divmp, ip6_t *);
3741         ip6->ip6_vfc = 0x60;
3742         if ((np->in_redir & NAT_DIVERTUDP) != 0)
3743                 ip6->ip6_nxt = IPPROTO_UDP;
3744         else
3745                 ip6->ip6_nxt = IPPROTO_IPIP;
3746         ip6->ip6_hlim = 255;
3747         ip6->ip6_plen = 0;
3748         ip6->ip6_src = np->in_snip6.in6;
3749         ip6->ip6_dst = np->in_dnip6.in6;
3750
3751         if (np->in_redir & NAT_DIVERTUDP) {
3752                 uh = (udphdr_t *)((u_char *)ip6 + sizeof(*ip6));
3753                 uh->uh_sum = 0;
3754                 uh->uh_ulen = 8;
3755                 uh->uh_sport = htons(np->in_spnext);
3756                 uh->uh_dport = htons(np->in_dpnext);
3757         }
3758
3759         return 0;
3760 }
3761
3762
3763 #define MINDECAP        (sizeof(ip6_t) + sizeof(udphdr_t) + sizeof(ip6_t))
3764
3765 /* ------------------------------------------------------------------------ */
3766 /* Function:    nat6_decap                                                  */
3767 /* Returns:     int - -1 == error, 0 == success                             */
3768 /* Parameters:  fin(I) - pointer to packet information                      */
3769 /*              nat(I) - pointer to current NAT session                     */
3770 /*                                                                          */
3771 /* This function is responsible for undoing a packet's encapsulation in the */
3772 /* reverse of an encap/divert rule.  After removing the outer encapsulation */
3773 /* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/
3774 /* match the "new" packet as it may still be used by IPFilter elsewhere.    */
3775 /* We use "dir" here as the basis for some of the expectations about the    */
3776 /* outer header.  If we return an error, the goal is to leave the original  */
3777 /* packet information undisturbed - this falls short at the end where we'd  */
3778 /* need to back a backup copy of "fin" - expensive.                         */
3779 /* ------------------------------------------------------------------------ */
3780 static int
3781 ipf_nat6_decap(fin, nat)
3782         fr_info_t *fin;
3783         nat_t *nat;
3784 {
3785         ipf_main_softc_t *softc = fin->fin_main_soft;
3786         ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3787         char *hdr;
3788         int skip;
3789         mb_t *m;
3790
3791         if ((fin->fin_flx & FI_ICMPERR) != 0) {
3792                 return 0;
3793         }
3794
3795         m = fin->fin_m;
3796         skip = fin->fin_hlen;
3797
3798         switch (nat->nat_dir)
3799         {
3800         case NAT_DIVERTIN :
3801         case NAT_DIVERTOUT :
3802                 if (fin->fin_plen < MINDECAP)
3803                         return -1;
3804                 skip += sizeof(udphdr_t);
3805                 break;
3806
3807         case NAT_ENCAPIN :
3808         case NAT_ENCAPOUT :
3809                 if (fin->fin_plen < (skip + sizeof(ip6_t)))
3810                         return -1;
3811                 break;
3812         default :
3813                 return -1;
3814                 /* NOTREACHED */
3815         }
3816
3817         /*
3818          * The aim here is to keep the original packet details in "fin" for
3819          * as long as possible so that returning with an error is for the
3820          * original packet and there is little undoing work to do.
3821          */
3822         if (M_LEN(m) < skip + sizeof(ip6_t)) {
3823                 if (ipf_pr_pullup(fin, skip + sizeof(ip6_t)) == -1)
3824                         return -1;
3825         }
3826
3827         hdr = MTOD(fin->fin_m, char *);
3828         fin->fin_ip6 = (ip6_t *)(hdr + skip);
3829
3830         if (ipf_pr_pullup(fin, skip + sizeof(ip6_t)) == -1) {
3831                 NBUMPSIDE6D(fin->fin_out, ns_decap_pullup);
3832                 return -1;
3833         }
3834
3835         fin->fin_hlen = sizeof(ip6_t);
3836         fin->fin_dlen -= skip;
3837         fin->fin_plen -= skip;
3838         fin->fin_ipoff += skip;
3839
3840         if (ipf_makefrip(sizeof(ip6_t), (ip_t *)hdr, fin) == -1) {
3841                 NBUMPSIDE6D(fin->fin_out, ns_decap_bad);
3842                 return -1;
3843         }
3844
3845         return skip;
3846 }
3847
3848
3849 /* ------------------------------------------------------------------------ */
3850 /* Function:    nat6_nextaddr                                               */
3851 /* Returns:     int - -1 == bad input (no new address),                     */
3852 /*                     0 == success and dst has new address                 */
3853 /* Parameters:  fin(I) - pointer to packet information                      */
3854 /*              na(I)  - how to generate new address                        */
3855 /*              old(I) - original address being replaced                    */
3856 /*              dst(O) - where to put the new address                       */
3857 /* Write Lock:  ipf_nat                                                     */
3858 /*                                                                          */
3859 /* This function uses the contents of the "na" structure, in combination    */
3860 /* with "old" to produce a new address to store in "dst".  Not all of the   */
3861 /* possible uses of "na" will result in a new address.                      */
3862 /* ------------------------------------------------------------------------ */
3863 static int
3864 ipf_nat6_nextaddr(fin, na, old, dst)
3865         fr_info_t *fin;
3866         nat_addr_t *na;
3867         i6addr_t *old, *dst;
3868 {
3869         ipf_main_softc_t *softc = fin->fin_main_soft;
3870         ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3871         i6addr_t newip, new;
3872         u_32_t amin, amax;
3873         int error;
3874
3875         new.i6[0] = 0;
3876         new.i6[1] = 0;
3877         new.i6[2] = 0;
3878         new.i6[3] = 0;
3879         amin = na->na_addr[0].in4.s_addr;
3880
3881         switch (na->na_atype)
3882         {
3883         case FRI_RANGE :
3884                 amax = na->na_addr[1].in4.s_addr;
3885                 break;
3886
3887         case FRI_NETMASKED :
3888         case FRI_DYNAMIC :
3889         case FRI_NORMAL :
3890                 /*
3891                  * Compute the maximum address by adding the inverse of the
3892                  * netmask to the minimum address.
3893                  */
3894                 amax = ~na->na_addr[1].in4.s_addr;
3895                 amax |= amin;
3896                 break;
3897
3898         case FRI_LOOKUP :
3899                 break;
3900
3901         case FRI_BROADCAST :
3902         case FRI_PEERADDR :
3903         case FRI_NETWORK :
3904         default :
3905                 return -1;
3906         }
3907
3908         error = -1;
3909         switch (na->na_function)
3910         {
3911         case IPLT_DSTLIST :
3912                 error = ipf_dstlist_select_node(fin, na->na_ptr, dst->i6,
3913                                                 NULL);
3914                 break;
3915
3916         case IPLT_NONE :
3917                 /*
3918                  * 0/0 as the new address means leave it alone.
3919                  */
3920                 if (na->na_addr[0].in4.s_addr == 0 &&
3921                     na->na_addr[1].in4.s_addr == 0) {
3922                         new = *old;
3923
3924                 /*
3925                  * 0/32 means get the interface's address
3926                  */
3927                 } else if (IP6_ISZERO(&na->na_addr[0].in6) &&
3928                            IP6_ISONES(&na->na_addr[1].in6)) {
3929                         if (ipf_ifpaddr(softc, 6, na->na_atype,
3930                                        fin->fin_ifp, &newip, NULL) == -1) {
3931                                 NBUMPSIDE6(fin->fin_out, ns_ifpaddrfail);
3932                                 return -1;
3933                         }
3934                         new = newip;
3935                 } else {
3936                         new.in6 = na->na_nextip6;
3937                 }
3938                 *dst = new;
3939                 error = 0;
3940                 break;
3941
3942         default :
3943                 NBUMPSIDE6(fin->fin_out, ns_badnextaddr);
3944                 break;
3945         }
3946
3947         return error;
3948 }
3949
3950
3951 /* ------------------------------------------------------------------------ */
3952 /* Function:    ipf_nat6_nextaddrinit                                       */
3953 /* Returns:     int - 0 == success, else error number                       */
3954 /* Parameters:  na(I)      - NAT address information for generating new addr*/
3955 /*              base(I)    - start of where to find strings                 */
3956 /*              initial(I) - flag indicating if it is the first call for    */
3957 /*                           this "na" structure.                           */
3958 /*              ifp(I)     - network interface to derive address            */
3959 /*                           information from.                              */
3960 /*                                                                          */
3961 /* This function is expected to be called in two scenarious: when a new NAT */
3962 /* rule is loaded into the kernel and when the list of NAT rules is sync'd  */
3963 /* up with the valid network interfaces (possibly due to them changing.)    */
3964 /* To distinguish between these, the "initial" parameter is used.  If it is */
3965 /* 1 then this indicates the rule has just been reloaded and 0 for when we  */
3966 /* are updating information.  This difference is important because in       */
3967 /* instances where we are not updating address information associated with  */
3968 /* a network interface, we don't want to disturb what the "next" address to */
3969 /* come out of ipf_nat6_nextaddr() will be.                                 */
3970 /* ------------------------------------------------------------------------ */
3971 static int
3972 ipf_nat6_nextaddrinit(softc, base, na, initial, ifp)
3973         ipf_main_softc_t *softc;
3974         char *base;
3975         nat_addr_t *na;
3976         int initial;
3977         void *ifp;
3978 {
3979         switch (na->na_atype)
3980         {
3981         case FRI_LOOKUP :
3982                 if (na->na_subtype == 0) {
3983                         na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT,
3984                                                         na->na_type,
3985                                                         na->na_num,
3986                                                         &na->na_func);
3987                 } else if (na->na_subtype == 1) {
3988                         na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT,
3989                                                          na->na_type,
3990                                                          base + na->na_num,
3991                                                          &na->na_func);
3992                 }
3993                 if (na->na_func == NULL) {
3994                         IPFERROR(60072);
3995                         return ESRCH;
3996                 }
3997                 if (na->na_ptr == NULL) {
3998                         IPFERROR(60073);
3999                         return ESRCH;
4000                 }
4001                 break;
4002         case FRI_DYNAMIC :
4003         case FRI_BROADCAST :
4004         case FRI_NETWORK :
4005         case FRI_NETMASKED :
4006         case FRI_PEERADDR :
4007                 if (ifp != NULL)
4008                         (void )ipf_ifpaddr(softc, 6, na->na_atype, ifp,
4009                                            &na->na_addr[0],
4010                                            &na->na_addr[1]);
4011                 break;
4012
4013         case FRI_SPLIT :
4014         case FRI_RANGE :
4015                 if (initial)
4016                         na->na_nextip6 = na->na_addr[0].in6;
4017                 break;
4018
4019         case FRI_NONE :
4020                 IP6_ANDASSIGN(&na->na_addr[0].in6, &na->na_addr[1].in6);
4021                 return 0;
4022
4023         case FRI_NORMAL :
4024                 IP6_ANDASSIGN(&na->na_addr[0].in6, &na->na_addr[1].in6);
4025                 break;
4026
4027         default :
4028                 IPFERROR(60074);
4029                 return EINVAL;
4030         }
4031
4032         if (initial && (na->na_atype == FRI_NORMAL)) {
4033                 if (IP6_ISZERO(&na->na_addr[0].in6)) {
4034                         if (IP6_ISONES(&na->na_addr[1].in6) ||
4035                             IP6_ISZERO(&na->na_addr[1].in6)) {
4036                                 return 0;
4037                         }
4038                 }
4039
4040                 na->na_nextip6 = na->na_addr[0].in6;
4041                 if (!IP6_ISONES(&na->na_addr[1].in6)) {
4042                         IP6_INC(&na->na_nextip6);
4043                 }
4044         }
4045
4046         return 0;
4047 }
4048
4049
4050 /* ------------------------------------------------------------------------ */
4051 /* Function:    ipf_nat6_icmpquerytype                                      */
4052 /* Returns:     int - 1 == success, 0 == failure                            */
4053 /* Parameters:  icmptype(I) - ICMP type number                              */
4054 /*                                                                          */
4055 /* Tests to see if the ICMP type number passed is a query/response type or  */
4056 /* not.                                                                     */
4057 /* ------------------------------------------------------------------------ */
4058 static int
4059 ipf_nat6_icmpquerytype(icmptype)
4060         int icmptype;
4061 {
4062
4063         /*
4064          * For the ICMP query NAT code, it is essential that both the query
4065          * and the reply match on the NAT rule. Because the NAT structure
4066          * does not keep track of the icmptype, and a single NAT structure
4067          * is used for all icmp types with the same src, dest and id, we
4068          * simply define the replies as queries as well. The funny thing is,
4069          * altough it seems silly to call a reply a query, this is exactly
4070          * as it is defined in the IPv4 specification
4071          */
4072
4073         switch (icmptype)
4074         {
4075
4076         case ICMP6_ECHO_REPLY:
4077         case ICMP6_ECHO_REQUEST:
4078         /* route aedvertisement/solliciation is currently unsupported: */
4079         /* it would require rewriting the ICMP data section            */
4080         case ICMP6_MEMBERSHIP_QUERY:
4081         case ICMP6_MEMBERSHIP_REPORT:
4082         case ICMP6_MEMBERSHIP_REDUCTION:
4083         case ICMP6_WRUREQUEST:
4084         case ICMP6_WRUREPLY:
4085         case MLD6_MTRACE_RESP:
4086         case MLD6_MTRACE:
4087                 return 1;
4088         default:
4089                 return 0;
4090         }
4091 }
4092 #endif /* USE_INET6 */