NAT: DS-Lite (VPP-1040) 38/9238/3
authorMatus Fabian <matfabia@cisco.com>
Mon, 6 Nov 2017 13:04:53 +0000 (05:04 -0800)
committerOle Trøan <otroan@employees.org>
Tue, 7 Nov 2017 11:52:39 +0000 (11:52 +0000)
Dual-Stack Lite enables a broadband service provider to share IPv4 addresses among customers by combining two well-known technologies: IPv4-in-IPv6 and NAT.

Change-Id: I039740f8548c623cd1ac89b8ecda1a6cc4aafb9c
Signed-off-by: Matus Fabian <matfabia@cisco.com>
15 files changed:
src/plugins/nat.am
src/plugins/nat/dslite.c [new file with mode: 0644]
src/plugins/nat/dslite.h [new file with mode: 0644]
src/plugins/nat/dslite_cli.c [new file with mode: 0644]
src/plugins/nat/dslite_dpo.c [new file with mode: 0644]
src/plugins/nat/dslite_dpo.h [new file with mode: 0644]
src/plugins/nat/dslite_in2out.c [new file with mode: 0644]
src/plugins/nat/dslite_out2in.c [new file with mode: 0644]
src/plugins/nat/in2out.c
src/plugins/nat/nat.api
src/plugins/nat/nat.c
src/plugins/nat/nat.h
src/plugins/nat/nat_api.c
test/test_nat.py
test/vpp_papi_provider.py

index b967a71..add82f0 100644 (file)
@@ -26,7 +26,12 @@ nat_plugin_la_SOURCES = nat/nat.c            \
         nat/nat64_cli.c                        \
         nat/nat64_in2out.c                     \
         nat/nat64_out2in.c                     \
         nat/nat64_cli.c                        \
         nat/nat64_in2out.c                     \
         nat/nat64_out2in.c                     \
-        nat/nat64_db.c
+        nat/nat64_db.c                         \
+        nat/dslite_dpo.c                       \
+        nat/dslite.c                           \
+        nat/dslite_in2out.c                    \
+        nat/dslite_out2in.c                    \
+        nat/dslite_cli.c
 
 API_FILES += nat/nat.api
 
 
 API_FILES += nat/nat.api
 
