The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netpfil/ipfilter/netinet/ip_nat6.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 96fd3b0a59aa08426bf7ad75d473e8fc


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.