nat: VRF routing & FIB improvements 29/35429/10
authorFilip Varga <fivarga@cisco.com>
Wed, 23 Feb 2022 23:45:48 +0000 (15:45 -0800)
committerOle Tr�an <otroan@employees.org>
Fri, 1 Apr 2022 13:26:04 +0000 (13:26 +0000)
This patch affects how destination fib is choosen during session
creation. Default behavior of choosing fib based on output
interfaces is kept.

Configuration gives you the ability to change default behavior
to direct or restrict traffic between different FIB tables.

NAT specific VRF routing options:
a) keeping communication in the same VRF
b) option to add multiple destination VRFs
c) option to control the resolution order of destination VRFs

TX FIB resolution is based on looking up RX FIB entry in NATs
VRF table and picking the first FIB that resolves
destination address.

Ticket: VPP-2009
Type: improvement

Change-Id: If500c48d7ce3466533ad9581c0847870788fc4fb
Signed-off-by: Filip Varga <fivarga@cisco.com>
src/plugins/nat/nat44-ed/nat44_ed.api
src/plugins/nat/nat44-ed/nat44_ed.c
src/plugins/nat/nat44-ed/nat44_ed.h
src/plugins/nat/nat44-ed/nat44_ed_api.c
src/plugins/nat/nat44-ed/nat44_ed_cfg.c [deleted file]
src/plugins/nat/nat44-ed/nat44_ed_cli.c
src/plugins/nat/nat44-ed/nat44_ed_in2out.c
src/plugins/nat/nat44-ed/nat44_ed_out2in.c

index afb5502..88496a3 100644 (file)
@@ -198,6 +198,58 @@ define nat_worker_details {
   string name[64];
 };
 
