F: src/vnet/sfdp/
+Plugin StateFul Data Plane Services
+I: sfdp_services
+F: src/plugins/sfdp_services
+
Plugin - Crypto - native
I: crypto-native
--- /dev/null
+# 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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <acl/acl_sample.h>
+
+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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#ifndef __included_acl_sample_h__
+#define __included_acl_sample_h__
+
+#include <vlib/vlib.h>
+#include <vnet/sfdp/sfdp.h>
+#include <acl/exports.h>
+
+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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <acl/acl_sample.h>
+
+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 <tenant-id> acl_index <acl_index> [disable]",
+ .function = sfdp_acl_sample_set_fn,
+};
\ No newline at end of file
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <acl/acl_sample.h>
+#include <vnet/sfdp/service.h>
+#include <vnet/sfdp/sfdp_funcs.h>
+
+#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] : "<none>");
+ 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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vnet/sfdp/sfdp.h>
+
+#include <sfdp_services/base/interface_input/interface_input.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vnet/ip/ip_types_api.h>
+
+#include <vnet/format_fns.h>
+#include <sfdp_services/base/interface_input/interface_input.api_enum.h>
+#include <sfdp_services/base/interface_input/interface_input.api_types.h>
+
+#define REPLY_MSG_ID_BASE vim->msg_id_base
+#include <vlibapi/api_helper_macros.h>
+
+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 <sfdp_services/base/interface_input/interface_input.api.c>
+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:
+ */
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <sfdp_services/base/interface_input/interface_input.h>
+
+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 <interface> tenant <tenant-id> [disable]",
+ .function = sfdp_interface_input_set_unset_fn,
+};
\ No newline at end of file
--- /dev/null
+/* 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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vnet/sfdp/sfdp.h>
+#include <sfdp_services/base/interface_input/interface_input.h>
+#include <vppinfra/pool.h>
+
+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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#ifndef __included_nat_h__
+#define __included_nat_h__
+
+#include <vlib/vlib.h>
+#include <vnet/sfdp/sfdp.h>
+
+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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/feature/feature.h>
+#include <sfdp_services/base/interface_input/interface_input.h>
+#include <vnet/sfdp/sfdp.h>
+#include <vnet/sfdp/common.h>
+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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/sfdp/sfdp.h>
+#include <vnet/sfdp/service.h>
+#include <vnet/sfdp/timer/timer.h>
+#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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vnet/sfdp/sfdp.h>
+
+#include <sfdp_services/base/nat/nat.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vnet/ip/ip_types_api.h>
+
+#include <vnet/format_fns.h>
+#include <sfdp_services/base/nat/nat.api_enum.h>
+#include <sfdp_services/base/nat/nat.api_types.h>
+
+#define REPLY_MSG_ID_BASE nat->msg_id_base
+#include <vlibapi/api_helper_macros.h>
+
+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 <sfdp_services/base/nat/nat.api.c>
+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:
+ */
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <sfdp_services/base/nat/nat.h>
+
+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 <interface> tenant <tenant-id> [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] <alloc-pool-id> <ip-addr>+",
+ .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 <tenant-id> outside-tenant <tenant-id> table "
+ "<table-id> alloc-pool <alloc-pool-id> [disable]",
+ .function = sfdp_nat_snat_set_unset_fn,
+};
\ No newline at end of file
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/feature/feature.h>
+#include <sfdp_services/base/nat/nat.h>
+#include <vnet/sfdp/sfdp.h>
+#include <vnet/sfdp/common.h>
+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",
+};
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <sfdp_services/base/nat/nat.h>
+#include <vnet/sfdp/service.h>
+#include <vnet/sfdp/sfdp_funcs.h>
+
+#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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/sfdp/sfdp.h>
+#include <sfdp_services/base/nat/nat.h>
+
+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
--- /dev/null
+/* 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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vnet/sfdp/sfdp.h>
+#include <sfdp_services/base/nat/nat.h>
+#include <vppinfra/pool.h>
+
+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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#ifndef __included_nat_h__
+#define __included_nat_h__
+
+#include <vlib/vlib.h>
+#include <vnet/sfdp/sfdp.h>
+#include <vnet/ip/ip46_address.h>
+
+#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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <sfdp_services/base/nat/nat.h>
+#include <vnet/sfdp/service.h>
+#include <vnet/sfdp/sfdp_funcs.h>
+#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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/sfdp/sfdp.h>
+#include <vnet/sfdp/service.h>
+#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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vnet/sfdp/sfdp.h>
+#include <sfdp_services/base/tcp-check/tcp_check.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vnet/ip/ip_types_api.h>
+#include <vnet/format_fns.h>
+#include <sfdp_services/base/tcp-check/tcp_check.api_enum.h>
+#include <sfdp_services/base/tcp-check/tcp_check.api_types.h>
+#include <vlibapi/api_helper_macros.h>
+
+#include <vnet/sfdp/sfdp_types_funcs.h>
+
+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 <sfdp_services/base/tcp-check/tcp_check.api.c>
+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:
+ */
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/sfdp/sfdp.h>
+#include <sfdp_services/base/tcp-check/tcp_check.h>
+
+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 <tenant-id>]",
+ .function = sfdp_tcp_check_show_sessions_command_fn,
+};
\ No newline at end of file
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/sfdp/sfdp.h>
+#include <vnet/sfdp/lookup/parser.h>
+#include <sfdp_services/base/tcp-check/tcp_check.h>
+
+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;
+}
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <sfdp_services/base/tcp-check/tcp_check.h>
+#include <vnet/sfdp/service.h>
+#include <vnet/sfdp/timer/timer.h>
+
+#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
+};
--- /dev/null
+/* 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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <sfdp_services/base/tcp-check/tcp_check.h>
+
+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);
--- /dev/null
+/* 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 <vlib/vlib.h>
+#include <vnet/sfdp/sfdp.h>
+/* 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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/feature/feature.h>
+#include <vnet/sfdp/sfdp.h>
+#include <vnet/sfdp/common.h>
+#include <vnet/sfdp/service.h>
+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
+};
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vnet/sfdp/sfdp.h>
+
+#include <sfdp_services/geneve/gateway.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vnet/ip/ip_types_api.h>
+#include <vnet/ethernet/ethernet_types_api.h>
+
+#include <vnet/format_fns.h>
+#include <sfdp_services/geneve/gateway.api_enum.h>
+#include <sfdp_services/geneve/gateway.api_types.h>
+
+#define REPLY_MSG_ID_BASE gw->msg_id_base
+#include <vlibapi/api_helper_macros.h>
+
+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 <sfdp_services/geneve/gateway.api.c>
+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);
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <sfdp_services/geneve/gateway.h>
+#include <vnet/plugin/plugin.h>
+#include <vnet/vnet.h>
+
+/*
+ * add CLI:
+ * set gateway geneve-output tenant <tenant-id> src <src ip> dst <dst ip>
+ * src-port <src-port> dst-port <dst-port> <forward|reverse>
+ *
+ * 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 <tenant-id> "
+ "src <src ip> dst <dst ip> "
+ "src-port <src-port> dst-port <dst-port> "
+ "[output-tenant <tenant-id>] "
+ "[src-mac <src-mac-address> dst-mac <dst-mac-address>]"
+ "<forward|reverse>",
+ .function = gateway_set_output_command_fn,
+};
+
+/*
+ * Add CLI:
+ * gateway geneve-input interface <ifname> <enable-disable>
+ *
+ */
+
+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 <ifname> <enable|disable>",
+ .function = gateway_geneve_enable_disable_command_fn,
+};
\ No newline at end of file
--- /dev/null
+/* 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;
+};
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#define _GNU_SOURCE
+#include <sys/mman.h>
+
+#include <sfdp_services/geneve/gateway.h>
+
+#include <vnet/plugin/plugin.h>
+#include <vnet/vnet.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+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
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#ifndef __included_gateway_h__
+#define __included_gateway_h__
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ethernet/ethernet.h>
+
+#include <vppinfra/hash.h>
+#include <vppinfra/error.h>
+#include <vppinfra/elog.h>
+
+#include <vppinfra/bihash_24_8.h>
+#include <vppinfra/bihash_8_8.h>
+
+#include <vppinfra/bihash_template.h>
+
+#include <vnet/sfdp/sfdp.h>
+
+#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__ */
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/feature/feature.h>
+#include <sfdp_services/geneve/gateway.h>
+#include <vnet/sfdp/common.h>
+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",
+};
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <sfdp_services/geneve/gateway.h>
+#include <vnet/sfdp/common.h>
+#include <vnet/sfdp/service.h>
+#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
-/*
- * 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 <vlib/vlib.h>
#include <vnet/vnet.h>
#include <vnet/sfdp/common.h>
#include <vnet/sfdp/sfdp.h>
-#include <vnet/sfdp/lookup/reass.h>
+#include <sfdp_services/lookup/reass.h>
typedef struct
{
-/*
- * 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 <vnet/ip/reass/ip4_full_reass.h>
#include <vnet/ip/reass/ip6_full_reass.h>
#include <vnet/ip/reass/ip4_sv_reass.h>
#include <vnet/ip/reass/ip6_sv_reass.h>
-#include <vnet/sfdp/lookup/reass.h>
#include <vnet/sfdp/sfdp.h>
+#include <sfdp_services/reass/reass.h>
sfdp_reass_main_t sfdp_reass_main;
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 =
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);
-/*
- * 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 <vlib/vlib.h>
#include <vnet/vnet.h>
#include <vnet/sfdp/common.h>
#include <vnet/sfdp/sfdp.h>
-#include <vnet/sfdp/lookup/reass.h>
+#include <sfdp_services/reass/reass.h>
typedef struct
{