session: add auto sdl 46/41846/19
authorSteven Luong <[email protected]>
Mon, 18 Nov 2024 20:08:57 +0000 (12:08 -0800)
committerFlorin Coras <[email protected]>
Mon, 23 Dec 2024 21:28:08 +0000 (21:28 +0000)
New CLI to enable/disable auto-sdl (requires session enable rt-backend sdl)
auto-sdl <enable|disable> [threshold <n>] [remove-timeout <t>]

threshold is defined as the number of packets before the SDL entry is created to deny the source.

remove-timeout is defined as the duration to remove the SDL entry which was created earlier.

Type: feature

Change-Id: I513094a59663970beae33257006c652674643764
Signed-off-by: Steven Luong <[email protected]>
20 files changed:
MAINTAINERS
src/plugins/auto_sdl/CMakeLists.txt [new file with mode: 0644]
src/plugins/auto_sdl/FEATURE.yaml [new file with mode: 0644]
src/plugins/auto_sdl/auto_sdl.api [new file with mode: 0644]
src/plugins/auto_sdl/auto_sdl.c [new file with mode: 0644]
src/plugins/auto_sdl/auto_sdl.h [new file with mode: 0644]
src/plugins/auto_sdl/auto_sdl_api.c [new file with mode: 0644]
src/plugins/auto_sdl/plugin.c [new file with mode: 0644]
src/plugins/auto_sdl/test/auto_sdl_test.c [new file with mode: 0644]
src/plugins/unittest/session_test.c
src/vnet/session/session_cli.c
src/vnet/session/session_sdl.c
src/vnet/session/session_sdl.h
src/vnet/session/session_table.h
src/vnet/tcp/tcp.c
src/vnet/tcp/tcp.h
src/vnet/tcp/tcp_output.c
src/vnet/tcp/tcp_sdl.h [new file with mode: 0644]
test/asf/asfframework.py
test/asf/test_auto_sdl.py [new file with mode: 0644]

index 8c3ea68..b1065cd 100644 (file)
@@ -857,6 +857,12 @@ I: cjson
 M:     Ole Troan <[email protected]>
 F:     src/vppinfra/cJSON.[ch]
 
+Auto SDL
+I:     auto_sdl
+Y:     src/plugins/auto_sdl/FEATURE.yaml
+M:     Steven Luong <[email protected]>
+F:     src/plugins/auto_sdl
+
 VAT2
 I:     vat2
 M:     Ole Troan <[email protected]>
