[BACK]Return to npf_nat.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / net / npf

Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.

Diff for /src/sys/net/npf/npf_nat.c between version 1.6 and 1.6.6.5

version 1.6, 2011/02/02 02:20:25 version 1.6.6.5, 2014/05/22 11:41:09
Line 1 
Line 1 
 /*      $NetBSD$        */  /*      $NetBSD$        */
   
 /*-  /*-
  * Copyright (c) 2010-2011 The NetBSD Foundation, Inc.   * Copyright (c) 2014 Mindaugas Rasiukevicius <rmind at netbsd org>
    * Copyright (c) 2010-2013 The NetBSD Foundation, Inc.
  * All rights reserved.   * All rights reserved.
  *   *
  * This material is based upon work partially supported by The   * This material is based upon work partially supported by The
Line 30 
Line 31 
  */   */
   
 /*  /*
  * NPF network address port translation (NAPT).   * NPF network address port translation (NAPT) and other forms of NAT.
  * Described in RFC 2663, RFC 3022.  Commonly just "NAT".   * Described in RFC 2663, RFC 3022, etc.
  *   *
  * Overview   * Overview
  *   *
Line 43 
Line 44 
  *   *
  *      There are two types of translation: outbound (NPF_NATOUT) and   *      There are two types of translation: outbound (NPF_NATOUT) and
  *      inbound (NPF_NATIN).  It should not be confused with connection   *      inbound (NPF_NATIN).  It should not be confused with connection
  *      direction.   *      direction.  See npf_nat_which() for the description of how the
  *   *      addresses are rewritten.
  *      Outbound NAT rewrites:  
  *      - Source on "forwards" stream.  
  *      - Destination on "backwards" stream.  
  *      Inbound NAT rewrites:  
  *      - Destination on "forwards" stream.  
  *      - Source on "backwards" stream.  
  *   *
  *      It should be noted that bi-directional NAT is a combined outbound   *      It should be noted that bi-directional NAT is a combined outbound
  *      and inbound translation, therefore constructed as two policies.   *      and inbound translation, therefore constructed as two policies.
Line 79 
Line 74 
 __KERNEL_RCSID(0, "$NetBSD$");  __KERNEL_RCSID(0, "$NetBSD$");
   
 #include <sys/param.h>  #include <sys/param.h>
 #include <sys/kernel.h>  #include <sys/types.h>
   
 #include <sys/atomic.h>  #include <sys/atomic.h>
 #include <sys/bitops.h>  #include <sys/bitops.h>
Line 87  __KERNEL_RCSID(0, "$NetBSD$");
Line 82  __KERNEL_RCSID(0, "$NetBSD$");
 #include <sys/kmem.h>  #include <sys/kmem.h>
 #include <sys/mutex.h>  #include <sys/mutex.h>
 #include <sys/pool.h>  #include <sys/pool.h>
   #include <sys/proc.h>
   #include <sys/cprng.h>
   
 #include <net/pfil.h>  #include <net/pfil.h>
 #include <netinet/in.h>  #include <netinet/in.h>
   
Line 103  typedef struct {
Line 101  typedef struct {
 /* Portmap range: [ 1024 .. 65535 ] */  /* Portmap range: [ 1024 .. 65535 ] */
 #define PORTMAP_FIRST           (1024)  #define PORTMAP_FIRST           (1024)
 #define PORTMAP_SIZE            ((65536 - PORTMAP_FIRST) / 32)  #define PORTMAP_SIZE            ((65536 - PORTMAP_FIRST) / 32)
 #define PORTMAP_FILLED          ((uint32_t)~0)  #define PORTMAP_FILLED          ((uint32_t)~0U)
 #define PORTMAP_MASK            (31)  #define PORTMAP_MASK            (31)
 #define PORTMAP_SHIFT           (5)  #define PORTMAP_SHIFT           (5)
   
 #define PORTMAP_MEM_SIZE        \  #define PORTMAP_MEM_SIZE        \
     (sizeof(npf_portmap_t) + (PORTMAP_SIZE * sizeof(uint32_t)))      (sizeof(npf_portmap_t) + (PORTMAP_SIZE * sizeof(uint32_t)))
   
 /* NAT policy structure. */  /*
    * NAT policy structure.
    */
 struct npf_natpolicy {  struct npf_natpolicy {
         LIST_HEAD(, npf_nat)    n_nat_list;          LIST_HEAD(, npf_nat)    n_nat_list;
           volatile u_int          n_refcnt;
         kmutex_t                n_lock;          kmutex_t                n_lock;
         kcondvar_t              n_cv;          kcondvar_t              n_cv;
         npf_portmap_t *         n_portmap;          npf_portmap_t *         n_portmap;
           /* NPF_NP_CMP_START */
         int                     n_type;          int                     n_type;
         u_int                   n_flags;          u_int                   n_flags;
         size_t                  n_addr_sz;          size_t                  n_addr_sz;
         npf_addr_t              n_taddr;          npf_addr_t              n_taddr;
           npf_netmask_t           n_tmask;
         in_port_t               n_tport;          in_port_t               n_tport;
           u_int                   n_algo;
           union {
                   uint16_t        n_npt66_adj;
           };
 };  };
   
 #define NPF_NP_CMP_START        offsetof(npf_natpolicy_t, n_type)  #define NPF_NP_CMP_START        offsetof(npf_natpolicy_t, n_type)
 #define NPF_NP_CMP_SIZE         (sizeof(npf_natpolicy_t) - NPF_NP_CMP_START)  #define NPF_NP_CMP_SIZE         (sizeof(npf_natpolicy_t) - NPF_NP_CMP_START)
   
 /* NAT translation entry for a session. */  /*
    * NAT translation entry for a session.
    */
 struct npf_nat {  struct npf_nat {
         /* Association (list entry and a link pointer) with NAT policy. */          /* Association (list entry and a link pointer) with NAT policy. */
         LIST_ENTRY(npf_nat)     nt_entry;          LIST_ENTRY(npf_nat)     nt_entry;
Line 151  static pool_cache_t  nat_cache __read_mo
Line 160  static pool_cache_t  nat_cache __read_mo
 void  void
 npf_nat_sysinit(void)  npf_nat_sysinit(void)
 {  {
   
         nat_cache = pool_cache_init(sizeof(npf_nat_t), coherency_unit,          nat_cache = pool_cache_init(sizeof(npf_nat_t), coherency_unit,
             0, 0, "npfnatpl", NULL, IPL_NET, NULL, NULL, NULL);              0, 0, "npfnatpl", NULL, IPL_NET, NULL, NULL, NULL);
         KASSERT(nat_cache != NULL);          KASSERT(nat_cache != NULL);
Line 160  npf_nat_sysinit(void)
Line 168  npf_nat_sysinit(void)
 void  void
 npf_nat_sysfini(void)  npf_nat_sysfini(void)
 {  {
           /* All NAT policies should already be destroyed. */
         /* NAT policies should already be destroyed. */  
         pool_cache_destroy(nat_cache);          pool_cache_destroy(nat_cache);
 }  }
   
Line 169  npf_nat_sysfini(void)
Line 176  npf_nat_sysfini(void)
  * npf_nat_newpolicy: create a new NAT policy.   * npf_nat_newpolicy: create a new NAT policy.
  *   *
  * => Shares portmap if policy is on existing translation address.   * => Shares portmap if policy is on existing translation address.
  * => XXX: serialise at upper layer.  
  */   */
 npf_natpolicy_t *  npf_natpolicy_t *
 npf_nat_newpolicy(prop_dictionary_t natdict, npf_ruleset_t *nrlset)  npf_nat_newpolicy(prop_dictionary_t natdict, npf_ruleset_t *nrlset)
Line 179  npf_nat_newpolicy(prop_dictionary_t natd
Line 185  npf_nat_newpolicy(prop_dictionary_t natd
         npf_portmap_t *pm;          npf_portmap_t *pm;
   
         np = kmem_zalloc(sizeof(npf_natpolicy_t), KM_SLEEP);          np = kmem_zalloc(sizeof(npf_natpolicy_t), KM_SLEEP);
         mutex_init(&np->n_lock, MUTEX_DEFAULT, IPL_SOFTNET);  
         cv_init(&np->n_cv, "npfnatcv");  
         LIST_INIT(&np->n_nat_list);  
   
         /* Translation type and flags. */          /* Translation type and flags. */
         prop_dictionary_get_int32(natdict, "type", &np->n_type);          prop_dictionary_get_int32(natdict, "type", &np->n_type);
         prop_dictionary_get_uint32(natdict, "flags", &np->n_flags);          prop_dictionary_get_uint32(natdict, "flags", &np->n_flags);
         KASSERT(np->n_type == NPF_NATIN || np->n_type == NPF_NATOUT);  
   
         /* Translation IP. */          /* Should be exclusively either inbound or outbound NAT. */
           if (((np->n_type == NPF_NATIN) ^ (np->n_type == NPF_NATOUT)) == 0) {
                   goto err;
           }
           mutex_init(&np->n_lock, MUTEX_DEFAULT, IPL_SOFTNET);
           cv_init(&np->n_cv, "npfnatcv");
           LIST_INIT(&np->n_nat_list);
   
           /* Translation IP, mask and port (if applicable). */
         obj = prop_dictionary_get(natdict, "translation-ip");          obj = prop_dictionary_get(natdict, "translation-ip");
         np->n_addr_sz = prop_data_size(obj);          np->n_addr_sz = prop_data_size(obj);
         KASSERT(np->n_addr_sz > 0 && np->n_addr_sz <= sizeof(npf_addr_t));          if (np->n_addr_sz == 0 || np->n_addr_sz > sizeof(npf_addr_t)) {
                   goto err;
           }
         memcpy(&np->n_taddr, prop_data_data_nocopy(obj), np->n_addr_sz);          memcpy(&np->n_taddr, prop_data_data_nocopy(obj), np->n_addr_sz);
           prop_dictionary_get_uint8(natdict, "translation-mask", &np->n_tmask);
         /* Translation port (for redirect case). */  
         prop_dictionary_get_uint16(natdict, "translation-port", &np->n_tport);          prop_dictionary_get_uint16(natdict, "translation-port", &np->n_tport);
   
           prop_dictionary_get_uint32(natdict, "translation-algo", &np->n_algo);
           switch (np->n_algo) {
           case NPF_ALGO_NPT66:
                   prop_dictionary_get_uint16(natdict, "npt66-adjustment",
                       &np->n_npt66_adj);
                   break;
           default:
                   if (np->n_tmask != NPF_NO_NETMASK)
                           goto err;
                   break;
           }
   
         /* Determine if port map is needed. */          /* Determine if port map is needed. */
         np->n_portmap = NULL;          np->n_portmap = NULL;
         if ((np->n_flags & NPF_NAT_PORTMAP) == 0) {          if ((np->n_flags & NPF_NAT_PORTMAP) == 0) {
Line 218  npf_nat_newpolicy(prop_dictionary_t natd
Line 241  npf_nat_newpolicy(prop_dictionary_t natd
                 KASSERT(np->n_portmap != NULL);                  KASSERT(np->n_portmap != NULL);
         }          }
         return np;          return np;
   err:
           kmem_free(np, sizeof(npf_natpolicy_t));
           return NULL;
 }  }
   
 /*  /*
Line 232  npf_nat_freepolicy(npf_natpolicy_t *np)
Line 258  npf_nat_freepolicy(npf_natpolicy_t *np)
         npf_session_t *se;          npf_session_t *se;
         npf_nat_t *nt;          npf_nat_t *nt;
   
         /* De-associate all entries from the policy. */          /*
            * Disassociate all entries from the policy.  At this point,
            * new entries can no longer be created for this policy.
            */
         mutex_enter(&np->n_lock);          mutex_enter(&np->n_lock);
         LIST_FOREACH(nt, &np->n_nat_list, nt_entry) {          LIST_FOREACH(nt, &np->n_nat_list, nt_entry) {
                 se = nt->nt_session; /* XXXSMP */                  se = nt->nt_session;
                 if (se == NULL) {                  KASSERT(se != NULL);
                         continue;  
                 }  
                 npf_session_expire(se);                  npf_session_expire(se);
         }          }
         while (!LIST_EMPTY(&np->n_nat_list)) {          while (!LIST_EMPTY(&np->n_nat_list)) {
Line 246  npf_nat_freepolicy(npf_natpolicy_t *np)
Line 273  npf_nat_freepolicy(npf_natpolicy_t *np)
         }          }
         mutex_exit(&np->n_lock);          mutex_exit(&np->n_lock);
   
           /* Kick the worker - all references should be going away. */
           npf_worker_signal();
           while (np->n_refcnt) {
                   kpause("npfgcnat", false, 1, NULL);
           }
           KASSERT(LIST_EMPTY(&np->n_nat_list));
   
         /* Destroy the port map, on last reference. */          /* Destroy the port map, on last reference. */
         if (pm && --pm->p_refcnt == 0) {          if (pm && --pm->p_refcnt == 0) {
                 KASSERT((np->n_flags & NPF_NAT_PORTMAP) != 0);                  KASSERT((np->n_flags & NPF_NAT_PORTMAP) != 0);
Line 256  npf_nat_freepolicy(npf_natpolicy_t *np)
Line 290  npf_nat_freepolicy(npf_natpolicy_t *np)
         kmem_free(np, sizeof(npf_natpolicy_t));          kmem_free(np, sizeof(npf_natpolicy_t));
 }  }
   
   void
   npf_nat_freealg(npf_natpolicy_t *np, npf_alg_t *alg)
   {
           npf_nat_t *nt;
   
           mutex_enter(&np->n_lock);
           LIST_FOREACH(nt, &np->n_nat_list, nt_entry) {
                   if (nt->nt_alg != alg) {
                           continue;
                   }
                   nt->nt_alg = NULL;
           }
           mutex_exit(&np->n_lock);
   }
   
 /*  /*
  * npf_nat_matchpolicy: compare two NAT policies.   * npf_nat_matchpolicy: compare two NAT policies.
  *   *
Line 295  npf_nat_sharepm(npf_natpolicy_t *np, npf
Line 344  npf_nat_sharepm(npf_natpolicy_t *np, npf
         /* If NAT policy has an old port map - drop the reference. */          /* If NAT policy has an old port map - drop the reference. */
         mpm = mnp->n_portmap;          mpm = mnp->n_portmap;
         if (mpm) {          if (mpm) {
                 /* Note: in such case, we must not be a last reference. */                  /* Note: at this point we cannot hold a last reference. */
                 KASSERT(mpm->p_refcnt > 1);                  KASSERT(mpm->p_refcnt > 1);
                 mpm->p_refcnt--;                  mpm->p_refcnt--;
         }          }
Line 319  npf_nat_getport(npf_natpolicy_t *np)
Line 368  npf_nat_getport(npf_natpolicy_t *np)
         u_int n = PORTMAP_SIZE, idx, bit;          u_int n = PORTMAP_SIZE, idx, bit;
         uint32_t map, nmap;          uint32_t map, nmap;
   
         idx = arc4random() % PORTMAP_SIZE;          idx = cprng_fast32() % PORTMAP_SIZE;
         for (;;) {          for (;;) {
                 KASSERT(idx < PORTMAP_SIZE);                  KASSERT(idx < PORTMAP_SIZE);
                 map = pm->p_bitmap[idx];                  map = pm->p_bitmap[idx];
Line 387  npf_nat_putport(npf_natpolicy_t *np, in_
Line 436  npf_nat_putport(npf_natpolicy_t *np, in_
 }  }
   
 /*  /*
    * npf_nat_which: tell which address (source or destination) should be
    * rewritten given the combination of the NAT type and flow direction.
    */
   static inline u_int
   npf_nat_which(const int type, bool forw)
   {
           /*
            * Outbound NAT rewrites:
            * - Source (NPF_SRC) on "forwards" stream.
            * - Destination (NPF_DST) on "backwards" stream.
            * Inbound NAT is other way round.
            */
           if (type == NPF_NATOUT) {
                   forw = !forw;
           } else {
                   KASSERT(type == NPF_NATIN);
           }
           CTASSERT(NPF_SRC == 0 && NPF_DST == 1);
           KASSERT(forw == NPF_SRC || forw == NPF_DST);
           return (u_int)forw;
   }
   
   /*
  * npf_nat_inspect: inspect packet against NAT ruleset and return a policy.   * npf_nat_inspect: inspect packet against NAT ruleset and return a policy.
    *
    * => Acquire a reference on the policy, if found.
  */   */
 static npf_natpolicy_t *  static npf_natpolicy_t *
 npf_nat_inspect(npf_cache_t *npc, nbuf_t *nbuf, ifnet_t *ifp, const int di)  npf_nat_inspect(npf_cache_t *npc, nbuf_t *nbuf, const int di)
 {  {
         npf_ruleset_t *rlset;          int slock = npf_config_read_enter();
           npf_ruleset_t *rlset = npf_config_natset();
         npf_natpolicy_t *np;          npf_natpolicy_t *np;
         npf_rule_t *rl;          npf_rule_t *rl;
   
         npf_core_enter();          rl = npf_ruleset_inspect(npc, nbuf, rlset, di, NPF_LAYER_3);
         rlset = npf_core_natset();  
         rl = npf_ruleset_inspect(npc, nbuf, rlset, ifp, di, NPF_LAYER_3);  
         if (rl == NULL) {          if (rl == NULL) {
                   npf_config_read_exit(slock);
                 return NULL;                  return NULL;
         }          }
         np = npf_rule_getnat(rl);          np = npf_rule_getnat(rl);
         if (np == NULL) {          atomic_inc_uint(&np->n_refcnt);
                 npf_core_exit();          npf_config_read_exit(slock);
                 return NULL;  
         }  
         return np;          return np;
 }  }
   
Line 414  npf_nat_inspect(npf_cache_t *npc, nbuf_t
Line 486  npf_nat_inspect(npf_cache_t *npc, nbuf_t
  * npf_nat_create: create a new NAT translation entry.   * npf_nat_create: create a new NAT translation entry.
  */   */
 static npf_nat_t *  static npf_nat_t *
 npf_nat_create(npf_cache_t *npc, npf_natpolicy_t *np)  npf_nat_create(npf_cache_t *npc, npf_natpolicy_t *np, npf_session_t *se)
 {  {
         const int proto = npf_cache_ipproto(npc);          const int proto = npc->npc_proto;
         npf_nat_t *nt;          npf_nat_t *nt;
   
         KASSERT(npf_iscached(npc, NPC_IP46 | NPC_LAYER4));          KASSERT(npf_iscached(npc, NPC_IP46));
           KASSERT(npf_iscached(npc, NPC_LAYER4));
   
         /* New NAT association. */          /* Construct a new NAT entry and associate it with the session. */
         nt = pool_cache_get(nat_cache, PR_NOWAIT);          nt = pool_cache_get(nat_cache, PR_NOWAIT);
         if (nt == NULL){          if (nt == NULL){
                 return NULL;                  return NULL;
         }          }
         npf_stats_inc(NPF_STAT_NAT_CREATE);          npf_stats_inc(NPF_STAT_NAT_CREATE);
         nt->nt_natpolicy = np;          nt->nt_natpolicy = np;
         nt->nt_session = NULL;          nt->nt_session = se;
         nt->nt_alg = NULL;          nt->nt_alg = NULL;
   
         mutex_enter(&np->n_lock);  
         LIST_INSERT_HEAD(&np->n_nat_list, nt, nt_entry);  
         mutex_exit(&np->n_lock);  
   
         /* Save the original address which may be rewritten. */          /* Save the original address which may be rewritten. */
         if (np->n_type == NPF_NATOUT) {          if (np->n_type == NPF_NATOUT) {
                 /* Source (local) for Outbound NAT. */                  /* Outbound NAT: source (think internal) address. */
                 memcpy(&nt->nt_oaddr, npc->npc_srcip, npc->npc_ipsz);                  memcpy(&nt->nt_oaddr, npc->npc_ips[NPF_SRC], npc->npc_alen);
         } else {          } else {
                 /* Destination (external) for Inbound NAT. */                  /* Inbound NAT: destination (think external) address. */
                 KASSERT(np->n_type == NPF_NATIN);                  KASSERT(np->n_type == NPF_NATIN);
                 memcpy(&nt->nt_oaddr, npc->npc_dstip, npc->npc_ipsz);                  memcpy(&nt->nt_oaddr, npc->npc_ips[NPF_DST], npc->npc_alen);
         }          }
   
         /*          /*
Line 452  npf_nat_create(npf_cache_t *npc, npf_nat
Line 521  npf_nat_create(npf_cache_t *npc, npf_nat
             (proto != IPPROTO_TCP && proto != IPPROTO_UDP)) {              (proto != IPPROTO_TCP && proto != IPPROTO_UDP)) {
                 nt->nt_oport = 0;                  nt->nt_oport = 0;
                 nt->nt_tport = 0;                  nt->nt_tport = 0;
                 return nt;                  goto out;
         }          }
   
         /* Save the relevant TCP/UDP port. */          /* Save the relevant TCP/UDP port. */
         if (proto == IPPROTO_TCP) {          if (proto == IPPROTO_TCP) {
                 struct tcphdr *th = &npc->npc_l4.tcp;                  const struct tcphdr *th = npc->npc_l4.tcp;
                 nt->nt_oport = (np->n_type == NPF_NATOUT) ?                  nt->nt_oport = (np->n_type == NPF_NATOUT) ?
                     th->th_sport : th->th_dport;                      th->th_sport : th->th_dport;
         } else {          } else {
                 struct udphdr *uh = &npc->npc_l4.udp;                  const struct udphdr *uh = npc->npc_l4.udp;
                 nt->nt_oport = (np->n_type == NPF_NATOUT) ?                  nt->nt_oport = (np->n_type == NPF_NATOUT) ?
                     uh->uh_sport : uh->uh_dport;                      uh->uh_sport : uh->uh_dport;
         }          }
Line 471  npf_nat_create(npf_cache_t *npc, npf_nat
Line 541  npf_nat_create(npf_cache_t *npc, npf_nat
         } else {          } else {
                 nt->nt_tport = np->n_tport;                  nt->nt_tport = np->n_tport;
         }          }
   out:
           mutex_enter(&np->n_lock);
           LIST_INSERT_HEAD(&np->n_nat_list, nt, nt_entry);
           mutex_exit(&np->n_lock);
         return nt;          return nt;
 }  }
   
 /*  /*
  * npf_nat_translate: perform address and/or port translation.   * npf_nat_translate: perform translation given the state data.
  */   */
 static int  static inline int
 npf_nat_translate(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt,  npf_nat_translate(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt, bool forw)
     const bool forw, const int di)  
 {  {
         void *n_ptr = nbuf_dataptr(nbuf);          const npf_natpolicy_t *np = nt->nt_natpolicy;
         npf_natpolicy_t *np = nt->nt_natpolicy;          const u_int which = npf_nat_which(np->n_type, forw);
         npf_addr_t *addr;          const npf_addr_t *addr;
         in_port_t port;          in_port_t port;
   
         KASSERT(npf_iscached(npc, NPC_IP46));          KASSERT(npf_iscached(npc, NPC_IP46));
           KASSERT(npf_iscached(npc, NPC_LAYER4));
   
         if (forw) {          if (forw) {
                 /* "Forwards" stream: use translation address/port. */                  /* "Forwards" stream: use translation address/port. */
                 KASSERT(  
                     (np->n_type == NPF_NATIN && di == PFIL_IN) ^  
                     (np->n_type == NPF_NATOUT && di == PFIL_OUT)  
                 );  
                 addr = &np->n_taddr;                  addr = &np->n_taddr;
                 port = nt->nt_tport;                  port = nt->nt_tport;
         } else {          } else {
                 /* "Backwards" stream: use original address/port. */                  /* "Backwards" stream: use original address/port. */
                 KASSERT(  
                     (np->n_type == NPF_NATIN && di == PFIL_OUT) ^  
                     (np->n_type == NPF_NATOUT && di == PFIL_IN)  
                 );  
                 addr = &nt->nt_oaddr;                  addr = &nt->nt_oaddr;
                 port = nt->nt_oport;                  port = nt->nt_oport;
         }          }
         KASSERT((np->n_flags & NPF_NAT_PORTS) != 0 || port == 0);          KASSERT((np->n_flags & NPF_NAT_PORTS) != 0 || port == 0);
   
         /* Execute ALG hook first. */          /* Execute ALG translation first. */
         npf_alg_exec(npc, nbuf, nt, di);          if ((npc->npc_info & NPC_ALG_EXEC) == 0) {
                   npc->npc_info |= NPC_ALG_EXEC;
         /*                  npf_alg_exec(npc, nbuf, nt, forw);
          * Rewrite IP and/or TCP/UDP checksums first, since it will use                  npf_recache(npc, nbuf);
          * the cache containing original values for checksum calculation.  
          */  
         if (!npf_rwrcksum(npc, nbuf, n_ptr, di, addr, port)) {  
                 return EINVAL;  
         }  
         /*  
          * Address translation: rewrite source/destination address, depending  
          * on direction (PFIL_OUT - for source, PFIL_IN - for destination).  
          */  
         if (!npf_rwrip(npc, nbuf, n_ptr, di, addr)) {  
                 return EINVAL;  
         }          }
         if ((np->n_flags & NPF_NAT_PORTS) == 0) {          KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
                 /* Done. */  
                 return 0;          /* Finally, perform the translation. */
         }          return npf_napt_rwr(npc, which, addr, port);
         switch (npf_cache_ipproto(npc)) {  }
         case IPPROTO_TCP:  
         case IPPROTO_UDP:  /*
                 KASSERT(npf_iscached(npc, NPC_TCP | NPC_UDP));   * npf_nat_algo: perform the translation given the algorithm.
                 /* Rewrite source/destination port. */   */
                 if (!npf_rwrport(npc, nbuf, n_ptr, di, port)) {  static inline int
                         return EINVAL;  npf_nat_algo(npf_cache_t *npc, const npf_natpolicy_t *np, bool forw)
                 }  {
                 break;          const u_int which = npf_nat_which(np->n_type, forw);
         case IPPROTO_ICMP:          int error;
                 KASSERT(npf_iscached(npc, NPC_ICMP));  
                 /* Nothing. */          switch (np->n_algo) {
           case NPF_ALGO_NPT66:
                   error = npf_npt66_rwr(npc, which, &np->n_taddr,
                       np->n_tmask, np->n_npt66_adj);
                 break;                  break;
         default:          default:
                 return ENOTSUP;                  error = npf_napt_rwr(npc, which, &np->n_taddr, np->n_tport);
                   break;
         }          }
         return 0;  
 }          return error;
   }
   
 /*  /*
  * npf_do_nat:   * npf_do_nat:
Line 557  npf_nat_translate(npf_cache_t *npc, nbuf
Line 617  npf_nat_translate(npf_cache_t *npc, nbuf
  *      - Associate a NAT policy with a session (may establish a new).   *      - Associate a NAT policy with a session (may establish a new).
  */   */
 int  int
 npf_do_nat(npf_cache_t *npc, npf_session_t *se, nbuf_t *nbuf,  npf_do_nat(npf_cache_t *npc, npf_session_t *se, nbuf_t *nbuf, const int di)
     ifnet_t *ifp, const int di)  
 {  {
         npf_session_t *nse = NULL;          npf_session_t *nse = NULL;
         npf_natpolicy_t *np;          npf_natpolicy_t *np;
         npf_nat_t *nt;          npf_nat_t *nt;
         int error;          int error;
         bool forw, new;          bool forw;
   
         /* All relevant IPv4 data should be already cached. */          /* All relevant IPv4 data should be already cached. */
         if (!npf_iscached(npc, NPC_IP46) || !npf_iscached(npc, NPC_LAYER4)) {          if (!npf_iscached(npc, NPC_IP46) || !npf_iscached(npc, NPC_LAYER4)) {
                 return 0;                  return 0;
         }          }
           KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
   
         /*          /*
          * Return the NAT entry associated with the session, if any.           * Return the NAT entry associated with the session, if any.
Line 578  npf_do_nat(npf_cache_t *npc, npf_session
Line 638  npf_do_nat(npf_cache_t *npc, npf_session
          */           */
         if (se && (nt = npf_session_retnat(se, di, &forw)) != NULL) {          if (se && (nt = npf_session_retnat(se, di, &forw)) != NULL) {
                 np = nt->nt_natpolicy;                  np = nt->nt_natpolicy;
                 new = false;  
                 goto translate;                  goto translate;
         }          }
   
         /*          /*
          * Inspect the packet for a NAT policy, if there is no session.           * Inspect the packet for a NAT policy, if there is no session.
          * Note: acquires the lock (releases, if not found).           * Note: acquires a reference if found.
          */           */
         np = npf_nat_inspect(npc, nbuf, ifp, di);          np = npf_nat_inspect(npc, nbuf, di);
         if (np == NULL) {          if (np == NULL) {
                 /* If packet does not match - done. */                  /* If packet does not match - done. */
                 return 0;                  return 0;
         }          }
         forw = true;          forw = true;
   
         /*          /* Static NAT - just perform the translation. */
          * Create a new NAT entry.  Note: it is safe to unlock, since the          if (np->n_flags & NPF_NAT_STATIC) {
          * NAT policy wont be desotroyed while there are list entries, which                  if (nbuf_cksum_barrier(nbuf, di)) {
          * are removed only on session expiration.  Currently, NAT entry is                          npf_recache(npc, nbuf);
          * not yet associated with any session.                  }
          */                  error = npf_nat_algo(npc, np, forw);
         nt = npf_nat_create(npc, np);                  atomic_dec_uint(&np->n_refcnt);
         if (nt == NULL) {                  return error;
                 npf_core_exit();  
                 return ENOMEM;  
         }  
         npf_core_exit();  
         new = true;  
   
         /* Determine whether any ALG matches. */  
         if (npf_alg_match(npc, nbuf, nt)) {  
                 KASSERT(nt->nt_alg != NULL);  
         }          }
   
         /*          /*
          * If there is no local session (no "keep state" rule - unusual, but           * If there is no local session (no "stateful" rule - unusual, but
          * possible configuration), establish one before translation.  Note           * possible configuration), establish one before translation.  Note
          * that it is not a "pass" session, therefore passing of "backwards"           * that it is not a "pass" session, therefore passing of "backwards"
          * stream depends on other, stateless filtering rules.           * stream depends on other, stateless filtering rules.
          */           */
         if (se == NULL) {          if (se == NULL) {
                 nse = npf_session_establish(npc, nbuf, di);                  nse = npf_session_establish(npc, nbuf, di, true);
                 if (nse == NULL) {                  if (nse == NULL) {
                         error = ENOMEM;                          atomic_dec_uint(&np->n_refcnt);
                         goto out;                          return ENOMEM;
                 }                  }
                 se = nse;                  se = nse;
         }          }
 translate:  
         /* Perform the translation. */          /*
         error = npf_nat_translate(npc, nbuf, nt, forw, di);           * Create a new NAT entry and associate with the session.
            * We will consume the reference on success (release on error).
            */
           nt = npf_nat_create(npc, np, se);
           if (nt == NULL) {
                   atomic_dec_uint(&np->n_refcnt);
                   error = ENOMEM;
                   goto out;
           }
   
           /* Associate the NAT translation entry with the session. */
           error = npf_session_setnat(se, nt, np->n_type);
         if (error) {          if (error) {
                   /* Will release the reference. */
                   npf_nat_destroy(nt);
                 goto out;                  goto out;
         }          }
   
         if (__predict_false(new)) {          /* Determine whether any ALG matches. */
                 /*          if (npf_alg_match(npc, nbuf, nt, di)) {
                  * Associate NAT translation entry with the session.                  KASSERT(nt->nt_alg != NULL);
                  * Note: packet now has a translated address in the cache.          }
                  */  
                 nt->nt_session = se;  translate:
                 error = npf_session_setnat(se, nt, di);          /* May need to process the delayed checksums first (XXX: NetBSD). */
           if (nbuf_cksum_barrier(nbuf, di)) {
                   npf_recache(npc, nbuf);
           }
   
           /* Perform the translation. */
           error = npf_nat_translate(npc, nbuf, nt, forw);
 out:  out:
           if (__predict_false(nse)) {
                 if (error) {                  if (error) {
                         /* If session was for NAT only - expire it. */                          /* It created for NAT - just expire. */
                         if (nse) {                          npf_session_expire(nse);
                                 npf_session_expire(nse);  
                         }  
                         /* Will free the structure and return the port. */  
                         npf_nat_expire(nt);  
                 }  
                 if (nse != NULL) {  
                         npf_session_release(nse);  
                 }                  }
                   npf_session_release(nse);
         }          }
         return error;          return error;
 }  }
Line 674  npf_nat_gettrans(npf_nat_t *nt, npf_addr
Line 738  npf_nat_gettrans(npf_nat_t *nt, npf_addr
 void  void
 npf_nat_getorig(npf_nat_t *nt, npf_addr_t **addr, in_port_t *port)  npf_nat_getorig(npf_nat_t *nt, npf_addr_t **addr, in_port_t *port)
 {  {
   
         *addr = &nt->nt_oaddr;          *addr = &nt->nt_oaddr;
         *port = nt->nt_oport;          *port = nt->nt_oport;
 }  }
Line 685  npf_nat_getorig(npf_nat_t *nt, npf_addr_
Line 748  npf_nat_getorig(npf_nat_t *nt, npf_addr_
 void  void
 npf_nat_setalg(npf_nat_t *nt, npf_alg_t *alg, uintptr_t arg)  npf_nat_setalg(npf_nat_t *nt, npf_alg_t *alg, uintptr_t arg)
 {  {
   
         nt->nt_alg = alg;          nt->nt_alg = alg;
         nt->nt_alg_arg = arg;          nt->nt_alg_arg = arg;
 }  }
   
 /*  /*
  * npf_nat_expire: free NAT-related data structures on session expiration.   * npf_nat_destroy: destroy NAT structure (performed on session expiration).
  */   */
 void  void
 npf_nat_expire(npf_nat_t *nt)  npf_nat_destroy(npf_nat_t *nt)
 {  {
         npf_natpolicy_t *np = nt->nt_natpolicy;          npf_natpolicy_t *np = nt->nt_natpolicy;
   
Line 703  npf_nat_expire(npf_nat_t *nt)
Line 765  npf_nat_expire(npf_nat_t *nt)
                 npf_nat_putport(np, nt->nt_tport);                  npf_nat_putport(np, nt->nt_tport);
         }          }
   
         /* Remove NAT entry from the list, notify any waiters if last entry. */  
         mutex_enter(&np->n_lock);          mutex_enter(&np->n_lock);
         LIST_REMOVE(nt, nt_entry);          LIST_REMOVE(nt, nt_entry);
         if (LIST_EMPTY(&np->n_nat_list)) {          if (LIST_EMPTY(&np->n_nat_list)) {
                   /* Notify any waiters if empty. */
                 cv_broadcast(&np->n_cv);                  cv_broadcast(&np->n_cv);
         }          }
           atomic_dec_uint(&np->n_refcnt);
         mutex_exit(&np->n_lock);          mutex_exit(&np->n_lock);
   
         /* Free structure, increase the counter. */  
         pool_cache_put(nat_cache, nt);          pool_cache_put(nat_cache, nt);
         npf_stats_inc(NPF_STAT_NAT_DESTROY);          npf_stats_inc(NPF_STAT_NAT_DESTROY);
 }  }
Line 726  npf_nat_save(prop_dictionary_t sedict, p
Line 788  npf_nat_save(prop_dictionary_t sedict, p
         prop_object_iterator_t it;          prop_object_iterator_t it;
         prop_dictionary_t npdict;          prop_dictionary_t npdict;
         prop_data_t nd, npd;          prop_data_t nd, npd;
         uintptr_t itnp;          uint64_t itnp;
   
         /* Set NAT entry data. */          /* Set NAT entry data. */
         nd = prop_data_create_data(nt, sizeof(npf_nat_t));          nd = prop_data_create_data(nt, sizeof(npf_nat_t));
Line 737  npf_nat_save(prop_dictionary_t sedict, p
Line 799  npf_nat_save(prop_dictionary_t sedict, p
         it = prop_array_iterator(natlist);          it = prop_array_iterator(natlist);
         while ((npdict = prop_object_iterator_next(it)) != NULL) {          while ((npdict = prop_object_iterator_next(it)) != NULL) {
                 CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t));                  CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t));
                 prop_dictionary_get_uint64(npdict, "id-ptr", (uint64_t *)&itnp);                  prop_dictionary_get_uint64(npdict, "id-ptr", &itnp);
                 if (itnp == (uintptr_t)np) {                  if ((uintptr_t)itnp == (uintptr_t)np) {
                         break;                          break;
                 }                  }
         }          }
Line 789  npf_nat_restore(prop_dictionary_t sedict
Line 851  npf_nat_restore(prop_dictionary_t sedict
                 return NULL;                  return NULL;
         }          }
   
         /* Match if there is an existing NAT policy. */          /*
         rl = npf_ruleset_matchnat(npf_core_natset(), __UNCONST(onp));           * Match if there is an existing NAT policy.  Will acquire the
            * reference on it if further operations are successful.
            */
           KASSERT(npf_config_locked_p());
           rl = npf_ruleset_matchnat(npf_config_natset(), __UNCONST(onp));
         if (rl == NULL) {          if (rl == NULL) {
                 return NULL;                  return NULL;
         }          }
Line 801  npf_nat_restore(prop_dictionary_t sedict
Line 867  npf_nat_restore(prop_dictionary_t sedict
         if (!npf_nat_takeport(np, ntraw->nt_tport)) {          if (!npf_nat_takeport(np, ntraw->nt_tport)) {
                 return NULL;                  return NULL;
         }          }
           atomic_inc_uint(&np->n_refcnt);
   
         /* Create and return NAT entry for association. */          /* Create and return NAT entry for association. */
         nt = pool_cache_get(nat_cache, PR_WAITOK);          nt = pool_cache_get(nat_cache, PR_WAITOK);
Line 815  npf_nat_restore(prop_dictionary_t sedict
Line 882  npf_nat_restore(prop_dictionary_t sedict
 #if defined(DDB) || defined(_NPF_TESTING)  #if defined(DDB) || defined(_NPF_TESTING)
   
 void  void
 npf_nat_dump(npf_nat_t *nt)  npf_nat_dump(const npf_nat_t *nt)
 {  {
         npf_natpolicy_t *np;          const npf_natpolicy_t *np;
         struct in_addr ip;          struct in_addr ip;
   
         np = nt->nt_natpolicy;          np = nt->nt_natpolicy;

Legend:
Removed from v.1.6  
changed lines
  Added in v.1.6.6.5

CVSweb <webmaster@jp.NetBSD.org>