+/** \brief Add/delete inter VRF NAT44-ED routing table
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param table_vrf_id - id of (rx) VRF used for resolving
+                          destination (tx) VRF during dynamic
+                          session creation
+    @param is_add - if true add else del
+*/
+autoreply define nat44_ed_add_del_vrf_table {
+  u32 client_index;
+  u32 context;
+  u32 table_vrf_id;
+  bool is_add;
+};
+
+/** \brief Add/del inter VRF NAT44-ED route record
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param table_vrf_id - id of the VRF NAT routing table
+    @param vrf_id - id of resolving destination (tx) VRF table
+    @param is_add - if true add else del
+*/
+autoreply define nat44_ed_add_del_vrf_route {
+  u32 client_index;
+  u32 context;
+  u32 table_vrf_id;
+  u32 vrf_id;
+  bool is_add;
+};
+
+/** \brief Dump NAT44-ED inter VRF NAT routing tables
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define nat44_ed_vrf_tables_dump {
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief NAT44-ED inter VRF NAT routing table details response
+    @param context - sender context, to match reply w/ request
+    @param table_vrf_id - id of the VRF NAT routing table
+    @param n_vrf_ids - number of vrf_ids
+    @param vrf_ids - ids of resolving destination (tx) VRFs
+*/
+define nat44_ed_vrf_tables_details {
+  u32 context;
+  u32 table_vrf_id;
+  u32 n_vrf_ids;
+  u32 vrf_ids[n_vrf_ids];
+};
+
 /** \brief Set TCP MSS rewriting configuration
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
index bf802f5..3b1c9a2 100644 (file)
@@ -626,6 +626,134 @@ nat44_ed_del_address (ip4_address_t addr, u8 twice_nat)
   return 0;
 }
 
+vrf_table_t *
+nat44_ed_get_vrf_table (u32 table_vrf_id)
+{
+  snat_main_t *sm = &snat_main;
+  vrf_table_t *t;
+
+  pool_foreach (t, sm->vrf_tables)
+    {
+      if (table_vrf_id == t->table_vrf_id)
+       {
+         return t;
+       }
+    }
+  return NULL;
+}
+
+vrf_route_t *
+nat44_ed_get_vrf_route (vrf_table_t *t, u32 vrf_id)
+{
+  vrf_route_t *r;
+
+  pool_foreach (r, t->routes)
+    {
+      if (vrf_id == r->vrf_id)
+       {
+         return r;
+       }
+    }
+  return NULL;
+}
+
+int
+nat44_ed_add_del_vrf_table (u32 table_vrf_id, bool is_add)
+{
+  snat_main_t *sm = &snat_main;
+  vrf_table_t *t;
+  vrf_route_t *r;
+
+  t = nat44_ed_get_vrf_table (table_vrf_id);
+  if (t)
+    {
+      if (is_add)
+       {
+         return VNET_API_ERROR_VALUE_EXIST;
+       }
+      pool_foreach (r, t->routes)
+       {
+         fib_table_unlock (r->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
+       }
+      fib_table_unlock (t->table_fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
+
+      pool_free (t->routes);
+      pool_put (sm->vrf_tables, t);
+    }
+  else
+    {
+      if (!is_add)
+       {
+         return VNET_API_ERROR_NO_SUCH_ENTRY;
+       }
+      pool_get (sm->vrf_tables, t);
+      clib_memset (t, 0, sizeof (*t));
+      t->table_vrf_id = table_vrf_id;
+      t->table_fib_index = fib_table_find_or_create_and_lock (
+       FIB_PROTOCOL_IP4, table_vrf_id, sm->fib_src_low);
+    }
+
+  return 0;
+}
+
+void
+nat44_ed_del_vrf_tables ()
+{
+  snat_main_t *sm = &snat_main;
+  vrf_table_t *t;
+  vrf_route_t *r;
+
+  pool_foreach (t, sm->vrf_tables)
+    {
+      pool_foreach (r, t->routes)
+       {
+         fib_table_unlock (r->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
+       }
+      fib_table_unlock (t->table_fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
+      pool_free (t->routes);
+    }
+  pool_free (sm->vrf_tables);
+}
+
+int
+nat44_ed_add_del_vrf_route (u32 table_vrf_id, u32 vrf_id, bool is_add)
+{
+  snat_main_t *sm = &snat_main;
+  vrf_table_t *t;
+  vrf_route_t *r;
+
+  t = nat44_ed_get_vrf_table (table_vrf_id);
+  if (!t)
+    {
+      return VNET_API_ERROR_NO_SUCH_ENTRY;
+    }
+
+  r = nat44_ed_get_vrf_route (t, vrf_id);
+  if (r)
+    {
+      if (is_add)
+       {
+         return VNET_API_ERROR_VALUE_EXIST;
+       }
+      fib_table_unlock (r->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
+      pool_put (t->routes, r);
+    }
+  else
+    {
+      if (!is_add)
+       {
+         return VNET_API_ERROR_NO_SUCH_ENTRY;
+       }
+      pool_get (t->routes, r);
+      clib_memset (r, 0, sizeof (*r));
+      r->vrf_id = vrf_id;
+      r->fib_index = fib_table_find_or_create_and_lock (
+       FIB_PROTOCOL_IP4, vrf_id, sm->fib_src_low);
+    }
+
+  return 0;
+}
+
 u32
 get_thread_idx_by_port (u16 e_port)
 {
@@ -1555,10 +1683,10 @@ update_per_vrf_sessions_vec (u32 fib_index, int is_del)
     }
 }
 
-static_always_inline nat_outside_fib_t *
-nat44_ed_get_outside_fib (nat_outside_fib_t *outside_fibs, u32 fib_index)
+static_always_inline nat_fib_t *
+nat44_ed_get_outside_fib (nat_fib_t *outside_fibs, u32 fib_index)
 {
-  nat_outside_fib_t *f;
+  nat_fib_t *f;
   vec_foreach (f, outside_fibs)
     {
       if (f->fib_index == fib_index)
@@ -1589,7 +1717,7 @@ nat44_ed_add_interface (u32 sw_if_index, u8 is_inside)
   const char *del_feature_name, *feature_name;
   snat_main_t *sm = &snat_main;
 
-  nat_outside_fib_t *outside_fib;
+  nat_fib_t *outside_fib;
   snat_interface_t *i;
   u32 fib_index;
   int rv;
@@ -1671,13 +1799,13 @@ nat44_ed_add_interface (u32 sw_if_index, u8 is_inside)
       outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index);
       if (outside_fib)
        {
-         outside_fib->refcount++;
+         outside_fib->ref_count++;
        }
       else
        {
          vec_add2 (sm->outside_fibs, outside_fib, 1);
          outside_fib->fib_index = fib_index;
-         outside_fib->refcount = 1;
+         outside_fib->ref_count = 1;
        }
 
       nat44_ed_add_del_nat_addr_fib_reg_entries (sw_if_index, 1);
@@ -1699,7 +1827,7 @@ nat44_ed_del_interface (u32 sw_if_index, u8 is_inside)
   const char *del_feature_name, *feature_name;
   snat_main_t *sm = &snat_main;
 
-  nat_outside_fib_t *outside_fib;
+  nat_fib_t *outside_fib;
   snat_interface_t *i;
   u32 fib_index;
   int rv;
@@ -1784,8 +1912,8 @@ nat44_ed_del_interface (u32 sw_if_index, u8 is_inside)
       outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index);
       if (outside_fib)
        {
-         outside_fib->refcount--;
-         if (!outside_fib->refcount)
+         outside_fib->ref_count--;
+         if (!outside_fib->ref_count)
            {
              vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs);
            }
@@ -1803,7 +1931,7 @@ nat44_ed_add_output_interface (u32 sw_if_index)
 {
   snat_main_t *sm = &snat_main;
 
-  nat_outside_fib_t *outside_fib;
+  nat_fib_t *outside_fib;
   snat_interface_t *i;
   u32 fib_index;
   int rv;
@@ -1881,13 +2009,13 @@ nat44_ed_add_output_interface (u32 sw_if_index)
   outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index);
   if (outside_fib)
     {
-      outside_fib->refcount++;
+      outside_fib->ref_count++;
     }
   else
     {
       vec_add2 (sm->outside_fibs, outside_fib, 1);
       outside_fib->fib_index = fib_index;
-      outside_fib->refcount = 1;
+      outside_fib->ref_count = 1;
     }
 
   nat44_ed_add_del_nat_addr_fib_reg_entries (sw_if_index, 1);
@@ -1903,7 +2031,7 @@ nat44_ed_del_output_interface (u32 sw_if_index)
 {
   snat_main_t *sm = &snat_main;
 
-  nat_outside_fib_t *outside_fib;
+  nat_fib_t *outside_fib;
   snat_interface_t *i;
   u32 fib_index;
   int rv;
@@ -1971,8 +2099,8 @@ nat44_ed_del_output_interface (u32 sw_if_index)
   outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index);
   if (outside_fib)
     {
-      outside_fib->refcount--;
-      if (!outside_fib->refcount)
+      outside_fib->ref_count--;
+      if (!outside_fib->ref_count)
        {
          vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs);
        }
@@ -2036,7 +2164,7 @@ nat44_ed_update_outside_fib_cb (ip4_main_t *im, uword opaque, u32 sw_if_index,
                                u32 new_fib_index, u32 old_fib_index)
 {
   snat_main_t *sm = &snat_main;
-  nat_outside_fib_t *outside_fib;
+  nat_fib_t *outside_fib;
   snat_interface_t *i;
   u8 is_add = 1;
   u8 match = 0;
@@ -2074,8 +2202,8 @@ nat44_ed_update_outside_fib_cb (ip4_main_t *im, uword opaque, u32 sw_if_index,
     {
       if (outside_fib->fib_index == old_fib_index)
        {
-         outside_fib->refcount--;
-         if (!outside_fib->refcount)
+         outside_fib->ref_count--;
+         if (!outside_fib->ref_count)
            vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs);
          break;
        }
@@ -2085,7 +2213,7 @@ nat44_ed_update_outside_fib_cb (ip4_main_t *im, uword opaque, u32 sw_if_index,
     {
       if (outside_fib->fib_index == new_fib_index)
        {
-         outside_fib->refcount++;
+         outside_fib->ref_count++;
          is_add = 0;
          break;
        }
@@ -2094,7 +2222,7 @@ nat44_ed_update_outside_fib_cb (ip4_main_t *im, uword opaque, u32 sw_if_index,
   if (is_add)
     {
       vec_add2 (sm->outside_fibs, outside_fib, 1);
-      outside_fib->refcount = 1;
+      outside_fib->ref_count = 1;
       outside_fib->fib_index = new_fib_index;
     }
 }
@@ -2518,6 +2646,8 @@ nat44_plugin_disable ()
   if (rc)
     error = 1;
 
+  nat44_ed_del_vrf_tables ();
+
   vec_free (sm->max_translations_per_fib);
   sm->max_translations_per_fib = 0;
 
@@ -2587,11 +2717,12 @@ nat44_ed_sm_match (snat_main_t *sm, ip4_address_t match_addr, u16 match_port,
       if (m)
        return m;
 
-      /* Try address only mapping */
+      // try address only mapping
       m = nat44_ed_sm_i2o_lookup (sm, match_addr, 0, match_fib_index, 0);
       if (m)
        return m;
 