diff --git a/src/plugins/nat/dslite.c b/src/plugins/nat/dslite.c
new file mode 100644 (file)
index 0000000..c86cc8d
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * 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.h>
+#include <nat/dslite_dpo.h>
+#include <vnet/fib/fib_table.h>
+
+dslite_main_t dslite_main;
+
+void
+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;
+  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;
+
+  dm->first_worker_index = 0;
+  dm->num_workers = 0;
+
+  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* */
+
+  dslite_dpo_module_init ();
+}
+
+int
+dslite_set_aftr_ip6_addr (dslite_main_t * dm, ip6_address_t * addr)
+{
+  dpo_id_t dpo_v6 = DPO_INVALID;
+
+  dslite_dpo_create (DPO_PROTO_IP6, 0, &dpo_v6);
+  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, FIB_SOURCE_PLUGIN_HI,
+                                  FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v6);
+  dpo_reset (&dpo_v6);
+
+  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_add_del_pool_addr (dslite_main_t * dm, ip4_address_t * addr, u8 is_add)
+{
+  vlib_thread_main_t *tm = vlib_get_thread_main ();
+  snat_address_t *a = 0;
+  int i = 0;
+  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,
+  };
+
+  for (i = 0; i < vec_len (dm->addr_pool); i++)
+    {
+      if (dm->addr_pool[i].addr.as_u32 == addr->as_u32)
+       {
+         a = dm->addr_pool + i;
+         break;
+       }
+    }
+  if (is_add)
+    {
+      if (a)
+       return VNET_API_ERROR_VALUE_EXIST;
+      vec_add2 (dm->addr_pool, a, 1);
+      a->addr = *addr;
+#define _(N, i, n, s) \
+      clib_bitmap_alloc (a->busy_##n##_port_bitmap, 65535); \
+      a->busy_##n##_ports = 0; \
+      vec_validate_init_empty (a->busy_##n##_ports_per_thread, tm->n_vlib_mains - 1, 0);
+      foreach_snat_protocol
+#undef _
+       dslite_dpo_create (DPO_PROTO_IP4, 0, &dpo_v4);
+      fib_table_entry_special_dpo_add (0, &pfx, FIB_SOURCE_PLUGIN_HI,
+                                      FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v4);
+      dpo_reset (&dpo_v4);
+    }
+  else
+    {
+      if (!a)
+       return VNET_API_ERROR_NO_SUCH_ENTRY;
+#define _(N, id, n, s) \
+      clib_bitmap_free (a->busy_##n##_port_bitmap); \
+      vec_free (a->busy_##n##_ports_per_thread);
+      foreach_snat_protocol
+#undef _
+       fib_table_entry_special_remove (0, &pfx, FIB_SOURCE_PLUGIN_HI);
+      vec_del1 (dm->addr_pool, i);
+    }
+  return 0;
+}
+
+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;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/dslite.h b/src/plugins/nat/dslite.h
new file mode 100644 (file)
index 0000000..4b7089b
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * 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/nat.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;
+  u32 outside_address_index;
+}) 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;
+  dslite_per_thread_data_t *per_thread_data;
+  snat_address_t *addr_pool;
+  u32 num_workers;
+  u32 first_worker_index;
+  u16 port_per_thread;
+} dslite_main_t;
+
+typedef struct
+{
+  u32 next_index;
+  u32 session_index;
+} dslite_trace_t;
+
+#define foreach_dslite_error                    \
+_(IN2OUT, "valid in2out DS-Lite packets")       \
+_(OUT2IN, "valid out2in 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;
+
+void dslite_init (vlib_main_t * vm);
+int dslite_set_aftr_ip6_addr (dslite_main_t * dm, ip6_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);
+
+#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_cli.c b/src/plugins/nat/dslite_cli.c
new file mode 100644 (file)
index 0000000..7282762
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * 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.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 i, 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;
+
+  for (i = 0; i < count; i++)
+    {
+      rv = dslite_add_del_pool_addr (dm, &this_addr, is_add);
+
+      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);
+         goto done;
+       case VNET_API_ERROR_VALUE_EXIST:
+         error =
+           clib_error_return (0, "DS-Lite pool address %U exist.",
+                              format_ip4_address, &this_addr);
+         goto done;
+       default:
+         break;
+
+       }
+      increment_v4_address (&this_addr);
+    }
+
+done:
+  unformat_free (line_input);
+
+  return error;
+}
+
+static 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;
+  snat_address_t *ap;
+
+  vlib_cli_output (vm, "DS-Lite pool:");
+
+  /* *INDENT-OFF* */
+  vec_foreach (ap, dm->addr_pool)
+    {
+      vlib_cli_output (vm, "%U", format_ip4_address, &ap->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 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* */
+
+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,
+};
+
+VLIB_CLI_COMMAND (show_dslite_pool_command, static) = {
+  .path = "show dslite pool",
+  .short_help = "show dslite pool",
+  .function = dslite_show_pool_command_fn,
+};
+
+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,
+};
+
+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,
+};
+
+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_dpo.c b/src/plugins/nat/dslite_dpo.c
new file mode 100644 (file)
index 0000000..376d717
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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_dpo.h>
+
+dpo_type_t dslite_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);
+}
+
+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));
+}
+
+static void
+dslite_dpo_lock (dpo_id_t * dpo)
+{
+}
+
+static void
+dslite_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 char *const dslite_ip4_nodes[] = {
+  "dslite-out2in",
+  NULL,
+};
+
+const static char *const dslite_ip6_nodes[] = {
+  "dslite-in2out",
+  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,
+};
+
+void
+dslite_dpo_module_init (void)
+{
+  dslite_dpo_type = dpo_register_new_type (&dslite_dpo_vft, dslite_nodes);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/dslite_dpo.h b/src/plugins/nat/dslite_dpo.h
new file mode 100644 (file)
index 0000000..ead0e7d
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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);
+
+u8 *format_dslite_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_in2out.c b/src/plugins/nat/dslite_in2out.c
new file mode 100644 (file)
index 0000000..9a7751c
--- /dev/null
@@ -0,0 +1,490 @@
+/*
+ * 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.h>
+
+vlib_node_registration_t dslite_in2out_node;
+vlib_node_registration_t dslite_in2out_slowpath_node;
+
+typedef enum
+{
+  DSLITE_IN2OUT_NEXT_IP4_LOOKUP,
+  DSLITE_IN2OUT_NEXT_IP6_LOOKUP,
+  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;
+  u32 address_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);
+      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_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);
+    }
+  else
+    {
+      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);
+      snat_free_outside_address_and_port (dm->addr_pool, thread_index,
+                                         &s->out2in,
+                                         s->outside_address_index);
+      s->outside_address_index = ~0;
+
+      if (snat_alloc_outside_address_and_port
+         (dm->addr_pool, 0, thread_index, &out2in_key,
+          &s->outside_address_index, 0, dm->port_per_thread, thread_index))
+       ASSERT (0);
+    }
+  else
+    {
+      if (snat_alloc_outside_address_and_port
+         (dm->addr_pool, 0, thread_index, &out2in_key, &address_index, 0,
+          dm->port_per_thread, thread_index))
+       {
+         *error = DSLITE_ERROR_OUT_OF_PORTS;
+         return DSLITE_IN2OUT_NEXT_DROP;
+       }
+      pool_get (dm->per_thread_data[thread_index].sessions, s);
+      memset (s, 0, sizeof (*s));
+      s->outside_address_index = address_index;
+      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);
+    }
+
+  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);
+
+  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_is_error_message (icmp))
+    {
+      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 = vlib_get_thread_index ();
+  f64 now = vlib_time_now (vm);
+  dslite_main_t *dm = &dslite_main;
+
+  node_index =
+    is_slow_path ? dslite_in2out_slowpath_node.
+    index : 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))
+           {
+             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);
+             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;
+}
+
+static uword
+dslite_in2out_node_fn (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) = {
+  .function = dslite_in2out_node_fn,
+  .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_LOOKUP] = "ip6-lookup",
+    [DSLITE_IN2OUT_NEXT_SLOWPATH] = "dslite-in2out-slowpath",
+  },
+};
+/* *INDENT-ON* */
+
+VLIB_NODE_FUNCTION_MULTIARCH (dslite_in2out_node, dslite_in2out_node_fn);
+
+static uword
+dslite_in2out_slowpath_node_fn (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) = {
+  .function = dslite_in2out_slowpath_node_fn,
+  .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_LOOKUP] = "ip6-lookup",
+    [DSLITE_IN2OUT_NEXT_SLOWPATH] = "dslite-in2out-slowpath",
+  },
+};
+/* *INDENT-ON* */
+
+VLIB_NODE_FUNCTION_MULTIARCH (dslite_in2out_slowpath_node,
+                             dslite_in2out_slowpath_node_fn);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/dslite_out2in.c b/src/plugins/nat/dslite_out2in.c
new file mode 100644 (file)
index 0000000..802b2a9
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * 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.h>
+
+vlib_node_registration_t dslite_out2in_node;
+
+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_is_error_message (icmp) || (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;
+}
+
+static uword
+dslite_out2in_node_fn (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 = vlib_get_thread_index ();
+  f64 now = vlib_time_now (vm);
+  dslite_main_t *dm = &dslite_main;
+
+  error_node = vlib_node_get_runtime (vm, 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) = {
+  .function = dslite_out2in_node_fn,
+  .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* */
+
+VLIB_NODE_FUNCTION_MULTIARCH (dslite_out2in_node, dslite_out2in_node_fn);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index 9f668d8..b059390 100755 (executable)
@@ -366,12 +366,15 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
                                               s->in2out.fib_index);
 
           snat_free_outside_address_and_port
                                               s->in2out.fib_index);
 
           snat_free_outside_address_and_port
