NAT66 1:1 mapping (VPP-1108) 96/10196/2
authorMatus Fabian <matfabia@cisco.com>
Mon, 22 Jan 2018 11:41:53 +0000 (03:41 -0800)
committerOle Trøan <otroan@employees.org>
Wed, 31 Jan 2018 17:29:13 +0000 (17:29 +0000)
Support the 1:1 translation of source address for IPv6

Change-Id: I934d18e5ec508bf7422d796ee5f172b79c048011
Signed-off-by: Matus Fabian <matfabia@cisco.com>
src/plugins/nat.am
src/plugins/nat/nat.api
src/plugins/nat/nat.c
src/plugins/nat/nat66.c [new file with mode: 0644]
src/plugins/nat/nat66.h [new file with mode: 0644]
src/plugins/nat/nat66_cli.c [new file with mode: 0644]
src/plugins/nat/nat66_in2out.c [new file with mode: 0644]
src/plugins/nat/nat66_out2in.c [new file with mode: 0644]
src/plugins/nat/nat_api.c
test/test_nat.py
test/vpp_papi_provider.py

index 65985fb..ce3e5a2 100644 (file)
@@ -36,7 +36,11 @@ nat_plugin_la_SOURCES = nat/nat.c            \
         nat/dslite_out2in.c                    \
         nat/dslite_cli.c                        \
         nat/dslite_ce_encap.c                   \
-        nat/dslite_ce_decap.c
+        nat/dslite_ce_decap.c                   \
+        nat/nat66.c                            \
+        nat/nat66_cli.c                        \
+        nat/nat66_in2out.c                     \
+        nat/nat66_out2in.c
 
 API_FILES += nat/nat.api
 
index ce565f6..1a96017 100644 (file)
@@ -1280,3 +1280,84 @@ define dslite_get_b4_addr_reply {
   u8 ip4_addr[4];
   u8 ip6_addr[16];
 };
+
+/*
+ * NAT66 APIs
+ */
+/** \brief Enable/disable NAT66 feature on the interface
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param is_add - 1 if add, 0 if delete
+    @param is_inside - 1 if inside, 0 if outside
+    @param sw_if_index - software index of the interface
+*/
+autoreply define nat66_add_del_interface {
+  u32 client_index;
+  u32 context;
+  u8 is_add;
+  u8 is_inside;
+  u32 sw_if_index;
+};
+
+/** \brief Dump interfaces with NAT66 feature
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define nat66_interface_dump {
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief NAT66 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 - software index of the interface
+*/
+define nat66_interface_details {
+  u32 context;
+  u8 is_inside;
+  u32 sw_if_index;
+};
+
+/** \brief Add/delete 1:1 NAT66
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param is_add - 1 if add, 0 if delete
+    @param local_ip_address - local IPv6 address
+    @param external_ip_address - external IPv6 address
+    @param vrf_id - VRF id of tenant
+*/
+autoreply define nat66_add_del_static_mapping {
+  u32 client_index;
+  u32 context;
+  u8 is_add;
+  u8 local_ip_address[16];
+  u8 external_ip_address[16];
+  u32 vrf_id;
+};
+
+/** \brief Dump NAT66 static mappings
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define nat66_static_mapping_dump {
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief NAT66 static mapping details response
+    @param context - sender context, to match reply w/ request
+    @param local_ip_address - local IPv6 address
+    @param external_ip_address - external IPv6 address
+    @param vrf_id - VRF id of tenant
+    @param total_bytes - count of bytes sent through static mapping
+    @param total_pkts - count of pakets sent through static mapping
+*/
+define nat66_static_mapping_details {
+  u32 context;
+  u8 local_ip_address[16];
+  u8 external_ip_address[16];
+  u32 vrf_id;
+  u64 total_bytes;
+  u64 total_pkts;
+};
index ef740d9..314fadf 100644 (file)
@@ -24,6 +24,7 @@
 #include <nat/nat_ipfix_logging.h>
 #include <nat/nat_det.h>
 #include <nat/nat64.h>
+#include <nat/nat66.h>
 #include <nat/dslite.h>
 #include <nat/nat_reass.h>
 #include <vnet/fib/fib_table.h>
@@ -1779,6 +1780,8 @@ static clib_error_t * snat_init (vlib_main_t * vm)
 
   dslite_init(vm);
 
+  nat66_init();
+
   /* Init virtual fragmenentation reassembly */
   return nat_reass_init(vm);
 }
diff --git a/src/plugins/nat/nat66.c b/src/plugins/nat/nat66.c
new file mode 100644 (file)
index 0000000..6257c99
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2018 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 NAT66 implementation
+ */
+
+#include <nat/nat66.h>
+#include <vnet/fib/fib_table.h>
+
+nat66_main_t nat66_main;
+
+/* *INDENT-OFF* */
+
+/* Hook up input features */
+VNET_FEATURE_INIT (nat66_in2out, static) = {
+  .arc_name = "ip6-unicast",
+  .node_name = "nat66-in2out",
+  .runs_before = VNET_FEATURES ("ip6-lookup"),
+};
+VNET_FEATURE_INIT (nat66_out2in, static) = {
+  .arc_name = "ip6-unicast",
+  .node_name = "nat66-out2in",
+  .runs_before = VNET_FEATURES ("ip6-lookup"),
+};
+
+/* *INDENT-ON* */
+
+
+void
+nat66_init (void)
+{
+  nat66_main_t *nm = &nat66_main;
+  u32 static_mapping_buckets = 1024;
+  uword static_mapping_memory_size = 64 << 20;
+
+  clib_bihash_init_24_8 (&nm->sm_l, "nat66-static-map-by-local",
+                        static_mapping_buckets, static_mapping_memory_size);
+  clib_bihash_init_24_8 (&nm->sm_e, "nat66-static-map-by-external",
+                        static_mapping_buckets, static_mapping_memory_size);
+
+  nm->session_counters.name = "session counters";
+}
+
+int
+nat66_interface_add_del (u32 sw_if_index, u8 is_inside, u8 is_add)
+{
+  nat66_main_t *nm = &nat66_main;
+  snat_interface_t *interface = 0, *i;
+  const char *feature_name;
+
+  /* *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->flags =
+       is_inside ? NAT_INTERFACE_FLAG_IS_INSIDE :
+       NAT_INTERFACE_FLAG_IS_OUTSIDE;
+    }
+  else
+    {
+      if (!interface)
+       return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+      pool_put (nm->interfaces, interface);
+    }
+
+  feature_name = is_inside ? "nat66-in2out" : "nat66-out2in";
+  return vnet_feature_enable_disable ("ip6-unicast", feature_name,
+                                     sw_if_index, is_add, 0, 0);
+}
+
+void
+nat66_interfaces_walk (nat66_interface_walk_fn_t fn, void *ctx)
+{
+  nat66_main_t *nm = &nat66_main;
+  snat_interface_t *i = 0;
+
+  /* *INDENT-OFF* */
+  pool_foreach (i, nm->interfaces,
+  ({
+    if (fn (i, ctx))
+      break;
+  }));
+  /* *INDENT-ON* */
+}
+
+nat66_static_mapping_t *
+nat66_static_mapping_get (ip6_address_t * addr, u32 fib_index, u8 is_local)
+{
+  nat66_main_t *nm = &nat66_main;
+  nat66_static_mapping_t *sm = 0;
+  nat66_sm_key_t sm_key;
+  clib_bihash_kv_24_8_t kv, value;
+
+  sm_key.addr.as_u64[0] = addr->as_u64[0];
+  sm_key.addr.as_u64[1] = addr->as_u64[1];
+  sm_key.fib_index = fib_index;
+  sm_key.rsvd = 0;
+
+  kv.key[0] = sm_key.as_u64[0];
+  kv.key[1] = sm_key.as_u64[1];
+  kv.key[2] = sm_key.as_u64[2];
+
+  if (!clib_bihash_search_24_8
+      (is_local ? &nm->sm_l : &nm->sm_e, &kv, &value))
+    sm = pool_elt_at_index (nm->sm, value.value);
+
+  return sm;
+}
+
+int
+nat66_static_mapping_add_del (ip6_address_t * l_addr, ip6_address_t * e_addr,
+                             u32 vrf_id, u8 is_add)
+{
+  nat66_main_t *nm = &nat66_main;
+  int rv = 0;
+  nat66_static_mapping_t *sm = 0;
+  nat66_sm_key_t sm_key;
+  clib_bihash_kv_24_8_t kv, value;
+  u32 fib_index = fib_table_find (FIB_PROTOCOL_IP6, vrf_id);
+
+  sm_key.addr.as_u64[0] = l_addr->as_u64[0];
+  sm_key.addr.as_u64[1] = l_addr->as_u64[1];
+  sm_key.fib_index = fib_index;
+  sm_key.rsvd = 0;
+  kv.key[0] = sm_key.as_u64[0];
+  kv.key[1] = sm_key.as_u64[1];
+  kv.key[2] = sm_key.as_u64[2];
+
+  if (!clib_bihash_search_24_8 (&nm->sm_l, &kv, &value))
+    sm = pool_elt_at_index (nm->sm, value.value);
+
+  if (is_add)
+    {
+      if (sm)
+       return VNET_API_ERROR_VALUE_EXIST;
+
+      fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id,
+                                                    FIB_SOURCE_PLUGIN_HI);
+      pool_get (nm->sm, sm);
+      memset (sm, 0, sizeof (*sm));
+      sm->l_addr.as_u64[0] = l_addr->as_u64[0];
+      sm->l_addr.as_u64[1] = l_addr->as_u64[1];
+      sm->e_addr.as_u64[0] = e_addr->as_u64[0];
+      sm->e_addr.as_u64[1] = e_addr->as_u64[1];
+      sm->fib_index = fib_index;
+
+      sm_key.fib_index = fib_index;
+      kv.key[0] = sm_key.as_u64[0];
+      kv.key[1] = sm_key.as_u64[1];
+      kv.key[2] = sm_key.as_u64[2];
+      kv.value = sm - nm->sm;
+      if (clib_bihash_add_del_24_8 (&nm->sm_l, &kv, 1))
+       clib_warning ("nat66-static-map-by-local add key failed");
+      sm_key.addr.as_u64[0] = e_addr->as_u64[0];
+      sm_key.addr.as_u64[1] = e_addr->as_u64[1];
+      sm_key.fib_index = 0;
+      kv.key[0] = sm_key.as_u64[0];
+      kv.key[1] = sm_key.as_u64[1];
+      kv.key[2] = sm_key.as_u64[2];
+      if (clib_bihash_add_del_24_8 (&nm->sm_e, &kv, 1))
+       clib_warning ("nat66-static-map-by-external add key failed");
+
+      vlib_validate_combined_counter (&nm->session_counters, kv.value);
+      vlib_zero_combined_counter (&nm->session_counters, kv.value);
+    }
+  else
+    {
+      if (!sm)
+       return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+      kv.value = sm - nm->sm;
+      if (clib_bihash_add_del_24_8 (&nm->sm_l, &kv, 0))
+       clib_warning ("nat66-static-map-by-local delete key failed");
+      sm_key.addr.as_u64[0] = e_addr->as_u64[0];
+      sm_key.addr.as_u64[1] = e_addr->as_u64[1];
+      sm_key.fib_index = 0;
+      kv.key[0] = sm_key.as_u64[0];
+      kv.key[1] = sm_key.as_u64[1];
+      kv.key[2] = sm_key.as_u64[2];
+      if (clib_bihash_add_del_24_8 (&nm->sm_e, &kv, 0))
+       clib_warning ("nat66-static-map-by-external delete key failed");
+      fib_table_unlock (sm->fib_index, FIB_PROTOCOL_IP6,
+                       FIB_SOURCE_PLUGIN_HI);
+      pool_put (nm->sm, sm);
+    }
+
+  return rv;
+}
+
+void
+nat66_static_mappings_walk (nat66_static_mapping_walk_fn_t fn, void *ctx)
+{
+  nat66_main_t *nm = &nat66_main;
+  nat66_static_mapping_t *sm = 0;
+
+  /* *INDENT-OFF* */
+  pool_foreach (sm, nm->sm,
+  ({
+    if (fn (sm, ctx))
+      break;
+  }));
+  /* *INDENT-ON* */
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/nat66.h b/src/plugins/nat/nat66.h
new file mode 100644 (file)
index 0000000..d2ddd0c
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2018 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 NAT66 global declarations
+ */
+#ifndef __included_nat66_h__
+#define __included_nat66_h__
+
+#include <vppinfra/bihash_24_8.h>
+#include <nat/nat.h>
+
+typedef struct
+{
+  ip6_address_t l_addr;
+  ip6_address_t e_addr;
+  u32 fib_index;
+} nat66_static_mapping_t;
+
+typedef struct
+{
+  union
+  {
+    struct
+    {
+      ip6_address_t addr;
+      u32 fib_index;
+      u32 rsvd;
+    };
+    u64 as_u64[3];
+  };
+} nat66_sm_key_t;
+
+typedef struct
+{
+  /** Interface pool */
+  snat_interface_t *interfaces;
+  /** Static mapping pool */
+  nat66_static_mapping_t *sm;
+  /** Static mapping by local address lookup table */
+  clib_bihash_24_8_t sm_l;
+  /** Static mapping by external address lookup table */
+  clib_bihash_24_8_t sm_e;
+  /** Session counters */
+  vlib_combined_counter_main_t session_counters;
+} nat66_main_t;
+
+extern nat66_main_t nat66_main;
+extern vlib_node_registration_t nat66_in2out_node;
+extern vlib_node_registration_t nat66_out2in_node;
+
+void nat66_init (void);
+typedef int (*nat66_interface_walk_fn_t) (snat_interface_t * i, void *ctx);
+void nat66_interfaces_walk (nat66_interface_walk_fn_t fn, void *ctx);
+int nat66_interface_add_del (u32 sw_if_index, u8 is_inside, u8 is_add);
+typedef int (*nat66_static_mapping_walk_fn_t) (nat66_static_mapping_t * sm,
+                                              void *ctx);
+void nat66_static_mappings_walk (nat66_static_mapping_walk_fn_t fn,
+                                void *ctx);
+nat66_static_mapping_t *nat66_static_mapping_get (ip6_address_t * addr,
+                                                 u32 fib_index, u8 is_local);
+int nat66_static_mapping_add_del (ip6_address_t * l_addr,
+                                 ip6_address_t * e_addr, u32 vrf_id,
+                                 u8 is_add);
+
+#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_nat66_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/nat66_cli.c b/src/plugins/nat/nat66_cli.c
new file mode 100644 (file)
index 0000000..d34c171
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2018 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 NAT66 CLI
+ */
+
+#include <nat/nat66.h>
+#include <nat/nat.h>
+#include <vnet/fib/fib_table.h>
+
+static clib_error_t *
+nat66_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 = nat66_interface_add_del (sw_if_index, 1, is_add);
+         switch (rv)
+           {
+           case VNET_API_ERROR_NO_SUCH_ENTRY:
+             error =
+               clib_error_return (0, "%U NAT66 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 NAT66 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 NAT66 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 = nat66_interface_add_del (sw_if_index, 0, is_add);
+         switch (rv)
+           {
+           case VNET_API_ERROR_NO_SUCH_ENTRY:
+             error =
+               clib_error_return (0, "%U NAT66 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 NAT66 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 NAT66 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
+nat66_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),
+                  nat_interface_is_inside (i) ? "in" : "out");
+  return 0;
+}
+
+static clib_error_t *
+nat66_show_interfaces_command_fn (vlib_main_t * vm, unformat_input_t * input,
+                                 vlib_cli_command_t * cmd)
+{
+  vlib_cli_output (vm, "NAT66 interfaces:");
+  nat66_interfaces_walk (nat66_cli_interface_walk, vm);
+
+  return 0;
+}
+
+static clib_error_t *
+nat66_add_del_static_mapping_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 l_addr, e_addr;
+  u32 vrf_id = 0;
+  int 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, "local %U external %U",
+                   unformat_ip6_address, &l_addr,
+                   unformat_ip6_address, &e_addr))
+       ;
+      else if (unformat (line_input, "vrf %u", &vrf_id))
+       ;
+      else if (unformat (line_input, "del"))
+       is_add = 0;
+      else
+       {
+         error = clib_error_return (0, "unknown input: '%U'",
+                                    format_unformat_error, line_input);
+         goto done;
+       }
+    }
+
+  rv = nat66_static_mapping_add_del (&l_addr, &e_addr, vrf_id, is_add);
+
+  switch (rv)
+    {
+    case VNET_API_ERROR_NO_SUCH_ENTRY:
+      error = clib_error_return (0, "NAT66 static mapping entry not exist.");
+      goto done;
+    case VNET_API_ERROR_VALUE_EXIST:
+      error = clib_error_return (0, "NAT66 static mapping entry exist.");
+      goto done;
+    default:
+      break;
+    }
+
+done:
+  unformat_free (line_input);
+
+  return error;
+}
+
+static int
+nat66_cli_static_mapping_walk (nat66_static_mapping_t * sm, void *ctx)
+{
+  nat66_main_t *nm = &nat66_main;
+  vlib_main_t *vm = ctx;
+  fib_table_t *fib;
+  vlib_counter_t vc;
+
+  fib = fib_table_get (sm->fib_index, FIB_PROTOCOL_IP6);
+  if (!fib)
+    return -1;
+
+  vlib_get_combined_counter (&nm->session_counters, sm - nm->sm, &vc);
+
+  vlib_cli_output (vm, " local %U external %U vrf %d",
+                  format_ip6_address, &sm->l_addr,
+                  format_ip6_address, &sm->e_addr, fib->ft_table_id);
+  vlib_cli_output (vm, "  total pkts %lld, total bytes %lld", vc.packets,
+                  vc.bytes);
+
+  return 0;
+}
+
+static clib_error_t *
+nat66_show_static_mappings_command_fn (vlib_main_t * vm,
+                                      unformat_input_t * input,
+                                      vlib_cli_command_t * cmd)
+{
+  vlib_cli_output (vm, "NAT66 static mappings:");
+  nat66_static_mappings_walk (nat66_cli_static_mapping_walk, vm);
+  return 0;
+}
+
+/* *INDENT-OFF* */
+/*?
+ * @cliexpar
+ * @cliexstart{set interface nat66}
+ * Enable/disable NAT66 feature on the interface.
+ * To enable NAT66 feature with local (IPv6) network interface
+ * GigabitEthernet0/8/0 and external (IPv4) network interface
+ * GigabitEthernet0/a/0 use:
+ *  vpp# set interface nat66 in GigabitEthernet0/8/0 out GigabitEthernet0/a/0
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (set_interface_nat66_command, static) = {
+  .path = "set interface nat66",
+  .short_help = "set interface nat66 in|out <intfc> [del]",
+  .function = nat66_interface_feature_command_fn,
+};
+
+/*?
+ * @cliexpar
+ * @cliexstart{show nat66 interfaces}
+ * Show interfaces with NAT66 feature.
+ * To show interfaces with NAT66 feature use:
+ *  vpp# show nat66 interfaces
+ *  NAT66 interfaces:
+ *   GigabitEthernet0/8/0 in
+ *   GigabitEthernet0/a/0 out
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (show_nat66_interfaces_command, static) = {
+  .path = "show nat66 interfaces",
+  .short_help = "show nat66 interfaces",
+  .function = nat66_show_interfaces_command_fn,
+};
+
+/*?
+ * @cliexpar
+ * @cliexstart{nat66 add static mapping}
+ * Add/delete NAT66 static mapping entry.
+ * To add NAT66 static mapping entry use:
+ *  vpp# nat66 add static mapping local fd01:1::4 external 2001:db8:c000:223::
+ *  vpp# nat66 add static mapping local fd01:1::2 external 2001:db8:c000:221:: vrf 10
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (show_nat66_add_del_static_mapping_command, static) = {
+  .path = "nat66 add static mapping",
+  .short_help = "nat66 add static mapping local <ip6-addr> external <ip6-addr>"
+                " [vfr <table-id>] [del]",
+  .function = nat66_add_del_static_mapping_command_fn,
+};
+
+/*?
+ * @cliexpar
+ * @cliexstart{show nat66 static mappings}
+ * Show NAT66 static mappings.
+ * To show NAT66 static mappings use:
+ *  vpp# show nat66 static mappings
+ *  NAT66 static mappings:
+ *   local fd01:1::4 external 2001:db8:c000:223:: vrf 0
+ *   local fd01:1::2 external 2001:db8:c000:221:: vrf 10
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (show_nat66_static_mappings_command, static) = {
+  .path = "show nat66 static mappings",
+  .short_help = "show nat66 static mappings",
+  .function = nat66_show_static_mappings_command_fn,
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/nat66_in2out.c b/src/plugins/nat/nat66_in2out.c
new file mode 100644 (file)
index 0000000..1ec4da7
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2018 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 NAT66 inside to outside network translation
+ */
+
+#include <nat/nat66.h>
+#include <vnet/ip/ip6_to_ip4.h>
+#include <vnet/fib/fib_table.h>
+
+typedef struct
+{
+  u32 sw_if_index;
+  u32 next_index;
+} nat66_in2out_trace_t;
+
+static u8 *
+format_nat66_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 *);
+  nat66_in2out_trace_t *t = va_arg (*args, nat66_in2out_trace_t *);
+
+  s =
+    format (s, "NAT66-in2out: sw_if_index %d, next index %d", t->sw_if_index,
+           t->next_index);
+
+  return s;
+}
+
+vlib_node_registration_t nat66_in2out_node;
+
+#define foreach_nat66_in2out_error                       \
+_(IN2OUT_PACKETS, "good in2out packets processed")       \
+_(NO_TRANSLATION, "no translation")                      \
+_(UNKNOWN, "unknown")
+
+typedef enum
+{
+#define _(sym,str) NAT66_IN2OUT_ERROR_##sym,
+  foreach_nat66_in2out_error
+#undef _
+    NAT66_IN2OUT_N_ERROR,
+} nat66_in2out_error_t;
+
+static char *nat66_in2out_error_strings[] = {
+#define _(sym,string) string,
+  foreach_nat66_in2out_error
+#undef _
+};
+
+typedef enum
+{
+  NAT66_IN2OUT_NEXT_IP6_LOOKUP,
+  NAT66_IN2OUT_NEXT_DROP,
+  NAT66_IN2OUT_N_NEXT,
+} nat66_in2out_next_t;
+
+static inline uword
+nat66_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
+                     vlib_frame_t * frame)
+{
+  u32 n_left_from, *from, *to_next;
+  nat66_in2out_next_t next_index;
+  u32 pkts_processed = 0;
+  u32 thread_index = vlib_get_thread_index ();
+  nat66_main_t *nm = &nat66_main;
+
+  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 = NAT66_IN2OUT_NEXT_IP6_LOOKUP;
+         ip6_header_t *ip60;
+         u16 l4_offset0, frag_offset0;
+         u8 l4_protocol0;
+         nat66_static_mapping_t *sm0;
+         u32 sw_if_index0, fib_index0;
+         udp_header_t *udp0;
+         tcp_header_t *tcp0;
+         icmp46_header_t *icmp0;
+         u16 *checksum0 = 0;
+         ip_csum_t csum0;
+
+         /* 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);
+
+         if (PREDICT_FALSE
+             (ip6_parse
+              (ip60, b0->current_length, &l4_protocol0, &l4_offset0,
+               &frag_offset0)))
+           {
+             next0 = NAT66_IN2OUT_NEXT_DROP;
+             b0->error = node->errors[NAT66_IN2OUT_ERROR_UNKNOWN];
+             goto trace0;
+           }
+
+         sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+         fib_index0 =
+           fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6,
+                                                sw_if_index0);
+
+         sm0 = nat66_static_mapping_get (&ip60->src_address, fib_index0, 1);
+         if (PREDICT_FALSE (!sm0))
+           {
+             goto trace0;
+           }
+
+         if (l4_protocol0 == IP_PROTOCOL_UDP)
+           {
+             udp0 = (udp_header_t *) u8_ptr_add (ip60, l4_offset0);
+             checksum0 = &udp0->checksum;
+           }
+         else if (l4_protocol0 == IP_PROTOCOL_TCP)
+           {
+             tcp0 = (tcp_header_t *) u8_ptr_add (ip60, l4_offset0);
+             checksum0 = &tcp0->checksum;
+           }
+         else if (l4_protocol0 == IP_PROTOCOL_ICMP6)
+           {
+             icmp0 = (icmp46_header_t *) u8_ptr_add (ip60, l4_offset0);
+             checksum0 = &icmp0->checksum;
+           }
+         else
+           goto skip_csum0;
+
+         csum0 = ip_csum_sub_even (*checksum0, ip60->src_address.as_u64[0]);
+         csum0 = ip_csum_sub_even (csum0, ip60->src_address.as_u64[1]);
+         csum0 = ip_csum_add_even (csum0, sm0->e_addr.as_u64[0]);
+         csum0 = ip_csum_add_even (csum0, sm0->e_addr.as_u64[1]);
+         *checksum0 = ip_csum_fold (csum0);
+
+       skip_csum0:
+         ip60->src_address.as_u64[0] = sm0->e_addr.as_u64[0];
+         ip60->src_address.as_u64[1] = sm0->e_addr.as_u64[1];
+
+         vlib_increment_combined_counter (&nm->session_counters,
+                                          thread_index, sm0 - nm->sm, 1,
+                                          vlib_buffer_length_in_chain (vm,
+                                                                       b0));
+
+       trace0:
+         if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+                            && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+           {
+             nat66_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 != NAT66_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, nat66_in2out_node.index,
+                              NAT66_IN2OUT_ERROR_IN2OUT_PACKETS,
+                              pkts_processed);
+  return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat66_in2out_node) = {
+  .function = nat66_in2out_node_fn,
+  .name = "nat66-in2out",
+  .vector_size = sizeof (u32),
+  .format_trace = format_nat66_in2out_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN (nat66_in2out_error_strings),
+  .error_strings = nat66_in2out_error_strings,
+  .n_next_nodes = NAT66_IN2OUT_N_NEXT,
+  /* edit / add dispositions here */
+  .next_nodes = {
+    [NAT66_IN2OUT_NEXT_DROP] = "error-drop",
+    [NAT66_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup",
+  },
+};
+/* *INDENT-ON* */
+
+VLIB_NODE_FUNCTION_MULTIARCH (nat66_in2out_node, nat66_in2out_node_fn);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/nat66_out2in.c b/src/plugins/nat/nat66_out2in.c
new file mode 100644 (file)
index 0000000..a28a4c3
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2018 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 NAT66 outside to inside network translation
+ */
+
+#include <nat/nat66.h>
+#include <vnet/ip/ip6_to_ip4.h>
+#include <vnet/fib/fib_table.h>
+
+typedef struct
+{
+  u32 sw_if_index;
+  u32 next_index;
+} nat66_out2in_trace_t;
+
+static u8 *
+format_nat66_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 *);
+  nat66_out2in_trace_t *t = va_arg (*args, nat66_out2in_trace_t *);
+
+  s =
+    format (s, "NAT66-out2in: sw_if_index %d, next index %d", t->sw_if_index,
+           t->next_index);
+
+  return s;
+}
+
+vlib_node_registration_t nat66_out2in_node;
+
+#define foreach_nat66_out2in_error                       \
+_(OUT2IN_PACKETS, "good out2in packets processed")       \
+_(NO_TRANSLATION, "no translation")                      \
+_(UNKNOWN, "unknown")
+
+typedef enum
+{
+#define _(sym,str) NAT66_OUT2IN_ERROR_##sym,
+  foreach_nat66_out2in_error
+#undef _
+    NAT66_OUT2IN_N_ERROR,
+} nat66_out2in_error_t;
+
+static char *nat66_out2in_error_strings[] = {
+#define _(sym,string) string,
+  foreach_nat66_out2in_error
+#undef _
+};
+
+typedef enum
+{
+  NAT66_OUT2IN_NEXT_IP6_LOOKUP,
+  NAT66_OUT2IN_NEXT_DROP,
+  NAT66_OUT2IN_N_NEXT,
+} nat66_out2in_next_t;
+
+static inline uword
+nat66_out2in_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
+                     vlib_frame_t * frame)
+{
+  u32 n_left_from, *from, *to_next;
+  nat66_out2in_next_t next_index;
+  u32 pkts_processed = 0;
+  u32 thread_index = vlib_get_thread_index ();
+  nat66_main_t *nm = &nat66_main;
+
+  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 = NAT66_OUT2IN_NEXT_IP6_LOOKUP;
+         ip6_header_t *ip60;
+         u16 l4_offset0, frag_offset0;
+         u8 l4_protocol0;
+         nat66_static_mapping_t *sm0;
+         u32 sw_if_index0, fib_index0;
+         udp_header_t *udp0;
+         tcp_header_t *tcp0;
+         icmp46_header_t *icmp0;
+         u16 *checksum0 = 0;
+         ip_csum_t csum0;
+
+         /* 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);
+
+         if (PREDICT_FALSE
+             (ip6_parse
+              (ip60, b0->current_length, &l4_protocol0, &l4_offset0,
+               &frag_offset0)))
+           {
+             next0 = NAT66_OUT2IN_NEXT_DROP;
+             b0->error = node->errors[NAT66_OUT2IN_ERROR_UNKNOWN];
+             goto trace0;
+           }
+
+         sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+         fib_index0 =
+           fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6,
+                                                sw_if_index0);
+
+         sm0 = nat66_static_mapping_get (&ip60->dst_address, fib_index0, 0);
+         if (PREDICT_FALSE (!sm0))
+           {
+             goto trace0;
+           }
+
+         if (l4_protocol0 == IP_PROTOCOL_UDP)
+           {
+             udp0 = (udp_header_t *) u8_ptr_add (ip60, l4_offset0);
+             checksum0 = &udp0->checksum;
+           }
+         else if (l4_protocol0 == IP_PROTOCOL_TCP)
+           {
+             tcp0 = (tcp_header_t *) u8_ptr_add (ip60, l4_offset0);
+             checksum0 = &tcp0->checksum;
+           }
+         else if (l4_protocol0 == IP_PROTOCOL_ICMP6)
+           {
+             icmp0 = (icmp46_header_t *) u8_ptr_add (ip60, l4_offset0);
+             checksum0 = &icmp0->checksum;
+           }
+         else
+           goto skip_csum0;
+
+         csum0 = ip_csum_sub_even (*checksum0, ip60->dst_address.as_u64[0]);
+         csum0 = ip_csum_sub_even (csum0, ip60->dst_address.as_u64[1]);
+         csum0 = ip_csum_add_even (csum0, sm0->l_addr.as_u64[0]);
+         csum0 = ip_csum_add_even (csum0, sm0->l_addr.as_u64[1]);
+         *checksum0 = ip_csum_fold (csum0);
+
+       skip_csum0:
+         ip60->dst_address.as_u64[0] = sm0->l_addr.as_u64[0];
+         ip60->dst_address.as_u64[1] = sm0->l_addr.as_u64[1];
+         vnet_buffer (b0)->sw_if_index[VLIB_TX] = sm0->fib_index;
+
+         vlib_increment_combined_counter (&nm->session_counters,
+                                          thread_index, sm0 - nm->sm, 1,
+                                          vlib_buffer_length_in_chain (vm,
+                                                                       b0));
+
+       trace0:
+         if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+                            && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+           {
+             nat66_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 != NAT66_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, nat66_out2in_node.index,
+                              NAT66_OUT2IN_ERROR_OUT2IN_PACKETS,
+                              pkts_processed);
+  return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat66_out2in_node) = {
+  .function = nat66_out2in_node_fn,
+  .name = "nat66-out2in",
+  .vector_size = sizeof (u32),
+  .format_trace = format_nat66_out2in_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN (nat66_out2in_error_strings),
+  .error_strings = nat66_out2in_error_strings,
+  .n_next_nodes = NAT66_OUT2IN_N_NEXT,
+  /* edit / add dispositions here */
+  .next_nodes = {
+    [NAT66_OUT2IN_NEXT_DROP] = "error-drop",
+    [NAT66_OUT2IN_NEXT_IP6_LOOKUP] = "ip6-lookup",
+  },
+};
+/* *INDENT-ON* */
+
+VLIB_NODE_FUNCTION_MULTIARCH (nat66_out2in_node, nat66_out2in_node_fn);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index 2fefb8b..433840e 100644 (file)
@@ -21,6 +21,7 @@
 #include <nat/nat.h>
 #include <nat/nat_det.h>
 #include <nat/nat64.h>
