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