+      // default static mapping fib index (based on configuration)
       if (sm->inside_fib_index != match_fib_index)
        {
          m = nat44_ed_sm_i2o_lookup (sm, match_addr, match_port,
@@ -2599,12 +2730,13 @@ nat44_ed_sm_match (snat_main_t *sm, ip4_address_t match_addr, u16 match_port,
          if (m)
            return m;
 
-         /* Try address only mapping */
+         // try address only mapping
          m = nat44_ed_sm_i2o_lookup (sm, match_addr, 0, sm->inside_fib_index,
                                      0);
          if (m)
            return m;
        }
+      // TODO: this specific use case may be deprecated (needs testing)
       if (sm->outside_fib_index != match_fib_index)
        {
          m = nat44_ed_sm_i2o_lookup (sm, match_addr, match_port,
@@ -2612,7 +2744,7 @@ nat44_ed_sm_match (snat_main_t *sm, ip4_address_t match_addr, u16 match_port,
          if (m)
            return m;
 
-         /* Try address only mapping */
+         // try address only mapping
          m = nat44_ed_sm_i2o_lookup (sm, match_addr, 0, sm->outside_fib_index,
                                      0);
          if (m)
@@ -2626,7 +2758,7 @@ nat44_ed_sm_match (snat_main_t *sm, ip4_address_t match_addr, u16 match_port,
       if (m)
        return m;
 
-      /* Try address only mapping */
+      // try address only mapping
       m = nat44_ed_sm_o2i_lookup (sm, match_addr, 0, 0, 0);
       if (m)
        return m;
@@ -2635,15 +2767,16 @@ nat44_ed_sm_match (snat_main_t *sm, ip4_address_t match_addr, u16 match_port,
 }
 
 int
-snat_static_mapping_match (vlib_main_t *vm, snat_main_t *sm,
-                          ip4_address_t match_addr, u16 match_port,
-                          u32 match_fib_index, ip_protocol_t match_protocol,
+snat_static_mapping_match (vlib_main_t *vm, ip4_address_t match_addr,
+                          u16 match_port, u32 match_fib_index,
+                          ip_protocol_t match_protocol,
                           ip4_address_t *mapping_addr, u16 *mapping_port,
                           u32 *mapping_fib_index, int by_external,
                           u8 *is_addr_only, twice_nat_type_t *twice_nat,
                           lb_nat_type_t *lb, ip4_address_t *ext_host_addr,
                           u8 *is_identity_nat, snat_static_mapping_t **out)
 {
+  snat_main_t *sm = &snat_main;
   snat_static_mapping_t *m;
   u32 rand, lo = 0, hi, mid, *tmp = 0, i;
   nat44_lb_addr_port_t *local;
@@ -2787,7 +2920,7 @@ nat44_ed_get_in2out_worker_index (vlib_buffer_t *b, ip4_header_t *ip,
       if (PREDICT_FALSE (is_output))
        {
          fib_index = sm->outside_fib_index;
-         nat_outside_fib_t *outside_fib;
+         nat_fib_t *outside_fib;
          fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
          fib_prefix_t pfx = {
                  .fp_proto = FIB_PROTOCOL_IP4,
index e2047fe..1ca7118 100644 (file)
@@ -353,18 +353,6 @@ typedef struct
   u32 addr_len;
 } snat_address_t;
 
-typedef struct
-{
-  u32 fib_index;
-  u32 ref_count;
-} nat_fib_t;
-
-typedef struct
-{
-  u32 fib_index;
-  u32 refcount;
-} nat_outside_fib_t;
-
 typedef struct
 {
   /* backend IP address */
@@ -504,6 +492,25 @@ typedef int (nat_alloc_out_addr_and_port_function_t) (
   ip_protocol_t proto, ip4_address_t *addr, u16 *port, u16 port_per_thread,
   u32 snat_thread_index);
 
+typedef struct nat_fib_s
+{
+  u32 fib_index;
+  u32 ref_count;
+} nat_fib_t;
+
+typedef struct vrf_route_s
+{
+  u32 vrf_id;
+  u32 fib_index;
+} vrf_route_t;
+
+typedef struct vrf_table_s
+{
+  u32 table_vrf_id;
+  u32 table_fib_index;
+  vrf_route_t *routes;
+} vrf_table_t;
+
 typedef struct snat_main_s
 {
   /* Thread settings */
@@ -525,6 +532,21 @@ typedef struct snat_main_s
   /* Endpoint dependent lookup table */
   clib_bihash_16_8_t flow_hash;
 
+  // vector of fibs
+  nat_fib_t *fibs;
+
+  u32 inside_vrf_id;
+  u32 inside_fib_index;
+
+  u32 outside_vrf_id;
+  u32 outside_fib_index;
+
+  // vector of outside fibs
+  nat_fib_t *outside_fibs;
+
+  // VRF routing table for dynamic sessions
+  vrf_table_t *vrf_tables;
+
   /* Interface pool */
   snat_interface_t *interfaces;
   snat_interface_t *output_feature_interfaces;
@@ -539,12 +561,6 @@ typedef struct snat_main_s
   /* first interface address should be auto-added */
   snat_address_resolve_t *addr_to_resolve;
 
-  /* vector of fibs */
-  nat_fib_t *fibs;
-
-  /* vector of outside fibs */
-  nat_outside_fib_t *outside_fibs;
-
   /* vector of fib entries */
   snat_fib_entry_reg_t *fib_entry_reg;
 
@@ -571,11 +587,6 @@ typedef struct snat_main_s
   u32 max_translations_per_thread;
   u32 *max_translations_per_fib;
 
-  u32 outside_vrf_id;
-  u32 outside_fib_index;
-  u32 inside_vrf_id;
-  u32 inside_fib_index;
-
   nat_timeouts_t timeouts;
 
   /* TCP MSS clamping */
@@ -849,6 +860,10 @@ int nat44_ed_del_address (ip4_address_t addr, u8 twice_nat);
 int nat44_ed_add_interface_address (u32 sw_if_index, u8 twice_nat);
 int nat44_ed_del_interface_address (u32 sw_if_index, u8 twice_nat);
 
+int nat44_ed_add_del_vrf_table (u32 table_vrf_id, bool is_add);
+int nat44_ed_add_del_vrf_route (u32 table_vrf_id, u32 vrf_id, bool is_add);
+void nat44_ed_del_vrf_tables ();
+
 int nat44_ed_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
                                 u16 l_port, u16 e_port, ip_protocol_t proto,
                                 u32 vrf_id, u32 sw_if_index, u32 flags,
@@ -910,31 +925,15 @@ int nat44_update_session_limit (u32 session_limit, u32 vrf_id);
 
 void expire_per_vrf_sessions (u32 fib_index);
 
-/**
- * @brief Match NAT44 static mapping.
- *
- * @param key             address and port to match
- * @param addr            external/local address of the matched mapping
- * @param port            port of the matched mapping
- * @param fib_index       fib index of the matched mapping
- * @param by_external     if 0 match by local address otherwise match by external
- *                        address
- * @param is_addr_only    1 if matched mapping is address only
- * @param twice_nat       matched mapping is twice NAT type
- * @param lb              1 if matched mapping is load-balanced
- * @param ext_host_addr   external host address
- * @param is_identity_nat 1 if indentity mapping
- * @param out             if !=0 set to pointer of the mapping structure
- *
- * @returns 0 if match found otherwise 1.
- */
-int snat_static_mapping_match (
-  vlib_main_t *vm, snat_main_t *sm, ip4_address_t match_addr, u16 match_port,
-  u32 match_fib_index, ip_protocol_t match_protocol,
-  ip4_address_t *mapping_addr, u16 *mapping_port, u32 *mapping_fib_index,
-  int by_external, u8 *is_addr_only, twice_nat_type_t *twice_nat,
-  lb_nat_type_t *lb, ip4_address_t *ext_host_addr, u8 *is_identity_nat,
-  snat_static_mapping_t **out);
+int snat_static_mapping_match (vlib_main_t *vm, ip4_address_t match_addr,
+                              u16 match_port, u32 match_fib_index,
+                              ip_protocol_t match_protocol,
+                              ip4_address_t *mapping_addr, u16 *mapping_port,
+                              u32 *mapping_fib_index, int by_external,
+                              u8 *is_addr_only, twice_nat_type_t *twice_nat,
+                              lb_nat_type_t *lb, ip4_address_t *ext_host_addr,
+                              u8 *is_identity_nat,
+                              snat_static_mapping_t **out);
 
 u32 get_thread_idx_by_port (u16 e_port);
 
index f4ba2bc..19e497e 100644 (file)
@@ -1194,6 +1194,79 @@ vl_api_nat44_show_running_config_t_handler (
     }));
 }
 
+static void
+vl_api_nat44_ed_add_del_vrf_table_t_handler (
+  vl_api_nat44_ed_add_del_vrf_table_t *mp)
+{
+  snat_main_t *sm = &snat_main;
+  vl_api_nat44_ed_add_del_vrf_table_reply_t *rmp;
+  int rv = nat44_ed_add_del_vrf_table (clib_net_to_host_u32 (mp->table_vrf_id),
+                                      mp->is_add);
+  REPLY_MACRO (VL_API_NAT44_ED_ADD_DEL_VRF_TABLE);
+}
+
+static void
+vl_api_nat44_ed_add_del_vrf_route_t_handler (
+  vl_api_nat44_ed_add_del_vrf_route_t *mp)
+{
+  snat_main_t *sm = &snat_main;
+  vl_api_nat44_ed_add_del_vrf_route_reply_t *rmp;
+  int rv =
+    nat44_ed_add_del_vrf_route (clib_net_to_host_u32 (mp->table_vrf_id),
+                               clib_net_to_host_u32 (mp->vrf_id), mp->is_add);
+  REPLY_MACRO (VL_API_NAT44_ED_ADD_DEL_VRF_ROUTE);
+}
+
+static void
+nat44_ed_vrf_tables_send_details (vl_api_registration_t *rp, u32 context,
+                                 vrf_table_t *t)
+{
+  snat_main_t *sm = &snat_main;
+  vl_api_nat44_ed_vrf_tables_details_t *mp;
+
+  u32 *vrf_ids = 0;
+  vrf_route_t *r;
+
+  mp = vl_msg_api_alloc_zero (sizeof (*mp) +
+                             sizeof (mp->vrf_ids[0]) * vec_len (t->routes));
+  mp->_vl_msg_id =
+    ntohs (VL_API_NAT44_ED_VRF_TABLES_DETAILS + sm->msg_id_base);
+  mp->context = context;
+  mp->n_vrf_ids = clib_host_to_net_u32 (vec_len (t->routes));
+
+  pool_foreach (r, t->routes)
+    {
+      vec_add1 (vrf_ids, r->vrf_id);
+    }
+
+  // copy the records
+  clib_memcpy (mp->vrf_ids, vrf_ids,
+              sizeof (mp->vrf_ids[0]) * vec_len (t->routes));
+
+  vec_free (vrf_ids);
+
+  // send the message
+  vl_api_send_msg (rp, (u8 *) mp);
+}
+
+static void
+vl_api_nat44_ed_vrf_tables_dump_t_handler (
+  vl_api_nat44_ed_vrf_tables_dump_t *mp)
+{
+  snat_main_t *sm = &snat_main;
+  vl_api_registration_t *rp;
+  vrf_table_t *t;
+
+  rp = vl_api_client_index_to_registration (mp->client_index);
+  if (rp == 0)
+    return;
+
+  pool_foreach (t, sm->vrf_tables)
+    {
+      nat44_ed_vrf_tables_send_details (rp, mp->context, t);
+    }
+}
+
 /* user (internal host) key */
 typedef struct
 {
diff --git a/src/plugins/nat/nat44-ed/nat44_ed_cfg.c b/src/plugins/nat/nat44-ed/nat44_ed_cfg.c
deleted file mode 100644 (file)
index e69de29..0000000
index 7693063..bddd635 100644 (file)
@@ -916,8 +916,6 @@ add_static_mapping_command_fn (vlib_main_t * vm,
       e_port = clib_host_to_net_u16 (e_port);
     }
 
-  // TODO: specific pool_addr for both pool & twice nat pool ?
-
   if (is_add)
     {
       rv =
@@ -930,25 +928,17 @@ add_static_mapping_command_fn (vlib_main_t * vm,
                                        vrf_id, sw_if_index, flags);
     }
 
-  // TODO: fix returns
-
   switch (rv)
     {
-    case VNET_API_ERROR_INVALID_VALUE:
-      error = clib_error_return (0, "External port already in use.");
-      goto done;
+    case VNET_API_ERROR_UNSUPPORTED:
+      error = clib_error_return (0, "Plugin disabled.");
+      break;
     case VNET_API_ERROR_NO_SUCH_ENTRY:
-      if (is_add)
-       error = clib_error_return (0, "External address must be allocated.");
-      else
-       error = clib_error_return (0, "Mapping not exist.");
-      goto done;
-    case VNET_API_ERROR_NO_SUCH_FIB:
-      error = clib_error_return (0, "No such VRF id.");
-      goto done;
+      error = clib_error_return (0, "Mapping not exist.");
+      break;
     case VNET_API_ERROR_VALUE_EXIST:
       error = clib_error_return (0, "Mapping already exist.");
-      goto done;
+      break;
     default:
       break;
     }
@@ -959,7 +949,6 @@ done:
   return error;
 }
 
-// TODO: either delete this bullshit or update it
 static clib_error_t *
 add_identity_mapping_command_fn (vlib_main_t * vm,
                                 unformat_input_t * input,
@@ -976,7 +965,6 @@ add_identity_mapping_command_fn (vlib_main_t * vm,
 
   flags = NAT_SM_FLAG_IDENTITY_NAT;
 
-  /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
     return clib_error_return (0, NAT44_ED_EXPECTED_ARGUMENT);
 
@@ -1029,25 +1017,17 @@ add_identity_mapping_command_fn (vlib_main_t * vm,
                                        sw_if_index, flags);
     }
 
-  // TODO: fix returns
-
   switch (rv)
     {
-    case VNET_API_ERROR_INVALID_VALUE:
-      error = clib_error_return (0, "External port already in use.");
-      goto done;
+    case VNET_API_ERROR_UNSUPPORTED:
+      error = clib_error_return (0, "Plugin disabled.");
+      break;
     case VNET_API_ERROR_NO_SUCH_ENTRY:
-      if (is_add)
-       error = clib_error_return (0, "External address must be allocated.");
-      else
-       error = clib_error_return (0, "Mapping not exist.");
-      goto done;
-    case VNET_API_ERROR_NO_SUCH_FIB:
-      error = clib_error_return (0, "No such VRF id.");
-      goto done;
+      error = clib_error_return (0, "Mapping not exist.");
+      break;
     case VNET_API_ERROR_VALUE_EXIST:
       error = clib_error_return (0, "Mapping already exist.");
-      goto done;
+      break;
     default:
       break;
     }
@@ -1073,7 +1053,6 @@ add_lb_static_mapping_command_fn (vlib_main_t * vm,
   int rv, is_add = 1;
   u32 flags = 0;
 
-  /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
     return clib_error_return (0, NAT44_ED_EXPECTED_ARGUMENT);
 
@@ -1341,6 +1320,156 @@ done:
   return error;
 }
 
+static clib_error_t *
+nat44_ed_add_del_vrf_table_command_fn (vlib_main_t *vm,
+                                      unformat_input_t *input,
+                                      vlib_cli_command_t *cmd)
+{
+  unformat_input_t _line_input, *line_input = &_line_input;
+  clib_error_t *error = 0;
+  bool is_add = true, not_set = true;
+  u32 vrf_id = ~0;
+  int rv;
+
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return clib_error_return (0, NAT44_ED_EXPECTED_ARGUMENT);
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "%u", &vrf_id))
+       ;
+      else if (not_set)
+       {
+         if (unformat (line_input, "add"))
+           {
+             is_add = true;
+           }
+         else if (unformat (line_input, "del"))
+           {
+             is_add = false;
+           }
+         not_set = false;
+       }
+      else
+       {
+         error = clib_error_return (0, "unknown input '%U'",
+                                    format_unformat_error, line_input);
+         goto done;
+       }
+    }
+
+  if (not_set)
+    {
+      error = clib_error_return (0, "missing required parameter");
+      goto done;
+    }
+
+  if (~0 == vrf_id)
+    {
+      error = clib_error_return (0, "missing vrf id");
+      goto done;
+    }
+
+  rv = nat44_ed_add_del_vrf_table (vrf_id, is_add);
+  if (rv)
+    {
+      error = clib_error_return (0, "%s vrf table returned %d",
+                                is_add ? "add" : "del", rv);
+    }
+
+done:
+  unformat_free (line_input);
+
+  return error;
+}
+
+static clib_error_t *
+nat44_ed_add_del_vrf_route_command_fn (vlib_main_t *vm,
+                                      unformat_input_t *input,
+                                      vlib_cli_command_t *cmd)
+{
+  unformat_input_t _line_input, *line_input = &_line_input;
+  clib_error_t *error = 0;
+  bool is_add = true, not_set = true;
+  u32 vrf_id = ~0, table_vrf_id = ~0;
+  int rv;
+
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return clib_error_return (0, NAT44_ED_EXPECTED_ARGUMENT);
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "table %u", &table_vrf_id))
+       ;
+      else if (unformat (line_input, "%u", &vrf_id))
+       ;
+      else if (not_set)
+       {
+         if (unformat (line_input, "add"))
+           {
+             is_add = true;
+           }
+         else if (unformat (line_input, "del"))
+           {
+             is_add = false;
+           }
+         not_set = false;
+       }
+      else
+       {
+         error = clib_error_return (0, "unknown input '%U'",
+                                    format_unformat_error, line_input);
+         goto done;
+       }
+    }
+
+  if (not_set)
+    {
+      error = clib_error_return (0, "missing required parameter");
+      goto done;
+    }
+
+  if ((~0 == vrf_id) || (~0 == table_vrf_id))
+    {
+      error = clib_error_return (0, "missing vrf id");
+      goto done;
+    }
+
+  rv = nat44_ed_add_del_vrf_route (table_vrf_id, vrf_id, is_add);
+  if (rv)
+    {
+      error = clib_error_return (0, "%s vrf table returned %d",
+                                is_add ? "add" : "del", rv);
+    }
+
+done:
+  unformat_free (line_input);
+
+  return error;
+}
+
+static clib_error_t *
+nat44_ed_show_vrf_tables_command_fn (vlib_main_t *vm, unformat_input_t *input,
+                                    vlib_cli_command_t *cmd)
+{
+  snat_main_t *sm = &snat_main;
+  vrf_table_t *t;
+  vrf_route_t *r;
+  int i = 0;
+
+  pool_foreach (t, sm->vrf_tables)
+    {
+      vlib_cli_output (vm, "table %u:", t->table_vrf_id);
+      pool_foreach (r, t->routes)
+       {
+         vlib_cli_output (vm, "[%u] vrf-id %u", i, r->vrf_id);
+         i++;
+       }
+    }
+
+  return 0;
+}
+
 static clib_error_t *
 nat44_show_interface_address_command_fn (vlib_main_t * vm,
                                         unformat_input_t * input,
@@ -2091,6 +2220,45 @@ VLIB_CLI_COMMAND (snat_add_interface_address_command, static) = {
     .function = snat_add_interface_address_command_fn,
 };
 
+/*?
+ * @cliexpar
+ * @cliexstart{nat44 vrf table}
+ * Add empty inter VRF routing table
+ *  vpp# nat44 vrf table add 10
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (nat44_ed_add_del_vrf_table_command, static) = {
+  .path = "nat44 vrf table",
+  .short_help = "nat44 vrf table [add|del] <vrf-id>",
+  .function = nat44_ed_add_del_vrf_table_command_fn,
+};
+
+/*?
+ * @cliexpar
+ * @cliexstart{nat44 vrf route}
+ * Add inter VRF route record to VRF routing table
+ *  vpp# nat44 vrf route add table 10 20
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (nat44_ed_add_del_vrf_route_command, static) = {
+  .path = "nat44 vrf route",
+  .short_help = "nat44 vrf route [add|del] table <vrf-id> <vrf-id>",
+  .function = nat44_ed_add_del_vrf_route_command_fn,
+};
+
+/*?
+ * @cliexpar
+ * @cliexstart{show nat44 vrf tables}
+ * Show inter VRF route tables
+ *  vpp# show nat44 vrf tables
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (nat44_ed_show_vrf_tables_command, static) = {
+  .path = "show nat44 vrf tables",
+  .short_help = "show nat44 vrf tables",
+  .function = nat44_ed_show_vrf_tables_command_fn,
+};
+
 /*?
  * @cliexpar
  * @cliexstart{show nat44 interface address}
index 6e0a21d..179405e 100644 (file)
@@ -96,74 +96,6 @@ format_nat_in2out_ed_trace (u8 * s, va_list * args)
   return s;
 }
 
-/**
- * @brief Check if packet should be translated
- *
- * Packets aimed at outside interface and external address with active session
- * should be translated.
- *
- * @param sm            NAT main
- * @param rt            NAT runtime data
- * @param sw_if_index0  index of the inside interface
- * @param ip0           IPv4 header
- * @param rx_fib_index0 RX FIB index
- *
- * @returns 0 if packet should be translated otherwise 1
- */
-static inline int
-snat_not_translate_fast (snat_main_t *sm, vlib_node_runtime_t *node,
-                        u32 sw_if_index0, ip4_header_t *ip0,
-                        u32 rx_fib_index0)
-{
-  fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
-  nat_outside_fib_t *outside_fib;
-  fib_prefix_t pfx = {
-    .fp_proto = FIB_PROTOCOL_IP4,
-    .fp_len = 32,
-    .fp_addr = {
-               .ip4.as_u32 = ip0->dst_address.as_u32,
-               }
-    ,
-  };
-
-  /* Don't NAT packet aimed at the intfc address */
-  if (PREDICT_FALSE (
-       is_interface_addr (sm, node, sw_if_index0, ip0->dst_address.as_u32)))
-    return 1;
-
-  fei = fib_table_lookup (rx_fib_index0, &pfx);
-  if (FIB_NODE_INDEX_INVALID != fei)
-    {
-      u32 sw_if_index = fib_entry_get_resolving_interface (fei);
-      if (sw_if_index == ~0)
-       {
-         vec_foreach (outside_fib, sm->outside_fibs)
-           {
-             fei = fib_table_lookup (outside_fib->fib_index, &pfx);
-             if (FIB_NODE_INDEX_INVALID != fei)
-               {
-                 sw_if_index = fib_entry_get_resolving_interface (fei);
-                 if (sw_if_index != ~0)
-                   break;
-               }
-           }
-       }
-      if (sw_if_index == ~0)
-       return 1;
-
-      snat_interface_t *i;
-      pool_foreach (i, sm->interfaces)
-       {
-         /* NAT packet aimed at outside interface */
-         if ((nat44_ed_is_interface_outside (i)) &&
-             (sw_if_index == i->sw_if_index))
-           return 0;
-       }
-    }
-
-  return 1;
-}
-
 static int
 nat_ed_alloc_addr_and_port_with_snat_address (
   snat_main_t *sm, u8 proto, u32 thread_index, snat_address_t *a,
@@ -340,31 +272,6 @@ nat_ed_alloc_addr_and_port (snat_main_t *sm, u32 rx_fib_index,
   return 1;
 }
 
-static_always_inline u32
-nat_outside_fib_index_lookup (snat_main_t * sm, ip4_address_t addr)
-{
-  fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
-  nat_outside_fib_t *outside_fib;
-  fib_prefix_t pfx = {
-    .fp_proto = FIB_PROTOCOL_IP4,
-    .fp_len = 32,
-    .fp_addr = {.ip4.as_u32 = addr.as_u32,}
-    ,
-  };
-  vec_foreach (outside_fib, sm->outside_fibs)
-    {
-      fei = fib_table_lookup (outside_fib->fib_index, &pfx);
-      if (FIB_NODE_INDEX_INVALID != fei)
-        {
-          if (fib_entry_get_resolving_interface (fei) != ~0)
-            {
-              return outside_fib->fib_index;
-            }
-        }
-    }
-  return ~0;
-}
-
 static_always_inline int
 nat44_ed_external_sm_lookup (snat_main_t *sm, ip4_address_t match_addr,
                             u16 match_port, ip_protocol_t match_protocol,
@@ -388,6 +295,131 @@ nat44_ed_external_sm_lookup (snat_main_t *sm, ip4_address_t match_addr,
   return 1;
 }
 
+static_always_inline vrf_table_t *
+get_vrf_table_by_fib (u32 fib_index)
+{
+  snat_main_t *sm = &snat_main;
+  vrf_table_t *t;
+
+  pool_foreach (t, sm->vrf_tables)
+    {
+      if (fib_index == t->table_fib_index)
+       {
+         return t;
+       }
+    }
+
+  return 0;
+}
+
+static_always_inline u32
+get_tx_fib_index (u32 rx_fib_index, ip4_address_t addr)
+{
+  fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
+  fib_prefix_t pfx = {
+    .fp_proto = FIB_PROTOCOL_IP4,
+    .fp_len = 32,
+    .fp_addr = {.ip4.as_u32 = addr.as_u32,}
+    ,
+  };
+
+  snat_main_t *sm = &snat_main;
+  vrf_table_t *t = get_vrf_table_by_fib (rx_fib_index);
+  // default to rx fib
+  u32 tx_fib_index = rx_fib_index;
+
+  if (0 != t)
+    {
+      // managed routes to other fibs
+      vrf_route_t *r;
+      pool_foreach (r, t->routes)
+       {
+         fei = fib_table_lookup (r->fib_index, &pfx);
+         if ((FIB_NODE_INDEX_INVALID != fei) &&
+             (~0 != fib_entry_get_resolving_interface (fei)))
+           {
+             tx_fib_index = r->fib_index;
+             break;
+           }
+       }
+    }
+  else
+    {
+      // default to configured fib
+      tx_fib_index = sm->outside_fib_index;
+
+      // default routes to other fibs
+      nat_fib_t *f;
+      vec_foreach (f, sm->outside_fibs)
+       {
+         fei = fib_table_lookup (f->fib_index, &pfx);
+         if ((FIB_NODE_INDEX_INVALID != fei) &&
+             (~0 != fib_entry_get_resolving_interface (fei)))
+           {
+             tx_fib_index = f->fib_index;
+             break;
+           }
+       }
+    }
+
+  return tx_fib_index;
+}
+
+static_always_inline int
+is_destination_resolvable (u32 rx_fib_index, ip4_address_t addr)
+{
+  fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
+  fib_prefix_t pfx = {
+    .fp_proto = FIB_PROTOCOL_IP4,
+    .fp_len = 32,
+    .fp_addr = {.ip4.as_u32 = addr.as_u32,}
+    ,
+  };
+
+  snat_main_t *sm = &snat_main;
+  vrf_table_t *t = get_vrf_table_by_fib (rx_fib_index);
+  u32 ii;
+
+  if (0 != t)
+    {
+      // managed routes to other fibs
+      vrf_route_t *r;
+      pool_foreach (r, t->routes)
+       {
+         fei = fib_table_lookup (r->fib_index, &pfx);
+         if ((FIB_NODE_INDEX_INVALID != fei) &&
+             (~0 != (ii = fib_entry_get_resolving_interface (fei))))
+           {
+             return 1;
+           }
+       }
+    }
+  else
+    {
+      // default routes to other fibs
+      nat_fib_t *f;
+      vec_foreach (f, sm->outside_fibs)
+       {
+         fei = fib_table_lookup (f->fib_index, &pfx);
+         if ((FIB_NODE_INDEX_INVALID != fei) &&
+             (~0 != (ii = fib_entry_get_resolving_interface (fei))))
+           {
+             snat_interface_t *i;
+             pool_foreach (i, sm->interfaces)
+               {
+                 if ((nat44_ed_is_interface_outside (i)) &&
+                     (ii == i->sw_if_index))
+                   {
+                     return 1;
+                   }
+               }
+           }
+       }
+    }
+
+  return 0;
+}
+
 static u32
 slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b,
              ip4_address_t l_addr, ip4_address_t r_addr, u16 l_port,
@@ -398,7 +430,7 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b,
   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
   ip4_address_t outside_addr;
   u16 outside_port;
-  u32 outside_fib_index;
+  u32 tx_fib_index;
   u8 is_identity_nat = 0;
 
   snat_session_t *s = NULL;
@@ -419,33 +451,14 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b,
        }
     }
 
-  outside_fib_index = sm->outside_fib_index;
-
-  switch (vec_len (sm->outside_fibs))
-    {
-    case 0:
-      outside_fib_index = sm->outside_fib_index;
-      break;
-    case 1:
-      outside_fib_index = sm->outside_fibs[0].fib_index;
-      break;
-    default:
-      outside_fib_index = nat_outside_fib_index_lookup (sm, r_addr);
-      break;
-    }
-
   ip4_address_t sm_addr;
   u16 sm_port;
   u32 sm_fib_index;
-  /* First try to match static mapping by local address and port */
-  int is_sm;
-  if (snat_static_mapping_match (vm, sm, l_addr, l_port, rx_fib_index, proto,
-                                &sm_addr, &sm_port, &sm_fib_index, 0, 0, 0,
-                                &lb, 0, &is_identity_nat, 0))
-    {
-      is_sm = 0;
-    }
-  else
+  int is_sm = 0;
+  // First try to match static mapping by local address and port
+  if (!snat_static_mapping_match (vm, l_addr, l_port, rx_fib_index, proto,
+                                 &sm_addr, &sm_port, &sm_fib_index, 0, 0, 0,
+                                 &lb, 0, &is_identity_nat, 0))
     {
       if (PREDICT_FALSE (is_identity_nat))
        {
@@ -468,21 +481,24 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b,
   s = nat_ed_session_alloc (sm, thread_index, now, proto);
   ASSERT (s);
 
+  tx_fib_index = get_tx_fib_index (rx_fib_index, r_addr);
+
   if (!is_sm)
     {
       s->in2out.addr = l_addr;
       s->in2out.port = l_port;
       s->proto = proto;
       s->in2out.fib_index = rx_fib_index;
-      s->out2in.fib_index = outside_fib_index;
+      s->out2in.fib_index = tx_fib_index;
 
       // suggest using local port to allocation function
       outside_port = l_port;
 
-      // hairpinning?
-      int is_hairpinning = nat44_ed_external_sm_lookup (sm, r_addr, r_port,
-                                                       proto, &daddr, &dport);
-      s->flags |= is_hairpinning * SNAT_SESSION_FLAG_HAIRPINNING;
+      if (PREDICT_FALSE (nat44_ed_external_sm_lookup (sm, r_addr, r_port,
+                                                     proto, &daddr, &dport)))
+       {
+         s->flags |= SNAT_SESSION_FLAG_HAIRPINNING;
+       }
 
       // destination addr/port updated with real values in
       // nat_ed_alloc_addr_and_port
@@ -520,7 +536,7 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b,
       s->in2out.port = l_port;
       s->proto = proto;
       s->in2out.fib_index = rx_fib_index;
-      s->out2in.fib_index = outside_fib_index;
+      s->out2in.fib_index = tx_fib_index;
       s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
 
       // hairpinning?
@@ -568,7 +584,7 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b,
       nat_6t_flow_sport_rewrite_set (&s->i2o, outside_port);
       nat_6t_flow_dport_rewrite_set (&s->i2o, dport);
     }
-  nat_6t_flow_txfib_rewrite_set (&s->i2o, outside_fib_index);
+  nat_6t_flow_txfib_rewrite_set (&s->i2o, tx_fib_index);
 
   if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, 1))
     {
@@ -600,38 +616,55 @@ error:
 }
 
 static_always_inline int
-nat44_ed_not_translate (vlib_main_t *vm, snat_main_t *sm,
-                       vlib_node_runtime_t *node, u32 sw_if_index,
-                       vlib_buffer_t *b, ip4_header_t *ip, u32 proto,
-                       u32 rx_fib_index)
+nat44_ed_not_translate (vlib_main_t *vm, vlib_node_runtime_t *node,
+                       u32 sw_if_index, vlib_buffer_t *b, ip4_header_t *ip,
+                       u32 proto, u32 rx_fib_index)
 {
+  snat_main_t *sm = &snat_main;
+
   clib_bihash_kv_16_8_t kv, value;
+  ip4_address_t placeholder_addr;
+  u32 placeholder_fib_index;
+  u16 placeholder_port;
 
   init_ed_k (&kv, ip->dst_address.as_u32,
             vnet_buffer (b)->ip.reass.l4_dst_port, ip->src_address.as_u32,
             vnet_buffer (b)->ip.reass.l4_src_port, sm->outside_fib_index,
             ip->protocol);
 
-  /* NAT packet aimed at external address if has active sessions */
-  if (clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value))
+  // do nat if active session or is static mapping
+  if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value) ||
+      !snat_static_mapping_match (
+       vm, ip->dst_address, vnet_buffer (b)->ip.reass.l4_dst_port,
+       sm->outside_fib_index, proto, &placeholder_addr, &placeholder_port,
+       &placeholder_fib_index, 1, 0, 0, 0, 0, 0, 0))
     {
-      /* or is static mappings */
-      ip4_address_t placeholder_addr;
-      u16 placeholder_port;
-      u32 placeholder_fib_index;
-      if (!snat_static_mapping_match (
-           vm, sm, ip->dst_address, vnet_buffer (b)->ip.reass.l4_dst_port,
-           sm->outside_fib_index, proto, &placeholder_addr, &placeholder_port,
-           &placeholder_fib_index, 1, 0, 0, 0, 0, 0, 0))
-       return 0;
+      return 0;
     }
-  else
-    return 0;
 
+  // do not nat if forwarding enabled
   if (sm->forwarding_enabled)
-    return 1;
+    {
+      return 1;
+    }
 
-  return snat_not_translate_fast (sm, node, sw_if_index, ip, rx_fib_index);
+  // do not nat packet aimed at the interface address
+  if (PREDICT_FALSE (
+       is_interface_addr (sm, node, sw_if_index, ip->dst_address.as_u32)))
+    {
+      return 1;
+    }
+
+  // do nat packets with resolvable destination
+  // destination can be resolved either by:
+  // a) vrf routing table entry
+  // b) (non output feature) outside interface fib
+  if (is_destination_resolvable (rx_fib_index, ip->dst_address))
+    {
+      return 0;
+    }
+
+  return 1;
 }
 
 static_always_inline int
@@ -810,7 +843,7 @@ icmp_in2out_ed_slow_path (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip,
   else
     {
       if (PREDICT_FALSE (nat44_ed_not_translate (
-           vm, sm, node, sw_if_index, b, ip, IP_PROTOCOL_ICMP, rx_fib_index)))
+           vm, node, sw_if_index, b, ip, IP_PROTOCOL_ICMP, rx_fib_index)))
        {
          return next;
        }
@@ -868,7 +901,7 @@ nat44_ed_in2out_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b,
   snat_static_mapping_t *m = NULL;
   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
   snat_session_t *s = NULL;
-  u32 outside_fib_index = sm->outside_fib_index;
+  u32 tx_fib_index;
   int i;
   ip4_address_t new_src_addr = { 0 };
   ip4_address_t new_dst_addr = ip->dst_address;
@@ -883,20 +916,9 @@ nat44_ed_in2out_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b,
       return 0;
     }
 
