cnat: Fix snat with dhcp
[vpp.git] / src / plugins / cnat / cnat_snat.c
index 86fac12..b063715 100644 (file)
 
 #include <vnet/ip/ip.h>
 #include <cnat/cnat_snat.h>
+#include <cnat/cnat_translation.h>
+
+cnat_snat_policy_main_t cnat_snat_policy_main;
+
+void
+cnat_set_snat_policy (cnat_snat_policy_t fp)
+{
+  cnat_snat_policy_main.snat_policy = fp;
+}
+
+static clib_error_t *
+cnat_snat_policy_cmd (vlib_main_t *vm, unformat_input_t *input,
+                     vlib_cli_command_t *cmd)
+{
+  cnat_snat_policy_t fp = NULL;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "none"))
+       ;
+      else
+       return clib_error_return (0, "unknown input '%U'",
+                                 format_unformat_error, input);
+    }
+
+  cnat_set_snat_policy (fp);
+  return NULL;
+}
+
+VLIB_CLI_COMMAND (cnat_snat_policy_command, static) = {
+  .path = "cnat set snat policy",
+  .short_help = "cnat set snat policy {none,k8s}",
+  .function = cnat_snat_policy_cmd,
+};
+
+static clib_error_t *
+show_cnat_snat_policy_cmd (vlib_main_t *vm, unformat_input_t *input,
+                          vlib_cli_command_t *cmd)
+{
+  u8 *s = format (NULL, "snat policy: ");
+  if (cnat_snat_policy_main.snat_policy == NULL)
+    s = format (s, "none");
+  else
+    s = format (s, "unknown (%x)", cnat_snat_policy_main.snat_policy);
+
+  vlib_cli_output (vm, (char *) s);
+  return NULL;
+}
+
+VLIB_CLI_COMMAND (show_cnat_snat_policy_command, static) = {
+  .path = "show cnat snat policy",
+  .short_help = "show cnat snat policy",
+  .function = show_cnat_snat_policy_cmd,
+};
 
 static void
 cnat_compute_prefix_lengths_in_search_order (cnat_snat_pfx_table_t *
@@ -23,13 +77,11 @@ cnat_compute_prefix_lengths_in_search_order (cnat_snat_pfx_table_t *
   int i;
   vec_reset_length (table->meta[af].prefix_lengths_in_search_order);
   /* Note: bitmap reversed so this is in fact a longest prefix match */
-  /* *INDENT-OFF* */
-  clib_bitmap_foreach (i, table->meta[af].non_empty_dst_address_length_bitmap,
-    ({
+  clib_bitmap_foreach (i, table->meta[af].non_empty_dst_address_length_bitmap)
+     {
       int dst_address_length = 128 - i;
       vec_add1 (table->meta[af].prefix_lengths_in_search_order, dst_address_length);
-    }));
-  /* *INDENT-ON* */
+    }
 }
 
 int
@@ -101,6 +153,48 @@ cnat_del_snat_prefix (ip_prefix_t * pfx)
   return 0;
 }
 
+int
+cnat_search_snat_prefix (ip46_address_t * addr, ip_address_family_t af)
+{
+  /* Returns 0 if addr matches any of the listed prefixes */
+  cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
+  clib_bihash_kv_24_8_t kv, val;
+  int i, n_p, rv;
+  n_p = vec_len (table->meta[af].prefix_lengths_in_search_order);
+  if (AF_IP4 == af)
+    {
+      kv.key[0] = addr->ip4.as_u32;
+      kv.key[1] = 0;
+    }
+  else
+    {
+      kv.key[0] = addr->as_u64[0];
+      kv.key[1] = addr->as_u64[1];
+    }
+
+  /*
+   * start search from a mask length same length or shorter.
+   * we don't want matches longer than the mask passed
+   */
+  i = 0;
+  for (; i < n_p; i++)
+    {
+      int dst_address_length =
+       table->meta[af].prefix_lengths_in_search_order[i];
+      ip6_address_t *mask = &table->ip_masks[dst_address_length];
+
+      ASSERT (dst_address_length >= 0 && dst_address_length <= 128);
+      /* As lengths are decreasing, masks are increasingly specific. */
+      kv.key[0] &= mask->as_u64[0];
+      kv.key[1] &= mask->as_u64[1];
+      kv.key[2] = ((u64) af << 32) | dst_address_length;
+      rv = clib_bihash_search_inline_2_24_8 (&table->ip_hash, &kv, &val);
+      if (rv == 0)
+       return 0;
+    }
+  return -1;
+}
+
 u8 *
 format_cnat_snat_prefix (u8 * s, va_list * args)
 {
@@ -115,13 +209,36 @@ format_cnat_snat_prefix (u8 * s, va_list * args)
   return (s);
 }
 
