summaryrefslogtreecommitdiffstats
path: root/src/plugins/nat/dslite
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/nat/dslite')
-rw-r--r--src/plugins/nat/dslite/dslite.api134
-rw-r--r--src/plugins/nat/dslite/dslite.c265
-rw-r--r--src/plugins/nat/dslite/dslite.h166
-rw-r--r--src/plugins/nat/dslite/dslite_api.c177
-rw-r--r--src/plugins/nat/dslite/dslite_ce_decap.c142
-rw-r--r--src/plugins/nat/dslite/dslite_ce_encap.c134
-rw-r--r--src/plugins/nat/dslite/dslite_cli.c404
-rw-r--r--src/plugins/nat/dslite/dslite_dpo.c130
-rw-r--r--src/plugins/nat/dslite/dslite_dpo.h38
-rw-r--r--src/plugins/nat/dslite/dslite_in2out.c515
-rw-r--r--src/plugins/nat/dslite/dslite_out2in.c294
11 files changed, 2399 insertions, 0 deletions
diff --git a/src/plugins/nat/dslite/dslite.api b/src/plugins/nat/dslite/dslite.api
new file mode 100644
index 00000000000..b5f8c5ba833
--- /dev/null
+++ b/src/plugins/nat/dslite/dslite.api
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2019 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";
+
+/**
+ * @file nat.api
+ * @brief VPP control-plane API messages.
+ *
+ * This file defines VPP control-plane API messages which are generally
+ * called through a shared memory interface.
+ */
+
+
+/*
+ * DS-Lite APIs
+ */
+
+/** \brief Add/delete address range to DS-Lite pool
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param start_addr - start IPv4 address of the range
+ @param end_addr - end IPv4 address of the range
+ @param is_add - true if add, false if delete
+*/
+autoreply define dslite_add_del_pool_addr_range {
+ u32 client_index;
+ u32 context;
+ vl_api_ip4_address_t start_addr;
+ vl_api_ip4_address_t end_addr;
+ bool is_add;
+};
+
+/** \brief Dump DS-Lite addresses
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+*/
+define dslite_address_dump {
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief DS-Lite address details response
+ @param context - sender context, to match reply w/ request
+ @param ip_address - IPv4 address
+*/
+define dslite_address_details {
+ u32 context;
+ vl_api_ip4_address_t ip_address;
+};
+
+/** \brief Set AFTR IPv6 and IPv4 addresses
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param ip4_addr - IPv4 address
+ @param ip6_addr - IPv6 address
+*/
+autoreply define dslite_set_aftr_addr {
+ u32 client_index;
+ u32 context;
+ vl_api_ip4_address_t ip4_addr;
+ vl_api_ip6_address_t ip6_addr;
+};
+
+/** \brief Get AFTR IPv6 and IPv4 addresses
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+*/
+define dslite_get_aftr_addr {
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Response to get AFTR IPv6 and IPv4 addresses
+ @param context - sender context, to match reply w/ request
+ @param retval - return code
+ @param ip4_addr - IPv4 address
+ @param ip6_addr - IPv6 address
+*/
+define dslite_get_aftr_addr_reply {
+ u32 context;
+ i32 retval;
+ vl_api_ip4_address_t ip4_addr;
+ vl_api_ip6_address_t ip6_addr;
+};
+
+/** \brief Set B4 IPv6 and IPv4 addresses
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param ip4_addr - IPv4 address
+ @param ip6_addr - IPv6 address
+*/
+autoreply define dslite_set_b4_addr {
+ u32 client_index;
+ u32 context;
+ vl_api_ip4_address_t ip4_addr;
+ vl_api_ip6_address_t ip6_addr;
+};
+
+/** \brief Get B4 IPv6 and IPv4 addresses
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+*/
+define dslite_get_b4_addr {
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Response to get B4 IPv6 and IPv4 addresses
+ @param context - sender context, to match reply w/ request
+ @param retval - return code
+ @param ip4_addr - IPv4 address
+ @param ip6_addr - IPv6 address
+*/
+define dslite_get_b4_addr_reply {
+ u32 context;
+ i32 retval;
+ vl_api_ip4_address_t ip4_addr;
+ vl_api_ip6_address_t ip6_addr;
+};
diff --git a/src/plugins/nat/dslite/dslite.c b/src/plugins/nat/dslite/dslite.c
new file mode 100644
index 00000000000..e03dd0c8431
--- /dev/null
+++ b/src/plugins/nat/dslite/dslite.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <vnet/plugin/plugin.h>
+#include <nat/dslite/dslite.h>
+#include <nat/dslite/dslite_dpo.h>
+#include <vnet/fib/fib_table.h>
+#include <vpp/app/version.h>
+
+dslite_main_t dslite_main;
+fib_source_t nat_fib_src_hi;
+
+clib_error_t *dslite_api_hookup (vlib_main_t * vm);
+
+void
+add_del_dslite_pool_addr_cb (ip4_address_t addr, u8 is_add, void *opaque);
+
+static clib_error_t *
+dslite_init (vlib_main_t * vm)
+{
+ dslite_main_t *dm = &dslite_main;
+ vlib_thread_registration_t *tr;
+ vlib_thread_main_t *tm = vlib_get_thread_main ();
+ uword *p;
+ vlib_node_t *node;
+ dslite_per_thread_data_t *td;
+ u32 translation_buckets = 1024;
+ u32 translation_memory_size = 128 << 20;
+ u32 b4_buckets = 128;
+ u32 b4_memory_size = 64 << 20;
+
+ node = vlib_get_node_by_name (vm, (u8 *) "dslite-in2out");
+ dm->dslite_in2out_node_index = node->index;
+
+ node = vlib_get_node_by_name (vm, (u8 *) "dslite-in2out-slowpath");
+ dm->dslite_in2out_slowpath_node_index = node->index;
+
+ node = vlib_get_node_by_name (vm, (u8 *) "dslite-out2in");
+ dm->dslite_out2in_node_index = node->index;
+
+ dm->first_worker_index = 0;
+ dm->num_workers = 0;
+
+ // init nat address pool
+ dm->pool.add_del_pool_addr_cb = add_del_dslite_pool_addr_cb;
+ dm->pool.alloc_addr_and_port_cb = nat_alloc_ip4_addr_and_port_cb_default;
+
+ p = hash_get_mem (tm->thread_registrations_by_name, "workers");
+ if (p)
+ {
+ tr = (vlib_thread_registration_t *) p[0];
+ if (tr)
+ {
+ dm->num_workers = tr->count;
+ dm->first_worker_index = tr->first_index;
+ }
+ }
+
+ if (dm->num_workers)
+ dm->port_per_thread = (0xffff - 1024) / dm->num_workers;
+ else
+ dm->port_per_thread = 0xffff - 1024;
+
+ vec_validate (dm->per_thread_data, tm->n_vlib_mains - 1);
+
+ /* *INDENT-OFF* */
+ vec_foreach (td, dm->per_thread_data)
+ {
+ clib_bihash_init_24_8 (&td->in2out, "in2out", translation_buckets,
+ translation_memory_size);
+
+ clib_bihash_init_8_8 (&td->out2in, "out2in", translation_buckets,
+ translation_memory_size);
+
+ clib_bihash_init_16_8 (&td->b4_hash, "b4s", b4_buckets, b4_memory_size);
+ }
+ /* *INDENT-ON* */
+
+ dm->is_ce = 0;
+
+ /* Init counters */
+ dm->total_b4s.name = "total-b4s";
+ dm->total_b4s.stat_segment_name = "/dslite/total-b4s";
+ vlib_validate_simple_counter (&dm->total_b4s, 0);
+ vlib_zero_simple_counter (&dm->total_b4s, 0);
+ dm->total_sessions.name = "total-sessions";
+ dm->total_sessions.stat_segment_name = "/dslite/total-sessions";
+ vlib_validate_simple_counter (&dm->total_sessions, 0);
+ vlib_zero_simple_counter (&dm->total_sessions, 0);
+
+ dslite_dpo_module_init ();
+
+ nat_fib_src_hi = fib_source_allocate ("dslite-hi",
+ FIB_SOURCE_PRIORITY_HI,
+ FIB_SOURCE_BH_SIMPLE);
+
+ return dslite_api_hookup (vm);
+}
+
+void
+dslite_set_ce (dslite_main_t * dm, u8 set)
+{
+ dm->is_ce = (set != 0);
+}
+
+int
+dslite_set_aftr_ip6_addr (dslite_main_t * dm, ip6_address_t * addr)
+{
+ dpo_id_t dpo = DPO_INVALID;
+
+ if (dm->is_ce)
+ {
+ dslite_ce_dpo_create (DPO_PROTO_IP4, 0, &dpo);
+ fib_prefix_t pfx = {
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_len = 0,
+ .fp_addr.ip4.as_u32 = 0,
+ };
+ fib_table_entry_special_dpo_add (0, &pfx, nat_fib_src_hi,
+ FIB_ENTRY_FLAG_EXCLUSIVE, &dpo);
+ }
+ else
+ {
+ dslite_dpo_create (DPO_PROTO_IP6, 0, &dpo);
+ fib_prefix_t pfx = {
+ .fp_proto = FIB_PROTOCOL_IP6,
+ .fp_len = 128,
+ .fp_addr.ip6.as_u64[0] = addr->as_u64[0],
+ .fp_addr.ip6.as_u64[1] = addr->as_u64[1],
+ };
+ fib_table_entry_special_dpo_add (0, &pfx, nat_fib_src_hi,
+ FIB_ENTRY_FLAG_EXCLUSIVE, &dpo);
+ }
+
+ dpo_reset (&dpo);
+
+ dm->aftr_ip6_addr.as_u64[0] = addr->as_u64[0];
+ dm->aftr_ip6_addr.as_u64[1] = addr->as_u64[1];
+ return 0;
+}
+
+int
+dslite_set_aftr_ip4_addr (dslite_main_t * dm, ip4_address_t * addr)
+{
+ dm->aftr_ip4_addr.as_u32 = addr->as_u32;
+ return 0;
+}
+
+int
+dslite_set_b4_ip6_addr (dslite_main_t * dm, ip6_address_t * addr)
+{
+ if (dm->is_ce)
+ {
+ dpo_id_t dpo = DPO_INVALID;
+
+ dslite_ce_dpo_create (DPO_PROTO_IP6, 0, &dpo);
+ fib_prefix_t pfx = {
+ .fp_proto = FIB_PROTOCOL_IP6,
+ .fp_len = 128,
+ .fp_addr.ip6.as_u64[0] = addr->as_u64[0],
+ .fp_addr.ip6.as_u64[1] = addr->as_u64[1],
+ };
+ fib_table_entry_special_dpo_add (0, &pfx, nat_fib_src_hi,
+ FIB_ENTRY_FLAG_EXCLUSIVE, &dpo);
+
+ dpo_reset (&dpo);
+
+ dm->b4_ip6_addr.as_u64[0] = addr->as_u64[0];
+ dm->b4_ip6_addr.as_u64[1] = addr->as_u64[1];
+ }
+ else
+ {
+ return VNET_API_ERROR_FEATURE_DISABLED;
+ }
+
+ return 0;
+}
+
+int
+dslite_set_b4_ip4_addr (dslite_main_t * dm, ip4_address_t * addr)
+{
+ if (dm->is_ce)
+ {
+ dm->b4_ip4_addr.as_u32 = addr->as_u32;
+ }
+ else
+ {
+ return VNET_API_ERROR_FEATURE_DISABLED;
+ }
+
+ return 0;
+}
+
+void
+add_del_dslite_pool_addr_cb (ip4_address_t addr, u8 is_add, void *opaque)
+{
+ dpo_id_t dpo_v4 = DPO_INVALID;
+ fib_prefix_t pfx = {
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_len = 32,
+ .fp_addr.ip4.as_u32 = addr.as_u32,
+ };
+
+ if (is_add)
+ {
+ dslite_dpo_create (DPO_PROTO_IP4, 0, &dpo_v4);
+ fib_table_entry_special_dpo_add (0, &pfx, nat_fib_src_hi,
+ FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v4);
+ dpo_reset (&dpo_v4);
+ }
+ else
+ {
+ fib_table_entry_special_remove (0, &pfx, nat_fib_src_hi);
+ }
+}
+
+u8 *
+format_dslite_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 *);
+ dslite_trace_t *t = va_arg (*args, dslite_trace_t *);
+
+ s =
+ format (s, "next index %d, session %d", t->next_index, t->session_index);
+
+ return s;
+}
+
+u8 *
+format_dslite_ce_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 *);
+ dslite_ce_trace_t *t = va_arg (*args, dslite_ce_trace_t *);
+
+ s = format (s, "next index %d", t->next_index);
+
+ return s;
+}
+
+VLIB_INIT_FUNCTION (dslite_init);
+
+VLIB_PLUGIN_REGISTER () =
+{
+.version = VPP_BUILD_VER,.description = "Dual-Stack Lite",};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/dslite/dslite.h b/src/plugins/nat/dslite/dslite.h
new file mode 100644
index 00000000000..d512cf47c9b
--- /dev/null
+++ b/src/plugins/nat/dslite/dslite.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __included_dslite_h__
+#define __included_dslite_h__
+
+#include <vppinfra/bihash_8_8.h>
+#include <vppinfra/bihash_16_8.h>
+#include <vppinfra/bihash_24_8.h>
+#include <nat/lib/alloc.h>
+#include <nat/nat.h>
+#include <nat/nat_inlines.h>
+
+typedef struct
+{
+ union
+ {
+ struct
+ {
+ ip6_address_t softwire_id;
+ ip4_address_t addr;
+ u16 port;
+ u8 proto;
+ u8 pad;
+ };
+ u64 as_u64[3];
+ };
+} dslite_session_key_t;
+
+/* *INDENT-OFF* */
+typedef CLIB_PACKED (struct
+{
+ snat_session_key_t out2in;
+ dslite_session_key_t in2out;
+ u32 per_b4_index;
+ u32 per_b4_list_head_index;
+ f64 last_heard;
+ u64 total_bytes;
+ u32 total_pkts;
+}) dslite_session_t;
+/* *INDENT-ON* */
+
+typedef struct
+{
+ ip6_address_t addr;
+ u32 sessions_per_b4_list_head_index;
+ u32 nsessions;
+} dslite_b4_t;
+
+typedef struct
+{
+ /* Main lookup tables */
+ clib_bihash_8_8_t out2in;
+ clib_bihash_24_8_t in2out;
+
+ /* Find a B4 */
+ clib_bihash_16_8_t b4_hash;
+
+ /* B4 pool */
+ dslite_b4_t *b4s;
+
+ /* Session pool */
+ dslite_session_t *sessions;
+
+ /* Pool of doubly-linked list elements */
+ dlist_elt_t *list_pool;
+} dslite_per_thread_data_t;
+
+typedef struct
+{
+ ip6_address_t aftr_ip6_addr;
+ ip4_address_t aftr_ip4_addr;
+ ip6_address_t b4_ip6_addr;
+ ip4_address_t b4_ip4_addr;
+ dslite_per_thread_data_t *per_thread_data;
+ u32 num_workers;
+ u32 first_worker_index;
+ u16 port_per_thread;
+
+ /* nat address pool */
+ nat_ip4_pool_t pool;
+
+ /* counters/gauges */
+ vlib_simple_counter_main_t total_b4s;
+ vlib_simple_counter_main_t total_sessions;
+
+ /* node index */
+ u32 dslite_in2out_node_index;
+ u32 dslite_in2out_slowpath_node_index;
+ u32 dslite_out2in_node_index;
+
+ /* If set then the DSLite component behaves as CPE/B4
+ * otherwise it behaves as AFTR */
+ u8 is_ce;
+
+ u16 msg_id_base;
+} dslite_main_t;
+
+typedef struct
+{
+ u32 next_index;
+ u32 session_index;
+} dslite_trace_t;
+
+typedef struct
+{
+ u32 next_index;
+} dslite_ce_trace_t;
+
+#define foreach_dslite_error \
+_(IN2OUT, "valid in2out DS-Lite packets") \
+_(OUT2IN, "valid out2in DS-Lite packets") \
+_(CE_ENCAP, "valid CE encap DS-Lite packets") \
+_(CE_DECAP, "valid CE decap DS-Lite packets") \
+_(NO_TRANSLATION, "no translation") \
+_(BAD_IP6_PROTOCOL, "bad ip6 protocol") \
+_(OUT_OF_PORTS, "out of ports") \
+_(UNSUPPORTED_PROTOCOL, "unsupported protocol") \
+_(BAD_ICMP_TYPE, "unsupported icmp type") \
+_(UNKNOWN, "unknown")
+
+typedef enum
+{
+#define _(sym,str) DSLITE_ERROR_##sym,
+ foreach_dslite_error
+#undef _
+ DSLITE_N_ERROR,
+} dslite_error_t;
+
+extern dslite_main_t dslite_main;
+extern vlib_node_registration_t dslite_in2out_node;
+extern vlib_node_registration_t dslite_in2out_slowpath_node;
+extern vlib_node_registration_t dslite_out2in_node;
+extern vlib_node_registration_t dslite_ce_encap_node;
+extern vlib_node_registration_t dslite_ce_decap_node;
+
+void dslite_set_ce (dslite_main_t * dm, u8 set);
+int dslite_set_aftr_ip6_addr (dslite_main_t * dm, ip6_address_t * addr);
+int dslite_set_b4_ip6_addr (dslite_main_t * dm, ip6_address_t * addr);
+int dslite_set_aftr_ip4_addr (dslite_main_t * dm, ip4_address_t * addr);
+int dslite_set_b4_ip4_addr (dslite_main_t * dm, ip4_address_t * addr);
+int dslite_add_del_pool_addr (dslite_main_t * dm, ip4_address_t * addr,
+ u8 is_add);
+u8 *format_dslite_trace (u8 * s, va_list * args);
+u8 *format_dslite_ce_trace (u8 * s, va_list * args);
+
+#endif /* __included_dslite_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/dslite/dslite_api.c b/src/plugins/nat/dslite/dslite_api.c
new file mode 100644
index 00000000000..420e8212ad9
--- /dev/null
+++ b/src/plugins/nat/dslite/dslite_api.c
@@ -0,0 +1,177 @@
+/*
+ *------------------------------------------------------------------
+ * dslite_api.c - DS-Lite API
+ *
+ * Copyright (c) 2019 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 <vnet/ip/ip_types_api.h>
+#include <nat/dslite/dslite.h>
+#include <nat/dslite/dslite.api_enum.h>
+#include <nat/dslite/dslite.api_types.h>
+#include <vnet/ip/ip.h>
+#include <vnet/fib/fib_table.h>
+#include <vlibmemory/api.h>
+
+#define REPLY_MSG_ID_BASE dm->msg_id_base
+#include <vlibapi/api_helper_macros.h>
+
+static void
+vl_api_dslite_set_aftr_addr_t_handler (vl_api_dslite_set_aftr_addr_t * mp)
+{
+ vl_api_dslite_set_aftr_addr_reply_t *rmp;
+ dslite_main_t *dm = &dslite_main;
+ int rv = 0;
+ ip6_address_t ip6_addr;
+ ip4_address_t ip4_addr;
+
+ memcpy (&ip6_addr.as_u8, mp->ip6_addr, 16);
+ memcpy (&ip4_addr.as_u8, mp->ip4_addr, 4);
+
+ rv = dslite_set_aftr_ip6_addr (dm, &ip6_addr);
+ if (rv == 0)
+ rv = dslite_set_aftr_ip4_addr (dm, &ip4_addr);
+
+ REPLY_MACRO (VL_API_DSLITE_SET_AFTR_ADDR_REPLY);
+}
+
+static void
+vl_api_dslite_get_aftr_addr_t_handler (vl_api_dslite_get_aftr_addr_t * mp)
+{
+ vl_api_dslite_get_aftr_addr_reply_t *rmp;
+ dslite_main_t *dm = &dslite_main;
+ int rv = 0;
+
+ /* *INDENT-OFF* */
+ REPLY_MACRO2 (VL_API_DSLITE_GET_AFTR_ADDR_REPLY,
+ ({
+ memcpy (rmp->ip4_addr, &dm->aftr_ip4_addr.as_u8, 4);
+ memcpy (rmp->ip6_addr, &dm->aftr_ip6_addr.as_u8, 16);
+ }))
+ /* *INDENT-ON* */
+}
+
+static void
+vl_api_dslite_set_b4_addr_t_handler (vl_api_dslite_set_b4_addr_t * mp)
+{
+ vl_api_dslite_set_b4_addr_reply_t *rmp;
+ dslite_main_t *dm = &dslite_main;
+ int rv = 0;
+ ip6_address_t ip6_addr;
+ ip4_address_t ip4_addr;
+
+ memcpy (&ip6_addr.as_u8, mp->ip6_addr, 16);
+ memcpy (&ip4_addr.as_u8, mp->ip4_addr, 4);
+
+ rv = dslite_set_b4_ip6_addr (dm, &ip6_addr);
+ if (rv == 0)
+ rv = dslite_set_b4_ip4_addr (dm, &ip4_addr);
+
+ REPLY_MACRO (VL_API_DSLITE_SET_B4_ADDR_REPLY);
+}
+
+static void
+vl_api_dslite_get_b4_addr_t_handler (vl_api_dslite_get_b4_addr_t * mp)
+{
+ vl_api_dslite_get_b4_addr_reply_t *rmp;
+ dslite_main_t *dm = &dslite_main;
+ int rv = 0;
+
+ /* *INDENT-OFF* */
+ REPLY_MACRO2 (VL_API_DSLITE_GET_B4_ADDR_REPLY,
+ ({
+ memcpy (rmp->ip4_addr, &dm->b4_ip4_addr.as_u8, 4);
+ memcpy (rmp->ip6_addr, &dm->b4_ip6_addr.as_u8, 16);
+ }))
+ /* *INDENT-ON* */
+}
+
+static void
+ vl_api_dslite_add_del_pool_addr_range_t_handler
+ (vl_api_dslite_add_del_pool_addr_range_t * mp)
+{
+ vl_api_dslite_add_del_pool_addr_range_reply_t *rmp;
+ dslite_main_t *dm = &dslite_main;
+ int rv = 0;
+ ip4_address_t this_addr;
+ u32 start_host_order, end_host_order;
+ int count;
+ u32 *tmp;
+
+ tmp = (u32 *) mp->start_addr;
+ start_host_order = clib_host_to_net_u32 (tmp[0]);
+ tmp = (u32 *) mp->end_addr;
+ end_host_order = clib_host_to_net_u32 (tmp[0]);
+
+ // TODO:
+ // end_host_order < start_host_order
+
+ count = (end_host_order - start_host_order) + 1;
+ memcpy (&this_addr.as_u8, mp->start_addr, 4);
+
+ rv = nat_add_del_ip4_pool_addrs (&dm->pool, this_addr, count, mp->is_add, 0);
+
+ REPLY_MACRO (VL_API_DSLITE_ADD_DEL_POOL_ADDR_RANGE_REPLY);
+}
+
+static void
+send_dslite_address_details (nat_ip4_pool_addr_t * a,
+ vl_api_registration_t * reg, u32 context)
+{
+ dslite_main_t *dm = &dslite_main;
+ vl_api_dslite_address_details_t *rmp;
+
+ rmp = vl_msg_api_alloc (sizeof (*rmp));
+
+ clib_memset (rmp, 0, sizeof (*rmp));
+
+ rmp->_vl_msg_id = ntohs (VL_API_DSLITE_ADDRESS_DETAILS + dm->msg_id_base);
+ clib_memcpy (rmp->ip_address, &(a->addr), 4);
+ rmp->context = context;
+
+ vl_api_send_msg (reg, (u8 *) rmp);
+}
+
+static void
+vl_api_dslite_address_dump_t_handler (vl_api_dslite_address_dump_t * mp)
+{
+ vl_api_registration_t *reg;
+ dslite_main_t *dm = &dslite_main;
+ nat_ip4_pool_addr_t *a;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ /* *INDENT-OFF* */
+ vec_foreach (a, dm->pool.pool_addr)
+ {
+ send_dslite_address_details (a, reg, mp->context);
+ }
+ /* *INDENT-ON* */
+}
+
+/* API definitions */
+#include <vnet/format_fns.h>
+#include <nat/dslite/dslite.api.c>
+
+/* Set up the API message handling tables */
+clib_error_t *
+dslite_api_hookup (vlib_main_t * vm)
+{
+ dslite_main_t *dm = &dslite_main;
+
+ dm->msg_id_base = setup_message_id_table ();
+ return 0;
+}
diff --git a/src/plugins/nat/dslite/dslite_ce_decap.c b/src/plugins/nat/dslite/dslite_ce_decap.c
new file mode 100644
index 00000000000..3d6ca992e45
--- /dev/null
+++ b/src/plugins/nat/dslite/dslite_ce_decap.c
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+#include <nat/dslite/dslite.h>
+#include <nat/nat_inlines.h>
+
+typedef enum
+{
+ DSLITE_CE_DECAP_NEXT_IP4_LOOKUP,
+ DSLITE_IN2OUT_NEXT_IP6_ICMP,
+ DSLITE_CE_DECAP_NEXT_DROP,
+ DSLITE_CE_DECAP_N_NEXT,
+} dslite_ce_decap_next_t;
+
+static char *dslite_ce_decap_error_strings[] = {
+#define _(sym,string) string,
+ foreach_dslite_error
+#undef _
+};
+
+VLIB_NODE_FN (dslite_ce_decap_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ u32 n_left_from, *from, *to_next;
+ dslite_ce_decap_next_t next_index;
+
+ 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 = DSLITE_CE_DECAP_NEXT_IP4_LOOKUP;
+ u8 error0 = DSLITE_ERROR_CE_DECAP;
+ ip4_header_t *ip40;
+ ip6_header_t *ip60;
+ u32 proto0;
+
+ /* 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 (ip60->protocol != IP_PROTOCOL_IP_IN_IP))
+ {
+ if (ip60->protocol == IP_PROTOCOL_ICMP6)
+ {
+ next0 = DSLITE_IN2OUT_NEXT_IP6_ICMP;
+ goto trace0;
+ }
+ error0 = DSLITE_ERROR_BAD_IP6_PROTOCOL;
+ next0 = DSLITE_CE_DECAP_NEXT_DROP;
+ goto trace0;
+ }
+
+ ip40 = vlib_buffer_get_current (b0) + sizeof (ip6_header_t);
+ proto0 = ip_proto_to_snat_proto (ip40->protocol);
+
+ if (PREDICT_FALSE (proto0 == ~0))
+ {
+ error0 = DSLITE_ERROR_UNSUPPORTED_PROTOCOL;
+ next0 = DSLITE_CE_DECAP_NEXT_DROP;
+ goto trace0;
+ }
+
+ ip40->tos =
+ (clib_net_to_host_u32
+ (ip60->ip_version_traffic_class_and_flow_label) & 0x0ff00000) >>
+ 20;
+ vlib_buffer_advance (b0, sizeof (ip6_header_t));
+
+ trace0:
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ dslite_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->next_index = next0;
+ }
+
+ b0->error = node->errors[error0];
+
+ /* 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);
+ }
+
+ return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (dslite_ce_decap_node) = {
+ .name = "dslite-ce-decap",
+ .vector_size = sizeof (u32),
+ .format_trace = format_dslite_ce_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = ARRAY_LEN (dslite_ce_decap_error_strings),
+ .error_strings = dslite_ce_decap_error_strings,
+ .n_next_nodes = DSLITE_CE_DECAP_N_NEXT,
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [DSLITE_CE_DECAP_NEXT_DROP] = "error-drop",
+ [DSLITE_CE_DECAP_NEXT_IP4_LOOKUP] = "ip4-lookup",
+ [DSLITE_IN2OUT_NEXT_IP6_ICMP] = "ip6-icmp-input",
+ },
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/dslite/dslite_ce_encap.c b/src/plugins/nat/dslite/dslite_ce_encap.c
new file mode 100644
index 00000000000..8cec5439243
--- /dev/null
+++ b/src/plugins/nat/dslite/dslite_ce_encap.c
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+#include <nat/dslite/dslite.h>
+#include <nat/nat_inlines.h>
+
+typedef enum
+{
+ DSLITE_CE_ENCAP_NEXT_IP6_LOOKUP,
+ DSLITE_CE_ENCAP_NEXT_DROP,
+ DSLITE_CE_ENCAP_N_NEXT,
+} dslite_ce_encap_next_t;
+
+static char *dslite_ce_encap_error_strings[] = {
+#define _(sym,string) string,
+ foreach_dslite_error
+#undef _
+};
+
+VLIB_NODE_FN (dslite_ce_encap_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ u32 n_left_from, *from, *to_next;
+ dslite_ce_encap_next_t next_index;
+ dslite_main_t *dm = &dslite_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 = DSLITE_CE_ENCAP_NEXT_IP6_LOOKUP;
+ u8 error0 = DSLITE_ERROR_CE_ENCAP;
+ ip4_header_t *ip40;
+ ip6_header_t *ip60;
+ u32 proto0;
+
+ /* speculatively enqueue b0 to the current next frame */
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ ip40 = vlib_buffer_get_current (b0);
+ proto0 = ip_proto_to_snat_proto (ip40->protocol);
+
+ if (PREDICT_FALSE (proto0 == ~0))
+ {
+ error0 = DSLITE_ERROR_UNSUPPORTED_PROTOCOL;
+ next0 = DSLITE_CE_ENCAP_NEXT_DROP;
+ goto trace0;
+ }
+
+ /* Construct IPv6 header */
+ vlib_buffer_advance (b0, -(sizeof (ip6_header_t)));
+ ip60 = vlib_buffer_get_current (b0);
+ ip60->ip_version_traffic_class_and_flow_label =
+ clib_host_to_net_u32 ((6 << 28) + (ip40->tos << 20));
+ ip60->payload_length = ip40->length;
+ ip60->protocol = IP_PROTOCOL_IP_IN_IP;
+ ip60->hop_limit = ip40->ttl;
+ ip60->dst_address.as_u64[0] = dm->aftr_ip6_addr.as_u64[0];
+ ip60->dst_address.as_u64[1] = dm->aftr_ip6_addr.as_u64[1];
+ ip60->src_address.as_u64[0] = dm->b4_ip6_addr.as_u64[0];
+ ip60->src_address.as_u64[1] = dm->b4_ip6_addr.as_u64[1];
+
+ trace0:
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ dslite_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->next_index = next0;
+ }
+
+ b0->error = node->errors[error0];
+
+ /* 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);
+ }
+
+ return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (dslite_ce_encap_node) = {
+ .name = "dslite-ce-encap",
+ .vector_size = sizeof (u32),
+ .format_trace = format_dslite_ce_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = ARRAY_LEN (dslite_ce_encap_error_strings),
+ .error_strings = dslite_ce_encap_error_strings,
+ .n_next_nodes = DSLITE_CE_ENCAP_N_NEXT,
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [DSLITE_CE_ENCAP_NEXT_DROP] = "error-drop",
+ [DSLITE_CE_ENCAP_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/dslite/dslite_cli.c b/src/plugins/nat/dslite/dslite_cli.c
new file mode 100644
index 00000000000..d5c0ca6498b
--- /dev/null
+++ b/src/plugins/nat/dslite/dslite_cli.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <nat/dslite/dslite.h>
+
+static clib_error_t *
+dslite_add_del_pool_addr_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ dslite_main_t *dm = &dslite_main;
+ unformat_input_t _line_input, *line_input = &_line_input;
+ ip4_address_t start_addr, end_addr, this_addr;
+ u32 start_host_order, end_host_order;
+ int count, rv;
+ u8 is_add = 1;
+ clib_error_t *error = 0;
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "%U - %U",
+ unformat_ip4_address, &start_addr,
+ unformat_ip4_address, &end_addr))
+ ;
+ else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr))
+ end_addr = start_addr;
+ else if (unformat (line_input, "del"))
+ is_add = 0;
+ else
+ {
+ error = clib_error_return (0, "unknown input '%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+ }
+
+ start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
+ end_host_order = clib_host_to_net_u32 (end_addr.as_u32);
+
+ if (end_host_order < start_host_order)
+ {
+ error = clib_error_return (0, "end address less than start address");
+ goto done;
+ }
+
+ count = (end_host_order - start_host_order) + 1;
+ this_addr = start_addr;
+
+ rv = nat_add_del_ip4_pool_addrs (&dm->pool, this_addr, count, is_add, 0);
+
+ switch (rv)
+ {
+ case VNET_API_ERROR_NO_SUCH_ENTRY:
+ error =
+ clib_error_return (0, "DS-Lite pool address %U not exist.",
+ format_ip4_address, &this_addr);
+ break;
+ case VNET_API_ERROR_VALUE_EXIST:
+ error =
+ clib_error_return (0, "DS-Lite pool address %U exist.",
+ format_ip4_address, &this_addr);
+ break;
+ }
+
+done:
+ unformat_free (line_input);
+
+ return error;
+}
+
+static clib_error_t *
+dslite_show_pool_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ dslite_main_t *dm = &dslite_main;
+ nat_ip4_pool_addr_t *a;
+
+ vlib_cli_output (vm, "DS-Lite pool:");
+
+ /* *INDENT-OFF* */
+ vec_foreach (a, dm->pool.pool_addr)
+ {
+ vlib_cli_output (vm, "%U", format_ip4_address, &a->addr);
+ }
+ /* *INDENT-ON* */
+ return 0;
+}
+
+static clib_error_t *
+dslite_set_aftr_tunnel_addr_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ dslite_main_t *dm = &dslite_main;
+ unformat_input_t _line_input, *line_input = &_line_input;
+ ip6_address_t ip6_addr;
+ int rv;
+ clib_error_t *error = 0;
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "%U", unformat_ip6_address, &ip6_addr))
+ ;
+ else
+ {
+ error = clib_error_return (0, "unknown input '%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+ }
+
+ rv = dslite_set_aftr_ip6_addr (dm, &ip6_addr);
+
+ if (rv)
+ error =
+ clib_error_return (0,
+ "Set DS-Lite AFTR tunnel endpoint address failed.");
+
+done:
+ unformat_free (line_input);
+
+ return error;
+}
+
+static clib_error_t *
+dslite_show_aftr_ip6_addr_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ dslite_main_t *dm = &dslite_main;
+
+ vlib_cli_output (vm, "%U", format_ip6_address, &dm->aftr_ip6_addr);
+ return 0;
+}
+
+static clib_error_t *
+dslite_set_b4_tunnel_addr_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ dslite_main_t *dm = &dslite_main;
+ unformat_input_t _line_input, *line_input = &_line_input;
+ ip6_address_t ip6_addr;
+ int rv;
+ clib_error_t *error = 0;
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "%U", unformat_ip6_address, &ip6_addr))
+ ;
+ else
+ {
+ error = clib_error_return (0, "unknown input '%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+ }
+
+ rv = dslite_set_b4_ip6_addr (dm, &ip6_addr);
+
+ if (rv)
+ error =
+ clib_error_return (0, "Set DS-Lite B4 tunnel endpoint address failed.");
+
+done:
+ unformat_free (line_input);
+
+ return error;
+}
+
+static clib_error_t *
+dslite_show_b4_ip6_addr_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ dslite_main_t *dm = &dslite_main;
+
+ vlib_cli_output (vm, "%U", format_ip6_address, &dm->b4_ip6_addr);
+ return 0;
+}
+
+static u8 *
+format_dslite_session (u8 * s, va_list * args)
+{
+ dslite_session_t *session = va_arg (*args, dslite_session_t *);
+ u32 indent = format_get_indent (s);
+
+ s = format (s, "%Uin %U:%u out %U:%u protocol %U\n",
+ format_white_space, indent + 2,
+ format_ip4_address, &session->in2out.addr,
+ clib_net_to_host_u16 (session->in2out.port),
+ format_ip4_address, &session->out2in.addr,
+ clib_net_to_host_u16 (session->out2in.port),
+ format_snat_protocol, session->in2out.proto);
+ s = format (s, "%Utotal pkts %d, total bytes %lld\n",
+ format_white_space, indent + 4,
+ session->total_pkts, session->total_bytes);
+ return s;
+}
+
+static u8 *
+format_dslite_b4 (u8 * s, va_list * args)
+{
+ dslite_per_thread_data_t *td = va_arg (*args, dslite_per_thread_data_t *);
+ dslite_b4_t *b4 = va_arg (*args, dslite_b4_t *);
+ dlist_elt_t *head, *elt;
+ u32 elt_index, head_index;
+ u32 session_index;
+ dslite_session_t *session;
+
+ s =
+ format (s, "B4 %U %d sessions\n", format_ip6_address, &b4->addr,
+ b4->nsessions);
+
+ if (b4->nsessions == 0)
+ return s;
+
+ head_index = b4->sessions_per_b4_list_head_index;
+ head = pool_elt_at_index (td->list_pool, head_index);
+ elt_index = head->next;
+ elt = pool_elt_at_index (td->list_pool, elt_index);
+ session_index = elt->value;
+ while (session_index != ~0)
+ {
+ session = pool_elt_at_index (td->sessions, session_index);
+ s = format (s, "%U", format_dslite_session, session);
+ elt_index = elt->next;
+ elt = pool_elt_at_index (td->list_pool, elt_index);
+ session_index = elt->value;
+ }
+
+ return s;
+}
+
+static clib_error_t *
+dslite_show_sessions_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ dslite_main_t *dm = &dslite_main;
+ dslite_per_thread_data_t *td;
+ dslite_b4_t *b4;
+
+ /* *INDENT-OFF* */
+ vec_foreach (td, dm->per_thread_data)
+ {
+ pool_foreach (b4, td->b4s,
+ ({
+ vlib_cli_output (vm, "%U", format_dslite_b4, td, b4);
+ }));
+ }
+ /* *INDENT-ON* */
+
+ return 0;
+}
+
+/* *INDENT-OFF* */
+
+/*?
+ * @cliexpar
+ * @cliexstart{dslite add pool address}
+ * Add/delete DS-Lite pool address for AFTR element.
+ * To add DS-Lite pool address use:
+ * vpp# dslite add pool address 10.1.1.3
+ * To add DS-Lite pool address range use:
+ * vpp# dslite add pool address 10.1.1.5 - 10.1.1.7
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (dslite_add_pool_address_command, static) = {
+ .path = "dslite add pool address",
+ .short_help = "dslite add pool address <ip4-range-start> [- <ip4-range-end>] "
+ " [del]",
+ .function = dslite_add_del_pool_addr_command_fn,
+};
+
+/*?
+ * @cliexpar
+ * @cliexstart{show dslite pool}
+ * Show DS-lite pool addresses.
+ * vpp# show dslite pool
+ * DS-Lite pool:
+ * 10.0.0.3
+ * 10.0.0.5
+ * 10.0.0.6
+ * 10.0.0.7
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (show_dslite_pool_command, static) = {
+ .path = "show dslite pool",
+ .short_help = "show dslite pool",
+ .function = dslite_show_pool_command_fn,
+};
+
+/*?
+ * @cliexpar
+ * @cliexstart{dslite set aftr-tunnel-endpoint-address}
+ * Set IPv6 tunnel endpoint address of the AFTR element.
+ * To set AFTR tunnel endpoint address use:
+ * vpp# dslite set aftr-tunnel-endpoint-address 2001:db8:85a3::8a2e:370:1
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (dslite_set_aftr_tunnel_addr, static) = {
+ .path = "dslite set aftr-tunnel-endpoint-address",
+ .short_help = "dslite set aftr-tunnel-endpoint-address <ip6>",
+ .function = dslite_set_aftr_tunnel_addr_command_fn,
+};
+
+/*?
+ * @cliexpar
+ * @cliexstart{show dslite aftr-tunnel-endpoint-address}
+ * Show IPv6 tunnel endpoint address of the AFTR element.
+ * vpp# show dslite aftr-tunnel-endpoint-address
+ * 2001:db8:85a3::8a2e:370:1
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (dslite_show_aftr_ip6_addr, static) = {
+ .path = "show dslite aftr-tunnel-endpoint-address",
+ .short_help = "show dslite aftr-tunnel-endpoint-address",
+ .function = dslite_show_aftr_ip6_addr_command_fn,
+};
+
+/*?
+ * @cliexpar
+ * @cliexstart{dslite set b4-tunnel-endpoint-address}
+ * Set IPv6 tunnel endpoint address of the B4 element.
+ * To set B4 tunnel endpoint address use:
+ * vpp# dslite set b4-tunnel-endpoint-address 2001:db8:62aa::375e:f4c1:1
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (dslite_set_b4_tunnel_addr, static) = {
+ .path = "dslite set b4-tunnel-endpoint-address",
+ .short_help = "dslite set b4-tunnel-endpoint-address <ip6>",
+ .function = dslite_set_b4_tunnel_addr_command_fn,
+};
+
+/*?
+ * @cliexpar
+ * @cliexstart{show dslite b4-tunnel-endpoint-address}
+ * Show IPv6 tunnel endpoint address of the B4 element.
+ * vpp# show dslite b4-tunnel-endpoint-address
+ * 2001:db8:62aa::375e:f4c1:1
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (dslite_show_b4_ip6_addr, static) = {
+ .path = "show dslite b4-tunnel-endpoint-address",
+ .short_help = "show dslite b4-tunnel-endpoint-address",
+ .function = dslite_show_b4_ip6_addr_command_fn,
+};
+
+/*?
+ * @cliexpar
+ * @cliexstart{show dslite sessions}
+ * Show DS-Lite sessions.
+ * vpp# show dslite sessions
+ * B4 fd01:2::2 1 sessions
+ * in 192.168.1.1:20000 out 10.0.0.3:16253 protocol udp
+ * total pkts 2, total bytes 136
+ * B4 fd01:2::3 2 sessions
+ * in 192.168.1.1:20001 out 10.0.0.3:18995 protocol tcp
+ * total pkts 2, total bytes 160
+ * in 192.168.1.1:4000 out 10.0.0.3:53893 protocol icmp
+ * total pkts 2, total bytes 136
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (dslite_show_sessions, static) = {
+ .path = "show dslite sessions",
+ .short_help = "show dslite sessions",
+ .function = dslite_show_sessions_command_fn,
+};
+
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/dslite/dslite_dpo.c b/src/plugins/nat/dslite/dslite_dpo.c
new file mode 100644
index 00000000000..009b5536289
--- /dev/null
+++ b/src/plugins/nat/dslite/dslite_dpo.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/ip/ip.h>
+#include <nat/dslite/dslite_dpo.h>
+
+dpo_type_t dslite_dpo_type;
+dpo_type_t dslite_ce_dpo_type;
+
+void
+dslite_dpo_create (dpo_proto_t dproto, u32 aftr_index, dpo_id_t * dpo)
+{
+ dpo_set (dpo, dslite_dpo_type, dproto, aftr_index);
+}
+
+void
+dslite_ce_dpo_create (dpo_proto_t dproto, u32 b4_index, dpo_id_t * dpo)
+{
+ dpo_set (dpo, dslite_ce_dpo_type, dproto, b4_index);
+}
+
+u8 *
+format_dslite_dpo (u8 * s, va_list * args)
+{
+ index_t index = va_arg (*args, index_t);
+ CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
+
+ return (format (s, "DS-Lite: AFTR:%d", index));
+}
+
+u8 *
+format_dslite_ce_dpo (u8 * s, va_list * args)
+{
+ index_t index = va_arg (*args, index_t);
+ CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
+
+ return (format (s, "DS-Lite: B4:%d", index));
+}
+
+static void
+dslite_dpo_lock (dpo_id_t * dpo)
+{
+}
+
+static void
+dslite_dpo_unlock (dpo_id_t * dpo)
+{
+}
+
+static void
+dslite_ce_dpo_lock (dpo_id_t * dpo)
+{
+}
+
+static void
+dslite_ce_dpo_unlock (dpo_id_t * dpo)
+{
+}
+
+const static dpo_vft_t dslite_dpo_vft = {
+ .dv_lock = dslite_dpo_lock,
+ .dv_unlock = dslite_dpo_unlock,
+ .dv_format = format_dslite_dpo,
+};
+
+const static dpo_vft_t dslite_ce_dpo_vft = {
+ .dv_lock = dslite_ce_dpo_lock,
+ .dv_unlock = dslite_ce_dpo_unlock,
+ .dv_format = format_dslite_ce_dpo,
+};
+
+const static char *const dslite_ip4_nodes[] = {
+ "dslite-out2in",
+ NULL,
+};
+
+const static char *const dslite_ip6_nodes[] = {
+ "dslite-in2out",
+ NULL,
+};
+
+const static char *const dslite_ce_ip4_nodes[] = {
+ "dslite-ce-encap",
+ NULL,
+};
+
+const static char *const dslite_ce_ip6_nodes[] = {
+ "dslite-ce-decap",
+ NULL,
+};
+
+const static char *const *const dslite_nodes[DPO_PROTO_NUM] = {
+ [DPO_PROTO_IP4] = dslite_ip4_nodes,
+ [DPO_PROTO_IP6] = dslite_ip6_nodes,
+ [DPO_PROTO_MPLS] = NULL,
+};
+
+const static char *const *const dslite_ce_nodes[DPO_PROTO_NUM] = {
+ [DPO_PROTO_IP4] = dslite_ce_ip4_nodes,
+ [DPO_PROTO_IP6] = dslite_ce_ip6_nodes,
+ [DPO_PROTO_MPLS] = NULL,
+};
+
+void
+dslite_dpo_module_init (void)
+{
+ dslite_dpo_type = dpo_register_new_type (&dslite_dpo_vft, dslite_nodes);
+ dslite_ce_dpo_type = dpo_register_new_type (&dslite_ce_dpo_vft,
+ dslite_ce_nodes);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/dslite/dslite_dpo.h b/src/plugins/nat/dslite/dslite_dpo.h
new file mode 100644
index 00000000000..53d37b27fb4
--- /dev/null
+++ b/src/plugins/nat/dslite/dslite_dpo.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __included_dslite_dpo_h__
+#define __included_dslite_dpo_h__
+
+#include <vnet/vnet.h>
+#include <vnet/dpo/dpo.h>
+
+void dslite_dpo_create (dpo_proto_t dproto, u32 aftr_index, dpo_id_t * dpo);
+void dslite_ce_dpo_create (dpo_proto_t dproto, u32 b4_index, dpo_id_t * dpo);
+
+u8 *format_dslite_dpo (u8 * s, va_list * args);
+u8 *format_dslite_ce_dpo (u8 * s, va_list * args);
+
+void dslite_dpo_module_init (void);
+
+#endif /* __included_dslite_dpo_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/dslite/dslite_in2out.c b/src/plugins/nat/dslite/dslite_in2out.c
new file mode 100644
index 00000000000..d1ac17509c6
--- /dev/null
+++ b/src/plugins/nat/dslite/dslite_in2out.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <nat/dslite/dslite.h>
+#include <nat/nat_inlines.h>
+#include <nat/nat_syslog.h>
+
+typedef enum
+{
+ DSLITE_IN2OUT_NEXT_IP4_LOOKUP,
+ DSLITE_IN2OUT_NEXT_IP6_ICMP,
+ DSLITE_IN2OUT_NEXT_DROP,
+ DSLITE_IN2OUT_NEXT_SLOWPATH,
+ DSLITE_IN2OUT_N_NEXT,
+} dslite_in2out_next_t;
+
+static char *dslite_in2out_error_strings[] = {
+#define _(sym,string) string,
+ foreach_dslite_error
+#undef _
+};
+
+static u32
+slow_path (dslite_main_t * dm, dslite_session_key_t * in2out_key,
+ dslite_session_t ** sp, u32 next, u8 * error, u32 thread_index)
+{
+ dslite_b4_t *b4;
+ clib_bihash_kv_16_8_t b4_kv, b4_value;
+ clib_bihash_kv_24_8_t in2out_kv;
+ clib_bihash_kv_8_8_t out2in_kv;
+ dlist_elt_t *head_elt, *oldest_elt, *elt;
+ u32 oldest_index;
+ dslite_session_t *s;
+ snat_session_key_t out2in_key;
+ nat_ip4_addr_port_t addr_port;
+ u32 b4_index;
+
+ out2in_key.protocol = in2out_key->proto;
+ out2in_key.fib_index = 0;
+
+ b4_kv.key[0] = in2out_key->softwire_id.as_u64[0];
+ b4_kv.key[1] = in2out_key->softwire_id.as_u64[1];
+
+ if (clib_bihash_search_16_8
+ (&dm->per_thread_data[thread_index].b4_hash, &b4_kv, &b4_value))
+ {
+ pool_get (dm->per_thread_data[thread_index].b4s, b4);
+ clib_memset (b4, 0, sizeof (*b4));
+ b4->addr.as_u64[0] = in2out_key->softwire_id.as_u64[0];
+ b4->addr.as_u64[1] = in2out_key->softwire_id.as_u64[1];
+
+ pool_get (dm->per_thread_data[thread_index].list_pool, head_elt);
+ b4->sessions_per_b4_list_head_index =
+ head_elt - dm->per_thread_data[thread_index].list_pool;
+ clib_dlist_init (dm->per_thread_data[thread_index].list_pool,
+ b4->sessions_per_b4_list_head_index);
+
+ b4_index = b4_kv.value = b4 - dm->per_thread_data[thread_index].b4s;
+ clib_bihash_add_del_16_8 (&dm->per_thread_data[thread_index].b4_hash,
+ &b4_kv, 1);
+
+ vlib_set_simple_counter (&dm->total_b4s, thread_index, 0,
+ pool_elts (dm->
+ per_thread_data[thread_index].b4s));
+ }
+ else
+ {
+ b4_index = b4_value.value;
+ b4 =
+ pool_elt_at_index (dm->per_thread_data[thread_index].b4s,
+ b4_value.value);
+ }
+
+ //TODO configurable quota
+ if (b4->nsessions >= 1000)
+ {
+ oldest_index =
+ clib_dlist_remove_head (dm->per_thread_data[thread_index].list_pool,
+ b4->sessions_per_b4_list_head_index);
+ ASSERT (oldest_index != ~0);
+ clib_dlist_addtail (dm->per_thread_data[thread_index].list_pool,
+ b4->sessions_per_b4_list_head_index, oldest_index);
+ oldest_elt =
+ pool_elt_at_index (dm->per_thread_data[thread_index].list_pool,
+ oldest_index);
+ s =
+ pool_elt_at_index (dm->per_thread_data[thread_index].sessions,
+ oldest_elt->value);
+
+ in2out_kv.key[0] = s->in2out.as_u64[0];
+ in2out_kv.key[1] = s->in2out.as_u64[1];
+ in2out_kv.key[2] = s->in2out.as_u64[2];
+ clib_bihash_add_del_24_8 (&dm->per_thread_data[thread_index].in2out,
+ &in2out_kv, 0);
+ out2in_kv.key = s->out2in.as_u64;
+ clib_bihash_add_del_8_8 (&dm->per_thread_data[thread_index].out2in,
+ &out2in_kv, 0);
+
+ addr_port.addr.as_u32 = s->out2in.addr.as_u32;
+ addr_port.port = s->out2in.port;
+
+ nat_free_ip4_addr_and_port (&dm->pool, thread_index,
+ s->out2in.protocol, &addr_port);
+
+ nat_syslog_dslite_apmdel (b4_index, &s->in2out.softwire_id,
+ &s->in2out.addr, s->in2out.port,
+ &s->out2in.addr, s->out2in.port,
+ s->in2out.proto);
+
+ if (nat_alloc_ip4_addr_and_port
+ (&dm->pool, 0, thread_index, thread_index,
+ dm->port_per_thread, out2in_key.protocol, &addr_port))
+ ASSERT (0);
+
+ out2in_key.addr.as_u32 = addr_port.addr.as_u32;
+ out2in_key.port = addr_port.port;
+ }
+ else
+ {
+ if (nat_alloc_ip4_addr_and_port
+ (&dm->pool, 0, thread_index, thread_index,
+ dm->port_per_thread, out2in_key.protocol, &addr_port))
+ {
+ *error = DSLITE_ERROR_OUT_OF_PORTS;
+ return DSLITE_IN2OUT_NEXT_DROP;
+ }
+
+ out2in_key.addr.as_u32 = addr_port.addr.as_u32;
+ out2in_key.port = addr_port.port;
+
+ pool_get (dm->per_thread_data[thread_index].sessions, s);
+ clib_memset (s, 0, sizeof (*s));
+ b4->nsessions++;
+
+ pool_get (dm->per_thread_data[thread_index].list_pool, elt);
+ clib_dlist_init (dm->per_thread_data[thread_index].list_pool,
+ elt - dm->per_thread_data[thread_index].list_pool);
+ elt->value = s - dm->per_thread_data[thread_index].sessions;
+ s->per_b4_index = elt - dm->per_thread_data[thread_index].list_pool;
+ s->per_b4_list_head_index = b4->sessions_per_b4_list_head_index;
+ clib_dlist_addtail (dm->per_thread_data[thread_index].list_pool,
+ s->per_b4_list_head_index,
+ elt - dm->per_thread_data[thread_index].list_pool);
+
+ vlib_set_simple_counter (&dm->total_sessions, thread_index, 0,
+ pool_elts (dm->per_thread_data
+ [thread_index].sessions));
+ }
+
+ s->in2out = *in2out_key;
+ s->out2in = out2in_key;
+ *sp = s;
+ in2out_kv.key[0] = s->in2out.as_u64[0];
+ in2out_kv.key[1] = s->in2out.as_u64[1];
+ in2out_kv.key[2] = s->in2out.as_u64[2];
+ in2out_kv.value = s - dm->per_thread_data[thread_index].sessions;
+ clib_bihash_add_del_24_8 (&dm->per_thread_data[thread_index].in2out,
+ &in2out_kv, 1);
+ out2in_kv.key = s->out2in.as_u64;
+ out2in_kv.value = s - dm->per_thread_data[thread_index].sessions;
+ clib_bihash_add_del_8_8 (&dm->per_thread_data[thread_index].out2in,
+ &out2in_kv, 1);
+
+ nat_syslog_dslite_apmadd (b4_index, &s->in2out.softwire_id, &s->in2out.addr,
+ s->in2out.port, &s->out2in.addr, s->out2in.port,
+ s->in2out.proto);
+
+ return next;
+}
+
+static inline u32
+dslite_icmp_in2out (dslite_main_t * dm, ip6_header_t * ip6,
+ ip4_header_t * ip4, dslite_session_t ** sp, u32 next,
+ u8 * error, u32 thread_index)
+{
+ dslite_session_t *s = 0;
+ icmp46_header_t *icmp = ip4_next_header (ip4);
+ clib_bihash_kv_24_8_t kv, value;
+ dslite_session_key_t key;
+ u32 n = next;
+ icmp_echo_header_t *echo;
+ u32 new_addr, old_addr;
+ u16 old_id, new_id;
+ ip_csum_t sum;
+
+ if (icmp_type_is_error_message (icmp->type))
+ {
+ n = DSLITE_IN2OUT_NEXT_DROP;
+ *error = DSLITE_ERROR_BAD_ICMP_TYPE;
+ goto done;
+ }
+
+ echo = (icmp_echo_header_t *) (icmp + 1);
+
+ key.addr = ip4->src_address;
+ key.port = echo->identifier;
+ key.proto = SNAT_PROTOCOL_ICMP;
+ key.softwire_id.as_u64[0] = ip6->src_address.as_u64[0];
+ key.softwire_id.as_u64[1] = ip6->src_address.as_u64[1];
+ key.pad = 0;
+ kv.key[0] = key.as_u64[0];
+ kv.key[1] = key.as_u64[1];
+ kv.key[2] = key.as_u64[2];
+
+ if (clib_bihash_search_24_8
+ (&dm->per_thread_data[thread_index].in2out, &kv, &value))
+ {
+ n = slow_path (dm, &key, &s, next, error, thread_index);
+ if (PREDICT_FALSE (next == DSLITE_IN2OUT_NEXT_DROP))
+ goto done;
+ }
+ else
+ {
+ s =
+ pool_elt_at_index (dm->per_thread_data[thread_index].sessions,
+ value.value);
+ }
+
+ old_addr = ip4->src_address.as_u32;
+ ip4->src_address = s->out2in.addr;
+ new_addr = ip4->src_address.as_u32;
+ sum = ip4->checksum;
+ sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, src_address);
+ ip4->checksum = ip_csum_fold (sum);
+
+ old_id = echo->identifier;
+ echo->identifier = new_id = s->out2in.port;
+ sum = icmp->checksum;
+ sum = ip_csum_update (sum, old_id, new_id, icmp_echo_header_t, identifier);
+ icmp->checksum = ip_csum_fold (sum);
+
+done:
+ *sp = s;
+ return n;
+}
+
+static inline uword
+dslite_in2out_node_fn_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame, u8 is_slow_path)
+{
+ u32 n_left_from, *from, *to_next;
+ dslite_in2out_next_t next_index;
+ u32 node_index;
+ vlib_node_runtime_t *error_node;
+ u32 thread_index = vm->thread_index;
+ f64 now = vlib_time_now (vm);
+ dslite_main_t *dm = &dslite_main;
+
+ node_index =
+ is_slow_path ? dm->dslite_in2out_slowpath_node_index :
+ dm->dslite_in2out_node_index;
+
+ error_node = vlib_node_get_runtime (vm, node_index);
+
+ 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 = DSLITE_IN2OUT_NEXT_IP4_LOOKUP;
+ ip4_header_t *ip40;
+ ip6_header_t *ip60;
+ u8 error0 = DSLITE_ERROR_IN2OUT;
+ u32 proto0;
+ dslite_session_t *s0 = 0;
+ clib_bihash_kv_24_8_t kv0, value0;
+ dslite_session_key_t key0;
+ udp_header_t *udp0;
+ tcp_header_t *tcp0;
+ ip_csum_t sum0;
+ u32 new_addr0, old_addr0;
+ u16 old_port0, new_port0;
+
+ /* 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 (ip60->protocol != IP_PROTOCOL_IP_IN_IP))
+ {
+ if (ip60->protocol == IP_PROTOCOL_ICMP6)
+ {
+ next0 = DSLITE_IN2OUT_NEXT_IP6_ICMP;
+ goto trace0;
+ }
+ error0 = DSLITE_ERROR_BAD_IP6_PROTOCOL;
+ next0 = DSLITE_IN2OUT_NEXT_DROP;
+ goto trace0;
+ }
+
+ ip40 = vlib_buffer_get_current (b0) + sizeof (ip6_header_t);
+ proto0 = ip_proto_to_snat_proto (ip40->protocol);
+
+ if (PREDICT_FALSE (proto0 == ~0))
+ {
+ error0 = DSLITE_ERROR_UNSUPPORTED_PROTOCOL;
+ next0 = DSLITE_IN2OUT_NEXT_DROP;
+ goto trace0;
+ }
+
+ udp0 = ip4_next_header (ip40);
+ tcp0 = (tcp_header_t *) udp0;
+
+ if (is_slow_path)
+ {
+ if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
+ {
+ next0 =
+ dslite_icmp_in2out (dm, ip60, ip40, &s0, next0, &error0,
+ thread_index);
+ if (PREDICT_FALSE (next0 == DSLITE_IN2OUT_NEXT_DROP))
+ goto trace0;
+
+ goto accounting0;
+ }
+ }
+ else
+ {
+ if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
+ {
+ next0 = DSLITE_IN2OUT_NEXT_SLOWPATH;
+ goto trace0;
+ }
+ }
+
+ key0.addr = ip40->src_address;
+ key0.port = udp0->src_port;
+ key0.proto = proto0;
+ key0.softwire_id.as_u64[0] = ip60->src_address.as_u64[0];
+ key0.softwire_id.as_u64[1] = ip60->src_address.as_u64[1];
+ key0.pad = 0;
+ kv0.key[0] = key0.as_u64[0];
+ kv0.key[1] = key0.as_u64[1];
+ kv0.key[2] = key0.as_u64[2];
+
+ if (clib_bihash_search_24_8
+ (&dm->per_thread_data[thread_index].in2out, &kv0, &value0))
+ {
+ if (is_slow_path)
+ {
+ next0 =
+ slow_path (dm, &key0, &s0, next0, &error0, thread_index);
+ if (PREDICT_FALSE (next0 == DSLITE_IN2OUT_NEXT_DROP))
+ goto trace0;
+ }
+ else
+ {
+ next0 = DSLITE_IN2OUT_NEXT_SLOWPATH;
+ goto trace0;
+ }
+ }
+ else
+ {
+ s0 =
+ pool_elt_at_index (dm->per_thread_data[thread_index].sessions,
+ value0.value);
+ }
+
+ old_addr0 = ip40->src_address.as_u32;
+ ip40->src_address = s0->out2in.addr;
+ new_addr0 = ip40->src_address.as_u32;
+ sum0 = ip40->checksum;
+ sum0 =
+ ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
+ src_address);
+ ip40->checksum = ip_csum_fold (sum0);
+ if (PREDICT_TRUE (proto0 == SNAT_PROTOCOL_TCP))
+ {
+ old_port0 = tcp0->src_port;
+ tcp0->src_port = s0->out2in.port;
+ new_port0 = tcp0->src_port;
+
+ sum0 = tcp0->checksum;
+ sum0 =
+ ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
+ dst_address);
+ sum0 =
+ ip_csum_update (sum0, old_port0, new_port0, ip4_header_t,
+ length);
+ //mss_clamping (&dslite_main, tcp0, &sum0);
+ tcp0->checksum = ip_csum_fold (sum0);
+ }
+ else
+ {
+ old_port0 = udp0->src_port;
+ udp0->src_port = s0->out2in.port;
+ udp0->checksum = 0;
+ }
+
+ accounting0:
+ /* Accounting */
+ s0->last_heard = now;
+ s0->total_pkts++;
+ s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
+ /* Per-B4 LRU list maintenance */
+ clib_dlist_remove (dm->per_thread_data[thread_index].list_pool,
+ s0->per_b4_index);
+ clib_dlist_addtail (dm->per_thread_data[thread_index].list_pool,
+ s0->per_b4_list_head_index, s0->per_b4_index);
+
+ ip40->tos =
+ (clib_net_to_host_u32
+ (ip60->ip_version_traffic_class_and_flow_label) & 0x0ff00000) >>
+ 20;
+ vlib_buffer_advance (b0, sizeof (ip6_header_t));
+
+ trace0:
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ dslite_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->next_index = next0;
+ t->session_index = ~0;
+ if (s0)
+ t->session_index =
+ s0 - dm->per_thread_data[thread_index].sessions;
+ }
+
+ b0->error = error_node->errors[error0];
+
+ /* 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);
+ }
+
+ return frame->n_vectors;
+}
+
+VLIB_NODE_FN (dslite_in2out_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return dslite_in2out_node_fn_inline (vm, node, frame, 0);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (dslite_in2out_node) = {
+ .name = "dslite-in2out",
+ .vector_size = sizeof (u32),
+ .format_trace = format_dslite_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = ARRAY_LEN (dslite_in2out_error_strings),
+ .error_strings = dslite_in2out_error_strings,
+ .n_next_nodes = DSLITE_IN2OUT_N_NEXT,
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [DSLITE_IN2OUT_NEXT_DROP] = "error-drop",
+ [DSLITE_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup",
+ [DSLITE_IN2OUT_NEXT_IP6_ICMP] = "ip6-icmp-input",
+ [DSLITE_IN2OUT_NEXT_SLOWPATH] = "dslite-in2out-slowpath",
+ },
+};
+/* *INDENT-ON* */
+
+VLIB_NODE_FN (dslite_in2out_slowpath_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return dslite_in2out_node_fn_inline (vm, node, frame, 1);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (dslite_in2out_slowpath_node) = {
+ .name = "dslite-in2out-slowpath",
+ .vector_size = sizeof (u32),
+ .format_trace = format_dslite_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = ARRAY_LEN (dslite_in2out_error_strings),
+ .error_strings = dslite_in2out_error_strings,
+ .n_next_nodes = DSLITE_IN2OUT_N_NEXT,
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [DSLITE_IN2OUT_NEXT_DROP] = "error-drop",
+ [DSLITE_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup",
+ [DSLITE_IN2OUT_NEXT_IP6_ICMP] = "ip6-lookup",
+ [DSLITE_IN2OUT_NEXT_SLOWPATH] = "dslite-in2out-slowpath",
+ },
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/dslite/dslite_out2in.c b/src/plugins/nat/dslite/dslite_out2in.c
new file mode 100644
index 00000000000..ac64a589509
--- /dev/null
+++ b/src/plugins/nat/dslite/dslite_out2in.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <nat/dslite/dslite.h>
+#include <nat/nat_inlines.h>
+
+typedef enum
+{
+ DSLITE_OUT2IN_NEXT_IP4_LOOKUP,
+ DSLITE_OUT2IN_NEXT_IP6_LOOKUP,
+ DSLITE_OUT2IN_NEXT_DROP,
+ DSLITE_OUT2IN_N_NEXT,
+} dslite_out2in_next_t;
+
+static char *dslite_out2in_error_strings[] = {
+#define _(sym,string) string,
+ foreach_dslite_error
+#undef _
+};
+
+static inline u32
+dslite_icmp_out2in (dslite_main_t * dm, ip4_header_t * ip4,
+ dslite_session_t ** sp, u32 next, u8 * error,
+ u32 thread_index)
+{
+ dslite_session_t *s = 0;
+ icmp46_header_t *icmp = ip4_next_header (ip4);
+ clib_bihash_kv_8_8_t kv, value;
+ snat_session_key_t key;
+ u32 n = next;
+ icmp_echo_header_t *echo;
+ u32 new_addr, old_addr;
+ u16 old_id, new_id;
+ ip_csum_t sum;
+
+ echo = (icmp_echo_header_t *) (icmp + 1);
+
+ if (icmp_type_is_error_message (icmp->type)
+ || (icmp->type != ICMP4_echo_reply))
+ {
+ n = DSLITE_OUT2IN_NEXT_DROP;
+ *error = DSLITE_ERROR_BAD_ICMP_TYPE;
+ goto done;
+ }
+
+ key.addr = ip4->dst_address;
+ key.port = echo->identifier;
+ key.protocol = SNAT_PROTOCOL_ICMP;
+ key.fib_index = 0;
+ kv.key = key.as_u64;
+
+ if (clib_bihash_search_8_8
+ (&dm->per_thread_data[thread_index].out2in, &kv, &value))
+ {
+ next = DSLITE_OUT2IN_NEXT_DROP;
+ *error = DSLITE_ERROR_NO_TRANSLATION;
+ goto done;
+ }
+ else
+ {
+ s =
+ pool_elt_at_index (dm->per_thread_data[thread_index].sessions,
+ value.value);
+ }
+
+ old_id = echo->identifier;
+ echo->identifier = new_id = s->in2out.port;
+ sum = icmp->checksum;
+ sum = ip_csum_update (sum, old_id, new_id, icmp_echo_header_t, identifier);
+ icmp->checksum = ip_csum_fold (sum);
+
+ old_addr = ip4->dst_address.as_u32;
+ ip4->dst_address = s->in2out.addr;
+ new_addr = ip4->dst_address.as_u32;
+
+ sum = ip4->checksum;
+ sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address);
+ ip4->checksum = ip_csum_fold (sum);
+
+done:
+ *sp = s;
+ return n;
+}
+
+VLIB_NODE_FN (dslite_out2in_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ u32 n_left_from, *from, *to_next;
+ dslite_out2in_next_t next_index;
+ vlib_node_runtime_t *error_node;
+ u32 thread_index = vm->thread_index;
+ f64 now = vlib_time_now (vm);
+ dslite_main_t *dm = &dslite_main;
+
+ error_node = vlib_node_get_runtime (vm, dm->dslite_out2in_node_index);
+
+ 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 = DSLITE_OUT2IN_NEXT_IP6_LOOKUP;
+ u8 error0 = DSLITE_ERROR_OUT2IN;
+ ip4_header_t *ip40;
+ ip6_header_t *ip60;
+ u32 proto0;
+ udp_header_t *udp0;
+ tcp_header_t *tcp0;
+ clib_bihash_kv_8_8_t kv0, value0;
+ snat_session_key_t key0;
+ dslite_session_t *s0 = 0;
+ ip_csum_t sum0;
+ u32 new_addr0, old_addr0;
+ u16 new_port0, old_port0;
+
+ /* speculatively enqueue b0 to the current next frame */
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ ip40 = vlib_buffer_get_current (b0);
+ proto0 = ip_proto_to_snat_proto (ip40->protocol);
+
+ if (PREDICT_FALSE (proto0 == ~0))
+ {
+ error0 = DSLITE_ERROR_UNSUPPORTED_PROTOCOL;
+ next0 = DSLITE_OUT2IN_NEXT_DROP;
+ goto trace0;
+ }
+
+ if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
+ {
+ next0 =
+ dslite_icmp_out2in (dm, ip40, &s0, next0, &error0,
+ thread_index);
+ if (PREDICT_FALSE (next0 == DSLITE_OUT2IN_NEXT_DROP))
+ goto trace0;
+
+ goto encap0;
+ }
+
+ udp0 = ip4_next_header (ip40);
+ tcp0 = (tcp_header_t *) udp0;
+
+ key0.addr = ip40->dst_address;
+ key0.port = udp0->dst_port;
+ key0.protocol = proto0;
+ key0.fib_index = 0;
+ kv0.key = key0.as_u64;
+
+ if (clib_bihash_search_8_8
+ (&dm->per_thread_data[thread_index].out2in, &kv0, &value0))
+ {
+ next0 = DSLITE_OUT2IN_NEXT_DROP;
+ error0 = DSLITE_ERROR_NO_TRANSLATION;
+ goto trace0;
+ }
+ else
+ {
+ s0 =
+ pool_elt_at_index (dm->per_thread_data[thread_index].sessions,
+ value0.value);
+ }
+
+ old_addr0 = ip40->dst_address.as_u32;
+ ip40->dst_address = s0->in2out.addr;
+ new_addr0 = ip40->dst_address.as_u32;
+
+ sum0 = ip40->checksum;
+ sum0 =
+ ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
+ dst_address);
+ ip40->checksum = ip_csum_fold (sum0);
+
+ if (PREDICT_TRUE (proto0 == SNAT_PROTOCOL_TCP))
+ {
+ old_port0 = tcp0->dst_port;
+ tcp0->dst_port = s0->in2out.port;
+ new_port0 = tcp0->dst_port;
+
+ sum0 = tcp0->checksum;
+ sum0 =
+ ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
+ dst_address);
+ sum0 =
+ ip_csum_update (sum0, old_port0, new_port0, ip4_header_t,
+ length);
+ tcp0->checksum = ip_csum_fold (sum0);
+ }
+ else
+ {
+ old_port0 = udp0->dst_port;
+ udp0->dst_port = s0->in2out.port;
+ udp0->checksum = 0;
+ }
+
+ encap0:
+ /* Construct IPv6 header */
+ vlib_buffer_advance (b0, -(sizeof (ip6_header_t)));
+ ip60 = vlib_buffer_get_current (b0);
+ ip60->ip_version_traffic_class_and_flow_label =
+ clib_host_to_net_u32 ((6 << 28) + (ip40->tos << 20));
+ ip60->payload_length = ip40->length;
+ ip60->protocol = IP_PROTOCOL_IP_IN_IP;
+ ip60->hop_limit = ip40->ttl;
+ ip60->src_address.as_u64[0] = dm->aftr_ip6_addr.as_u64[0];
+ ip60->src_address.as_u64[1] = dm->aftr_ip6_addr.as_u64[1];
+ ip60->dst_address.as_u64[0] = s0->in2out.softwire_id.as_u64[0];
+ ip60->dst_address.as_u64[1] = s0->in2out.softwire_id.as_u64[1];
+
+ /* Accounting */
+ s0->last_heard = now;
+ s0->total_pkts++;
+ s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
+ /* Per-B4 LRU list maintenance */
+ clib_dlist_remove (dm->per_thread_data[thread_index].list_pool,
+ s0->per_b4_index);
+ clib_dlist_addtail (dm->per_thread_data[thread_index].list_pool,
+ s0->per_b4_list_head_index, s0->per_b4_index);
+ trace0:
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ dslite_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->next_index = next0;
+ t->session_index = ~0;
+ if (s0)
+ t->session_index =
+ s0 - dm->per_thread_data[thread_index].sessions;
+ }
+
+ b0->error = error_node->errors[error0];
+
+ /* 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);
+ }
+
+ return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (dslite_out2in_node) = {
+ .name = "dslite-out2in",
+ .vector_size = sizeof (u32),
+ .format_trace = format_dslite_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = ARRAY_LEN (dslite_out2in_error_strings),
+ .error_strings = dslite_out2in_error_strings,
+ .n_next_nodes = DSLITE_OUT2IN_N_NEXT,
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [DSLITE_OUT2IN_NEXT_DROP] = "error-drop",
+ [DSLITE_OUT2IN_NEXT_IP4_LOOKUP] = "ip4-lookup",
+ [DSLITE_OUT2IN_NEXT_IP6_LOOKUP] = "ip6-lookup",
+ },
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */