summaryrefslogtreecommitdiffstats
path: root/src/plugins/nat/nat66
diff options
context:
space:
mode:
authorOle Troan <ot@cisco.com>2020-07-01 13:16:16 +0200
committerAndrew Yourtchenko <ayourtch@gmail.com>2020-07-02 13:13:27 +0000
commit96068d6b94207435f9c9619e2ce7921ebc812ade (patch)
treef7f36f8cc2dfc70730102d72b0ea254d10f84457 /src/plugins/nat/nat66
parentc6eae9c079defa4812270945d614c4598db262d8 (diff)
nat: nat66 to plugin
Type: refactor Change-Id: I8c1f0c02a4522c1f9e461ddadd59938579ec00c6 Signed-off-by: Ole Troan <ot@cisco.com>
Diffstat (limited to 'src/plugins/nat/nat66')
-rw-r--r--src/plugins/nat/nat66/nat66.api99
-rw-r--r--src/plugins/nat/nat66/nat66.c284
-rw-r--r--src/plugins/nat/nat66/nat66.h123
-rw-r--r--src/plugins/nat/nat66/nat66_api.c174
-rw-r--r--src/plugins/nat/nat66/nat66_cli.c319
-rw-r--r--src/plugins/nat/nat66/nat66_in2out.c262
-rw-r--r--src/plugins/nat/nat66/nat66_out2in.c220
7 files changed, 1481 insertions, 0 deletions
diff --git a/src/plugins/nat/nat66/nat66.api b/src/plugins/nat/nat66/nat66.api
new file mode 100644
index 00000000000..6906e41f539
--- /dev/null
+++ b/src/plugins/nat/nat66/nat66.api
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2020 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.
+ */
+
+option version = "1.0.0";
+import "vnet/ip/ip_types.api";
+import "vnet/interface_types.api";
+import "plugins/nat/nat_types.api";
+
+/** \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 - true if add, false if delete
+ @param flags - flag NAT_IS_INSIDE if interface is inside or
+ interface is outside,
+ @param sw_if_index - software index of the interface
+*/
+autoreply define nat66_add_del_interface {
+ u32 client_index;
+ u32 context;
+ bool is_add;
+ vl_api_nat_config_flags_t flags;
+ vl_api_interface_index_t 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 flags - flag NAT_IS_INSIDE if interface is inside or
+ interface is outside,
+ @param sw_if_index - software index of the interface
+*/
+define nat66_interface_details {
+ u32 context;
+ vl_api_nat_config_flags_t flags;
+ vl_api_interface_index_t 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 - true if add, false 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;
+ bool is_add;
+ vl_api_ip6_address_t local_ip_address;
+ vl_api_ip6_address_t external_ip_address;
+ 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;
+ vl_api_ip6_address_t local_ip_address;
+ vl_api_ip6_address_t external_ip_address;
+ u32 vrf_id;
+ u64 total_bytes;
+ u64 total_pkts;
+};
diff --git a/src/plugins/nat/nat66/nat66.c b/src/plugins/nat/nat66/nat66.c
new file mode 100644
index 00000000000..36d5d733a6d
--- /dev/null
+++ b/src/plugins/nat/nat66/nat66.c
@@ -0,0 +1,284 @@
+/*
+ * 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 <vpp/app/version.h>
+#include <vnet/plugin/plugin.h>
+#include <nat/nat66/nat66.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/ip/reass/ip6_sv_reass.h>
+
+nat66_main_t nat66_main;
+fib_source_t nat_fib_src_hi;
+
+/* *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"),
+ .runs_after = VNET_FEATURES ("ip6-sv-reassembly-feature"),
+};
+VNET_FEATURE_INIT (nat66_out2in, static) = {
+ .arc_name = "ip6-unicast",
+ .node_name = "nat66-out2in",
+ .runs_before = VNET_FEATURES ("ip6-lookup"),
+ .runs_after = VNET_FEATURES ("ip6-sv-reassembly-feature"),
+};
+
+/* *INDENT-ON* */
+
+clib_error_t *nat66_plugin_api_hookup (vlib_main_t * vm);
+static clib_error_t *
+nat66_init (vlib_main_t * vm)
+{
+ nat66_main_t *nm = &nat66_main;
+ vlib_node_t *node;
+ u32 static_mapping_buckets = 1024;
+ uword static_mapping_memory_size = 64 << 20;
+
+ node = vlib_get_node_by_name (vm, (u8 *) "nat66-in2out");
+ nm->in2out_node_index = node->index;
+
+ node = vlib_get_node_by_name (vm, (u8 *) "nat66-out2in");
+ nm->out2in_node_index = node->index;
+
+ 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";
+
+ nat_fib_src_hi = fib_source_allocate ("nat66-hi",
+ FIB_SOURCE_PRIORITY_HI,
+ FIB_SOURCE_BH_SIMPLE);
+
+ return nat66_plugin_api_hookup (vm);
+}
+
+int
+nat66_interface_add_del (u32 sw_if_index, u8 is_inside, u8 is_add)
+{
+ nat66_main_t *nm = &nat66_main;
+ nat66_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 ? NAT66_INTERFACE_FLAG_IS_INSIDE :
+ NAT66_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";
+ int rv = ip6_sv_reass_enable_disable_with_refcnt (sw_if_index, is_add);
+ if (rv)
+ return rv;
+ 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;
+ nat66_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,
+ nat_fib_src_hi);
+ pool_get (nm->sm, sm);
+ clib_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))
+ nat66_elog_warn ("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))
+ nat66_elog_warn ("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))
+ nat66_elog_warn ("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))
+ nat66_elog_warn ("nat66-static-map-by-external delete key failed");
+ fib_table_unlock (sm->fib_index, FIB_PROTOCOL_IP6, nat_fib_src_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* */
+}
+
+/*static*/ void
+nat66_config (void)
+{
+ nat66_main_t *nm = &nat66_main;
+ u32 outside_ip6_vrf_id = 0;
+
+ nm->outside_vrf_id = outside_ip6_vrf_id;
+ nm->outside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
+ outside_ip6_vrf_id,
+ nat_fib_src_hi);
+
+}
+
+/* *INDENT-OFF* */
+VLIB_PLUGIN_REGISTER () =
+{
+ .version = VPP_BUILD_VER,
+ .description = "NAT66",
+};
+
+VLIB_INIT_FUNCTION (nat66_init);
+
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/nat66/nat66.h b/src/plugins/nat/nat66/nat66.h
new file mode 100644
index 00000000000..9757f030032
--- /dev/null
+++ b/src/plugins/nat/nat66/nat66.h
@@ -0,0 +1,123 @@
+/*
+ * 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 <vnet/ip/ip.h>
+#include <vppinfra/bihash_24_8.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
+{
+ u32 sw_if_index;
+ u8 flags;
+} nat66_interface_t;
+#define NAT66_INTERFACE_FLAG_IS_INSIDE 1
+#define NAT66_INTERFACE_FLAG_IS_OUTSIDE 2
+#define nat66_interface_is_inside(i) i->flags & NAT66_INTERFACE_FLAG_IS_INSIDE
+#define nat66_interface_is_outside(i) i->flags & NAT66_INTERFACE_FLAG_IS_OUTSIDE
+
+typedef struct
+{
+ /** Interface pool */
+ nat66_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;
+ /** node index **/
+ u32 in2out_node_index;
+ u32 out2in_node_index;
+
+ u32 outside_vrf_id;
+ u32 outside_fib_index;
+
+ u16 msg_id_base;
+ u8 log_level;
+} nat66_main_t;
+
+#define nat66_elog(_level, _str) \
+do \
+ { \
+ nat66_main_t *nm = &nat66_main; \
+ if (PREDICT_FALSE (nm->log_level >= _level)) \
+ { \
+ ELOG_TYPE_DECLARE (e) = \
+ { \
+ .format = "nat66-msg " _str, \
+ .format_args = "", \
+ }; \
+ ELOG_DATA (&vlib_global_main.elog_main, e); \
+ } \
+ } while (0);
+
+#define nat66_elog_warn(nat_elog_str) \
+ nat66_elog(0x02, "[warning] " nat_elog_str)
+
+
+extern nat66_main_t nat66_main;
+extern vlib_node_registration_t nat66_in2out_node;
+extern vlib_node_registration_t nat66_out2in_node;
+
+typedef int (*nat66_interface_walk_fn_t) (nat66_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);
+
+#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/nat66_api.c b/src/plugins/nat/nat66/nat66_api.c
new file mode 100644
index 00000000000..2a2b3cc07ad
--- /dev/null
+++ b/src/plugins/nat/nat66/nat66_api.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#include <vlibmemory/api.h>
+#include <nat/nat66/nat66.h>
+#include <nat/nat66/nat66.api_enum.h>
+#include <nat/nat66/nat66.api_types.h>
+#include <vnet/fib/fib_table.h>
+
+#define REPLY_MSG_ID_BASE nm->msg_id_base
+#include <vlibapi/api_helper_macros.h>
+
+/*************/
+/*** NAT66 ***/
+/*************/
+
+static void
+vl_api_nat66_add_del_interface_t_handler (vl_api_nat66_add_del_interface_t *
+ mp)
+{
+ nat66_main_t *nm = &nat66_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->flags & NAT_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_static_mapping_t_handler
+ (vl_api_nat66_add_del_static_mapping_t * mp)
+{
+ nat66_main_t *nm = &nat66_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);
+}
+
+typedef struct nat66_api_walk_ctx_t_
+{
+ vl_api_registration_t *rp;
+ u32 context;
+} nat66_api_walk_ctx_t;
+
+static int
+nat66_api_interface_walk (nat66_interface_t * i, void *arg)
+{
+ vl_api_nat66_interface_details_t *rmp;
+ nat66_main_t *nm = &nat66_main;
+ nat66_api_walk_ctx_t *ctx = arg;
+
+ rmp = vl_msg_api_alloc (sizeof (*rmp));
+ clib_memset (rmp, 0, sizeof (*rmp));
+ rmp->_vl_msg_id = ntohs (VL_API_NAT66_INTERFACE_DETAILS + nm->msg_id_base);
+ rmp->sw_if_index = ntohl (i->sw_if_index);
+ if (nat66_interface_is_inside (i))
+ rmp->flags |= NAT_IS_INSIDE;
+ rmp->context = ctx->context;
+
+ vl_api_send_msg (ctx->rp, (u8 *) rmp);
+
+ return 0;
+}
+
+static void
+vl_api_nat66_interface_dump_t_handler (vl_api_nat66_interface_dump_t * mp)
+{
+ vl_api_registration_t *rp;
+
+ rp = vl_api_client_index_to_registration (mp->client_index);
+ if (rp == 0)
+ return;
+
+ nat66_api_walk_ctx_t ctx = {
+ .rp = rp,
+ .context = mp->context,
+ };
+
+ nat66_interfaces_walk (nat66_api_interface_walk, &ctx);
+}
+
+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;
+ 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));
+ clib_memset (rmp, 0, sizeof (*rmp));
+ rmp->_vl_msg_id =
+ ntohs (VL_API_NAT66_STATIC_MAPPING_DETAILS + nm->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_api_send_msg (ctx->rp, (u8 *) rmp);
+
+ return 0;
+}
+
+static void
+vl_api_nat66_static_mapping_dump_t_handler (vl_api_nat66_static_mapping_dump_t
+ * mp)
+{
+ vl_api_registration_t *rp;
+
+ rp = vl_api_client_index_to_registration (mp->client_index);
+ if (rp == 0)
+ return;
+
+ nat66_api_walk_ctx_t ctx = {
+ .rp = rp,
+ .context = mp->context,
+ };
+
+ nat66_static_mappings_walk (nat66_api_static_mapping_walk, &ctx);
+}
+
+/* API definitions */
+#include <vnet/format_fns.h>
+#include <nat/nat66/nat66.api.c>
+
+/* Set up the API message handling tables */
+clib_error_t *
+nat66_plugin_api_hookup (vlib_main_t * vm)
+{
+ nat66_main_t *nm = &nat66_main;
+
+ nm->msg_id_base = setup_message_id_table ();
+
+ return 0;
+}
diff --git a/src/plugins/nat/nat66/nat66_cli.c b/src/plugins/nat/nat66/nat66_cli.c
new file mode 100644
index 00000000000..da963877c8a
--- /dev/null
+++ b/src/plugins/nat/nat66/nat66_cli.c
@@ -0,0 +1,319 @@
+/*
+ * 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/nat66.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 (nat66_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),
+ nat66_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/nat66_in2out.c b/src/plugins/nat/nat66/nat66_in2out.c
new file mode 100644
index 00000000000..5a027e2cd50
--- /dev/null
+++ b/src/plugins/nat/nat66/nat66_in2out.c
@@ -0,0 +1,262 @@
+/*
+ * 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/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;
+}
+
+#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 u8
+nat66_not_translate (u32 rx_fib_index, ip6_address_t ip6_addr)
+{
+ nat66_main_t *nm = &nat66_main;
+ u32 sw_if_index;
+ nat66_interface_t *i;
+ fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
+ fib_prefix_t pfx = {
+ .fp_proto = FIB_PROTOCOL_IP6,
+ .fp_len = 128,
+ .fp_addr = {
+ .ip6 = ip6_addr,
+ },
+ };
+
+ fei = fib_table_lookup (rx_fib_index, &pfx);
+ if (FIB_NODE_INDEX_INVALID == fei)
+ return 1;
+ sw_if_index = fib_entry_get_resolving_interface (fei);
+
+ if (sw_if_index == ~0)
+ {
+ fei = fib_table_lookup (nm->outside_fib_index, &pfx);
+ if (FIB_NODE_INDEX_INVALID == fei)
+ return 1;
+ sw_if_index = fib_entry_get_resolving_interface (fei);
+ }
+
+ /* *INDENT-OFF* */
+ pool_foreach (i, nm->interfaces,
+ ({
+ /* NAT packet aimed at outside interface */
+ if (nat66_interface_is_outside (i) && sw_if_index == i->sw_if_index)
+ return 0;
+ }));
+ /* *INDENT-ON* */
+
+ return 1;
+}
+
+VLIB_NODE_FN (nat66_in2out_node) (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 = vm->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
+ (vm, b0, 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);
+
+ if (nat66_not_translate (fib_index0, ip60->dst_address))
+ goto trace0;
+
+ 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, nm->in2out_node_index,
+ NAT66_IN2OUT_ERROR_IN2OUT_PACKETS,
+ pkts_processed);
+ return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat66_in2out_node) = {
+ .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* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/nat66/nat66_out2in.c b/src/plugins/nat/nat66/nat66_out2in.c
new file mode 100644
index 00000000000..563ad6f5178
--- /dev/null
+++ b/src/plugins/nat/nat66/nat66_out2in.c
@@ -0,0 +1,220 @@
+/*
+ * 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/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;
+}
+
+#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;
+
+VLIB_NODE_FN (nat66_out2in_node) (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 = vm->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
+ (vm, b0, 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, nm->out2in_node_index,
+ NAT66_OUT2IN_ERROR_OUT2IN_PACKETS,
+ pkts_processed);
+ return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat66_out2in_node) = {
+ .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* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */