nat: nat44-ed pool address allocation improvement 63/34363/11
authorFilip Varga <fivarga@cisco.com>
Mon, 4 Oct 2021 12:36:05 +0000 (14:36 +0200)
committerOle Tr�an <otroan@employees.org>
Tue, 4 Jan 2022 12:03:17 +0000 (12:03 +0000)
This patch improves algorithm responsible for choosing nat pool address
during dynamic session creation.

Patch synchronizes nat pool address attributes with vpp interfaces doing
so gives nat option to determine correct nat pool address  when creating
dynamic session.

Improvements:
    * match dst address subnet and src fib with nat pool address
    * for output-feature also fallback match of tx_sw_if_index with nat
      pool address

Type: improvement

Change-Id: I594638bc76fc1153f582add376838b4b72ff573a
Signed-off-by: Filip Varga <fivarga@cisco.com>
src/plugins/nat/nat44-ed/nat44_ed.c
src/plugins/nat/nat44-ed/nat44_ed.h
src/plugins/nat/nat44-ed/nat44_ed_cli.c
src/plugins/nat/nat44-ed/nat44_ed_in2out.c

index 56c5ab2..7907d8f 100644 (file)
@@ -309,6 +309,112 @@ is_snat_address_used_in_static_mapping (snat_main_t *sm, ip4_address_t addr)
   return 0;
 }
 
+static ip_interface_address_t *
+nat44_ed_get_ip_interface_address (u32 sw_if_index, ip4_address_t addr)
+{
+  snat_main_t *sm = &snat_main;
+
+  ip_lookup_main_t *lm = &sm->ip4_main->lookup_main;
+  ip_interface_address_t *ia;
+  ip4_address_t *ip4a;
+
+  foreach_ip_interface_address (
+    lm, ia, sw_if_index, 1, ({
+      ip4a = ip_interface_address_get_address (lm, ia);
+      nat_log_debug ("sw_if_idx: %u addr: %U ? %U", sw_if_index,
+                    format_ip4_address, ip4a, format_ip4_address, &addr);
+      if (ip4a->as_u32 == addr.as_u32)
+       {
+         return ia;
+       }
+    }));
+  return NULL;
+}
+
+static int
+nat44_ed_resolve_nat_addr_len (snat_address_t *ap,
+                              snat_interface_t *interfaces)
+{
+  ip_interface_address_t *ia;
+  snat_interface_t *i;
+  u32 fib_index;
+
+  pool_foreach (i, interfaces)
+    {
+      if (!nat44_ed_is_interface_outside (i))
+       {
+         continue;
+       }
+
+      fib_index = ip4_fib_table_get_index_for_sw_if_index (i->sw_if_index);
+      if (fib_index != ap->fib_index)
+       {
+         continue;
+       }
+
+      if ((ia = nat44_ed_get_ip_interface_address (i->sw_if_index, ap->addr)))
+       {
+         ap->addr_len = ia->address_length;
+         ap->sw_if_index = i->sw_if_index;
+         ap->net.as_u32 = (ap->addr.as_u32 >> (32 - ap->addr_len))
+                          << (32 - ap->addr_len);
+
+         nat_log_debug ("pool addr %U binds to -> sw_if_idx: %u net: %U/%u",
+                        format_ip4_address, &ap->addr, ap->sw_if_index,
+                        format_ip4_address, &ap->net, ap->addr_len);
+         return 0;
+       }
+    }
+  return 1;
+}
+
+static void
+nat44_ed_update_outside_if_addresses (snat_address_t *ap)
+{
+  snat_main_t *sm = &snat_main;
+
+  if (!nat44_ed_resolve_nat_addr_len (ap, sm->interfaces))
+    {
+      return;
+    }
+
+  if (!nat44_ed_resolve_nat_addr_len (ap, sm->output_feature_interfaces))
+    {
+      return;
+    }
+}
+
+static void
+nat44_ed_bind_if_addr_to_nat_addr (u32 sw_if_index)
+{
+  snat_main_t *sm = &snat_main;
+  ip_interface_address_t *ia;
+  snat_address_t *ap;
+
+  u32 fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
+
+  vec_foreach (ap, sm->addresses)
+    {
+      if (fib_index != ap->fib_index)
+       {
+         continue;
+       }
+
+      if ((ia = nat44_ed_get_ip_interface_address (sw_if_index, ap->addr)))
+       {
+         ap->addr_len = ia->address_length;
+         ap->sw_if_index = sw_if_index;
+         ap->net.as_u32 = (ap->addr.as_u32 >> (32 - ap->addr_len))
+                          << (32 - ap->addr_len);
+
+         nat_log_debug ("pool addr %U binds to -> sw_if_idx: %u net: %U/%u",
+                        format_ip4_address, &ap->addr, ap->sw_if_index,
+                        format_ip4_address, &ap->net, ap->addr_len);
+         return;
+       }
+    }
+}
+
 static void
 nat44_ed_add_del_addr_to_fib (ip4_address_t *addr, u8 p_len, u32 sw_if_index,
                              int is_add)
