NAT64: custom prefix 55/7255/2
authorMatus Fabian <matfabia@cisco.com>
Wed, 21 Jun 2017 13:15:18 +0000 (06:15 -0700)
committerOle Trøan <otroan@employees.org>
Wed, 21 Jun 2017 16:27:41 +0000 (16:27 +0000)
Change-Id: If397b49861468eed29b964fa64b186f80eb0eceb
Signed-off-by: Matus Fabian <matfabia@cisco.com>
src/plugins/snat/nat64.c
src/plugins/snat/nat64.h
src/plugins/snat/nat64_cli.c
src/plugins/snat/nat64_in2out.c
src/plugins/snat/nat64_out2in.c
src/plugins/snat/snat_api.c
test/test_snat.py
test/vpp_papi_provider.py

index d6e052f..d7772a7 100644 (file)
@@ -38,6 +38,13 @@ VNET_FEATURE_INIT (nat64_out2in, static) = {
   .runs_before = VNET_FEATURES ("ip4-lookup"),
 };
 
+static u8 well_known_prefix[] = {
+  0x00, 0x64, 0xff, 0x9b,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00
+};
+
 /* *INDENT-ON* */
 
 clib_error_t *
@@ -600,6 +607,209 @@ nat64_tcp_session_set_state (nat64_db_st_entry_t * ste, tcp_header_t * tcp,
     }
 }
 
+int
+nat64_add_del_prefix (ip6_address_t * prefix, u8 plen, u32 vrf_id, u8 is_add)
+{
+  nat64_main_t *nm = &nat64_main;
+  nat64_prefix_t *p = 0;
+  int i;
+
+  /* Verify prefix length */
+  if (plen != 32 && plen != 40 && plen != 48 && plen != 56 && plen != 64
+      && plen != 96)
+    return VNET_API_ERROR_INVALID_VALUE;
+
+  /* Check if tenant already have prefix */
+  for (i = 0; i < vec_len (nm->pref64); i++)
+    {
+      if (nm->pref64[i].vrf_id == vrf_id)
+       {
+         p = nm->pref64 + i;
+         break;
+       }
+    }
+
+  if (is_add)
+    {
+      if (!p)
+       {
+         vec_add2 (nm->pref64, p, 1);
+         p->fib_index =
+           fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id);
+         p->vrf_id = vrf_id;
+       }
+
+      p->prefix.as_u64[0] = prefix->as_u64[0];
+      p->prefix.as_u64[1] = prefix->as_u64[1];
+      p->plen = plen;
+    }
+  else
+    {
+      if (!p)
+       return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+      vec_del1 (nm->pref64, i);
+    }
+
+  return 0;
+}
+
+void
+nat64_prefix_walk (nat64_prefix_walk_fn_t fn, void *ctx)
+{
+  nat64_main_t *nm = &nat64_main;
+  nat64_prefix_t *p = 0;
+
+  /* *INDENT-OFF* */
+  vec_foreach (p, nm->pref64)
+    {
+      if (fn (p, ctx))
+        break;
+    };
+  /* *INDENT-ON* */
+}
+
+void
+nat64_compose_ip6 (ip6_address_t * ip6, ip4_address_t * ip4, u32 fib_index)
+{
+  nat64_main_t *nm = &nat64_main;
+  nat64_prefix_t *p, *gp = 0, *prefix = 0;
+
+  /* *INDENT-OFF* */
+  vec_foreach (p, nm->pref64)
+    {
+      if (p->fib_index == fib_index)
+        {
+          prefix = p;
+          break;
+        }
+
+      if (p->fib_index == 0)
+        gp = p;
+    };
+  /* *INDENT-ON* */
+
+  if (!prefix)
+    prefix = gp;
+
+  if (prefix)
+    {
+      memset (ip6, 0, 16);
+      memcpy (ip6, &p->prefix, p->plen);
+      switch (p->plen)
+       {
+       case 32:
+         ip6->as_u32[1] = ip4->as_u32;
+         break;
+       case 40:
+         ip6->as_u8[5] = ip4->as_u8[0];
+         ip6->as_u8[6] = ip4->as_u8[1];
+         ip6->as_u8[7] = ip4->as_u8[2];
+         ip6->as_u8[9] = ip4->as_u8[3];
+         break;
+       case 48:
+         ip6->as_u8[6] = ip4->as_u8[0];
+         ip6->as_u8[7] = ip4->as_u8[1];
+         ip6->as_u8[9] = ip4->as_u8[2];
+         ip6->as_u8[10] = ip4->as_u8[3];
+         break;
+       case 56:
+         ip6->as_u8[7] = ip4->as_u8[0];
+         ip6->as_u8[9] = ip4->as_u8[1];
+         ip6->as_u8[10] = ip4->as_u8[2];
+         ip6->as_u8[11] = ip4->as_u8[3];
+         break;
+       case 64:
+         ip6->as_u8[9] = ip4->as_u8[0];
+         ip6->as_u8[10] = ip4->as_u8[1];
+         ip6->as_u8[11] = ip4->as_u8[2];
+         ip6->as_u8[12] = ip4->as_u8[3];
+         break;
+       case 96:
+         ip6->as_u32[3] = ip4->as_u32;
+         break;
+       default:
+         clib_warning ("invalid prefix length");
+         break;
+       }
+    }
+  else
+    {
+      memcpy (ip6, well_known_prefix, 16);
+      ip6->as_u32[3] = ip4->as_u32;
+    }
+}
+
+void
+nat64_extract_ip4 (ip6_address_t * ip6, ip4_address_t * ip4, u32 fib_index)
+{
+  nat64_main_t *nm = &nat64_main;
+  nat64_prefix_t *p, *gp = 0;
+  u8 plen = 0;
+
+  /* *INDENT-OFF* */
+  vec_foreach (p, nm->pref64)
+    {
+      if (p->fib_index == fib_index)
+        {
+          plen = p->plen;
+          break;
+        }
+
+      if (p->vrf_id == 0)
+        gp = p;
+    };
+  /* *INDENT-ON* */
+
+  if (!plen)
+    {
+      if (gp)
+       plen = gp->plen;
+      else
+       plen = 96;
+    }
+
+  switch (plen)
+    {
+    case 32:
+      ip4->as_u32 = ip6->as_u32[1];
+      break;
+    case 40:
+      ip4->as_u8[0] = ip6->as_u8[5];
+      ip4->as_u8[1] = ip6->as_u8[6];
+      ip4->as_u8[2] = ip6->as_u8[7];
+      ip4->as_u8[3] = ip6->as_u8[9];
+      break;
+    case 48:
+      ip4->as_u8[0] = ip6->as_u8[6];
+      ip4->as_u8[1] = ip6->as_u8[7];
+      ip4->as_u8[2] = ip6->as_u8[9];
+      ip4->as_u8[3] = ip6->as_u8[10];
+      break;
+    case 56:
+      ip4->as_u8[0] = ip6->as_u8[7];
+      ip4->as_u8[1] = ip6->as_u8[9];
+      ip4->as_u8[2] = ip6->as_u8[10];
+      ip4->as_u8[3] = ip6->as_u8[11];
+      break;
+    case 64:
+      ip4->as_u8[0] = ip6->as_u8[9];
+      ip4->as_u8[1] = ip6->as_u8[10];
+      ip4->as_u8[2] = ip6->as_u8[11];
+      ip4->as_u8[3] = ip6->as_u8[12];
+      break;
+    case 96:
+      ip4->as_u32 = ip6->as_u32[3];
+      break;
+    default:
+      clib_warning ("invalid prefix length");
+      break;
+    }
+
+  clib_warning ("%U %U plen %u", format_ip6_address, ip6, format_ip4_address,
+               ip4, plen);
+}
+
 /**
  * @brief The 'nat64-expire-walk' process's main loop.
  *
index df0c470..771b907 100644 (file)
@@ -39,6 +39,14 @@ typedef enum
 #undef _
 } nat64_tcp_ses_state_t;
 
+typedef struct
+{
+  ip6_address_t prefix;
+  u8 plen;
+  u32 vrf_id;
+  u32 fib_index;
+} nat64_prefix_t;
+
 typedef struct
 {
   /** Interface pool */
