From: Mohammed Hawari Date: Fri, 10 Oct 2025 06:45:39 +0000 (+0200) Subject: sfdp_services: plugin with basic SFDP services X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F73%2F43873%2F8;p=vpp.git sfdp_services: plugin with basic SFDP services Change-Id: Id33e9f40fcc20d38c9749aada26a1b345b3ad027 Type: feature Signed-off-by: Mohammed Hawari --- diff --git a/MAINTAINERS b/MAINTAINERS index 6ba9e56a791..18e9ac38131 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -223,6 +223,11 @@ M: Mohammed Hawari M: Ole Troan F: src/vnet/sfdp/ +Plugin StateFul Data Plane Services +I: sfdp_services +M: Mohammed Hawari +F: src/plugins/sfdp_services + Plugin - Crypto - native I: crypto-native M: Damjan Marion diff --git a/src/plugins/sfdp_services/CMakeLists.txt b/src/plugins/sfdp_services/CMakeLists.txt new file mode 100644 index 00000000000..c8806d0497c --- /dev/null +++ b/src/plugins/sfdp_services/CMakeLists.txt @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2025 Cisco Systems, Inc. +# + +#include_directories(${CMAKE_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +add_vpp_plugin(sfdp_services + SOURCES + base/tcp-check/node.c + base/tcp-check/tcp_check.c + base/tcp-check/format.c + base/tcp-check/cli.c + base/tcp-check/api.c + + base/l4-lifecycle/node.c + + base/nat/nat.c + base/nat/cli.c + base/nat/api.c + base/nat/format.c + base/nat/fastpath_node.c + base/nat/slowpath_node.c + base/nat/external_input_node.c + + base/interface_input/interface_input.c + base/interface_input/cli.c + base/interface_input/api.c + base/interface_input/node.c + + acl/acl_sample.c + acl/cli.c + acl/node.c + + dot1q/dot1q.c + + geneve/gateway.c + geneve/cli.c + geneve/api.c + geneve/geneve_input/node.c + geneve/geneve_output/node.c + + reass/reass.c + reass/sv_reass_node.c + #reass/full_reass_node.c + + MULTIARCH_SOURCES + base/tcp-check/node.c + base/l4-lifecycle/node.c + base/interface_input/node.c + acl/node.c + geneve/geneve_input/node.c + geneve/geneve_output/node.c + dot1q/dot1q.c + reass/sv_reass_node.c + #reass/full_reass_node.c + + API_FILES + base/nat/nat.api + base/tcp-check/tcp_check.api + base/interface_input/interface_input.api + geneve/gateway.api +) \ No newline at end of file diff --git a/src/plugins/sfdp_services/acl/acl_sample.c b/src/plugins/sfdp_services/acl/acl_sample.c new file mode 100644 index 00000000000..a91ae4c305a --- /dev/null +++ b/src/plugins/sfdp_services/acl/acl_sample.c @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include + +static void +acl_sample_validate_tenant_lc (u16 tenant_idx) +{ + sfdp_acl_main_t *vam = &sfdp_acl_main; + sfdp_main_t *sfdp = &sfdp_main; + sfdp_tenant_t *tenant = sfdp_tenant_at_index (sfdp, tenant_idx); + u32 lc; + + if (vec_elt_at_index (vam->lc_by_tenant_idx, tenant_idx)[0] != ~0) + return; + + lc = acl_plugin.get_lookup_context_index (vam->acl_user_id, + tenant->tenant_id, 0); + vec_elt_at_index (vam->lc_by_tenant_idx, tenant_idx)[0] = lc; +}; + +clib_error_t * +sfdp_acl_sample_tenant_set_acl (sfdp_acl_main_t *vam, u64 tenant_id, + u32 acl_index, bool disable) +{ + sfdp_main_t *sfdp = &sfdp_main; + clib_error_t *err = 0; + clib_bihash_kv_8_8_t kv = { .key = tenant_id, .value = 0 }; + u16 tenant_idx; + u32 lc; + u32 *acl_vec = 0; + if (clib_bihash_search_inline_8_8 (&sfdp->tenant_idx_by_id, &kv)) + { + err = clib_error_return (0, "Invalid tenant id: %llu", tenant_id); + return err; + } + tenant_idx = kv.value; + acl_sample_validate_tenant_lc (tenant_idx); + lc = vam->lc_by_tenant_idx[tenant_idx]; + if (!disable) + vec_add1 (acl_vec, acl_index); + acl_plugin.set_acl_vec_for_context (lc, acl_vec); + vec_free (acl_vec); + return err; +} + +static clib_error_t * +acl_sample_init (vlib_main_t *vm) +{ + sfdp_acl_main_t *vam = &sfdp_acl_main; + sfdp_main_t *sfdp = &sfdp_main; + clib_error_t *err = acl_plugin_exports_init (&acl_plugin); + int i; + if (err) + return err; + + vam->acl_user_id = + acl_plugin.register_user_module ("sfdp ACL plugin", "tenant id", NULL); + + vec_validate (vam->lc_by_tenant_idx, (1ULL << sfdp->log2_tenants) - 1); + + vec_foreach_index (i, vam->lc_by_tenant_idx) + vam->lc_by_tenant_idx[i] = ~0; + + return 0; +} + +VLIB_MAIN_LOOP_ENTER_FUNCTION (acl_sample_init) = { + +}; + +sfdp_acl_main_t sfdp_acl_main; +acl_plugin_methods_t acl_plugin; \ No newline at end of file diff --git a/src/plugins/sfdp_services/acl/acl_sample.h b/src/plugins/sfdp_services/acl/acl_sample.h new file mode 100644 index 00000000000..f84d46b9fd2 --- /dev/null +++ b/src/plugins/sfdp_services/acl/acl_sample.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#ifndef __included_acl_sample_h__ +#define __included_acl_sample_h__ + +#include +#include +#include + +typedef struct +{ + u32 acl_user_id; + u32 *lc_by_tenant_idx; /* vec */ +} sfdp_acl_main_t; + +clib_error_t *sfdp_acl_sample_tenant_set_acl (sfdp_acl_main_t *vam, + u64 tenant_id, u32 acl_index, + bool disable); + +extern sfdp_acl_main_t sfdp_acl_main; +extern acl_plugin_methods_t acl_plugin; +#endif \ No newline at end of file diff --git a/src/plugins/sfdp_services/acl/cli.c b/src/plugins/sfdp_services/acl/cli.c new file mode 100644 index 00000000000..20591ca2980 --- /dev/null +++ b/src/plugins/sfdp_services/acl/cli.c @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include + +static clib_error_t * +sfdp_acl_sample_set_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + sfdp_acl_main_t *vam = &sfdp_acl_main; + unformat_input_t line_input_, *line_input = &line_input_; + + clib_error_t *err = 0; + u32 sw_if_index = ~0; + u32 tenant_id = ~0; + u32 acl_index = ~0; + u8 disable = 0; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "tenant %d", &tenant_id)) + ; + + else if (unformat (line_input, "acl_index %d", &acl_index)) + ; + else if (unformat (line_input, "disable")) + disable = 1; + else if (unformat (line_input, "%U", unformat_vnet_sw_interface, + vnet_get_main (), &sw_if_index)) + ; + else + { + err = unformat_parse_error (line_input); + goto done; + } + } + err = sfdp_acl_sample_tenant_set_acl (vam, tenant_id, acl_index, disable); +done: + unformat_free (line_input); + return err; +} + +VLIB_CLI_COMMAND (sfdp_acl_sample_set_cmd, static) = { + .path = "set sfdp acl", + .short_help = + "set sfdp acl tenant acl_index [disable]", + .function = sfdp_acl_sample_set_fn, +}; \ No newline at end of file diff --git a/src/plugins/sfdp_services/acl/node.c b/src/plugins/sfdp_services/acl/node.c new file mode 100644 index 00000000000..aba7f60f830 --- /dev/null +++ b/src/plugins/sfdp_services/acl/node.c @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include +#include + +#define foreach_sfdp_acl_sample_error _ (DROP, "drop") + +typedef enum +{ +#define _(sym, str) SFDP_ACL_SAMPLE_ERROR_##sym, + foreach_sfdp_acl_sample_error +#undef _ + SFDP_ACL_SAMPLE_N_ERROR, +} sfdp_acl_sample_error_t; + +static char *sfdp_acl_sample_error_strings[] = { +#define _(sym, string) string, + foreach_sfdp_acl_sample_error +#undef _ +}; + +typedef struct +{ + u32 thread_index; + u32 flow_id; + u8 matched; + u8 action; +} sfdp_acl_sample_trace_t; + +static u8 * +format_sfdp_acl_sample_trace (u8 *s, va_list *args) +{ + vlib_main_t __clib_unused *vm = va_arg (*args, vlib_main_t *); + vlib_node_t __clib_unused *node = va_arg (*args, vlib_node_t *); + sfdp_acl_sample_trace_t *t = va_arg (*args, sfdp_acl_sample_trace_t *); + const char *action_str[] = { "deny", "permit", "permit+reflect" }; + + s = format ( + s, "sfdp-acl-sample: flow-id %u (session %u, %s) status: %s, action: %s\n", + t->flow_id, t->flow_id >> 1, t->flow_id & 0x1 ? "reverse" : "forward", + t->matched ? "matched" : "unmatched", + t->matched ? action_str[t->action] : ""); + return s; +} + +SFDP_SERVICE_DECLARE (drop) +SFDP_SERVICE_DECLARE (sfdp_acl_sample) +static_always_inline void +sfdp_acl_sample_process_one (sfdp_acl_main_t *vam, sfdp_session_t *session, + u8 dir, u16 *to_next, vlib_buffer_t **b, + u8 *matched, u8 *action) +{ + u16 tenant_idx = session->tenant_idx; + u32 lc_index = vam->lc_by_tenant_idx[tenant_idx]; + fa_5tuple_opaque_t fa_5tuple; + u32 match_acl_index = ~0; + u32 match_acl_pos = ~0; + u32 match_rule_index = ~0; + u32 trace_bitmap = 0; + + if (lc_index == ~0) + goto end_of_packet; + + acl_plugin_fill_5tuple_inline (acl_plugin.p_acl_main, lc_index, b[0], 0, 1, + 0, &fa_5tuple); + + if (acl_plugin_match_5tuple_inline ( + acl_plugin.p_acl_main, lc_index, &fa_5tuple, 0, action, &match_acl_pos, + &match_acl_index, &match_rule_index, &trace_bitmap)) + { + matched[0] = 1; + if (action[0] == 0) + { + /* Drop this flow, in this direction, forever */ + session->bitmaps[dir] |= SFDP_SERVICE_MASK (drop); + sfdp_buffer (b[0])->service_bitmap = SFDP_SERVICE_MASK (drop); + } + else if (action[0] == 1) + /* Allow this packet only */ + ; + else + { + /* Allow this flow and cache the decision in forward and reverse + * direction */ + session->bitmaps[SFDP_FLOW_FORWARD] &= + ~SFDP_SERVICE_MASK (sfdp_acl_sample); + session->bitmaps[SFDP_FLOW_REVERSE] &= + ~SFDP_SERVICE_MASK (sfdp_acl_sample); + } + } + else + { + matched[0] = 0; + } + +end_of_packet: + sfdp_next (b[0], to_next); + return; +} + +static_always_inline u16 +sfdp_acl_sample_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame) +{ + + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + sfdp_acl_main_t *vam = &sfdp_acl_main; + u32 thread_index = vlib_get_thread_index (); + sfdp_session_t *session; + u32 session_idx; + u8 dir; + u32 *from = vlib_frame_vector_args (frame); + u32 n_left = frame->n_vectors; + u16 next_indices[VLIB_FRAME_SIZE], *to_next = next_indices; + u8 matched[VLIB_FRAME_SIZE], action[VLIB_FRAME_SIZE]; + u8 *m = matched; + u8 *a = action; + vlib_get_buffers (vm, from, bufs, n_left); + while (n_left > 0) + { + session_idx = sfdp_session_from_flow_index (b[0]->flow_id); + dir = sfdp_direction_from_flow_index (b[0]->flow_id); + session = sfdp_session_at_index (session_idx); + + sfdp_acl_sample_process_one (vam, session, dir, to_next, b, m, a); + n_left -= 1; + b += 1; + to_next += 1; + m += 1; + a += 1; + } + vlib_buffer_enqueue_to_next (vm, node, from, next_indices, frame->n_vectors); + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) + { + int i; + b = bufs; + m = matched; + a = action; + n_left = frame->n_vectors; + for (i = 0; i < n_left; i++) + { + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) + { + sfdp_acl_sample_trace_t *t = + vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->flow_id = b[0]->flow_id; + t->thread_index = thread_index; + t->matched = m[0]; + t->action = a[0]; + b++; + m++; + a++; + } + else + break; + } + } + return frame->n_vectors; +} + +VLIB_NODE_FN (sfdp_acl_sample_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return sfdp_acl_sample_inline (vm, node, frame); +} + +VLIB_REGISTER_NODE (sfdp_acl_sample_node) = { + .name = "sfdp-acl-sample", + .vector_size = sizeof (u32), + .format_trace = format_sfdp_acl_sample_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN (sfdp_acl_sample_error_strings), + .error_strings = sfdp_acl_sample_error_strings +}; + +SFDP_SERVICE_DEFINE (sfdp_acl_sample) = { + .node_name = "sfdp-acl-sample", + .runs_before = SFDP_SERVICES ("sfdp-geneve-output", "ip4-lookup"), + .runs_after = SFDP_SERVICES ("sfdp-drop", "sfdp-l4-lifecycle", + "sfdp-tcp-check"), + .is_terminal = 0 +}; + +SFDP_SERVICE_DEFINE (ip4_lookup) = { + .node_name = "ip4-lookup", + .runs_before = SFDP_SERVICES (0), + .runs_after = SFDP_SERVICES ("sfdp-drop", "sfdp-l4-lifecycle", + "sfdp-tcp-check"), + .is_terminal = 1 +}; \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/interface_input/api.c b/src/plugins/sfdp_services/base/interface_input/api.c new file mode 100644 index 00000000000..0811bae7513 --- /dev/null +++ b/src/plugins/sfdp_services/base/interface_input/api.c @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define REPLY_MSG_ID_BASE vim->msg_id_base +#include + +static void +vl_api_sfdp_interface_input_set_t_handler ( + vl_api_sfdp_interface_input_set_t *mp) +{ + sfdp_interface_input_main_t *vim = &sfdp_interface_input_main; + u32 sw_if_index = clib_net_to_host_u32 (mp->sw_if_index); + u32 tenant_id = clib_net_to_host_u32 (mp->tenant_id); + u8 unset = mp->is_disable; + clib_error_t *err = + sfdp_interface_input_set_tenant (vim, sw_if_index, tenant_id, unset); + int rv = err ? -1 : 0; + vl_api_sfdp_interface_input_set_reply_t *rmp; + REPLY_MACRO (VL_API_SFDP_INTERFACE_INPUT_SET_REPLY); +} + +#include +static clib_error_t * +sfdp_interface_input_api_hookup (vlib_main_t *vm) +{ + sfdp_interface_input_main_t *vim = &sfdp_interface_input_main; + vim->msg_id_base = setup_message_id_table (); + return 0; +} +VLIB_API_INIT_FUNCTION (sfdp_interface_input_api_hookup); +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/sfdp_services/base/interface_input/cli.c b/src/plugins/sfdp_services/base/interface_input/cli.c new file mode 100644 index 00000000000..3de9c24e245 --- /dev/null +++ b/src/plugins/sfdp_services/base/interface_input/cli.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include + +static clib_error_t * +sfdp_interface_input_set_unset_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t line_input_, *line_input = &line_input_; + + clib_error_t *err = 0; + u32 sw_if_index = ~0; + u32 tenant_id = ~0; + u8 unset = 0; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "tenant %d", &tenant_id)) + ; + else if (unformat (line_input, "disable")) + unset = 1; + else if (unformat (line_input, "%U", unformat_vnet_sw_interface, + vnet_get_main (), &sw_if_index)) + ; + else + { + err = unformat_parse_error (line_input); + goto done; + } + } + err = sfdp_interface_input_set_tenant (&sfdp_interface_input_main, + sw_if_index, tenant_id, unset); +done: + unformat_free (line_input); + return err; +} + +VLIB_CLI_COMMAND (sfdp_nat_external_interface_set_unset, static) = { + .path = "set sfdp interface-input", + .short_help = + "set sfdp interface-input tenant [disable]", + .function = sfdp_interface_input_set_unset_fn, +}; \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/interface_input/interface_input.api b/src/plugins/sfdp_services/base/interface_input/interface_input.api new file mode 100644 index 00000000000..9cec6064a55 --- /dev/null +++ b/src/plugins/sfdp_services/base/interface_input/interface_input.api @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +option version = "0.0.1"; + +import "vnet/ip/ip_types.api"; +import "vnet/interface_types.api"; + +autoreply define sfdp_interface_input_set +{ + u32 client_index; + u32 context; + + vl_api_interface_index_t sw_if_index; + u32 tenant_id; + u8 is_disable; +}; \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/interface_input/interface_input.c b/src/plugins/sfdp_services/base/interface_input/interface_input.c new file mode 100644 index 00000000000..63f52786fb4 --- /dev/null +++ b/src/plugins/sfdp_services/base/interface_input/interface_input.c @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include + +clib_error_t * +sfdp_interface_input_set_tenant (sfdp_interface_input_main_t *vim, + u32 sw_if_index, u32 tenant_id, u8 unset) +{ + sfdp_main_t *sfdp = &sfdp_main; + clib_bihash_kv_8_8_t kv = { .key = tenant_id, .value = 0 }; + vnet_main_t *vnm = vnet_get_main (); + u16 *config; + + if (clib_bihash_search_inline_8_8 (&sfdp->tenant_idx_by_id, &kv)) + return clib_error_return (0, "Tenant with id %d not found"); + + vec_validate (vim->tenant_idx_by_sw_if_idx, sw_if_index); + config = vim->tenant_idx_by_sw_if_idx + sw_if_index; + + if (config[0] == ((u16) ~0) && unset) + return clib_error_return ( + 0, "Outside tenant %d is not configured on interface %U", tenant_id, + format_vnet_sw_if_index_name, vnm, sw_if_index); + + if (config[0] != (u16) (~0) && !unset) + return clib_error_return (0, "Interface %U is already configured", + format_vnet_sw_if_index_name, vnm, sw_if_index); + + if (!unset) + { + vnet_feature_enable_disable ("ip4-unicast", "sfdp-interface-input", + sw_if_index, 1, 0, 0); + config[0] = kv.value; + } + + else + { + vnet_feature_enable_disable ("ip4-unicast", "sfdp-interface-input", + sw_if_index, 0, 0, 0); + config[0] = (u16) (~0); + } + + return 0; +} + +clib_error_t * +sfdp_interface_input_add_del_sw_interface (vnet_main_t *vnm, u32 sw_if_index, + u32 is_add) +{ + sfdp_interface_input_main_t *vim = &sfdp_interface_input_main; + uword old_size = vec_len (vim->tenant_idx_by_sw_if_idx); + if (sw_if_index >= old_size) + { + vec_validate (vim->tenant_idx_by_sw_if_idx, sw_if_index); + for (int i = old_size; i <= sw_if_index; i++) + vim->tenant_idx_by_sw_if_idx[i] = (u16) (~0); + } + return 0; +} + +VNET_SW_INTERFACE_ADD_DEL_FUNCTION (sfdp_interface_input_add_del_sw_interface); +sfdp_interface_input_main_t sfdp_interface_input_main; \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/interface_input/interface_input.h b/src/plugins/sfdp_services/base/interface_input/interface_input.h new file mode 100644 index 00000000000..153c7edb0a4 --- /dev/null +++ b/src/plugins/sfdp_services/base/interface_input/interface_input.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#ifndef __included_nat_h__ +#define __included_nat_h__ + +#include +#include + +typedef struct +{ + u16 *tenant_idx_by_sw_if_idx; /* vec */ + u16 msg_id_base; +} sfdp_interface_input_main_t; + +extern sfdp_interface_input_main_t sfdp_interface_input_main; + +clib_error_t * +sfdp_interface_input_set_tenant (sfdp_interface_input_main_t *nat, + u32 sw_if_index, u32 tenant_id, u8 unset); +#endif \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/interface_input/node.c b/src/plugins/sfdp_services/base/interface_input/node.c new file mode 100644 index 00000000000..1b0e6333f51 --- /dev/null +++ b/src/plugins/sfdp_services/base/interface_input/node.c @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include +#include +#include +typedef struct +{ + u32 tenant_id; + u32 sw_if_index; +} sfdp_interface_input_trace_t; + +static u8 * +format_sfdp_interface_input_trace (u8 *s, va_list *args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + sfdp_interface_input_trace_t *t = + va_arg (*args, sfdp_interface_input_trace_t *); + + s = format (s, "sfdp-interface-input: sw_if_index %d, tenant %d\n", + t->sw_if_index, t->tenant_id); + + return s; +} + +#define foreach_sfdp_interface_input_next _ (LOOKUP, "sfdp-lookup-ip4") +#define foreach_sfdp_interface_input_error _ (NOERROR, "No error") + +typedef enum +{ +#define _(sym, str) SFDP_INTERFACE_INPUT_ERROR_##sym, + foreach_sfdp_interface_input_error +#undef _ + SFDP_INTERFACE_INPUT_N_ERROR, +} sfdp_interface_input_error_t; + +static char *sfdp_interface_input_error_strings[] = { +#define _(sym, string) string, + foreach_sfdp_interface_input_error +#undef _ +}; + +typedef enum +{ +#define _(s, n) SFDP_INTERFACE_INPUT_NEXT_##s, + foreach_sfdp_interface_input_next +#undef _ + SFDP_INTERFACE_INPUT_N_NEXT +} sfdp_interface_input_next_t; + +static_always_inline uword +sfdp_interface_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame) +{ + /* + * use VNI as tenant ID + * tenant_id -> tenant index + * drop unknown tenants + * store tenant_id into opaque1 + * advance current data to beginning of IP packet + */ + sfdp_main_t *sfdp = &sfdp_main; + sfdp_interface_input_main_t *vim = &sfdp_interface_input_main; + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; + vlib_combined_counter_main_t *cm = + &sfdp->tenant_data_ctr[SFDP_TENANT_DATA_COUNTER_INCOMING]; + + u32 *from = vlib_frame_vector_args (frame); + u32 n_left = frame->n_vectors; + u16 next_indices[VLIB_FRAME_SIZE], *current_next; + uword thread_index = vlib_get_thread_index (); + vlib_get_buffers (vm, from, bufs, n_left); + b = bufs; + current_next = next_indices; + + while (n_left) + { + u32 len = vlib_buffer_length_in_chain (vm, b[0]); + u32 rx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_RX]; + u32 tenant_idx = vim->tenant_idx_by_sw_if_idx[rx_sw_if_index]; + sfdp_tenant_t *tenant; + if (tenant_idx == ~0) + { + vnet_feature_next_u16 (current_next, b[0]); + goto end_of_packet; + } + tenant = sfdp_tenant_at_index (sfdp, tenant_idx); + b[0]->flow_id = tenant->context_id; + sfdp_buffer (b[0])->tenant_index = tenant_idx; + current_next[0] = SFDP_INTERFACE_INPUT_NEXT_LOOKUP; + + vlib_increment_combined_counter (cm, thread_index, tenant_idx, 1, len); + end_of_packet: + b += 1; + current_next += 1; + n_left -= 1; + } + vlib_buffer_enqueue_to_next (vm, node, from, next_indices, frame->n_vectors); + return frame->n_vectors; +} + +VLIB_NODE_FN (sfdp_interface_input_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return sfdp_interface_input_inline (vm, node, frame); +} + +VLIB_REGISTER_NODE (sfdp_interface_input_node) = { + .name = "sfdp-interface-input", + .vector_size = sizeof (u32), + .format_trace = format_sfdp_interface_input_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN (sfdp_interface_input_error_strings), + .error_strings = sfdp_interface_input_error_strings, + .n_next_nodes = SFDP_INTERFACE_INPUT_N_NEXT, + .next_nodes = { + [SFDP_INTERFACE_INPUT_NEXT_LOOKUP] = "sfdp-lookup-ip4", + }, +}; + +VNET_FEATURE_INIT (sfdp_interface_input_feat, static) = { + .arc_name = "ip4-unicast", + .node_name = "sfdp-interface-input", +}; \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/l4-lifecycle/node.c b/src/plugins/sfdp_services/base/l4-lifecycle/node.c new file mode 100644 index 00000000000..8deda32291a --- /dev/null +++ b/src/plugins/sfdp_services/base/l4-lifecycle/node.c @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include +#include +#define foreach_sfdp_l4_lifecycle_error _ (DROP, "drop") + +typedef enum +{ +#define _(sym, str) SFDP_L4_LIFECYCLE_ERROR_##sym, + foreach_sfdp_l4_lifecycle_error +#undef _ + SFDP_L4_LIFECYCLE_N_ERROR, +} sfdp_l4_lifecycle_error_t; + +static char *sfdp_l4_lifecycle_error_strings[] = { +#define _(sym, string) string, + foreach_sfdp_l4_lifecycle_error +#undef _ +}; + +typedef struct +{ + u32 flow_id; + u8 new_state; +} sfdp_l4_lifecycle_trace_t; + +static u8 * +format_sfdp_l4_lifecycle_trace (u8 *s, va_list *args) +{ + vlib_main_t __clib_unused *vm = va_arg (*args, vlib_main_t *); + vlib_node_t __clib_unused *node = va_arg (*args, vlib_node_t *); + sfdp_l4_lifecycle_trace_t *t = va_arg (*args, sfdp_l4_lifecycle_trace_t *); + + s = format ( + s, "sfdp-l4-lifecycle: flow-id %u (session %u, %s) new_state: %U", + t->flow_id, t->flow_id >> 1, t->flow_id & 0x1 ? "reverse" : "forward", + format_sfdp_session_state, t->new_state); + return s; +} + +SFDP_SERVICE_DECLARE (tcp_check) +SFDP_SERVICE_DECLARE (l4_lifecycle) +VLIB_NODE_FN (sfdp_l4_lifecycle_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + sfdp_main_t *sfdp = &sfdp_main; + + u32 thread_index = vm->thread_index; + sfdp_timer_per_thread_data_t *tptd = + vec_elt_at_index (sfdp_timer_main.per_thread_data, thread_index); + + u16 next_indices[VLIB_FRAME_SIZE], *to_next = next_indices; + u32 *from = vlib_frame_vector_args (frame); + u32 n_left = frame->n_vectors; + + vlib_get_buffers (vm, from, bufs, n_left); + + while (n_left) + { + u32 session_idx = sfdp_session_from_flow_index (b[0]->flow_id); + u16 tenant_idx = sfdp_buffer (b[0])->tenant_index; + sfdp_session_t *session = sfdp_session_at_index (session_idx); + sfdp_tenant_t *tenant = sfdp_tenant_at_index (sfdp, tenant_idx); + u8 direction = sfdp_direction_from_flow_index (b[0]->flow_id); + /* TODO: prefetch, 4-loop, remove ifs and do state-transition-timer LUT? + */ + if (session->proto == IP_PROTOCOL_TCP) + { + session->bitmaps[SFDP_FLOW_FORWARD] &= + ~SFDP_SERVICE_MASK (l4_lifecycle); + session->bitmaps[SFDP_FLOW_REVERSE] &= + ~SFDP_SERVICE_MASK (l4_lifecycle); + sfdp_buffer (b[0])->service_bitmap |= SFDP_SERVICE_MASK (tcp_check); + session->bitmaps[SFDP_FLOW_FORWARD] |= SFDP_SERVICE_MASK (tcp_check); + session->bitmaps[SFDP_FLOW_REVERSE] |= SFDP_SERVICE_MASK (tcp_check); + } + else + { + if (session->state == SFDP_SESSION_STATE_FSOL && + direction == SFDP_FLOW_REVERSE) + /*Establish the session*/ + session->state = SFDP_SESSION_STATE_ESTABLISHED; + + if (session->state == SFDP_SESSION_STATE_ESTABLISHED) + { + /* TODO: must be configurable per tenant */ + sfdp_session_timer_update ( + &tptd->wheel, SFDP_SESSION_TIMER (session), tptd->current_time, + tenant->timeouts[SFDP_TIMEOUT_ESTABLISHED]); + } + } + sfdp_next (b[0], to_next); + + b++; + to_next++; + n_left--; + } + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) + { + n_left = frame->n_vectors; + b = bufs; + for (int i = 0; i < n_left; i++) + { + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) + { + sfdp_l4_lifecycle_trace_t *t = + vlib_add_trace (vm, node, b[0], sizeof (*t)); + u32 session_idx = sfdp_session_from_flow_index (b[0]->flow_id); + sfdp_session_t *session = sfdp_session_at_index (session_idx); + u16 state = session->state; + t->flow_id = b[0]->flow_id; + t->new_state = state; + b++; + } + else + break; + } + } + vlib_buffer_enqueue_to_next (vm, node, from, next_indices, frame->n_vectors); + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (sfdp_l4_lifecycle_node) = { + .name = "sfdp-l4-lifecycle", + .vector_size = sizeof (u32), + .format_trace = format_sfdp_l4_lifecycle_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN (sfdp_l4_lifecycle_error_strings), + .error_strings = sfdp_l4_lifecycle_error_strings, +}; + +SFDP_SERVICE_DEFINE (l4_lifecycle) = { + .node_name = "sfdp-l4-lifecycle", + .runs_before = SFDP_SERVICES (0), + .runs_after = SFDP_SERVICES ("sfdp-drop"), + .is_terminal = 0 +}; \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/nat/api.c b/src/plugins/sfdp_services/base/nat/api.c new file mode 100644 index 00000000000..08b0df12a7d --- /dev/null +++ b/src/plugins/sfdp_services/base/nat/api.c @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define REPLY_MSG_ID_BASE nat->msg_id_base +#include + +static void +vl_api_sfdp_nat_set_external_interface_t_handler ( + vl_api_sfdp_nat_set_external_interface_t *mp) +{ + nat_main_t *nat = &nat_main; + u32 sw_if_index = clib_net_to_host_u32 (mp->sw_if_index); + u32 tenant_id = clib_net_to_host_u32 (mp->tenant_id); + u8 unset = mp->is_disable; + clib_error_t *err = + nat_external_interface_set_tenant (nat, sw_if_index, tenant_id, unset); + int rv = err ? -1 : 0; + vl_api_sfdp_nat_set_external_interface_reply_t *rmp; + REPLY_MACRO (VL_API_SFDP_NAT_SET_EXTERNAL_INTERFACE_REPLY); +} + +static void +vl_api_sfdp_nat_alloc_pool_add_del_t_handler ( + vl_api_sfdp_nat_alloc_pool_add_del_t *mp) +{ + nat_main_t *nat = &nat_main; + u32 alloc_pool_id = clib_net_to_host_u32 (mp->alloc_pool_id); + u8 is_del = mp->is_del; + uword n_addr = clib_net_to_host_u32 (mp->n_addr); + ip4_address_t *addrs = 0; + clib_error_t *err; + int rv; + vl_api_sfdp_nat_alloc_pool_add_del_reply_t *rmp; + vec_resize (addrs, n_addr); + for (int i = 0; i < n_addr; i++) + ip4_address_decode (mp->addr[i], addrs + i); + + err = nat_alloc_pool_add_del (nat, alloc_pool_id, is_del, addrs); + vec_free (addrs); + rv = err ? -1 : 0; + REPLY_MACRO (VL_API_SFDP_NAT_ALLOC_POOL_ADD_DEL_REPLY); +} + +static void +vl_api_sfdp_nat_snat_set_unset_t_handler (vl_api_sfdp_nat_snat_set_unset_t *mp) +{ + nat_main_t *nat = &nat_main; + u32 tenant_id = clib_net_to_host_u32 (mp->tenant_id); + u32 outside_tenant_id = clib_net_to_host_u32 (mp->outside_tenant_id); + u32 table_id = clib_net_to_host_u32 (mp->table_id); + u32 alloc_pool_id = clib_net_to_host_u32 (mp->alloc_pool_id); + u8 unset = mp->is_disable; + clib_error_t *err; + int rv; + vl_api_sfdp_nat_alloc_pool_add_del_reply_t *rmp; + + err = nat_tenant_set_snat (nat, tenant_id, outside_tenant_id, table_id, + alloc_pool_id, unset); + rv = err ? -1 : 0; + REPLY_MACRO (VL_API_SFDP_NAT_SNAT_SET_UNSET_REPLY); +} + +#include +static clib_error_t * +sfdp_nat_api_hookup (vlib_main_t *vm) +{ + nat_main_t *nat = &nat_main; + nat->msg_id_base = setup_message_id_table (); + return 0; +} +VLIB_API_INIT_FUNCTION (sfdp_nat_api_hookup); +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/sfdp_services/base/nat/cli.c b/src/plugins/sfdp_services/base/nat/cli.c new file mode 100644 index 00000000000..bd2aa4c6592 --- /dev/null +++ b/src/plugins/sfdp_services/base/nat/cli.c @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include + +static clib_error_t * +sfdp_nat_external_interface_set_unset_fn (vlib_main_t *vm, + unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t line_input_, *line_input = &line_input_; + + clib_error_t *err = 0; + u32 sw_if_index = ~0; + u32 tenant_id = ~0; + u8 unset = 0; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "tenant %d", &tenant_id)) + ; + else if (unformat (line_input, "disable")) + unset = 1; + else if (unformat (line_input, "%U", unformat_vnet_sw_interface, + vnet_get_main (), &sw_if_index)) + ; + else + { + err = unformat_parse_error (line_input); + goto done; + } + } + err = nat_external_interface_set_tenant (&nat_main, sw_if_index, tenant_id, + unset); +done: + unformat_free (line_input); + return err; +} + +VLIB_CLI_COMMAND (sfdp_nat_external_interface_set_unset, static) = { + .path = "set sfdp nat external-interface", + .short_help = + "set sfdp nat external-interface tenant [disable]", + .function = sfdp_nat_external_interface_set_unset_fn, +}; + +static clib_error_t * +sfdp_nat_alloc_pool_add_del_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t line_input_, *line_input = &line_input_; + + clib_error_t *err = 0; + u8 is_del = 0; + u32 alloc_pool_id = ~0; + ip4_address_t tmp; + ip4_address_t *addr = 0; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "add %d", &alloc_pool_id)) + is_del = 0; + else if (unformat (line_input, "del %d", &alloc_pool_id)) + is_del = 1; + else if (unformat (line_input, "%U", unformat_ip4_address, &tmp)) + vec_add1 (addr, tmp); + else + { + err = unformat_parse_error (line_input); + goto done; + } + } + nat_alloc_pool_add_del (&nat_main, alloc_pool_id, is_del, addr); +done: + unformat_free (line_input); + vec_free (addr); + return err; +} + +VLIB_CLI_COMMAND (sfdp_nat_alloc_pool_add_del, static) = { + .path = "sfdp nat alloc-pool", + .short_help = "sfdp nat alloc-pool [add|del] +", + .function = sfdp_nat_alloc_pool_add_del_fn, +}; + +static clib_error_t * +sfdp_nat_snat_set_unset_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t line_input_, *line_input = &line_input_; + + clib_error_t *err = 0; + u32 tenant_id = ~0; + u32 outside_tenant_id = ~0; + u32 table_id = ~0; + u32 alloc_pool_id = ~0; + u8 unset = 0; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "tenant %d", &tenant_id)) + ; + else if (unformat (line_input, "outside-tenant %d", &outside_tenant_id)) + ; + else if (unformat (line_input, "table %d", &table_id)) + ; + else if (unformat (line_input, "alloc-pool %d", &alloc_pool_id)) + ; + else if (unformat (line_input, "disable")) + unset = 1; + else + { + err = unformat_parse_error (line_input); + goto done; + } + } + err = nat_tenant_set_snat (&nat_main, tenant_id, outside_tenant_id, table_id, + alloc_pool_id, unset); +done: + unformat_free (line_input); + return err; +} + +VLIB_CLI_COMMAND (sfdp_nat_snat_set_unset, static) = { + .path = "set sfdp nat snat", + .short_help = + "set sfdp nat snat tenant outside-tenant table " + " alloc-pool [disable]", + .function = sfdp_nat_snat_set_unset_fn, +}; \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/nat/external_input_node.c b/src/plugins/sfdp_services/base/nat/external_input_node.c new file mode 100644 index 00000000000..a162d0804d0 --- /dev/null +++ b/src/plugins/sfdp_services/base/nat/external_input_node.c @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include +#include +#include +typedef struct +{ + u32 tenant_id; + u32 sw_if_index; +} nat_external_input_trace_t; + +static u8 * +format_nat_external_input_trace (u8 *s, va_list *args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + nat_external_input_trace_t *t = va_arg (*args, nat_external_input_trace_t *); + + s = format (s, "nat-external-input: sw_if_index %d, tenant %d\n", + t->sw_if_index, t->tenant_id); + + return s; +} + +#define foreach_nat_external_input_next _ (LOOKUP, "sfdp-lookup-ip4") +#define foreach_nat_external_input_error _ (NOERROR, "No error") + +typedef enum +{ +#define _(sym, str) NAT_EXTERNAL_INPUT_ERROR_##sym, + foreach_nat_external_input_error +#undef _ + NAT_EXTERNAL_INPUT_N_ERROR, +} nat_external_input_error_t; + +static char *nat_external_input_error_strings[] = { +#define _(sym, string) string, + foreach_nat_external_input_error +#undef _ +}; + +typedef enum +{ +#define _(s, n) NAT_EXTERNAL_INPUT_NEXT_##s, + foreach_nat_external_input_next +#undef _ + NAT_EXTERNAL_INPUT_N_NEXT +} nat_external_input_next_t; + +static_always_inline uword +nat_external_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame) +{ + /* + * use VNI as tenant ID + * tenant_id -> tenant index + * drop unknown tenants + * store tenant_id into opaque1 + * advance current data to beginning of IP packet + */ + sfdp_main_t *sfdp = &sfdp_main; + nat_main_t *nat = &nat_main; + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; + vlib_combined_counter_main_t *cm = + &sfdp->tenant_data_ctr[SFDP_TENANT_DATA_COUNTER_INCOMING]; + + u32 *from = vlib_frame_vector_args (frame); + u32 n_left = frame->n_vectors; + u16 next_indices[VLIB_FRAME_SIZE], *current_next; + uword thread_index = vlib_get_thread_index (); + vlib_get_buffers (vm, from, bufs, n_left); + b = bufs; + current_next = next_indices; + + while (n_left) + { + u32 len = vlib_buffer_length_in_chain (vm, b[0]); + u32 rx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_RX]; + u32 tenant_idx = nat->tenant_idx_by_sw_if_idx[rx_sw_if_index]; + sfdp_tenant_t *tenant; + if (tenant_idx == NAT_INVALID_TENANT_IDX) + { + vnet_feature_next_u16 (current_next, b[0]); + goto end_of_packet; + } + tenant = sfdp_tenant_at_index (sfdp, tenant_idx); + b[0]->flow_id = tenant->context_id; + sfdp_buffer (b[0])->tenant_index = tenant_idx; + current_next[0] = NAT_EXTERNAL_INPUT_NEXT_LOOKUP; + + vlib_increment_combined_counter (cm, thread_index, tenant_idx, 1, len); + end_of_packet: + b += 1; + current_next += 1; + n_left -= 1; + } + vlib_buffer_enqueue_to_next (vm, node, from, next_indices, frame->n_vectors); + return frame->n_vectors; +} + +VLIB_NODE_FN (nat_external_input_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return nat_external_input_inline (vm, node, frame); +} + +VLIB_REGISTER_NODE (nat_external_input_node) = { + .name = "nat-external-input", + .vector_size = sizeof (u32), + .format_trace = format_nat_external_input_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN (nat_external_input_error_strings), + .error_strings = nat_external_input_error_strings, + .n_next_nodes = NAT_EXTERNAL_INPUT_N_NEXT, + .next_nodes = { + [NAT_EXTERNAL_INPUT_NEXT_LOOKUP] = "sfdp-lookup-ip4", + }, +}; + +VNET_FEATURE_INIT (nat_external_input_feat, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat-external-input", +}; diff --git a/src/plugins/sfdp_services/base/nat/fastpath_node.c b/src/plugins/sfdp_services/base/nat/fastpath_node.c new file mode 100644 index 00000000000..5beb14783c0 --- /dev/null +++ b/src/plugins/sfdp_services/base/nat/fastpath_node.c @@ -0,0 +1,262 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include +#include + +#define foreach_sfdp_nat_fastpath_error _ (DROP, "drop") + +#define foreach_sfdp_nat_terminal_next \ + _ (DROP, "error-drop") \ + _ (IP4_LOOKUP, "ip4-lookup") + +typedef enum +{ +#define _(n, x) SFDP_NAT_TERMINAL_NEXT_##n, + foreach_sfdp_nat_terminal_next +#undef _ + SFDP_NAT_TERMINAL_N_NEXT +} sfdp_nat_terminal_next_t; + +typedef enum +{ +#define _(sym, str) SFDP_NAT_FASTPATH_ERROR_##sym, + foreach_sfdp_nat_fastpath_error +#undef _ + SFDP_NAT_FASTPATH_N_ERROR, +} sfdp_nat_fastpath_error_t; + +static char *sfdp_nat_fastpath_error_strings[] = { +#define _(sym, string) string, + foreach_sfdp_nat_fastpath_error +#undef _ +}; + +typedef struct +{ + u32 thread_index; + u32 flow_id; +} sfdp_nat_fastpath_trace_t; + +static u8 * +format_sfdp_nat_fastpath_trace (u8 *s, va_list *args) +{ + vlib_main_t __clib_unused *vm = va_arg (*args, vlib_main_t *); + vlib_node_t __clib_unused *node = va_arg (*args, vlib_node_t *); + sfdp_nat_fastpath_trace_t *t = va_arg (*args, sfdp_nat_fastpath_trace_t *); + nat_main_t *nm = &nat_main; + nat_rewrite_data_t *rewrite = vec_elt_at_index (nm->flows, t->flow_id); + s = format ( + s, "sfdp-nat-fastpath: flow-id %u (session %u, %s) rewrite: %U\n", + t->flow_id, t->flow_id >> 1, t->flow_id & 0x1 ? "reverse" : "forward", + format_sfdp_nat_rewrite, rewrite); + + return s; +} + +SFDP_SERVICE_DECLARE (drop) + +static_always_inline void +nat_fastpath_process_one (nat_rewrite_data_t *nat_session, + sfdp_session_t *session, u16 *to_next, + vlib_buffer_t **b, u8 is_terminal) +{ + u8 *data = vlib_buffer_get_current (b[0]); + u8 proto = nat_session->rewrite.proto; + u32 ops; + ip4_header_t *ip4 = (void *) data; + ip_csum_t ip_sum = 0, tcp_sum = 0, udp_sum = 0, icmp_sum = 0; + tcp_header_t *tcp; + udp_header_t *udp; + icmp46_header_t *icmp; + u16 *icmp_id; + + if (session->session_version != nat_session->version) + { + sfdp_buffer (b[0])->service_bitmap = SFDP_SERVICE_MASK (drop); + goto end_of_packet; + } + + ops = nat_session->ops; + + ip_sum = ip4->checksum; + ip_sum = ip_csum_sub_even (ip_sum, nat_session->l3_csum_delta); + ip_sum = ip_csum_fold (ip_sum); + ip4->checksum = ip_sum; + + if (ops & NAT_REWRITE_OP_SADDR) + ip4->src_address = nat_session->rewrite.saddr; + + if (ops & NAT_REWRITE_OP_DADDR) + ip4->dst_address = nat_session->rewrite.daddr; + + if (proto == IP_PROTOCOL_TCP) + { + tcp = ip4_next_header (ip4); + tcp_sum = tcp->checksum; + tcp_sum = ip_csum_sub_even (tcp_sum, nat_session->l3_csum_delta); + tcp_sum = ip_csum_sub_even (tcp_sum, nat_session->l4_csum_delta); + tcp_sum = ip_csum_fold (tcp_sum); + tcp->checksum = tcp_sum; + + if (ops & NAT_REWRITE_OP_SPORT) + tcp->src_port = nat_session->rewrite.sport; + + if (ops & NAT_REWRITE_OP_DPORT) + tcp->dst_port = nat_session->rewrite.dport; + } + else if (proto == IP_PROTOCOL_UDP) + { + udp = ip4_next_header (ip4); + udp_sum = udp->checksum; + udp_sum = ip_csum_sub_even (udp_sum, nat_session->l3_csum_delta); + udp_sum = ip_csum_sub_even (udp_sum, nat_session->l4_csum_delta); + udp_sum = ip_csum_fold (udp_sum); + udp->checksum = udp_sum; + + if (ops & NAT_REWRITE_OP_SPORT) + udp->src_port = nat_session->rewrite.sport; + + if (ops & NAT_REWRITE_OP_DPORT) + udp->dst_port = nat_session->rewrite.dport; + } + else if (proto == IP_PROTOCOL_ICMP) + { + icmp = ip4_next_header (ip4); + icmp_sum = icmp->checksum; + icmp_id = (u16 *) (icmp + 1); + icmp_sum = ip_csum_sub_even (icmp_sum, nat_session->l4_csum_delta); + icmp_sum = ip_csum_fold (icmp_sum); + icmp->checksum = icmp_sum; + if (ops & NAT_REWRITE_OP_ICMP_ID) + *icmp_id = nat_session->rewrite.icmp_id; + } + else + { + /*FIXME, must be done at the beginning!*/ + sfdp_buffer (b[0])->service_bitmap = SFDP_SERVICE_MASK (drop); + goto end_of_packet; + } + + if (ops & NAT_REWRITE_OP_TXFIB) + vnet_buffer (b[0])->sw_if_index[VLIB_TX] = nat_session->rewrite.fib_index; + + if (is_terminal) + { + to_next[0] = SFDP_NAT_TERMINAL_NEXT_IP4_LOOKUP; + return; + } + +end_of_packet: + sfdp_next (b[0], to_next); + return; +} + +static_always_inline u16 +sfdp_nat_fastpath_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame, u8 is_terminal) +{ + + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + nat_main_t *nat = &nat_main; + u32 thread_index = vlib_get_thread_index (); + + sfdp_session_t *session; + u32 session_idx; + nat_rewrite_data_t *nat_rewrite; + u32 *from = vlib_frame_vector_args (frame); + u32 n_left = frame->n_vectors; + u16 next_indices[VLIB_FRAME_SIZE], *to_next = next_indices; + + vlib_get_buffers (vm, from, bufs, n_left); + while (n_left > 0) + { + session_idx = sfdp_session_from_flow_index (b[0]->flow_id); + session = sfdp_session_at_index (session_idx); + nat_rewrite = vec_elt_at_index (nat->flows, b[0]->flow_id); + + nat_fastpath_process_one (nat_rewrite, session, to_next, b, is_terminal); + n_left -= 1; + b += 1; + to_next += 1; + } + vlib_buffer_enqueue_to_next (vm, node, from, next_indices, frame->n_vectors); + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) + { + int i; + b = bufs; + n_left = frame->n_vectors; + for (i = 0; i < n_left; i++) + { + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) + { + sfdp_nat_fastpath_trace_t *t = + vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->flow_id = b[0]->flow_id; + t->thread_index = thread_index; + b++; + } + else + break; + } + } + return frame->n_vectors; +} + +VLIB_NODE_FN (sfdp_nat_early_rewrite_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return sfdp_nat_fastpath_inline (vm, node, frame, 0); +} + +VLIB_NODE_FN (sfdp_nat_late_rewrite_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return sfdp_nat_fastpath_inline (vm, node, frame, 1); +} + +VLIB_REGISTER_NODE (sfdp_nat_early_rewrite_node) = { + .name = "sfdp-nat-early-rewrite", + .vector_size = sizeof (u32), + .format_trace = format_sfdp_nat_fastpath_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN (sfdp_nat_fastpath_error_strings), + .error_strings = sfdp_nat_fastpath_error_strings +}; + +VLIB_REGISTER_NODE (sfdp_nat_late_rewrite_node) = { + .name = "sfdp-nat-late-rewrite", + .vector_size = sizeof (u32), + .format_trace = format_sfdp_nat_fastpath_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN (sfdp_nat_fastpath_error_strings), + .error_strings = sfdp_nat_fastpath_error_strings, + .n_next_nodes = SFDP_NAT_TERMINAL_N_NEXT, + .next_nodes = { +#define _(n, x) [SFDP_NAT_TERMINAL_NEXT_##n] = x, + foreach_sfdp_nat_terminal_next +#undef _ + } + +}; + +SFDP_SERVICE_DEFINE (nat_late_rewrite) = { + .node_name = "sfdp-nat-late-rewrite", + .runs_before = SFDP_SERVICES (0), + .runs_after = SFDP_SERVICES ("sfdp-drop", "sfdp-l4-lifecycle", + "sfdp-tcp-check", "sfdp-nat-output"), + .is_terminal = 1 +}; + +SFDP_SERVICE_DEFINE (nat_early_rewrite) = { + .node_name = "sfdp-nat-early-rewrite", + .runs_before = SFDP_SERVICES ("sfdp-geneve-output"), + .runs_after = SFDP_SERVICES ("sfdp-drop", "sfdp-l4-lifecycle", + "sfdp-tcp-check"), + .is_terminal = 0 +}; \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/nat/format.c b/src/plugins/sfdp_services/base/nat/format.c new file mode 100644 index 00000000000..b30086620f4 --- /dev/null +++ b/src/plugins/sfdp_services/base/nat/format.c @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include + +static u8 * +format_sfdp_nat_rewrite_SADDR (u8 *s, va_list *args) +{ + nat_rewrite_data_t *rewrite = va_arg (*args, nat_rewrite_data_t *); + s = format (s, "%U", format_ip4_address, &rewrite->rewrite.saddr); + return s; +} + +static u8 * +format_sfdp_nat_rewrite_SPORT (u8 *s, va_list *args) +{ + nat_rewrite_data_t *rewrite = va_arg (*args, nat_rewrite_data_t *); + s = format (s, "%u", clib_net_to_host_u16 (rewrite->rewrite.sport)); + return s; +} + +static u8 * +format_sfdp_nat_rewrite_DADDR (u8 *s, va_list *args) +{ + nat_rewrite_data_t *rewrite = va_arg (*args, nat_rewrite_data_t *); + s = format (s, "%U", format_ip4_address, &rewrite->rewrite.daddr); + return s; +} +static u8 * +format_sfdp_nat_rewrite_DPORT (u8 *s, va_list *args) +{ + nat_rewrite_data_t *rewrite = va_arg (*args, nat_rewrite_data_t *); + s = format (s, "%u", clib_net_to_host_u16 (rewrite->rewrite.dport)); + return s; +} +static u8 * +format_sfdp_nat_rewrite_ICMP_ID (u8 *s, va_list *args) +{ + nat_rewrite_data_t *rewrite = va_arg (*args, nat_rewrite_data_t *); + s = format (s, "%u", rewrite->rewrite.icmp_id); + return s; +} +static u8 * +format_sfdp_nat_rewrite_TXFIB (u8 *s, va_list *args) +{ + nat_rewrite_data_t *rewrite = va_arg (*args, nat_rewrite_data_t *); + s = format (s, "fib-index %u", rewrite->rewrite.fib_index); + return s; +} + +u8 * +format_sfdp_nat_rewrite (u8 *s, va_list *args) +{ + nat_rewrite_data_t *rewrite = va_arg (*args, nat_rewrite_data_t *); +#define _(sym, x, str) \ + if (rewrite->ops & NAT_REWRITE_OP_##sym) \ + s = format (s, "rewrite %s (to %U),", str, format_sfdp_nat_rewrite_##sym, \ + rewrite); + foreach_nat_rewrite_op +#undef _ + // if (s && s[vec_len (s) - 1] == ',') vec_resize (s, vec_len (s) - 1); + return s; +} \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/nat/nat.api b/src/plugins/sfdp_services/base/nat/nat.api new file mode 100644 index 00000000000..41378c865c8 --- /dev/null +++ b/src/plugins/sfdp_services/base/nat/nat.api @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +option version = "0.0.1"; + +import "vnet/ip/ip_types.api"; +import "vnet/interface_types.api"; + +autoreply define sfdp_nat_set_external_interface +{ + u32 client_index; + u32 context; + + vl_api_interface_index_t sw_if_index; + u32 tenant_id; + u8 is_disable; +}; + +autoreply define sfdp_nat_alloc_pool_add_del +{ + u32 client_index; + u32 context; + + u32 alloc_pool_id; + u8 is_del; + u32 n_addr; + vl_api_ip4_address_t addr[n_addr]; +}; + +autoreply define sfdp_nat_snat_set_unset +{ + u32 client_index; + u32 context; + + u32 tenant_id; + u32 outside_tenant_id; + u32 table_id; + u32 alloc_pool_id; + u8 is_disable; +}; \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/nat/nat.c b/src/plugins/sfdp_services/base/nat/nat.c new file mode 100644 index 00000000000..7b0f9e3bab4 --- /dev/null +++ b/src/plugins/sfdp_services/base/nat/nat.c @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include + +clib_error_t * +nat_external_interface_set_tenant (nat_main_t *nat, u32 sw_if_index, + u32 tenant_id, u8 unset) +{ + sfdp_main_t *sfdp = &sfdp_main; + clib_bihash_kv_8_8_t kv = { .key = tenant_id, .value = 0 }; + vnet_main_t *vnm = vnet_get_main (); + u16 *config; + + if (clib_bihash_search_inline_8_8 (&sfdp->tenant_idx_by_id, &kv)) + return clib_error_return (0, "Tenant with id %d not found"); + + vec_validate (nat->tenants, kv.value); + vec_validate (nat->tenant_idx_by_sw_if_idx, sw_if_index); + config = nat->tenant_idx_by_sw_if_idx + sw_if_index; + + if (config[0] == NAT_INVALID_TENANT_IDX && unset) + return clib_error_return ( + 0, "Outside tenant %d is not configured on interface %U", tenant_id, + format_vnet_sw_if_index_name, vnm, sw_if_index); + + if (config[0] != NAT_INVALID_TENANT_IDX && !unset) + return clib_error_return (0, "Interface %U is already configured", + format_vnet_sw_if_index_name, vnm, sw_if_index); + + if (!unset) + { + vnet_feature_enable_disable ("ip4-unicast", "nat-external-input", + sw_if_index, 1, 0, 0); + config[0] = kv.value; + } + + else + { + vnet_feature_enable_disable ("ip4-unicast", "nat-external-input", + sw_if_index, 0, 0, 0); + config[0] = NAT_INVALID_TENANT_IDX; + } + + return 0; +} + +clib_error_t * +nat_alloc_pool_add_del (nat_main_t *nat, u32 alloc_pool_id, u8 is_del, + ip4_address_t *addr) +{ + u16 alloc_pool_idx; + uword *val = hash_get (nat->alloc_pool_idx_by_id, alloc_pool_id); + + if (!val && is_del) + return clib_error_return (0, "Allocation pool %d does not exist", + alloc_pool_id); + if (val && !is_del) + return clib_error_return (0, "Existing allocation pool with id %d", + alloc_pool_id); + + if (is_del) + { + pool_put_index (nat->alloc_pool, val[0]); + hash_unset (nat->alloc_pool_idx_by_id, alloc_pool_id); + } + else + { + nat_alloc_pool_t *alloc_pool; + uword num = vec_len (addr); + uword num_static = clib_min (num, NAT_ALLOC_POOL_ARRAY_SZ); + pool_get_zero (nat->alloc_pool, alloc_pool); + alloc_pool->num = num; + alloc_pool_idx = alloc_pool - nat->alloc_pool; + num -= num_static; + for (int i = 0; i < num_static; i++) + alloc_pool->addr[i] = addr[i]; + if (num > 0) + { + addr += num_static; + vec_validate (alloc_pool->remaining, num); + for (int i = 0; i < num; i++) + alloc_pool->remaining[i] = addr[i]; + } + hash_set (nat->alloc_pool_idx_by_id, alloc_pool_id, alloc_pool_idx); + } + return 0; +} + +clib_error_t * +nat_tenant_set_snat (nat_main_t *nat, u32 tenant_id, u32 outside_tenant_id, + u32 table_id, u32 alloc_pool_id, u8 unset) +{ + ip4_main_t *im = &ip4_main; + uword *fib_index = hash_get (im->fib_index_by_table_id, table_id); + uword *out_alloc_pool_idx = + hash_get (nat->alloc_pool_idx_by_id, alloc_pool_id); + clib_bihash_kv_8_8_t kv = { .key = tenant_id, .value = 0 }; + sfdp_main_t *sfdp = &sfdp_main; + nat_tenant_t *tenant; + sfdp_tenant_t *outside_tenant; + uword tenant_idx; + uword outside_tenant_idx; + + if (!unset && !fib_index) + return clib_error_return (0, "Unknown table %d", table_id); + + if (!unset && !out_alloc_pool_idx) + return clib_error_return (0, "Unknown allocation pool %d", alloc_pool_id); + + if (clib_bihash_search_inline_8_8 (&sfdp->tenant_idx_by_id, &kv)) + return clib_error_return (0, "Unknown tenant %d", tenant_id); + + tenant_idx = kv.value; + kv.key = outside_tenant_id; + kv.value = 0; + if (!unset && clib_bihash_search_inline_8_8 (&sfdp->tenant_idx_by_id, &kv)) + return clib_error_return (0, "Unknown tenant %d", tenant_id); + else + outside_tenant_idx = kv.value; + + vec_validate (nat->tenants, tenant_idx); + tenant = vec_elt_at_index (nat->tenants, tenant_idx); + + if (unset && !(tenant->flags & NAT_TENANT_FLAG_SNAT)) + return clib_error_return (0, "SNAT is not set on tenant %d", tenant_id); + + if (!unset && (tenant->flags & NAT_TENANT_FLAG_SNAT)) + return clib_error_return (0, "SNAT is already set on tenant %d", + tenant_id); + + if (unset) + { + tenant->flags &= ~NAT_TENANT_FLAG_SNAT; + tenant->fib_index = ~0; + tenant->out_alloc_pool_idx = ~0; + tenant->reverse_context = ~0; + } + else + { + outside_tenant = sfdp_tenant_at_index (sfdp, outside_tenant_idx); + tenant->flags |= NAT_TENANT_FLAG_SNAT; + tenant->fib_index = fib_index[0]; + tenant->out_alloc_pool_idx = out_alloc_pool_idx[0]; + tenant->reverse_context = outside_tenant->context_id; + } + return 0; +} + +static clib_error_t * +nat_init (vlib_main_t *vm) +{ + nat_main_t *nat = &nat_main; + sfdp_main_t *sfdp = &sfdp_main; + + nat->alloc_pool_idx_by_id = hash_create (0, sizeof (uword)); + vec_validate (nat->flows, (2ULL << sfdp->log2_sessions) - 1); + + return 0; +} +VLIB_INIT_FUNCTION (nat_init); + +clib_error_t * +nat_add_del_sw_interface (vnet_main_t *vnm, u32 sw_if_index, u32 is_add) +{ + nat_main_t *nat = &nat_main; + uword old_size = vec_len (nat->tenant_idx_by_sw_if_idx); + if (sw_if_index >= old_size) + { + vec_validate (nat->tenant_idx_by_sw_if_idx, sw_if_index); + for (int i = old_size; i <= sw_if_index; i++) + nat->tenant_idx_by_sw_if_idx[i] = NAT_INVALID_TENANT_IDX; + } + return 0; +} + +VNET_SW_INTERFACE_ADD_DEL_FUNCTION (nat_add_del_sw_interface); +nat_main_t nat_main; \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/nat/nat.h b/src/plugins/sfdp_services/base/nat/nat.h new file mode 100644 index 00000000000..2084750b12b --- /dev/null +++ b/src/plugins/sfdp_services/base/nat/nat.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#ifndef __included_nat_h__ +#define __included_nat_h__ + +#include +#include +#include + +#define NAT_INVALID_TENANT_IDX (u16) (~0) +#define NAT_ALLOC_POOL_ARRAY_SZ 13 + +#define foreach_nat_tenant_flag _ (SNAT, 0x1, "snat") + +enum +{ +#define _(name, x, str) NAT_TENANT_FLAG_##name = (x), + foreach_nat_tenant_flag +#undef _ + NAT_TENANT_N_FLAGS +}; + +typedef struct +{ + u16 flags; + u32 reverse_context; + uword out_alloc_pool_idx; + uword fib_index; +} nat_tenant_t; + +#define foreach_nat_rewrite_op \ + _ (SADDR, 0x1, "src-addr") \ + _ (SPORT, 0x2, "src-port") \ + _ (DADDR, 0x4, "dst-addr") \ + _ (DPORT, 0x8, "dst-port") \ + _ (ICMP_ID, 0x10, "icmp-id") \ + _ (TXFIB, 0x20, "tx-fib") + +typedef enum +{ +#define _(sym, x, s) NAT_REWRITE_OP_##sym = x, + foreach_nat_rewrite_op +#undef _ +} nat_rewrite_op_t; +typedef struct +{ + CLIB_CACHE_LINE_ALIGN_MARK (cache0); + struct + { + ip4_address_t saddr, daddr; + u16 sport; + u16 dport; + u32 fib_index; + u16 icmp_id; + u8 proto; + } rewrite; + u32 ops; /* see nat_rewrite_op_t */ + uword l3_csum_delta; + uword l4_csum_delta; + session_version_t version; +} nat_rewrite_data_t; +STATIC_ASSERT_SIZEOF (nat_rewrite_data_t, CLIB_CACHE_LINE_BYTES); + +typedef struct +{ + CLIB_CACHE_LINE_ALIGN_MARK (cache0); + u16 flags; + u16 num; + ip4_address_t addr[NAT_ALLOC_POOL_ARRAY_SZ]; + ip4_address_t *remaining; +} nat_alloc_pool_t; +STATIC_ASSERT_SIZEOF (nat_alloc_pool_t, CLIB_CACHE_LINE_BYTES); + +typedef struct +{ + u16 *tenant_idx_by_sw_if_idx; /* vec */ + nat_tenant_t *tenants; /* vec */ + nat_alloc_pool_t *alloc_pool; /* pool of allocation pools */ + nat_rewrite_data_t *flows; /* by flow_index */ + uword *alloc_pool_idx_by_id; /* hash */ + u16 msg_id_base; +} nat_main_t; + +extern nat_main_t nat_main; + +clib_error_t *nat_external_interface_set_tenant (nat_main_t *nat, + u32 sw_if_index, + u32 tenant_id, u8 unset); + +clib_error_t *nat_alloc_pool_add_del (nat_main_t *nat, u32 alloc_pool_id, + u8 is_del, ip4_address_t *addr); + +clib_error_t *nat_tenant_set_snat (nat_main_t *nat, u32 tenant_id, + u32 outside_tenant_id, u32 table_id, + u32 alloc_pool_id, u8 unset); +format_function_t format_sfdp_nat_rewrite; +#endif \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/nat/slowpath_node.c b/src/plugins/sfdp_services/base/nat/slowpath_node.c new file mode 100644 index 00000000000..952051dcda3 --- /dev/null +++ b/src/plugins/sfdp_services/base/nat/slowpath_node.c @@ -0,0 +1,289 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include +#include +#define foreach_sfdp_nat_slowpath_error _ (DROP, "drop") + +typedef enum +{ +#define _(sym, str) SFDP_NAT_SLOWPATH_ERROR_##sym, + foreach_sfdp_nat_slowpath_error +#undef _ + SFDP_NAT_SLOWPATH_N_ERROR, +} sfdp_nat_slowpath_error_t; + +static char *sfdp_nat_slowpath_error_strings[] = { +#define _(sym, string) string, + foreach_sfdp_nat_slowpath_error +#undef _ +}; + +typedef struct +{ + u32 flow_id; + u32 thread_index; +} sfdp_nat_slowpath_trace_t; + +format_function_t format_sfdp_bitmap; + +SFDP_SERVICE_DECLARE (drop) +static u8 * +format_sfdp_nat_slowpath_trace (u8 *s, va_list *args) +{ + vlib_main_t __clib_unused *vm = va_arg (*args, vlib_main_t *); + vlib_node_t __clib_unused *node = va_arg (*args, vlib_node_t *); + sfdp_nat_slowpath_trace_t *t = va_arg (*args, sfdp_nat_slowpath_trace_t *); + /* FIXME: This is a scam, the session-idx can be invalid at format time!*/ + sfdp_session_t *session = sfdp_session_at_index (t->flow_id >> 1); + u32 scope_index = session->scope_index; + + s = format (s, "sfdp-nat-output: flow-id %u (session %u, %s)\n", t->flow_id, + t->flow_id >> 1, t->flow_id & 0x1 ? "reverse" : "forward"); + s = format (s, " new forward service chain: %U\n", format_sfdp_bitmap, + scope_index, session->bitmaps[SFDP_FLOW_FORWARD]); + s = format (s, " new reverse service chain: %U\n", format_sfdp_bitmap, + scope_index, session->bitmaps[SFDP_FLOW_REVERSE]); + + return s; +} +SFDP_SERVICE_DECLARE (nat_late_rewrite) +SFDP_SERVICE_DECLARE (nat_early_rewrite) +SFDP_SERVICE_DECLARE (nat_output) +static_always_inline void +nat_slow_path_process_one (sfdp_main_t *sfdp, u32 *fib_index_by_sw_if_index, + u16 thread_index, nat_main_t *nm, + nat_tenant_t *tenant, u32 session_index, + nat_rewrite_data_t *nat_session, + sfdp_session_t *session, u16 *to_next, + vlib_buffer_t **b) +{ + uword l3_sum_delta_forward = 0; + uword l4_sum_delta_forward = 0; + uword l3_sum_delta_reverse = 0; + uword l4_sum_delta_reverse = 0; + sfdp_session_ip46_key_t new_key46 = { 0 }; + sfdp_session_ip4_key_t *new_key = &new_key46.key4; + new_key46.key4 = session->keys[SFDP_SESSION_KEY_PRIMARY].key4; + u8 pseudo_dir = session->pseudo_dir[SFDP_SESSION_KEY_PRIMARY]; + u8 proto = session->proto; + u8 n_retries = 0; + u32 *ip4_key_src_addr = + pseudo_dir ? &new_key->ip4_key.ip_addr_hi : &new_key->ip4_key.ip_addr_lo; + u32 ip4_old_src_addr; + u32 ip4_new_src_addr; + u16 *ip4_key_src_port; + u16 *ip4_key_dst_port; + u16 ip4_old_port; + u16 ip4_new_port; + + nat_alloc_pool_t *pool; + u32 src_addr_index; + u64 h; + u32 pseudo_flow_index; + u32 old_fib_index; + + if (PREDICT_FALSE (!(tenant->flags & NAT_TENANT_FLAG_SNAT))) + { + sfdp_buffer (b[0])->service_bitmap = SFDP_SERVICE_MASK (drop); + goto end_of_packet; + } + + pool = pool_elt_at_index (nm->alloc_pool, tenant->out_alloc_pool_idx); + + if (PREDICT_FALSE (session->session_version == nat_session->version)) + { + /* NAT State is already created, certainly a packet in flight. Refresh + * bitmap */ + sfdp_buffer (b[0])->service_bitmap = + session->bitmaps[b[0]->flow_id & 0x1]; + goto end_of_packet; + } + + /* TODO: handle case with many addresses in pool (slowpath) */ + if (PREDICT_FALSE (pool->num > NAT_ALLOC_POOL_ARRAY_SZ)) + ASSERT (0); + + new_key->context_id = tenant->reverse_context; + + /* Allocate a new source */ + ip4_old_src_addr = *ip4_key_src_addr; + src_addr_index = ip4_old_src_addr % pool->num; + ip4_new_src_addr = pool->addr[src_addr_index].as_u32; + *ip4_key_src_addr = ip4_new_src_addr; + pseudo_dir = sfdp_renormalise_ip4_key (new_key, pseudo_dir); + pseudo_flow_index = (session_index << 1) | (pseudo_dir & 0x1); + /* Allocate a new port */ + ip4_key_src_port = + pseudo_dir ? &new_key->ip4_key.port_hi : &new_key->ip4_key.port_lo; + ip4_key_dst_port = + pseudo_dir ? &new_key->ip4_key.port_lo : &new_key->ip4_key.port_hi; + ip4_old_port = *ip4_key_src_port; + + /* First try with original src port */ + ip4_new_port = ip4_old_port; + while ((++n_retries) < 5 && sfdp_session_try_add_secondary_key ( + sfdp, thread_index, pseudo_flow_index, + &new_key46, IP46_TYPE_IP4, &h)) + { + /* Use h to try a different port */ + u32 h2 = h; + u64 reduced = h2; + reduced *= 64512ULL; + reduced >>= 32; + ip4_new_port = clib_host_to_net_u16 (1024 + reduced); + *ip4_key_src_port = ip4_new_port; + if (PREDICT_FALSE (proto == IP_PROTOCOL_ICMP)) + *ip4_key_dst_port = ip4_new_port; + } + + if (n_retries == 5) + { + /* Port allocation failure */ + /* TODO: do the sensible thing, drop the packet + increase counters */ + sfdp_buffer (b[0])->service_bitmap = SFDP_SERVICE_MASK (drop); + goto end_of_packet; + } + + /* Build the rewrites in both directions */ + l3_sum_delta_forward = + ip_csum_add_even (l3_sum_delta_forward, ip4_new_src_addr); + l3_sum_delta_forward = + ip_csum_sub_even (l3_sum_delta_forward, ip4_old_src_addr); + + l4_sum_delta_forward = ip_csum_add_even (l4_sum_delta_forward, ip4_new_port); + l4_sum_delta_forward = ip_csum_sub_even (l4_sum_delta_forward, ip4_old_port); + + l3_sum_delta_reverse = + ip_csum_add_even (l3_sum_delta_reverse, ip4_old_src_addr); + l3_sum_delta_reverse = + ip_csum_sub_even (l3_sum_delta_reverse, ip4_new_src_addr); + + l4_sum_delta_reverse = ip_csum_add_even (l4_sum_delta_reverse, ip4_old_port); + l4_sum_delta_reverse = ip_csum_sub_even (l4_sum_delta_reverse, ip4_new_port); + + old_fib_index = vec_elt (fib_index_by_sw_if_index, + vnet_buffer (b[0])->sw_if_index[VLIB_RX]); + nat_session[0].version = session->session_version; + nat_session[1].version = session->session_version; + + if (PREDICT_TRUE (proto != IP_PROTOCOL_ICMP)) + { + nat_session[0].ops = + NAT_REWRITE_OP_SADDR | NAT_REWRITE_OP_SPORT | NAT_REWRITE_OP_TXFIB; + nat_session[0].rewrite.sport = ip4_new_port; + nat_session[1].ops = + NAT_REWRITE_OP_DADDR | NAT_REWRITE_OP_DPORT | NAT_REWRITE_OP_TXFIB; + nat_session[1].rewrite.dport = ip4_old_port; + } + else + { + nat_session[0].ops = + NAT_REWRITE_OP_SADDR | NAT_REWRITE_OP_ICMP_ID | NAT_REWRITE_OP_TXFIB; + nat_session[0].rewrite.icmp_id = ip4_new_port; + nat_session[1].ops = + NAT_REWRITE_OP_DADDR | NAT_REWRITE_OP_ICMP_ID | NAT_REWRITE_OP_TXFIB; + nat_session[1].rewrite.icmp_id = ip4_old_port; + } + + nat_session[0].rewrite.saddr.as_u32 = ip4_new_src_addr; + nat_session[0].rewrite.fib_index = tenant->fib_index; + nat_session[0].rewrite.proto = proto; + nat_session[0].l3_csum_delta = l3_sum_delta_forward; + nat_session[0].l4_csum_delta = l4_sum_delta_forward; + + nat_session[1].rewrite.daddr.as_u32 = ip4_old_src_addr; + nat_session[1].rewrite.fib_index = old_fib_index; + nat_session[1].rewrite.proto = proto; + nat_session[1].l3_csum_delta = l3_sum_delta_reverse; + nat_session[1].l4_csum_delta = l4_sum_delta_reverse; + + sfdp_buffer (b[0])->service_bitmap |= SFDP_SERVICE_MASK (nat_late_rewrite); + session->bitmaps[SFDP_FLOW_FORWARD] &= ~SFDP_SERVICE_MASK (nat_output); + session->bitmaps[SFDP_FLOW_REVERSE] &= ~SFDP_SERVICE_MASK (nat_output); + session->bitmaps[SFDP_FLOW_FORWARD] |= SFDP_SERVICE_MASK (nat_late_rewrite); + session->bitmaps[SFDP_FLOW_REVERSE] |= SFDP_SERVICE_MASK (nat_early_rewrite); + +end_of_packet: + sfdp_next (b[0], to_next); + return; +} + +VLIB_NODE_FN (sfdp_nat_slowpath_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + sfdp_main_t *sfdp = &sfdp_main; + ip4_main_t *im = &ip4_main; + nat_main_t *nat = &nat_main; + u32 thread_index = vlib_get_thread_index (); + sfdp_session_t *session; + nat_tenant_t *tenant; + u32 session_idx; + u32 tenant_idx; + nat_rewrite_data_t *nat_rewrites; /* rewrite data in both directions */ + u32 *from = vlib_frame_vector_args (frame); + u32 n_left = frame->n_vectors; + u16 next_indices[VLIB_FRAME_SIZE], *to_next = next_indices; + + vlib_get_buffers (vm, from, bufs, n_left); + while (n_left > 0) + { + session_idx = sfdp_session_from_flow_index (b[0]->flow_id); + session = sfdp_session_at_index (session_idx); + tenant_idx = sfdp_buffer (b[0])->tenant_index; + nat_rewrites = vec_elt_at_index (nat->flows, session_idx << 1); + tenant = vec_elt_at_index (nat->tenants, tenant_idx); + + // nat_slow_path_process_one (tenant, nat_rewrites, session, to_next, b); + nat_slow_path_process_one (sfdp, im->fib_index_by_sw_if_index, + thread_index, nat, tenant, session_idx, + nat_rewrites, session, to_next, b); + n_left -= 1; + b += 1; + to_next += 1; + } + vlib_buffer_enqueue_to_next (vm, node, from, next_indices, frame->n_vectors); + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) + { + int i; + b = bufs; + n_left = frame->n_vectors; + for (i = 0; i < n_left; i++) + { + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) + { + sfdp_nat_slowpath_trace_t *t = + vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->flow_id = b[0]->flow_id; + t->thread_index = thread_index; + b++; + } + else + break; + } + } + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (sfdp_nat_slowpath_node) = { + .name = "sfdp-nat-output", + .vector_size = sizeof (u32), + .format_trace = format_sfdp_nat_slowpath_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN (sfdp_nat_slowpath_error_strings), + .error_strings = sfdp_nat_slowpath_error_strings +}; + +SFDP_SERVICE_DEFINE (nat_output) = { + .node_name = "sfdp-nat-output", + .runs_before = SFDP_SERVICES ("sfdp-geneve-output", "sfdp-nat-late-rewrite"), + .runs_after = SFDP_SERVICES ("sfdp-drop", "sfdp-l4-lifecycle", + "sfdp-tcp-check"), + .is_terminal = 0 +}; \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/sample/node.c b/src/plugins/sfdp_services/base/sample/node.c new file mode 100644 index 00000000000..5cd28f7da35 --- /dev/null +++ b/src/plugins/sfdp_services/base/sample/node.c @@ -0,0 +1,233 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include +#define foreach_sample_terminal_error _ (DROP, "drop") + +typedef enum +{ +#define _(sym, str) SAMPLE_TERMINAL_ERROR_##sym, + foreach_sample_terminal_error +#undef _ + SAMPLE_TERMINAL_N_ERROR, +} sample_terminal_error_t; + +static char *sample_terminal_error_strings[] = { +#define _(sym, string) string, + foreach_sample_terminal_error +#undef _ +}; + +#define foreach_sample_terminal_next _ (DROP, "error-drop") + +typedef enum +{ +#define _(n, x) SAMPLE_TERMINAL_NEXT_##n, + foreach_sample_terminal_next +#undef _ + SAMPLE_TERMINAL_N_NEXT +} sample_terminal_next_t; + +typedef struct +{ + u32 flow_id; +} sample_terminal_trace_t; + +static u8 * +format_sample_terminal_trace (u8 *s, va_list *args) +{ + vlib_main_t __clib_unused *vm = va_arg (*args, vlib_main_t *); + vlib_node_t __clib_unused *node = va_arg (*args, vlib_node_t *); + sample_terminal_trace_t *t = va_arg (*args, sample_terminal_trace_t *); + + s = + format (s, "sample-terminal-drop: flow-id %u (session %u, %s)", t->flow_id, + t->flow_id >> 1, t->flow_id & 0x1 ? "reverse" : "forward"); + return s; +} + +VLIB_NODE_FN (sample_terminal_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; + u32 *from = vlib_frame_vector_args (frame); + u32 n_left = frame->n_vectors; + + vlib_buffer_enqueue_to_single_next (vm, node, from, + SAMPLE_TERMINAL_NEXT_DROP, n_left); + vlib_node_increment_counter (vm, node->node_index, + SAMPLE_TERMINAL_ERROR_DROP, n_left); + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) + { + int i; + vlib_get_buffers (vm, from, bufs, n_left); + b = bufs; + for (i = 0; i < n_left; i++) + { + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) + { + sample_terminal_trace_t *t = + vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->flow_id = b[0]->flow_id; + b++; + } + else + break; + } + } + return frame->n_vectors; +} + +#define foreach_sample_non_terminal_error _ (DROP, "drop") + +typedef enum +{ +#define _(sym, str) SAMPLE_NON_TERMINAL_ERROR_##sym, + foreach_sample_non_terminal_error +#undef _ + SAMPLE_NON_TERMINAL_N_ERROR, +} sample_non_terminal_error_t; + +static char *sample_non_terminal_error_strings[] = { +#define _(sym, string) string, + foreach_sample_non_terminal_error +#undef _ +}; + +typedef struct +{ + u32 flow_id; + u8 new_state; +} sample_non_terminal_trace_t; + +static u8 * +format_sample_non_terminal_trace (u8 *s, va_list *args) +{ + vlib_main_t __clib_unused *vm = va_arg (*args, vlib_main_t *); + vlib_node_t __clib_unused *node = va_arg (*args, vlib_node_t *); + sample_non_terminal_trace_t *t = + va_arg (*args, sample_non_terminal_trace_t *); + + s = format ( + s, "sample-non-terminal: flow-id %u (session %u, %s) new_state: %U", + t->flow_id, t->flow_id >> 1, t->flow_id & 0x1 ? "reverse" : "forward", + format_sfdp_session_state, t->new_state); + return s; +} + +VLIB_NODE_FN (sample_non_terminal_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + sfdp_main_t *sfdp = &sfdp_main; + + u32 thread_index = vm->thread_index; + sfdp_per_thread_data_t *ptd = + vec_elt_at_index (sfdp->per_thread_data, thread_index); + + u16 next_indices[VLIB_FRAME_SIZE], *to_next = next_indices; + u32 *from = vlib_frame_vector_args (frame); + u32 n_left = frame->n_vectors; + + vlib_get_buffers (vm, from, bufs, n_left); + + while (n_left) + { + u32 session_idx = sfdp_session_from_flow_index (b[0]->flow_id); + u16 tenant_idx = sfdp_buffer (b[0])->tenant_index; + sfdp_session_t *session = sfdp_session_at_index (session_idx); + sfdp_tenant_t *tenant = sfdp_tenant_at_index (sfdp, tenant_idx); + CLIB_UNUSED (u8 direction) = + sfdp_direction_from_flow_index (b[0]->flow_id); + /* Set the state of the session to established */ + session->state = SFDP_SESSION_STATE_ESTABLISHED; + /* Rearm the session timeout to + * tenant->timeouts[SFDP_TIMEOUT_ESTABLISHED] from now */ + sfdp_session_timer_update (&ptd->wheel, &session->timer, + ptd->current_time, + tenant->timeouts[SFDP_TIMEOUT_ESTABLISHED]); + /* Next service in chain for this packet */ + sfdp_next (b[0], to_next); + + b++; + to_next++; + n_left--; + } + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) + { + n_left = frame->n_vectors; + b = bufs; + for (int i = 0; i < n_left; i++) + { + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) + { + sample_non_terminal_trace_t *t = + vlib_add_trace (vm, node, b[0], sizeof (*t)); + u32 session_idx = sfdp_session_from_flow_index (b[0]->flow_id); + sfdp_session_t *session = + sfdp_session_at_index (ptd, session_idx); + u16 state = session->state; + t->flow_id = b[0]->flow_id; + t->new_state = state; + b++; + } + else + break; + } + } + vlib_buffer_enqueue_to_next (vm, node, from, next_indices, frame->n_vectors); + return frame->n_vectors; +} + +/* This service is a terminal service, i.e., its next nodes are outside + of sfdp (here for example, error-drop) */ + +VLIB_REGISTER_NODE (sample_terminal_node) = { + .name = "sample-terminal", + .vector_size = sizeof (u32), + .format_trace = format_sample_terminal_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN (sample_terminal_error_strings), + .error_strings = sample_terminal_error_strings, + + .n_next_nodes = SAMPLE_TERMINAL_N_NEXT, + .next_nodes = { +#define _(n, x) [SAMPLE_TERMINAL_NEXT_##n] = x, + foreach_sample_terminal_next +#undef _ + } + +}; + +SFDP_SERVICE_DEFINE (sample) = { + .node_name = "sample-terminal", + .runs_before = SFDP_SERVICES (0), + .runs_after = SFDP_SERVICES (0), + .is_terminal = 1, +}; + +/* This service is a nonterminal service, i.e., next nodes cannot be specified + */ + +VLIB_REGISTER_NODE (sample_non_terminal_node) = { + .name = "sample-non-terminal", + .vector_size = sizeof (u32), + .format_trace = format_sample_non_terminal_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN (sample_non_terminal_error_strings), + .error_strings = sample_non_terminal_error_strings, + +}; + +SFDP_SERVICE_DEFINE (sample_non_terminal) = { + .node_name = "sample-non-terminal", + .runs_before = SFDP_SERVICES (0), + .runs_after = SFDP_SERVICES ("sfdp-drop"), + .is_terminal = 0 +}; \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/tcp-check/api.c b/src/plugins/sfdp_services/base/tcp-check/api.c new file mode 100644 index 00000000000..c4cd9a8c8e6 --- /dev/null +++ b/src/plugins/sfdp_services/base/tcp-check/api.c @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static u32 +sfdp_tcp_check_session_flags_encode (u32 x) +{ + return clib_host_to_net_u32 (x); +}; + +static void +sfdp_tcp_send_session_details (vl_api_registration_t *rp, u32 context, + u32 session_index, u32 thread_index, + sfdp_session_t *session, + sfdp_tcp_check_session_state_t *tcp_session) +{ + sfdp_main_t *sfdp = &sfdp_main; + sfdp_tcp_check_main_t *tcp = &sfdp_tcp; + vl_api_sfdp_tcp_session_details_t *mp; + sfdp_session_ip46_key_t skey; + sfdp_tenant_t *tenant; + u32 tenant_id; + size_t msg_size; + u8 n_keys = sfdp_session_n_keys (session); + tenant = sfdp_tenant_at_index (sfdp, session->tenant_idx); + tenant_id = tenant->tenant_id; + msg_size = sizeof (*mp) + sizeof (mp->keys[0]) * n_keys; + + mp = vl_msg_api_alloc_zero (msg_size); + mp->_vl_msg_id = ntohs (VL_API_SFDP_TCP_SESSION_DETAILS + tcp->msg_id_base); + + /* fill in the message */ + mp->context = context; + mp->session_id = clib_host_to_net_u64 (session->session_id); + mp->thread_index = clib_host_to_net_u32 (thread_index); + mp->tenant_id = clib_host_to_net_u32 (tenant_id); + mp->session_idx = clib_host_to_net_u32 (session_index); + mp->session_type = sfdp_session_type_encode (session->type); + mp->flags = sfdp_tcp_check_session_flags_encode (tcp_session->flags); + mp->n_keys = n_keys; + for (int i = 0; i < n_keys; i++) + { + if ((i == 0 && + session->key_flags & SFDP_SESSION_KEY_FLAG_PRIMARY_VALID_IP4) || + (i == 1 && + session->key_flags & SFDP_SESSION_KEY_FLAG_SECONDARY_VALID_IP4)) + { + sfdp_normalise_ip4_key (session, &skey.key4, i); + sfdp_session_ip46_key_encode (&skey, IP46_TYPE_IP4, &mp->keys[i]); + } + if ((i == 0 && + session->key_flags & SFDP_SESSION_KEY_FLAG_PRIMARY_VALID_IP6) || + (i == 1 && + session->key_flags & SFDP_SESSION_KEY_FLAG_SECONDARY_VALID_IP6)) + { + sfdp_normalise_ip6_key (session, &skey.key6, i); + sfdp_session_ip46_key_encode (&skey, IP46_TYPE_IP6, &mp->keys[i]); + } + } + vl_api_send_msg (rp, (u8 *) mp); +} + +static void +vl_api_sfdp_tcp_session_dump_t_handler (vl_api_sfdp_tcp_session_dump_t *mp) +{ + sfdp_main_t *sfdp = &sfdp_main; + sfdp_tcp_check_main_t *tcp = &sfdp_tcp; + sfdp_session_t *session; + sfdp_tcp_check_session_state_t *tcp_session; + uword session_index; + vl_api_registration_t *rp; + rp = vl_api_client_index_to_registration (mp->client_index); + if (rp == 0) + return; + + sfdp_foreach_session (sfdp, session_index, session) + { + if (session->proto != IP_PROTOCOL_TCP) + continue; + tcp_session = vec_elt_at_index (tcp->state, session_index); + sfdp_tcp_send_session_details (rp, mp->context, session_index, + session->owning_thread_index, session, + tcp_session); + } +} +#include +static clib_error_t * +sfdp_tcp_check_plugin_api_hookup (vlib_main_t *vm) +{ + sfdp_tcp_check_main_t *tcp = &sfdp_tcp; + tcp->msg_id_base = setup_message_id_table (); + return 0; +} +VLIB_API_INIT_FUNCTION (sfdp_tcp_check_plugin_api_hookup); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/sfdp_services/base/tcp-check/cli.c b/src/plugins/sfdp_services/base/tcp-check/cli.c new file mode 100644 index 00000000000..aafe056d12e --- /dev/null +++ b/src/plugins/sfdp_services/base/tcp-check/cli.c @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include + +static clib_error_t * +sfdp_tcp_check_show_sessions_command_fn (vlib_main_t *vm, + unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t line_input_, *line_input = &line_input_; + clib_error_t *err = 0; + sfdp_main_t *sfdp = &sfdp_main; + sfdp_tcp_check_main_t *vtcm = &sfdp_tcp; + sfdp_session_t *session; + sfdp_tcp_check_session_state_t *tcp_session; + sfdp_tenant_t *tenant; + u32 session_index; + u32 tenant_id = ~0; + + if (unformat_user (input, unformat_line_input, line_input)) + { + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "tenant %d", &tenant_id)) + ; + else + { + err = unformat_parse_error (line_input); + break; + } + } + unformat_free (line_input); + } + + if (!err) + { + table_t session_table_ = {}, *session_table = &session_table_; + u32 n = 0; + table_add_header_col (session_table, 8, "id", "tenant", "index", "type", + "context", "ingress", "egress", "flags"); + sfdp_foreach_session (sfdp, session_index, session) + { + tenant = sfdp_tenant_at_index (sfdp, session->tenant_idx); + if (tenant_id != ~0 && tenant_id != tenant->tenant_id) + continue; + if (session->proto != IP_PROTOCOL_TCP) + continue; + tcp_session = vec_elt_at_index (vtcm->state, session_index); + n = sfdp_table_format_insert_tcp_check_session ( + session_table, n, sfdp, session_index, session, tcp_session); + } + vlib_cli_output (vm, "%U", format_table, session_table); + table_free (session_table); + } + + return err; +} + +VLIB_CLI_COMMAND (show_sfdp_tcp_check_sessions_command, static) = { + .path = "show sfdp tcp session-table", + .short_help = "show sfdp tcp session-table [tenant ]", + .function = sfdp_tcp_check_show_sessions_command_fn, +}; \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/tcp-check/format.c b/src/plugins/sfdp_services/base/tcp-check/format.c new file mode 100644 index 00000000000..f87a8f903fd --- /dev/null +++ b/src/plugins/sfdp_services/base/tcp-check/format.c @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include +#include + +u8 * +format_sfdp_tcp_check_session_flags (u8 *s, va_list *args) +{ + u32 flags = va_arg (*args, u32); +#define _(name, x, str) \ + if (flags & SFDP_TCP_CHECK_SESSION_FLAG_##name) \ + s = format (s, "%s", (str)); + foreach_sfdp_tcp_check_session_flag +#undef _ + + return s; +} + +u32 +sfdp_table_format_insert_tcp_check_session ( + table_t *t, u32 n, sfdp_main_t *sfdp, u32 session_index, + sfdp_session_t *session, sfdp_tcp_check_session_state_t *tcp_session) +{ + sfdp_parser_main_t *pm = &sfdp_parser_main; + u64 session_net = clib_host_to_net_u64 (session->session_id); + sfdp_tenant_t *tenant = sfdp_tenant_at_index (sfdp, session->tenant_idx); + sfdp_session_ip46_key_t skey; + __clib_aligned (CLIB_CACHE_LINE_BYTES) + u8 kdata[SFDP_PARSER_MAX_KEY_SIZE]; + sfdp_parser_data_t *parser; + /* Session id */ + table_format_cell (t, n, 0, "0x%U", format_hex_bytes, &session_net, + sizeof (session_net)); + /* Tenant id */ + table_format_cell (t, n, 1, "%d", tenant->tenant_id); + /* Session index */ + table_format_cell (t, n, 2, "%d", session_index); + /* Session type */ + table_format_cell (t, n, 3, "%U", format_sfdp_session_type, session->type, + session->parser_index[SFDP_SESSION_KEY_PRIMARY]); + /* Session flags */ + table_format_cell (t, n, 4, "%U", format_sfdp_tcp_check_session_flags, + tcp_session->flags); + if (session->key_flags & SFDP_SESSION_KEY_FLAG_PRIMARY_VALID_IP4) + { + sfdp_normalise_ip4_key (session, &skey.key4, SFDP_SESSION_KEY_PRIMARY); + table_format_cell (t, n, 5, "%U", format_sfdp_ipv4_context_id, + &skey.key4); + table_format_cell (t, n, 6, "%U", format_sfdp_ipv4_ingress, &skey.key4); + table_format_cell (t, n, 7, "%U", format_sfdp_ipv4_egress, &skey.key4); + } + else if (session->key_flags & SFDP_SESSION_KEY_FLAG_PRIMARY_VALID_IP6) + { + sfdp_normalise_ip6_key (session, &skey.key6, SFDP_SESSION_KEY_PRIMARY); + table_format_cell (t, n, 5, "%U", format_sfdp_ipv6_context_id, + &skey.key6); + table_format_cell (t, n, 6, "%U", format_sfdp_ipv6_ingress, &skey.key6); + table_format_cell (t, n, 7, "%U", format_sfdp_ipv6_egress, &skey.key6); + } + else if (session->key_flags & SFDP_SESSION_KEY_FLAG_PRIMARY_VALID_USER) + { + parser = vec_elt_at_index ( + pm->parsers, session->parser_index[SFDP_SESSION_KEY_PRIMARY]); + parser->normalize_key_fn (session, kdata, SFDP_SESSION_KEY_PRIMARY); + table_format_cell ( + t, n, 5, "%U", parser->format_fn[SFDP_PARSER_FORMAT_FUNCTION_CONTEXT], + kdata); + table_format_cell ( + t, n, 6, "%U", parser->format_fn[SFDP_PARSER_FORMAT_FUNCTION_INGRESS], + kdata); + table_format_cell (t, n, 7, "%U", + parser->format_fn[SFDP_PARSER_FORMAT_FUNCTION_EGRESS], + kdata); + } + n += 1; + if (session->key_flags & SFDP_SESSION_KEY_FLAG_SECONDARY_VALID_IP4) + { + sfdp_normalise_ip4_key (session, &skey.key4, SFDP_SESSION_KEY_SECONDARY); + table_format_cell (t, n, 5, "%U", format_sfdp_ipv4_context_id, + &skey.key4); + table_format_cell (t, n, 6, "%U", format_sfdp_ipv4_ingress, &skey.key4); + table_format_cell (t, n, 7, "%U", format_sfdp_ipv4_egress, &skey.key4); + n += 1; + } + else if (session->key_flags & SFDP_SESSION_KEY_FLAG_SECONDARY_VALID_IP6) + { + sfdp_normalise_ip6_key (session, &skey.key6, SFDP_SESSION_KEY_SECONDARY); + table_format_cell (t, n, 5, "%U", format_sfdp_ipv6_context_id, + &skey.key6); + table_format_cell (t, n, 6, "%U", format_sfdp_ipv6_ingress, &skey.key6); + table_format_cell (t, n, 7, "%U", format_sfdp_ipv6_egress, &skey.key6); + n += 1; + } + else if (session->key_flags & SFDP_SESSION_KEY_FLAG_SECONDARY_VALID_USER) + { + parser = vec_elt_at_index ( + pm->parsers, session->parser_index[SFDP_SESSION_KEY_SECONDARY]); + parser->normalize_key_fn (session, kdata, SFDP_SESSION_KEY_SECONDARY); + table_format_cell ( + t, n, 5, "%U", parser->format_fn[SFDP_PARSER_FORMAT_FUNCTION_CONTEXT], + kdata); + table_format_cell ( + t, n, 6, "%U", parser->format_fn[SFDP_PARSER_FORMAT_FUNCTION_INGRESS], + kdata); + table_format_cell (t, n, 7, "%U", + parser->format_fn[SFDP_PARSER_FORMAT_FUNCTION_EGRESS], + kdata); + n += 1; + } + return n; +} diff --git a/src/plugins/sfdp_services/base/tcp-check/node.c b/src/plugins/sfdp_services/base/tcp-check/node.c new file mode 100644 index 00000000000..6c395e51cf8 --- /dev/null +++ b/src/plugins/sfdp_services/base/tcp-check/node.c @@ -0,0 +1,310 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include +#include + +#define foreach_sfdp_tcp_check_error _ (DROP, "drop") + +typedef enum +{ +#define _(sym, str) SFDP_TCP_CHECK_ERROR_##sym, + foreach_sfdp_tcp_check_error +#undef _ + SFDP_TCP_CHECK_N_ERROR, +} sfdp_tcp_check_error_t; + +static char *sfdp_tcp_check_error_strings[] = { +#define _(sym, string) string, + foreach_sfdp_tcp_check_error +#undef _ +}; + +typedef struct +{ + u32 flow_id; + u32 old_state_flags; + u32 new_state_flags; +} sfdp_tcp_check_trace_t; + +static u8 * +format_sfdp_tcp_check_trace (u8 *s, va_list *args) +{ + vlib_main_t __clib_unused *vm = va_arg (*args, vlib_main_t *); + vlib_node_t __clib_unused *node = va_arg (*args, vlib_node_t *); + sfdp_tcp_check_trace_t *t = va_arg (*args, sfdp_tcp_check_trace_t *); + u32 indent = format_get_indent (s); + indent += 2; + s = format (s, "sfdp-tcp-check: flow-id %u (session %u, %s)\n", t->flow_id, + t->flow_id >> 1, t->flow_id & 0x1 ? "reverse" : "forward"); + s = format (s, "%Uold session flags: %U\n", format_white_space, indent, + format_sfdp_tcp_check_session_flags, t->old_state_flags); + s = format (s, "%Unew session flags: %U\n", format_white_space, indent, + format_sfdp_tcp_check_session_flags, t->new_state_flags); + return s; +} + +SFDP_SERVICE_DECLARE (drop) +static_always_inline void +update_state_one_pkt (sfdp_tw_t *tw, sfdp_tenant_t *tenant, + sfdp_tcp_check_session_state_t *tcp_session, + sfdp_session_t *session, f64 current_time, u8 dir, + u16 *to_next, vlib_buffer_t **b, u32 *sf, u32 *nsf) +{ + /* Parse the packet */ + /* TODO: !!! Broken with IP options !!! */ + u8 *data = vlib_buffer_get_current (b[0]); + tcp_header_t *tcph = + (void *) (data + (session->type == SFDP_SESSION_TYPE_IP4 ? + sizeof (ip4_header_t) : + sizeof (ip6_header_t))); + ip4_header_t *ip4 = (void *) data; + ip6_header_t *ip6 = (void *) data; + /* Ignore non first fragments */ + if (session->type == SFDP_SESSION_TYPE_IP4 && + ip4->flags_and_fragment_offset & + clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS - 1)) + { + sfdp_next (b[0], to_next); + return; + } + + if (session->type == SFDP_SESSION_TYPE_IP6 && ip6_ext_hdr (ip6->protocol)) + { + ip6_ext_hdr_chain_t chain = { 0 }; + int res = ip6_ext_header_walk (b[0], ip6, IP_PROTOCOL_IPV6_FRAGMENTATION, + &chain); + if (res >= 0 && chain.eh[res].protocol == IP_PROTOCOL_IPV6_FRAGMENTATION) + { + ip6_frag_hdr_t *frag = + ip6_ext_next_header_offset (ip6, chain.eh[res].offset); + if (ip6_frag_hdr_offset (frag)) + { + sfdp_next (b[0], to_next); + return; + } + } + tcph = + ip6_ext_next_header_offset (ip6, chain.eh[chain.length - 1].offset); + } + + u8 flags = tcph->flags & SFDP_TCP_CHECK_TCP_FLAGS_MASK; + u32 acknum = clib_net_to_host_u32 (tcph->ack_number); + u32 seqnum = clib_net_to_host_u32 (tcph->seq_number); + u32 next_timeout = 0; + u8 remove_session = 0; + if (PREDICT_FALSE (tcp_session->version != session->session_version)) + { + tcp_session->version = session->session_version; + tcp_session->flags = 0; + tcp_session->as_u64_0 = 0; + if (flags != SFDP_TCP_CHECK_TCP_FLAGS_SYN) + { + /* Abnormal, put the session in blocked state */ + session->bitmaps[SFDP_FLOW_FORWARD] = SFDP_SERVICE_MASK (drop); + session->bitmaps[SFDP_FLOW_REVERSE] = SFDP_SERVICE_MASK (drop); + sfdp_buffer (b[0])->service_bitmap = SFDP_SERVICE_MASK (drop); + tcp_session->flags = SFDP_TCP_CHECK_SESSION_FLAG_BLOCKED; + } + } + nsf[0] = (sf[0] = tcp_session->flags); + if (dir == SFDP_FLOW_FORWARD) + { + if (sf[0] & SFDP_TCP_CHECK_SESSION_FLAG_BLOCKED) + goto out; + if (flags & SFDP_TCP_CHECK_TCP_FLAGS_SYN) + { + /* New session, must be a SYN otherwise bad */ + if (sf[0] == 0) + nsf[0] = SFDP_TCP_CHECK_SESSION_FLAG_WAIT_FOR_RESP_SYN | + SFDP_TCP_CHECK_SESSION_FLAG_WAIT_FOR_RESP_ACK_TO_SYN; + else + { + remove_session = 1; + goto out; + } + } + if (flags & SFDP_TCP_CHECK_TCP_FLAGS_ACK) + { + /* Either ACK to SYN */ + if (sf[0] & SFDP_TCP_CHECK_SESSION_FLAG_WAIT_FOR_INIT_ACK_TO_SYN) + nsf[0] &= ~SFDP_TCP_CHECK_SESSION_FLAG_WAIT_FOR_INIT_ACK_TO_SYN; + /* Or ACK to FIN */ + if (sf[0] & SFDP_TCP_CHECK_SESSION_FLAG_SEEN_FIN_RESP && + acknum == tcp_session->fin_num[SFDP_FLOW_REVERSE]) + nsf[0] |= SFDP_TCP_CHECK_SESSION_FLAG_SEEN_ACK_TO_FIN_INIT; + /* Or regular ACK */ + } + if (flags & SFDP_TCP_CHECK_TCP_FLAGS_FIN) + { + /*If we were up, we are not anymore */ + nsf[0] &= ~SFDP_TCP_CHECK_SESSION_FLAG_ESTABLISHED; + /*Seen our FIN, wait for the other FIN and for an ACK*/ + tcp_session->fin_num[SFDP_FLOW_FORWARD] = seqnum + 1; + nsf[0] |= SFDP_TCP_CHECK_SESSION_FLAG_SEEN_FIN_INIT; + } + if (flags & SFDP_TCP_CHECK_TCP_FLAGS_RST) + { + /* Reason to kill the connection */ + remove_session = 1; + goto out; + } + } + if (dir == SFDP_FLOW_REVERSE) + { + if (sf[0] & SFDP_TCP_CHECK_SESSION_FLAG_BLOCKED) + goto out; + if (flags & SFDP_TCP_CHECK_TCP_FLAGS_SYN) + { + if (sf[0] & SFDP_TCP_CHECK_SESSION_FLAG_WAIT_FOR_RESP_SYN) + nsf[0] ^= SFDP_TCP_CHECK_SESSION_FLAG_WAIT_FOR_RESP_SYN | + SFDP_TCP_CHECK_SESSION_FLAG_WAIT_FOR_INIT_ACK_TO_SYN; + } + if (flags & SFDP_TCP_CHECK_TCP_FLAGS_ACK) + { + /* Either ACK to SYN */ + if (sf[0] & SFDP_TCP_CHECK_SESSION_FLAG_WAIT_FOR_RESP_ACK_TO_SYN) + nsf[0] &= ~SFDP_TCP_CHECK_SESSION_FLAG_WAIT_FOR_RESP_ACK_TO_SYN; + /* Or ACK to FIN */ + if (sf[0] & SFDP_TCP_CHECK_SESSION_FLAG_SEEN_FIN_INIT && + acknum == tcp_session->fin_num[SFDP_FLOW_FORWARD]) + nsf[0] |= SFDP_TCP_CHECK_SESSION_FLAG_SEEN_ACK_TO_FIN_RESP; + /* Or regular ACK */ + } + if (flags & SFDP_TCP_CHECK_TCP_FLAGS_FIN) + { + /*If we were up, we are not anymore */ + nsf[0] &= ~SFDP_TCP_CHECK_SESSION_FLAG_ESTABLISHED; + /* Seen our FIN, wait for the other FIN and for an ACK */ + tcp_session->fin_num[SFDP_FLOW_REVERSE] = seqnum + 1; + nsf[0] |= SFDP_TCP_CHECK_SESSION_FLAG_SEEN_FIN_RESP; + } + if (flags & SFDP_TCP_CHECK_TCP_FLAGS_RST) + { + /* Reason to kill the connection */ + nsf[0] = SFDP_TCP_CHECK_SESSION_FLAG_REMOVING; + remove_session = 1; + goto out; + } + } + /* If all flags are cleared connection is established! */ + if (nsf[0] == 0) + { + nsf[0] = SFDP_TCP_CHECK_SESSION_FLAG_ESTABLISHED; + session->state = SFDP_SESSION_STATE_ESTABLISHED; + } + + /* If all FINs are ACKED, game over */ + if ((nsf[0] & (SFDP_TCP_CHECK_SESSION_FLAG_SEEN_ACK_TO_FIN_INIT)) && + (nsf[0] & SFDP_TCP_CHECK_SESSION_FLAG_SEEN_ACK_TO_FIN_RESP)) + { + nsf[0] = SFDP_TCP_CHECK_SESSION_FLAG_REMOVING; + remove_session = 1; + } +out: + tcp_session->flags = nsf[0]; + if (remove_session) + next_timeout = 0; + else if (nsf[0] & SFDP_TCP_CHECK_SESSION_FLAG_ESTABLISHED) + next_timeout = tenant->timeouts[SFDP_TIMEOUT_TCP_ESTABLISHED]; + else if (nsf[0] & SFDP_TCP_CHECK_SESSION_FLAG_BLOCKED) + next_timeout = tenant->timeouts[SFDP_TIMEOUT_SECURITY]; + else + next_timeout = tenant->timeouts[SFDP_TIMEOUT_EMBRYONIC]; + + sfdp_session_timer_update_maybe_past (tw, SFDP_SESSION_TIMER (session), + current_time, next_timeout); + sfdp_next (b[0], to_next); + return; +} + +VLIB_NODE_FN (sfdp_tcp_check_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + sfdp_main_t *sfdp = &sfdp_main; + sfdp_timer_main_t *sfdpt = &sfdp_timer_main; + sfdp_tcp_check_main_t *vtcm = &sfdp_tcp; + u32 thread_index = vlib_get_thread_index (); + sfdp_timer_per_thread_data_t *timer_ptd = + vec_elt_at_index (sfdpt->per_thread_data, thread_index); + + sfdp_session_t *session; + sfdp_tenant_t *tenant; + u32 session_idx; + sfdp_tcp_check_session_state_t *tcp_session; + sfdp_tw_t *tw = &timer_ptd->wheel; + u32 *from = vlib_frame_vector_args (frame); + u32 n_left = frame->n_vectors; + u16 next_indices[VLIB_FRAME_SIZE], *to_next = next_indices; + u32 state_flags[VLIB_FRAME_SIZE], *sf = state_flags; + u32 new_state_flags[VLIB_FRAME_SIZE], *nsf = new_state_flags; + f64 current_time = timer_ptd->current_time; + + vlib_get_buffers (vm, from, bufs, n_left); + while (n_left > 0) + { + session_idx = sfdp_session_from_flow_index (b[0]->flow_id); + session = sfdp_session_at_index (session_idx); + tcp_session = vec_elt_at_index (vtcm->state, session_idx); + tenant = sfdp_tenant_at_index (sfdp, sfdp_buffer (b[0])->tenant_index); + if (sfdp_direction_from_flow_index (b[0]->flow_id) == SFDP_FLOW_FORWARD) + update_state_one_pkt (tw, tenant, tcp_session, session, current_time, + SFDP_FLOW_FORWARD, to_next, b, sf, nsf); + else + update_state_one_pkt (tw, tenant, tcp_session, session, current_time, + SFDP_FLOW_REVERSE, to_next, b, sf, nsf); + n_left -= 1; + b += 1; + to_next += 1; + sf += 1; + nsf += 1; + } + vlib_buffer_enqueue_to_next (vm, node, from, next_indices, frame->n_vectors); + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) + { + int i; + b = bufs; + sf = state_flags; + nsf = new_state_flags; + n_left = frame->n_vectors; + for (i = 0; i < n_left; i++) + { + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) + { + sfdp_tcp_check_trace_t *t = + vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->flow_id = b[0]->flow_id; + t->old_state_flags = sf[0]; + t->new_state_flags = nsf[0]; + b++; + sf++; + nsf++; + } + else + break; + } + } + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (sfdp_tcp_check_node) = { + .name = "sfdp-tcp-check", + .vector_size = sizeof (u32), + .format_trace = format_sfdp_tcp_check_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN (sfdp_tcp_check_error_strings), + .error_strings = sfdp_tcp_check_error_strings +}; + +SFDP_SERVICE_DEFINE (tcp_check) = { + .node_name = "sfdp-tcp-check", + .runs_before = SFDP_SERVICES (0), + .runs_after = SFDP_SERVICES ("sfdp-drop", "sfdp-l4-lifecycle"), + .is_terminal = 0 +}; diff --git a/src/plugins/sfdp_services/base/tcp-check/tcp_check.api b/src/plugins/sfdp_services/base/tcp-check/tcp_check.api new file mode 100644 index 00000000000..93144bb350f --- /dev/null +++ b/src/plugins/sfdp_services/base/tcp-check/tcp_check.api @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +option version = "0.0.1"; +import "vnet/ip/ip_types.api"; +import "vnet/sfdp/sfdp_types.api"; + +enumflag sfdp_tcp_check_session_flags : u32 +{ + SFDP_API_TCP_CHECK_SESSION_FLAG_WAIT_FOR_RESP_SYN = 0x1, + SFDP_API_TCP_CHECK_SESSION_FLAG_WAIT_FOR_INIT_ACK_TO_SYN, + SFDP_API_TCP_CHECK_SESSION_FLAG_WAIT_FOR_RESP_ACK_TO_SYN = 0x4, + SFDP_API_TCP_CHECK_SESSION_FLAG_SEEN_FIN_INIT = 0x8, + SFDP_API_TCP_CHECK_SESSION_FLAG_SEEN_FIN_RESP = 0x10, + SFDP_API_TCP_CHECK_SESSION_FLAG_SEEN_ACK_TO_FIN_INIT = 0x20, + SFDP_API_TCP_CHECK_SESSION_FLAG_SEEN_ACK_TO_FIN_RESP = 0x40, + SFDP_API_TCP_CHECK_SESSION_FLAG_ESTABLISHED = 0x80, + SFDP_API_TCP_CHECK_SESSION_FLAG_REMOVING = 0x100, + SFDP_API_TCP_CHECK_SESSION_FLAG_BLOCKED = 0x200, +}; + +define sfdp_tcp_session_dump +{ + u32 client_index; + u32 context; +}; + +define sfdp_tcp_session_details +{ + u32 context; + + u64 session_id; + u32 thread_index; + u32 tenant_id; + u32 session_idx; + vl_api_sfdp_session_type_t session_type; + vl_api_sfdp_tcp_check_session_flags_t flags; + u8 n_keys; + vl_api_sfdp_session_key_t keys[n_keys]; +}; \ No newline at end of file diff --git a/src/plugins/sfdp_services/base/tcp-check/tcp_check.c b/src/plugins/sfdp_services/base/tcp-check/tcp_check.c new file mode 100644 index 00000000000..37f353fc040 --- /dev/null +++ b/src/plugins/sfdp_services/base/tcp-check/tcp_check.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include + +sfdp_tcp_check_main_t sfdp_tcp; + +static clib_error_t * +sfdp_tcp_check_init (vlib_main_t *vm) +{ + sfdp_tcp_check_main_t *vtcm = &sfdp_tcp; + vec_validate (vtcm->state, sfdp_num_sessions ()); + return 0; +}; + +VLIB_INIT_FUNCTION (sfdp_tcp_check_init); diff --git a/src/plugins/sfdp_services/base/tcp-check/tcp_check.h b/src/plugins/sfdp_services/base/tcp-check/tcp_check.h new file mode 100644 index 00000000000..3918ce6fcd5 --- /dev/null +++ b/src/plugins/sfdp_services/base/tcp-check/tcp_check.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#ifndef __included_sfdp_tcp_check_h__ +#define __included_sfdp_tcp_check_h__ + +#include +#include +/* Convention: uppercase relates to responder lowercase to initiator */ +#define foreach_sfdp_tcp_check_session_flag \ + _ (WAIT_FOR_RESP_SYN, 0, "S") \ + _ (WAIT_FOR_INIT_ACK_TO_SYN, 1, "a") \ + _ (WAIT_FOR_RESP_ACK_TO_SYN, 2, "A") \ + _ (SEEN_FIN_INIT, 3, "f") \ + _ (SEEN_FIN_RESP, 4, "F") \ + _ (SEEN_ACK_TO_FIN_INIT, 5, "r") \ + _ (SEEN_ACK_TO_FIN_RESP, 6, "R") \ + _ (ESTABLISHED, 7, "U") \ + _ (REMOVING, 8, "D") \ + _ (BLOCKED, 9, "X") + +typedef enum +{ +#define _(name, x, str) SFDP_TCP_CHECK_SESSION_FLAG_##name = (1 << (x)), + foreach_sfdp_tcp_check_session_flag SFDP_TCP_CHECK_SESSION_N_FLAG +#undef _ +} sfdp_tcp_check_session_flag_t; + +#define SFDP_TCP_CHECK_TCP_FLAGS_MASK (0x17) +#define SFDP_TCP_CHECK_TCP_FLAGS_FIN (0x1) +#define SFDP_TCP_CHECK_TCP_FLAGS_SYN (0x2) +#define SFDP_TCP_CHECK_TCP_FLAGS_RST (0x4) +#define SFDP_TCP_CHECK_TCP_FLAGS_ACK (0x10) +/* transitions are labelled with TCP Flags encoded in u8 as + 0 0 0 ACK 0 RST SYN FIN + Transition table for each direction is 32x9 + result of the lookup is (what is set, what is cleared) + */ +/*#define foreach_sfdp_tcp_check_forward_transition \ +_(WAIT_FOR_INIT_ACK_TO_SYN, ACK, )*/ + +typedef struct +{ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + u32 flags; + union + { + u32 fin_num[SFDP_FLOW_F_B_N]; + u64 as_u64_0; + }; + session_version_t version; +} sfdp_tcp_check_session_state_t; + +typedef struct +{ + sfdp_tcp_check_session_state_t *state; /* vec indexed by session-index */ + u16 msg_id_base; +} sfdp_tcp_check_main_t; + +extern sfdp_tcp_check_main_t sfdp_tcp; + +format_function_t format_sfdp_tcp_check_session_flags; +u32 sfdp_table_format_insert_tcp_check_session ( + table_t *t, u32 n, sfdp_main_t *sfdp, u32 session_index, + sfdp_session_t *session, sfdp_tcp_check_session_state_t *tcp_session); +#endif /* __included_sfdp_tcp_check_h__ */ \ No newline at end of file diff --git a/src/plugins/sfdp_services/dot1q/dot1q.c b/src/plugins/sfdp_services/dot1q/dot1q.c new file mode 100644 index 00000000000..b0995bbdc41 --- /dev/null +++ b/src/plugins/sfdp_services/dot1q/dot1q.c @@ -0,0 +1,296 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include +#include +#include +typedef struct +{ + u32 next_index; + u32 sw_if_index; +} sfdp_dummy_dot1q_input_trace_t; + +static u8 * +format_sfdp_dummy_dot1q_input_trace (u8 *s, va_list *args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + CLIB_UNUSED (sfdp_dummy_dot1q_input_trace_t * t) = + va_arg (*args, sfdp_dummy_dot1q_input_trace_t *); + + /*s = format (s, "snort-enq: sw_if_index %d, next index %d\n", + t->sw_if_index, t->next_index);*/ + + return s; +} + +#define foreach_sfdp_dummy_dot1q_input_next \ + _ (LOOKUP_IP4, "sfdp-lookup-ip4") \ + _ (LOOKUP_IP6, "sfdp-lookup-ip6") +#define foreach_sfdp_dummy_dot1q_input_error _ (NOERROR, "No error") + +typedef enum +{ +#define _(sym, str) SFDP_DUMMY_DOT1Q_ERROR_##sym, + foreach_sfdp_dummy_dot1q_input_error +#undef _ + SFDP_DUMMY_DOT1Q_N_ERROR, +} sfdp_dummy_dot1q_input_error_t; + +static char *sfdp_dummy_dot1q_input_error_strings[] = { +#define _(sym, string) string, + foreach_sfdp_dummy_dot1q_input_error +#undef _ +}; + +typedef enum +{ +#define _(s, n) SFDP_DUMMY_DOT1Q_INPUT_NEXT_##s, + foreach_sfdp_dummy_dot1q_input_next +#undef _ + SFDP_DUMMY_DOT1Q_INPUT_N_NEXT +} sfdp_dummy_dot1q_input_next_t; + +/*-----------------------------*/ + +typedef struct +{ + u32 next_index; + u32 sw_if_index; +} sfdp_dummy_dot1q_output_trace_t; + +static u8 * +format_sfdp_dummy_dot1q_output_trace (u8 *s, va_list *args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + CLIB_UNUSED (sfdp_dummy_dot1q_output_trace_t * t) = + va_arg (*args, sfdp_dummy_dot1q_output_trace_t *); + + /*s = format (s, "snort-enq: sw_if_index %d, next index %d\n", + t->sw_if_index, t->next_index);*/ + + return s; +} + +#define foreach_sfdp_dummy_dot1q_output_next \ + _ (LOOKUP_IP4, "sfdp-lookup-ip4") \ + _ (LOOKUP_IP6, "sfdp-lookup-ip6") +#define foreach_sfdp_dummy_dot1q_output_error _ (NOERROR, "No error") + +typedef enum +{ +#define _(sym, str) SFDP_DUMMY_DOT1Q_OUTPUT_ERROR_##sym, + foreach_sfdp_dummy_dot1q_output_error +#undef _ + SFDP_DUMMY_DOT1Q_OUTPUT_N_ERROR, +} sfdp_dummy_dot1q_output_error_t; + +static char *sfdp_dummy_dot1q_output_error_strings[] = { +#define _(sym, string) string, + foreach_sfdp_dummy_dot1q_output_error +#undef _ +}; + +typedef enum +{ +#define _(s, n) SFDP_DUMMY_DOT1Q_OUTPUT_NEXT_##s, + foreach_sfdp_dummy_dot1q_output_next +#undef _ + SFDP_DUMMY_DOT1Q_OUTPUT_N_NEXT +} sfdp_dummy_dot1q_output_next_t; + +static_always_inline void +process_one_pkt (vlib_main_t *vm, sfdp_main_t *sfdp, + vlib_combined_counter_main_t *cm, u32 thread_index, + vlib_buffer_t **b, u16 *current_next) +{ + sfdp_tenant_t *tenant; + clib_bihash_kv_8_8_t kv = { 0 }; + u8 *data = vlib_buffer_get_current (b[0]); + u32 orig_len = vlib_buffer_length_in_chain (vm, b[0]); + ethernet_header_t *eth = (void *) data; + u32 tenant_id = 0; + u32 off = sizeof (eth[0]); + u16 type = clib_net_to_host_u16 (eth->type); + u16 tenant_idx; + if (type == ETHERNET_TYPE_VLAN) + { + ethernet_vlan_header_t *vlan = (void *) (data + sizeof (eth[0])); + tenant_id = clib_net_to_host_u16 (vlan->priority_cfi_and_id) & 0xfff; + type = clib_net_to_host_u16 (vlan->type); + off += sizeof (vlan[0]); + } + if (type != ETHERNET_TYPE_IP4 && type != ETHERNET_TYPE_IP6) + { + vnet_feature_next_u16 (current_next, b[0]); + return; + } + /* Tenant-id lookup */ + kv.key = (u64) tenant_id; + if (clib_bihash_search_inline_8_8 (&sfdp->tenant_idx_by_id, &kv)) + { + /* Not found */ + vnet_feature_next_u16 (current_next, b[0]); + return; + } + tenant_idx = kv.value; + tenant = sfdp_tenant_at_index (sfdp, tenant_idx); + b[0]->flow_id = tenant->context_id; + sfdp_buffer (b[0])->tenant_index = tenant_idx; + vnet_buffer (b[0])->l2_hdr_offset = b[0]->current_data; + vnet_buffer (b[0])->l3_hdr_offset = b[0]->current_data + off; + b[0]->flags |= + VNET_BUFFER_F_L2_HDR_OFFSET_VALID | VNET_BUFFER_F_L3_HDR_OFFSET_VALID; + current_next[0] = type == ETHERNET_TYPE_IP4 ? + SFDP_DUMMY_DOT1Q_INPUT_NEXT_LOOKUP_IP4 : + SFDP_DUMMY_DOT1Q_INPUT_NEXT_LOOKUP_IP6; + vlib_increment_combined_counter (cm, thread_index, tenant_idx, 1, + orig_len - off); + vlib_buffer_advance (b[0], off); +} + +static_always_inline uword +sfdp_dummy_dot1q_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame) +{ + /* + * use VNI as tenant ID + * tenant_id -> tenant index + * drop unknown tenants + * store tenant_id into opaque1 + * advance current data to beginning of IP packet + */ + sfdp_main_t *sfdp = &sfdp_main; + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; + u32 *from = vlib_frame_vector_args (frame); + u32 n_left = frame->n_vectors; + u16 next_indices[VLIB_FRAME_SIZE], *current_next; + u32 thread_index = vlib_get_thread_index (); + vlib_combined_counter_main_t *cm = + &sfdp->tenant_data_ctr[SFDP_TENANT_DATA_COUNTER_INCOMING]; + vlib_get_buffers (vm, from, bufs, n_left); + b = bufs; + current_next = next_indices; + + while (n_left) + { + process_one_pkt (vm, sfdp, cm, thread_index, b, current_next); + b += 1; + current_next += 1; + n_left -= 1; + } + vlib_buffer_enqueue_to_next (vm, node, from, next_indices, frame->n_vectors); + return frame->n_vectors; +} + +VLIB_NODE_FN (sfdp_dummy_dot1q_input_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return sfdp_dummy_dot1q_input_inline (vm, node, frame); +} + +VLIB_REGISTER_NODE (sfdp_dummy_dot1q_input_node) = { + .name = "sfdp-dummy-dot1q-input", + .vector_size = sizeof (u32), + .format_trace = format_sfdp_dummy_dot1q_input_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN (sfdp_dummy_dot1q_input_error_strings), + .error_strings = sfdp_dummy_dot1q_input_error_strings, + .n_next_nodes = SFDP_DUMMY_DOT1Q_INPUT_N_NEXT, + .next_nodes = { + [SFDP_DUMMY_DOT1Q_INPUT_NEXT_LOOKUP_IP4] = "sfdp-lookup-ip4", + [SFDP_DUMMY_DOT1Q_INPUT_NEXT_LOOKUP_IP6] = "sfdp-lookup-ip6", + }, +}; + +VNET_FEATURE_INIT (sfdp_dummy_dot1q_input_feat, static) = { + .arc_name = "device-input", + .node_name = "sfdp-dummy-dot1q-input", +}; + +#define SFDP_PREFETCH_SIZE 8 +VLIB_NODE_FN (sfdp_dummy_dot1q_output_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + sfdp_main_t *sfdp = &sfdp_main; + vlib_combined_counter_main_t *cm = + &sfdp->tenant_data_ctr[SFDP_TENANT_DATA_COUNTER_OUTGOING]; + u32 thread_index = vlib_get_thread_index (); + u16 tenant_idx[SFDP_PREFETCH_SIZE]; + u32 orig_len[SFDP_PREFETCH_SIZE]; + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + u16 next_indices[VLIB_FRAME_SIZE], *to_next = next_indices; + + u32 *from = vlib_frame_vector_args (frame); + u32 n_left = frame->n_vectors; + + vlib_get_buffers (vm, from, bufs, n_left); + + while (n_left > SFDP_PREFETCH_SIZE) + { + word l2_len[SFDP_PREFETCH_SIZE]; + if (n_left > 2 * SFDP_PREFETCH_SIZE) + for (int i = 0; i < SFDP_PREFETCH_SIZE; i++) + vlib_prefetch_buffer_header (b[0], STORE); + + for (int i = 0; i < SFDP_PREFETCH_SIZE; i++) + { + orig_len[i] = vlib_buffer_length_in_chain (vm, b[i]); + tenant_idx[i] = sfdp_buffer (b[i])->tenant_index; + vlib_increment_combined_counter (cm, thread_index, tenant_idx[i], 1, + orig_len[i]); + l2_len[i] = vnet_buffer (b[i])->l3_hdr_offset; + l2_len[i] -= vnet_buffer (b[i])->l2_hdr_offset; + vlib_buffer_advance (b[i], -l2_len[i]); + vnet_feature_next_u16 (to_next + i, b[i]); + } + + b += SFDP_PREFETCH_SIZE; + to_next += SFDP_PREFETCH_SIZE; + n_left -= SFDP_PREFETCH_SIZE; + } + while (n_left) + { + word l2_len; + u32 orig_len = vlib_buffer_length_in_chain (vm, b[0]); + u16 tenant_idx = sfdp_buffer (b[0])->tenant_index; + vlib_increment_combined_counter (cm, thread_index, tenant_idx, 1, + orig_len); + l2_len = vnet_buffer (b[0])->l3_hdr_offset; + l2_len -= vnet_buffer (b[0])->l2_hdr_offset; + vlib_buffer_advance (b[0], -l2_len); + vnet_feature_next_u16 (to_next, b[0]); + + b += 1; + to_next += 1; + n_left -= 1; + } + vlib_buffer_enqueue_to_next (vm, node, from, next_indices, frame->n_vectors); + return frame->n_vectors; +} +#undef SFDP_PREFETCH_SIZE + +VLIB_REGISTER_NODE (sfdp_dummy_dot1q_output_node) = { + .name = "sfdp-dummy-dot1q-output", + .vector_size = sizeof (u32), + .format_trace = format_sfdp_dummy_dot1q_output_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN (sfdp_dummy_dot1q_output_error_strings), + .error_strings = sfdp_dummy_dot1q_output_error_strings, + + .sibling_of = "sfdp-dummy-dot1q-input" +}; + +SFDP_SERVICE_DEFINE (dummy_dot1q_output) = { + .node_name = "sfdp-dummy-dot1q-output", + .runs_before = SFDP_SERVICES (0), + .runs_after = SFDP_SERVICES ("sfdp-drop", "sfdp-l4-lifecycle", + "sfdp-tcp-check"), + .is_terminal = 1 +}; diff --git a/src/plugins/sfdp_services/geneve/api.c b/src/plugins/sfdp_services/geneve/api.c new file mode 100644 index 00000000000..94e52d21292 --- /dev/null +++ b/src/plugins/sfdp_services/geneve/api.c @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define REPLY_MSG_ID_BASE gw->msg_id_base +#include + +static void +vl_api_sfdp_gateway_set_geneve_output_t_handler ( + vl_api_sfdp_gateway_set_geneve_output_t *mp) +{ + gw_main_t *gw = &gateway_main; + int rv; + vl_api_sfdp_gateway_set_geneve_output_reply_t *rmp; + gw_set_geneve_output_args_t args; + args.tenant_id = clib_net_to_host_u32 (mp->tenant_id); + ASSERT (mp->src.af == ADDRESS_IP4); + ASSERT (mp->dst.af == ADDRESS_IP4); + ip4_address_decode (mp->src.un.ip4, &args.src_addr); + ip4_address_decode (mp->dst.un.ip4, &args.dst_addr); + args.src_port = mp->src_port; + args.dst_port = mp->dst_port; + args.direction = mp->dir; + args.static_mac = mp->static_mac; + args.output_tenant_id = clib_net_to_host_u32 (mp->output_tenant_id); + mac_address_decode (mp->src_mac, &args.src_mac); + mac_address_decode (mp->dst_mac, &args.dst_mac); + gw_set_geneve_output (&args); + rv = args.err ? -1 : 0; + REPLY_MACRO (VL_API_SFDP_GATEWAY_SET_GENEVE_OUTPUT_REPLY); +} + +static void +vl_api_sfdp_gateway_geneve_input_enable_disable_t_handler ( + vl_api_sfdp_gateway_geneve_input_enable_disable_t *mp) +{ + gw_main_t *gw = &gateway_main; + int rv; + vl_api_sfdp_gateway_geneve_input_enable_disable_reply_t *rmp; + gw_enable_disable_geneve_input_args_t args; + args.enable_disable = mp->is_enable; + args.sw_if_index = clib_net_to_host_u32 (mp->sw_if_index); + gw_enable_disable_geneve_input (&args); + rv = args.err ? -1 : 0; + REPLY_MACRO (VL_API_SFDP_GATEWAY_GENEVE_INPUT_ENABLE_DISABLE_REPLY); +} + +#include +static clib_error_t * +sfdp_gateway_api_hookup (vlib_main_t *vm) +{ + gw_main_t *gw = &gateway_main; + gw->msg_id_base = setup_message_id_table (); + return 0; +} +VLIB_API_INIT_FUNCTION (sfdp_gateway_api_hookup); diff --git a/src/plugins/sfdp_services/geneve/cli.c b/src/plugins/sfdp_services/geneve/cli.c new file mode 100644 index 00000000000..e1af6a4498f --- /dev/null +++ b/src/plugins/sfdp_services/geneve/cli.c @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include + +/* + * add CLI: + * set gateway geneve-output tenant src dst + * src-port dst-port + * + * it sets the geneve output data in each direction + */ + +static clib_error_t * +gateway_set_output_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t line_input_, *line_input = &line_input_; + gw_set_geneve_output_args_t args = { .tenant_id = ~0, + .src_addr = { .as_u32 = ~0 }, + .dst_addr = { .as_u32 = ~0 }, + .src_port = ~0, + .dst_port = ~0, + .direction = ~0, + .output_tenant_id = ~0, + .src_mac = { .bytes = { 0 } }, + .dst_mac = { .bytes = { 0 } } }; + clib_error_t *err = 0; + u32 tmp; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "tenant %d", &args.tenant_id)) + ; + else if (unformat (line_input, "output-tenant %d", + &args.output_tenant_id)) + ; + else if (unformat (line_input, "src %U", unformat_ip4_address, + &args.src_addr)) + ; + else if (unformat (line_input, "dst %U", unformat_ip4_address, + &args.dst_addr)) + ; + else if (unformat (line_input, "src-port %d", &tmp)) + args.src_port = clib_host_to_net_u16 (tmp); + else if (unformat (line_input, "dst-port %d", &tmp)) + args.dst_port = clib_host_to_net_u16 (tmp); + else if (unformat (line_input, "src-mac %U dst-mac %U", + unformat_mac_address, &args.src_mac, + unformat_mac_address, &args.dst_mac)) + args.static_mac = 1; + else if (unformat (line_input, "forward")) + args.direction = SFDP_FLOW_FORWARD; + else if (unformat (line_input, "reverse")) + args.direction = SFDP_FLOW_REVERSE; + else + { + err = unformat_parse_error (line_input); + goto done; + } + } + if (args.tenant_id == ~0 || args.src_addr.as_u32 == ~0 || + args.dst_addr.as_u32 == ~0 || args.src_port == (u16) ~0 || + args.dst_port == (u16) ~0 || args.direction == (u8) ~0) + { + err = clib_error_return (0, "missing geneve output parameters"); + goto done; + } + gw_set_geneve_output (&args); + err = args.err; +done: + unformat_free (line_input); + return err; +} + +VLIB_CLI_COMMAND (gateway_set_output_command, static) = { + .path = "set sfdp gateway geneve-output", + .short_help = "set sfdp gateway geneve-output tenant " + "src dst " + "src-port dst-port " + "[output-tenant ] " + "[src-mac dst-mac ]" + "", + .function = gateway_set_output_command_fn, +}; + +/* + * Add CLI: + * gateway geneve-input interface + * + */ + +static clib_error_t * +gateway_geneve_enable_disable_command_fn (vlib_main_t *vm, + unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + int enable_disable = -1; + gw_enable_disable_geneve_input_args_t args; + unformat_input_t line_input_, *line_input = &line_input_; + clib_error_t *err = 0; + args.sw_if_index = ~0; + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "interface %U", unformat_vnet_sw_interface, + vnet_get_main (), &args.sw_if_index)) + ; + else if (unformat (line_input, "enable")) + enable_disable = 1; + else if (unformat (line_input, "disable")) + enable_disable = 0; + else + { + err = unformat_parse_error (line_input); + goto done; + } + } + if (enable_disable == -1) + { + err = clib_error_return (0, "enable or disable?"); + goto done; + } + if (args.sw_if_index == ~0) + { + err = clib_error_return (0, "valid interface name required"); + goto done; + } + args.enable_disable = enable_disable; + gw_enable_disable_geneve_input (&args); + err = args.err; + +done: + unformat_free (line_input); + return err; +} + +VLIB_CLI_COMMAND (gateway_geneve_input_enable_disable_command, static) = { + .path = "sfdp gateway geneve-input", + .short_help = + "sfdp gateway geneve-input interface ", + .function = gateway_geneve_enable_disable_command_fn, +}; \ No newline at end of file diff --git a/src/plugins/sfdp_services/geneve/gateway.api b/src/plugins/sfdp_services/geneve/gateway.api new file mode 100644 index 00000000000..e8ffbbd2097 --- /dev/null +++ b/src/plugins/sfdp_services/geneve/gateway.api @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +option version = "0.0.1"; + +import "vnet/ip/ip_types.api"; +import "vnet/ethernet/ethernet_types.api"; +import "vnet/interface_types.api"; + +enum sfdp_session_direction : u8 +{ + SFDP_API_FORWARD = 0, + SFDP_API_REVERSE = 1, +}; + +autoreply define sfdp_gateway_set_geneve_output +{ + u32 client_index; + u32 context; + + u32 tenant_id; + u32 output_tenant_id; + vl_api_address_t src; + vl_api_address_t dst; + u16 src_port; + u16 dst_port; + u8 static_mac; + vl_api_mac_address_t src_mac; + vl_api_mac_address_t dst_mac; + vl_api_sfdp_session_direction_t dir; +}; + +autoreply define sfdp_gateway_geneve_input_enable_disable +{ + u32 client_index; + u32 context; + + vl_api_interface_index_t sw_if_index; + u8 is_enable; +}; diff --git a/src/plugins/sfdp_services/geneve/gateway.c b/src/plugins/sfdp_services/geneve/gateway.c new file mode 100644 index 00000000000..719210c2fe0 --- /dev/null +++ b/src/plugins/sfdp_services/geneve/gateway.c @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#define _GNU_SOURCE +#include + +#include + +#include +#include + +#include +#include + +gw_main_t gateway_main; + +static void +gateway_init_main_if_needed (gw_main_t *gm) +{ + static u32 done = 0; + sfdp_main_t *sfdp = &sfdp_main; + + if (done) + return; + + vec_validate (gm->output, sfdp_num_sessions () << 1); + vec_validate (gm->tenants, 1ULL << sfdp->log2_tenants); + + done = 1; +} + +static clib_error_t * +gateway_init (vlib_main_t *vm) +{ + return 0; +} + +void +gw_enable_disable_geneve_input (gw_enable_disable_geneve_input_args_t *args) +{ + gw_main_t *gm = &gateway_main; + int rv = 0; + gateway_init_main_if_needed (gm); + rv = vnet_feature_enable_disable ("ip4-unicast", "sfdp-geneve-input", + args->sw_if_index, args->enable_disable, 0, + 0); + args->rv = rv; + if (rv) + args->err = clib_error_return ( + 0, "Failed vnet_feature_enable_disable with error %d : %U", rv, + format_vnet_api_errno, rv); + else + args->err = 0; +} + +void +gw_set_geneve_output (gw_set_geneve_output_args_t *args) +{ + gw_main_t *gm = &gateway_main; + sfdp_main_t *sfdp = &sfdp_main; + sfdp_tenant_t *vt; + gw_tenant_t *gt; + clib_bihash_kv_8_8_t kv = {}; + u8 dir = !!args->direction; + kv.key = args->tenant_id; + if (clib_bihash_search_inline_8_8 (&sfdp->tenant_idx_by_id, &kv)) + { + args->rv = -1; + args->err = + clib_error_return (0, "tenant-id %d not found", args->tenant_id); + return; + } + vt = sfdp_tenant_at_index (sfdp, kv.value); + gt = gw_tenant_at_index (gm, kv.value); + + /* Caching tenant id in gt */ + gt->output_tenant_id = + args->output_tenant_id == ~0 ? vt->tenant_id : args->output_tenant_id; + gt->flags = GW_TENANT_F_OUTPUT_DATA_SET; + gt->geneve_src_ip[dir] = args->src_addr; + gt->geneve_dst_ip[dir] = args->dst_addr; + gt->geneve_src_port[dir] = args->src_port; + gt->geneve_dst_port[dir] = args->dst_port; + if (args->static_mac) + { + gt->flags |= GW_TENANT_F_STATIC_MAC; + gt->src_mac[dir] = args->src_mac; + gt->dst_mac[dir] = args->dst_mac; + } + args->rv = 0; + args->err = 0; +} + +VLIB_INIT_FUNCTION (gateway_init); +VLIB_PLUGIN_REGISTER () = { + .version = SFDP_GW_PLUGIN_BUILD_VER, + .description = "sfdp Gateway Plugin", +}; \ No newline at end of file diff --git a/src/plugins/sfdp_services/geneve/gateway.h b/src/plugins/sfdp_services/geneve/gateway.h new file mode 100644 index 00000000000..49fd93e8348 --- /dev/null +++ b/src/plugins/sfdp_services/geneve/gateway.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#ifndef __included_gateway_h__ +#define __included_gateway_h__ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include + +#define foreach_gw_tenant_flag \ + _ (OUTPUT_DATA_SET, "output-data-set", 0) \ + _ (STATIC_MAC, "static-mac", 1) + +typedef enum +{ +#define _(a, b, c) GW_TENANT_F_##a = (1 << (c)), + foreach_gw_tenant_flag +#undef _ +} gw_tenant_flags_t; + +typedef struct +{ + /* Here goes the geneve rewrite */ + session_version_t session_version; + u16 encap_size; + u8 encap_data[124]; +} gw_geneve_output_data_t; +STATIC_ASSERT (sizeof (gw_geneve_output_data_t) == 128, ""); + +typedef struct +{ + u32 output_tenant_id; + u32 flags; + + /* Geneve output spec for forward/reverse packets */ + ip4_address_t geneve_src_ip[SFDP_FLOW_F_B_N]; + ip4_address_t geneve_dst_ip[SFDP_FLOW_F_B_N]; + u16 geneve_src_port[SFDP_FLOW_F_B_N]; + u16 geneve_dst_port[SFDP_FLOW_F_B_N]; + mac_address_t src_mac[SFDP_FLOW_F_B_N]; + mac_address_t dst_mac[SFDP_FLOW_F_B_N]; + +} gw_tenant_t; + +typedef struct +{ + /* pool of tenants */ + gw_tenant_t *tenants; + gw_geneve_output_data_t *output; /* by flow_index */ + u16 msg_id_base; +} gw_main_t; + +typedef struct +{ + int rv; + clib_error_t *err; + u32 sw_if_index; + u8 enable_disable; +} gw_enable_disable_geneve_input_args_t; + +typedef struct +{ + int rv; + clib_error_t *err; + u32 tenant_id; + ip4_address_t src_addr; + ip4_address_t dst_addr; + u16 src_port; /*network order*/ + u16 dst_port; /*network order*/ + u8 direction; /* 0 is forward, 1 is reverse */ + u8 static_mac; + u32 output_tenant_id; /* ~0 means output on the same tenant as input */ + mac_address_t src_mac; + mac_address_t dst_mac; +} gw_set_geneve_output_args_t; + +extern gw_main_t gateway_main; + +static_always_inline gw_tenant_t * +gw_tenant_at_index (gw_main_t *gm, u32 idx) +{ + return vec_elt_at_index (gm->tenants, idx); +} + +void +gw_enable_disable_geneve_input (gw_enable_disable_geneve_input_args_t *args); +void gw_set_geneve_output (gw_set_geneve_output_args_t *args); + +#define SFDP_GW_PLUGIN_BUILD_VER "1.0" + +#endif /* __included_gateway_h__ */ diff --git a/src/plugins/sfdp_services/geneve/geneve_input/node.c b/src/plugins/sfdp_services/geneve/geneve_input/node.c new file mode 100644 index 00000000000..6132a013d37 --- /dev/null +++ b/src/plugins/sfdp_services/geneve/geneve_input/node.c @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include +#include +typedef struct +{ + u32 next_index; + u32 sw_if_index; +} sfdp_geneve_input_trace_t; + +static u8 * +format_sfdp_geneve_input_trace (u8 *s, va_list *args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + sfdp_geneve_input_trace_t *t = va_arg (*args, sfdp_geneve_input_trace_t *); + + s = format (s, "snort-enq: sw_if_index %d, next index %d\n", t->sw_if_index, + t->next_index); + + return s; +} + +#define foreach_sfdp_geneve_input_next \ + _ (LOOKUP_IP4, "sfdp-lookup-ip4") \ + _ (LOOKUP_IP6, "sfdp-lookup-ip6") +#define foreach_sfdp_geneve_input_error _ (NOERROR, "No error") + +typedef enum +{ +#define _(sym, str) SFDP_GENEVE_INPUT_ERROR_##sym, + foreach_sfdp_geneve_input_error +#undef _ + SFDP_GENEVE_INPUT_N_ERROR, +} sfdp_geneve_input_error_t; + +static char *sfdp_geneve_input_error_strings[] = { +#define _(sym, string) string, + foreach_sfdp_geneve_input_error +#undef _ +}; + +typedef enum +{ +#define _(s, n) SFDP_GENEVE_INPUT_NEXT_##s, + foreach_sfdp_geneve_input_next +#undef _ + SFDP_GENEVE_INPUT_N_NEXT +} sfdp_geneve_input_next_t; + +static_always_inline uword +sfdp_geneve_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame) +{ + /* + * use VNI as tenant ID + * tenant_id -> tenant index + * drop unknown tenants + * store tenant_id into opaque1 + * advance current data to beginning of IP packet + */ + sfdp_main_t *sfdp = &sfdp_main; + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; + vlib_combined_counter_main_t *cm = + &sfdp->tenant_data_ctr[SFDP_TENANT_DATA_COUNTER_INCOMING]; + sfdp_tenant_t *tenant; + u32 *from = vlib_frame_vector_args (frame); + u32 n_left = frame->n_vectors; + u16 next_indices[VLIB_FRAME_SIZE], *current_next; + u32 thread_index = vlib_get_thread_index (); + + vlib_get_buffers (vm, from, bufs, n_left); + b = bufs; + current_next = next_indices; + + while (n_left) + { + ip4_header_t *ip4 = vlib_buffer_get_current (b[0]); + udp_header_t *udp; + u32 *gnv; + u32 tenant_id; + u16 tenant_idx; + clib_bihash_kv_8_8_t kv = {}; + u16 off = 0; + u32 len = vlib_buffer_length_in_chain (vm, b[0]); + u16 ethtype; + if (ip4->protocol != IP_PROTOCOL_UDP) + { + vnet_feature_next_u16 (current_next, b[0]); + goto end_of_packet; + } + off += ip4_header_bytes (ip4); + udp = (udp_header_t *) (b[0]->data + b[0]->current_data + off); + if (udp->dst_port != 0xC117) + { + vnet_feature_next_u16 (current_next, b[0]); + goto end_of_packet; + } + off += sizeof (udp[0]); + gnv = (u32 *) (b[0]->data + b[0]->current_data + off); + + /* Extract VNI */ + tenant_id = clib_net_to_host_u32 (gnv[1]) >> 8; + kv.key = (u64) tenant_id; + if (clib_bihash_search_inline_8_8 (&sfdp->tenant_idx_by_id, &kv)) + { + /* Not found */ + vnet_feature_next_u16 (current_next, b[0]); + goto end_of_packet; + } + + /* Store tenant_id as flow_id (to simplify the future lookup) */ + tenant_idx = kv.value; + tenant = sfdp_tenant_at_index (sfdp, tenant_idx); + b[0]->flow_id = tenant->context_id; + sfdp_buffer (b[0])->tenant_index = tenant_idx; + ethtype = *(u16 *) (b[0]->data + b[0]->current_data + off + 20); + ethtype = clib_net_to_host_u16 (ethtype); + current_next[0] = ethtype == ETHERNET_TYPE_IP6 ? + SFDP_GENEVE_INPUT_NEXT_LOOKUP_IP6 : + SFDP_GENEVE_INPUT_NEXT_LOOKUP_IP4; + off += + 8 /* geneve header no options */ + 14 /* ethernet header, no tag*/; + vlib_buffer_advance (b[0], off); + vlib_increment_combined_counter (cm, thread_index, tenant_idx, 1, + len - off); + end_of_packet: + b += 1; + current_next += 1; + n_left -= 1; + } + vlib_buffer_enqueue_to_next (vm, node, from, next_indices, frame->n_vectors); + return frame->n_vectors; +} + +VLIB_NODE_FN (sfdp_geneve_input_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return sfdp_geneve_input_inline (vm, node, frame); +} + +VLIB_REGISTER_NODE (sfdp_geneve_input_node) = { + .name = "sfdp-geneve-input", + .vector_size = sizeof (u32), + .format_trace = format_sfdp_geneve_input_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN (sfdp_geneve_input_error_strings), + .error_strings = sfdp_geneve_input_error_strings, + .n_next_nodes = SFDP_GENEVE_INPUT_N_NEXT, + .next_nodes = { + [SFDP_GENEVE_INPUT_NEXT_LOOKUP_IP4] = "sfdp-lookup-ip4", + [SFDP_GENEVE_INPUT_NEXT_LOOKUP_IP6] = "sfdp-lookup-ip6", + }, +}; + +VNET_FEATURE_INIT (sfdp_geneve_input_feat, static) = { + .arc_name = "ip4-unicast", + .node_name = "sfdp-geneve-input", +}; diff --git a/src/plugins/sfdp_services/geneve/geneve_output/node.c b/src/plugins/sfdp_services/geneve/geneve_output/node.c new file mode 100644 index 00000000000..6b3f52e5f50 --- /dev/null +++ b/src/plugins/sfdp_services/geneve/geneve_output/node.c @@ -0,0 +1,313 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include +#include +#define SFDP_GENEVE_OPTION_CLASS ((u16) 0xDEAD) +#define SFDP_GENEVE_OPTION_TYPE_SESSION_ID ((u8) 0xBE) +#define SFDP_GENEVE_OPTION_SESSION_ID_SIZE ((u8) 0x2) +#define SFDP_GENEVE_OPTION_SESSION_ID_FIRST_WORD \ + (SFDP_GENEVE_OPTION_CLASS << 16) | \ + (SFDP_GENEVE_OPTION_TYPE_SESSION_ID << 8) | \ + (SFDP_GENEVE_OPTION_SESSION_ID_SIZE << 0) +#define SFDP_GENEVE_OPTION_LEN (12) +#define SFDP_GENEVE_TOTAL_LEN (8 + SFDP_GENEVE_OPTION_LEN) + +#define foreach_sfdp_geneve_output_error _ (NO_OUTPUT, "no output data") + +typedef enum +{ +#define _(sym, str) SFDP_GENEVE_OUTPUT_ERROR_##sym, + foreach_sfdp_geneve_output_error +#undef _ + SFDP_GENEVE_OUTPUT_N_ERROR, +} sfdp_geneve_output_error_t; + +static char *sfdp_geneve_output_error_strings[] = { +#define _(sym, string) string, + foreach_sfdp_geneve_output_error +#undef _ +}; + +#define foreach_sfdp_geneve_output_next \ + _ (DROP, "error-drop") \ + _ (IP4_LOOKUP, "ip4-lookup") + +typedef enum +{ +#define _(n, x) SFDP_GENEVE_OUTPUT_NEXT_##n, + foreach_sfdp_geneve_output_next +#undef _ + SFDP_GENEVE_OUTPUT_N_NEXT +} sfdp_geneve_output_next_t; + +typedef struct +{ + u32 flow_id; + u16 encap_size; + u8 encap_data[124]; +} sfdp_geneve_output_trace_t; + +static u8 * +format_sfdp_geneve_output_trace (u8 *s, va_list *args) +{ + vlib_main_t __clib_unused *vm = va_arg (*args, vlib_main_t *); + vlib_node_t __clib_unused *node = va_arg (*args, vlib_node_t *); + sfdp_geneve_output_trace_t *t = va_arg (*args, sfdp_geneve_output_trace_t *); + u32 indent = format_get_indent (s); + s = + format (s, "sfdp-geneve_output: flow-id %u (session %u, %s)\n", t->flow_id, + t->flow_id >> 1, t->flow_id & 0x1 ? "reverse" : "forward"); + s = format (s, "%U", format_white_space, indent); + s = format (s, "encap-data: %U", format_hex_bytes, t->encap_data, + t->encap_size); + return s; +} + +static_always_inline int +sfdp_geneve_output_load_data (gw_main_t *gm, + gw_geneve_output_data_t *geneve_out, + sfdp_session_t *session, vlib_buffer_t *b) +{ + u32 tenant_idx = sfdp_buffer (b)->tenant_index; + gw_tenant_t *tenant = gw_tenant_at_index (gm, tenant_idx); + u8 direction = b->flow_id & 0x1; + ip4_header_t *ip4 = (void *) geneve_out->encap_data; + udp_header_t *udp; + ethernet_header_t *eth; + u32 *gnv; + if (PREDICT_FALSE (!(tenant->flags & GW_TENANT_F_OUTPUT_DATA_SET))) + return -1; + geneve_out->session_version = session->session_version; + geneve_out->encap_size = 0; + /* Start with IP header */ + ip4->src_address = tenant->geneve_src_ip[direction]; + ip4->dst_address = tenant->geneve_dst_ip[direction]; + ip4->protocol = IP_PROTOCOL_UDP; + ip4->ip_version_and_header_length = 0x45; + ip4->tos = IP_DSCP_CS0; + ip4->ttl = 0xff; + ip4->flags_and_fragment_offset = 0; + ip4->length = 0; + ip4->checksum = ip4_header_checksum (ip4); + ip4->length = /* stored in host byte order, incremented and swapped + later */ + sizeof (ip4_header_t) + sizeof (udp_header_t) + SFDP_GENEVE_TOTAL_LEN + + sizeof (ethernet_header_t); + geneve_out->encap_size += sizeof (*ip4); + udp = (void *) (geneve_out->encap_data + geneve_out->encap_size); + udp->src_port = tenant->geneve_src_port[direction]; + udp->dst_port = tenant->geneve_dst_port[direction]; + udp->checksum = 0; + udp->length = /* stored in host byte order, incremented and swapped + later */ + sizeof (udp_header_t) + SFDP_GENEVE_TOTAL_LEN + sizeof (ethernet_header_t); + geneve_out->encap_size += sizeof (*udp); + gnv = (void *) (geneve_out->encap_data + geneve_out->encap_size); + gnv[0] = + // Not sure if 0x0C or 0x03 (number of bytes or of 4B-words???) + clib_host_to_net_u32 (0x03006558); /*3 words of option geneve version 0*/ + gnv[1] = clib_host_to_net_u32 (tenant->output_tenant_id << 8); + gnv[2] = clib_host_to_net_u32 (SFDP_GENEVE_OPTION_SESSION_ID_FIRST_WORD); + gnv[3] = + clib_host_to_net_u32 (session->session_id >> 32); /* session id high */ + gnv[4] = clib_host_to_net_u32 (session->session_id | + direction); /* session id low */ + geneve_out->encap_size += SFDP_GENEVE_TOTAL_LEN; + eth = (void *) (geneve_out->encap_data + geneve_out->encap_size); + if (tenant->flags & GW_TENANT_F_STATIC_MAC) + { + clib_memcpy_fast (eth->src_address, tenant->src_mac[direction].bytes, + sizeof (mac_address_t)); + clib_memcpy_fast (eth->dst_address, tenant->dst_mac[direction].bytes, + sizeof (mac_address_t)); + eth->type = clib_host_to_net_u16 (ETHERNET_TYPE_IP4); + } + else + clib_memcpy_fast (eth, b->data + b->current_data - sizeof (*eth), + sizeof (*eth)); + + geneve_out->encap_size += sizeof (*eth); + ASSERT (geneve_out->encap_size < sizeof (geneve_out->encap_data)); + return 0; +} + +static_always_inline void +geneve_output_rewrite_one (vlib_main_t *vm, vlib_node_runtime_t *node, + gw_main_t *gm, vlib_combined_counter_main_t *cm, + gw_geneve_output_data_t *geneve_out, + sfdp_session_t *session, u32 thread_index, + u32 session_idx, u16 *to_next, vlib_buffer_t **b) +{ + if (PREDICT_FALSE ( + geneve_out->session_version != session->session_version && + sfdp_geneve_output_load_data (gm, geneve_out, session, b[0]))) + { + to_next[0] = SFDP_GENEVE_OUTPUT_NEXT_DROP; + vlib_node_increment_counter (vm, node->node_index, + SFDP_GENEVE_OUTPUT_ERROR_NO_OUTPUT, 1); + } + else + { + ip4_header_t *ip; + udp_header_t *udp; + ip_csum_t csum; + u8 *data; + u16 orig_len = vlib_buffer_length_in_chain (vm, b[0]); + b[0]->flags |= + (VNET_BUFFER_F_IS_IP4 | VNET_BUFFER_F_L3_HDR_OFFSET_VALID | + VNET_BUFFER_F_L4_HDR_OFFSET_VALID); + vnet_buffer (b[0])->oflags |= + VNET_BUFFER_OFFLOAD_F_UDP_CKSUM | VNET_BUFFER_OFFLOAD_F_IP_CKSUM; + vlib_buffer_advance (b[0], -geneve_out->encap_size); + data = vlib_buffer_get_current (b[0]); + vnet_buffer (b[0])->l3_hdr_offset = b[0]->current_data; + vnet_buffer (b[0])->l4_hdr_offset = + b[0]->current_data + sizeof (ip4_header_t); + clib_memcpy_fast (data, geneve_out->encap_data, geneve_out->encap_size); + /* fixup */ + ip = (void *) data; + ip->length = clib_net_to_host_u16 (ip->length + orig_len); + csum = ip->checksum; + csum = ip_csum_update (csum, 0, ip->length, ip4_header_t, length); + ip->checksum = ip_csum_fold (csum); + udp = (void *) (data + sizeof (ip4_header_t)); + udp->length = clib_net_to_host_u16 (udp->length + orig_len); + to_next[0] = SFDP_GENEVE_OUTPUT_NEXT_IP4_LOOKUP; + vlib_increment_combined_counter (cm, thread_index, session->tenant_idx, + 1, orig_len); + } +} + +#define vlib_prefetch_buffer_data_with_offset(b, type, offset) \ + CLIB_PREFETCH (b->data + b->current_data + (offset), CLIB_CACHE_LINE_BYTES, \ + type) +VLIB_NODE_FN (sfdp_geneve_output_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + gw_main_t *gm = &gateway_main; + sfdp_main_t *sfdp = &sfdp_main; + vlib_combined_counter_main_t *cm = + &sfdp->tenant_data_ctr[SFDP_TENANT_DATA_COUNTER_OUTGOING]; + u32 thread_index = vm->thread_index; + + u16 next_indices[VLIB_FRAME_SIZE], *to_next = next_indices; + u32 *from = vlib_frame_vector_args (frame); + u32 n_left = frame->n_vectors; + + vlib_get_buffers (vm, from, bufs, n_left); + + /* Pipeline load buffer data -> load session_data + geneve_output_data + * ->process */ +#define SFDP_PREFETCH_SIZE 2 + while (n_left >= SFDP_PREFETCH_SIZE) + { + u32 si[SFDP_PREFETCH_SIZE * 2]; + sfdp_session_t *session[SFDP_PREFETCH_SIZE]; + gw_geneve_output_data_t *geneve_out[SFDP_PREFETCH_SIZE]; + if (n_left >= SFDP_PREFETCH_SIZE * 3) + { + for (int i = 0; i < SFDP_PREFETCH_SIZE; i++) + { + vlib_prefetch_buffer_header (b[2 * SFDP_PREFETCH_SIZE + i], + STORE); + vlib_prefetch_buffer_data_with_offset ( + b[2 * SFDP_PREFETCH_SIZE + i], STORE, -64); + } + } + if (n_left >= SFDP_PREFETCH_SIZE * 2) + { + for (int i = 0; i < SFDP_PREFETCH_SIZE; i++) + { + si[SFDP_PREFETCH_SIZE + i] = sfdp_session_from_flow_index ( + b[SFDP_PREFETCH_SIZE + i]->flow_id); + CLIB_PREFETCH (sfdp->sessions + si[SFDP_PREFETCH_SIZE + i], + CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (gm->output + b[SFDP_PREFETCH_SIZE + i]->flow_id, + 2 * CLIB_CACHE_LINE_BYTES, LOAD); + } + } + for (int i = 0; i < SFDP_PREFETCH_SIZE; i++) + { + si[i] = sfdp_session_from_flow_index (b[i]->flow_id); + session[i] = sfdp_session_at_index (si[i]); + geneve_out[i] = vec_elt_at_index (gm->output, b[i]->flow_id); + geneve_output_rewrite_one (vm, node, gm, cm, geneve_out[i], + session[i], thread_index, si[i], + to_next + i, b + i); + } + to_next += SFDP_PREFETCH_SIZE; + b += SFDP_PREFETCH_SIZE; + n_left -= SFDP_PREFETCH_SIZE; + } + + while (n_left) + { + u32 session_idx = sfdp_session_from_flow_index (b[0]->flow_id); + sfdp_session_t *session = sfdp_session_at_index (session_idx); + gw_geneve_output_data_t *geneve_out = + vec_elt_at_index (gm->output, b[0]->flow_id); + + geneve_output_rewrite_one (vm, node, gm, cm, geneve_out, session, + thread_index, session_idx, to_next, b); + to_next++; + b++; + n_left--; + } + + vlib_buffer_enqueue_to_next (vm, node, from, next_indices, frame->n_vectors); + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) + { + int i; + n_left = frame->n_vectors; + b = bufs; + for (i = 0; i < n_left; i++) + { + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) + { + sfdp_geneve_output_trace_t *t = + vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->flow_id = b[0]->flow_id; + t->encap_size = gm->output[b[0]->flow_id].encap_size; + clib_memcpy_fast (t->encap_data, + gm->output[b[0]->flow_id].encap_data, + gm->output[b[0]->flow_id].encap_size); + b++; + } + else + break; + } + } + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (sfdp_geneve_output_node) = { + .name = "sfdp-geneve-output", + .vector_size = sizeof (u32), + .format_trace = format_sfdp_geneve_output_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN (sfdp_geneve_output_error_strings), + .error_strings = sfdp_geneve_output_error_strings, + + .n_next_nodes = SFDP_GENEVE_OUTPUT_N_NEXT, + .next_nodes = { +#define _(n, x) [SFDP_GENEVE_OUTPUT_NEXT_##n] = x, + foreach_sfdp_geneve_output_next +#undef _ + } + +}; + +SFDP_SERVICE_DEFINE (geneve_output) = { + .node_name = "sfdp-geneve-output", + .runs_before = SFDP_SERVICES (0), + .runs_after = SFDP_SERVICES ("sfdp-drop", "sfdp-l4-lifecycle", + "sfdp-tcp-check"), + .is_terminal = 1 +}; \ No newline at end of file diff --git a/src/vnet/sfdp/lookup/full_reass_node.c b/src/plugins/sfdp_services/reass/full_reass_node.c similarity index 86% rename from src/vnet/sfdp/lookup/full_reass_node.c rename to src/plugins/sfdp_services/reass/full_reass_node.c index bbd3e208fdd..50ea59735e2 100644 --- a/src/vnet/sfdp/lookup/full_reass_node.c +++ b/src/plugins/sfdp_services/reass/full_reass_node.c @@ -1,23 +1,12 @@ -/* - * Copyright (c) 2024 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. */ #include #include #include #include -#include +#include typedef struct { diff --git a/src/vnet/sfdp/lookup/reass.c b/src/plugins/sfdp_services/reass/reass.c similarity index 71% rename from src/vnet/sfdp/lookup/reass.c rename to src/plugins/sfdp_services/reass/reass.c index a1966a2fab1..4270a68be15 100644 --- a/src/vnet/sfdp/lookup/reass.c +++ b/src/plugins/sfdp_services/reass/reass.c @@ -1,24 +1,13 @@ -/* - * Copyright (c) 2024 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. */ #include #include #include #include -#include #include +#include sfdp_reass_main_t sfdp_reass_main; @@ -32,18 +21,19 @@ sfdp_reass_main_init (vlib_main_t *vm) vrm->ip6_sv_reass_next_index = ip6_sv_reass_custom_context_register_next_node ( sfdp_lookup_ip6_node.index); - vrm->ip4_full_reass_next_index = + /*vrm->ip4_full_reass_next_index = ip4_full_reass_custom_context_register_next_node ( sfdp_lookup_ip4_node.index); vrm->ip6_full_reass_next_index = ip6_full_reass_custom_context_register_next_node ( sfdp_lookup_ip6_node.index); vrm->ip4_full_reass_err_next_index = ip4_full_reass_get_error_next_index (); - vrm->ip6_full_reass_err_next_index = ip6_full_reass_get_error_next_index (); + vrm->ip6_full_reass_err_next_index = ip6_full_reass_get_error_next_index + ();*/ return 0; } -void +/*void sfdp_ip4_full_reass_custom_context_register_next_node (u16 node_index) { sfdp_reass_main.ip4_full_reass_next_index = @@ -70,5 +60,5 @@ sfdp_ip6_full_reass_custom_context_register_next_err_node (u16 node_index) sfdp_reass_main.ip6_full_reass_err_next_index = ip6_full_reass_custom_context_register_next_node (node_index); } - +*/ VLIB_INIT_FUNCTION (sfdp_reass_main_init); diff --git a/src/vnet/sfdp/lookup/reass.h b/src/plugins/sfdp_services/reass/reass.h similarity index 100% rename from src/vnet/sfdp/lookup/reass.h rename to src/plugins/sfdp_services/reass/reass.h diff --git a/src/vnet/sfdp/lookup/sv_reass_node.c b/src/plugins/sfdp_services/reass/sv_reass_node.c similarity index 85% rename from src/vnet/sfdp/lookup/sv_reass_node.c rename to src/plugins/sfdp_services/reass/sv_reass_node.c index e8d6ce81d92..fead7b3a9d9 100644 --- a/src/vnet/sfdp/lookup/sv_reass_node.c +++ b/src/plugins/sfdp_services/reass/sv_reass_node.c @@ -1,23 +1,12 @@ -/* - * Copyright (c) 2022 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Cisco Systems, Inc. */ #include #include #include #include -#include +#include typedef struct {