@@ -424,6 +530,7 @@ nat44_ed_add_address (ip4_address_t *addr, u32 vrf_id, u8 twice_nat)
       vec_add2 (sm->addresses, ap, 1);
     }
 
+  ap->addr_len = ~0;
   ap->fib_index = ~0;
   ap->addr = *addr;
 
@@ -433,12 +540,13 @@ nat44_ed_add_address (ip4_address_t *addr, u32 vrf_id, u8 twice_nat)
        FIB_PROTOCOL_IP4, vrf_id, sm->fib_src_low);
     }
 
-    if (!twice_nat)
-  {
-    // if we don't have enabled interface we don't add address
-    // to fib
-    nat44_ed_add_del_addr_to_fib_foreach_out_if (addr, 1);
-  }
+  if (!twice_nat)
+    {
+      // if we don't have enabled interface we don't add address
+      // to fib
+      nat44_ed_add_del_addr_to_fib_foreach_out_if (addr, 1);
+      nat44_ed_update_outside_if_addresses (ap);
+    }
   return 0;
 }
 
@@ -1641,6 +1749,8 @@ nat44_ed_add_interface (u32 sw_if_index, u8 is_inside)
 
       nat44_ed_add_del_addr_to_fib_foreach_addr (sw_if_index, 1);
       nat44_ed_add_del_addr_to_fib_foreach_addr_only_sm (sw_if_index, 1);
+
+      nat44_ed_bind_if_addr_to_nat_addr (sw_if_index);
     }
   else
     {
@@ -1850,6 +1960,8 @@ nat44_ed_add_output_interface (u32 sw_if_index)
   nat44_ed_add_del_addr_to_fib_foreach_addr (sw_if_index, 1);
   nat44_ed_add_del_addr_to_fib_foreach_addr_only_sm (sw_if_index, 1);
 
+  nat44_ed_bind_if_addr_to_nat_addr (sw_if_index);
+
   return 0;
 }
 