@@ -47,7 +55,10 @@ typedef struct
   /** Address pool vector */
   snat_address_t *addr_pool;
 
-  /** BIB and session DB **/
+  /** Pref64 vector */
+  nat64_prefix_t *pref64;
+
+  /** BIB and session DB */
   nat64_db_t db;
 
   /* values of various timeouts */
@@ -251,6 +262,52 @@ void nat64_session_reset_timeout (nat64_db_st_entry_t * ste,
 void nat64_tcp_session_set_state (nat64_db_st_entry_t * ste,
                                  tcp_header_t * tcp, u8 is_ip6);
 
+/**
+ * @brief Add/delete NAT64 prefix.
+ *
+ * @param prefix NAT64 prefix.
+ * @param plen Prefix length.
+ * @param vrf_id VRF id of tenant.
+ * @param is_add 1 if add, 0 if delete.
+ *
+ * @returns 0 on success, non-zero value otherwise.
+ */
+int nat64_add_del_prefix (ip6_address_t * prefix, u8 plen, u32 vrf_id,
+                         u8 is_add);
+
+/**
+ * @brief Call back function when walking addresses in NAT64 prefixes, non-zero
+ * return value stop walk.
+ */
+typedef int (*nat64_prefix_walk_fn_t) (nat64_prefix_t * pref64, void *ctx);
+
+/**
+ * @brief Walk NAT64 prefixes.
+ *
+ * @param fn The function to invoke on each entry visited.
+ * @param ctx A context passed in the visit function.
+ */
+void nat64_prefix_walk (nat64_prefix_walk_fn_t fn, void *ctx);
+
+/**
+ * Compose IPv4-embedded IPv6 addresses.
+ * @param ip6 IPv4-embedded IPv6 addresses.
+ * @param ip4 IPv4 address.
+ * @param fib_index Tenant FIB index.
+ */
+void nat64_compose_ip6 (ip6_address_t * ip6, ip4_address_t * ip4,
+                       u32 fib_index);
+
+/**
+ * Extract IPv4 address from the IPv4-embedded IPv6 addresses.
+ *
+ * @param ip6 IPv4-embedded IPv6 addresses.
+ * @param ip4 IPv4 address.
+ * @param fib_index Tenant FIB index.
+ */
+void nat64_extract_ip4 (ip6_address_t * ip6, ip4_address_t * ip4,
+                       u32 fib_index);
+
 #define u8_ptr_add(ptr, index) (((u8 *)ptr) + index)
 #define u16_net_add(u, val) clib_host_to_net_u16(clib_net_to_host_u16(u) + (val))
 
index 293ceaf..25345cd 100644 (file)
@@ -621,6 +621,96 @@ done:
   return error;
 }
 
