NAT64: Add NAT64 support for snat plugin (VPP-699) 20/7020/7
authorMatus Fabian <matfabia@cisco.com>
Tue, 6 Jun 2017 11:53:28 +0000 (04:53 -0700)
committerOle Trøan <otroan@employees.org>
Thu, 8 Jun 2017 11:39:53 +0000 (11:39 +0000)
Basic NAT64 feature (no hairpinning, no multi-thread).

Change-Id: I392fccbce93e70c117f4a9a7ec7cf08d6c537f2d
Signed-off-by: Matus Fabian <matfabia@cisco.com>
14 files changed:
src/plugins/snat.am
src/plugins/snat/nat64.c [new file with mode: 0644]
src/plugins/snat/nat64.h [new file with mode: 0644]
src/plugins/snat/nat64_cli.c [new file with mode: 0644]
src/plugins/snat/nat64_db.c [new file with mode: 0644]
src/plugins/snat/nat64_db.h [new file with mode: 0644]
src/plugins/snat/nat64_in2out.c [new file with mode: 0644]
src/plugins/snat/nat64_out2in.c [new file with mode: 0644]
src/plugins/snat/snat.api
src/plugins/snat/snat.c
src/plugins/snat/snat.h
src/plugins/snat/snat_api.c
test/test_snat.py
test/vpp_papi_provider.py

index 4892e42..6b75f3d 100644 (file)
@@ -21,7 +21,12 @@ snat_plugin_la_SOURCES = snat/snat.c         \
         snat/out2in.c                          \
        snat/snat_plugin.api.h                  \
         snat/snat_ipfix_logging.c              \
-        snat/snat_det.c
+        snat/snat_det.c                        \
+        snat/nat64.c                           \
+        snat/nat64_cli.c                       \
+        snat/nat64_in2out.c                    \
+        snat/nat64_out2in.c                    \
+        snat/nat64_db.c
 
 API_FILES += snat/snat.api
 