@@ -3244,7 +3356,7 @@ nat44_ed_add_del_interface_address_cb (ip4_main_t *im, uword opaque,
 {
   snat_main_t *sm = &snat_main;
   snat_static_map_resolve_t *rp;
-  snat_address_t *addresses = sm->addresses;
+  snat_address_t *ap, *addresses = sm->addresses;
   u8 twice_nat = 0;
   int rv, i;
 
@@ -3259,6 +3371,33 @@ nat44_ed_add_del_interface_address_cb (ip4_main_t *im, uword opaque,
       if (!is_sw_if_index_reg_for_auto_resolve (
            sm->auto_add_sw_if_indices_twice_nat, sw_if_index))
        {
+         // interface resolve
+         u32 fib_index =
+           ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
+         vec_foreach (ap, sm->addresses)
+           {
+             if ((fib_index == ap->fib_index) &&
+                 (address->as_u32 == ap->addr.as_u32))
+               {
+                 if (!is_delete)
+                   {
+                     ap->addr_len = address_length;
+                     ap->sw_if_index = sw_if_index;
+                     ap->net.as_u32 = (ap->addr.as_u32 >> (32 - ap->addr_len))
+                                      << (32 - ap->addr_len);
+
+                     nat_log_debug (
+                       "pool addr %U binds to -> sw_if_idx: %u net: %U/%u",
+                       format_ip4_address, &ap->addr, ap->sw_if_index,
+                       format_ip4_address, &ap->net, ap->addr_len);
+                   }
+                 else
+                   {
+                     ap->addr_len = ~0;
+                   }
+                 break;
+               }
+           }
          return;
        }
       else
index 9fb34aa..d8cc0d3 100644 (file)
@@ -359,7 +359,10 @@ typedef CLIB_PACKED(struct
 typedef struct
 {
   ip4_address_t addr;
+  ip4_address_t net;
+  u32 sw_if_index;
   u32 fib_index;
+  u32 addr_len;
 } snat_address_t;
 
 typedef struct
index ba74b7d..9743ce6 100644 (file)
@@ -632,10 +632,14 @@ nat44_show_addresses_command_fn (vlib_main_t * vm, unformat_input_t * input,
     {
       vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr);
       if (ap->fib_index != ~0)
-          vlib_cli_output (vm, "  tenant VRF: %u",
-            fib_table_get(ap->fib_index, FIB_PROTOCOL_IP4)->ft_table_id);
+       vlib_cli_output (
+         vm, "  tenant VRF: %u",
+         fib_table_get (ap->fib_index, FIB_PROTOCOL_IP4)->ft_table_id);
       else
         vlib_cli_output (vm, "  tenant VRF independent");
+
+      if (ap->addr_len != ~0)
+       vlib_cli_output (vm, "  synced with interface address");
     }
   vlib_cli_output (vm, "NAT44 twice-nat pool addresses:");
   vec_foreach (ap, sm->twice_nat_addresses)
@@ -646,6 +650,9 @@ nat44_show_addresses_command_fn (vlib_main_t * vm, unformat_input_t * input,
             fib_table_get(ap->fib_index, FIB_PROTOCOL_IP4)->ft_table_id);
       else
         vlib_cli_output (vm, "  tenant VRF independent");
+
+      if (ap->addr_len != ~0)
+       vlib_cli_output (vm, "  synced with interface address");
     }
   return 0;
 }
index 73dacce..e93198a 100644 (file)
@@ -201,50 +201,129 @@ nat_ed_alloc_addr_and_port_with_snat_address (
 }
 
 static int
-nat_ed_alloc_addr_and_port (snat_main_t *sm, u32 rx_fib_index, u32 nat_proto,
+nat_ed_alloc_addr_and_port (snat_main_t *sm, u32 rx_fib_index,
+                           u32 tx_sw_if_index, u32 nat_proto,
                            u32 thread_index, ip4_address_t s_addr,
-                           u32 snat_thread_index, snat_session_t *s,
-                           ip4_address_t *outside_addr, u16 *outside_port)
+                           ip4_address_t d_addr, u32 snat_thread_index,
+                           snat_session_t *s, ip4_address_t *outside_addr,
+                           u16 *outside_port)
 {
-  int i;
-  snat_address_t *a, *ga = 0;
-
   if (vec_len (sm->addresses) > 0)
     {
       u32 s_addr_offset = s_addr.as_u32 % vec_len (sm->addresses);
+      snat_address_t *a, *ja = 0, *ra = 0, *ba = 0;
+      int i;
 
-      for (i = s_addr_offset; i < vec_len (sm->addresses); ++i)
+      // output feature
+      if (tx_sw_if_index != ~0)
        {
-         a = sm->addresses + i;
-         if (a->fib_index == rx_fib_index)
+         for (i = s_addr_offset; i < vec_len (sm->addresses); ++i)
            {
-             return nat_ed_alloc_addr_and_port_with_snat_address (
-               sm, nat_proto, thread_index, a, sm->port_per_thread,
-               snat_thread_index, s, outside_addr, outside_port);
+             a = sm->addresses + i;
+             if (a->fib_index == rx_fib_index)
+               {
+                 if (a->sw_if_index == tx_sw_if_index)
+                   {
+                     if ((a->addr_len != ~0) &&
+                         (a->net.as_u32 ==
+                          (d_addr.as_u32 & ip4_main.fib_masks[a->addr_len])))
+
+                       {
+                         return nat_ed_alloc_addr_and_port_with_snat_address (
+                           sm, nat_proto, thread_index, a,
+                           sm->port_per_thread, snat_thread_index, s,
+                           outside_addr, outside_port);
+                       }
+                     ra = a;
+                   }
+                 ja = a;
+               }
+             else if (a->fib_index == ~0)
+               {
+                 ba = a;
+               }
            }
-         else if (a->fib_index == ~0)
+         for (i = 0; i < s_addr_offset; ++i)
            {
-             ga = a;
+             a = sm->addresses + i;
+             if (a->fib_index == rx_fib_index)
+               {
+                 if (a->sw_if_index == tx_sw_if_index)
+                   {
+                     if ((a->addr_len != ~0) &&
+                         (a->net.as_u32 ==
+                          (d_addr.as_u32 & ip4_main.fib_masks[a->addr_len])))
+
+                       {
+                         return nat_ed_alloc_addr_and_port_with_snat_address (
+                           sm, nat_proto, thread_index, a,
+                           sm->port_per_thread, snat_thread_index, s,
+                           outside_addr, outside_port);
+                       }
+                     ra = a;
+                   }
+                 ja = a;
+               }
+             else if (a->fib_index == ~0)
+               {
+                 ba = a;
+               }
            }
-       }
-
-      for (i = 0; i < s_addr_offset; ++i)
-       {
-         a = sm->addresses + i;
-         if (a->fib_index == rx_fib_index)
+         if (ra)
            {
              return nat_ed_alloc_addr_and_port_with_snat_address (
-               sm, nat_proto, thread_index, a, sm->port_per_thread,
+               sm, nat_proto, thread_index, ra, sm->port_per_thread,
                snat_thread_index, s, outside_addr, outside_port);
            }
-         else if (a->fib_index == ~0)
+       }
+      else
+       {
+         // frist try nat pool addresses to sw interface addreses mappings
+         for (i = s_addr_offset; i < vec_len (sm->addresses); ++i)
            {
-             ga = a;
+             a = sm->addresses + i;
+             if (a->fib_index == rx_fib_index)
+               {
+                 if ((a->addr_len != ~0) &&
+                     (a->net.as_u32 ==
+                      (d_addr.as_u32 & ip4_main.fib_masks[a->addr_len])))
+                   {
+                     return nat_ed_alloc_addr_and_port_with_snat_address (
+                       sm, nat_proto, thread_index, a, sm->port_per_thread,
+                       snat_thread_index, s, outside_addr, outside_port);
+                   }
+                 ja = a;
+               }
+             else if (a->fib_index == ~0)
+               {
+                 ba = a;
+               }
+           }
+         for (i = 0; i < s_addr_offset; ++i)
+           {
+             a = sm->addresses + i;
+             if (a->fib_index == rx_fib_index)
+               {
+                 if ((a->addr_len != ~0) &&
+                     (a->net.as_u32 ==
+                      (d_addr.as_u32 & ip4_main.fib_masks[a->addr_len])))
+                   {
+                     return nat_ed_alloc_addr_and_port_with_snat_address (
+                       sm, nat_proto, thread_index, a, sm->port_per_thread,
+                       snat_thread_index, s, outside_addr, outside_port);
+                   }
+                 ja = a;
+               }
+             else if (a->fib_index == ~0)
+               {
+                 ba = a;
+               }
            }
        }
 
-      if (ga)
+      if (ja || ba)
        {
+         a = ja ? ja : ba;
          return nat_ed_alloc_addr_and_port_with_snat_address (
            sm, nat_proto, thread_index, a, sm->port_per_thread,
            snat_thread_index, s, outside_addr, outside_port);
@@ -266,7 +345,6 @@ nat_outside_fib_index_lookup (snat_main_t * sm, ip4_address_t addr)
     .fp_addr = {.ip4.as_u32 = addr.as_u32,}
     ,
   };
-  // TODO: multiple vrfs none can resolve addr
   vec_foreach (outside_fib, sm->outside_fibs)
     {
       fei = fib_table_lookup (outside_fib->fib_index, &pfx);
@@ -307,7 +385,7 @@ nat44_ed_external_sm_lookup (snat_main_t *sm, ip4_address_t match_addr,
 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,
-             u16 r_port, u8 proto, u32 rx_fib_index,
+             u16 r_port, u8 proto, u32 rx_fib_index, u32 tx_sw_if_index,
              snat_session_t **sessionp, vlib_node_runtime_t *node, u32 next,
              u32 thread_index, f64 now)
 {
@@ -415,9 +493,9 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b,
        }
       nat_6t_flow_txfib_rewrite_set (&s->o2i, rx_fib_index);
 
-      if (nat_ed_alloc_addr_and_port (sm, rx_fib_index, proto, thread_index,
-                                     l_addr, tsm->snat_thread_index, s,
-                                     &outside_addr, &outside_port))
+      if (nat_ed_alloc_addr_and_port (
+           sm, rx_fib_index, tx_sw_if_index, proto, thread_index, l_addr,
+           r_addr, tsm->snat_thread_index, s, &outside_addr, &outside_port))
        {
          nat_elog_notice (sm, "addresses exhausted");
          b->error = node->errors[NAT_IN2OUT_ED_ERROR_OUT_OF_PORTS];
@@ -682,11 +760,11 @@ nat44_ed_not_translate_output_feature (snat_main_t *sm, vlib_buffer_t *b,
 
       /* hairpinning */
       pool_foreach (i, sm->output_feature_interfaces)
-       {
-        if ((nat44_ed_is_interface_inside (i)) &&
-            (rx_sw_if_index == i->sw_if_index))
-          return 0;
-      }
+       {
+         if ((nat44_ed_is_interface_inside (i)) &&
+             (rx_sw_if_index == i->sw_if_index))
+           return 0;
+       }
       return 1;
     }
 
@@ -696,9 +774,10 @@ nat44_ed_not_translate_output_feature (snat_main_t *sm, vlib_buffer_t *b,
 static inline u32
 icmp_in2out_ed_slow_path (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip,
                          icmp46_header_t *icmp, u32 sw_if_index,
-                         u32 rx_fib_index, vlib_node_runtime_t *node,
-                         u32 next, f64 now, u32 thread_index,
-                         snat_session_t **s_p, int is_multi_worker)
+                         u32 tx_sw_if_index, u32 rx_fib_index,
+                         vlib_node_runtime_t *node, u32 next, f64 now,
+                         u32 thread_index, snat_session_t **s_p,
+                         int is_multi_worker)
 {
   vlib_main_t *vm = vlib_get_main ();
   u16 checksum;
@@ -717,11 +796,11 @@ icmp_in2out_ed_slow_path (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip,
       return NAT_NEXT_DROP;
     }
 
-  if (vnet_buffer (b)->sw_if_index[VLIB_TX] != ~0)
+  if (tx_sw_if_index != ~0)
     {
       if (PREDICT_FALSE (nat44_ed_not_translate_output_feature (
            sm, b, ip, lookup_sport, lookup_dport, thread_index, sw_if_index,
-           vnet_buffer (b)->sw_if_index[VLIB_TX], now, is_multi_worker)))
+           tx_sw_if_index, now, is_multi_worker)))
        {
          return next;
        }
@@ -742,9 +821,10 @@ icmp_in2out_ed_slow_path (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip,
       return NAT_NEXT_DROP;
     }
 
-  next = slow_path_ed (vm, sm, b, ip->src_address, ip->dst_address,
-                      lookup_sport, lookup_dport, ip->protocol, rx_fib_index,
-                      &s, node, next, thread_index, vlib_time_now (vm));
+  next =
+    slow_path_ed (vm, sm, b, ip->src_address, ip->dst_address, lookup_sport,
+                 lookup_dport, ip->protocol, rx_fib_index, tx_sw_if_index, &s,
+                 node, next, thread_index, vlib_time_now (vm));
 
   if (NAT_NEXT_DROP == next)
     goto out;
@@ -1316,8 +1396,9 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t *vm,
       if (PREDICT_FALSE (proto0 == IP_PROTOCOL_ICMP))
        {
          next[0] = icmp_in2out_ed_slow_path (
-           sm, b0, ip0, icmp0, rx_sw_if_index0, rx_fib_index0, node, next[0],
-           now, thread_index, &s0, is_multi_worker);
+           sm, b0, ip0, icmp0, rx_sw_if_index0, tx_sw_if_index0,
+           rx_fib_index0, node, next[0], now, thread_index, &s0,
+           is_multi_worker);
          if (NAT_NEXT_DROP != next[0] && s0 &&
              NAT_ED_TRNSL_ERR_SUCCESS !=
                (translation_error = nat_6t_flow_buf_translate_i2o (
@@ -1383,11 +1464,12 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t *vm,
                goto trace0;
            }
 
-         next[0] = slow_path_ed (
-           vm, sm, b0, ip0->src_address, ip0->dst_address,
-           vnet_buffer (b0)->ip.reass.l4_src_port,
-           vnet_buffer (b0)->ip.reass.l4_dst_port, ip0->protocol,
-           rx_fib_index0, &s0, node, next[0], thread_index, now);
+         next[0] =
+           slow_path_ed (vm, sm, b0, ip0->src_address, ip0->dst_address,
+                         vnet_buffer (b0)->ip.reass.l4_src_port,
+                         vnet_buffer (b0)->ip.reass.l4_dst_port,
+                         ip0->protocol, rx_fib_index0, tx_sw_if_index0, &s0,
+                         node, next[0], thread_index, now);
 
          if (PREDICT_FALSE (next[0] == NAT_NEXT_DROP))
            goto trace0;