+static clib_error_t *
+nat64_add_del_prefix_command_fn (vlib_main_t * vm, unformat_input_t * input,
+                                vlib_cli_command_t * cmd)
+{
+  nat64_main_t *nm = &nat64_main;
+  clib_error_t *error = 0;
+  unformat_input_t _line_input, *line_input = &_line_input;
+  u8 is_add = 1;
+  u32 vrf_id = 0;
+  ip6_address_t prefix;
+  u32 plen = 0;
+  int rv;
+
+  if (nm->is_disabled)
+    return clib_error_return (0,
+                             "NAT64 disabled, multi thread not supported");
+
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat
+         (line_input, "%U/%u", unformat_ip6_address, &prefix, &plen))
+       ;
+      else if (unformat (line_input, "tenant-vrf %u", &vrf_id))
+       ;
+      else if (unformat (line_input, "del"))
+       is_add = 0;
+      else
+       {
+         error = clib_error_return (0, "unknown input: '%U'",
+                                    format_unformat_error, line_input);
+         goto done;
+       }
+    }
+
+  if (!plen)
+    {
+      error = clib_error_return (0, "NAT64 prefix must be set.");
+      goto done;
+    }
+
+  rv = nat64_add_del_prefix (&prefix, (u8) plen, vrf_id, is_add);
+
+  switch (rv)
+    {
+    case VNET_API_ERROR_NO_SUCH_ENTRY:
+      error = clib_error_return (0, "NAT64 prefix not exist.");
+      goto done;
+    case VNET_API_ERROR_INVALID_VALUE:
+      error = clib_error_return (0, "Invalid prefix length.");
+      goto done;
+    default:
+      break;
+    }
+
+done:
+  unformat_free (line_input);
+
+  return error;
+}
+
+static int
+nat64_cli_prefix_walk (nat64_prefix_t * p, void *ctx)
+{
+  vlib_main_t *vm = ctx;
+
+  vlib_cli_output (vm, " %U/%u tenant-vrf %u",
+                  format_ip6_address, &p->prefix, p->plen, p->vrf_id);
+
+  return 0;
+}
+
+static clib_error_t *
+nat64_show_prefix_command_fn (vlib_main_t * vm, unformat_input_t * input,
+                             vlib_cli_command_t * cmd)
+{
+  nat64_main_t *nm = &nat64_main;
+
+  if (nm->is_disabled)
+    return clib_error_return (0,
+                             "NAT64 disabled, multi thread not supported");
+
+  vlib_cli_output (vm, "NAT64 prefix:");
+  nat64_prefix_walk (nat64_cli_prefix_walk, vm);
+
+  return 0;
+}
+
 /* *INDENT-OFF* */
 
 VLIB_CLI_COMMAND (nat64_add_pool_address_command, static) = {
@@ -680,6 +770,19 @@ VLIB_CLI_COMMAND (show_nat64_st_command, static) = {
   .function = nat64_show_st_command_fn,
 };
 
+VLIB_CLI_COMMAND (nat64_add_del_prefix_command, static) = {
+  .path = "nat64 add prefix",
+  .short_help = "nat64 add prefix <ip6-prefix>/<plen> [tenant-vrf <vrf-id>] "
+                "[del]",
+  .function = nat64_add_del_prefix_command_fn,
+};
+
+VLIB_CLI_COMMAND (show_nat64_prefix_command, static) = {
+  .path = "show nat64 prefix",
+  .short_help = "show nat64 prefix",
+  .function = nat64_show_prefix_command_fn,
+};
+
 /* *INDENT-ON* */
 
 /*
index fd32bfc..126b076 100644 (file)
 #include <vnet/ip/ip6_to_ip4.h>
 #include <vnet/fib/fib_table.h>
 
-/* *INDENT-OFF* */
-static u8 well_known_prefix[] = {
-  0x00, 0x64, 0xff, 0x9b,
-  0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00
-};
-/* *INDENT-ON* */
-
 typedef struct
 {
   u32 sw_if_index;
@@ -166,6 +157,7 @@ nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
            return -1;
        }
 
+      nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
       ste =
        nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
                                  &daddr.ip4, dport);