diff --git a/src/plugins/snat/nat64.c b/src/plugins/snat/nat64.c
new file mode 100644 (file)
index 0000000..9b6b3c8
--- /dev/null
@@ -0,0 +1,590 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @file
+ * @brief NAT64 implementation
+ */
+
+#include <snat/nat64.h>
+#include <snat/nat64_db.h>
+#include <vnet/fib/ip4_fib.h>
+
+
+nat64_main_t nat64_main;
+
+/* *INDENT-OFF* */
+
+/* Hook up input features */
+VNET_FEATURE_INIT (nat64_in2out, static) = {
+  .arc_name = "ip6-unicast",
+  .node_name = "nat64-in2out",
+  .runs_before = VNET_FEATURES ("ip6-lookup"),
+};
+VNET_FEATURE_INIT (nat64_out2in, static) = {
+  .arc_name = "ip4-unicast",
+  .node_name = "nat64-out2in",
+  .runs_before = VNET_FEATURES ("ip4-lookup"),
+};
+
+/* *INDENT-ON* */
+
+clib_error_t *
+nat64_init (vlib_main_t * vm)
+{
+  nat64_main_t *nm = &nat64_main;
+  clib_error_t *error = 0;
+
+  if (nat64_db_init (&nm->db))
+    error = clib_error_return (0, "NAT64 DB init failed");
+
+  /* set session timeouts to default values */
+  nm->udp_timeout = SNAT_UDP_TIMEOUT;
+  nm->icmp_timeout = SNAT_ICMP_TIMEOUT;
+  nm->tcp_trans_timeout = SNAT_TCP_TRANSITORY_TIMEOUT;
+  nm->tcp_est_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT;
+  nm->tcp_incoming_syn_timeout = SNAT_TCP_INCOMING_SYN;
+
+  return error;
+}
+
+int
+nat64_add_del_pool_addr (ip4_address_t * addr, u32 vrf_id, u8 is_add)
+{
+  nat64_main_t *nm = &nat64_main;
+  snat_address_t *a = 0;
+  snat_interface_t *interface;
+  int i;
+
+  /* Check if address already exists */
+  for (i = 0; i < vec_len (nm->addr_pool); i++)
+    {
+      if (nm->addr_pool[i].addr.as_u32 == addr->as_u32)
+       {
+         a = nm->addr_pool + i;
+         break;
+       }
+    }
+
+  if (is_add)
+    {
+      if (a)
+       return VNET_API_ERROR_VALUE_EXIST;
+
+      vec_add2 (nm->addr_pool, a, 1);
+      a->addr = *addr;
+      a->fib_index = ip4_fib_index_from_table_id (vrf_id);
+#define _(N, i, n, s) \
+      clib_bitmap_alloc (a->busy_##n##_port_bitmap, 65535);
+      foreach_snat_protocol
+#undef _
+    }
+  else
+    {
+      if (!a)
+       return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+#define _(N, id, n, s) \
+      clib_bitmap_free (a->busy_##n##_port_bitmap);
+      foreach_snat_protocol
+#undef _
+       vec_del1 (nm->addr_pool, i);
+    }
+
+  /* Add/del external address to FIB */
+  /* *INDENT-OFF* */
+  pool_foreach (interface, nm->interfaces,
+  ({
+    if (interface->is_inside)
+      continue;
+
+    snat_add_del_addr_to_fib (addr, 32, interface->sw_if_index, is_add);
+    break;
+  }));
+  /* *INDENT-ON* */
+
+  return 0;
+}
+
+void
+nat64_pool_addr_walk (nat64_pool_addr_walk_fn_t fn, void *ctx)
+{
+  nat64_main_t *nm = &nat64_main;
+  snat_address_t *a = 0;
+
+  /* *INDENT-OFF* */
+  vec_foreach (a, nm->addr_pool)
+    {
+      if (fn (a, ctx))
+        break;
+    };
+  /* *INDENT-ON* */
+}
+
+int
+nat64_add_del_interface (u32 sw_if_index, u8 is_inside, u8 is_add)
+{
+  nat64_main_t *nm = &nat64_main;
+  snat_interface_t *interface = 0, *i;
+  snat_address_t *ap;
+  const char *feature_name, *arc_name;
+
+  /* Check if address already exists */
+  /* *INDENT-OFF* */
+  pool_foreach (i, nm->interfaces,
+  ({
+    if (i->sw_if_index == sw_if_index)
+      {
+        interface = i;
+        break;
+      }
+  }));
+  /* *INDENT-ON* */
+
+  if (is_add)
+    {
+      if (interface)
+       return VNET_API_ERROR_VALUE_EXIST;
+
+      pool_get (nm->interfaces, interface);
+      interface->sw_if_index = sw_if_index;
+      interface->is_inside = is_inside;
+
+    }
+  else
+    {
+      if (!interface)
+       return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+      pool_put (nm->interfaces, interface);
+    }
+
+  if (!is_inside)
+    {
+      /* *INDENT-OFF* */
+      vec_foreach (ap, nm->addr_pool)
+        snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, is_add);
+      /* *INDENT-ON* */
+    }
+
+  arc_name = is_inside ? "ip6-unicast" : "ip4-unicast";
+  feature_name = is_inside ? "nat64-in2out" : "nat64-out2in";
+
+  return vnet_feature_enable_disable (arc_name, feature_name, sw_if_index,
+                                     is_add, 0, 0);
+}
+
+void
+nat64_interfaces_walk (nat64_interface_walk_fn_t fn, void *ctx)
+{
+  nat64_main_t *nm = &nat64_main;
+  snat_interface_t *i = 0;
+
+  /* *INDENT-OFF* */
+  pool_foreach (i, nm->interfaces,
+  ({
+    if (fn (i, ctx))
+      break;
+  }));
+  /* *INDENT-ON* */
+}
+
+int
+nat64_alloc_out_addr_and_port (u32 fib_index, snat_protocol_t proto,
+                              ip4_address_t * addr, u16 * port)
+{
+  nat64_main_t *nm = &nat64_main;
+  snat_main_t *sm = &snat_main;
+  int i;
+  snat_address_t *a;
+  u32 portnum;
+
+  for (i = 0; i < vec_len (nm->addr_pool); i++)
+    {
+      a = nm->addr_pool + i;
+      switch (proto)
+       {
+#define _(N, j, n, s) \
+        case SNAT_PROTOCOL_##N: \
+          if (a->busy_##n##_ports < (65535-1024)) \
+            { \
+              while (1) \
+                { \
+                  portnum = random_u32 (&sm->random_seed); \
+                  portnum &= 0xFFFF; \
+                  if (portnum < 1024) \
+                    continue; \
+                  if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \
+                                                portnum)) \
+                    continue; \
+                  clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \
+                                            portnum, 1); \
+                  a->busy_##n##_ports++; \
+                  *port = portnum; \
+                  addr->as_u32 = a->addr.as_u32; \
+                  return 0; \
+                } \
+            } \
+          break;
+         foreach_snat_protocol
+#undef _
+       default:
+         clib_warning ("unknown protocol");
+         return 1;
+       }
+
+    }
+  /* Totally out of translations to use... */
+  //TODO: IPFix
+  return 1;
+}
+
+void
+nat64_free_out_addr_and_port (ip4_address_t * addr, u16 port,
+                             snat_protocol_t proto)
+{
+  nat64_main_t *nm = &nat64_main;
+  int i;
+  snat_address_t *a;
+
+  for (i = 0; i < vec_len (nm->addr_pool); i++)
+    {
+      a = nm->addr_pool + i;
+      if (addr->as_u32 != a->addr.as_u32)
+       continue;
+      switch (proto)
+       {
+#define _(N, j, n, s) \
+        case SNAT_PROTOCOL_##N: \
+          ASSERT (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \
+                  port) == 1); \
+          clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, port, 0); \
+          a->busy_##n##_ports--; \
+          break;
+         foreach_snat_protocol
+#undef _
+       default:
+         clib_warning ("unknown protocol");
+         return;
+       }
+      break;
+    }
+}
+
+int
+nat64_add_del_static_bib_entry (ip6_address_t * in_addr,
+                               ip4_address_t * out_addr, u16 in_port,
+                               u16 out_port, u8 proto, u32 vrf_id, u8 is_add)
+{
+  nat64_main_t *nm = &nat64_main;
+  nat64_db_bib_entry_t *bibe;
+  u32 fib_index =
+    fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id);
+  snat_protocol_t p = ip_proto_to_snat_proto (proto);
+  ip46_address_t addr;
+  int i;
+  snat_address_t *a;
+
+  addr.as_u64[0] = in_addr->as_u64[0];
+  addr.as_u64[1] = in_addr->as_u64[1];
+  bibe =
+    nat64_db_bib_entry_find (&nm->db, &addr, clib_host_to_net_u16 (in_port),
+                            p, fib_index, 1);
+
+  if (is_add)
+    {
+      if (bibe)
+       return VNET_API_ERROR_VALUE_EXIST;
+
+      for (i = 0; i < vec_len (nm->addr_pool); i++)
+       {
+         a = nm->addr_pool + i;
+         if (out_addr->as_u32 != a->addr.as_u32)
+           continue;
+         switch (p)
+           {
+#define _(N, j, n, s) \
+            case SNAT_PROTOCOL_##N: \
+              if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \
+                                            out_port)) \
+                return VNET_API_ERROR_INVALID_VALUE; \
+              clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \
+                                        out_port, 1); \
+              if (out_port > 1024) \
+                a->busy_##n##_ports++; \
+              break;
+             foreach_snat_protocol
+#undef _
+           default:
+             clib_warning ("unknown protocol");
+             return VNET_API_ERROR_INVALID_VALUE_2;
+           }
+         break;
+       }
+      bibe =
+       nat64_db_bib_entry_create (&nm->db, in_addr, out_addr,
+                                  clib_host_to_net_u16 (in_port),
+                                  clib_host_to_net_u16 (out_port), fib_index,
+                                  p, 1);
+      if (!bibe)
+       return VNET_API_ERROR_UNSPECIFIED;
+    }
+  else
+    {
+      if (!bibe)
+       return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+      nat64_free_out_addr_and_port (out_addr, out_port, p);
+      nat64_db_bib_entry_free (&nm->db, bibe);
+    }
+
+  return 0;
+}
+
+int
+nat64_set_udp_timeout (u32 timeout)
+{
+  nat64_main_t *nm = &nat64_main;
+
+  if (timeout == 0)
+    nm->udp_timeout = SNAT_UDP_TIMEOUT;
+  else if (timeout < SNAT_UDP_TIMEOUT_MIN)
+    return VNET_API_ERROR_INVALID_VALUE;
+  else
+    nm->udp_timeout = timeout;
+
+  return 0;
+}
+
+u32
+nat64_get_udp_timeout (void)
+{
+  nat64_main_t *nm = &nat64_main;
+
+  return nm->udp_timeout;
+}
+
+int
+nat64_set_icmp_timeout (u32 timeout)
+{
+  nat64_main_t *nm = &nat64_main;
+
+  if (timeout == 0)
+    nm->icmp_timeout = SNAT_ICMP_TIMEOUT;
+  else
+    nm->icmp_timeout = timeout;
+
+  return 0;
+}
+
+u32
+nat64_get_icmp_timeout (void)
+{
+  nat64_main_t *nm = &nat64_main;
+
+  return nm->icmp_timeout;
+}
+
+int
+nat64_set_tcp_timeouts (u32 trans, u32 est, u32 incoming_syn)
+{
+  nat64_main_t *nm = &nat64_main;
+
+  if (trans == 0)
+    nm->tcp_trans_timeout = SNAT_TCP_TRANSITORY_TIMEOUT;
+  else
+    nm->tcp_trans_timeout = trans;
+
+  if (est == 0)
+    nm->tcp_est_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT;
+  else
+    nm->tcp_est_timeout = est;
+
+  if (incoming_syn == 0)
+    nm->tcp_incoming_syn_timeout = SNAT_TCP_INCOMING_SYN;
+  else
+    nm->tcp_incoming_syn_timeout = incoming_syn;
+
+  return 0;
+}
+
+u32
+nat64_get_tcp_trans_timeout (void)
+{
+  nat64_main_t *nm = &nat64_main;
+
+  return nm->tcp_trans_timeout;
+}
+
+u32
+nat64_get_tcp_est_timeout (void)
+{
+  nat64_main_t *nm = &nat64_main;
+
+  return nm->tcp_est_timeout;
+}
+
+u32
+nat64_get_tcp_incoming_syn_timeout (void)
+{
+  nat64_main_t *nm = &nat64_main;
+
+  return nm->tcp_incoming_syn_timeout;
+}
+
+void
+nat64_session_reset_timeout (nat64_db_st_entry_t * ste, vlib_main_t * vm)
+{
+  nat64_main_t *nm = &nat64_main;
+  u32 now = (u32) vlib_time_now (vm);
+
+  switch (ste->proto)
+    {
+    case SNAT_PROTOCOL_ICMP:
+      ste->expire = now + nm->icmp_timeout;
+      return;
+    case SNAT_PROTOCOL_TCP:
+      {
+       switch (ste->tcp_state)
+         {
+         case NAT64_TCP_STATE_V4_INIT:
+         case NAT64_TCP_STATE_V6_INIT:
+         case NAT64_TCP_STATE_V4_FIN_RCV:
+         case NAT64_TCP_STATE_V6_FIN_RCV:
+         case NAT64_TCP_STATE_V6_FIN_V4_FIN_RCV:
+         case NAT64_TCP_STATE_TRANS:
+           ste->expire = now + nm->tcp_trans_timeout;
+           return;
+         case NAT64_TCP_STATE_ESTABLISHED:
+           ste->expire = now + nm->tcp_est_timeout;
+           return;
+         default:
+           return;
+         }
+      }
+    case SNAT_PROTOCOL_UDP:
+      ste->expire = now + nm->udp_timeout;
+      return;
+    default:
+      return;
+    }
+}
+
+void
+nat64_tcp_session_set_state (nat64_db_st_entry_t * ste, tcp_header_t * tcp,
+                            u8 is_ip6)
+{
+  switch (ste->tcp_state)
+    {
+    case NAT64_TCP_STATE_CLOSED:
+      {
+       if (tcp->flags & TCP_FLAG_SYN)
+         {
+           if (is_ip6)
+             ste->tcp_state = NAT64_TCP_STATE_V6_INIT;
+           else
+             ste->tcp_state = NAT64_TCP_STATE_V4_INIT;
+         }
+       return;
+      }
+    case NAT64_TCP_STATE_V4_INIT:
+      {
+       if (is_ip6 && (tcp->flags & TCP_FLAG_SYN))
+         ste->tcp_state = NAT64_TCP_STATE_ESTABLISHED;
+       return;
+      }
+    case NAT64_TCP_STATE_V6_INIT:
+      {
+       if (!is_ip6 && (tcp->flags & TCP_FLAG_SYN))
+         ste->tcp_state = NAT64_TCP_STATE_ESTABLISHED;
+       return;
+      }
+    case NAT64_TCP_STATE_ESTABLISHED:
+      {
+       if (tcp->flags & TCP_FLAG_FIN)
+         {
+           if (is_ip6)
+             ste->tcp_state = NAT64_TCP_STATE_V6_FIN_RCV;
+           else
+             ste->tcp_state = NAT64_TCP_STATE_V4_FIN_RCV;
+         }
+       else if (tcp->flags & TCP_FLAG_RST)
+         {
+           ste->tcp_state = NAT64_TCP_STATE_TRANS;
+         }
+       return;
+      }
+    case NAT64_TCP_STATE_V4_FIN_RCV:
+      {
+       if (is_ip6 && (tcp->flags & TCP_FLAG_FIN))
+         ste->tcp_state = NAT64_TCP_STATE_V6_FIN_V4_FIN_RCV;
+       return;
+      }
+    case NAT64_TCP_STATE_V6_FIN_RCV:
+      {
+       if (!is_ip6 && (tcp->flags & TCP_FLAG_FIN))
+         ste->tcp_state = NAT64_TCP_STATE_V6_FIN_V4_FIN_RCV;
+       return;
+      }
+    case NAT64_TCP_STATE_TRANS:
+      {
+       if (!(tcp->flags & TCP_FLAG_RST))
+         ste->tcp_state = NAT64_TCP_STATE_ESTABLISHED;
+       return;
+      }
+    default:
+      return;
+    }
+}
+
+/**
+ * @brief The 'nat64-expire-walk' process's main loop.
+ *
+ * Check expire time for NAT64 sessions.
+ */
+static uword
+nat64_expire_walk_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
+                     vlib_frame_t * f)
+{
+  nat64_main_t *nm = &nat64_main;
+
+  while (1)
+    {
+      vlib_process_wait_for_event_or_clock (vm, 10.0);
+      vlib_process_get_events (vm, NULL);
+      u32 now = (u32) vlib_time_now (vm);
+
+      nad64_db_st_free_expired (&nm->db, now);
+    }
+
+  return 0;
+}
+
+static vlib_node_registration_t nat64_expire_walk_node;
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat64_expire_walk_node, static) = {
+    .function = nat64_expire_walk_fn,
+    .type = VLIB_NODE_TYPE_PROCESS,
+    .name = "nat64-expire-walk",
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/snat/nat64.h b/src/plugins/snat/nat64.h
new file mode 100644 (file)
index 0000000..429b8c0
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @file
+ * @brief NAT64 global declarations
+ */
+#ifndef __included_nat64_h__
+#define __included_nat64_h__
+
+#include <snat/snat.h>
+#include <snat/nat64_db.h>
+
+#define foreach_nat64_tcp_ses_state            \
+  _(0, CLOSED, "closed")                       \
+  _(1, V4_INIT, "v4-init")                     \
+  _(2, V6_INIT, "v6-init")                     \
+  _(3, ESTABLISHED, "established")             \
+  _(4, V4_FIN_RCV, "v4-fin-rcv")               \
+  _(5, V6_FIN_RCV, "v6-fin-rcv")               \
+  _(6, V6_FIN_V4_FIN_RCV, "v6-fin-v4-fin-rcv") \
+  _(7, TRANS, "trans")
+
+typedef enum
+{
+#define _(v, N, s) NAT64_TCP_STATE_##N = v,
+  foreach_nat64_tcp_ses_state
+#undef _
+} nat64_tcp_ses_state_t;
+
+typedef struct
+{
+  /** Interface pool */
+  snat_interface_t *interfaces;
+
+  /** Address pool vector */
+  snat_address_t *addr_pool;
+
+  /** BIB and session DB **/
+  nat64_db_t db;
+
+  /* values of various timeouts */
+  u32 udp_timeout;
+  u32 icmp_timeout;
+  u32 tcp_trans_timeout;
+  u32 tcp_est_timeout;
+  u32 tcp_incoming_syn_timeout;
+
+  snat_main_t *sm;
+} nat64_main_t;
+
+extern nat64_main_t nat64_main;
+extern vlib_node_registration_t nat64_in2out_node;
+extern vlib_node_registration_t nat64_out2in_node;
+
+/**
+ * @brief Add/delete address to NAT64 pool.
+ *
+ * @param addr   IPv4 address.
+ * @param vrf_id VRF id of tenant, ~0 means independent of VRF.
+ * @param is_add 1 if add, 0 if delete.
+ *
+ * @returns 0 on success, non-zero value otherwise.
+ */
+int nat64_add_del_pool_addr (ip4_address_t * addr, u32 vrf_id, u8 is_add);
+
+/**
+ * @brief Call back function when walking addresses in NAT64 pool, non-zero
+ * return value stop walk.
+ */
+typedef int (*nat64_pool_addr_walk_fn_t) (snat_address_t * addr, void *ctx);
+
+/**
+ * @brief Walk NAT64 pool.
+ *
+ * @param fn The function to invoke on each entry visited.
+ * @param ctx A context passed in the visit function.
+ */
+void nat64_pool_addr_walk (nat64_pool_addr_walk_fn_t fn, void *ctx);
+
+/**
+ * @brief Enable/disable NAT64 feature on the interface.
+ *
+ * @param sw_if_index Index of the interface.
+ * @param is_inside   1 if inside, 0 if outside.
+ * @param is_add      1 if add, 0 if delete.
+ *
+ * @returns 0 on success, non-zero value otherwise.
+ */
+int nat64_add_del_interface (u32 sw_if_index, u8 is_inside, u8 is_add);
+
+/**
+ * @brief Call back function when walking interfaces with NAT64 feature,
+ * non-zero return value stop walk.
+ */
+typedef int (*nat64_interface_walk_fn_t) (snat_interface_t * i, void *ctx);
+
+/**
+ * @brief Walk NAT64 interfaces.
+ *
+ * @param fn The function to invoke on each entry visited.
+ * @param ctx A context passed in the visit function.
+ */
+void nat64_interfaces_walk (nat64_interface_walk_fn_t fn, void *ctx);
+
+/**
+ * @brief Initialize NAT64.
+ *
+ * @param vm vlib main.
+ *
+ * @return error code.
+ */
+clib_error_t *nat64_init (vlib_main_t * vm);
+
+/**
+ * @brief Add/delete static NAT64 BIB entry.
+ *
+ * @param in_addr  Inside IPv6 address.
+ * @param out_addr Outside IPv4 address.
+ * @param in_port  Inside port number.
+ * @param out_port Outside port number.
+ * @param proto    L4 protocol.
+ * @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_static_bib_entry (ip6_address_t * in_addr,
+                                   ip4_address_t * out_addr, u16 in_port,
+                                   u16 out_port, u8 proto, u32 vrf_id,
+                                   u8 is_add);
+
+/**
+ * @brief Alloce IPv4 address and port pair from NAT64 pool.
+ *
+ * @param fib_index FIB index of tenant.
+ * @param proto     L4 protocol.
+ * @param addr      Allocated IPv4 address.
+ * @param port      Allocated port number.
+ *
+ * @returns 0 on success, non-zero value otherwise.
+ */
+int nat64_alloc_out_addr_and_port (u32 fib_index, snat_protocol_t proto,
+                                  ip4_address_t * addr, u16 * port);
+
+/**
+ * @brief Free IPv4 address and port pair from NAT64 pool.
+ *
+ * @param addr  IPv4 address to free.
+ * @param port  Port number to free.
+ * @param proto L4 protocol.
+ *
+ * @returns 0 on success, non-zero value otherwise.
+ */
+void nat64_free_out_addr_and_port (ip4_address_t * addr, u16 port,
+                                  snat_protocol_t proto);
+
+/**
+ * @brief Set UDP session timeout.
+ *
+ * @param timeout Timeout value in seconds (if 0 reset to default value 300sec).
+ *
+ * @returns 0 on success, non-zero value otherwise.
+ */
+int nat64_set_udp_timeout (u32 timeout);
+
+/**
+ * @brief Get UDP session timeout.
+ *
+ * @returns UDP session timeout in seconds.
+ */
+u32 nat64_get_udp_timeout (void);
+
+/**
+ * @brief Set ICMP session timeout.
+ *
+ * @param timeout Timeout value in seconds (if 0 reset to default value 60sec).
+ *
+ * @returns 0 on success, non-zero value otherwise.
+ */
+int nat64_set_icmp_timeout (u32 timeout);
+
+/**
+ * @brief Get ICMP session timeout.
+ *
+ * @returns ICMP session timeout in seconds.
+ */
+u32 nat64_get_icmp_timeout (void);
+
+/**
+ * @brief Set TCP session timeouts.
+ *
+ * @param trans Transitory timeout in seconds (if 0 reset to default value 240sec).
+ * @param est Established timeout in seconds (if 0 reset to default value 7440sec).
+ * @param incoming_syn Incoming SYN timeout in seconds (if 0 reset to default value 6sec).
+ *
+ * @returns 0 on success, non-zero value otherwise.
+ */
+int nat64_set_tcp_timeouts (u32 trans, u32 est, u32 incoming_syn);
+
+/**
+ * @brief Get TCP transitory timeout.
+ *
+ * @returns TCP transitory timeout in seconds.
+ */
+u32 nat64_get_tcp_trans_timeout (void);
+
+/**
+ * @brief Get TCP established timeout.
+ *
+ * @returns TCP established timeout in seconds.
+ */
+u32 nat64_get_tcp_est_timeout (void);
+
+/**
+ * @brief Get TCP incoming SYN timeout.
+ *
+ * @returns TCP incoming SYN timeout in seconds.
+ */
+u32 nat64_get_tcp_incoming_syn_timeout (void);
+
+/**
+ * @brief Reset NAT64 session timeout.
+ *
+ * @param ste Session table entry.
+ * @param vm VLIB main.
+ **/
+void nat64_session_reset_timeout (nat64_db_st_entry_t * ste,
+                                 vlib_main_t * vm);
+
+/**
+ * @brief Set NAT64 TCP session state.
+ *
+ * @param ste Session table entry.
+ * @param tcp TCP header.
+ * @param is_ip6 1 if IPv6 packet, 0 if IPv4.
+ */
+void nat64_tcp_session_set_state (nat64_db_st_entry_t * ste,
+                                 tcp_header_t * tcp, u8 is_ip6);
+
+#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))
+
+#endif /* __included_nat64_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/snat/nat64_cli.c b/src/plugins/snat/nat64_cli.c
new file mode 100644 (file)
index 0000000..3fad75e
--- /dev/null
@@ -0,0 +1,633 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @file
+ * @brief NAT64 CLI
+ */
+
+#include <snat/nat64.h>
+#include <snat/snat.h>
+#include <vnet/fib/fib_table.h>
+
+static clib_error_t *
+nat64_add_del_pool_addr_command_fn (vlib_main_t * vm,
+                                   unformat_input_t * input,
+                                   vlib_cli_command_t * cmd)
+{
+  unformat_input_t _line_input, *line_input = &_line_input;
+  ip4_address_t start_addr, end_addr, this_addr;
+  u32 start_host_order, end_host_order;
+  int i, count, rv;
+  u32 vrf_id = ~0;
+  u8 is_add = 1;
+  clib_error_t *error = 0;
+
+  /* Get a line of input. */
+  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_ip4_address, &start_addr,
+                   unformat_ip4_address, &end_addr))
+       ;
+      else if (unformat (line_input, "tenant-vrf %u", &vrf_id))
+       ;
+      else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr))
+       end_addr = start_addr;
+      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;
+       }
+    }
+
+  start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
+  end_host_order = clib_host_to_net_u32 (end_addr.as_u32);
+
+  if (end_host_order < start_host_order)
+    {
+      error = clib_error_return (0, "end address less than start address");
+      goto done;
+    }
+
+  count = (end_host_order - start_host_order) + 1;
+  this_addr = start_addr;
+
+  for (i = 0; i < count; i++)
+    {
+      rv = nat64_add_del_pool_addr (&this_addr, vrf_id, is_add);
+
+      switch (rv)
+       {
+       case VNET_API_ERROR_NO_SUCH_ENTRY:
+         error =
+           clib_error_return (0, "NAT64 pool address %U not exist.",
+                              format_ip4_address, &this_addr);
+         goto done;
+       case VNET_API_ERROR_VALUE_EXIST:
+         error =
+           clib_error_return (0, "NAT64 pool address %U exist.",
+                              format_ip4_address, &this_addr);
+         goto done;
+       default:
+         break;
+
+       }
+      increment_v4_address (&this_addr);
+    }
+
+done:
+  unformat_free (line_input);
+
+  return error;
+}
+
+static int
+nat64_cli_pool_walk (snat_address_t * ap, void *ctx)
+{
+  vlib_main_t *vm = ctx;
+
+  if (ap->fib_index != ~0)
+    {
+      fib_table_t *fib;
+      fib = fib_table_get (ap->fib_index, FIB_PROTOCOL_IP4);
+      if (!fib)
+       return -1;
+      vlib_cli_output (vm, " %U tenant VRF: %u", format_ip4_address,
+                      &ap->addr, fib->ft_table_id);
+    }
+  else
+    vlib_cli_output (vm, " %U", format_ip4_address, &ap->addr);
+
+  return 0;
+}
+
+static clib_error_t *
+nat64_show_pool_command_fn (vlib_main_t * vm,
+                           unformat_input_t * input,
+                           vlib_cli_command_t * cmd)
+{
+  vlib_cli_output (vm, "NAT64 pool:");
+  nat64_pool_addr_walk (nat64_cli_pool_walk, vm);
+
+  return 0;
+}
+
+static clib_error_t *
+nat64_interface_feature_command_fn (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 ();
+  clib_error_t *error = 0;
+  u32 sw_if_index;
+  u32 *inside_sw_if_indices = 0;
+  u32 *outside_sw_if_indices = 0;
+  u8 is_add = 1;
+  int i, rv;
+
+  /* Get a line of input. */
+  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, "in %U", unformat_vnet_sw_interface,
+                   vnm, &sw_if_index))
+       vec_add1 (inside_sw_if_indices, sw_if_index);
+      else if (unformat (line_input, "out %U", unformat_vnet_sw_interface,
+                        vnm, &sw_if_index))
+       vec_add1 (outside_sw_if_indices, sw_if_index);
+      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 (vec_len (inside_sw_if_indices))
+    {
+      for (i = 0; i < vec_len (inside_sw_if_indices); i++)
+       {
+         sw_if_index = inside_sw_if_indices[i];
+         rv = nat64_add_del_interface (sw_if_index, 1, is_add);
+         switch (rv)
+           {
+           case VNET_API_ERROR_NO_SUCH_ENTRY:
+             error =
+               clib_error_return (0, "%U NAT64 feature not enabled.",
+                                  format_vnet_sw_interface_name, vnm,
+                                  vnet_get_sw_interface (vnm, sw_if_index));
+             goto done;
+           case VNET_API_ERROR_VALUE_EXIST:
+             error =
+               clib_error_return (0, "%U NAT64 feature already enabled.",
+                                  format_vnet_sw_interface_name, vnm,
+                                  vnet_get_sw_interface (vnm, sw_if_index));
+             goto done;
+           case VNET_API_ERROR_INVALID_VALUE:
+           case VNET_API_ERROR_INVALID_VALUE_2:
+             error =
+               clib_error_return (0,
+                                  "%U NAT64 feature enable/disable failed.",
+                                  format_vnet_sw_interface_name, vnm,
+                                  vnet_get_sw_interface (vnm, sw_if_index));
+             goto done;
+           default:
+             break;
+
+           }
+       }
+    }
+
+  if (vec_len (outside_sw_if_indices))
+    {
+      for (i = 0; i < vec_len (outside_sw_if_indices); i++)
+       {
+         sw_if_index = outside_sw_if_indices[i];
+         rv = nat64_add_del_interface (sw_if_index, 0, is_add);
+         switch (rv)
+           {
+           case VNET_API_ERROR_NO_SUCH_ENTRY:
+             error =
+               clib_error_return (0, "%U NAT64 feature not enabled.",
+                                  format_vnet_sw_interface_name, vnm,
+                                  vnet_get_sw_interface (vnm, sw_if_index));
+             goto done;
+           case VNET_API_ERROR_VALUE_EXIST:
+             error =
+               clib_error_return (0, "%U NAT64 feature already enabled.",
+                                  format_vnet_sw_interface_name, vnm,
+                                  vnet_get_sw_interface (vnm, sw_if_index));
+             goto done;
+           case VNET_API_ERROR_INVALID_VALUE:
+           case VNET_API_ERROR_INVALID_VALUE_2:
+             error =
+               clib_error_return (0,
+                                  "%U NAT64 feature enable/disable failed.",
+                                  format_vnet_sw_interface_name, vnm,
+                                  vnet_get_sw_interface (vnm, sw_if_index));
+             goto done;
+           default:
+             break;
+
+           }
+       }
+    }
+
+done:
+  unformat_free (line_input);
+  vec_free (inside_sw_if_indices);
+  vec_free (outside_sw_if_indices);
+
+  return error;
+}
+
+static int
+nat64_cli_interface_walk (snat_interface_t * i, void *ctx)
+{
+  vlib_main_t *vm = ctx;
+  vnet_main_t *vnm = vnet_get_main ();
+
+  vlib_cli_output (vm, " %U %s", format_vnet_sw_interface_name, vnm,
+                  vnet_get_sw_interface (vnm, i->sw_if_index),
+                  i->is_inside ? "in" : "out");
+  return 0;
+}
+
+static clib_error_t *
+nat64_show_interfaces_command_fn (vlib_main_t * vm,
+                                 unformat_input_t *
+                                 input, vlib_cli_command_t * cmd)
+{
+  vlib_cli_output (vm, "NAT64 interfaces:");
+  nat64_interfaces_walk (nat64_cli_interface_walk, vm);
+
+  return 0;
+}
+
+static clib_error_t *
+nat64_add_del_static_bib_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;
+  u8 is_add = 1;
+  ip6_address_t in_addr;
+  ip4_address_t out_addr;
+  u16 in_port;
+  u16 out_port;
+  u32 vrf_id = 0;
+  snat_protocol_t proto = 0;
+  u8 p = 0;
+  int rv;
+
+  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,
+                   &in_addr, &in_port))
+       ;
+      else if (unformat (line_input, "%U %u", unformat_ip4_address,
+                        &out_addr, &out_port))
+       ;
+      else if (unformat (line_input, "vrf %u", &vrf_id))
+       ;
+      else if (unformat (line_input, "%U", unformat_snat_protocol, &proto))
+       ;
+      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;
+       }
+    }
+
+  p = snat_proto_to_ip_proto (proto);
+
+  rv =
+    nat64_add_del_static_bib_entry (&in_addr, &out_addr, in_port, out_port, p,
+                                   vrf_id, is_add);
+
+  switch (rv)
+    {
+    case VNET_API_ERROR_NO_SUCH_ENTRY:
+      error = clib_error_return (0, "NAT64 BIB entry not exist.");
+      goto done;
+    case VNET_API_ERROR_VALUE_EXIST:
+      error = clib_error_return (0, "NAT64 BIB entry exist.");
+      goto done;
+    case VNET_API_ERROR_UNSPECIFIED:
+      error = clib_error_return (0, "Crerate NAT64 BIB entry failed.");
+      goto done;
+    case VNET_API_ERROR_INVALID_VALUE:
+      error =
+       clib_error_return (0, "Outside addres %U and port %u already in use.",
+                          format_ip4_address, &out_addr, out_port);
+      goto done;
+    default:
+      break;
+    }
+
+done:
+  unformat_free (line_input);
+
+  return error;
+}
+
+static int
+nat64_cli_bib_walk (nat64_db_bib_entry_t * bibe, void *ctx)
+{
+  vlib_main_t *vm = ctx;
+  fib_table_t *fib;
+
+  fib = fib_table_get (bibe->fib_index, FIB_PROTOCOL_IP6);
+  if (!fib)
+    return -1;
+
+  vlib_cli_output (vm, " %U %u %U %u %U vrf %u %s %u sessions",
+                  format_ip6_address, &bibe->in_addr,
+                  clib_net_to_host_u16 (bibe->in_port), format_ip4_address,
+                  &bibe->out_addr, clib_net_to_host_u16 (bibe->out_port),
+                  format_snat_protocol, bibe->proto, fib->ft_table_id,
+                  bibe->is_static ? "static" : "dynamic", bibe->ses_num);
+  return 0;
+}
+
+static clib_error_t *
+nat64_show_bib_command_fn (vlib_main_t * vm,
+                          unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  nat64_main_t *nm = &nat64_main;
+  unformat_input_t _line_input, *line_input = &_line_input;
+  clib_error_t *error = 0;
+  snat_protocol_t proto = 0;
+
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  if (unformat (line_input, "%U", unformat_snat_protocol, &proto))
+    ;
+  else
+    {
+      error = clib_error_return (0, "unknown input: '%U'",
+                                format_unformat_error, line_input);
+      goto done;
+    }
+
+  vlib_cli_output (vm, "NAT64 %U BIB:", format_snat_protocol, proto);
+  nat64_db_bib_walk (&nm->db, proto, nat64_cli_bib_walk, vm);
+
+done:
+  unformat_free (line_input);
+
+  return error;
+}
+
+static clib_error_t *
+nat64_set_timeouts_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;
+  u32 timeout, tcp_trans, tcp_est, tcp_incoming_syn;
+
+  tcp_trans = nat64_get_tcp_trans_timeout ();
+  tcp_est = nat64_get_tcp_est_timeout ();
+  tcp_incoming_syn = nat64_get_tcp_incoming_syn_timeout ();
+
+  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, "udp %u", &timeout))
+       {
+         if (nat64_set_udp_timeout (timeout))
+           {
+             error = clib_error_return (0, "Invalid UDP timeout value");
+             goto done;
+           }
+       }
+      else if (unformat (line_input, "icmp %u", &timeout))
+       {
+         if (nat64_set_icmp_timeout (timeout))
+           {
+             error = clib_error_return (0, "Invalid ICMP timeout value");
+             goto done;
+           }
+       }
+      else if (unformat (line_input, "tcp-trans %u", &tcp_trans))
+       {
+         if (nat64_set_tcp_timeouts (tcp_trans, tcp_est, tcp_incoming_syn))
+           {
+             error =
+               clib_error_return (0, "Invalid TCP transitory tiemout value");
+             goto done;
+           }
+       }
+      else if (unformat (line_input, "tcp-est %u", &tcp_est))
+       {
+         if (nat64_set_tcp_timeouts (tcp_trans, tcp_est, tcp_incoming_syn))
+           {
+             error =
+               clib_error_return (0,
+                                  "Invalid TCP established tiemout value");
+             goto done;
+           }
+       }
+      else
+       if (unformat (line_input, "tcp-incoming-syn %u", &tcp_incoming_syn))
+       {
+         if (nat64_set_tcp_timeouts (tcp_trans, tcp_est, tcp_incoming_syn))
+           {
+             error =
+               clib_error_return (0,
+                                  "Invalid TCP incoming SYN tiemout value");
+             goto done;
+           }
+       }
+      else if (unformat (line_input, "reset"))
+       {
+         nat64_set_udp_timeout (0);
+         nat64_set_icmp_timeout (0);
+         nat64_set_tcp_timeouts (0, 0, 0);
+       }
+      else
+       {
+         error = clib_error_return (0, "unknown input '%U'",
+                                    format_unformat_error, line_input);
+         goto done;
+       }
+    }
+
+done:
+  unformat_free (line_input);
+
+  return error;
+}
+
+static clib_error_t *
+nat64_show_timeouts_command_fn (vlib_main_t * vm, unformat_input_t * input,
+                               vlib_cli_command_t * cmd)
+{
+  vlib_cli_output (vm, "NAT64 session timeouts:");
+  vlib_cli_output (vm, " UDP %usec", nat64_get_udp_timeout ());
+  vlib_cli_output (vm, " ICMP %usec", nat64_get_icmp_timeout ());
+  vlib_cli_output (vm, " TCP transitory %usec",
+                  nat64_get_tcp_trans_timeout ());
+  vlib_cli_output (vm, " TCP established %usec",
+                  nat64_get_tcp_est_timeout ());
+  vlib_cli_output (vm, " TCP incoming SYN %usec",
+                  nat64_get_tcp_incoming_syn_timeout ());
+
+  return 0;
+}
+
+static int
+nat64_cli_st_walk (nat64_db_st_entry_t * ste, void *ctx)
+{
+  vlib_main_t *vm = ctx;
+  nat64_main_t *nm = &nat64_main;
+  nat64_db_bib_entry_t *bibe;
+  fib_table_t *fib;
+
+  bibe = nat64_db_bib_entry_by_index (&nm->db, ste->proto, ste->bibe_index);
+  if (!bibe)
+    return -1;
+
+  fib = fib_table_get (bibe->fib_index, FIB_PROTOCOL_IP6);
+  if (!fib)
+    return -1;
+
+  u32 vrf_id = fib->ft_table_id;
+
+  if (ste->proto == SNAT_PROTOCOL_ICMP)
+    vlib_cli_output (vm, " %U %U %u %U %U %u %U vrf %u",
+                    format_ip6_address, &bibe->in_addr,
+                    format_ip6_address, &ste->in_r_addr,
+                    clib_net_to_host_u16 (bibe->in_port),
+                    format_ip4_address, &bibe->out_addr,
+                    format_ip4_address, &ste->out_r_addr,
+                    clib_net_to_host_u16 (bibe->out_port),
+                    format_snat_protocol, bibe->proto, vrf_id);
+  else
+    vlib_cli_output (vm, " %U %u %U %u %U %u %U %u %U vrf %u",
+                    format_ip6_address, &bibe->in_addr,
+                    clib_net_to_host_u16 (bibe->in_port),
+                    format_ip6_address, &ste->in_r_addr,
+                    clib_net_to_host_u16 (ste->r_port),
+                    format_ip4_address, &bibe->out_addr,
+                    clib_net_to_host_u16 (bibe->out_port),
+                    format_ip4_address, &ste->out_r_addr,
+                    clib_net_to_host_u16 (ste->r_port),
+                    format_snat_protocol, bibe->proto, vrf_id);
+  return 0;
+}
+
+static clib_error_t *
+nat64_show_st_command_fn (vlib_main_t * vm,
+                         unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  nat64_main_t *nm = &nat64_main;
+  unformat_input_t _line_input, *line_input = &_line_input;
+  clib_error_t *error = 0;
+  snat_protocol_t proto = 0;
+
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  if (unformat (line_input, "%U", unformat_snat_protocol, &proto))
+    ;
+  else
+    {
+      error = clib_error_return (0, "unknown input: '%U'",
+                                format_unformat_error, line_input);
+      goto done;
+    }
+
+  vlib_cli_output (vm, "NAT64 %U session table:", format_snat_protocol,
+                  proto);
+  nat64_db_st_walk (&nm->db, proto, nat64_cli_st_walk, vm);
+
+done:
+  unformat_free (line_input);
+
+  return error;
+}
+
+/* *INDENT-OFF* */
+
+VLIB_CLI_COMMAND (nat64_add_pool_address_command, static) = {
+  .path = "nat64 add pool address",
+  .short_help = "nat64 add pool address <ip4-range-start> [- <ip4-range-end>] "
+                "[tenant-vrf <vrf-id>] [del]",
+  .function = nat64_add_del_pool_addr_command_fn,
+};
+
+VLIB_CLI_COMMAND (show_nat64_pool_command, static) = {
+  .path = "show nat64 pool",
+  .short_help = "show nat64 pool",
+  .function = nat64_show_pool_command_fn,
+};
+
+VLIB_CLI_COMMAND (set_interface_nat64_command, static) = {
+  .path = "set interface nat64",
+  .short_help = "set interface nat64 in|out <intfc> [del]",
+  .function = nat64_interface_feature_command_fn,
+};
+
+VLIB_CLI_COMMAND (show_nat64_interfaces_command, static) = {
+  .path = "show nat64 interfaces",
+  .short_help = "show nat64 interfaces",
+  .function = nat64_show_interfaces_command_fn,
+};
+
+VLIB_CLI_COMMAND (nat64_add_del_static_bib_command, static) = {
+  .path = "nat64 add static bib",
+  .short_help = "nat64 add static bib <ip6-addr> <port> <ip4-addr> <port> "
+                "tcp|udp|icmp [vfr <table-id>] [del]",
+  .function = nat64_add_del_static_bib_command_fn,
+};
+
+VLIB_CLI_COMMAND (show_nat64_bib_command, static) = {
+  .path = "show nat64 bib",
+  .short_help = "show nat64 bib tcp|udp|icmp",
+  .function = nat64_show_bib_command_fn,
+};
+
+VLIB_CLI_COMMAND (set_nat64_timeouts_command, static) = {
+  .path = "set nat64 timeouts",
+  .short_help = "set nat64 timeouts udp <sec> icmp <sec> tcp-trans <sec> "
+                "tcp-est <sec> tcp-incoming-syn <sec> | reset",
+  .function = nat64_set_timeouts_command_fn,
+};
+
+VLIB_CLI_COMMAND (show_nat64_timeouts_command, static) = {
+  .path = "show nat64 tiemouts",
+  .short_help = "show nat64 tiemouts",
+  .function = nat64_show_timeouts_command_fn,
+};
+
+VLIB_CLI_COMMAND (show_nat64_st_command, static) = {
+  .path = "show nat64 session table",
+  .short_help = "show nat64 session table tcp|udp|icmp",
+  .function = nat64_show_st_command_fn,
+};
+
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/snat/nat64_db.c b/src/plugins/snat/nat64_db.c
new file mode 100644 (file)
index 0000000..d9989c9
--- /dev/null
@@ -0,0 +1,497 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @file
+ * @brief NAT64 DB
+ */
+#include <snat/nat64_db.h>
+
+int
+nat64_db_init (nat64_db_t * db)
+{
+  u32 bib_buckets = 1024;
+  u32 bib_memory_size = 128 << 20;
+  u32 st_buckets = 2048;
+  u32 st_memory_size = 256 << 20;
+
+  clib_bihash_init_24_8 (&db->bib.in2out, "bib-in2out", bib_buckets,
+                        bib_memory_size);
+
+  clib_bihash_init_24_8 (&db->bib.out2in, "bib-out2in", bib_buckets,
+                        bib_memory_size);
+
+  clib_bihash_init_48_8 (&db->st.in2out, "st-in2out", st_buckets,
+                        st_memory_size);
+
+  clib_bihash_init_48_8 (&db->st.out2in, "st-out2in", st_buckets,
+                        st_memory_size);
+
+  return 0;
+}
+
+nat64_db_bib_entry_t *
+nat64_db_bib_entry_create (nat64_db_t * db, ip6_address_t * in_addr,
+                          ip4_address_t * out_addr, u16 in_port,
+                          u16 out_port, u32 fib_index, snat_protocol_t proto,
+                          u8 is_static)
+{
+  nat64_db_bib_entry_t *bibe;
+  nat64_db_bib_entry_key_t bibe_key;
+  clib_bihash_kv_24_8_t kv;
+
+  /* create pool entry */
+  switch (proto)
+    {
+/* *INDENT-OFF* */
+#define _(N, i, n, s) \
+    case SNAT_PROTOCOL_##N: \
+      pool_get (db->bib._##n##_bib, bibe); \
+      kv.value = bibe - db->bib._##n##_bib; \
+      break;
+      foreach_snat_protocol
+#undef _
+/* *INDENT-ON* */
+    default:
+      clib_warning ("unknown protocol %u", proto);
+      return 0;
+    }
+  memset (bibe, 0, sizeof (*bibe));
+  bibe->in_addr.as_u64[0] = in_addr->as_u64[0];
+  bibe->in_addr.as_u64[1] = in_addr->as_u64[1];
+  bibe->in_port = in_port;
+  bibe->out_addr.as_u32 = out_addr->as_u32;
+  bibe->out_port = out_port;
+  bibe->fib_index = fib_index;
+  bibe->proto = proto;
+  bibe->is_static = is_static;
+
+  /* create hash lookup */
+  bibe_key.addr.as_u64[0] = bibe->in_addr.as_u64[0];
+  bibe_key.addr.as_u64[1] = bibe->in_addr.as_u64[1];
+  bibe_key.fib_index = bibe->fib_index;
+  bibe_key.port = bibe->in_port;
+  bibe_key.proto = bibe->proto;
+  bibe_key.rsvd = 0;
+  kv.key[0] = bibe_key.as_u64[0];
+  kv.key[1] = bibe_key.as_u64[1];
+  kv.key[2] = bibe_key.as_u64[2];
+  clib_bihash_add_del_24_8 (&db->bib.in2out, &kv, 1);
+
+  memset (&bibe_key.addr, 0, sizeof (bibe_key.addr));
+  bibe_key.addr.ip4.as_u32 = bibe->out_addr.as_u32;
+  bibe_key.fib_index = 0;
+  bibe_key.port = bibe->out_port;
+  kv.key[0] = bibe_key.as_u64[0];
+  kv.key[1] = bibe_key.as_u64[1];
+  kv.key[2] = bibe_key.as_u64[2];
+  clib_bihash_add_del_24_8 (&db->bib.out2in, &kv, 1);
+
+  return bibe;
+}
+
+void
+nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe)
+{
+  nat64_db_bib_entry_key_t bibe_key;
+  clib_bihash_kv_24_8_t kv;
+  nat64_db_bib_entry_t *bib;
+
+  switch (bibe->proto)
+    {
+/* *INDENT-OFF* */
+#define _(N, i, n, s) \
+    case SNAT_PROTOCOL_##N: \
+      bib = db->bib._##n##_bib; \
+      break;
+      foreach_snat_protocol
+#undef _
+/* *INDENT-ON* */
+    default:
+      clib_warning ("unknown protocol %u", bibe->proto);
+      return;
+    }
+
+  /* delete hash lookup */
+  bibe_key.addr.as_u64[0] = bibe->in_addr.as_u64[0];
+  bibe_key.addr.as_u64[1] = bibe->in_addr.as_u64[1];
+  bibe_key.fib_index = bibe->fib_index;
+  bibe_key.port = bibe->in_port;
+  bibe_key.proto = bibe->proto;
+  bibe_key.rsvd = 0;
+  kv.key[0] = bibe_key.as_u64[0];
+  kv.key[1] = bibe_key.as_u64[1];
+  kv.key[2] = bibe_key.as_u64[2];
+  clib_bihash_add_del_24_8 (&db->bib.in2out, &kv, 0);
+
+  memset (&bibe_key.addr, 0, sizeof (bibe_key.addr));
+  bibe_key.addr.ip4.as_u32 = bibe->out_addr.as_u32;
+  bibe_key.fib_index = 0;
+  bibe_key.port = bibe->out_port;
+  kv.key[0] = bibe_key.as_u64[0];
+  kv.key[1] = bibe_key.as_u64[1];
+  kv.key[2] = bibe_key.as_u64[2];
+  clib_bihash_add_del_24_8 (&db->bib.out2in, &kv, 0);
+
+  /* delete from pool */
+  pool_put (bib, bibe);
+}
+
+nat64_db_bib_entry_t *
+nat64_db_bib_entry_find (nat64_db_t * db, ip46_address_t * addr, u16 port,
+                        snat_protocol_t proto, u32 fib_index, u8 is_ip6)
+{
+  nat64_db_bib_entry_t *bibe = 0;
+  nat64_db_bib_entry_key_t bibe_key;
+  clib_bihash_kv_24_8_t kv, value;
+  nat64_db_bib_entry_t *bib;
+
+  switch (proto)
+    {
+/* *INDENT-OFF* */
+#define _(N, i, n, s) \
+    case SNAT_PROTOCOL_##N: \
+      bib = db->bib._##n##_bib; \
+      break;
+      foreach_snat_protocol
+#undef _
+/* *INDENT-ON* */
+    default:
+      clib_warning ("unknown protocol %u", proto);
+      return 0;
+    }
+
+  bibe_key.addr.as_u64[0] = addr->as_u64[0];
+  bibe_key.addr.as_u64[1] = addr->as_u64[1];
+  bibe_key.fib_index = fib_index;
+  bibe_key.port = port;
+  bibe_key.proto = proto;
+  bibe_key.rsvd = 0;
+
+  kv.key[0] = bibe_key.as_u64[0];
+  kv.key[1] = bibe_key.as_u64[1];
+  kv.key[2] = bibe_key.as_u64[2];
+
+  if (!clib_bihash_search_24_8
+      (is_ip6 ? &db->bib.in2out : &db->bib.out2in, &kv, &value))
+    bibe = pool_elt_at_index (bib, value.value);
+
+  return bibe;
+}
+
+void
+nat64_db_bib_walk (nat64_db_t * db, snat_protocol_t proto,
+                  nat64_db_bib_walk_fn_t fn, void *ctx)
+{
+  nat64_db_bib_entry_t *bib, *bibe;
+
+  switch (proto)
+    {
+/* *INDENT-OFF* */
+#define _(N, i, n, s) \
+    case SNAT_PROTOCOL_##N: \
+      bib = db->bib._##n##_bib; \
+      break;
+      foreach_snat_protocol
+#undef _
+/* *INDENT-ON* */
+    default:
+      clib_warning ("unknown protocol");
+      return;
+    }
+
+  /* *INDENT-OFF* */
+  pool_foreach (bibe, bib,
+  ({
+    if (fn (bibe, ctx))
+      return;
+  }));
+  /* *INDENT-ON* */
+}
+
+nat64_db_bib_entry_t *
+nat64_db_bib_entry_by_index (nat64_db_t * db, snat_protocol_t proto,
+                            u32 bibe_index)
+{
+  nat64_db_bib_entry_t *bib;
+
+  switch (proto)
+    {
+/* *INDENT-OFF* */
+#define _(N, i, n, s) \
+    case SNAT_PROTOCOL_##N: \
+      bib = db->bib._##n##_bib; \
+      break;
+      foreach_snat_protocol
+#undef _
+/* *INDENT-ON* */
+    default:
+      clib_warning ("unknown protocol %u", proto);
+      return 0;
+    }
+
+  return pool_elt_at_index (bib, bibe_index);
+}
+
+void
+nat64_db_st_walk (nat64_db_t * db, snat_protocol_t proto,
+                 nat64_db_st_walk_fn_t fn, void *ctx)
+{
+  nat64_db_st_entry_t *st, *ste;
+
+  switch (proto)
+    {
+/* *INDENT-OFF* */
+#define _(N, i, n, s) \
+    case SNAT_PROTOCOL_##N: \
+      st = db->st._##n##_st; \
+      break;
+      foreach_snat_protocol
+#undef _
+/* *INDENT-ON* */
+    default:
+      clib_warning ("unknown protocol");
+      return;
+    }
+
+  /* *INDENT-OFF* */
+  pool_foreach (ste, st,
+  ({
+    if (fn (ste, ctx))
+      return;
+  }));
+  /* *INDENT-ON* */
+}
+
+nat64_db_st_entry_t *
+nat64_db_st_entry_create (nat64_db_t * db, nat64_db_bib_entry_t * bibe,
+                         ip6_address_t * in_r_addr,
+                         ip4_address_t * out_r_addr, u16 r_port)
+{
+  nat64_db_st_entry_t *ste;
+  nat64_db_bib_entry_t *bib;
+  nat64_db_st_entry_key_t ste_key;
+  clib_bihash_kv_48_8_t kv;
+
+  /* create pool entry */
+  switch (bibe->proto)
+    {
+/* *INDENT-OFF* */
+#define _(N, i, n, s) \
+    case SNAT_PROTOCOL_##N: \
+      pool_get (db->st._##n##_st, ste); \
+      kv.value = ste - db->st._##n##_st; \
+      bib = db->bib._##n##_bib; \
+      break;
+      foreach_snat_protocol
+#undef _
+/* *INDENT-ON* */
+    default:
+      clib_warning ("unknown protocol %u", bibe->proto);
+      return 0;
+    }
+  memset (ste, 0, sizeof (*ste));
+  ste->in_r_addr.as_u64[0] = in_r_addr->as_u64[0];
+  ste->in_r_addr.as_u64[1] = in_r_addr->as_u64[1];
+  ste->out_r_addr.as_u32 = out_r_addr->as_u32;
+  ste->r_port = r_port;
+  ste->bibe_index = bibe - bib;
+  ste->proto = bibe->proto;
+
+  /* increment session number for BIB entry */
+  bibe->ses_num++;
+
+  /* create hash lookup */
+  memset (&ste_key, 0, sizeof (ste_key));
+  ste_key.l_addr.as_u64[0] = bibe->in_addr.as_u64[0];
+  ste_key.l_addr.as_u64[1] = bibe->in_addr.as_u64[1];
+  ste_key.r_addr.as_u64[0] = ste->in_r_addr.as_u64[0];
+  ste_key.r_addr.as_u64[1] = ste->in_r_addr.as_u64[1];
+  ste_key.fib_index = bibe->fib_index;
+  ste_key.l_port = bibe->in_port;
+  ste_key.r_port = ste->r_port;
+  ste_key.proto = ste->proto;
+  kv.key[0] = ste_key.as_u64[0];
+  kv.key[1] = ste_key.as_u64[1];
+  kv.key[2] = ste_key.as_u64[2];
+  kv.key[3] = ste_key.as_u64[3];
+  kv.key[4] = ste_key.as_u64[4];
+  kv.key[5] = ste_key.as_u64[5];
+  clib_bihash_add_del_48_8 (&db->st.in2out, &kv, 1);
+
+  memset (&ste_key, 0, sizeof (ste_key));
+  ste_key.l_addr.ip4.as_u32 = bibe->out_addr.as_u32;
+  ste_key.r_addr.ip4.as_u32 = ste->out_r_addr.as_u32;
+  ste_key.l_port = bibe->out_port;
+  ste_key.r_port = ste->r_port;
+  ste_key.proto = ste->proto;
+  kv.key[0] = ste_key.as_u64[0];
+  kv.key[1] = ste_key.as_u64[1];
+  kv.key[2] = ste_key.as_u64[2];
+  kv.key[3] = ste_key.as_u64[3];
+  kv.key[4] = ste_key.as_u64[4];
+  kv.key[5] = ste_key.as_u64[5];
+  clib_bihash_add_del_48_8 (&db->st.out2in, &kv, 1);
+
+  return ste;
+}
+
+void
+nat64_db_st_entry_free (nat64_db_t * db, nat64_db_st_entry_t * ste)
+{
+  nat64_db_st_entry_t *st;
+  nat64_db_bib_entry_t *bib, *bibe;
+  nat64_db_st_entry_key_t ste_key;
+  clib_bihash_kv_48_8_t kv;
+
+  switch (ste->proto)
+    {
+/* *INDENT-OFF* */
+#define _(N, i, n, s) \
+    case SNAT_PROTOCOL_##N: \
+      st = db->st._##n##_st; \
+      bib = db->bib._##n##_bib; \
+      break;
+      foreach_snat_protocol
+#undef _
+/* *INDENT-ON* */
+    default:
+      clib_warning ("unknown protocol %u", ste->proto);
+      return;
+    }
+
+  bibe = pool_elt_at_index (bib, ste->bibe_index);
+
+  /* delete hash lookup */
+  memset (&ste_key, 0, sizeof (ste_key));
+  ste_key.l_addr.as_u64[0] = bibe->in_addr.as_u64[0];
+  ste_key.l_addr.as_u64[1] = bibe->in_addr.as_u64[1];
+  ste_key.r_addr.as_u64[0] = ste->in_r_addr.as_u64[0];
+  ste_key.r_addr.as_u64[1] = ste->in_r_addr.as_u64[1];
+  ste_key.fib_index = bibe->fib_index;
+  ste_key.l_port = bibe->in_port;
+  ste_key.r_port = ste->r_port;
+  ste_key.proto = ste->proto;
+  kv.key[0] = ste_key.as_u64[0];
+  kv.key[1] = ste_key.as_u64[1];
+  kv.key[2] = ste_key.as_u64[2];
+  kv.key[3] = ste_key.as_u64[3];
+  kv.key[4] = ste_key.as_u64[4];
+  kv.key[5] = ste_key.as_u64[5];
+  clib_bihash_add_del_48_8 (&db->st.in2out, &kv, 0);
+
+  memset (&ste_key, 0, sizeof (ste_key));
+  ste_key.l_addr.ip4.as_u32 = bibe->out_addr.as_u32;
+  ste_key.r_addr.ip4.as_u32 = ste->out_r_addr.as_u32;
+  ste_key.l_port = bibe->out_port;
+  ste_key.r_port = ste->r_port;
+  ste_key.proto = ste->proto;
+  kv.key[0] = ste_key.as_u64[0];
+  kv.key[1] = ste_key.as_u64[1];
+  kv.key[2] = ste_key.as_u64[2];
+  kv.key[3] = ste_key.as_u64[3];
+  kv.key[4] = ste_key.as_u64[4];
+  kv.key[5] = ste_key.as_u64[5];
+  clib_bihash_add_del_48_8 (&db->st.out2in, &kv, 0);
+
+  /* delete from pool */
+  pool_put (st, ste);
+
+  /* decrement session number for BIB entry */
+  bibe->ses_num--;
+
+  /* delete BIB entry if last session and dynamic */
+  if (!bibe->is_static && !bibe->ses_num)
+    nat64_db_bib_entry_free (db, bibe);
+}
+
+nat64_db_st_entry_t *
+nat64_db_st_entry_find (nat64_db_t * db, ip46_address_t * l_addr,
+                       ip46_address_t * r_addr, u16 l_port, u16 r_port,
+                       snat_protocol_t proto, u32 fib_index, u8 is_ip6)
+{
+  nat64_db_st_entry_t *ste = 0;
+  nat64_db_st_entry_t *st;
+  nat64_db_st_entry_key_t ste_key;
+  clib_bihash_kv_48_8_t kv, value;
+
+  switch (proto)
+    {
+/* *INDENT-OFF* */
+#define _(N, i, n, s) \
+    case SNAT_PROTOCOL_##N: \
+      st = db->st._##n##_st; \
+      break;
+      foreach_snat_protocol
+#undef _
+/* *INDENT-ON* */
+    default:
+      clib_warning ("unknown protocol %u", proto);
+      return ste;
+    }
+
+  memset (&ste_key, 0, sizeof (ste_key));
+  ste_key.l_addr.as_u64[0] = l_addr->as_u64[0];
+  ste_key.l_addr.as_u64[1] = l_addr->as_u64[1];
+  ste_key.r_addr.as_u64[0] = r_addr->as_u64[0];
+  ste_key.r_addr.as_u64[1] = r_addr->as_u64[1];
+  ste_key.fib_index = fib_index;
+  ste_key.l_port = l_port;
+  ste_key.r_port = r_port;
+  ste_key.proto = proto;
+  kv.key[0] = ste_key.as_u64[0];
+  kv.key[1] = ste_key.as_u64[1];
+  kv.key[2] = ste_key.as_u64[2];
+  kv.key[3] = ste_key.as_u64[3];
+  kv.key[4] = ste_key.as_u64[4];
+  kv.key[5] = ste_key.as_u64[5];
+
+  if (!clib_bihash_search_48_8
+      (is_ip6 ? &db->st.in2out : &db->st.out2in, &kv, &value))
+    ste = pool_elt_at_index (st, value.value);
+
+  return ste;
+}
+
+void
+nad64_db_st_free_expired (nat64_db_t * db, u32 now)
+{
+  u32 *ste_to_be_free = 0, *ste_index;
+  nat64_db_st_entry_t *st, *ste;
+
+/* *INDENT-OFF* */
+#define _(N, i, n, s) \
+  st = db->st._##n##_st; \
+  pool_foreach (ste, st, ({\
+    if (i == SNAT_PROTOCOL_TCP && !ste->tcp_state) \
+      continue; \
+    if (ste->expire < now) \
+      vec_add1 (ste_to_be_free, ste - st); \
+  })); \
+  vec_foreach (ste_index, ste_to_be_free) \
+    pool_put_index (st, ste_index[0]); \
+  vec_free (ste_to_be_free); \
+  ste_to_be_free = 0;
+  foreach_snat_protocol
+#undef _
+/* *INDENT-ON* */
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/snat/nat64_db.h b/src/plugins/snat/nat64_db.h
new file mode 100644 (file)
index 0000000..caea3bf
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @file
+ * @brief NAT64 DB
+ */
+#ifndef __included_nat64_db_h__
+#define __included_nat64_db_h__
+
+#include <vppinfra/bihash_24_8.h>
+#include <vppinfra/bihash_48_8.h>
+#include <snat/snat.h>
+
+
+typedef struct
+{
+  union
+  {
+    struct
+    {
+      ip46_address_t addr;
+      u32 fib_index;
+      u16 port;
+      u8 proto;
+      u8 rsvd;
+    };
+    u64 as_u64[3];
+  };
+} nat64_db_bib_entry_key_t;
+
+/* *INDENT-OFF* */
+typedef CLIB_PACKED(struct
+{
+  ip6_address_t in_addr;
+  u16 in_port;
+  ip4_address_t out_addr;
+  u16 out_port;
+  u32 fib_index;
+  u32 ses_num;
+  u8 proto;
+  u8 is_static;
+}) nat64_db_bib_entry_t;
+/* *INDENT-ON* */
+
+typedef struct
+{
+  /* BIBs */
+/* *INDENT-OFF* */
+#define _(N, i, n, s) \
+  nat64_db_bib_entry_t *_##n##_bib;
+  foreach_snat_protocol
+#undef _
+/* *INDENT-ON* */
+
+  /* BIB lookup */
+  clib_bihash_24_8_t in2out;
+  clib_bihash_24_8_t out2in;
+} nat64_db_bib_t;
+
+typedef struct
+{
+  union
+  {
+    struct
+    {
+      ip46_address_t l_addr;
+      ip46_address_t r_addr;
+      u32 fib_index;
+      u16 l_port;
+      u16 r_port;
+      u8 proto;
+      u8 rsvd[7];
+    };
+    u64 as_u64[6];
+  };
+} nat64_db_st_entry_key_t;
+
+/* *INDENT-OFF* */
+typedef CLIB_PACKED(struct
+{
+  ip6_address_t in_r_addr;
+  ip4_address_t out_r_addr;
+  u16 r_port;
+  u32 bibe_index;
+  u32 expire;
+  u8 proto;
+  u8 tcp_state;
+}) nat64_db_st_entry_t;
+/* *INDENT-ON* */
+
+typedef struct
+{
+  /* session tables */
+/* *INDENT-OFF* */
+#define _(N, i, n, s) \
+  nat64_db_st_entry_t *_##n##_st;
+  foreach_snat_protocol
+#undef _
+/* *INDENT-ON* */
+
+  /* session lookup */
+  clib_bihash_48_8_t in2out;
+  clib_bihash_48_8_t out2in;
+} nat64_db_st_t;
+
+typedef struct
+{
+  nat64_db_bib_t bib;
+  nat64_db_st_t st;
+} nat64_db_t;
+
+/**
+ * @brief Initialize NAT64 DB.
+ *
+ * @param db NAT64 DB.
+ *
+ * @returns 0 on success, non-zero value otherwise.
+ */
+int nat64_db_init (nat64_db_t * db);
+
+/**
+ * @brief Create new NAT64 BIB entry.
+ *
+ * @param db NAT64 DB.
+ * @param in_addr Inside IPv6 address.
+ * @param out_addr Outside IPv4 address.
+ * @param in_port Inside port number.
+ * @param out_port Outside port number.
+ * @param fib_index FIB index.
+ * @param proto L4 protocol.
+ * @param is_static 1 if static, 0 if dynamic.
+ *
+ * @returns BIB entry on success, 0 otherwise.
+ */
+nat64_db_bib_entry_t *nat64_db_bib_entry_create (nat64_db_t * db,
+                                                ip6_address_t * in_addr,
+                                                ip4_address_t * out_addr,
+                                                u16 in_port, u16 out_port,
+                                                u32 fib_index,
+                                                snat_protocol_t proto,
+                                                u8 is_static);
+
+/**
+ * @brief Free NAT64 BIB entry.
+ *
+ * @param db NAT64 DB.
+ * @param bibe BIB entry.
+ */
+void nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe);
+
+/**
+ * @brief Call back function when walking NAT64 BIB, non-zero
+ * return value stop walk.
+ */
+typedef int (*nat64_db_bib_walk_fn_t) (nat64_db_bib_entry_t * bibe,
+                                      void *ctx);
+/**
+ * @brief Walk NAT64 BIB.
+ *
+ * @param db NAT64 DB.
+ * @param proto BIB protocol (TCP/UDP/ICMP).
+ * @param fn The function to invoke on each entry visited.
+ * @param ctx A context passed in the visit function.
+ */
+void nat64_db_bib_walk (nat64_db_t * db, snat_protocol_t proto,
+                       nat64_db_bib_walk_fn_t fn, void *ctx);
+
+/**
+ * @brief Find NAT64 BIB entry.
+ *
+ * @param db NAT64 DB.
+ * @param addr IP address.
+ * @param port Port number.
+ * @param proto L4 protocol.
+ * @param fib_index FIB index.
+ * @param is_ip6 1 if find by IPv6 (inside) address, 0 by IPv4 (outside).
+ *
+ * @return BIB entry if found.
+ */
+nat64_db_bib_entry_t *nat64_db_bib_entry_find (nat64_db_t * db,
+                                              ip46_address_t * addr,
+                                              u16 port,
+                                              snat_protocol_t proto,
+                                              u32 fib_index, u8 is_ip6);
+
+/**
+ * @brief Get BIB entry by index and protocol.
+ *
+ * @param db NAT64 DB.
+ * @param proto L4 protocol.
+ * @param bibe_index BIB entry index.
+ *
+ * @return BIB entry if found.
+ */
+nat64_db_bib_entry_t *nat64_db_bib_entry_by_index (nat64_db_t * db,
+                                                  snat_protocol_t proto,
+                                                  u32 bibe_index);
+/**
+ * @brief Create new NAT64 session table entry.
+ *
+ * @param db NAT64 DB.
+ * @param bibe Corresponding BIB entry.
+ * @param in_r_addr Inside IPv6 address of the remote host.
+ * @param out_r_addr Outside IPv4 address of the remote host.
+ * @param r_port Remote host port number.
+ *
+ * @returns BIB entry on success, 0 otherwise.
+ */
+nat64_db_st_entry_t *nat64_db_st_entry_create (nat64_db_t * db,
+                                              nat64_db_bib_entry_t * bibe,
+                                              ip6_address_t * in_r_addr,
+                                              ip4_address_t * out_r_addr,
+                                              u16 r_port);
+
+/**
+ * @brief Free NAT64 session table entry.
+ *
+ * @param db NAT64 DB.
+ * @param ste Session table entry.
+ */
+void nat64_db_st_entry_free (nat64_db_t * db, nat64_db_st_entry_t * ste);
+
+/**
+ * @brief Find NAT64 session table entry.
+ *
+ * @param db NAT64 DB.
+ * @param l_addr Local host address.
+ * @param r_addr Remote host address.
+ * @param l_port Local host port number.
+ * @param r_port Remote host port number.
+ * @param proto L4 protocol.
+ * @param fib_index FIB index.
+ * @param is_ip6 1 if find by IPv6 (inside) address, 0 by IPv4 (outside).
+ *
+ * @return BIB entry if found.
+ */
+nat64_db_st_entry_t *nat64_db_st_entry_find (nat64_db_t * db,
+                                            ip46_address_t * l_addr,
+                                            ip46_address_t * r_addr,
+                                            u16 l_port, u16 r_port,
+                                            snat_protocol_t proto,
+                                            u32 fib_index, u8 is_ip6);
+
+/**
+ * @brief Call back function when walking NAT64 session table, non-zero
+ * return value stop walk.
+ */
+typedef int (*nat64_db_st_walk_fn_t) (nat64_db_st_entry_t * ste, void *ctx);
+
+/**
+ * @brief Walk NAT64 session table.
+ *
+ * @param db NAT64 DB.
+ * @param proto Session table protocol (TCP/UDP/ICMP).
+ * @param fn The function to invoke on each entry visited.
+ * @param ctx A context passed in the visit function.
+ */
+void nat64_db_st_walk (nat64_db_t * db, snat_protocol_t proto,
+                      nat64_db_st_walk_fn_t fn, void *ctx);
+
+/**
+ * @brief Free expired session entries in session tables.
+ *
+ * @param db NAT64 DB.
+ * @param now Current time.
+ */
+void nad64_db_st_free_expired (nat64_db_t * db, u32 now);
+
+#endif /* __included_nat64_db_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/snat/nat64_in2out.c b/src/plugins/snat/nat64_in2out.c
new file mode 100644 (file)
index 0000000..d9d94a9
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @file
+ * @brief NAT64 IPv6 to IPv4 translation (inside to outside network)
+ */
+
+#include <snat/nat64.h>
+#include <vnet/ip/ip6_to_ip4.h>
+#include <vnet/fib/fib_table.h>
+
+typedef struct
+{
+  u32 sw_if_index;
+  u32 next_index;
+} nat64_in2out_trace_t;
+
+static u8 *
+format_nat64_in2out_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  nat64_in2out_trace_t *t = va_arg (*args, nat64_in2out_trace_t *);
+
+  s =
+    format (s, "NAT64-in2out: sw_if_index %d, next index %d", t->sw_if_index,
+           t->next_index);
+
+  return s;
+}
+
+vlib_node_registration_t nat64_in2out_node;
+
+#define foreach_nat64_in2out_error                 \
+_(UNSUPPORTED_PROTOCOL, "unsupported protocol")    \
+_(IN2OUT_PACKETS, "good in2out packets processed") \
+_(NO_TRANSLATION, "no translation")                \
+_(UNKNOWN, "unknown")
+
+typedef enum
+{
+#define _(sym,str) NAT64_IN2OUT_ERROR_##sym,
+  foreach_nat64_in2out_error
+#undef _
+    NAT64_IN2OUT_N_ERROR,
+} nat64_in2out_error_t;
+
+static char *nat64_in2out_error_strings[] = {
+#define _(sym,string) string,
+  foreach_nat64_in2out_error
+#undef _
+};
+
+typedef enum
+{
+  NAT64_IN2OUT_NEXT_LOOKUP,
+  NAT64_IN2OUT_NEXT_DROP,
+  NAT64_IN2OUT_N_NEXT,
+} nat64_in2out_next_t;
+
+typedef struct nat64_in2out_set_ctx_t_
+{
+  vlib_buffer_t *b;
+  vlib_main_t *vm;
+} nat64_in2out_set_ctx_t;
+
+static int
+nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
+                            void *arg)
+{
+  nat64_main_t *nm = &nat64_main;
+  nat64_in2out_set_ctx_t *ctx = arg;
+  nat64_db_bib_entry_t *bibe;
+  nat64_db_st_entry_t *ste;
+  ip46_address_t saddr, daddr;
+  u32 sw_if_index, fib_index;
+  udp_header_t *udp = ip6_next_header (ip6);
+  snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol);
+  u16 sport = udp->src_port;
+  u16 dport = udp->dst_port;
+
+  sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
+  fib_index =
+    fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
+
+  saddr.as_u64[0] = ip6->src_address.as_u64[0];
+  saddr.as_u64[1] = ip6->src_address.as_u64[1];
+  daddr.as_u64[0] = ip6->dst_address.as_u64[0];
+  daddr.as_u64[1] = ip6->dst_address.as_u64[1];
+
+  ste =
+    nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto,
+                           fib_index, 1);
+
+  if (ste)
+    {
+      bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
+      if (!bibe)
+       return -1;
+    }
+  else
+    {
+      bibe =
+       nat64_db_bib_entry_find (&nm->db, &saddr, sport, proto, fib_index, 1);
+
+      if (!bibe)
+       {
+         u16 out_port;
+         ip4_address_t out_addr;
+         if (nat64_alloc_out_addr_and_port
+             (fib_index, proto, &out_addr, &out_port))
+           return -1;
+
+         bibe =
+           nat64_db_bib_entry_create (&nm->db, &ip6->src_address, &out_addr,
+                                      sport, clib_host_to_net_u16 (out_port),
+                                      fib_index, proto, 0);
+         if (!bibe)
+           return -1;
+       }
+
+      ste =
+       nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
+                                 &daddr.ip4, dport);
+      if (!ste)
+       return -1;
+    }
+
+  nat64_session_reset_timeout (ste, ctx->vm);
+
+  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;
+
+  return 0;
+}
+
+static int
+nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
+{
+  nat64_main_t *nm = &nat64_main;
+  nat64_in2out_set_ctx_t *ctx = arg;
+  nat64_db_bib_entry_t *bibe;
+  nat64_db_st_entry_t *ste;
+  ip46_address_t saddr, daddr;
+  u32 sw_if_index, fib_index;
+  icmp46_header_t *icmp = ip6_next_header (ip6);
+
+  sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
+  fib_index =
+    fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
+
+  saddr.as_u64[0] = ip6->src_address.as_u64[0];
+  saddr.as_u64[1] = ip6->src_address.as_u64[1];
+  daddr.as_u64[0] = ip6->dst_address.as_u64[0];
+  daddr.as_u64[1] = ip6->dst_address.as_u64[1];
+
+  if (icmp->type == ICMP4_echo_request || icmp->type == ICMP4_echo_reply)
+    {
+      u16 in_id = ((u16 *) (icmp))[2];
+      ste =
+       nat64_db_st_entry_find (&nm->db, &saddr, &daddr, in_id, 0,
+                               SNAT_PROTOCOL_ICMP, fib_index, 1);
+
+      if (ste)
+       {
+         bibe =
+           nat64_db_bib_entry_by_index (&nm->db, SNAT_PROTOCOL_ICMP,
+                                        ste->bibe_index);
+         if (!bibe)
+           return -1;
+       }
+      else
+       {
+         bibe =
+           nat64_db_bib_entry_find (&nm->db, &saddr, in_id,
+                                    SNAT_PROTOCOL_ICMP, fib_index, 1);
+
+         if (!bibe)
+           {
+             u16 out_id;
+             ip4_address_t out_addr;
+             if (nat64_alloc_out_addr_and_port
+                 (fib_index, SNAT_PROTOCOL_ICMP, &out_addr, &out_id))
+               return -1;
+
+             bibe =
+               nat64_db_bib_entry_create (&nm->db, &ip6->src_address,
+                                          &out_addr, in_id,
+                                          clib_host_to_net_u16 (out_id),
+                                          fib_index, SNAT_PROTOCOL_ICMP, 0);
+             if (!bibe)
+               return -1;
+           }
+         ste =
+           nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
+                                     &daddr.ip4, 0);
+         if (!ste)
+           return -1;
+       }
+
+      nat64_session_reset_timeout (ste, ctx->vm);
+
+      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;
+    }
+  else
+    {
+      //TODO: ICMP error
+      clib_warning ("not ICMP echo request/reply, %u", icmp->type);
+      return -1;
+    }
+
+  return 0;
+}
+
+static int
+nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
+                               void *ctx)
+{
+  //TODO:
+  return -1;
+}
+
+static uword
+nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
+                     vlib_frame_t * frame)
+{
+  u32 n_left_from, *from, *to_next;
+  nat64_in2out_next_t next_index;
+  u32 pkts_processed = 0;
+
+  from = vlib_frame_vector_args (frame);
+  n_left_from = frame->n_vectors;
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         u32 bi0;
+         vlib_buffer_t *b0;
+         u32 next0;
+         ip6_header_t *ip60;
+         u16 l4_offset0, frag_offset0;
+         u8 l4_protocol0;
+         u32 proto0;
+         nat64_in2out_set_ctx_t ctx0;
+
+         /* speculatively enqueue b0 to the current next frame */
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         ip60 = vlib_buffer_get_current (b0);
+
+         ctx0.b = b0;
+         ctx0.vm = vm;
+
+         next0 = NAT64_IN2OUT_NEXT_LOOKUP;
+
+         if (PREDICT_FALSE
+             (ip6_parse
+              (ip60, b0->current_length, &l4_protocol0, &l4_offset0,
+               &frag_offset0)))
+           {
+             next0 = NAT64_IN2OUT_NEXT_DROP;
+             b0->error = node->errors[NAT64_IN2OUT_ERROR_UNKNOWN];
+             goto trace0;
+           }
+
+         proto0 = ip_proto_to_snat_proto (l4_protocol0);
+         if (PREDICT_FALSE ((proto0 == ~0) || (frag_offset0 != 0)))
+           {
+             next0 = NAT64_IN2OUT_NEXT_DROP;
+             b0->error =
+               node->errors[NAT64_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL];
+             goto trace0;
+           }
+
+         if (proto0 == SNAT_PROTOCOL_ICMP)
+           {
+             if (icmp6_to_icmp
+                 (b0, nat64_in2out_icmp_set_cb, &ctx0,
+                  nat64_in2out_inner_icmp_set_cb, &ctx0))
+               {
+                 next0 = NAT64_IN2OUT_NEXT_DROP;
+                 b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
+                 goto trace0;
+               }
+           }
+         else
+           {
+             if (ip6_to_ip4_tcp_udp
+                 (b0, nat64_in2out_tcp_udp_set_cb, &ctx0, 0))
+               {
+                 next0 = NAT64_IN2OUT_NEXT_DROP;
+                 b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
+                 goto trace0;
+               }
+           }
+
+       trace0:
+         if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+                            && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+           {
+             nat64_in2out_trace_t *t =
+               vlib_add_trace (vm, node, b0, sizeof (*t));
+             t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+             t->next_index = next0;
+           }
+
+         pkts_processed += next0 != NAT64_IN2OUT_NEXT_DROP;
+
+         /* verify speculative enqueue, maybe switch current next frame */
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+                                          n_left_to_next, bi0, next0);
+       }
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+  vlib_node_increment_counter (vm, nat64_in2out_node.index,
+                              NAT64_IN2OUT_ERROR_IN2OUT_PACKETS,
+                              pkts_processed);
+  return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat64_in2out_node) = {
+  .function = nat64_in2out_node_fn,.name = "nat64-in2out",
+  .vector_size = sizeof (u32),
+  .format_trace = format_nat64_in2out_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN (nat64_in2out_error_strings),
+  .error_strings = nat64_in2out_error_strings,
+  .n_next_nodes = 2,
+  /* edit / add dispositions here */
+  .next_nodes = {
+    [NAT64_IN2OUT_NEXT_DROP] = "error-drop",
+    [NAT64_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
+  },
+};
+/* *INDENT-ON* */
+
+VLIB_NODE_FUNCTION_MULTIARCH (nat64_in2out_node, nat64_in2out_node_fn);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/snat/nat64_out2in.c b/src/plugins/snat/nat64_out2in.c
new file mode 100644 (file)
index 0000000..3eed997
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @file
+ * @brief NAT64 IPv4 to IPv6 translation (otside to inside network)
+ */
+
+#include <snat/nat64.h>
+#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;
+  u32 next_index;
+} nat64_out2in_trace_t;
+
+static u8 *
+format_nat64_out2in_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  nat64_out2in_trace_t *t = va_arg (*args, nat64_out2in_trace_t *);
+
+  s =
+    format (s, "NAT64-out2in: sw_if_index %d, next index %d", t->sw_if_index,
+           t->next_index);
+
+  return s;
+}
+
+vlib_node_registration_t nat64_out2in_node;
+
+#define foreach_nat64_out2in_error                 \
+_(UNSUPPORTED_PROTOCOL, "Unsupported protocol")    \
+_(OUT2IN_PACKETS, "Good out2in packets processed") \
+_(NO_TRANSLATION, "No translation")                \
+_(UNKNOWN, "unknown")
+
+typedef enum
+{
+#define _(sym,str) NAT64_OUT2IN_ERROR_##sym,
+  foreach_nat64_out2in_error
+#undef _
+    NAT64_OUT2IN_N_ERROR,
+} nat64_out2in_error_t;
+
+static char *nat64_out2in_error_strings[] = {
+#define _(sym,string) string,
+  foreach_nat64_out2in_error
+#undef _
+};
+
+typedef enum
+{
+  NAT64_OUT2IN_NEXT_LOOKUP,
+  NAT64_OUT2IN_NEXT_DROP,
+  NAT64_OUT2IN_N_NEXT,
+} nat64_out2in_next_t;
+
+typedef struct nat64_out2in_set_ctx_t_
+{
+  vlib_buffer_t *b;
+  vlib_main_t *vm;
+} nat64_out2in_set_ctx_t;
+
+static int
+nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
+                            void *arg)
+{
+  nat64_main_t *nm = &nat64_main;
+  nat64_out2in_set_ctx_t *ctx = arg;
+  nat64_db_bib_entry_t *bibe;
+  nat64_db_st_entry_t *ste;
+  ip46_address_t saddr, daddr;
+  ip6_address_t ip6_saddr;
+  udp_header_t *udp = ip4_next_header (ip4);
+  snat_protocol_t proto = ip_proto_to_snat_proto (ip4->protocol);
+  u16 dport = udp->dst_port;
+  u16 sport = udp->src_port;
+  u32 sw_if_index, fib_index;
+
+  sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
+  fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
+
+  memset (&saddr, 0, sizeof (saddr));
+  saddr.ip4.as_u32 = ip4->src_address.as_u32;
+  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);
+  if (ste)
+    {
+      bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
+      if (!bibe)
+       return -1;
+    }
+  else
+    {
+      bibe =
+       nat64_db_bib_entry_find (&nm->db, &daddr, dport, proto, fib_index, 0);
+
+      if (!bibe)
+       return -1;
+
+      ste =
+       nat64_db_st_entry_create (&nm->db, bibe, &ip6_saddr, &saddr.ip4,
+                                 sport);
+    }
+
+  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->dst_address.as_u64[0] = bibe->in_addr.as_u64[0];
+  ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
+  udp->dst_port = bibe->in_port;
+
+  return 0;
+}
+
+static int
+nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
+{
+  nat64_main_t *nm = &nat64_main;
+  nat64_out2in_set_ctx_t *ctx = arg;
+  nat64_db_bib_entry_t *bibe;
+  nat64_db_st_entry_t *ste;
+  ip46_address_t saddr, daddr;
+  ip6_address_t ip6_saddr;
+  u32 sw_if_index, fib_index;
+  icmp46_header_t *icmp = ip4_next_header (ip4);
+
+  sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
+  fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
+
+  memset (&saddr, 0, sizeof (saddr));
+  saddr.ip4.as_u32 = ip4->src_address.as_u32;
+  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];
+      ste =
+       nat64_db_st_entry_find (&nm->db, &daddr, &saddr, out_id, 0,
+                               SNAT_PROTOCOL_ICMP, fib_index, 0);
+
+      if (ste)
+       {
+         bibe =
+           nat64_db_bib_entry_by_index (&nm->db, SNAT_PROTOCOL_ICMP,
+                                        ste->bibe_index);
+         if (!bibe)
+           return -1;
+       }
+      else
+       {
+         bibe =
+           nat64_db_bib_entry_find (&nm->db, &daddr, out_id,
+                                    SNAT_PROTOCOL_ICMP, fib_index, 0);
+         if (!bibe)
+           return -1;
+
+         ste =
+           nat64_db_st_entry_create (&nm->db, bibe, &ip6_saddr, &saddr.ip4,
+                                     0);
+       }
+
+      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->dst_address.as_u64[0] = bibe->in_addr.as_u64[0];
+      ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
+      ((u16 *) (icmp))[2] = bibe->in_port;
+
+    }
+  else
+    {
+      //TODO: ICMP error
+      clib_warning ("not ICMP echo request/reply, %u", icmp->type);
+      return -1;
+    }
+
+  return 0;
+}
+
+static int
+nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
+                               void *ctx)
+{
+  //TODO:
+  return -1;
+}
+
+static uword
+nat64_out2in_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
+                     vlib_frame_t * frame)
+{
+  u32 n_left_from, *from, *to_next;
+  nat64_out2in_next_t next_index;
+  u32 pkts_processed = 0;
+
+  from = vlib_frame_vector_args (frame);
+  n_left_from = frame->n_vectors;
+  next_index = node->cached_next_index;
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         u32 bi0;
+         vlib_buffer_t *b0;
+         u32 next0;
+         ip4_header_t *ip40;
+         u32 proto0;
+         nat64_out2in_set_ctx_t ctx0;
+
+         /* speculatively enqueue b0 to the current next frame */
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         ip40 = vlib_buffer_get_current (b0);
+
+         ctx0.b = b0;
+         ctx0.vm = vm;
+
+         next0 = NAT64_OUT2IN_NEXT_LOOKUP;
+
+         proto0 = ip_proto_to_snat_proto (ip40->protocol);
+         if (PREDICT_FALSE (proto0 == ~0))
+           {
+             next0 = NAT64_OUT2IN_NEXT_DROP;
+             b0->error =
+               node->errors[NAT64_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL];
+             goto trace0;
+           }
+
+         if (proto0 == SNAT_PROTOCOL_ICMP)
+           {
+             if (icmp_to_icmp6
+                 (b0, nat64_out2in_icmp_set_cb, &ctx0,
+                  nat64_out2in_inner_icmp_set_cb, &ctx0))
+               {
+                 next0 = NAT64_OUT2IN_NEXT_DROP;
+                 b0->error = node->errors[NAT64_OUT2IN_ERROR_NO_TRANSLATION];
+                 goto trace0;
+               }
+           }
+         else
+           {
+             if (ip4_to_ip6_tcp_udp (b0, nat64_out2in_tcp_udp_set_cb, &ctx0))
+               {
+                 next0 = NAT64_OUT2IN_NEXT_DROP;
+                 b0->error = node->errors[NAT64_OUT2IN_ERROR_NO_TRANSLATION];
+                 goto trace0;
+               }
+           }
+
+       trace0:
+         if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+                            && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+           {
+             nat64_out2in_trace_t *t =
+               vlib_add_trace (vm, node, b0, sizeof (*t));
+             t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+             t->next_index = next0;
+           }
+
+         pkts_processed += next0 != NAT64_OUT2IN_NEXT_DROP;
+
+         /* verify speculative enqueue, maybe switch current next frame */
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+                                          n_left_to_next, bi0, next0);
+       }
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+  vlib_node_increment_counter (vm, nat64_out2in_node.index,
+                              NAT64_OUT2IN_ERROR_OUT2IN_PACKETS,
+                              pkts_processed);
+  return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat64_out2in_node) = {
+  .function = nat64_out2in_node_fn,
+  .name = "nat64-out2in",
+  .vector_size = sizeof (u32),
+  .format_trace = format_nat64_out2in_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN (nat64_out2in_error_strings),
+  .error_strings = nat64_out2in_error_strings,.n_next_nodes = 2,
+  /* edit / add dispositions here */
+  .next_nodes = {
+    [NAT64_OUT2IN_NEXT_DROP] = "error-drop",
+    [NAT64_OUT2IN_NEXT_LOOKUP] = "ip6-lookup",
+  },
+};
+/* *INDENT-ON* */
+
+VLIB_NODE_FUNCTION_MULTIARCH (nat64_out2in_node, nat64_out2in_node_fn);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index e92675d..e6289af 100644 (file)
@@ -600,3 +600,215 @@ define snat_det_session_details {
   u8 state;
   u32 expire;
 };