-  switch (vec_len (sm->outside_fibs))
-    {
-    case 0:
-      outside_fib_index = sm->outside_fib_index;
-      break;
-    case 1:
-      outside_fib_index = sm->outside_fibs[0].fib_index;
-      break;
-    default:
-      outside_fib_index = nat_outside_fib_index_lookup (sm, ip->dst_address);
-      break;
-    }
+  tx_fib_index = get_tx_fib_index (rx_fib_index, ip->dst_address);
 
-  /* Try to find static mapping first */
+  // Try to find static mapping first
   m = nat44_ed_sm_i2o_lookup (sm, ip->src_address, 0, rx_fib_index,
                              ip->protocol);
   if (m)
@@ -910,7 +932,7 @@ nat44_ed_in2out_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b,
          if (s->ext_host_addr.as_u32 == ip->dst_address.as_u32)
            {
              init_ed_k (&s_kv, s->out2in.addr.as_u32, 0,
-                        ip->dst_address.as_u32, 0, outside_fib_index,
+                        ip->dst_address.as_u32, 0, tx_fib_index,
                         ip->protocol);
              if (clib_bihash_search_16_8 (&sm->flow_hash, &s_kv, &s_value))
                {
@@ -925,7 +947,7 @@ nat44_ed_in2out_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b,
          for (i = 0; i < vec_len (sm->addresses); i++)
            {
              init_ed_k (&s_kv, sm->addresses[i].addr.as_u32, 0,
-                        ip->dst_address.as_u32, 0, outside_fib_index,
+                        ip->dst_address.as_u32, 0, tx_fib_index,
                         ip->protocol);
              if (clib_bihash_search_16_8 (&sm->flow_hash, &s_kv, &s_value))
                {
@@ -952,7 +974,7 @@ nat44_ed_in2out_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b,
   nat_6t_i2o_flow_init (sm, thread_index, s, ip->src_address, 0,
                        ip->dst_address, 0, rx_fib_index, ip->protocol);
   nat_6t_flow_saddr_rewrite_set (&s->i2o, new_src_addr.as_u32);
-  nat_6t_flow_txfib_rewrite_set (&s->i2o, outside_fib_index);
+  nat_6t_flow_txfib_rewrite_set (&s->i2o, tx_fib_index);
 
   // hairpinning?
   int is_hairpinning = nat44_ed_external_sm_lookup (
@@ -960,17 +982,17 @@ nat44_ed_in2out_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b,
   s->flags |= is_hairpinning * SNAT_SESSION_FLAG_HAIRPINNING;
 
   nat_6t_flow_daddr_rewrite_set (&s->i2o, new_dst_addr.as_u32);
-  nat_6t_flow_txfib_rewrite_set (&s->i2o, outside_fib_index);
+  nat_6t_flow_txfib_rewrite_set (&s->i2o, tx_fib_index);
 
   nat_6t_o2i_flow_init (sm, thread_index, s, new_dst_addr, 0, new_src_addr, 0,
-                       outside_fib_index, ip->protocol);
+                       tx_fib_index, ip->protocol);
   nat_6t_flow_saddr_rewrite_set (&s->o2i, ip->dst_address.as_u32);
   nat_6t_flow_daddr_rewrite_set (&s->o2i, ip->src_address.as_u32);
   nat_6t_flow_txfib_rewrite_set (&s->o2i, rx_fib_index);
 
   s->ext_host_addr.as_u32 = ip->dst_address.as_u32;
   s->out2in.addr.as_u32 = new_src_addr.as_u32;
-  s->out2in.fib_index = outside_fib_index;
+  s->out2in.fib_index = tx_fib_index;
   s->in2out.addr.as_u32 = ip->src_address.as_u32;
   s->in2out.fib_index = rx_fib_index;
   s->in2out.port = s->out2in.port = ip->protocol;
@@ -1445,8 +1467,8 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t *vm,
          else
            {
              if (PREDICT_FALSE (
-                   nat44_ed_not_translate (vm, sm, node, rx_sw_if_index0, b0,
-                                           ip0, proto0, rx_fib_index0)))
+                   nat44_ed_not_translate (vm, node, rx_sw_if_index0, b0, ip0,
+                                           proto0, rx_fib_index0)))
                goto trace0;
            }
 
index aa0cc50..e53d9c5 100644 (file)
@@ -182,7 +182,7 @@ icmp_out2in_ed_slow_path (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip,
       goto out;
     }
 
-  if (snat_static_mapping_match (vm, sm, ip->dst_address, lookup_sport,
+  if (snat_static_mapping_match (vm, ip->dst_address, lookup_sport,
                                 rx_fib_index, ip->protocol, &sm_addr,
                                 &sm_port, &sm_fib_index, 1, &is_addr_only, 0,
                                 0, 0, &identity_nat, &m))
@@ -1164,10 +1164,9 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm,
             destination address and port in packet */
 
          if (snat_static_mapping_match (
-               vm, sm, ip0->dst_address,
-               vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0, proto0,
-               &sm_addr, &sm_port, &sm_fib_index, 1, 0, &twice_nat0, &lb_nat0,
-               &ip0->src_address, &identity_nat0, &m))
+               vm, ip0->dst_address, vnet_buffer (b0)->ip.reass.l4_dst_port,
+               rx_fib_index0, proto0, &sm_addr, &sm_port, &sm_fib_index, 1, 0,
+               &twice_nat0, &lb_nat0, &ip0->src_address, &identity_nat0, &m))
            {
              /*
               * Send DHCP packets to the ipv4 stack, or we won't