+#include <nat/nat66.h>
 #include <nat/dslite.h>
 #include <nat/nat_reass.h>
 #include <vlibapi/api.h>
@@ -2594,6 +2595,192 @@ static void *vl_api_dslite_add_del_pool_addr_range_t_print
 }
 
 
+/*************/
+/*** NAT66 ***/
+/*************/
+
+static void
+vl_api_nat66_add_del_interface_t_handler (vl_api_nat66_add_del_interface_t *
+                                         mp)
+{
+  snat_main_t *sm = &snat_main;
+  vl_api_nat66_add_del_interface_reply_t *rmp;
+  int rv = 0;
+
+  VALIDATE_SW_IF_INDEX (mp);
+
+  rv =
+    nat66_interface_add_del (ntohl (mp->sw_if_index), mp->is_inside,
+                            mp->is_add);
+
+  BAD_SW_IF_INDEX_LABEL;
+
+  REPLY_MACRO (VL_API_NAT66_ADD_DEL_INTERFACE_REPLY);
+}
+
+static void *
+vl_api_nat66_add_del_interface_t_print (vl_api_nat66_add_del_interface_t * mp,
+                                       void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: nat66_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 void
+  vl_api_nat66_add_del_static_mapping_t_handler
+  (vl_api_nat66_add_del_static_mapping_t * mp)
+{
+  snat_main_t *sm = &snat_main;
+  vl_api_nat66_add_del_static_mapping_reply_t *rmp;
+  ip6_address_t l_addr, e_addr;
+  int rv = 0;
+
+  memcpy (&l_addr.as_u8, mp->local_ip_address, 16);
+  memcpy (&e_addr.as_u8, mp->external_ip_address, 16);
+
+  rv =
+    nat66_static_mapping_add_del (&l_addr, &e_addr,
+                                 clib_net_to_host_u32 (mp->vrf_id),
+                                 mp->is_add);
+
+  REPLY_MACRO (VL_API_NAT66_ADD_DEL_STATIC_MAPPING_REPLY);
+}
+
+static void *vl_api_nat66_add_del_static_mapping_t_print
+  (vl_api_nat66_add_del_static_mapping_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: nat66_add_del_static_mapping ");
+  s = format (s, "local_ip_address %U external_ip_address %U vrf_id %d %s",
+             format_ip6_address, mp->local_ip_address,
+             format_ip6_address, mp->external_ip_address,
+             clib_net_to_host_u32 (mp->vrf_id), mp->is_add ? "" : "del");
+
+  FINISH;
+}
+
+typedef struct nat66_api_walk_ctx_t_
+{
+  svm_queue_t *q;
+  u32 context;
+} nat66_api_walk_ctx_t;
+
+static int
+nat66_api_interface_walk (snat_interface_t * i, void *arg)
+{
+  vl_api_nat66_interface_details_t *rmp;
+  snat_main_t *sm = &snat_main;
+  nat66_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_NAT66_INTERFACE_DETAILS + sm->msg_id_base);
+  rmp->sw_if_index = ntohl (i->sw_if_index);
+  rmp->is_inside = nat_interface_is_inside (i);
+  rmp->context = ctx->context;
+
+  vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp);
+
+  return 0;
+}
+
+static void
+vl_api_nat66_interface_dump_t_handler (vl_api_nat66_interface_dump_t * mp)
+{
+  svm_queue_t *q;
+
+  q = vl_api_client_index_to_input_queue (mp->client_index);
+  if (q == 0)
+    return;
+
+  nat66_api_walk_ctx_t ctx = {
+    .q = q,
+    .context = mp->context,
+  };
+
+  nat66_interfaces_walk (nat66_api_interface_walk, &ctx);
+}
+
+static void *
+vl_api_nat66_interface_dump_t_print (vl_api_nat66_interface_dump_t * mp,
+                                    void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: nat66_interface_dump ");
+
+  FINISH;
+}
+
+static int
+nat66_api_static_mapping_walk (nat66_static_mapping_t * m, void *arg)
+{
+  vl_api_nat66_static_mapping_details_t *rmp;
+  nat66_main_t *nm = &nat66_main;
+  snat_main_t *sm = &snat_main;
+  nat66_api_walk_ctx_t *ctx = arg;
+  fib_table_t *fib;
+  vlib_counter_t vc;
+
+  fib = fib_table_get (m->fib_index, FIB_PROTOCOL_IP6);
+  if (!fib)
+    return -1;
+
+  vlib_get_combined_counter (&nm->session_counters, m - nm->sm, &vc);
+
+  rmp = vl_msg_api_alloc (sizeof (*rmp));
+  memset (rmp, 0, sizeof (*rmp));
+  rmp->_vl_msg_id =
+    ntohs (VL_API_NAT66_STATIC_MAPPING_DETAILS + sm->msg_id_base);
+  clib_memcpy (rmp->local_ip_address, &m->l_addr, 16);
+  clib_memcpy (rmp->external_ip_address, &m->e_addr, 16);
+  rmp->vrf_id = ntohl (fib->ft_table_id);
+  rmp->total_bytes = clib_host_to_net_u64 (vc.bytes);
+  rmp->total_pkts = clib_host_to_net_u64 (vc.packets);
+  rmp->context = ctx->context;
+
+  vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp);
+
+  return 0;
+}
+
+static void
+vl_api_nat66_static_mapping_dump_t_handler (vl_api_nat66_static_mapping_dump_t
+                                           * mp)
+{
+  svm_queue_t *q;
+
+  q = vl_api_client_index_to_input_queue (mp->client_index);
+  if (q == 0)
+    return;
+
+  nat66_api_walk_ctx_t ctx = {
+    .q = q,
+    .context = mp->context,
+  };
+
+  nat66_static_mappings_walk (nat66_api_static_mapping_walk, &ctx);
+}
+
+static void *
+vl_api_nat66_static_mapping_dump_t_print (vl_api_nat66_static_mapping_dump_t *
+                                         mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: nat66_static_mapping_dump ");
+
+  FINISH;
+}
+
+
 /* List of message types that this plugin understands */
 #define foreach_snat_plugin_api_msg                                     \
 _(NAT_CONTROL_PING, nat_control_ping)                                   \
@@ -2650,7 +2837,11 @@ _(DSLITE_ADD_DEL_POOL_ADDR_RANGE, dslite_add_del_pool_addr_range)       \
 _(DSLITE_SET_AFTR_ADDR, dslite_set_aftr_addr)                           \
 _(DSLITE_GET_AFTR_ADDR, dslite_get_aftr_addr)                           \
 _(DSLITE_SET_B4_ADDR, dslite_set_b4_addr)                               \
-_(DSLITE_GET_B4_ADDR, dslite_get_b4_addr)
+_(DSLITE_GET_B4_ADDR, dslite_get_b4_addr)                               \
+_(NAT66_ADD_DEL_INTERFACE, nat66_add_del_interface)                     \
+_(NAT66_INTERFACE_DUMP, nat66_interface_dump)                           \
+_(NAT66_ADD_DEL_STATIC_MAPPING, nat66_add_del_static_mapping)           \
+_(NAT66_STATIC_MAPPING_DUMP, nat66_static_mapping_dump)
 
 /* Set up the API message handling tables */
 static clib_error_t *
index e3b4485..7ff3f85 100644 (file)
@@ -6185,5 +6185,136 @@ class TestDSliteCE(MethodHolder):
             self.logger.info(
                 self.vapi.cli("show dslite b4-tunnel-endpoint-address"))
 