-            (sm, thread_index, &s->out2in, s->outside_address_index);
+            (sm->addresses, thread_index, &s->out2in, s->outside_address_index);
         }
       s->outside_address_index = ~0;
 
         }
       s->outside_address_index = ~0;
 
-      if (snat_alloc_outside_address_and_port (sm, rx_fib_index0, thread_index,
-                                               &key1, &address_index))
+      if (snat_alloc_outside_address_and_port (sm->addresses, rx_fib_index0,
+                                               thread_index, &key1,
+                                               &address_index, sm->vrf_mode,
+                                               sm->port_per_thread,
+                                               sm->per_thread_data[thread_index].snat_thread_index))
         {
           ASSERT(0);
 
         {
           ASSERT(0);
 
@@ -389,9 +392,11 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
         {
           static_mapping = 0;
           /* Try to create dynamic translation */
         {
           static_mapping = 0;
           /* Try to create dynamic translation */
-          if (snat_alloc_outside_address_and_port (sm, rx_fib_index0,
+          if (snat_alloc_outside_address_and_port (sm->addresses, rx_fib_index0,
                                                    thread_index, &key1,
                                                    thread_index, &key1,
-                                                   &address_index))
+                                                   &address_index, sm->vrf_mode,
+                                                   sm->port_per_thread,
+                                                   sm->per_thread_data[thread_index].snat_thread_index))
             {
               b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS];
               return SNAT_IN2OUT_NEXT_DROP;
             {
               b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS];
               return SNAT_IN2OUT_NEXT_DROP;
@@ -1265,7 +1270,8 @@ create_ses:
                                                   s->out2in.port,
                                                   s->in2out.fib_index);
 
                                                   s->out2in.port,
                                                   s->in2out.fib_index);
 
-              snat_free_outside_address_and_port (sm, thread_index, &s->out2in,
+              snat_free_outside_address_and_port (sm->addresses, thread_index,
+                                                  &s->out2in,
                                                   s->outside_address_index);
 
               /* Remove in2out, out2in keys */
                                                   s->outside_address_index);
 
               /* Remove in2out, out2in keys */
index 98a6f06..187de25 100644 (file)
@@ -1567,3 +1567,30 @@ define nat64_prefix_details {
   u8 prefix_len;
   u32 vrf_id;
 };
   u8 prefix_len;
   u32 vrf_id;
 };
+
+
+/*
+ * 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 address of the range
+    @param end_addr - end address of the range
+    @param is_add - 1 if add, 0 if delete
+*/
+autoreply define dslite_add_del_pool_addr_range {
+  u32 client_index;
+  u32 context;
+  u8 start_addr[4];
+  u8 end_addr[4];
+  u8 is_add;
+};
+
+autoreply define dslite_set_aftr_addr {
+  u32 client_index;
+  u32 context;
+  u8 ip4_addr[4];
+  u8 ip6_addr[16];
+};
index 189c594..cd5a6eb 100644 (file)
@@ -23,6 +23,7 @@
 #include <nat/nat_ipfix_logging.h>
 #include <nat/nat_det.h>
 #include <nat/nat64.h>
 #include <nat/nat_ipfix_logging.h>
 #include <nat/nat_det.h>
 #include <nat/nat64.h>
+#include <nat/dslite.h>
 #include <vnet/fib/fib_table.h>
 #include <vnet/fib/ip4_fib.h>
 
 #include <vnet/fib/fib_table.h>
 #include <vnet/fib/ip4_fib.h>
 
@@ -1448,12 +1449,14 @@ static clib_error_t * snat_init (vlib_main_t * vm)
 
   error = nat64_init(vm);
 
 
   error = nat64_init(vm);
 
+  dslite_init(vm);
+
   return error;
 }
 
 VLIB_INIT_FUNCTION (snat_init);
 
   return error;
 }
 
 VLIB_INIT_FUNCTION (snat_init);
 