@@ -178,7 +170,7 @@ nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
   ip4->src_address.as_u32 = bibe->out_addr.as_u32;
   udp->src_port = bibe->out_port;
 
-  ip4->dst_address.as_u32 = daddr.ip4.as_u32;
+  ip4->dst_address.as_u32 = ste->out_r_addr.as_u32;
 
   if (proto == SNAT_PROTOCOL_TCP)
     {
@@ -252,6 +244,8 @@ nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
              if (!bibe)
                return -1;
            }
+
+         nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
          ste =
            nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
                                      &daddr.ip4, 0);
@@ -264,7 +258,7 @@ nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
       ip4->src_address.as_u32 = bibe->out_addr.as_u32;
       ((u16 *) (icmp))[2] = bibe->out_port;
 
-      ip4->dst_address.as_u32 = daddr.ip4.as_u32;
+      ip4->dst_address.as_u32 = ste->out_r_addr.as_u32;
     }
   else
     {
@@ -272,7 +266,7 @@ nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
        return -1;
 
       ip4->src_address.as_u32 = nm->addr_pool[0].addr.as_u32;
-      ip4->dst_address.as_u32 = daddr.ip4.as_u32;
+      nat64_extract_ip4 (&ip6->dst_address, &ip4->dst_address, fib_index);
     }
 
   return 0;
@@ -321,7 +315,7 @@ nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
 
       ip4->dst_address.as_u32 = bibe->out_addr.as_u32;
       ((u16 *) (icmp))[2] = bibe->out_port;
-      ip4->src_address.as_u32 = saddr.ip4.as_u32;
+      ip4->src_address.as_u32 = ste->out_r_addr.as_u32;
     }
   else
     {
@@ -345,7 +339,7 @@ nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
 
       ip4->dst_address.as_u32 = bibe->out_addr.as_u32;
       udp->dst_port = bibe->out_port;
-      ip4->src_address.as_u32 = saddr.ip4.as_u32;
+      ip4->src_address.as_u32 = ste->out_r_addr.as_u32;
 
       if (proto == SNAT_PROTOCOL_TCP)
        checksum = &tcp->checksum;
@@ -428,6 +422,7 @@ nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
            return -1;
        }
 
+      nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
       ste =
        nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
                                  &daddr.ip4, dport);
@@ -438,15 +433,12 @@ nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
   nat64_session_reset_timeout (ste, vm);
 
   sport = udp->src_port = bibe->out_port;
-  memcpy (&ip6->src_address, well_known_prefix, sizeof (ip6_address_t));
-  saddr.ip6.as_u32[3] = ip6->src_address.as_u32[3] = bibe->out_addr.as_u32;
+  nat64_compose_ip6 (&ip6->src_address, &bibe->out_addr, fib_index);
 
-  saddr.ip6.as_u32[0] = 0;
-  saddr.ip6.as_u32[1] = 0;
-  saddr.ip6.as_u32[2] = 0;
-  daddr.ip6.as_u32[0] = 0;
-  daddr.ip6.as_u32[1] = 0;
-  daddr.ip6.as_u32[2] = 0;
+  memset (&saddr, 0, sizeof (saddr));
+  memset (&daddr, 0, sizeof (daddr));
+  saddr.ip4.as_u32 = bibe->out_addr.as_u32;
+  daddr.ip4.as_u32 = ste->out_r_addr.as_u32;
 
   ste =
     nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, 0,
@@ -545,22 +537,17 @@ nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
   if (!ste)
     return -1;
 
-  clib_warning ("");
   bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
   if (!bibe)
     return -1;
 
   dport = udp->dst_port = bibe->out_port;
-  memcpy (&inner_ip6->dst_address, well_known_prefix, sizeof (ip6_address_t));
-  daddr.ip6.as_u32[3] = inner_ip6->dst_address.as_u32[3] =
-    bibe->out_addr.as_u32;
+  nat64_compose_ip6 (&inner_ip6->dst_address, &bibe->out_addr, fib_index);
 
-  saddr.ip6.as_u32[0] = 0;
-  saddr.ip6.as_u32[1] = 0;
-  saddr.ip6.as_u32[2] = 0;
-  daddr.ip6.as_u32[0] = 0;
-  daddr.ip6.as_u32[1] = 0;
-  daddr.ip6.as_u32[2] = 0;
+  memset (&saddr, 0, sizeof (saddr));
+  memset (&daddr, 0, sizeof (daddr));
+  saddr.ip4.as_u32 = ste->out_r_addr.as_u32;
+  daddr.ip4.as_u32 = bibe->out_addr.as_u32;
 
   ste =
     nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, 0,