+
+/** \brief Add/delete address range to NAT64 pool
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param start_addr - start address of the range
+    @param end_addr - end address of the range
+    @param vrf_id - VRF id of tenant, ~0 means independent of VRF
+    @param is_add - 1 if add, 0 if delete
+*/
+autoreply define nat64_add_del_pool_addr_range {
+  u32 client_index;
+  u32 context;
+  u8 start_addr[4];
+  u8 end_addr[4];
+  u32 vrf_id;
+  u8 is_add;
+};
+
+/** \brief Dump NAT64 pool addresses
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define nat64_pool_addr_dump {
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief NAT64 pool address details response
+    @param context - sender context, to match reply w/ request
+    @param address - IPv4 address
+    @param vfr_id - VRF id of tenant, ~0 means independent of VRF
+*/
+define nat64_pool_addr_details {
+  u32 context;
+  u8 address[4];
+  u32 vrf_id;
+};
+
+/** \brief Enable/disable NAT64 feature on the interface
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param sw_if_index - index of the interface
+    @param is_inside - 1 if inside, 0 if outside
+    @param is_add - 1 if add, 0 if delete
+*/
+autoreply define nat64_add_del_interface {
+  u32 client_index;
+  u32 context;
+  u32 sw_if_index;
+  u8 is_inside;
+  u8 is_add;
+};
+
+/** \brief Dump interfaces with NAT64 feature
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define nat64_interface_dump {
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief NAT64 interface details response
+    @param context - sender context, to match reply w/ request
+    @param is_inside - 1 if inside, 0 if outside
+    @param sw_if_index - index of the interface
+*/
+define nat64_interface_details {
+  u32 context;
+  u8 is_inside;
+  u32 sw_if_index;
+};
+
+/** \brief Add/delete NAT64 static BIB entry
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param i_addr - inside IPv6 address
+    @param o_addr - outside IPv4 address
+    @param i_port - inside port number
+    @param o_port - outside port number
+    @param vrf_id - VRF id of tenant
+    @param proto - protocol number
+    @param is_add - 1 if add, 0 if delete
+*/
+ autoreply define nat64_add_del_static_bib {
+  u32 client_index;
+  u32 context;
+  u8 i_addr[16];
+  u8 o_addr[4];
+  u16 i_port;
+  u16 o_port;
+  u32 vrf_id;
+  u8 proto;
+  u8 is_add;
+};
+
+/** \brief Dump NAT64 BIB
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param proto - protocol of the BIB
+*/
+define nat64_bib_dump {
+  u32 client_index;
+  u32 context;
+  u8 proto;
+};
+
+/** \brief NAT64 BIB details response
+    @param context - sender context, to match reply w/ request
+    @param i_addr - inside IPv6 address
+    @param o_addr - outside IPv4 address
+    @param i_port - inside port number
+    @param o_port - outside port number
+    @param vrf_id - VRF id of tenant
+    @param proto - protocol number
+    @param is_static - 1 if static BIB entry, 0 if dynamic
+    @param ses_num - number of sessions associated with the BIB entry
+*/
+define nat64_bib_details {
+  u32 context;
+  u8 i_addr[16];
+  u8 o_addr[4];
+  u16 i_port;
+  u16 o_port;
+  u32 vrf_id;
+  u8 proto;
+  u8 is_static;
+  u32 ses_num;
+};
+
+/** \brief Set values of timeouts for NAT64 (seconds, 0 = default)
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param udp - UDP timeout (default 300sec)
+    @param icmp - ICMP timeout (default 60sec)
+    @param tcp_trans - TCP transitory timeout (default 240sec)
+    @param tcp_est - TCP established timeout (default 7440sec)
+    @param tcp_incoming_syn - TCP incoming SYN timeout (default 6sec)
+*/
+autoreply define nat64_set_timeouts {
+  u32 client_index;
+  u32 context;
+  u32 udp;
+  u32 icmp;
+  u32 tcp_trans;
+  u32 tcp_est;
+  u32 tcp_incoming_syn;
+};
+
+/** \brief Get values of timeouts for NAT64 (seconds)
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define nat64_get_timeouts {
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief Get values of timeouts for NAT64 reply
+    @param context - sender context, to match reply w/ request
+    @param retval - return code
+    @param udp - UDP timeout
+    @param icmp - ICMP timeout
+    @param tcp_trans - TCP transitory timeout
+    @param tcp_est - TCP established timeout
+    @param tcp_incoming_syn - TCP incoming SYN timeout
+*/
+define nat64_get_timeouts_reply {
+  u32 context;
+  i32 retval;
+  u32 udp;
+  u32 icmp;
+  u32 tcp_trans;
+  u32 tcp_est;
+  u32 tcp_incoming_syn;
+};
+
+/** \brief Dump NAT64 session table
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param proto - protocol of the session table
+*/
+define nat64_st_dump {
+  u32 client_index;
+  u32 context;
+  u8 proto;
+};
+
+/** \brief NAT64 session table details response
+    @param context - sender context, to match reply w/ request
+    @param il_addr - inside IPv6 address of the local host
+    @param ol_addr - outside IPv4 address of the local host
+    @param il_port - inside port number id of the local host/inside ICMP id
+    @param ol_port - outside port number of the local host/outside ICMP id
+    @param il_addr - inside IPv6 address of the remote host
+    @param ol_addr - outside IPv4 address of the remote host
+    @param l_port - port number of the remote host (not used for ICMP)
+    @param vrf_id - VRF id of tenant
+    @param proto - protocol number
+*/
+define nat64_st_details {
+  u32 context;
+  u8 il_addr[16];
+  u8 ol_addr[4];
+  u16 il_port;
+  u16 ol_port;
+  u8 ir_addr[16];
+  u8 or_addr[4];
+  u16 r_port;
+  u32 vrf_id;
+  u8 proto;
+};
index 8aa9a83..e95abf2 100644 (file)
@@ -22,6 +22,7 @@
 #include <snat/snat.h>
 #include <snat/snat_ipfix_logging.h>
 #include <snat/snat_det.h>
