summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Luong <sluong@cisco.com>2024-11-18 12:08:57 -0800
committerFlorin Coras <florin.coras@gmail.com>2024-12-23 21:28:08 +0000
commit6d4dbd4f29d6789cf4ea799d0b2eb9d489fa339d (patch)
tree3c1b6b1d56bd86a1d6a99e36c020a81f54a43f8b
parent755690c6c31671bdce4771db04199e151c32c5d0 (diff)
session: add auto sdl
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 <sluong@cisco.com>
-rw-r--r--MAINTAINERS6
-rw-r--r--src/plugins/auto_sdl/CMakeLists.txt18
-rw-r--r--src/plugins/auto_sdl/FEATURE.yaml12
-rw-r--r--src/plugins/auto_sdl/auto_sdl.api25
-rw-r--r--src/plugins/auto_sdl/auto_sdl.c670
-rw-r--r--src/plugins/auto_sdl/auto_sdl.h106
-rw-r--r--src/plugins/auto_sdl/auto_sdl_api.c67
-rw-r--r--src/plugins/auto_sdl/plugin.c11
-rw-r--r--src/plugins/auto_sdl/test/auto_sdl_test.c270
-rw-r--r--src/plugins/unittest/session_test.c3
-rw-r--r--src/vnet/session/session_cli.c5
-rw-r--r--src/vnet/session/session_sdl.c122
-rw-r--r--src/vnet/session/session_sdl.h36
-rw-r--r--src/vnet/session/session_table.h2
-rw-r--r--src/vnet/tcp/tcp.c8
-rw-r--r--src/vnet/tcp/tcp.h3
-rw-r--r--src/vnet/tcp/tcp_output.c28
-rw-r--r--src/vnet/tcp/tcp_sdl.h27
-rw-r--r--test/asf/asfframework.py5
-rw-r--r--test/asf/test_auto_sdl.py593
20 files changed, 1982 insertions, 35 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 8c3ea68dae5..b1065cdbb71 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -857,6 +857,12 @@ I: cjson
M: Ole Troan <ot@cisco.com>
F: src/vppinfra/cJSON.[ch]
+Auto SDL
+I: auto_sdl
+Y: src/plugins/auto_sdl/FEATURE.yaml
+M: Steven Luong <sluong@cisco.com>
+F: src/plugins/auto_sdl
+
VAT2
I: vat2
M: Ole Troan <ot@cisco.com>
diff --git a/src/plugins/auto_sdl/CMakeLists.txt b/src/plugins/auto_sdl/CMakeLists.txt
new file mode 100644
index 00000000000..0f73272a47d
--- /dev/null
+++ b/src/plugins/auto_sdl/CMakeLists.txt
@@ -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
index 00000000000..857319742e1
--- /dev/null
+++ b/src/plugins/auto_sdl/FEATURE.yaml
@@ -0,0 +1,12 @@
+---
+name: Auto SDL
+maintainer: Steven Luong <sluong@cisco.com>
+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
index 00000000000..eeb43fb9a2c
--- /dev/null
+++ b/src/plugins/auto_sdl/auto_sdl.api
@@ -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
index 00000000000..91e469bb9f0
--- /dev/null
+++ b/src/plugins/auto_sdl/auto_sdl.c
@@ -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
index 00000000000..c74a48b642b
--- /dev/null
+++ b/src/plugins/auto_sdl/auto_sdl.h
@@ -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
index 00000000000..dc150da6024
--- /dev/null
+++ b/src/plugins/auto_sdl/auto_sdl_api.c
@@ -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
index 00000000000..a401fc71db6
--- /dev/null
+++ b/src/plugins/auto_sdl/plugin.c
@@ -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
index 00000000000..690ebff7d11
--- /dev/null
+++ b/src/plugins/auto_sdl/test/auto_sdl_test.c
@@ -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:
+ */
diff --git a/src/plugins/unittest/session_test.c b/src/plugins/unittest/session_test.c
index 7702e817070..fe4664bda4a 100644
--- a/src/plugins/unittest/session_test.c
+++ b/src/plugins/unittest/session_test.c
@@ -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,
diff --git a/src/vnet/session/session_cli.c b/src/vnet/session/session_cli.c
index 0ed2876469b..aff102a6989 100644
--- a/src/vnet/session/session_cli.c
+++ b/src/vnet/session/session_cli.c
@@ -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);
}
diff --git a/src/vnet/session/session_sdl.c b/src/vnet/session/session_sdl.c
index f1dfac4e1ab..a18674cd83b 100644
--- a/src/vnet/session/session_sdl.c
+++ b/src/vnet/session/session_sdl.c
@@ -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>]",
diff --git a/src/vnet/session/session_sdl.h b/src/vnet/session/session_sdl.h
index 8d8b5b2d29e..c5c4b40f05c 100644
--- a/src/vnet/session/session_sdl.h
+++ b/src/vnet/session/session_sdl.h
@@ -16,6 +16,40 @@
#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_ */
/*
diff --git a/src/vnet/session/session_table.h b/src/vnet/session/session_table.h
index 126e849beae..e3d5c4db91c 100644
--- a/src/vnet/session/session_table.h
+++ b/src/vnet/session/session_table.h
@@ -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);
diff --git a/src/vnet/tcp/tcp.c b/src/vnet/tcp/tcp.c
index 1afc07918b7..aea49558882 100644
--- a/src/vnet/tcp/tcp.c
+++ b/src/vnet/tcp/tcp.c
@@ -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
*/
diff --git a/src/vnet/tcp/tcp.h b/src/vnet/tcp/tcp.h
index 8676db413a0..8feac807d59 100644
--- a/src/vnet/tcp/tcp.h
+++ b/src/vnet/tcp/tcp.h
@@ -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;
diff --git a/src/vnet/tcp/tcp_output.c b/src/vnet/tcp/tcp_output.c
index dd1ec555902..2fd20acf241 100644
--- a/src/vnet/tcp/tcp_output.c
+++ b/src/vnet/tcp/tcp_output.c
@@ -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
index 00000000000..482881b5b43
--- /dev/null
+++ b/src/vnet/tcp/tcp_sdl.h
@@ -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:
+ */
diff --git a/test/asf/asfframework.py b/test/asf/asfframework.py
index c9c20106086..841a923dc79 100644
--- a/test/asf/asfframework.py
+++ b/test/asf/asfframework.py
@@ -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
index 00000000000..6b101c54e5c
--- /dev/null
+++ b/test/asf/test_auto_sdl.py
@@ -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
+@unittest.skipUnless(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)