aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatus Fabian <matfabia@cisco.com>2018-01-22 03:41:53 -0800
committerOle Trøan <otroan@employees.org>2018-01-31 17:29:13 +0000
commitf2a23cc8602525a325eaeaae6aca5e18882c33da (patch)
tree272d142e1e71d80151a1ba9d68314a31468d9803
parentf7ad5cbe819533523169e8a88876b94b9f38789c (diff)
NAT66 1:1 mapping (VPP-1108)
Support the 1:1 translation of source address for IPv6 Change-Id: I934d18e5ec508bf7422d796ee5f172b79c048011 Signed-off-by: Matus Fabian <matfabia@cisco.com>
-rw-r--r--src/plugins/nat.am6
-rw-r--r--src/plugins/nat/nat.api81
-rw-r--r--src/plugins/nat/nat.c3
-rw-r--r--src/plugins/nat/nat66.c239
-rw-r--r--src/plugins/nat/nat66.h89
-rw-r--r--src/plugins/nat/nat66_cli.c320
-rw-r--r--src/plugins/nat/nat66_in2out.c224
-rw-r--r--src/plugins/nat/nat66_out2in.c225
-rw-r--r--src/plugins/nat/nat_api.c193
-rw-r--r--test/test_nat.py131
-rw-r--r--test/vpp_papi_provider.py48
11 files changed, 1557 insertions, 2 deletions
diff --git a/src/plugins/nat.am b/src/plugins/nat.am
index 65985fb6bdd..ce3e5a2d3ae 100644
--- a/src/plugins/nat.am
+++ b/src/plugins/nat.am
@@ -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
diff --git a/src/plugins/nat/nat.api b/src/plugins/nat/nat.api
index ce565f6b9b5..1a960173c43 100644
--- a/src/plugins/nat/nat.api
+++ b/src/plugins/nat/nat.api
@@ -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;
+};
diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c
index ef740d9d12b..314fadf613d 100644
--- a/src/plugins/nat/nat.c
+++ b/src/plugins/nat/nat.c
@@ -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
index 00000000000..6257c990400
--- /dev/null
+++ b/src/plugins/nat/nat66.c
@@ -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
index 00000000000..d2ddd0c70e1
--- /dev/null
+++ b/src/plugins/nat/nat66.h
@@ -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
index 00000000000..d34c1715aea
--- /dev/null
+++ b/src/plugins/nat/nat66_cli.c
@@ -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
index 00000000000..1ec4da78d63
--- /dev/null
+++ b/src/plugins/nat/nat66_in2out.c
@@ -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
index 00000000000..a28a4c3ade5
--- /dev/null
+++ b/src/plugins/nat/nat66_out2in.c
@@ -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:
+ */
diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c
index 2fefb8b7c1b..433840e2b89 100644
--- a/src/plugins/nat/nat_api.c
+++ b/src/plugins/nat/nat_api.c
@@ -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 *
diff --git a/test/test_nat.py b/test/test_nat.py
index e3b44859f61..7ff3f8517ad 100644
--- a/test/test_nat.py
+++ b/test/test_nat.py
@@ -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)
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index b791444fdcd..0269736cd78 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -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)