+#include <snat/nat64.h>
 #include <vnet/fib/fib_table.h>
 #include <vnet/fib/ip4_fib.h>
 
@@ -781,6 +782,8 @@ static clib_error_t * snat_init (vlib_main_t * vm)
   /* Init IPFIX logging */
   snat_ipfix_logging_init(vm);
 
+  error = nat64_init(vm);
+
   return error;
 }
 
index d76026c..016c2ff 100644 (file)
 
 
 #define SNAT_UDP_TIMEOUT 300
+#define SNAT_UDP_TIMEOUT_MIN 120
 #define SNAT_TCP_TRANSITORY_TIMEOUT 240
 #define SNAT_TCP_ESTABLISHED_TIMEOUT 7440
+#define SNAT_TCP_INCOMING_SYN 6
 #define SNAT_ICMP_TIMEOUT 60
 
 /* Key */
@@ -383,14 +385,15 @@ typedef struct {
   u16 sequence;
 } icmp_echo_header_t;
 
-always_inline snat_protocol_t
+always_inline u32
 ip_proto_to_snat_proto (u8 ip_proto)
 {
-  snat_protocol_t snat_proto = ~0;
+  u32 snat_proto = ~0;
 
   snat_proto = (ip_proto == IP_PROTOCOL_UDP) ? SNAT_PROTOCOL_UDP : snat_proto;
   snat_proto = (ip_proto == IP_PROTOCOL_TCP) ? SNAT_PROTOCOL_TCP : snat_proto;
   snat_proto = (ip_proto == IP_PROTOCOL_ICMP) ? SNAT_PROTOCOL_ICMP : snat_proto;
+  snat_proto = (ip_proto == IP_PROTOCOL_ICMP6) ? SNAT_PROTOCOL_ICMP : snat_proto;
 
   return snat_proto;
 }