@@ -587,8 +574,7 @@ nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
   if (!vec_len (nm->addr_pool))
     return -1;
 
-  memcpy (&ip6->src_address, well_known_prefix, sizeof (ip6_address_t));
-  ip6->src_address.as_u32[3] = nm->addr_pool[0].addr.as_u32;
+  nat64_compose_ip6 (&ip6->src_address, &nm->addr_pool[0].addr, fib_index);
   ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0];
   ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1];
 
index 0a5fbe5..755aa63 100644 (file)
 #include <vnet/ip/ip4_to_ip6.h>
 #include <vnet/fib/ip4_fib.h>
 
-/* *INDENT-OFF* */
-static u8 well_known_prefix[] = {
-  0x00, 0x64, 0xff, 0x9b,
-  0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00
-};
-/* *INDENT-ON* */
-
 typedef struct
 {
   u32 sw_if_index;
@@ -112,9 +103,6 @@ nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
   memset (&daddr, 0, sizeof (daddr));
   daddr.ip4.as_u32 = ip4->dst_address.as_u32;
 
-  memcpy (&ip6_saddr, well_known_prefix, sizeof (ip6_saddr));
-  ip6_saddr.as_u32[3] = ip4->src_address.as_u32;
-
   ste =
     nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto,
                            fib_index, 0);
@@ -132,6 +120,7 @@ nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
       if (!bibe)
        return -1;
 
+      nat64_compose_ip6 (&ip6_saddr, &ip4->src_address, bibe->fib_index);
       ste =
        nat64_db_st_entry_create (&nm->db, bibe, &ip6_saddr, &saddr.ip4,
                                  sport);
@@ -139,8 +128,8 @@ nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
 
   nat64_session_reset_timeout (ste, ctx->vm);
 
-  ip6->src_address.as_u64[0] = ip6_saddr.as_u64[0];
-  ip6->src_address.as_u64[1] = ip6_saddr.as_u64[1];
+  ip6->src_address.as_u64[0] = ste->in_r_addr.as_u64[0];
+  ip6->src_address.as_u64[1] = ste->in_r_addr.as_u64[1];
 
   ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0];
   ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
@@ -179,9 +168,6 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
   memset (&daddr, 0, sizeof (daddr));
   daddr.ip4.as_u32 = ip4->dst_address.as_u32;
 
-  memcpy (&ip6_saddr, well_known_prefix, sizeof (ip6_saddr));
-  ip6_saddr.as_u32[3] = ip4->src_address.as_u32;
-
   if (icmp->type == ICMP6_echo_request || icmp->type == ICMP6_echo_reply)
     {
       u16 out_id = ((u16 *) (icmp))[2];
@@ -205,6 +191,7 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
          if (!bibe)
            return -1;
 
+         nat64_compose_ip6 (&ip6_saddr, &ip4->src_address, bibe->fib_index);
          ste =
            nat64_db_st_entry_create (&nm->db, bibe, &ip6_saddr, &saddr.ip4,
                                      0);
@@ -212,8 +199,8 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
 
       nat64_session_reset_timeout (ste, ctx->vm);
 
-      ip6->src_address.as_u64[0] = ip6_saddr.as_u64[0];
-      ip6->src_address.as_u64[1] = ip6_saddr.as_u64[1];
+      ip6->src_address.as_u64[0] = ste->in_r_addr.as_u64[0];
+      ip6->src_address.as_u64[1] = ste->in_r_addr.as_u64[1];
 
       ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0];
       ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
@@ -225,8 +212,8 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
     {
       ip6_header_t *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
 
-      ip6->src_address.as_u64[0] = ip6_saddr.as_u64[0];
-      ip6->src_address.as_u64[1] = ip6_saddr.as_u64[1];
+      nat64_compose_ip6 (&ip6->src_address, &ip4->src_address,
+                        vnet_buffer (ctx->b)->sw_if_index[VLIB_TX]);
       ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0];
       ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1];
     }
@@ -243,7 +230,6 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
   nat64_db_bib_entry_t *bibe;
   nat64_db_st_entry_t *ste;
   ip46_address_t saddr, daddr;
-  ip6_address_t ip6_daddr;
   u32 sw_if_index, fib_index;
   snat_protocol_t proto = ip_proto_to_snat_proto (ip4->protocol);
 
@@ -256,9 +242,6 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
   memset (&daddr, 0, sizeof (daddr));
   daddr.ip4.as_u32 = ip4->dst_address.as_u32;
 