+
+class TestNAT66(MethodHolder):
+    """ NAT66 Test Cases """
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestNAT66, cls).setUpClass()
+
+        try:
+            cls.nat_addr = 'fd01:ff::2'
+            cls.nat_addr_n = socket.inet_pton(socket.AF_INET6, cls.nat_addr)
+
+            cls.create_pg_interfaces(range(2))
+            cls.interfaces = list(cls.pg_interfaces)
+
+            for i in cls.interfaces:
+                i.admin_up()
+                i.config_ip6()
+                i.configure_ipv6_neighbors()
+
+        except Exception:
+            super(TestNAT66, cls).tearDownClass()
+            raise
+
+    def test_static(self):
+        """ 1:1 NAT66 test """
+        self.vapi.nat66_add_del_interface(self.pg0.sw_if_index)
+        self.vapi.nat66_add_del_interface(self.pg1.sw_if_index, is_inside=0)
+        self.vapi.nat66_add_del_static_mapping(self.pg0.remote_ip6n,
+                                               self.nat_addr_n)
+
+        # in2out
+        pkts = []
+        p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+             IPv6(src=self.pg0.remote_ip6, dst=self.pg1.remote_ip6) /
+             TCP())
+        pkts.append(p)
+        p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+             IPv6(src=self.pg0.remote_ip6, dst=self.pg1.remote_ip6) /
+             UDP())
+        pkts.append(p)
+        p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+             IPv6(src=self.pg0.remote_ip6, dst=self.pg1.remote_ip6) /
+             ICMPv6EchoRequest())
+        pkts.append(p)
+        p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+             IPv6(src=self.pg0.remote_ip6, dst=self.pg1.remote_ip6) /
+             GRE() / IP() / TCP())
+        pkts.append(p)
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg1.get_capture(len(pkts))
+        for packet in capture:
+            try:
+                self.assertEqual(packet[IPv6].src, self.nat_addr)
+                self.assertEqual(packet[IPv6].dst, self.pg1.remote_ip6)
+                if packet.haslayer(TCP):
+                    self.check_tcp_checksum(packet)
+                elif packet.haslayer(UDP):
+                    self.check_udp_checksum(packet)
+                elif packet.haslayer(ICMPv6EchoRequest):
+                    self.check_icmpv6_checksum(packet)
+            except:
+                self.logger.error(ppp("Unexpected or invalid packet:", packet))
+                raise
+
+        # out2in
+        pkts = []
+        p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
+             IPv6(src=self.pg1.remote_ip6, dst=self.nat_addr) /
+             TCP())
+        pkts.append(p)
+        p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
+             IPv6(src=self.pg1.remote_ip6, dst=self.nat_addr) /
+             UDP())
+        pkts.append(p)
+        p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
+             IPv6(src=self.pg1.remote_ip6, dst=self.nat_addr) /
+             ICMPv6EchoReply())
+        pkts.append(p)
+        p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
+             IPv6(src=self.pg1.remote_ip6, dst=self.nat_addr) /
+             GRE() / IP() / TCP())
+        pkts.append(p)
+        self.pg1.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg0.get_capture(len(pkts))
+        for packet in capture:
+            try:
+                self.assertEqual(packet[IPv6].src, self.pg1.remote_ip6)
+                self.assertEqual(packet[IPv6].dst, self.pg0.remote_ip6)
+                if packet.haslayer(TCP):
+                    self.check_tcp_checksum(packet)
+                elif packet.haslayer(UDP):
+                    self.check_udp_checksum(packet)
+                elif packet.haslayer(ICMPv6EchoReply):
+                    self.check_icmpv6_checksum(packet)
+            except:
+                self.logger.error(ppp("Unexpected or invalid packet:", packet))
+                raise
+
+        sm = self.vapi.nat66_static_mapping_dump()
+        self.assertEqual(len(sm), 1)
+        self.assertEqual(sm[0].total_pkts, 8)
+
+    def clear_nat66(self):
+        """
+        Clear NAT66 configuration.
+        """
+        interfaces = self.vapi.nat66_interface_dump()
+        for intf in interfaces:
+            self.vapi.nat66_add_del_interface(intf.sw_if_index,
+                                              intf.is_inside,
+                                              is_add=0)
+
+        static_mappings = self.vapi.nat66_static_mapping_dump()
+        for sm in static_mappings:
+            self.vapi.nat66_add_del_static_mapping(sm.local_ip_address,
+                                                   sm.external_ip_address,
+                                                   sm.vrf_id,
+                                                   is_add=0)
+
+    def tearDown(self):
+        super(TestNAT66, self).tearDown()
+        if not self.vpp_dead:
+            self.logger.info(self.vapi.cli("show nat66 interfaces"))
+            self.logger.info(self.vapi.cli("show nat66 static mappings"))
+            self.clear_nat66()
+
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)
index b791444..0269736 100644 (file)
@@ -1860,6 +1860,54 @@ class VppPapiProvider(object):
              'end_addr': end_addr,
              'is_add': is_add})
 