@@ -445,6 +448,9 @@ clib_error_t * snat_api_init(vlib_main_t * vm, snat_main_t * sm);
 int snat_set_workers (uword * bitmap);
 int snat_interface_add_del(u32 sw_if_index, u8 is_inside, int is_del);
 int snat_add_interface_address(snat_main_t *sm, u32 sw_if_index, int is_del);
+uword unformat_snat_protocol(unformat_input_t * input, va_list * args);
+u8 * format_snat_protocol(u8 * s, va_list * args);
+
 static_always_inline u8
 icmp_is_error_message (icmp46_header_t * icmp)
 {
index 64532c8..2a7f115 100644 (file)
 
 #include <snat/snat.h>
 #include <snat/snat_det.h>
+#include <snat/nat64.h>
 #include <vlibapi/api.h>
 #include <vlibmemory/api.h>
 #include <vlibsocket/api.h>
 #include <snat/snat_msg_enum.h>
-#include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/fib_table.h>
 
 /* define message structures */
 #define vl_typedefs
@@ -138,7 +139,10 @@ static void
   rmp->is_ip4 = 1;
   clib_memcpy (rmp->ip_address, &(a->addr), 4);
   if (a->fib_index != ~0)
-    rmp->vrf_id = ntohl (ip4_fib_get (a->fib_index)->table_id);
+    {
+      fib_table_t *fib = fib_table_get (a->fib_index, FIB_PROTOCOL_IP4);
+      rmp->vrf_id = ntohl (fib->ft_table_id);
+    }
   else
     rmp->vrf_id = ~0;
   rmp->context = context;
@@ -669,14 +673,13 @@ static void
 {
   vl_api_snat_user_details_t *rmp;
   snat_main_t *sm = &snat_main;
-  ip4_fib_t *fib_table;
+  fib_table_t *fib = fib_table_get (u->fib_index, FIB_PROTOCOL_IP4);
 
   rmp = vl_msg_api_alloc (sizeof (*rmp));
   memset (rmp, 0, sizeof (*rmp));
   rmp->_vl_msg_id = ntohs (VL_API_SNAT_USER_DETAILS + sm->msg_id_base);
 
-  fib_table = ip4_fib_get (u->fib_index);
-  rmp->vrf_id = ntohl (fib_table->table_id);
+  rmp->vrf_id = ntohl (fib->ft_table_id);
 
   rmp->is_ip4 = 1;
   clib_memcpy (rmp->ip_address, &(u->addr), 4);
@@ -763,7 +766,7 @@ static void
     return;
 
   clib_memcpy (&ukey.addr, mp->ip_address, 4);
-  ukey.fib_index = ip4_fib_index_from_table_id (ntohl (mp->vrf_id));
+  ukey.fib_index = fib_table_find (FIB_PROTOCOL_IP4, ntohl (mp->vrf_id));
   key.key = ukey.as_u64;
   if (!clib_bihash_search_8_8 (&sm->worker_by_in, &key, &value))
     tsm = vec_elt_at_index (sm->per_thread_data, value.value);
@@ -805,6 +808,10 @@ static void *vl_api_snat_user_session_dump_t_print
   FINISH;
 }
 
+/****************************/
+/*** detrministic NAT/CGN ***/
+/****************************/
+
 static void
 vl_api_snat_add_det_map_t_handler (vl_api_snat_add_det_map_t * mp)
 {
@@ -1193,6 +1200,435 @@ static void *vl_api_snat_det_session_dump_t_print
   FINISH;
 }
 
+/*************/
+/*** NAT64 ***/
+/*************/
+
+static void
+  vl_api_nat64_add_del_pool_addr_range_t_handler
+  (vl_api_nat64_add_del_pool_addr_range_t * mp)
+{
+  vl_api_nat64_add_del_pool_addr_range_reply_t *rmp;
+  snat_main_t *sm = &snat_main;
+  int rv = 0;
+  ip4_address_t this_addr;
+  u32 start_host_order, end_host_order;
+  u32 vrf_id;
+  int i, count;
+  u32 *tmp;
+
+  tmp = (u32 *) mp->start_addr;
+  start_host_order = clib_host_to_net_u32 (tmp[0]);
+  tmp = (u32 *) mp->end_addr;
+  end_host_order = clib_host_to_net_u32 (tmp[0]);
+
+  count = (end_host_order - start_host_order) + 1;
+
+  vrf_id = clib_host_to_net_u32 (mp->vrf_id);
+
+  memcpy (&this_addr.as_u8, mp->start_addr, 4);
+
+  for (i = 0; i < count; i++)
+    {
+      if ((rv = nat64_add_del_pool_addr (&this_addr, vrf_id, mp->is_add)))
+       goto send_reply;
+
+      increment_v4_address (&this_addr);
+    }
+
+send_reply:
+  REPLY_MACRO (VL_API_NAT64_ADD_DEL_POOL_ADDR_RANGE_REPLY);
+}
+
+static void *vl_api_nat64_add_del_pool_addr_range_t_print
+  (vl_api_nat64_add_del_pool_addr_range_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: nat64_add_del_pool_addr_range");
+  s = format (s, "%U - %U vrf_id %u %s\n",
+             format_ip4_address, mp->start_addr,
+             format_ip4_address, mp->end_addr,
+             ntohl (mp->vrf_id), mp->is_add ? "" : "del");
+
+  FINISH;
+}
+
+typedef struct nat64_api_walk_ctx_t_
+{
+  unix_shared_memory_queue_t *q;
+  u32 context;
+} nat64_api_walk_ctx_t;
+
+static int
+nat64_api_pool_walk (snat_address_t * a, void *arg)
+{
+  vl_api_nat64_pool_addr_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_POOL_ADDR_DETAILS + sm->msg_id_base);
+  clib_memcpy (rmp->address, &(a->addr), 4);
+  if (a->fib_index != ~0)
+    {
+      fib_table_t *fib = fib_table_get (a->fib_index, FIB_PROTOCOL_IP4);
+      if (!fib)
+       return -1;
+      rmp->vrf_id = ntohl (fib->ft_table_id);
+    }
+  else
+    rmp->vrf_id = ~0;
+  rmp->context = ctx->context;
+
+  vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp);
+
+  return 0;
+}
+
+static void
+vl_api_nat64_pool_addr_dump_t_handler (vl_api_nat64_pool_addr_dump_t * mp)
+{
+  unix_shared_memory_queue_t *q;
+
+  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_pool_addr_walk (nat64_api_pool_walk, &ctx);
+}
+
+static void *
+vl_api_nat64_pool_addr_dump_t_print (vl_api_nat64_pool_addr_dump_t * mp,
+                                    void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: nat64_pool_addr_dump\n");
+
+  FINISH;
+}
+
+static void
+vl_api_nat64_add_del_interface_t_handler (vl_api_nat64_add_del_interface_t *
+                                         mp)
+{
+  snat_main_t *sm = &snat_main;
+  vl_api_nat64_add_del_interface_reply_t *rmp;
+  int rv = 0;
+
+  VALIDATE_SW_IF_INDEX (mp);
+
+  rv =
+    nat64_add_del_interface (ntohl (mp->sw_if_index), mp->is_inside,
+                            mp->is_add);
+
+  BAD_SW_IF_INDEX_LABEL;
+
+  REPLY_MACRO (VL_API_NAT64_ADD_DEL_INTERFACE_REPLY);
+}
+
+static void *
+vl_api_nat64_add_del_interface_t_print (vl_api_nat64_add_del_interface_t * mp,
+                                       void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: nat64_add_del_interface ");
+  s = format (s, "sw_if_index %d %s %s",
+             clib_host_to_net_u32 (mp->sw_if_index),
+             mp->is_inside ? "in" : "out", mp->is_add ? "" : "del");
+
+  FINISH;
+}
+
+static int
+nat64_api_interface_walk (snat_interface_t * i, void *arg)
+{
+  vl_api_nat64_interface_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_INTERFACE_DETAILS + sm->msg_id_base);
+  rmp->sw_if_index = ntohl (i->sw_if_index);
+  rmp->is_inside = i->is_inside;
+  rmp->context = ctx->context;
+
+  vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp);
+
+  return 0;
+}
+
+static void
+vl_api_nat64_interface_dump_t_handler (vl_api_nat64_interface_dump_t * mp)
+{
+  unix_shared_memory_queue_t *q;
+
+  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_interfaces_walk (nat64_api_interface_walk, &ctx);
+}
+
+static void *
+vl_api_nat64_interface_dump_t_print (vl_api_nat64_interface_dump_t * mp,
+                                    void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: snat_interface_dump ");
+
+  FINISH;
+}
+
+static void
+  vl_api_nat64_add_del_static_bib_t_handler
+  (vl_api_nat64_add_del_static_bib_t * mp)
+{
+  snat_main_t *sm = &snat_main;
+  vl_api_nat64_add_del_static_bib_reply_t *rmp;
+  ip6_address_t in_addr;
+  ip4_address_t out_addr;
+  int rv = 0;
+
+  memcpy (&in_addr.as_u8, mp->i_addr, 16);
+  memcpy (&out_addr.as_u8, mp->o_addr, 4);
+
+  rv =
+    nat64_add_del_static_bib_entry (&in_addr, &out_addr,
+                                   clib_net_to_host_u16 (mp->i_port),
+                                   clib_net_to_host_u16 (mp->o_port),
+                                   mp->proto,
+                                   clib_net_to_host_u32 (mp->vrf_id),
+                                   mp->is_add);
+
+  REPLY_MACRO (VL_API_NAT64_ADD_DEL_STATIC_BIB_REPLY);
+}
+
+static void *vl_api_nat64_add_del_static_bib_t_print
+  (vl_api_nat64_add_del_static_bib_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: nat64_add_del_static_bib ");
+  s = format (s, "protocol %d i_addr %U o_addr %U ",
+             mp->proto,
+             format_ip6_address, mp->i_addr, format_ip4_address, mp->o_addr);
+
+  if (mp->vrf_id != ~0)
+    s = format (s, "vrf %d", clib_net_to_host_u32 (mp->vrf_id));
+
+  FINISH;
+}
+
+static int
+nat64_api_bib_walk (nat64_db_bib_entry_t * bibe, void *arg)
+{
+  vl_api_nat64_bib_details_t *rmp;
+  snat_main_t *sm = &snat_main;
+  nat64_api_walk_ctx_t *ctx = arg;
+  fib_table_t *fib;
+
+  fib = fib_table_get (bibe->fib_index, FIB_PROTOCOL_IP6);
+  if (!fib)
+    return -1;
+
+  rmp = vl_msg_api_alloc (sizeof (*rmp));
+  memset (rmp, 0, sizeof (*rmp));
+  rmp->_vl_msg_id = ntohs (VL_API_NAT64_BIB_DETAILS + sm->msg_id_base);
+  rmp->context = ctx->context;
+  clib_memcpy (rmp->i_addr, &(bibe->in_addr), 16);
+  clib_memcpy (rmp->o_addr, &(bibe->out_addr), 4);
+  rmp->i_port = bibe->in_port;
+  rmp->o_port = bibe->out_port;
+  rmp->vrf_id = ntohl (fib->ft_table_id);
+  rmp->proto = snat_proto_to_ip_proto (bibe->proto);
+  rmp->is_static = bibe->is_static;
+  rmp->ses_num = ntohl (bibe->ses_num);
+
+  vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp);
+
+  return 0;
+}
+
+static void
+vl_api_nat64_bib_dump_t_handler (vl_api_nat64_bib_dump_t * mp)
+{
+  unix_shared_memory_queue_t *q;
+  nat64_main_t *nm = &nat64_main;
+  snat_protocol_t proto;
+
+  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,
+  };
+
+  proto = ip_proto_to_snat_proto (mp->proto);
+
+  nat64_db_bib_walk (&nm->db, proto, nat64_api_bib_walk, &ctx);
+}
+
+static void *
+vl_api_nat64_bib_dump_t_print (vl_api_nat64_bib_dump_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: snat_bib_dump protocol %d", mp->proto);
+
+  FINISH;
+}
+
+static void
+vl_api_nat64_set_timeouts_t_handler (vl_api_nat64_set_timeouts_t * mp)
+{
+  snat_main_t *sm = &snat_main;
+  vl_api_nat64_set_timeouts_reply_t *rmp;
+  int rv = 0;
+
+  rv = nat64_set_icmp_timeout (ntohl (mp->icmp));
+  if (rv)
+    goto send_reply;
+  rv = nat64_set_udp_timeout (ntohl (mp->udp));
+  if (rv)
+    goto send_reply;
+  rv =
+    nat64_set_tcp_timeouts (ntohl (mp->tcp_trans), ntohl (mp->tcp_est),
+                           ntohl (mp->tcp_incoming_syn));
+
+send_reply:
+  REPLY_MACRO (VL_API_NAT64_SET_TIMEOUTS_REPLY);
+}
+
+static void *vl_api_nat64_set_timeouts_t_print
+  (vl_api_nat64_set_timeouts_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: nat64_set_timeouts ");
+  s =
+    format (s,
+           "udp %d icmp %d, tcp_trans %d, tcp_est %d, tcp_incoming_syn %d\n",
+           ntohl (mp->udp), ntohl (mp->icmp), ntohl (mp->tcp_trans),
+           ntohl (mp->tcp_est), ntohl (mp->tcp_incoming_syn));
+
+  FINISH;
+}
+
+static void
+vl_api_nat64_get_timeouts_t_handler (vl_api_nat64_get_timeouts_t * mp)
+{
+  snat_main_t *sm = &snat_main;
+  vl_api_nat64_get_timeouts_reply_t *rmp;
+  int rv = 0;
+
+  /* *INDENT-OFF* */
+  REPLY_MACRO2 (VL_API_NAT64_GET_TIMEOUTS_REPLY,
+  ({
+    rmp->udp = htonl (nat64_get_udp_timeout());
+    rmp->icmp = htonl (nat64_get_icmp_timeout());
+    rmp->tcp_trans = htonl (nat64_get_tcp_trans_timeout());
+    rmp->tcp_est = htonl (nat64_get_tcp_est_timeout());
+    rmp->tcp_incoming_syn = htonl (nat64_get_tcp_incoming_syn_timeout());
+  }))
+  /* *INDENT-ON* */
+}
+
+static void *vl_api_nat64_get_timeouts_t_print
+  (vl_api_nat64_get_timeouts_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: nat64_get_timeouts");
+
+  FINISH;
+}
+
+static int
+nat64_api_st_walk (nat64_db_st_entry_t * ste, void *arg)
+{
+  vl_api_nat64_st_details_t *rmp;
+  snat_main_t *sm = &snat_main;
+  nat64_api_walk_ctx_t *ctx = arg;
+  nat64_main_t *nm = &nat64_main;
+  nat64_db_bib_entry_t *bibe;
+  fib_table_t *fib;
+
+  bibe = nat64_db_bib_entry_by_index (&nm->db, ste->proto, ste->bibe_index);
+  if (!bibe)
+    return -1;
+
+  fib = fib_table_get (bibe->fib_index, FIB_PROTOCOL_IP6);
+  if (!fib)
+    return -1;
+
+  rmp = vl_msg_api_alloc (sizeof (*rmp));
+  memset (rmp, 0, sizeof (*rmp));
+  rmp->_vl_msg_id = ntohs (VL_API_NAT64_ST_DETAILS + sm->msg_id_base);
+  rmp->context = ctx->context;
+  clib_memcpy (rmp->il_addr, &(bibe->in_addr), 16);
+  clib_memcpy (rmp->ol_addr, &(bibe->out_addr), 4);
+  rmp->il_port = bibe->in_port;
+  rmp->ol_port = bibe->out_port;
+  clib_memcpy (rmp->ir_addr, &(ste->in_r_addr), 16);
+  clib_memcpy (rmp->or_addr, &(ste->out_r_addr), 4);
+  rmp->il_port = ste->r_port;
+  rmp->vrf_id = ntohl (fib->ft_table_id);
+  rmp->proto = snat_proto_to_ip_proto (ste->proto);
+
+  vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp);
+
+  return 0;
+}
+
+static void
+vl_api_nat64_st_dump_t_handler (vl_api_nat64_st_dump_t * mp)
+{
+  unix_shared_memory_queue_t *q;
+  nat64_main_t *nm = &nat64_main;
+  snat_protocol_t proto;
+
+  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,
+  };
+
+  proto = ip_proto_to_snat_proto (mp->proto);
+
+  nat64_db_st_walk (&nm->db, proto, nat64_api_st_walk, &ctx);
+}
+
+static void *
+vl_api_nat64_st_dump_t_print (vl_api_nat64_st_dump_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: snat_st_dump protocol %d", mp->proto);
+
+  FINISH;
+}
+
 /* List of message types that this plugin understands */
 #define foreach_snat_plugin_api_msg                                     \
 _(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range)                       \