-  memcpy (&ip6_daddr, well_known_prefix, sizeof (ip6_daddr));
-  ip6_daddr.as_u32[3] = ip4->dst_address.as_u32;
-
   if (proto == SNAT_PROTOCOL_ICMP)
     {
       icmp46_header_t *icmp = ip4_next_header (ip4);
@@ -279,8 +262,8 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
       if (!bibe)
        return -1;
 
-      ip6->dst_address.as_u64[0] = ip6_daddr.as_u64[0];
-      ip6->dst_address.as_u64[1] = ip6_daddr.as_u64[1];
+      ip6->dst_address.as_u64[0] = ste->in_r_addr.as_u64[0];
+      ip6->dst_address.as_u64[1] = ste->in_r_addr.as_u64[1];
       ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0];
       ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1];
       ((u16 *) (icmp))[2] = bibe->in_port;
@@ -306,8 +289,7 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
       if (!bibe)
        return -1;
 
-      ip6->dst_address.as_u64[0] = ip6_daddr.as_u64[0];
-      ip6->dst_address.as_u64[1] = ip6_daddr.as_u64[1];
+      nat64_compose_ip6 (&ip6->dst_address, &daddr.ip4, bibe->fib_index);
       ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0];
       ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1];
       udp->src_port = bibe->in_port;
index aa0b59a..638f576 100644 (file)
@@ -1677,6 +1677,95 @@ vl_api_nat64_st_dump_t_print (vl_api_nat64_st_dump_t * mp, void *handle)
   FINISH;
 }
 
+static void
+vl_api_nat64_add_del_prefix_t_handler (vl_api_nat64_add_del_prefix_t * mp)
+{
+  vl_api_nat64_add_del_prefix_reply_t *rmp;
+  snat_main_t *sm = &snat_main;
+  nat64_main_t *nm = &nat64_main;
+  ip6_address_t prefix;
+  int rv = 0;
+
+  if (nm->is_disabled)
+    {
+      rv = VNET_API_ERROR_FEATURE_DISABLED;
+      goto send_reply;
+    }
+
+  memcpy (&prefix.as_u8, mp->prefix, 16);
+
+  rv =
+    nat64_add_del_prefix (&prefix, mp->prefix_len,
+                         clib_net_to_host_u32 (mp->vrf_id), mp->is_add);
+send_reply:
+  REPLY_MACRO (VL_API_NAT64_ADD_DEL_PREFIX_REPLY);
+}
+
+static void *
+vl_api_nat64_add_del_prefix_t_print (vl_api_nat64_add_del_prefix_t * mp,
+                                    void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: nat64_add_del_prefix %U/%u vrf_id %u %s\n",
+             format_ip6_address, mp->prefix, mp->prefix_len,
+             ntohl (mp->vrf_id), mp->is_add ? "" : "del");
+
+  FINISH;
+}
+
+static int
+nat64_api_prefix_walk (nat64_prefix_t * p, void *arg)
+{
+  vl_api_nat64_prefix_details_t *rmp;
+  snat_main_t *sm = &snat_main;
+  nat64_api_walk_ctx_t *ctx = arg;
+
+  rmp = vl_msg_api_alloc (sizeof (*rmp));
+  memset (rmp, 0, sizeof (*rmp));
+  rmp->_vl_msg_id = ntohs (VL_API_NAT64_PREFIX_DETAILS + sm->msg_id_base);
+  clib_memcpy (rmp->prefix, &(p->prefix), 16);
+  rmp->prefix_len = p->plen;
+  rmp->vrf_id = ntohl (p->vrf_id);
+  rmp->context = ctx->context;
+
+  vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp);
+
+  return 0;
+}
+
+static void
+vl_api_nat64_prefix_dump_t_handler (vl_api_nat64_prefix_dump_t * mp)
+{
+  unix_shared_memory_queue_t *q;
+  nat64_main_t *nm = &nat64_main;
+
+  if (nm->is_disabled)
+    return;
+
+  q = vl_api_client_index_to_input_queue (mp->client_index);
+  if (q == 0)
+    return;
+
+  nat64_api_walk_ctx_t ctx = {
+    .q = q,
+    .context = mp->context,
+  };
+
+  nat64_prefix_walk (nat64_api_prefix_walk, &ctx);
+}
+
+static void *
+vl_api_nat64_prefix_dump_t_print (vl_api_nat64_prefix_dump_t * mp,
+                                 void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: nat64_prefix_dump\n");
+
+  FINISH;
+}
+
 /* List of message types that this plugin understands */
 #define foreach_snat_plugin_api_msg                                     \
 _(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range)                       \
@@ -1711,7 +1800,9 @@ _(NAT64_ADD_DEL_STATIC_BIB, nat64_add_del_static_bib)                   \
 _(NAT64_BIB_DUMP, nat64_bib_dump)                                       \
 _(NAT64_SET_TIMEOUTS, nat64_set_timeouts)                               \
 _(NAT64_GET_TIMEOUTS, nat64_get_timeouts)                               \