-void snat_free_outside_address_and_port (snat_main_t * sm,
+void snat_free_outside_address_and_port (snat_address_t * addresses,
                                          u32 thread_index,
                                          snat_session_key_t * k,
                                          u32 address_index)
                                          u32 thread_index,
                                          snat_session_key_t * k,
                                          u32 address_index)
@@ -1461,9 +1464,9 @@ void snat_free_outside_address_and_port (snat_main_t * sm,
   snat_address_t *a;
   u16 port_host_byte_order = clib_net_to_host_u16 (k->port);
 
   snat_address_t *a;
   u16 port_host_byte_order = clib_net_to_host_u16 (k->port);
 
-  ASSERT (address_index < vec_len (sm->addresses));
+  ASSERT (address_index < vec_len (addresses));
 
 
-  a = sm->addresses + address_index;
+  a = addresses + address_index;
 
   switch (k->protocol)
     {
 
   switch (k->protocol)
     {
@@ -1572,38 +1575,42 @@ int snat_static_mapping_match (snat_main_t * sm,
 }
 
 static_always_inline u16
 }
 
 static_always_inline u16
-snat_random_port (snat_main_t * sm, u16 min, u16 max)
+snat_random_port (u16 min, u16 max)
 {
 {
+  snat_main_t *sm = &snat_main;
   return min + random_u32 (&sm->random_seed) /
     (random_u32_max() / (max - min + 1) + 1);
 }
 
   return min + random_u32 (&sm->random_seed) /
     (random_u32_max() / (max - min + 1) + 1);
 }
 
-int snat_alloc_outside_address_and_port (snat_main_t * sm,
+int snat_alloc_outside_address_and_port (snat_address_t * addresses,
                                          u32 fib_index,
                                          u32 thread_index,
                                          snat_session_key_t * k,
                                          u32 fib_index,
                                          u32 thread_index,
                                          snat_session_key_t * k,
-                                         u32 * address_indexp)
+                                         u32 * address_indexp,
+                                         u8 vrf_mode,
+                                         u16 port_per_thread,
+                                         u32 snat_thread_index)
 {
   int i;
   snat_address_t *a;
   u32 portnum;
 
 {
   int i;
   snat_address_t *a;
   u32 portnum;
 
-  for (i = 0; i < vec_len (sm->addresses); i++)
+  for (i = 0; i < vec_len (addresses); i++)
     {
     {
-      a = sm->addresses + i;
-      if (sm->vrf_mode && a->fib_index != ~0 && a->fib_index != fib_index)
+      a = addresses + i;
+      if (vrf_mode && a->fib_index != ~0 && a->fib_index != fib_index)
         continue;
       switch (k->protocol)
         {
 #define _(N, j, n, s) \
         case SNAT_PROTOCOL_##N: \
         continue;
       switch (k->protocol)
         {
 #define _(N, j, n, s) \
         case SNAT_PROTOCOL_##N: \
-          if (a->busy_##n##_ports_per_thread[thread_index] < sm->port_per_thread) \
+          if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread) \
             { \
               while (1) \
                 { \
             { \
               while (1) \
                 { \
-                  portnum = (sm->port_per_thread * \
-                    sm->per_thread_data[thread_index].snat_thread_index) + \
-                    snat_random_port(sm, 1, sm->port_per_thread) + 1024; \
+                  portnum = (port_per_thread * \
+                    snat_thread_index) + \
+                    snat_random_port(1, port_per_thread) + 1024; \
                   if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
                     continue; \
                   clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
                   if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
                     continue; \
                   clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
index aac46bf..b72e075 100644 (file)
@@ -376,16 +376,19 @@ extern vlib_node_registration_t snat_det_out2in_node;
 extern vlib_node_registration_t snat_hairpin_dst_node;
 extern vlib_node_registration_t snat_hairpin_src_node;
 
 extern vlib_node_registration_t snat_hairpin_dst_node;
 extern vlib_node_registration_t snat_hairpin_src_node;
 
-void snat_free_outside_address_and_port (snat_main_t * sm,
+void snat_free_outside_address_and_port (snat_address_t * addresses,
                                          u32 thread_index,
                                          snat_session_key_t * k,
                                          u32 address_index);
 
                                          u32 thread_index,
                                          snat_session_key_t * k,
                                          u32 address_index);
 
-int snat_alloc_outside_address_and_port (snat_main_t * sm,
+int snat_alloc_outside_address_and_port (snat_address_t * addresses,
                                          u32 fib_index,
                                          u32 thread_index,
                                          snat_session_key_t * k,
                                          u32 fib_index,
                                          u32 thread_index,
                                          snat_session_key_t * k,
-                                         u32 * address_indexp);
+                                         u32 * address_indexp,
+                                         u8 vrf_mode,
+                                         u16 port_per_thread,
+                                         u32 snat_thread_index);
 
 int snat_static_mapping_match (snat_main_t * sm,
                                snat_session_key_t match,
 
 int snat_static_mapping_match (snat_main_t * sm,
                                snat_session_key_t match,
index f80a506..0ffa2f0 100644 (file)
@@ -21,6 +21,7 @@
 #include <nat/nat.h>
 #include <nat/nat_det.h>
 #include <nat/nat64.h>
 #include <nat/nat.h>
 #include <nat/nat_det.h>
 #include <nat/nat64.h>
+#include <nat/dslite.h>
 #include <vlibapi/api.h>
 #include <vlibmemory/api.h>
 
 #include <vlibapi/api.h>
 #include <vlibmemory/api.h>
 
@@ -3289,6 +3290,87 @@ vl_api_nat64_prefix_dump_t_print (vl_api_nat64_prefix_dump_t * mp,
   FINISH;
 }
 
   FINISH;
 }
 
+/***************/
+/*** DS-Lite ***/
+/***************/
+
+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;
+  snat_main_t *sm = &snat_main;
+  dslite_main_t *dm = &dslite_main;
+  int rv = 0;
+  ip6_address_t ip6_addr;
+
+  memcpy (&ip6_addr.as_u8, mp->ip6_addr, 16);
+
+  rv = dslite_set_aftr_ip6_addr (dm, &ip6_addr);
+
+  REPLY_MACRO (VL_API_DSLITE_SET_AFTR_ADDR_REPLY);
+}
+
+static void *
+vl_api_dslite_set_aftr_addr_t_print (vl_api_dslite_set_aftr_addr_t * mp,
+                                    void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: dslite_set_aftr_addr ");
+  s = format (s, "ip6_addr %U ip4_addr %U\n",
+             format_ip6_address, mp->ip6_addr,
+             format_ip4_address, mp->ip4_addr);
+
+  FINISH;
+}
+
+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;
+  snat_main_t *sm = &snat_main;
+  dslite_main_t *dm = &dslite_main;
+  int rv = 0;
+  ip4_address_t this_addr;
+  u32 start_host_order, end_host_order;
+  int i, count;
+  u32 *tmp;
+
+  tmp = (u32 *) mp->start_addr;
+  start_host_order = clib_host_to_net_u32 (tmp[0]);
+  tmp = (u32 *) mp->end_addr;
+  end_host_order = clib_host_to_net_u32 (tmp[0]);
+
+  count = (end_host_order - start_host_order) + 1;
+  memcpy (&this_addr.as_u8, mp->start_addr, 4);
+
+  for (i = 0; i < count; i++)
+    {
+      if ((rv = dslite_add_del_pool_addr (dm, &this_addr, mp->is_add)))
+       goto send_reply;
+
+      increment_v4_address (&this_addr);
+    }
+
+send_reply:
+  REPLY_MACRO (VL_API_DSLITE_ADD_DEL_POOL_ADDR_RANGE_REPLY);
+}
+
+static void *vl_api_dslite_add_del_pool_addr_range_t_print
+  (vl_api_dslite_add_del_pool_addr_range_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: dslite_add_del_pool_addr_range ");
+  s = format (s, "%U - %U\n",
+             format_ip4_address, mp->start_addr,
+             format_ip4_address, mp->end_addr);
+
+  FINISH;
+}
+
+
 /* List of message types that this plugin understands */
 #define foreach_snat_plugin_api_msg                                     \
 _(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range)                       \
 /* List of message types that this plugin understands */
 #define foreach_snat_plugin_api_msg                                     \
 _(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range)                       \
@@ -3360,7 +3442,9 @@ _(NAT64_SET_TIMEOUTS, nat64_set_timeouts)                               \
 _(NAT64_GET_TIMEOUTS, nat64_get_timeouts)                               \
 _(NAT64_ST_DUMP, nat64_st_dump)                                         \
 _(NAT64_ADD_DEL_PREFIX, nat64_add_del_prefix)                           \
 _(NAT64_GET_TIMEOUTS, nat64_get_timeouts)                               \
 _(NAT64_ST_DUMP, nat64_st_dump)                                         \
 _(NAT64_ADD_DEL_PREFIX, nat64_add_del_prefix)                           \
-_(NAT64_PREFIX_DUMP, nat64_prefix_dump)
+_(NAT64_PREFIX_DUMP, nat64_prefix_dump)                                 \
+_(DSLITE_ADD_DEL_POOL_ADDR_RANGE, dslite_add_del_pool_addr_range)       \
+_(DSLITE_SET_AFTR_ADDR, dslite_set_aftr_addr)
 
 /* Set up the API message handling tables */
 static clib_error_t *
 
 /* Set up the API message handling tables */
 static clib_error_t *
index 37e1b1e..e420baf 100644 (file)
@@ -5,6 +5,7 @@ import unittest
 import struct
 
 from framework import VppTestCase, VppTestRunner, running_extended_tests
 import struct
 
 from framework import VppTestCase, VppTestRunner, running_extended_tests
+from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto
 from scapy.layers.inet import IP, TCP, UDP, ICMP
 from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
 from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6EchoReply
 from scapy.layers.inet import IP, TCP, UDP, ICMP
 from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
 from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6EchoReply
@@ -4007,5 +4008,152 @@ class TestNAT64(MethodHolder):
             self.logger.info(self.vapi.cli("show nat64 session table all"))
             self.clear_nat64()
 
             self.logger.info(self.vapi.cli("show nat64 session table all"))
             self.clear_nat64()
 
+
+class TestDSlite(MethodHolder):
+    """ DS-Lite Test Cases """
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestDSlite, cls).setUpClass()
+
+        try:
+            cls.nat_addr = '10.0.0.3'
+            cls.nat_addr_n = socket.inet_pton(socket.AF_INET, cls.nat_addr)
+
+            cls.create_pg_interfaces(range(2))
+            cls.pg0.admin_up()
+            cls.pg0.config_ip4()
+            cls.pg0.resolve_arp()
+            cls.pg1.admin_up()
+            cls.pg1.config_ip6()
+            cls.pg1.generate_remote_hosts(2)
+            cls.pg1.configure_ipv6_neighbors()
+
+        except Exception:
+            super(TestDSlite, cls).tearDownClass()
+            raise
+
+    def test_dslite(self):
+        """ Test DS-Lite """
+        self.vapi.dslite_add_del_pool_addr_range(self.nat_addr_n,
+                                                 self.nat_addr_n)
+        aftr_ip4 = '192.0.0.1'
+        aftr_ip4_n = socket.inet_pton(socket.AF_INET, aftr_ip4)
+        aftr_ip6 = '2001:db8:85a3::8a2e:370:1'
+        aftr_ip6_n = socket.inet_pton(socket.AF_INET6, aftr_ip6)
+        self.vapi.dslite_set_aftr_addr(aftr_ip6_n, aftr_ip4_n)
+
+        # UDP
+        p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
+             IPv6(dst=aftr_ip6, src=self.pg1.remote_hosts[0].ip6) /
+             IP(dst=self.pg0.remote_ip4, src='192.168.1.1') /
+             UDP(sport=20000, dport=10000))
+        self.pg1.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg0.get_capture(1)
+        capture = capture[0]
+        self.assertFalse(capture.haslayer(IPv6))
+        self.assertEqual(capture[IP].src, self.nat_addr)
+        self.assertEqual(capture[IP].dst, self.pg0.remote_ip4)
+        self.assertNotEqual(capture[UDP].sport, 20000)
+        self.assertEqual(capture[UDP].dport, 10000)
+        self.check_ip_checksum(capture)
+        out_port = capture[UDP].sport
+
+        p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+             IP(dst=self.nat_addr, src=self.pg0.remote_ip4) /
+             UDP(sport=10000, dport=out_port))
+        self.pg0.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg1.get_capture(1)
+        capture = capture[0]
+        self.assertEqual(capture[IPv6].src, aftr_ip6)
+        self.assertEqual(capture[IPv6].dst, self.pg1.remote_hosts[0].ip6)
+        self.assertEqual(capture[IP].src, self.pg0.remote_ip4)
+        self.assertEqual(capture[IP].dst, '192.168.1.1')
+        self.assertEqual(capture[UDP].sport, 10000)
+        self.assertEqual(capture[UDP].dport, 20000)
+        self.check_ip_checksum(capture)
+
+        # TCP
+        p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
+             IPv6(dst=aftr_ip6, src=self.pg1.remote_hosts[1].ip6) /
+             IP(dst=self.pg0.remote_ip4, src='192.168.1.1') /
+             TCP(sport=20001, dport=10001))
+        self.pg1.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg0.get_capture(1)
+        capture = capture[0]
+        self.assertFalse(capture.haslayer(IPv6))
+        self.assertEqual(capture[IP].src, self.nat_addr)
+        self.assertEqual(capture[IP].dst, self.pg0.remote_ip4)
+        self.assertNotEqual(capture[TCP].sport, 20001)
+        self.assertEqual(capture[TCP].dport, 10001)
+        self.check_ip_checksum(capture)
+        self.check_tcp_checksum(capture)
+        out_port = capture[TCP].sport
+
+        p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+             IP(dst=self.nat_addr, src=self.pg0.remote_ip4) /
+             TCP(sport=10001, dport=out_port))
+        self.pg0.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg1.get_capture(1)
+        capture = capture[0]
+        self.assertEqual(capture[IPv6].src, aftr_ip6)
+        self.assertEqual(capture[IPv6].dst, self.pg1.remote_hosts[1].ip6)
+        self.assertEqual(capture[IP].src, self.pg0.remote_ip4)
+        self.assertEqual(capture[IP].dst, '192.168.1.1')
+        self.assertEqual(capture[TCP].sport, 10001)
+        self.assertEqual(capture[TCP].dport, 20001)
+        self.check_ip_checksum(capture)
+        self.check_tcp_checksum(capture)
+
+        # ICMP
+        p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
+             IPv6(dst=aftr_ip6, src=self.pg1.remote_hosts[1].ip6) /
+             IP(dst=self.pg0.remote_ip4, src='192.168.1.1') /
+             ICMP(id=4000, type='echo-request'))
+        self.pg1.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg0.get_capture(1)
+        capture = capture[0]
+        self.assertFalse(capture.haslayer(IPv6))
+        self.assertEqual(capture[IP].src, self.nat_addr)
+        self.assertEqual(capture[IP].dst, self.pg0.remote_ip4)
+        self.assertNotEqual(capture[ICMP].id, 4000)
+        self.check_ip_checksum(capture)
+        self.check_icmp_checksum(capture)
+        out_id = capture[ICMP].id
+
+        p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+             IP(dst=self.nat_addr, src=self.pg0.remote_ip4) /
+             ICMP(id=out_id, type='echo-reply'))
+        self.pg0.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg1.get_capture(1)
+        capture = capture[0]
+        self.assertEqual(capture[IPv6].src, aftr_ip6)
+        self.assertEqual(capture[IPv6].dst, self.pg1.remote_hosts[1].ip6)
+        self.assertEqual(capture[IP].src, self.pg0.remote_ip4)
+        self.assertEqual(capture[IP].dst, '192.168.1.1')
+        self.assertEqual(capture[ICMP].id, 4000)
+        self.check_ip_checksum(capture)
+        self.check_icmp_checksum(capture)
+
+    def tearDown(self):
+        super(TestDSlite, self).tearDown()
+        if not self.vpp_dead:
+            self.logger.info(self.vapi.cli("show dslite pool"))
+            self.logger.info(
+                self.vapi.cli("show dslite aftr-tunnel-endpoint-address"))
+            self.logger.info(self.vapi.cli("show dslite sessions"))
+
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)
index d84012b..468cf83 100644 (file)
@@ -1647,6 +1647,34 @@ class VppPapiProvider(object):
         """
         return self.api(self.papi.nat64_prefix_dump, {})
 
         """
         return self.api(self.papi.nat64_prefix_dump, {})
 
+    def dslite_set_aftr_addr(self, ip6, ip4):
+        """Set DS-Lite AFTR addresses
+
+        :param ip4: IPv4 address
+        :param ip6: IPv6 address
+        """
+        return self.api(
+            self.papi.dslite_set_aftr_addr,
+            {'ip4_addr': ip4,
+             'ip6_addr': ip6})
+
+    def dslite_add_del_pool_addr_range(
+            self,
+            start_addr,
+            end_addr,
+            is_add=1):
+        """Add/del address range to DS-Lite pool
+
+        :param start_addr: First IP address
+        :param end_addr: Last IP address
+        :param is_add: 1 if add, 0 if delete (Default value = 1)
+        """
+        return self.api(
+            self.papi.dslite_add_del_pool_addr_range,
+            {'start_addr': start_addr,
+             'end_addr': end_addr,
+             'is_add': is_add})
+
     def control_ping(self):
         self.api(self.papi.control_ping)
 
     def control_ping(self):
         self.api(self.papi.control_ping)