@@ -1218,8 +1654,16 @@ _(SNAT_DET_SET_TIMEOUTS, snat_det_set_timeouts)                         \
 _(SNAT_DET_GET_TIMEOUTS, snat_det_get_timeouts)                         \
 _(SNAT_DET_CLOSE_SESSION_OUT, snat_det_close_session_out)               \
 _(SNAT_DET_CLOSE_SESSION_IN, snat_det_close_session_in)                 \
-_(SNAT_DET_SESSION_DUMP, snat_det_session_dump)
-
+_(SNAT_DET_SESSION_DUMP, snat_det_session_dump)                         \
+_(NAT64_ADD_DEL_POOL_ADDR_RANGE, nat64_add_del_pool_addr_range)         \
+_(NAT64_POOL_ADDR_DUMP, nat64_pool_addr_dump)                           \
+_(NAT64_ADD_DEL_INTERFACE, nat64_add_del_interface)                     \
+_(NAT64_INTERFACE_DUMP, nat64_interface_dump)                           \
+_(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)
 
 /* Set up the API message handling tables */
 static clib_error_t *
index 0eceaab..64fa305 100644 (file)
@@ -7,6 +7,7 @@ import struct
 from framework import VppTestCase, VppTestRunner, running_extended_tests
 from scapy.layers.inet import IP, TCP, UDP, ICMP
 from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