+void
+cnat_set_snat (ip4_address_t * ip4, ip6_address_t * ip6, u32 sw_if_index)
+{
+  cnat_lazy_init ();
+
+  cnat_translation_unwatch_addr (INDEX_INVALID, CNAT_RESOLV_ADDR_SNAT);
+
+  ip_address_set (&cnat_main.snat_ip4.ce_ip, ip4, AF_IP4);
+  ip_address_set (&cnat_main.snat_ip6.ce_ip, ip6, AF_IP6);
+  cnat_main.snat_ip4.ce_sw_if_index = sw_if_index;
+  cnat_main.snat_ip6.ce_sw_if_index = sw_if_index;
+
+  cnat_resolve_ep (&cnat_main.snat_ip4);
+  cnat_resolve_ep (&cnat_main.snat_ip6);
+  cnat_translation_watch_addr (INDEX_INVALID, 0, &cnat_main.snat_ip4,
+                              CNAT_RESOLV_ADDR_SNAT);
+  cnat_translation_watch_addr (INDEX_INVALID, 0, &cnat_main.snat_ip6,
+                              CNAT_RESOLV_ADDR_SNAT);
+}
+
 static clib_error_t *
-cnat_set_snat (vlib_main_t * vm,
-              unformat_input_t * input, vlib_cli_command_t * cmd)
+cnat_set_snat_cli (vlib_main_t * vm,
+                  unformat_input_t * input, vlib_cli_command_t * cmd)
 {
   unformat_input_t _line_input, *line_input = &_line_input;
+  vnet_main_t *vnm = vnet_get_main ();
+  ip4_address_t ip4 = { {0} };
+  ip6_address_t ip6 = { {0} };
   clib_error_t *e = 0;
-  ip_address_t addr;
+  u32 sw_if_index = INDEX_INVALID;
 
   cnat_lazy_init ();
 
@@ -131,15 +248,13 @@ cnat_set_snat (vlib_main_t * vm,
 
   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     {
-      if (unformat (line_input, "%U", unformat_ip_address, &addr))
-       {
-         if (ip_addr_version (&addr) == AF_IP4)
-           clib_memcpy (&cnat_main.snat_ip4, &ip_addr_v4 (&addr),
-                        sizeof (ip4_address_t));
-         else
-           clib_memcpy (&cnat_main.snat_ip6, &ip_addr_v6 (&addr),
-                        sizeof (ip6_address_t));
-       }
+      if (unformat_user (line_input, unformat_ip4_address, &ip4))
+       ;
+      else if (unformat_user (line_input, unformat_ip6_address, &ip6))
+       ;
+      else if (unformat_user (line_input, unformat_vnet_sw_interface,
+                             vnm, &sw_if_index))
+       ;
       else
        {
          e = clib_error_return (0, "unknown input '%U'",
@@ -148,20 +263,20 @@ cnat_set_snat (vlib_main_t * vm,
        }
     }
 
+  cnat_set_snat (&ip4, &ip6, sw_if_index);
+
 done:
   unformat_free (line_input);
 
   return (e);
 }
 
-/* *INDENT-OFF* */
 VLIB_CLI_COMMAND (cnat_set_snat_command, static) =
 {
   .path = "cnat snat with",
-  .short_help = "cnat snat with [<ip4-address>][<ip6-address>]",
-  .function = cnat_set_snat,
+  .short_help = "cnat snat with [<ip4-address>][<ip6-address>][sw_if_index]",
+  .function = cnat_set_snat_cli,
 };
-/* *INDENT-ON* */
 
 static clib_error_t *
 cnat_snat_exclude (vlib_main_t * vm,
@@ -195,14 +310,12 @@ cnat_snat_exclude (vlib_main_t * vm,
   return (NULL);
 }
 
-/* *INDENT-OFF* */
 VLIB_CLI_COMMAND (cnat_snat_exclude_command, static) =
 {
   .path = "cnat snat exclude",
   .short_help = "cnat snat exclude [ip]",
   .function = cnat_snat_exclude,
 };
-/* *INDENT-ON* */
 
 static clib_error_t *
 cnat_show_snat (vlib_main_t * vm,
@@ -210,27 +323,25 @@ cnat_show_snat (vlib_main_t * vm,
 {
   cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
   vlib_cli_output (vm, "Source NAT\nip4: %U\nip6: %U\n",
-                  format_ip4_address, &cnat_main.snat_ip4,
-                  format_ip6_address, &cnat_main.snat_ip6);
+                  format_cnat_endpoint, &cnat_main.snat_ip4,
+                  format_cnat_endpoint, &cnat_main.snat_ip6);
   vlib_cli_output (vm, "Prefixes:\n%U\n",
                   format_bihash_24_8, &table->ip_hash, 1);
   return (NULL);
 }
 
-/* *INDENT-OFF* */
 VLIB_CLI_COMMAND (cnat_show_snat_command, static) =
 {
   .path = "show cnat snat",
   .short_help = "show cnat snat",
   .function = cnat_show_snat,
 };
-/* *INDENT-ON* */
 
 static clib_error_t *
 cnat_snat_init (vlib_main_t * vm)
 {
-  cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
   cnat_main_t *cm = &cnat_main;
+  cnat_snat_pfx_table_t *table = &cm->snat_pfx_table;
   int i;
   for (i = 0; i < ARRAY_LEN (table->ip_masks); i++)
     {