diff options
author | Steven Luong <sluong@cisco.com> | 2024-11-18 12:08:57 -0800 |
---|---|---|
committer | Florin Coras <florin.coras@gmail.com> | 2024-12-23 21:28:08 +0000 |
commit | 6d4dbd4f29d6789cf4ea799d0b2eb9d489fa339d (patch) | |
tree | 3c1b6b1d56bd86a1d6a99e36c020a81f54a43f8b | |
parent | 755690c6c31671bdce4771db04199e151c32c5d0 (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-- | MAINTAINERS | 6 | ||||
-rw-r--r-- | src/plugins/auto_sdl/CMakeLists.txt | 18 | ||||
-rw-r--r-- | src/plugins/auto_sdl/FEATURE.yaml | 12 | ||||
-rw-r--r-- | src/plugins/auto_sdl/auto_sdl.api | 25 | ||||
-rw-r--r-- | src/plugins/auto_sdl/auto_sdl.c | 670 | ||||
-rw-r--r-- | src/plugins/auto_sdl/auto_sdl.h | 106 | ||||
-rw-r--r-- | src/plugins/auto_sdl/auto_sdl_api.c | 67 | ||||
-rw-r--r-- | src/plugins/auto_sdl/plugin.c | 11 | ||||
-rw-r--r-- | src/plugins/auto_sdl/test/auto_sdl_test.c | 270 | ||||
-rw-r--r-- | src/plugins/unittest/session_test.c | 3 | ||||
-rw-r--r-- | src/vnet/session/session_cli.c | 5 | ||||
-rw-r--r-- | src/vnet/session/session_sdl.c | 122 | ||||
-rw-r--r-- | src/vnet/session/session_sdl.h | 36 | ||||
-rw-r--r-- | src/vnet/session/session_table.h | 2 | ||||
-rw-r--r-- | src/vnet/tcp/tcp.c | 8 | ||||
-rw-r--r-- | src/vnet/tcp/tcp.h | 3 | ||||
-rw-r--r-- | src/vnet/tcp/tcp_output.c | 28 | ||||
-rw-r--r-- | src/vnet/tcp/tcp_sdl.h | 27 | ||||
-rw-r--r-- | test/asf/asfframework.py | 5 | ||||
-rw-r--r-- | test/asf/test_auto_sdl.py | 593 |
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) |