-_(NAT64_ST_DUMP, nat64_st_dump)
+_(NAT64_ST_DUMP, nat64_st_dump)                                         \
+_(NAT64_ADD_DEL_PREFIX, nat64_add_del_prefix)                           \
+_(NAT64_PREFIX_DUMP, nat64_prefix_dump)
 
 /* Set up the API message handling tables */
 static clib_error_t *
index 7544582..d826877 100644 (file)
@@ -157,16 +157,65 @@ class MethodHolder(VppTestCase):
 
         return pkts
 
-    def create_stream_in_ip6(self, in_if, out_if, hlim=64):
+    def compose_ip6(self, ip4, pref, plen):
+        """
+        Compose IPv4-embedded IPv6 addresses
+
+        :param ip4: IPv4 address
+        :param pref: IPv6 prefix
+        :param plen: IPv6 prefix length
+        :returns: IPv4-embedded IPv6 addresses
+        """
+        pref_n = list(socket.inet_pton(socket.AF_INET6, pref))
+        ip4_n = list(socket.inet_pton(socket.AF_INET, ip4))
+        if plen == 32:
+            pref_n[4] = ip4_n[0]
+            pref_n[5] = ip4_n[1]
+            pref_n[6] = ip4_n[2]
+            pref_n[7] = ip4_n[3]
+        elif plen == 40:
+            pref_n[5] = ip4_n[0]
+            pref_n[6] = ip4_n[1]
+            pref_n[7] = ip4_n[2]
+            pref_n[9] = ip4_n[3]
+        elif plen == 48:
+            pref_n[6] = ip4_n[0]
+            pref_n[7] = ip4_n[1]
+            pref_n[9] = ip4_n[2]
+            pref_n[10] = ip4_n[3]
+        elif plen == 56:
+            pref_n[7] = ip4_n[0]
+            pref_n[9] = ip4_n[1]
+            pref_n[10] = ip4_n[2]
+            pref_n[11] = ip4_n[3]
+        elif plen == 64:
+            pref_n[9] = ip4_n[0]
+            pref_n[10] = ip4_n[1]
+            pref_n[11] = ip4_n[2]
+            pref_n[12] = ip4_n[3]
+        elif plen == 96:
+            pref_n[12] = ip4_n[0]
+            pref_n[13] = ip4_n[1]
+            pref_n[14] = ip4_n[2]
+            pref_n[15] = ip4_n[3]
+        return socket.inet_ntop(socket.AF_INET6, ''.join(pref_n))
+
+    def create_stream_in_ip6(self, in_if, out_if, hlim=64, pref=None, plen=0):
         """
         Create IPv6 packet stream for inside network
 
         :param in_if: Inside interface
         :param out_if: Outside interface
         :param ttl: Hop Limit of generated packets
+        :param pref: NAT64 prefix
+        :param plen: NAT64 prefix length
         """
         pkts = []
-        dst = ''.join(['64:ff9b::', out_if.remote_ip4])
+        if pref is None:
+            dst = ''.join(['64:ff9b::', out_if.remote_ip4])
+        else:
+            dst = self.compose_ip6(out_if.remote_ip4, pref, plen)
+
         # TCP
         p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
              IPv6(src=in_if.remote_ip6, dst=dst, hlim=hlim) /
@@ -3101,6 +3150,84 @@ class TestNAT64(MethodHolder):
                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
                 raise
 