+from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6EchoReply
 from scapy.layers.l2 import Ether, ARP
 from scapy.data import IP_PROTOS
 from scapy.packet import bind_layers
@@ -37,13 +38,13 @@ class MethodHolder(VppTestCase):
         # TCP
         p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
              IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) /
-             TCP(sport=self.tcp_port_in))
+             TCP(sport=self.tcp_port_in, dport=20))
         pkts.append(p)
 
         # UDP
         p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
              IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) /
-             UDP(sport=self.udp_port_in))
+             UDP(sport=self.udp_port_in, dport=20))
         pkts.append(p)
 
         # ICMP
@@ -54,6 +55,36 @@ class MethodHolder(VppTestCase):
 
         return pkts
 
+    def create_stream_in_ip6(self, in_if, out_if, hlim=64):
+        """
+        Create IPv6 packet stream for inside network
+
+        :param in_if: Inside interface
+        :param out_if: Outside interface
+        :param ttl: Hop Limit of generated packets
+        """
+        pkts = []
+        dst = ''.join(['64:ff9b::', out_if.remote_ip4])
+        # TCP
+        p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
+             IPv6(src=in_if.remote_ip6, dst=dst, hlim=hlim) /
+             TCP(sport=self.tcp_port_in, dport=20))
+        pkts.append(p)
+
+        # UDP
+        p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
+             IPv6(src=in_if.remote_ip6, dst=dst, hlim=hlim) /
+             UDP(sport=self.udp_port_in, dport=20))
+        pkts.append(p)
+
+        # ICMP
+        p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
+             IPv6(src=in_if.remote_ip6, dst=dst, hlim=hlim) /
+             ICMPv6EchoRequest(id=self.icmp_id_in))
+        pkts.append(p)
+
+        return pkts
+
     def create_stream_out(self, out_if, dst_ip=None, ttl=64):
         """
         Create packet stream for outside network
@@ -68,13 +99,13 @@ class MethodHolder(VppTestCase):
         # TCP
         p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
              IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
-             TCP(dport=self.tcp_port_out))
+             TCP(dport=self.tcp_port_out, sport=20))
         pkts.append(p)
 
         # UDP
         p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
              IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
-             UDP(dport=self.udp_port_out))
+             UDP(dport=self.udp_port_out, sport=20))
         pkts.append(p)
 
         # ICMP
@@ -86,7 +117,7 @@ class MethodHolder(VppTestCase):
         return pkts
 
     def verify_capture_out(self, capture, nat_ip=None, same_port=False,
-                           packet_num=3):
+                           packet_num=3, dst_ip=None):
         """
         Verify captured packets on outside network
 
@@ -94,6 +125,7 @@ class MethodHolder(VppTestCase):
         :param nat_ip: Translated IP address (Default use global SNAT address)
         :param same_port: Sorce port number is not translated (Default False)
         :param packet_num: Expected number of packets (Default 3)