+    def nat66_add_del_interface(
+            self,
+            sw_if_index,
+            is_inside=1,
+            is_add=1):
+        """Enable/disable NAT66 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.nat66_add_del_interface,
+            {'sw_if_index': sw_if_index,
+             'is_inside': is_inside,
+             'is_add': is_add})
+
+    def nat66_add_del_static_mapping(
+            self,
+            in_ip,
+            out_ip,
+            vrf_id=0,
+            is_add=1):
+        """Add/delete NAT66 static mapping
+
+        :param in_ip: Inside IPv6 address
+        :param out_ip: Outside IPv6 address
+        :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.nat66_add_del_static_mapping,
+            {'local_ip_address': in_ip,
+             'external_ip_address': out_ip,
+             'vrf_id': vrf_id,
+             'is_add': is_add})
+
+    def nat66_interface_dump(self):
+        """Dump interfaces with NAT66 feature
+        :return: Dictionary of interfaces with NAT66 feature
+        """
+        return self.api(self.papi.nat66_interface_dump, {})
+
+    def nat66_static_mapping_dump(self):
+        """Dump NAT66 static mappings
+        :return: Dictionary of NAT66 static mappings
+        """
+        return self.api(self.papi.nat66_static_mapping_dump, {})
+
     def control_ping(self):
         self.api(self.papi.control_ping)