diff --git a/src/plugins/auto_sdl/CMakeLists.txt b/src/plugins/auto_sdl/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0f73272
--- /dev/null
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright(c) 2024 Cisco Systems, Inc.
+
+add_vpp_plugin(auto_sdl
+  SOURCES
+  auto_sdl.c
+  auto_sdl.h
+  plugin.c
+  auto_sdl_api.c
+
+  API_FILES
+  auto_sdl.api
+)
+
+add_vpp_plugin(auto_sdl_unittest
+  SOURCES
+  test/auto_sdl_test.c
+)
diff --git a/src/plugins/auto_sdl/FEATURE.yaml b/src/plugins/auto_sdl/FEATURE.yaml
new file mode 100644 (file)
index 0000000..8573197
--- /dev/null
@@ -0,0 +1,12 @@
+---
+name: Auto SDL
+maintainer: Steven Luong <[email protected]>
+features:
+  - Auto SDL
+description: "Automatically create a Source Deny List (SDL) entry when the
+            traffic arrived meets the specified threshold. An example usage
+            is in TCP -- when TCP is flooded with SYN packets by the same
+            source that exceed the threshold, an SDL entry is automatically
+            created to deny a new TCP session to be created."
+state: production
+properties: [API, CLI, MULTITHREAD]
diff --git a/src/plugins/auto_sdl/auto_sdl.api b/src/plugins/auto_sdl/auto_sdl.api
new file mode 100644 (file)
index 0000000..eeb43fb
--- /dev/null
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2024 Cisco Systems, Inc.
+ */
+
+option version = "1.0.0";
+
+/** \brief auto sdl config
+    @param client_index - opaque cookie to identify the sender
+    @param threshold - number of times to hit for an auto SDL entry is created
+    @param remove_timeout - timeout value for the auto SDL entries after they are created
+    @param enable - enable/disable
+  */
+autoreply define auto_sdl_config {
+  u32 client_index;
+  u32 context;
+  u32 threshold [default=5];
+  u32 remove_timeout [default=300];
+  bool enable;
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/auto_sdl/auto_sdl.c b/src/plugins/auto_sdl/auto_sdl.c
new file mode 100644 (file)
index 0000000..91e469b
--- /dev/null
@@ -0,0 +1,670 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2024 Cisco Systems, Inc.
+ */
+
+#include <vnet/session/session.h>
+#include <vnet/session/session_sdl.h>
+#include <vnet/tcp/tcp_sdl.h>
+#include <plugins/auto_sdl/auto_sdl.h>
+
+static auto_sdl_plugin_methods_t auto_sdl_plugin;
+static u32 *asdl_fib_index_to_table_index[2];
+static auto_sdl_main_t asdl_main;
+static auto_sdl_main_t *asdl = &asdl_main;
+
+VLIB_REGISTER_LOG_CLASS (auto_sdl_log, static) = { .class_name = "auto",
+                                                  .subclass_name = "sdl" };
+
+#define log_debug(fmt, ...)                                                   \
+  vlib_log_debug (auto_sdl_log.class, "%s: " fmt, __func__, __VA_ARGS__)
+#define log_warn(fmt, ...) vlib_log_warn (auto_sdl_log.class, fmt, __VA_ARGS__)
+#define log_err(fmt, ...)  vlib_log_err (auto_sdl_log.class, fmt, __VA_ARGS__)
+
+static auto_sdl_per_fib_t *
+auto_sdlb_get_for_fib_index (u32 fib_proto, u32 fib_index, int alloc)
+{
+  auto_sdl_per_fib_t *asdlb = 0;
+#define AUTO_SDL_FIB_INDEX_VALID_MASK 0x80000000
+
+  if (vec_len (asdl_fib_index_to_table_index[fib_proto]) > fib_index)
+    {
+      if (asdl_fib_index_to_table_index[fib_proto][fib_index] &
+         AUTO_SDL_FIB_INDEX_VALID_MASK)
+       asdlb = pool_elt_at_index (
+         asdl->asdl_pool,
+         (asdl_fib_index_to_table_index[fib_proto][fib_index] &
+          ~AUTO_SDL_FIB_INDEX_VALID_MASK));
+    }
+  if (alloc && !asdlb)
+    {
+      vec_validate (asdl_fib_index_to_table_index[fib_proto], fib_index);
+      pool_get_zero (asdl->asdl_pool, asdlb);
+      asdl_fib_index_to_table_index[fib_proto][fib_index] =
+       (AUTO_SDL_FIB_INDEX_VALID_MASK | (asdlb - asdl->asdl_pool));
+    }
+
+  return asdlb;
+}
+
+static void
+auto_sdl_add_del (auto_sdl_mapping_t *mapping, u32 is_add)
+{
+  session_rule_add_del_args_t args;
+  session_table_t *st;
+  u32 fib_proto = mapping->prefix.fp_proto;
+
+  st = session_table_get_for_fib_index (fib_proto, mapping->fib_index);
+  if (st == 0)
+    {
+      log_err ("Skipping add/del an SDL entry %%U: session table not found"
+              "for FIB index %u",
+              format_ip46_address, &mapping->prefix.fp_addr, IP46_TYPE_ANY,
+              mapping->fib_index);
+      return;
+    }
+
+  memset (&args, 0, sizeof (args));
+  args.transport_proto = TRANSPORT_PROTO_TCP;
+
+  clib_memcpy (&args.table_args.rmt, &mapping->prefix,
+              sizeof (args.table_args.rmt));
+  args.table_args.action_index = mapping->action_index;
+  args.table_args.is_add = is_add;
+  args.table_args.tag = mapping->tag;
+  args.appns_index = *vec_elt_at_index (st->appns_index, 0);
+  args.scope = SESSION_RULE_SCOPE_GLOBAL;
+  log_debug ("%s sdl entry %U, appns_index %d", is_add ? "added" : "deleted",
+            format_ip46_address, &args.table_args.rmt.fp_addr, IP46_TYPE_ANY,
+            args.appns_index);
+  vnet_session_rule_add_del (&args);
+}
+
+static void
+auto_sdl_free_mapping (auto_sdl_mapping_t *mapping)
+{
+  u32 is_ip6 = (mapping->prefix.fp_proto == FIB_PROTOCOL_IP6) ? 1 : 0;
+  auto_sdl_per_fib_t *asdlb = auto_sdlb_get_for_fib_index (
+    mapping->prefix.fp_proto, mapping->fib_index, 0);
+
+  if (!asdlb)
+    return;
+  if (is_ip6)
+    hash_unset_mem_free (&asdlb->auto_sdl_fib_pool,
+                        &mapping->prefix.fp_addr.ip6);
+  else
+    hash_unset (asdlb->auto_sdl_fib_pool, mapping->prefix.fp_addr.ip4.as_u32);
+  vec_free (mapping->tag);
+  pool_put (asdl->auto_sdl_pool, mapping);
+}
+
+static void
+auto_sdl_cleanup_by_fib_index (u32 fib_proto, u32 fib_index)
+{
+  hash_pair_t *p;
+  auto_sdl_mapping_t *mapping;
+  auto_sdl_per_fib_t *asdlb =
+    auto_sdlb_get_for_fib_index (fib_proto, fib_index, 0);
+  uword *entry_indicies = NULL, *entry;
+
+  if (!asdlb)
+    return;
+  hash_foreach_pair (p, asdlb->auto_sdl_fib_pool,
+                    ({ vec_add1 (entry_indicies, p->value[0]); }));
+  /* Block the worker threads trying to access the lock */
+  clib_spinlock_lock_if_init (&asdl->spinlock);
+  vec_foreach (entry, entry_indicies)
+    {
+      mapping = pool_elt_at_index (asdl->auto_sdl_pool, *entry);
+      auto_sdl_add_del (mapping, 0);
+      TW (tw_timer_stop) (&asdl->tw_wheel, mapping->tw_handle);
+      auto_sdl_free_mapping (mapping);
+    }
+  vec_free (entry_indicies);
+  hash_free (asdlb->auto_sdl_fib_pool);
+  pool_put (asdl->asdl_pool, asdlb);
+  asdl_fib_index_to_table_index[fib_proto][fib_index] &=
+    ~AUTO_SDL_FIB_INDEX_VALID_MASK;
+  clib_spinlock_unlock_if_init (&asdl->spinlock);
+}
+
+static void
+auto_sdl_process_expired_timer (vlib_main_t *vm, u32 mi)
+{
+  u32 pool_index = mi & 0x3FFFFFFF;
+  auto_sdl_mapping_t *mapping =
+    pool_elt_at_index (asdl->auto_sdl_pool, pool_index);
+  u32 is_ip6 = (mapping->prefix.fp_proto == FIB_PROTOCOL_IP6) ? 1 : 0;
+
+  if ((mapping->counter >= asdl->threshold) &&
+      (clib_atomic_load_relax_n (&mapping->sdl_added) == 0))
+    {
+      auto_sdl_add_del (mapping, 1);
+      mapping->tw_handle =
+       TW (tw_timer_start) (&asdl->tw_wheel, mapping - asdl->auto_sdl_pool,
+                            is_ip6, asdl->remove_timeout);
+      clib_atomic_store_relax_n (&mapping->sdl_added, 1);
+    }
+  else
+    {
+      auto_sdl_add_del (mapping, 0);
+      auto_sdl_free_mapping (mapping);
+    }
+}
+
+static uword
+auto_sdl_process (vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
+{
+  u32 *expired = 0;
+  f64 period = 1.0;
+
+  while (1)
+    {
+      vlib_process_wait_for_event_or_clock (vm, period);
+
+      /* currently no signals are expected - just wait for clock */
+      (void) vlib_process_get_events (vm, 0);
+
+      expired = TW (tw_timer_expire_timers_vec) (&asdl->tw_wheel,
+                                                vlib_time_now (vm), expired);
+      if (vec_len (expired) > 0)
+       {
+         u32 *mi = 0;
+
+         clib_spinlock_lock_if_init (&asdl->spinlock);
+         vec_foreach (mi, expired)
+           auto_sdl_process_expired_timer (vm, mi[0]);
+         clib_spinlock_unlock_if_init (&asdl->spinlock);
+         vec_set_len (expired, 0);
+       }
+    }
+
+  /* unreachable */
+  return 0;
+}
+
+static void
+auto_sdl_init (void)
+{
+  if (asdl->inited)
+    return;
+  TW (tw_timer_wheel_init) (&asdl->tw_wheel, NULL, 1.0, ~0);
+  asdl->pid = vlib_process_create (vlib_get_main (), "auto sdl process",
+                                  auto_sdl_process, 16);
+  asdl->tw_wheel.last_run_time = vlib_time_now (vlib_get_main ());
+  if (vlib_get_thread_main ()->n_vlib_mains > 1)
+    clib_spinlock_init (&asdl->spinlock);
+  asdl->inited = 1;
+}
+
+/*
+ * May be called by worker thread
+ */
+static int
+auto_sdl_track_prefix (auto_sdl_track_prefix_args_t *args)
+{
+  auto_sdl_mapping_t *mapping;
+  uword *value;
+  vlib_main_t *vm = vlib_get_main ();
+  u32 is_ip6 = (args->prefix.fp_proto == FIB_PROTOCOL_IP6) ? 1 : 0;
+  u32 time_expired;
+  auto_sdl_per_fib_t *asdlb =
+    auto_sdlb_get_for_fib_index (args->prefix.fp_proto, args->fib_index, 1);
+
+  if (session_sdl_is_enabled () == 0)
+    {
+      log_err ("Skipping add an auto SDL entry %%U: session sdl not enabled",
+              format_ip46_address, &args->prefix.fp_addr, IP46_TYPE_ANY);
+      return -1;
+    }
+  if (asdlb == 0 || asdl->auto_sdl_enable == 0)
+    return -1;
+
+  /* Obtain the lock, preventing main thread from freeing the hash or mapping
+   * entries */
+  clib_spinlock_lock_if_init (&asdl->spinlock);
+  if (is_ip6)
+    {
+      if (asdlb->auto_sdl_fib_pool == 0)
+       asdlb->auto_sdl_fib_pool =
+         hash_create_mem (0, sizeof (ip46_address_t), sizeof (uword));
+      value =
+       hash_get_mem (asdlb->auto_sdl_fib_pool, &args->prefix.fp_addr.ip6);
+    }
+  else
+    value =
+      hash_get (asdlb->auto_sdl_fib_pool, args->prefix.fp_addr.ip4.as_u32);
+
+  if (value)
+    {
+      mapping = pool_elt_at_index (asdl->auto_sdl_pool, *value);
+      mapping->counter++;
+      if ((mapping->counter >= asdl->threshold) &&
+         (clib_atomic_load_relax_n (&mapping->sdl_added) == 0))
+       TW (tw_timer_update) (&asdl->tw_wheel, mapping->tw_handle, 0);
+    }
+  else
+    {
+      pool_get_zero (asdl->auto_sdl_pool, mapping);
+      mapping->counter = 1;
+      mapping->fib_index = args->fib_index;
+      mapping->action_index = args->action_index;
+      if (args->tag)
+       mapping->tag = vec_dup (args->tag);
+      clib_memcpy (&mapping->prefix, &args->prefix, sizeof (mapping->prefix));
+      if (is_ip6)
+       hash_set_mem_alloc (&asdlb->auto_sdl_fib_pool,
+                           &mapping->prefix.fp_addr.ip6,
+                           mapping - asdl->auto_sdl_pool);
+      else
+       hash_set (asdlb->auto_sdl_fib_pool, args->prefix.fp_addr.ip4.as_u32,
+                 mapping - asdl->auto_sdl_pool);
+      if (mapping->counter >= asdl->threshold)
+       time_expired = 1; // 0 interval is not allowed
+      else
+       time_expired = clib_atomic_load_relax_n (&asdl->remove_timeout);
+      mapping->tw_handle = TW (tw_timer_start) (
+       &asdl->tw_wheel, mapping - asdl->auto_sdl_pool, is_ip6, time_expired);
+    }
+  mapping->last_updated = vlib_time_now (vm);
+  clib_spinlock_unlock_if_init (&asdl->spinlock);
+
+  return 0;
+}
+
+static void
+auto_sdl_cleanup (void)
+{
+  auto_sdl_mapping_t *mapping;
+  int i, fib_index;
+  auto_sdl_per_fib_t *asdlb;
+
+  /* Block the worker threads trying to access the lock */
+  clib_spinlock_lock_if_init (&asdl->spinlock);
+  pool_foreach_index (i, asdl->auto_sdl_pool)
+    {
+      mapping = pool_elt_at_index (asdl->auto_sdl_pool, i);
+      auto_sdl_add_del (mapping, 0);
+      TW (tw_timer_stop) (&asdl->tw_wheel, mapping->tw_handle);
+      auto_sdl_free_mapping (mapping);
+    }
+  clib_spinlock_unlock_if_init (&asdl->spinlock);
+
+  vec_foreach_index (fib_index,
+                    asdl_fib_index_to_table_index[FIB_PROTOCOL_IP4])
+    {
+      if (asdl_fib_index_to_table_index[FIB_PROTOCOL_IP4][fib_index] &
+         AUTO_SDL_FIB_INDEX_VALID_MASK)
+       {
+         asdlb = pool_elt_at_index (
+           asdl->asdl_pool,
+           (asdl_fib_index_to_table_index[FIB_PROTOCOL_IP4][fib_index] &
+            ~AUTO_SDL_FIB_INDEX_VALID_MASK));
+         hash_free (asdlb->auto_sdl_fib_pool);
+         asdl_fib_index_to_table_index[FIB_PROTOCOL_IP4][fib_index] &=
+           ~AUTO_SDL_FIB_INDEX_VALID_MASK;
+         pool_put (asdl->asdl_pool, asdlb);
+       }
+    }
+  vec_foreach_index (fib_index,
+                    asdl_fib_index_to_table_index[FIB_PROTOCOL_IP6])
+    {
+      if (asdl_fib_index_to_table_index[FIB_PROTOCOL_IP6][fib_index] &
+         AUTO_SDL_FIB_INDEX_VALID_MASK)
+       {
+         asdlb = pool_elt_at_index (
+           asdl->asdl_pool,
+           (asdl_fib_index_to_table_index[FIB_PROTOCOL_IP6][fib_index] &
+            ~AUTO_SDL_FIB_INDEX_VALID_MASK));
+         hash_free (asdlb->auto_sdl_fib_pool);
+         asdl_fib_index_to_table_index[FIB_PROTOCOL_IP6][fib_index] &=
+           ~AUTO_SDL_FIB_INDEX_VALID_MASK;
+         pool_put (asdl->asdl_pool, asdlb);
+       }
+    }
+}
+
+static void
+auto_sdl_callback (int which, session_sdl_callback_t *args)
+{
+  switch (which)
+    {
+    case SESSION_SDL_CALLBACK_CONFIG_DISABLE:
+      {
+       auto_sdl_config_args_t arg = {
+         .enable = 0,
+       };
+       auto_sdl_config (&arg);
+      }
+      break;
+    case SESSION_SDL_CALLBACK_TABLE_CLEAN_UP:
+      auto_sdl_cleanup_by_fib_index (args->fib_proto, args->fib_index);
+      break;
+    default:
+      break;
+    }
+}
+
+clib_error_t *
+auto_sdl_config (auto_sdl_config_args_t *args)
+{
+  if (args->enable)
+    {
+      if (session_sdl_register_callbacks (auto_sdl_callback))
+       return clib_error_return (0, "error registering sdl callbacks");
+      auto_sdl_init ();
+      asdl->remove_timeout = args->remove_timeout;
+      asdl->threshold = args->threshold;
+      tcp_sdl_enable_disable (auto_sdl_track_prefix);
+    }
+  else
+    {
+      tcp_sdl_enable_disable (0);
+
+      /* clean up all auto-sdl entries */
+      auto_sdl_cleanup ();
+      session_sdl_deregister_callbacks (auto_sdl_callback);
+    }
+
+  asdl->auto_sdl_enable = args->enable;
+
+  return 0;
+}
+
+static clib_error_t *
+auto_sdl_command_fn (vlib_main_t *vm, unformat_input_t *input,
+                    vlib_cli_command_t *cmd)
+{
+  clib_error_t *error = 0;
+  auto_sdl_config_args_t args = {
+    .remove_timeout = AUTO_SDL_REMOVE_TIMEOUT,
+    .threshold = AUTO_SDL_THRESHOLD,
+    .enable = ~0,
+  };
+
+  if (session_sdl_is_enabled () == 0)
+    {
+      unformat_skip_line (input);
+      vlib_cli_output (vm, "session sdl engine is not enabled");
+      goto done;
+    }
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "enable"))
+       args.enable = 1;
+      else if (unformat (input, "disable"))
+       args.enable = 0;
+      else if (unformat (input, "threshold %d", &args.threshold))
+       ;
+      else if (unformat (input, "remove-timeout %d", &args.remove_timeout))
+       ;
+      else
+       {
+         vlib_cli_output (vm, "unknown input `%U'", format_unformat_error,
+                          input);
+         goto done;
+       }
+    }
+
+  if (args.enable == ~0)
+    {
+      vlib_cli_output (vm, "enable/disable must be entered");
+      goto done;
+    }
+
+  error = auto_sdl_config (&args);
+done:
+  return error;
+}
+
+VLIB_CLI_COMMAND (auto_sdl_enable_disable, static) = {
+  .path = "auto-sdl",
+  .short_help =
+    "auto-sdl <enable|disable> [threshold <n>] [remove-timeout <t>]",
+  .function = auto_sdl_command_fn,
+  .is_mp_safe = 1,
+};
+
+static void
+show_auto_sdl_header (vlib_main_t *vm)
+{
+  vlib_cli_output (vm, "%-43s %-6s %-7s %-9s %-8s %-9s", "Prefix", "Action",
+                  "Counter", "FIB Index", "Age Sec.", "TW Handle");
+}
+
+static void
+show_auto_sdl_map_entry (vlib_main_t *vm, auto_sdl_mapping_t *mapping, f64 now)
+{
+  vlib_cli_output (vm, "%-43U %-6u %-7u %-9u %-8.2f %-9u", format_fib_prefix,
+                  &mapping->prefix, mapping->action_index, mapping->counter,
+                  mapping->fib_index, now - mapping->last_updated,
+                  mapping->tw_handle);
+}
+
+static int
+auto_sdl_mapping_ip4_address_compare (ip4_address_t *a1, ip4_address_t *a2)
+{
+  return ((clib_net_to_host_u32 (a1->data_u32) >
+          clib_net_to_host_u32 (a2->data_u32)) ?
+           1 :
+           -1);
+}
+
+static int
+auto_sdl_mapping_ip6_address_compare (ip6_address_t *a1, ip6_address_t *a2)
+{
+  for (int i = 0; i < ARRAY_LEN (a1->as_u16); i++)
+    {
+      int cmp = (clib_net_to_host_u16 (a1->as_u16[i]) -
+                clib_net_to_host_u16 (a2->as_u16[i]));
+      if (cmp != 0)
+       return cmp;
+    }
+  return 0;
+}
+
+static int
+auto_sdl_map_entry_cmp_for_sort (void *i1, void *i2)
+{
+  uword *entry1 = i1, *entry2 = i2;
+  auto_sdl_mapping_t *mapping1 =
+    pool_elt_at_index (asdl->auto_sdl_pool, *entry1);
+  auto_sdl_mapping_t *mapping2 =
+    pool_elt_at_index (asdl->auto_sdl_pool, *entry2);
+  int cmp = 0;
+
+  switch (mapping1->prefix.fp_proto)
+    {
+    case FIB_PROTOCOL_IP4:
+      cmp = auto_sdl_mapping_ip4_address_compare (
+       &mapping1->prefix.fp_addr.ip4, &mapping2->prefix.fp_addr.ip4);
+      break;
+    case FIB_PROTOCOL_IP6:
+      cmp = auto_sdl_mapping_ip6_address_compare (
+       &mapping1->prefix.fp_addr.ip6, &mapping2->prefix.fp_addr.ip6);
+      break;
+    case FIB_PROTOCOL_MPLS:
+    default:
+      ASSERT (0);
+      cmp = 0;
+      break;
+    }
+
+  if (0 == cmp)
+    cmp = (mapping1->prefix.fp_len - mapping2->prefix.fp_len);
+  return (cmp);
+}
+
+static void
+show_auto_sdl_hash (vlib_main_t *vm, auto_sdl_per_fib_t *asdlb, f64 now)
+{
+  hash_pair_t *p;
+  auto_sdl_mapping_t *mapping;
+  uword *entry_indicies = NULL, *entry;
+
+  if (!asdlb)
+    return;
+  hash_foreach_pair (p, asdlb->auto_sdl_fib_pool,
+                    ({ vec_add1 (entry_indicies, p->value[0]); }));
+  vec_sort_with_function (entry_indicies, auto_sdl_map_entry_cmp_for_sort);
+  vec_foreach (entry, entry_indicies)
+    {
+      mapping = pool_elt_at_index (asdl->auto_sdl_pool, *entry);
+      show_auto_sdl_map_entry (vm, mapping, now);
+    }
+  vec_free (entry_indicies);
+}
+
+static void
+show_auto_sdl (vlib_main_t *vm, app_namespace_t *app_ns)
+{
+  f64 now = vlib_time_now (vm);
+  u32 fib_index;
+  auto_sdl_per_fib_t *asdlb;
+
+  show_auto_sdl_header (vm);
+  fib_index = app_namespace_get_fib_index (app_ns, FIB_PROTOCOL_IP4);
+  asdlb = auto_sdlb_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index, 0);
+  show_auto_sdl_hash (vm, asdlb, now);
+
+  fib_index = app_namespace_get_fib_index (app_ns, FIB_PROTOCOL_IP6);
+  asdlb = auto_sdlb_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index, 0);
+  show_auto_sdl_hash (vm, asdlb, now);
+}
+
+static void
+show_auto_sdl_one_entry (vlib_main_t *vm, app_namespace_t *app_ns,
+                        ip46_address_t *rmt_ip, u32 fib_proto)
+{
+  auto_sdl_mapping_t *mapping;
+  f64 now = vlib_time_now (vm);
+  u32 fib_index;
+  auto_sdl_per_fib_t *asdlb;
+  uword *value = 0;
+
+  show_auto_sdl_header (vm);
+  if (fib_proto == FIB_PROTOCOL_IP6)
+    {
+      fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
+      asdlb = auto_sdlb_get_for_fib_index (fib_proto, fib_index, 0);
+      if (asdlb)
+       value = hash_get_mem (asdlb->auto_sdl_fib_pool, &rmt_ip->ip6);
+    }
+  else
+    {
+      fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
+      asdlb = auto_sdlb_get_for_fib_index (fib_proto, fib_index, 0);
+      if (asdlb)
+       value = hash_get (asdlb->auto_sdl_fib_pool, rmt_ip->ip4.as_u32);
+    }
+  if (value)
+    {
+      mapping = pool_elt_at_index (asdl->auto_sdl_pool, *value);
+      show_auto_sdl_map_entry (vm, mapping, now);
+    }
+}
+
+static uword
+auto_sdl_pool_size (void)
+{
+  return (pool_elts (asdl->auto_sdl_pool));
+}
+
+static clib_error_t *
+show_auto_sdl_command_fn (vlib_main_t *vm, unformat_input_t *input,
+                         vlib_cli_command_t *cmd)
+{
+  ip46_address_t rmt_ip;
+  u8 show_one = 0;
+  app_namespace_t *app_ns = 0;
+  u8 *ns_id = 0, fib_proto = FIB_PROTOCOL_IP4;
+  int summary = 0;
+
+  if (session_sdl_is_enabled () == 0)
+    {
+      vlib_cli_output (vm, "session sdl engine is not enabled");
+      unformat_skip_line (input);
+      goto done;
+    }
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "appns %_%s%_", &ns_id))
+       ;
+      else if (unformat (input, "%U", unformat_ip4_address, &rmt_ip.ip4))
+       show_one = 1;
+      else if (unformat (input, "%U", unformat_ip6_address, &rmt_ip.ip6))
+       {
+         fib_proto = FIB_PROTOCOL_IP6;
+         show_one = 1;
+       }
+      else if (unformat (input, "summary"))
+       summary = 1;
+      else
+       {
+         vlib_cli_output (vm, "unknown input `%U'", format_unformat_error,
+                          input);
+         goto done;
+       }
+    }
+
+  if (ns_id)
+    {
+      app_ns = app_namespace_get_from_id (ns_id);
+      if (!app_ns)
+       {
+         vlib_cli_output (vm, "appns %v doesn't exist", ns_id);
+         goto done;
+       }
+    }
+  else
+    app_ns = app_namespace_get_default ();
+
+  if (summary)
+    vlib_cli_output (vm, "total number of auto-sdl entries: %lu",
+                    auto_sdl_pool_size ());
+  else if (show_one)
+    show_auto_sdl_one_entry (vm, app_ns, &rmt_ip, fib_proto);
+  else
+    show_auto_sdl (vm, app_ns);
+
+done:
+  vec_free (ns_id);
+  return 0;
+}
+
+VLIB_CLI_COMMAND (show_auto_sdl_command, static) = {
+  .path = "show auto-sdl",
+  .short_help = "show auto-sdl [appns <id>] [<rmt-ip>]|[summary]",
+  .function = show_auto_sdl_command_fn,
+  .is_mp_safe = 1,
+};
+
+__clib_export clib_error_t *
+auto_sdl_plugin_methods_vtable_init (auto_sdl_plugin_methods_t *m)
+{
+  m->p_asdl_main = asdl;
+#define _(name) m->name = auto_sdl_##name;
+  foreach_auto_sdl_plugin_exported_method_name
+#undef _
+    return 0;
+}
+
+static clib_error_t *
+auto_sdl_plugin_init (vlib_main_t *vm)
+{
+  clib_error_t *error = 0;
+
+  error = auto_sdl_plugin_exports_init (&auto_sdl_plugin);
+  return error;
+}
+
+VLIB_INIT_FUNCTION (auto_sdl_plugin_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/auto_sdl/auto_sdl.h b/src/plugins/auto_sdl/auto_sdl.h
new file mode 100644 (file)
index 0000000..c74a48b
--- /dev/null
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2024 Cisco Systems, Inc.
+ */
+
+#ifndef __AUTO_SDL_H__
+#define __AUTO_SDL_H__
+
+#include <vlib/unix/plugin.h>
+#include <vnet/fib/fib_types.h>
+#include <vnet/dpo/dpo.h>
+#include <vppinfra/tw_timer_4t_3w_256sl.h>
+
+typedef struct
+{
+  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+  fib_prefix_t prefix;
+  u32 action_index;
+  u32 fib_index;
+  u8 *tag;
+
+  u32 last_updated;
+  u32 tw_handle;
+  u32 counter;
+  u8 sdl_added;
+} auto_sdl_mapping_t;
+
+#define AUTO_SDL_REMOVE_TIMEOUT 300 /* 5 minutes */
+#define AUTO_SDL_THRESHOLD     5   /* 5 times */
+
+typedef struct auto_sdl_per_fib_
+{
+  uword *auto_sdl_fib_pool;
+} auto_sdl_per_fib_t;
+
+typedef struct auto_sdl_main
+{
+  u32 remove_timeout;
+  u32 threshold;
+  auto_sdl_mapping_t *auto_sdl_pool;
+  clib_spinlock_t spinlock;
+  TWT (tw_timer_wheel) tw_wheel;
+  u32 pid;
+  auto_sdl_per_fib_t *asdl_pool;
+  u8 inited;
+  u8 auto_sdl_enable;
+} auto_sdl_main_t;
+
+typedef struct _auto_sdl_config_args_t
+{
+  u32 threshold;
+  u32 remove_timeout;
+  i8 enable;
+} auto_sdl_config_args_t;
+
+clib_error_t *auto_sdl_config (auto_sdl_config_args_t *args);
+
+typedef uword (*auto_sdl_pool_size_fn_t) (void);
+typedef int (*auto_sdl_track_prefix_fn_t) (auto_sdl_track_prefix_args_t *args);
+typedef clib_error_t *(*auto_sdl_config_fn_t) (auto_sdl_config_args_t *args);
+
+#define foreach_auto_sdl_plugin_exported_method_name                          \
+  _ (track_prefix)                                                            \
+  _ (pool_size)                                                               \
+  _ (config)
+
+#define _(name) auto_sdl_##name##_fn_t name;
+typedef struct
+{
+  void *p_asdl_main;
+  foreach_auto_sdl_plugin_exported_method_name
+} auto_sdl_plugin_methods_t;
+#undef _
+
+#define AUTO_SDL_LOAD_SYMBOL_FROM_PLUGIN_TO(p, s, st)                         \
+  ({                                                                          \
+    st = vlib_get_plugin_symbol (p, #s);                                      \
+    if (!st)                                                                  \
+      return clib_error_return (0, "Plugin %s and/or symbol %s not found.",   \
+                               p, #s);                                       \
+  })
+
+typedef clib_error_t *(*auto_sdl_plugin_methods_vtable_init_fn_t) (
+  auto_sdl_plugin_methods_t *m);
+
+__clib_export clib_error_t *
+auto_sdl_plugin_methods_vtable_init (auto_sdl_plugin_methods_t *m);
+
+static inline clib_error_t *
+auto_sdl_plugin_exports_init (auto_sdl_plugin_methods_t *m)
+{
+  auto_sdl_plugin_methods_vtable_init_fn_t mvi;
+
+  AUTO_SDL_LOAD_SYMBOL_FROM_PLUGIN_TO (
+    "auto_sdl_plugin.so", auto_sdl_plugin_methods_vtable_init, mvi);
+
+  return (mvi (m));
+}
+
+#endif /* __AUTO_SDL_H__ */
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/auto_sdl/auto_sdl_api.c b/src/plugins/auto_sdl/auto_sdl_api.c
new file mode 100644 (file)
index 0000000..dc150da
--- /dev/null
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2024 Cisco Systems, Inc.
+ */
+
+#include <vlibmemory/api.h>
+#include <vnet/session/session.h>
+#include <vnet/session/session_sdl.h>
+#include <vnet/tcp/tcp_sdl.h>
+#include <auto_sdl/auto_sdl.h>
+#include <auto_sdl/auto_sdl.api_enum.h>
+#include <auto_sdl/auto_sdl.api_types.h>
+
+static u16 msg_id_base;
+
+#define REPLY_MSG_ID_BASE msg_id_base
+
+#include <vlibapi/api_helper_macros.h>
+
+static void
+vl_api_auto_sdl_config_t_handler (vl_api_auto_sdl_config_t *mp)
+{
+  vl_api_auto_sdl_config_reply_t *rmp;
+  auto_sdl_config_args_t args;
+  int rv = 0;
+
+  if ((session_sdl_is_enabled () == 0))
+    {
+      rv = VNET_API_ERROR_FEATURE_DISABLED;
+      goto done;
+    }
+
+  args.threshold = clib_host_to_net_u32 (mp->threshold);
+  args.remove_timeout = clib_host_to_net_u32 (mp->remove_timeout);
+  args.enable = mp->enable;
+  auto_sdl_config (&args);
+
+done:
+  REPLY_MACRO (VL_API_AUTO_SDL_CONFIG_REPLY);
+}
+
+#include <auto_sdl/auto_sdl.api.c>
+static clib_error_t *
+auto_sdl_api_hookup (vlib_main_t *vm)
+{
+  api_main_t *am = vlibapi_get_main ();
+
+  /*
+   * Set up the (msg_name, crc, message-id) table
+   */
+  REPLY_MSG_ID_BASE = setup_message_id_table ();
+
+  vl_api_set_msg_thread_safe (am, REPLY_MSG_ID_BASE + VL_API_AUTO_SDL_CONFIG,
+                             1);
+  vl_api_set_msg_thread_safe (
+    am, REPLY_MSG_ID_BASE + VL_API_AUTO_SDL_CONFIG_REPLY, 1);
+  return 0;
+}
+
+VLIB_API_INIT_FUNCTION (auto_sdl_api_hookup);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/auto_sdl/plugin.c b/src/plugins/auto_sdl/plugin.c
new file mode 100644 (file)
index 0000000..a401fc7
--- /dev/null
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2024 Cisco Systems, Inc.
+ */
+
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+
+VLIB_PLUGIN_REGISTER () = {
+  .version = VPP_BUILD_VER,
+  .description = "Auto SDL",
+};
diff --git a/src/plugins/auto_sdl/test/auto_sdl_test.c b/src/plugins/auto_sdl/test/auto_sdl_test.c
new file mode 100644 (file)
index 0000000..690ebff
--- /dev/null
@@ -0,0 +1,270 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2024 Cisco Systems, Inc.
+ */
+
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+#include <arpa/inet.h>
+#include <vnet/session/session.h>
+#include <vnet/session/session_rules_table.h>
+#include <vnet/tcp/tcp_sdl.h>
+#include <plugins/auto_sdl/auto_sdl.h>
+
+#define AUTO_SDL_TEST_I(_cond, _comment, _args...)                            \
+  ({                                                                          \
+    int _evald = (_cond);                                                     \
+    if (!(_evald))                                                            \
+      {                                                                       \
+       fformat (stderr, "FAIL:%d: " _comment "\n", __LINE__, ##_args);       \
+      }                                                                       \
+    else                                                                      \
+      {                                                                       \
+       fformat (stderr, "PASS:%d: " _comment "\n", __LINE__, ##_args);       \
+      }                                                                       \
+    _evald;                                                                   \
+  })
+
+#define AUTO_SDL_TEST(_cond, _comment, _args...)                              \
+  {                                                                           \
+    if (!AUTO_SDL_TEST_I (_cond, _comment, ##_args))                          \
+      {                                                                       \
+       return 1;                                                             \
+      }                                                                       \
+  }
+
+static void
+auto_sdl_test_disable_rt_backend_engine (vlib_main_t *vm)
+{
+  session_enable_disable_args_t args = { .is_en = 0,
+                                        .rt_engine_type =
+                                          RT_BACKEND_ENGINE_DISABLE };
+  vnet_session_enable_disable (vm, &args);
+}
+
+static void
+auto_sdl_test_enable_sdl_engine (vlib_main_t *vm)
+{
+  session_enable_disable_args_t args = { .is_en = 1,
+                                        .rt_engine_type =
+                                          RT_BACKEND_ENGINE_SDL };
+  vnet_session_enable_disable (vm, &args);
+}
+
+static int
+auto_sdl_test_auto_sdl (vlib_main_t *vm, unformat_input_t *input)
+{
+  u32 rmt_plen = 0;
+  ip46_address_t rmt_ip = {};
+  int fib_proto = ~0;
+  u8 *ns_id = 0;
+  auto_sdl_track_prefix_args_t args;
+  app_namespace_t *app_ns;
+  u8 *tag = 0;
+  u32 action = 0;
+  int error = 0;
+  auto_sdl_config_args_t asdl_args = {
+    .enable = 1,
+    .remove_timeout = 300,
+    .threshold = 1,
+  };
+  auto_sdl_plugin_methods_t auto_sdl_plugin;
+  clib_error_t *init_res;
+
+  auto_sdl_test_disable_rt_backend_engine (vm);
+  auto_sdl_test_enable_sdl_engine (vm);
+  if (session_sdl_is_enabled () == 0)
+    {
+      vlib_cli_output (vm, "session sdl engine is not enabled");
+      return -1;
+    }
+  init_res = auto_sdl_plugin_exports_init (&auto_sdl_plugin);
+  if (init_res)
+    {
+      vlib_cli_output (vm, "Error in auto sdl plugin init");
+      return -1;
+    }
+  auto_sdl_plugin.config (&asdl_args);
+
+  if (unformat_check_input (input) == UNFORMAT_END_OF_INPUT)
+    {
+      const char ip_str[] = "10.1.0.0";
+      const char ip6_str[] = "2501:0db8:85a3:0000:0000:8a2e:0371:0";
+      u32 address;
+      ip6_address_t address6;
+      memset (&args, 0, sizeof (args));
+      rmt_plen = 32;
+      fib_proto = FIB_PROTOCOL_IP4;
+      app_ns = app_namespace_get_default ();
+      inet_pton (AF_INET, ip_str, &address);
+      address = htonl (address);
+      for (int j = 1; j <= 10; j++)
+       {
+         address += (j << 8);
+         for (int i = 1; i < 255; i++)
+           {
+             address++;
+             rmt_ip.ip4.as_u32 = ntohl (address);
+             args.prefix.fp_addr = rmt_ip;
+             args.prefix.fp_proto = fib_proto;
+             args.prefix.fp_len = rmt_plen;
+             args.action_index = action;
+             args.tag = tag;
+             args.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
+
+             if (auto_sdl_plugin.track_prefix (&args) != 0)
+               {
+                 vlib_cli_output (vm, "error adding track prefix");
+                 error = -1;
+                 goto done;
+               }
+           }
+       }
+      /* Add ip6 */
+      inet_pton (AF_INET6, ip6_str, &address6);
+      fib_proto = FIB_PROTOCOL_IP6;
+      rmt_plen = 128;
+      for (int i = 1; i < 255; i++)
+       {
+         address = htonl (address6.as_u32[3]);
+         address++;
+         address6.as_u32[3] = ntohl (address);
+         memcpy (&rmt_ip.ip6, &address6, sizeof (address6));
+         args.prefix.fp_addr = rmt_ip;
+         args.prefix.fp_proto = fib_proto;
+         args.prefix.fp_len = rmt_plen;
+         args.action_index = action;
+         args.tag = tag;
+         args.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
+
+         if (auto_sdl_plugin.track_prefix (&args) != 0)
+           {
+             vlib_cli_output (vm, "error adding track prefix");
+             error = -1;
+             goto done;
+           }
+       }
+
+      uword expected = 254 * 10 + 254;
+      uword total = auto_sdl_plugin.pool_size ();
+      AUTO_SDL_TEST ((total == expected),
+                    "total auto sdl entries is %u, expected %u", total,
+                    expected);
+      auto_sdl_test_disable_rt_backend_engine (vm);
+      total = auto_sdl_plugin.pool_size ();
+      expected = 0;
+      AUTO_SDL_TEST ((total == expected),
+                    "total auto sdl entries is %u, expected %u", total,
+                    expected);
+      goto done;
+    }
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "%U/%d", unformat_ip4_address, &rmt_ip.ip4,
+                   &rmt_plen))
+       fib_proto = FIB_PROTOCOL_IP4;
+      else if (unformat (input, "%U/%d", unformat_ip6_address, &rmt_ip.ip6,
+                        &rmt_plen))
+       fib_proto = FIB_PROTOCOL_IP6;
+      else if (unformat (input, "action %d", &action))
+       ;
+      else if (unformat (input, "tag %_%v%_", &tag))
+       ;
+      else if (unformat (input, "appns %_%v%_", &ns_id))
+       ;
+      else
+       {
+         vlib_cli_output (vm, "unknown input `%U'", format_unformat_error,
+                          input);
+         error = -1;
+         goto done;
+       }
+    }
+
+  if (fib_proto == ~0)
+    {
+      vlib_cli_output (vm, "tracked prefix must be entered");
+      error = -1;
+      goto done;
+    }
+
+  if (vec_len (tag) > SESSION_RULE_TAG_MAX_LEN)
+    {
+      vlib_cli_output (vm, "tag too long (max u64)");
+      error = -1;
+      goto done;
+    }
+
+  if (ns_id)
+    {
+      app_ns = app_namespace_get_from_id (ns_id);
+      if (!app_ns)
+       {
+         vlib_cli_output (vm, "namespace %v does not exist", ns_id);
+         error = -1;
+         goto done;
+       }
+    }
+  else
+    app_ns = app_namespace_get_default ();
+
+  memset (&args, 0, sizeof (args));
+  args.prefix.fp_addr = rmt_ip;
+  args.prefix.fp_proto = fib_proto;
+  args.prefix.fp_len = rmt_plen;
+  args.action_index = action;
+  args.tag = tag;
+  args.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
+
+  if (auto_sdl_plugin.track_prefix (&args) != 0)
+    {
+      vlib_cli_output (vm, "error adding track prefix");
+      error = -1;
+    }
+done:
+  vec_free (ns_id);
+  vec_free (tag);
+  return error;
+
+  return 0;
+}
+
+static clib_error_t *
+auto_sdl_test_command_fn (vlib_main_t *vm, unformat_input_t *input,
+                         vlib_cli_command_t *cmd_arg)
+{
+  int res = 0;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "all"))
+       ;
+      res = auto_sdl_test_auto_sdl (vm, input);
+      goto done;
+    }
+
+done:
+  if (res)
+    return clib_error_return (0, "Auto SDL unit test failed");
+  return 0;
+}
+
+VLIB_CLI_COMMAND (auto_sdl_test_command, static) = {
+  .path = "test auto-sdl",
+  .short_help = "auto-sdl unit tests",
+  .function = auto_sdl_test_command_fn,
+};
+
+VLIB_PLUGIN_REGISTER () = {
+  .version = VPP_BUILD_VER,
+  .description = "Auto SDL - Unit Test",
+  .default_disabled = 1,
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index 7702e81..fe4664b 100644 (file)
@@ -2504,8 +2504,7 @@ done:
   return 0;
 }
 
-VLIB_CLI_COMMAND (tcp_test_command, static) =
-{
+VLIB_CLI_COMMAND (session_test_command, static) = {
   .path = "test session",
   .short_help = "internal session unit tests",
   .function = session_test,
index 0ed2876..aff102a 100644 (file)
@@ -976,7 +976,7 @@ static clib_error_t *
 session_enable_disable_fn (vlib_main_t * vm, unformat_input_t * input,
                           vlib_cli_command_t * cmd)
 {
-  session_enable_disable_args_t args;
+  session_enable_disable_args_t args = {};
   session_main_t *smm = &session_main;
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
@@ -1010,6 +1010,9 @@ session_enable_disable_fn (vlib_main_t * vm, unformat_input_t * input,
       return clib_error_return (
        0, "session is already enable. Must disable first");
 
+  if ((smm->is_enabled == 0) && (args.is_en == 0))
+    return clib_error_return (0, "session is already disabled");
+
   return vnet_session_enable_disable (vm, &args);
 }
 
index f1dfac4..a18674c 100644 (file)
@@ -29,8 +29,8 @@ VLIB_REGISTER_LOG_CLASS (session_sdl_log, static) = { .class_name = "session",
 #define log_err(fmt, ...)                                                     \
   vlib_log_err (session_sdl_log.class, fmt, __VA_ARGS__)
 
-static fib_source_t sdl_fib_src;
-static dpo_type_t sdl_dpo_type;
+static session_sdl_main_t sdl_main;
+static session_sdl_main_t *sdlm = &sdl_main;
 
 const static char *const *const session_sdl_dpo_nodes[DPO_PROTO_NUM] = {
   [DPO_PROTO_IP4] = (const char *const[]){ "ip4-drop", 0 },
@@ -89,7 +89,7 @@ session_sdl_lookup6 (u32 srtg_handle, u32 proto, ip6_address_t *lcl_ip,
     return SESSION_TABLE_INVALID_INDEX;
   lbi = ip6_fib_table_fwding_lookup (sdlb->ip6_fib_index, rmt_ip);
   dpo = load_balance_get_fwd_bucket (load_balance_get (lbi), 0);
-  if (dpo->dpoi_type != sdl_dpo_type)
+  if (dpo->dpoi_type != sdlm->dpo_type)
     return SESSION_TABLE_INVALID_INDEX;
   return (dpo->dpoi_index);
 }
@@ -107,7 +107,7 @@ session_sdl_lookup4 (u32 srtg_handle, u32 proto, ip4_address_t *lcl_ip,
     return SESSION_TABLE_INVALID_INDEX;
   lbi = ip4_fib_forwarding_lookup (sdlb->ip_fib_index, rmt_ip);
   dpo = load_balance_get_fwd_bucket (load_balance_get (lbi), 0);
-  if (dpo->dpoi_type != sdl_dpo_type)
+  if (dpo->dpoi_type != sdlm->dpo_type)
     return SESSION_TABLE_INVALID_INDEX;
   return (dpo->dpoi_index);
 }
@@ -184,7 +184,8 @@ session_sdl4_fib_table_show_one (session_rules_table_t *srt, u32 fib_index,
 
   fib = ip4_fib_get (fib_index);
   fei = ip4_fib_table_lookup (fib, address, mask_len);
-  if (fei != FIB_NODE_INDEX_INVALID && fib_entry_is_sourced (fei, sdl_fib_src))
+  if (fei != FIB_NODE_INDEX_INVALID &&
+      fib_entry_is_sourced (fei, sdlm->fib_src))
     {
       u8 *tag = session_rules_table_rule_tag (srt, fei, 1);
       fib_entry_t *fib_entry = fib_entry_get (fei);
@@ -206,7 +207,8 @@ session_sdl6_fib_table_show_one (session_rules_table_t *srt, u32 fib_index,
   fib_node_index_t fei;
 
   fei = ip6_fib_table_lookup (fib_index, address, mask_len);
-  if (fei != FIB_NODE_INDEX_INVALID && fib_entry_is_sourced (fei, sdl_fib_src))
+  if (fei != FIB_NODE_INDEX_INVALID &&
+      fib_entry_is_sourced (fei, sdlm->fib_src))
     {
       u8 *tag = session_rules_table_rule_tag (srt, fei, 0);
       fib_entry_t *fib_entry = fib_entry_get (fei);
@@ -264,7 +266,7 @@ session_sdl_table_init (session_table_t *st, u8 fib_proto)
       snprintf (name, sizeof (name), "sdl4 %s", app_ns->ns_id);
       sdlb->ip_table_id = ip_table_get_unused_id (FIB_PROTOCOL_IP4);
       sdlb->ip_fib_index = fib_table_find_or_create_and_lock_w_name (
-       FIB_PROTOCOL_IP4, sdlb->ip_table_id, sdl_fib_src, (const u8 *) name);
+       FIB_PROTOCOL_IP4, sdlb->ip_table_id, sdlm->fib_src, (const u8 *) name);
     }
 
   if (fib_proto == FIB_PROTOCOL_IP6 || all)
@@ -272,7 +274,8 @@ session_sdl_table_init (session_table_t *st, u8 fib_proto)
       snprintf (name, sizeof (name), "sdl6 %s", app_ns->ns_id);
       sdlb->ip6_table_id = ip_table_get_unused_id (FIB_PROTOCOL_IP6);
       sdlb->ip6_fib_index = fib_table_find_or_create_and_lock_w_name (
-       FIB_PROTOCOL_IP6, sdlb->ip6_table_id, sdl_fib_src, (const u8 *) name);
+       FIB_PROTOCOL_IP6, sdlb->ip6_table_id, sdlm->fib_src,
+       (const u8 *) name);
     }
 
   srt->rules_by_tag = hash_create_vec (0, sizeof (u8), sizeof (uword));
@@ -285,18 +288,33 @@ session_sdl_table_free (session_table_t *st, u8 fib_proto)
   session_rules_table_t *srt = srtg_handle_to_srt (st->srtg_handle, 0);
   session_sdl_block_t *sdlb;
   u8 all = fib_proto > FIB_PROTOCOL_IP6 ? 1 : 0;
+  u32 fib_index, appns_index = *vec_elt_at_index (st->appns_index, 0);
+  app_namespace_t *app_ns = app_namespace_get (appns_index);
+  session_sdl_callback_fn_t *cb;
 
   ASSERT (st->is_local == 0);
+  if (st->is_local == 1)
+    return;
   sdlb = &srt->sdl_block;
   if ((fib_proto == FIB_PROTOCOL_IP4 || all) && (sdlb->ip_fib_index != ~0))
     {
-      fib_table_flush (sdlb->ip_fib_index, FIB_PROTOCOL_IP4, sdl_fib_src);
-      fib_table_unlock (sdlb->ip_fib_index, FIB_PROTOCOL_IP4, sdl_fib_src);
+      fib_index = app_namespace_get_fib_index (app_ns, FIB_PROTOCOL_IP4);
+      session_sdl_callback_t args = { .fib_proto = FIB_PROTOCOL_IP4,
+                                     .fib_index = fib_index };
+      vec_foreach (cb, sdlm->sdl_callbacks)
+       (*cb) (SESSION_SDL_CALLBACK_TABLE_CLEAN_UP, &args);
+      fib_table_flush (sdlb->ip_fib_index, FIB_PROTOCOL_IP4, sdlm->fib_src);
+      fib_table_unlock (sdlb->ip_fib_index, FIB_PROTOCOL_IP4, sdlm->fib_src);
     }
   if ((fib_proto == FIB_PROTOCOL_IP6 || all) && (sdlb->ip6_fib_index != ~0))
     {
-      fib_table_flush (sdlb->ip6_fib_index, FIB_PROTOCOL_IP6, sdl_fib_src);
-      fib_table_unlock (sdlb->ip6_fib_index, FIB_PROTOCOL_IP6, sdl_fib_src);
+      fib_index = app_namespace_get_fib_index (app_ns, FIB_PROTOCOL_IP6);
+      session_sdl_callback_t args = { .fib_proto = FIB_PROTOCOL_IP6,
+                                     .fib_index = fib_index };
+      vec_foreach (cb, sdlm->sdl_callbacks)
+       (*cb) (SESSION_SDL_CALLBACK_TABLE_CLEAN_UP, &args);
+      fib_table_flush (sdlb->ip6_fib_index, FIB_PROTOCOL_IP6, sdlm->fib_src);
+      fib_table_unlock (sdlb->ip6_fib_index, FIB_PROTOCOL_IP6, sdlm->fib_src);
     }
 
   hash_free (srt->tags_by_rules);
@@ -345,8 +363,8 @@ session_sdl_add_del (u32 srtg_handle, u32 proto,
          err = SESSION_E_IPINUSE;
          goto done;
        }
-      dpo_set (&paths->dpo, sdl_dpo_type, dpo_proto, args->action_index);
-      fei = fib_table_entry_path_add2 (fib_index, &pfx, sdl_fib_src,
+      dpo_set (&paths->dpo, sdlm->dpo_type, dpo_proto, args->action_index);
+      fei = fib_table_entry_path_add2 (fib_index, &pfx, sdlm->fib_src,
                                       FIB_ENTRY_FLAG_EXCLUSIVE, paths);
       session_rules_table_add_tag (srt, args->tag, fei, is_ip4);
       dpo_reset (&paths->dpo);
@@ -364,7 +382,7 @@ session_sdl_add_del (u32 srtg_handle, u32 proto,
            }
        }
 
-      if (!fib_entry_is_sourced (fei, sdl_fib_src))
+      if (!fib_entry_is_sourced (fei, sdlm->fib_src))
        {
          err = SESSION_E_NOROUTE;
          goto done;
@@ -372,7 +390,7 @@ session_sdl_add_del (u32 srtg_handle, u32 proto,
 
       fib_entry_t *fib_entry = fib_entry_get (fei);
       pfx = fib_entry->fe_prefix;
-      fib_table_entry_special_remove (fib_index, &pfx, sdl_fib_src);
+      fib_table_entry_special_remove (fib_index, &pfx, sdlm->fib_src);
       session_rules_table_del_tag (srt, args->tag, is_ip4);
     }
 done:
@@ -393,16 +411,15 @@ static const session_rt_engine_vft_t session_sdl_vft = {
 };
 
 static void
-session_sdl_fib_init (void)
+session_sdl_init (void)
 {
-  static u32 session_fib_inited = 0;
-
-  if (session_fib_inited)
+  if (sdlm->sdl_inited)
     return;
-  session_fib_inited = 1;
-  sdl_fib_src = fib_source_allocate ("session sdl", FIB_SOURCE_PRIORITY_LOW,
-                                    FIB_SOURCE_BH_SIMPLE);
-  sdl_dpo_type =
+
+  sdlm->sdl_inited = 1;
+  sdlm->fib_src = fib_source_allocate ("session sdl", FIB_SOURCE_PRIORITY_LOW,
+                                      FIB_SOURCE_BH_SIMPLE);
+  sdlm->dpo_type =
     dpo_register_new_type (&session_sdl_dpo_vft, session_sdl_dpo_nodes);
 }
 
@@ -431,6 +448,9 @@ clib_error_t *
 session_sdl_enable_disable (int enable)
 {
   clib_error_t *error = 0;
+  session_sdl_callback_fn_t *cb;
+  session_sdl_callback_t args;
+  session_sdl_callback_fn_t *callbacks;
 
   if (enable)
     {
@@ -440,7 +460,7 @@ session_sdl_enable_disable (int enable)
          log_err ("error in enabling sdl: %U", format_clib_error, error);
          return error;
        }
-      session_sdl_fib_init ();
+      session_sdl_init ();
     }
   else
     {
@@ -449,6 +469,18 @@ session_sdl_enable_disable (int enable)
       error = session_rule_table_deregister_engine (&session_sdl_vft);
       if (error)
        log_err ("error in disabling sdl: %U", format_clib_error, error);
+
+      /*
+       * Disabling sdl also disables auto sdl.
+       * But first, make a copy of the callbacks since the callback function
+       * may delete the callbacks, deregistering.
+       */
+      callbacks = vec_dup (sdlm->sdl_callbacks);
+
+      clib_memset (&args, 0, sizeof (args));
+      vec_foreach (cb, callbacks)
+       (*cb) (SESSION_SDL_CALLBACK_CONFIG_DISABLE, &args);
+      vec_free (callbacks);
     }
 
   return error;
@@ -520,9 +552,8 @@ session_sdl_command_fn (vlib_main_t *vm, unformat_input_t *input,
        }
     }
   else
-    {
-      app_ns = app_namespace_get_default ();
-    }
+    app_ns = app_namespace_get_default ();
+
   appns_index = app_namespace_index (app_ns);
 
   if (is_add && !conn_set && action == 0)
@@ -563,7 +594,7 @@ done:
 
 VLIB_CLI_COMMAND (session_sdl_command, static) = {
   .path = "session sdl",
-  .short_help = "session sdl [add|del] [appns <ns_id>] <rmt-ip/plen> action "
+  .short_help = "session sdl <add|del> [appns <ns_id>] <rmt-ip/plen> action "
                "<action> [tag <tag>]",
   .function = session_sdl_command_fn,
   .is_mp_safe = 1,
@@ -672,7 +703,7 @@ session_sdl_table_walk4 (u32 srtg_handle, session_sdl_table_walk_fn_t fn,
   vec_foreach (fei, ctx.ifsw_indicies)
     {
       if (*fei != FIB_NODE_INDEX_INVALID &&
-         fib_entry_is_sourced (*fei, sdl_fib_src))
+         fib_entry_is_sourced (*fei, sdlm->fib_src))
        {
          u8 *tag = session_rules_table_rule_tag (srt, *fei, 1);
          fib_entry_t *fib_entry = fib_entry_get (*fei);
@@ -712,7 +743,7 @@ session_sdl_table_walk6 (u32 srtg_handle, session_sdl_table_walk_fn_t fn,
   vec_foreach (fei, ctx.entries)
     {
       if (*fei != FIB_NODE_INDEX_INVALID &&
-         fib_entry_is_sourced (*fei, sdl_fib_src))
+         fib_entry_is_sourced (*fei, sdlm->fib_src))
        {
          u8 *tag = session_rules_table_rule_tag (srt, *fei, 0);
          fib_entry_t *fib_entry = fib_entry_get (*fei);
@@ -730,6 +761,35 @@ session_sdl_table_walk6 (u32 srtg_handle, session_sdl_table_walk_fn_t fn,
   vec_free (ctx.entries);
 }
 
+int
+session_sdl_register_callbacks (session_sdl_callback_fn_t cb)
+{
+  int i;
+
+  vec_foreach_index (i, sdlm->sdl_callbacks)
+    {
+      if (cb == *vec_elt_at_index (sdlm->sdl_callbacks, i))
+       return -1;
+    }
+  vec_add1 (sdlm->sdl_callbacks, cb);
+  return 0;
+}
+
+void
+session_sdl_deregister_callbacks (session_sdl_callback_fn_t cb)
+{
+  int i;
+
+  vec_foreach_index (i, sdlm->sdl_callbacks)
+    {
+      if (cb == *vec_elt_at_index (sdlm->sdl_callbacks, i))
+       {
+         vec_del1 (sdlm->sdl_callbacks, i);
+         break;
+       }
+    }
+}
+
 VLIB_CLI_COMMAND (show_session_sdl_command, static) = {
   .path = "show session sdl",
   .short_help = "show session sdl [appns <id> <rmt-ip>]",
index 8d8b5b2..c5c4b40 100644 (file)
 #ifndef SRC_VNET_SESSION_SESSION_SDL_H_
 #define SRC_VNET_SESSION_SESSION_SDL_H_
 
+#include <vnet/fib/fib_types.h>
+#include <vnet/fib/fib_source.h>
+#include <vnet/dpo/dpo.h>
+
+typedef enum
+{
+  SESSION_SDL_CALLBACK_TABLE_CLEAN_UP,
+  SESSION_SDL_CALLBACK_CONFIG_DISABLE,
+} session_sdl_callback_event_t;
+
+typedef struct session_sdl_callback_
+{
+  union
+  {
+    /* For table clean up */
+    struct
+    {
+      u32 fib_proto;
+      u32 fib_index;
+    };
+  };
+} session_sdl_callback_t;
+
+typedef void (*session_sdl_callback_fn_t) (int which,
+                                          session_sdl_callback_t *args);
+typedef struct session_sdl_main
+{
+  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+  fib_source_t fib_src;
+  dpo_type_t dpo_type;
+  u8 sdl_inited;
+  session_sdl_callback_fn_t *sdl_callbacks;
+} session_sdl_main_t;
+
 clib_error_t *session_sdl_enable_disable (int enable);
 
 typedef void (*session_sdl_table_walk_fn_t) (u32 fei, ip46_address_t *lcl_ip,
@@ -25,6 +59,8 @@ void session_sdl_table_walk4 (u32 srtg_handle, session_sdl_table_walk_fn_t fn,
                              void *args);
 void session_sdl_table_walk6 (u32 srtg_handle, session_sdl_table_walk_fn_t fn,
                              void *args);
+int session_sdl_register_callbacks (session_sdl_callback_fn_t cb);
+void session_sdl_deregister_callbacks (session_sdl_callback_fn_t cb);
 
 #endif /* SRC_VNET_SESSION_SESSION_SDL_H_ */
 /*
index 126e849..e3d5c4d 100644 (file)
@@ -46,7 +46,7 @@ typedef struct _session_lookup_table
 
   /** For global tables only one fib proto is active. This is a
    * byproduct of fib table ids not necessarily being the same for
-   * identical fib idices of v4 and v6 fib protos */
+   * identical fib indices of v4 and v6 fib protos */
   u8 active_fib_proto;
   /* Required for pool_get_aligned(...) */
     CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
index 1afc079..aea4955 100644 (file)
@@ -1616,6 +1616,14 @@ tcp_punt_unknown (vlib_main_t * vm, u8 is_ip4, u8 is_add)
     tm->punt_unknown6 = is_add;
 }
 
+void
+tcp_sdl_enable_disable (tcp_sdl_cb_fn_t fp)
+{
+  tcp_main_t *tm = &tcp_main;
+
+  tm->sdl_cb = fp;
+}
+
 /**
  * Initialize default values for tcp parameters
  */
index 8676db4..8feac80 100644 (file)
@@ -25,6 +25,7 @@
 #include <vnet/tcp/tcp_sack.h>
 #include <vnet/tcp/tcp_bt.h>
 #include <vnet/tcp/tcp_cc.h>
+#include <vnet/tcp/tcp_sdl.h>
 
 typedef void (timer_expiration_handler) (tcp_connection_t * tc);
 
@@ -265,6 +266,8 @@ typedef struct _tcp_main
 
   /** message ID base for API */
   u16 msg_id_base;
+
+  tcp_sdl_cb_fn_t sdl_cb;
 } tcp_main_t;
 
 extern tcp_main_t tcp_main;
index dd1ec55..2fd20ac 100644 (file)
@@ -1282,6 +1282,32 @@ tcp_cc_init_rxt_timeout (tcp_connection_t * tc)
   tcp_recovery_on (tc);
 }
 
+static void
+tcp_check_syn_flood (tcp_connection_t *tc)
+{
+  tcp_main_t *tm = &tcp_main;
+  auto_sdl_track_prefix_args_t args = {};
+
+  if (tm->sdl_cb == 0)
+    return;
+
+  args.prefix.fp_addr = tc->c_rmt_ip;
+  if (tc->c_is_ip4)
+    {
+      args.prefix.fp_proto = FIB_PROTOCOL_IP4;
+      args.prefix.fp_len = 32;
+    }
+  else
+    {
+      args.prefix.fp_proto = FIB_PROTOCOL_IP6;
+      args.prefix.fp_len = 128;
+    }
+  args.fib_index = tc->c_fib_index;
+  args.action_index = 0;
+  args.tag = 0;
+  tm->sdl_cb (&args);
+}
+
 void
 tcp_timer_retransmit_handler (tcp_connection_t * tc)
 {
@@ -1397,6 +1423,8 @@ tcp_timer_retransmit_handler (tcp_connection_t * tc)
          tcp_connection_timers_reset (tc);
          tcp_program_cleanup (wrk, tc);
          tcp_worker_stats_inc (wrk, tr_abort, 1);
+
+         tcp_check_syn_flood (tc);
          return;
        }
 
diff --git a/src/vnet/tcp/tcp_sdl.h b/src/vnet/tcp/tcp_sdl.h
new file mode 100644 (file)
index 0000000..482881b
--- /dev/null
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2024 Cisco Systems, Inc.
+ */
+
+#ifndef _vnet_tcp_sdl_h_
+#define _vnet_tcp_sdl_h_
+
+typedef struct _auto_sdl_track_prefix_args
+{
+  fib_prefix_t prefix;
+  u8 *tag;
+  u32 action_index;
+  u32 fib_index;
+} auto_sdl_track_prefix_args_t;
+
+typedef int (*tcp_sdl_cb_fn_t) (auto_sdl_track_prefix_args_t *args);
+extern void tcp_sdl_enable_disable (tcp_sdl_cb_fn_t fp);
+
+#endif /* _vnet_tcp_sdl_h_ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index c9c2010..841a923 100644 (file)
@@ -459,6 +459,11 @@ class VppAsfTestCase(CPUInterface, unittest.TestCase):
                 "{",
                 "enable",
                 "}",
+                "plugin",
+                "auto_sdl_unittest_plugin.so",
+                "{",
+                "enable",
+                "}",
             ]
             + cls.extra_vpp_plugin_config
             + [
diff --git a/test/asf/test_auto_sdl.py b/test/asf/test_auto_sdl.py
new file mode 100644 (file)
index 0000000..6b101c5
--- /dev/null
@@ -0,0 +1,593 @@
+#!/usr/bin/env python3
+
+import subprocess
+import socket
+
+import unittest
+
+from asfframework import (
+    VppAsfTestCase,
+    VppTestRunner,
+    tag_fixme_vpp_workers,
+    tag_run_solo,
+)
+from config import config
+from ipaddress import IPv4Network, IPv6Network
+from vpp_acl import AclRule, VppAcl, VppAclInterface
+
+from vpp_ip_route import (
+    VppIpRoute,
+    VppRoutePath,
+    VppIpTable,
+)
+
+from vpp_papi import VppEnum
+
+
+VPP_TAP_IP4 = "8.8.8.1"
+VPP_TAP_IP6 = "2001::1"
+
+HOST_TAP_IP4 = "8.8.8.2"
+HOST_TAP_IP6 = "2001::2"
+SCALE_COUNT = 10
+
+
+@tag_fixme_vpp_workers
+class TestAutoSDLUnitTests(VppAsfTestCase):
+    """Auto SDL Unit Tests Case"""
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestAutoSDLUnitTests, cls).setUpClass()
+
+    @classmethod
+    def tearDownClass(cls):
+        super(TestAutoSDLUnitTests, cls).tearDownClass()
+
+    def setUp(self):
+        super(TestAutoSDLUnitTests, self).setUp()
+        self.vapi.session_enable_disable(is_enable=1)
+
+    def test_session(self):
+        """Auto SDL Unit Tests"""
+        error = self.vapi.cli("test auto-sdl all")
+
+        if error:
+            self.logger.critical(error)
+        self.assertNotIn("failed", error)
+
+    def tearDown(self):
+        super(TestAutoSDLUnitTests, self).tearDown()
+        self.vapi.session_enable_disable(is_enable=0)
+
+
+@tag_fixme_vpp_workers
[email protected](config.extended, "part of extended tests")
+class TestAutoSDL(VppAsfTestCase):
+    """Auto SDL Baasic Test Case"""
+
+    tcp_startup = ["syn-rcvd-time 1"]
+
+    @classmethod
+    def setUpClass(cls):
+        if cls.tcp_startup:
+            conf = "tcp {" + " ".join(cls.tcp_startup) + "}"
+            cls.extra_vpp_config = [conf]
+        super(TestAutoSDL, cls).setUpClass()
+
+    @classmethod
+    def tearDownClass(cls):
+        super(TestAutoSDL, cls).tearDownClass()
+
+    def setUp(self):
+        super(TestAutoSDL, self).setUp()
+
+        self.vapi.session_enable_disable_v2(
+            rt_engine_type=VppEnum.vl_api_rt_backend_engine_t.RT_BACKEND_ENGINE_API_SDL
+        )
+
+        # self.logger.info(self.vapi.cli("create tap host-ip4-addr HOST_TAP_IP/24"))
+        self.tap0 = self.vapi.tap_create_v3(
+            id=0,
+            host_ip4_prefix=HOST_TAP_IP4 + "/24",
+            host_ip4_prefix_set=True,
+            host_ip6_prefix=HOST_TAP_IP6 + "/64",
+            host_ip6_prefix_set=True,
+        )
+
+        # self.logger.info(self.vapi.cli("set interface state tap0 up"))
+        self.vapi.sw_interface_set_flags(sw_if_index=self.tap0.sw_if_index, flags=1)
+
+    def tearDown(self):
+        self.logger.info(
+            self.vapi.sw_interface_add_del_address(
+                is_add=0,
+                sw_if_index=self.tap0.sw_if_index,
+                prefix=VPP_TAP_IP4 + "/24",
+                del_all=1,
+            )
+        )
+        # self.logger.info(self.vapi.cli("set interface ip address tap0 VPP_TAP_IP6/64"))
+        self.logger.info(
+            self.vapi.sw_interface_add_del_address(
+                is_add=0,
+                sw_if_index=self.tap0.sw_if_index,
+                prefix=VPP_TAP_IP6 + "/64",
+                del_all=1,
+            )
+        )
+        self.logger.info(self.vapi.tap_delete_v2(self.tap0.sw_if_index))
+        self.logger.info(
+            self.vapi.session_enable_disable_v2(
+                rt_engine_type=VppEnum.vl_api_rt_backend_engine_t.RT_BACKEND_ENGINE_API_DISABLE
+            )
+        )
+        dump = self.vapi.session_sdl_v3_dump()
+        self.assertTrue(len(dump) == 0)
+        super(TestAutoSDL, self).tearDown()
+
+    def test_auto_sdl(self):
+        """Auto SDL test"""
+
+        # self.logger.info(self.vapi.cli("set interface ip address tap0 VPP_TAP_IP/24"))
+        self.vapi.sw_interface_add_del_address(
+            sw_if_index=self.tap0.sw_if_index, prefix=VPP_TAP_IP4 + "/24"
+        )
+        # self.logger.info(self.vapi.cli("set interface ip address tap0 VPP_TAP_IP6/64"))
+        self.vapi.sw_interface_add_del_address(
+            sw_if_index=self.tap0.sw_if_index, prefix=VPP_TAP_IP6 + "/64"
+        )
+
+        # start the cli server
+        self.logger.info("Starting cli sever")
+        self.logger.info(self.vapi.cli("http cli server"))
+
+        self.logger.info(
+            self.vapi.cli("http cli server uri http://::0/80 listener add")
+        )
+
+        # Test 1. No ACL. curl should work.
+        self.logger.info("Starting test 1")
+        for i in range(10):
+            try:
+                process = subprocess.run(
+                    [
+                        "curl",
+                        "--noproxy",
+                        "'*'",
+                        "http://" + VPP_TAP_IP4 + ":80/sh/version",
+                    ],
+                    capture_output=True,
+                    timeout=2,
+                )
+            except:
+                self.logger.info("timeout")
+            else:
+                break
+        self.assertEqual(0, process.returncode)
+        self.logger.info("Test 1 passed")
+
+        # Test 2. Add ACL to block the source.
+        rule = AclRule(
+            is_permit=0,
+            proto=6,
+            src_prefix=IPv4Network("8.8.0.0/16"),
+            dst_prefix=IPv4Network(VPP_TAP_IP4 + "/32"),
+            ports=80,
+        )
+        acl = VppAcl(self, rules=[rule])
+        acl.add_vpp_config()
+
+        # Apply the ACL on the interface output
+        # Auto SDL entry should be created and timed out accordingly
+        acl_if_e = VppAclInterface(
+            self, sw_if_index=self.tap0.sw_if_index, n_input=0, acls=[acl]
+        )
+        acl_if_e.add_vpp_config()
+
+        self.vapi.auto_sdl_config(threshold=2, remove_timeout=3, enable=True)
+
+        for i in range(2):
+            try:
+                process = subprocess.run(
+                    [
+                        "curl",
+                        "--noproxy",
+                        "'*'",
+                        "http://" + VPP_TAP_IP4 + ":80/sh/version",
+                    ],
+                    capture_output=True,
+                    timeout=2,
+                )
+            except:
+                self.logger.info("curl timeout -- as exepcted")
+
+        # check for the SDL entry
+        for i in range(10):
+            dump = self.vapi.session_sdl_v3_dump()
+            if len(dump) == 0:
+                self.sleep(1)
+        self.assertTrue(len(dump) > 0)
+        self.assertEqual(dump[0].rmt, IPv4Network(HOST_TAP_IP4 + "/32"))
+
+        # verify entry is timed out and removed eventually
+        for i in range(10):
+            dump = self.vapi.session_sdl_v3_dump()
+            if len(dump) > 0:
+                self.sleep(1)
+        self.assertTrue(len(dump) == 0)
+
+        acl_if_e.remove_vpp_config()
+        acl.remove_vpp_config()
+        self.logger.info("Test 2 passed")
+
+        # Test 3: Do the same for IPv6
+        process = subprocess.run(
+            [
+                "curl",
+                "-6",
+                "--noproxy",
+                "'*'",
+                "http://" + "[" + VPP_TAP_IP6 + "]" + ":80/sh/version",
+            ],
+            capture_output=True,
+        )
+        self.assertEqual(0, process.returncode)
+
+        rule = AclRule(
+            is_permit=0,
+            proto=6,
+            src_prefix=IPv6Network(HOST_TAP_IP6 + "/128"),
+            dst_prefix=IPv6Network(VPP_TAP_IP6 + "/128"),
+            ports=80,
+        )
+        acl = VppAcl(self, rules=[rule])
+        acl.add_vpp_config()
+
+        # Apply the ACL on the interface output
+        acl_if_e = VppAclInterface(
+            self, sw_if_index=self.tap0.sw_if_index, n_input=0, acls=[acl]
+        )
+        acl_if_e.add_vpp_config()
+
+        for i in range(2):
+            try:
+                process = subprocess.run(
+                    [
+                        "curl",
+                        "-6",
+                        "--noproxy",
+                        "'*'",
+                        "http://" + "[" + VPP_TAP_IP6 + "]" + ":80/sh/version",
+                    ],
+                    capture_output=True,
+                    timeout=2,
+                )
+            except:
+                self.logger.info("curl timeout -- as exepcted")
+
+        acl_if_e.remove_vpp_config()
+        acl.remove_vpp_config()
+
+        # verify the SDL entry is added
+        for i in range(5):
+            dump = self.vapi.session_sdl_v3_dump()
+            if len(dump) == 0:
+                self.sleep(1)
+        self.assertTrue(len(dump) > 0)
+        self.assertEqual(dump[0].rmt, IPv6Network(HOST_TAP_IP6 + "/128"))
+
+        # verify the entry is removed after timeout
+        for i in range(10):
+            dump = self.vapi.session_sdl_v3_dump()
+            if len(dump) > 0:
+                self.sleep(1)
+        self.assertEqual(len(dump), 0)
+        self.logger.info("Test 3 passed")
+
+        self.vapi.auto_sdl_config(enable=False)
+
+        # bring down the cli server
+        self.logger.info(self.vapi.cli("http cli server listener del"))
+        self.logger.info(
+            self.vapi.cli("http cli server uri http://::0/80 listener del")
+        )
+
+    def test_auto_sdl_appns_ip4(self):
+        """Auto SDL with appns test -- ip4"""
+
+        # Test tap0 in appns 1
+        table_id = 1
+        tbl = VppIpTable(self, table_id)
+        tbl.add_vpp_config()
+
+        # place tap0 to vrf 1
+        self.vapi.sw_interface_set_table(
+            self.tap0.sw_if_index, is_ipv6=0, vrf_id=table_id
+        )
+
+        # place tap0 to appns 1
+        self.vapi.app_namespace_add_del_v4(
+            namespace_id="1", secret=1, sw_if_index=self.tap0.sw_if_index
+        )
+        # configure ip4 address on tap0
+        self.vapi.sw_interface_add_del_address(
+            sw_if_index=self.tap0.sw_if_index, prefix=VPP_TAP_IP4 + "/24"
+        )
+
+        # start http cli server in appns 1
+        self.logger.info(self.vapi.cli("http cli server appns 1 secret 1"))
+
+        process = subprocess.run(
+            [
+                "curl",
+                "--noproxy",
+                "'*'",
+                "http://" + VPP_TAP_IP4 + ":80/sh/version",
+            ],
+            timeout=1,
+            capture_output=True,
+        )
+        self.assertEqual(0, process.returncode)
+
+        # Apply the ACL on the interface output
+        rule = AclRule(
+            is_permit=0,
+            proto=6,
+            src_prefix=IPv4Network("8.8.0.0/16"),
+            dst_prefix=IPv4Network(VPP_TAP_IP4 + "/32"),
+            ports=80,
+        )
+        acl = VppAcl(self, rules=[rule])
+        acl.add_vpp_config()
+
+        acl_if_e = VppAclInterface(
+            self, sw_if_index=self.tap0.sw_if_index, n_input=0, acls=[acl]
+        )
+        acl_if_e.add_vpp_config()
+
+        self.vapi.auto_sdl_config(threshold=1, remove_timeout=60, enable=True)
+        for i in range(10):
+            try:
+                process = subprocess.run(
+                    [
+                        "curl",
+                        "--noproxy",
+                        "'*'",
+                        "http://" + VPP_TAP_IP4 + ":80/sh/version",
+                    ],
+                    capture_output=True,
+                    timeout=1,
+                )
+            except:
+                self.logger.info("connect timeout -- as expected")
+            else:
+                dump = self.vapi.session_sdl_v3_dump()
+                if len(dump) == 0:
+                    self.sleep(1)
+                else:
+                    break
+
+        for i in range(60):
+            dump = self.vapi.session_sdl_v3_dump()
+            if len(dump) != 1:
+                self.sleep(1)
+        self.assertEqual(len(dump), 1)
+        self.assertEqual(dump[0].rmt, IPv4Network(HOST_TAP_IP4 + "/32"))
+        self.logger.info("Test 6 passed")
+
+        acl_if_e.remove_vpp_config()
+        acl.remove_vpp_config()
+
+        # bring down the cli server
+        self.logger.info(self.vapi.cli("http cli server listener del"))
+
+        # delete namespace
+        self.vapi.app_namespace_add_del_v4(
+            namespace_id="1",
+            secret=1,
+            sw_if_index=self.tap0.sw_if_index,
+            is_add=0,
+        )
+
+        # Disable auto sdl -- quicker than waiting the entry to timeout
+        self.vapi.auto_sdl_config(enable=False)
+
+        # disable session sdl
+        self.vapi.session_enable_disable_v2(
+            rt_engine_type=VppEnum.vl_api_rt_backend_engine_t.RT_BACKEND_ENGINE_API_DISABLE
+        )
+
+        dump = self.vapi.session_sdl_v3_dump()
+        self.assertTrue(len(dump) == 0)
+
+    def test_auto_sdl_appns_ip6(self):
+        """Auto SDL with appns test -- ip6"""
+
+        # Test tap0 in appns 1
+        table_id = 1
+        tbl = VppIpTable(self, table_id, is_ip6=1)
+        tbl.add_vpp_config()
+
+        # place tap0 to vrf 1
+        self.vapi.sw_interface_set_table(
+            self.tap0.sw_if_index, is_ipv6=1, vrf_id=table_id
+        )
+
+        # place tap0 to appns 1
+        self.vapi.app_namespace_add_del_v4(
+            namespace_id="1", secret=1, sw_if_index=self.tap0.sw_if_index
+        )
+        # configure ip6 address on tap0
+        self.vapi.sw_interface_add_del_address(
+            sw_if_index=self.tap0.sw_if_index, prefix=VPP_TAP_IP6 + "/64"
+        )
+
+        # start http cli server in appns 1
+        self.logger.info(
+            self.vapi.cli("http cli server appns 1 secret 1 uri http://::0/80")
+        )
+
+        self.sleep(3)
+        process = subprocess.run(
+            [
+                "curl",
+                "-6",
+                "--noproxy",
+                "'*'",
+                "http://" + "[" + VPP_TAP_IP6 + "]" + ":80/sh/version",
+            ],
+            timeout=5,
+            capture_output=True,
+        )
+        self.assertEqual(0, process.returncode)
+
+        # Apply the ACL on the interface output
+        rule = AclRule(
+            is_permit=0,
+            proto=6,
+            src_prefix=IPv6Network(HOST_TAP_IP6 + "/128"),
+            dst_prefix=IPv6Network(VPP_TAP_IP6 + "/128"),
+            ports=80,
+        )
+        acl = VppAcl(self, rules=[rule])
+        acl.add_vpp_config()
+
+        acl_if_e = VppAclInterface(
+            self, sw_if_index=self.tap0.sw_if_index, n_input=0, acls=[acl]
+        )
+        acl_if_e.add_vpp_config()
+
+        self.vapi.auto_sdl_config(threshold=1, remove_timeout=60, enable=True)
+        for i in range(10):
+            try:
+                process = subprocess.run(
+                    [
+                        "curl",
+                        "-6",
+                        "--noproxy",
+                        "'*'",
+                        "http://" + "[" + VPP_TAP_IP6 + "]" + ":80/sh/version",
+                    ],
+                    capture_output=True,
+                    timeout=1,
+                )
+            except:
+                self.logger.info("connect timeout -- as expected")
+            else:
+                dump = self.vapi.session_sdl_v3_dump()
+                if len(dump) == 0:
+                    self.sleep(1)
+                else:
+                    break
+
+        for i in range(60):
+            dump = self.vapi.session_sdl_v3_dump()
+            if len(dump) != 1:
+                self.sleep(1)
+        self.assertEqual(len(dump), 1)
+        self.assertEqual(dump[0].rmt, IPv6Network(HOST_TAP_IP6 + "/128"))
+        self.logger.info("Test 6 passed")
+
+        acl_if_e.remove_vpp_config()
+        acl.remove_vpp_config()
+
+        # bring down the cli server
+        self.logger.info(
+            self.vapi.cli("http cli server uri http://::0/80 listener del")
+        )
+
+        # delete namespace
+        self.vapi.app_namespace_add_del_v4(
+            namespace_id="1",
+            secret=1,
+            sw_if_index=self.tap0.sw_if_index,
+            is_add=0,
+        )
+
+    @unittest.skip("test disabled for auto sdl")
+    def test_auto_sdl_scale(self):
+        """Auto SDL scale test"""
+
+        # Test 4: Scale
+        # Send 250 packets from different sources. Should create 250 auto-SDL
+        # and SDL entries
+        # self.logger.info(self.vapi.cli("set interface ip address tap0 VPP_TAP_IP/24"))
+
+        self.vapi.sw_interface_add_del_address(
+            sw_if_index=self.tap0.sw_if_index, prefix=VPP_TAP_IP4 + "/24"
+        )
+
+        # start the cli server
+        self.logger.info("Starting cli sever")
+        self.logger.info(self.vapi.cli("http cli server"))
+
+        rule = AclRule(
+            is_permit=0,
+            proto=6,
+            src_prefix=IPv4Network("8.8.0.0/16"),
+            dst_prefix=IPv4Network(VPP_TAP_IP4 + "/32"),
+            ports=80,
+        )
+        acl = VppAcl(self, rules=[rule])
+        acl.add_vpp_config()
+
+        # Apply the ACL on the interface output
+        acl_if_e = VppAclInterface(
+            self, sw_if_index=self.tap0.sw_if_index, n_input=0, acls=[acl]
+        )
+        acl_if_e.add_vpp_config()
+
+        # set the remove_timeout to a large value. Otherwise, some entries may
+        # get timed out before we accumulate all of them for verification
+        self.vapi.auto_sdl_config(threshold=1, remove_timeout=300, enable=True)
+
+        for i in range(SCALE_COUNT):
+            prefix = "8.8.8.{0}".format(i + 3)
+            prefix_mask = "8.8.8.{0}/24".format(i + 3)
+            prefix_port = (prefix, 5000)
+            process = subprocess.run(
+                [
+                    "ip",
+                    "address",
+                    "add",
+                    prefix_mask,
+                    "dev",
+                    "tap0",
+                ],
+                capture_output=True,
+            )
+            self.assertEqual(process.returncode, 0)
+            for i in range(2):
+                try:
+                    s = socket.create_connection(
+                        (VPP_TAP_IP4, 80), timeout=0.5, source_address=prefix_port
+                    )
+                except:
+                    self.logger.info("connect timeout -- as exepcted")
+
+        # check for the SDL entry
+        for i in range(60):
+            dump = self.vapi.session_sdl_v3_dump()
+            if len(dump) != SCALE_COUNT:
+                self.sleep(1)
+
+        self.assertEqual(len(dump), SCALE_COUNT)
+        self.logger.info("Test 4 passed")
+
+        # Test 5: Disable auto-sdl
+        # It should clean up the Auto SDL and SDL entries immediately
+        self.logger.info(self.vapi.auto_sdl_config(enable=False))
+        dump = self.vapi.session_sdl_v3_dump()
+        self.assertEqual(len(dump), 0)
+
+        acl_if_e.remove_vpp_config()
+        acl.remove_vpp_config()
+        self.logger.info("Test 5 passed")
+
+        # bring down the cli server
+        self.vapi.cli("http cli server listener del")
+
+
+if __name__ == "__main__":
+    unittest.main(testRunner=VppTestRunner)