+        :param dst_ip: Destination IP address (Default do not verify)
         """
         if nat_ip is None:
             nat_ip = self.snat_addr
@@ -101,6 +133,8 @@ class MethodHolder(VppTestCase):
         for packet in capture:
             try:
                 self.assertEqual(packet[IP].src, nat_ip)
+                if dst_ip is not None:
+                    self.assertEqual(packet[IP].dst, dst_ip)
                 if packet.haslayer(TCP):
                     if same_port:
                         self.assertEqual(packet[TCP].sport, self.tcp_port_in)
@@ -149,6 +183,32 @@ class MethodHolder(VppTestCase):
                                       "(inside network):", packet))
                 raise
 
+    def verify_capture_in_ip6(self, capture, src_ip, dst_ip, packet_num=3):
+        """
+        Verify captured IPv6 packets on inside network
+
+        :param capture: Captured packets
+        :param src_ip: Source IP
+        :param dst_ip: Destination IP address
+        :param packet_num: Expected number of packets (Default 3)
+        """
+        self.assertEqual(packet_num, len(capture))
+        for packet in capture:
+            try:
+                self.assertEqual(packet[IPv6].src, src_ip)
+                self.assertEqual(packet[IPv6].dst, dst_ip)
+                if packet.haslayer(TCP):
+                    self.assertEqual(packet[TCP].dport, self.tcp_port_in)
+                elif packet.haslayer(UDP):
+                    self.assertEqual(packet[UDP].dport, self.udp_port_in)
+                else:
+                    self.assertEqual(packet[ICMPv6EchoReply].id,
+                                     self.icmp_id_in)
+            except:
+                self.logger.error(ppp("Unexpected or invalid packet "
+                                      "(inside network):", packet))
+                raise
+
     def verify_capture_no_translation(self, capture, ingress_if, egress_if):
         """
         Verify captured packet that don't have to be translated
@@ -2305,5 +2365,352 @@ class TestDeterministicNAT(MethodHolder):
             self.logger.info(self.vapi.cli("show snat detail"))
             self.clear_snat()
 
+
+class TestNAT64(MethodHolder):
+    """ NAT64 Test Cases """
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestNAT64, cls).setUpClass()
+
+        try:
+            cls.tcp_port_in = 6303
+            cls.tcp_port_out = 6303
+            cls.udp_port_in = 6304
+            cls.udp_port_out = 6304
+            cls.icmp_id_in = 6305
+            cls.icmp_id_out = 6305
+            cls.nat_addr = '10.0.0.3'
+            cls.nat_addr_n = socket.inet_pton(socket.AF_INET, cls.nat_addr)
+
+            cls.create_pg_interfaces(range(2))
+            cls.ip6_interfaces = list(cls.pg_interfaces[0:1])
+            cls.ip4_interfaces = list(cls.pg_interfaces[1:2])
+
+            for i in cls.ip6_interfaces:
+                i.admin_up()
+                i.config_ip6()
+                i.resolve_ndp()
+
+            for i in cls.ip4_interfaces:
+                i.admin_up()
+                i.config_ip4()
+                i.resolve_arp()
+
+        except Exception:
+            super(TestNAT64, cls).tearDownClass()
+            raise
+
+    def test_pool(self):
+        """ Add/delete address to NAT64 pool """
+        nat_addr = socket.inet_pton(socket.AF_INET, '1.2.3.4')
+
+        self.vapi.nat64_add_del_pool_addr_range(nat_addr, nat_addr)
+
+        addresses = self.vapi.nat64_pool_addr_dump()
+        self.assertEqual(len(addresses), 1)
+        self.assertEqual(addresses[0].address, nat_addr)
+
+        self.vapi.nat64_add_del_pool_addr_range(nat_addr, nat_addr, is_add=0)
+
+        addresses = self.vapi.nat64_pool_addr_dump()
+        self.assertEqual(len(addresses), 0)
+
+    def test_interface(self):
+        """ Enable/disable NAT64 feature on the interface """
+        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)
+
+        interfaces = self.vapi.nat64_interface_dump()
+        self.assertEqual(len(interfaces), 2)
+        pg0_found = False
+        pg1_found = False
+        for intf in interfaces:
+            if intf.sw_if_index == self.pg0.sw_if_index:
+                self.assertEqual(intf.is_inside, 1)
+                pg0_found = True
+            elif intf.sw_if_index == self.pg1.sw_if_index:
+                self.assertEqual(intf.is_inside, 0)
+                pg1_found = True
+        self.assertTrue(pg0_found)
+        self.assertTrue(pg1_found)
+
+        features = self.vapi.cli("show interface features pg0")
+        self.assertNotEqual(features.find('nat64-in2out'), -1)
+        features = self.vapi.cli("show interface features pg1")
+        self.assertNotEqual(features.find('nat64-out2in'), -1)
+
+        self.vapi.nat64_add_del_interface(self.pg0.sw_if_index, is_add=0)
+        self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_add=0)
+
+        interfaces = self.vapi.nat64_interface_dump()
+        self.assertEqual(len(interfaces), 0)
+
+    def test_static_bib(self):
+        """ Add/delete static BIB entry """
+        in_addr = socket.inet_pton(socket.AF_INET6,
+                                   '2001:db8:85a3::8a2e:370:7334')
+        out_addr = socket.inet_pton(socket.AF_INET, '10.1.1.3')
+        in_port = 1234
+        out_port = 5678
+        proto = IP_PROTOS.tcp
+
+        self.vapi.nat64_add_del_static_bib(in_addr,
+                                           out_addr,
+                                           in_port,
+                                           out_port,
+                                           proto)
+        bib = self.vapi.nat64_bib_dump(IP_PROTOS.tcp)
+        static_bib_num = 0
+        for bibe in bib:
+            if bibe.is_static:
+                static_bib_num += 1
+                self.assertEqual(bibe.i_addr, in_addr)
+                self.assertEqual(bibe.o_addr, out_addr)
+                self.assertEqual(bibe.i_port, in_port)
+                self.assertEqual(bibe.o_port, out_port)
+        self.assertEqual(static_bib_num, 1)
+
+        self.vapi.nat64_add_del_static_bib(in_addr,
+                                           out_addr,
+                                           in_port,
+                                           out_port,
+                                           proto,
+                                           is_add=0)
+        bib = self.vapi.nat64_bib_dump(IP_PROTOS.tcp)
+        static_bib_num = 0
+        for bibe in bib:
+            if bibe.is_static:
+                static_bib_num += 1
+        self.assertEqual(static_bib_num, 0)
+
+    def test_set_timeouts(self):
+        """ Set NAT64 timeouts """
+        # verify default values
+        timeouts = self.vapi.nat64_get_timeouts()
+        self.assertEqual(timeouts.udp, 300)
+        self.assertEqual(timeouts.icmp, 60)
+        self.assertEqual(timeouts.tcp_trans, 240)
+        self.assertEqual(timeouts.tcp_est, 7440)
+        self.assertEqual(timeouts.tcp_incoming_syn, 6)
+
+        # set and verify custom values
+        self.vapi.nat64_set_timeouts(udp=200, icmp=30, tcp_trans=250,
+                                     tcp_est=7450, tcp_incoming_syn=10)
+        timeouts = self.vapi.nat64_get_timeouts()
+        self.assertEqual(timeouts.udp, 200)
+        self.assertEqual(timeouts.icmp, 30)
+        self.assertEqual(timeouts.tcp_trans, 250)
+        self.assertEqual(timeouts.tcp_est, 7450)
+        self.assertEqual(timeouts.tcp_incoming_syn, 10)
+
+    def test_dynamic(self):
+        """ NAT64 dynamic translation test """
+        self.tcp_port_in = 6303
+        self.udp_port_in = 6304
+        self.icmp_id_in = 6305
+
+        ses_num_start = self.nat64_get_ses_num()
+
+        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)
+
+        # in2out
+        pkts = self.create_stream_in_ip6(self.pg0, self.pg1)
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg1.get_capture(3)
+        self.verify_capture_out(capture, packet_num=3, nat_ip=self.nat_addr,
+                                dst_ip=self.pg1.remote_ip4)
+
+        # out2in
+        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(3)
+        ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4]))
+        self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg0.remote_ip6)
+
+        # in2out
+        pkts = self.create_stream_in_ip6(self.pg0, self.pg1)
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg1.get_capture(3)
+        self.verify_capture_out(capture, packet_num=3, nat_ip=self.nat_addr,
+                                dst_ip=self.pg1.remote_ip4)
+
+        # out2in
+        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(3)
+        ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4]))
+        self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg0.remote_ip6)
+
+        ses_num_end = self.nat64_get_ses_num()
+
+        self.assertEqual(ses_num_end - ses_num_start, 3)
+
+    def test_static(self):
+        """ NAT64 static translation test """
+        self.tcp_port_in = 60303
+        self.udp_port_in = 60304
+        self.icmp_id_in = 60305
+        self.tcp_port_out = 60303
+        self.udp_port_out = 60304
+        self.icmp_id_out = 60305
+
+        ses_num_start = self.nat64_get_ses_num()
+
+        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_static_bib(self.pg0.remote_ip6n,
+                                           self.nat_addr_n,
+                                           self.tcp_port_in,
+                                           self.tcp_port_out,
+                                           IP_PROTOS.tcp)
+        self.vapi.nat64_add_del_static_bib(self.pg0.remote_ip6n,
+                                           self.nat_addr_n,
+                                           self.udp_port_in,
+                                           self.udp_port_out,
+                                           IP_PROTOS.udp)
+        self.vapi.nat64_add_del_static_bib(self.pg0.remote_ip6n,
+                                           self.nat_addr_n,
+                                           self.icmp_id_in,
+                                           self.icmp_id_out,
+                                           IP_PROTOS.icmp)
+
+        # in2out
+        pkts = self.create_stream_in_ip6(self.pg0, self.pg1)
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg1.get_capture(3)
+        self.verify_capture_out(capture, packet_num=3, nat_ip=self.nat_addr,
+                                dst_ip=self.pg1.remote_ip4, same_port=True)
+
+        # out2in
+        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(3)
+        ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4]))
+        self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg0.remote_ip6)
+
+        ses_num_end = self.nat64_get_ses_num()
+
+        self.assertEqual(ses_num_end - ses_num_start, 3)
+
+    @unittest.skipUnless(running_extended_tests(), "part of extended tests")
+    def test_session_timeout(self):
+        """ NAT64 session timeout """
+        self.icmp_id_in = 1234
+        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_set_timeouts(icmp=5)
+
+        pkts = self.create_stream_in_ip6(self.pg0, self.pg1)
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg1.get_capture(3)
+
+        ses_num_before_timeout = self.nat64_get_ses_num()
+
+        sleep(15)
+
+        # ICMP session after timeout
+        ses_num_after_timeout = self.nat64_get_ses_num()
+        self.assertNotEqual(ses_num_before_timeout, ses_num_after_timeout)
+
+    def nat64_get_ses_num(self):
+        """
+        Return number of active NAT64 sessions.
+        """
+        ses_num = 0
+        st = self.vapi.nat64_st_dump(IP_PROTOS.tcp)
+        ses_num += len(st)
+        st = self.vapi.nat64_st_dump(IP_PROTOS.udp)
+        ses_num += len(st)
+        st = self.vapi.nat64_st_dump(IP_PROTOS.icmp)
+        ses_num += len(st)
+        return ses_num
+
+    def clear_nat64(self):
+        """
+        Clear NAT64 configuration.
+        """
+        self.vapi.nat64_set_timeouts()
+
+        interfaces = self.vapi.nat64_interface_dump()
+        for intf in interfaces:
+            self.vapi.nat64_add_del_interface(intf.sw_if_index,
+                                              intf.is_inside,
+                                              is_add=0)
+
+        bib = self.vapi.nat64_bib_dump(IP_PROTOS.tcp)
+        for bibe in bib:
+            if bibe.is_static:
+                self.vapi.nat64_add_del_static_bib(bibe.i_addr,
+                                                   bibe.o_addr,
+                                                   bibe.i_port,
+                                                   bibe.o_port,
+                                                   bibe.proto,
+                                                   bibe.vrf_id,
+                                                   is_add=0)
+
+        bib = self.vapi.nat64_bib_dump(IP_PROTOS.udp)
+        for bibe in bib:
+            if bibe.is_static:
+                self.vapi.nat64_add_del_static_bib(bibe.i_addr,
+                                                   bibe.o_addr,
+                                                   bibe.i_port,
+                                                   bibe.o_port,
+                                                   bibe.proto,
+                                                   bibe.vrf_id,
+                                                   is_add=0)
+
+        bib = self.vapi.nat64_bib_dump(IP_PROTOS.icmp)
+        for bibe in bib:
+            if bibe.is_static:
+                self.vapi.nat64_add_del_static_bib(bibe.i_addr,
+                                                   bibe.o_addr,
+                                                   bibe.i_port,
+                                                   bibe.o_port,
+                                                   bibe.proto,
+                                                   bibe.vrf_id,
+                                                   is_add=0)
+
+        adresses = self.vapi.nat64_pool_addr_dump()
+        for addr in adresses:
+            self.vapi.nat64_add_del_pool_addr_range(addr.address,
+                                                    addr.address,
+                                                    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 bib tcp"))
+            self.logger.info(self.vapi.cli("show nat64 bib udp"))
+            self.logger.info(self.vapi.cli("show nat64 bib icmp"))
+            self.logger.info(self.vapi.cli("show nat64 session table tcp"))
+            self.logger.info(self.vapi.cli("show nat64 session table udp"))
+            self.logger.info(self.vapi.cli("show nat64 session table icmp"))
+            self.clear_nat64()
+
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)
index b310d09..4c02a34 100644 (file)
@@ -1327,6 +1327,124 @@ class VppPapiProvider(object):
             {'is_ip4': is_ip4,
              'user_addr': user_addr})
 
+    def nat64_add_del_pool_addr_range(
+            self,
+            start_addr,
+            end_addr,
+            vrf_id=0xFFFFFFFF,
+            is_add=1):
+        """Add/del address range to NAT64 pool
+
+        :param start_addr: First IP address
+        :param end_addr: Last IP address
+        :param vrf_id: VRF id for the address range
+        :param is_add: 1 if add, 0 if delete (Default value = 1)
+        """
+        return self.api(
+            self.papi.nat64_add_del_pool_addr_range,
+            {'start_addr': start_addr,
+             'end_addr': end_addr,
+             'vrf_id': vrf_id,
+             'is_add': is_add})
+
+    def nat64_pool_addr_dump(self):
+        """Dump NAT64 pool addresses
+        :return: Dictionary of NAT64 pool addresses
+        """
+        return self.api(self.papi.nat64_pool_addr_dump, {})
+
+    def nat64_add_del_interface(
+            self,
+            sw_if_index,
+            is_inside=1,
+            is_add=1):
+        """Enable/disable NAT64 feature on the interface
+           :param sw_if_index: Index of the interface
+           :param is_inside: 1 if inside, 0 if outside (Default value = 1)
+           :param is_add: 1 if add, 0 if delete (Default value = 1)
+        """
+        return self.api(
+            self.papi.nat64_add_del_interface,
+            {'sw_if_index': sw_if_index,
+             'is_inside': is_inside,
+             'is_add': is_add})
+
+    def nat64_interface_dump(self):
+        """Dump interfaces with NAT64 feature
+        :return: Dictionary of interfaces with NAT64 feature
+        """
+        return self.api(self.papi.nat64_interface_dump, {})
+
+    def nat64_add_del_static_bib(
+            self,
+            in_ip,
+            out_ip,
+            in_port,
+            out_port,
+            protocol,
+            vrf_id=0,
+            is_add=1):
+        """Add/delete S-NAT static BIB entry
+
+        :param in_ip: Inside IPv6 address
+        :param out_ip: Outside IPv4 address
+        :param in_port: Inside port number
+        :param out_port: Outside port number
+        :param protocol: IP protocol
+        :param vrf_id: VRF ID (Default value = 0)
+        :param is_add: 1 if add, 0 if delete (Default value = 1)
+        """
+        return self.api(
+            self.papi.nat64_add_del_static_bib,
+            {'i_addr': in_ip,
+             'o_addr': out_ip,
+             'i_port': in_port,
+             'o_port': out_port,
+             'vrf_id': vrf_id,
+             'proto': protocol,
+             'is_add': is_add})
+
+    def nat64_bib_dump(self, protocol):
+        """Dump NAT64 BIB
+
+        :param protocol: IP protocol
+        :returns: Dictionary of NAT64 BIB entries
+        """
+        return self.api(self.papi.nat64_bib_dump, {'proto': protocol})
+
+    def nat64_set_timeouts(self, udp=300, icmp=60, tcp_trans=240, tcp_est=7440,
+                           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)
+        """
+        return self.api(
+            self.papi.nat64_set_timeouts,
+            {'udp': udp,
+             'icmp': icmp,
+             'tcp_trans': tcp_trans,
+             'tcp_est': tcp_est,
+             'tcp_incoming_syn': tcp_incoming_syn})
+
+    def nat64_get_timeouts(self):
+        """Get values of timeouts for NAT64
+
+        :return: Timeouts for NAT64 (in seconds)
+        """
+        return self.api(self.papi.nat64_get_timeouts, {})
+
+    def nat64_st_dump(self, protocol):
+        """Dump NAT64 session table
+
+        :param protocol: IP protocol
+        :returns: Dictionary of NAT64 sesstion table entries
+        """
+        return self.api(self.papi.nat64_st_dump, {'proto': protocol})
+
     def control_ping(self):
         self.api(self.papi.control_ping)