+    def test_prefix(self):
+        """ NAT64 Network-Specific Prefix """
+
+        self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n,
+                                                self.nat_addr_n)
+        self.vapi.nat64_add_del_interface(self.pg0.sw_if_index)
+        self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0)
+        self.vapi.nat64_add_del_pool_addr_range(self.vrf1_nat_addr_n,
+                                                self.vrf1_nat_addr_n,
+                                                vrf_id=self.vrf1_id)
+        self.vapi.nat64_add_del_interface(self.pg2.sw_if_index)
+
+        # Add global prefix
+        global_pref64 = "2001:db8::"
+        global_pref64_n = socket.inet_pton(socket.AF_INET6, global_pref64)
+        global_pref64_len = 32
+        self.vapi.nat64_add_del_prefix(global_pref64_n, global_pref64_len)
+
+        prefix = self.vapi.nat64_prefix_dump()
+        self.assertEqual(len(prefix), 1)
+        self.assertEqual(prefix[0].prefix, global_pref64_n)
+        self.assertEqual(prefix[0].prefix_len, global_pref64_len)
+        self.assertEqual(prefix[0].vrf_id, 0)
+
+        # Add tenant specific prefix
+        vrf1_pref64 = "2001:db8:122:300::"
+        vrf1_pref64_n = socket.inet_pton(socket.AF_INET6, vrf1_pref64)
+        vrf1_pref64_len = 56
+        self.vapi.nat64_add_del_prefix(vrf1_pref64_n,
+                                       vrf1_pref64_len,
+                                       vrf_id=self.vrf1_id)
+        prefix = self.vapi.nat64_prefix_dump()
+        self.assertEqual(len(prefix), 2)
+
+        # Global prefix
+        pkts = self.create_stream_in_ip6(self.pg0,
+                                         self.pg1,
+                                         pref=global_pref64,
+                                         plen=global_pref64_len)
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg1.get_capture(len(pkts))
+        self.verify_capture_out(capture, nat_ip=self.nat_addr,
+                                dst_ip=self.pg1.remote_ip4)
+
+        pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr)
+        self.pg1.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg0.get_capture(len(pkts))
+        dst_ip = self.compose_ip6(self.pg1.remote_ip4,
+                                  global_pref64,
+                                  global_pref64_len)
+        self.verify_capture_in_ip6(capture, dst_ip, self.pg0.remote_ip6)
+
+        # Tenant specific prefix
+        pkts = self.create_stream_in_ip6(self.pg2,
+                                         self.pg1,
+                                         pref=vrf1_pref64,
+                                         plen=vrf1_pref64_len)
+        self.pg2.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg1.get_capture(len(pkts))
+        self.verify_capture_out(capture, nat_ip=self.vrf1_nat_addr,
+                                dst_ip=self.pg1.remote_ip4)
+
+        pkts = self.create_stream_out(self.pg1, dst_ip=self.vrf1_nat_addr)
+        self.pg1.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg2.get_capture(len(pkts))
+        dst_ip = self.compose_ip6(self.pg1.remote_ip4,
+                                  vrf1_pref64,
+                                  vrf1_pref64_len)
+        self.verify_capture_in_ip6(capture, dst_ip, self.pg2.remote_ip6)
+
     def nat64_get_ses_num(self):
         """
         Return number of active NAT64 sessions.
@@ -3166,11 +3293,19 @@ class TestNAT64(MethodHolder):
                                                     vrf_id=addr.vrf_id,
                                                     is_add=0)
 
+        prefixes = self.vapi.nat64_prefix_dump()
+        for prefix in prefixes:
+            self.vapi.nat64_add_del_prefix(prefix.prefix,
+                                           prefix.prefix_len,
+                                           vrf_id=prefix.vrf_id,
+                                           is_add=0)
+
     def tearDown(self):
         super(TestNAT64, self).tearDown()
         if not self.vpp_dead:
             self.logger.info(self.vapi.cli("show nat64 pool"))
             self.logger.info(self.vapi.cli("show nat64 interfaces"))
+            self.logger.info(self.vapi.cli("show nat64 prefix"))
             self.logger.info(self.vapi.cli("show nat64 bib tcp"))
             self.logger.info(self.vapi.cli("show nat64 bib udp"))
             self.logger.info(self.vapi.cli("show nat64 bib icmp"))
index 5f27e85..4f7e9fe 100644 (file)
@@ -1424,11 +1424,11 @@ class VppPapiProvider(object):
                            tcp_incoming_syn=6):
         """Set values of timeouts for NAT64 (in seconds)
 
-        :param udp - UDP timeout (Default value = 300)
-        :param icmp - ICMP timeout (Default value = 60)
-        :param tcp_trans - TCP transitory timeout (Default value = 240)
-        :param tcp_est - TCP established timeout (Default value = 7440)
-        :param tcp_incoming_syn - TCP incoming SYN timeout (Default value = 6)
+        :param udpi: UDP timeout (Default value = 300)
+        :param icmp: ICMP timeout (Default value = 60)
+        :param tcp_trans: TCP transitory timeout (Default value = 240)
+        :param tcp_est: TCP established timeout (Default value = 7440)
+        :param tcp_incoming_syn: TCP incoming SYN timeout (Default value = 6)
         """
         return self.api(
             self.papi.nat64_set_timeouts,
@@ -1453,6 +1453,28 @@ class VppPapiProvider(object):
         """
         return self.api(self.papi.nat64_st_dump, {'proto': protocol})
 
+    def nat64_add_del_prefix(self, prefix, plen, vrf_id=0, is_add=1):
+        """Add/del NAT64 prefix
+
+        :param prefix: NAT64 prefix
+        :param plen: NAT64 prefix length
+        :param vrf_id: VRF id of tenant (Default 0)
+        :param is_add: 1 if add, 0 if delete (Default value = 1)
+        """
+        return self.api(
+            self.papi.nat64_add_del_prefix,
+            {'prefix': prefix,
+             'prefix_len': plen,
+             'vrf_id': vrf_id,
+             'is_add': is_add})
+
+    def nat64_prefix_dump(self):
+        """Dump NAT64 prefix
+
+        :returns: Dictionary of NAT64 prefixes
+        """
+        return self.api(self.papi.nat64_prefix_dump, {})
+
     def control_ping(self):
         self.api(self.papi.control_ping)