From f391d158fbf5aa9e98206a4d3d67f4fa2c11847d Mon Sep 17 00:00:00 2001 From: Florin Coras Date: Wed, 20 Aug 2025 13:33:24 -0400 Subject: [PATCH] hsi: support to intercept all proto traffic Type: improvement Change-Id: Ieebc0a8383f8c86756e4bd36501c2faa9df87a98 Signed-off-by: Florin Coras --- src/plugins/hsi/hsi.c | 157 ++++++++++++++++++++++++++++++++++++++++++----- src/plugins/hsi/hsi.h | 2 + src/vnet/tcp/tcp_input.c | 2 +- 3 files changed, 143 insertions(+), 18 deletions(-) diff --git a/src/plugins/hsi/hsi.c b/src/plugins/hsi/hsi.c index 0fea0a3f288..e55ef52b91b 100644 --- a/src/plugins/hsi/hsi.c +++ b/src/plugins/hsi/hsi.c @@ -18,6 +18,30 @@ #include #include +#include + +typedef struct hsi_main_ +{ + u8 intercept_type; + + /* ipv4 and ipv6 for tcp and udp */ + session_handle_t intercept_listeners[2][2]; +} hsi_main_t; + +static hsi_main_t hsi_main; + +static inline u8 +hsi_intercept_proto_flag (transport_proto_t proto, u8 is_ip4) +{ + /* This leverages the fact that TCP is 0 and UDP is 1 */ + return (1 << (proto << 1 | is_ip4)); +} + +static inline u8 +hsi_have_intercept_proto (transport_proto_t proto, u8 is_ip4) +{ + return (hsi_main.intercept_type & hsi_intercept_proto_flag (proto, is_ip4)); +} char *hsi_error_strings[] = { #define hsi_error(n, s) s, @@ -28,20 +52,26 @@ char *hsi_error_strings[] = { typedef enum hsi_input_next_ { HSI_INPUT_NEXT_UDP_INPUT, + HSI_INPUT_NEXT_UDP_INPUT_NOLOOKUP, HSI_INPUT_NEXT_TCP_INPUT, HSI_INPUT_NEXT_TCP_INPUT_NOLOOKUP, + HSI_INPUT_NEXT_TCP_LISTEN, HSI_INPUT_N_NEXT } hsi_input_next_t; #define foreach_hsi4_input_next \ _ (UDP_INPUT, "udp4-input") \ + _ (UDP_INPUT_NOLOOKUP, "udp4-input-nolookup") \ _ (TCP_INPUT, "tcp4-input") \ - _ (TCP_INPUT_NOLOOKUP, "tcp4-input-nolookup") + _ (TCP_INPUT_NOLOOKUP, "tcp4-input-nolookup") \ + _ (TCP_LISTEN, "tcp4-listen") #define foreach_hsi6_input_next \ _ (UDP_INPUT, "udp6-input") \ + _ (UDP_INPUT_NOLOOKUP, "udp6-input-nolookup") \ _ (TCP_INPUT, "tcp6-input") \ - _ (TCP_INPUT_NOLOOKUP, "tcp6-input-nolookup") + _ (TCP_INPUT_NOLOOKUP, "tcp6-input-nolookup") \ + _ (TCP_LISTEN, "tcp6-listen") typedef struct { @@ -62,7 +92,7 @@ format_hsi_trace (u8 *s, va_list *args) return s; } -always_inline u8 +always_inline session_t * hsi_udp_lookup (vlib_buffer_t *b, void *ip_hdr, u8 is_ip4) { udp_header_t *hdr; @@ -85,7 +115,7 @@ hsi_udp_lookup (vlib_buffer_t *b, void *ip_hdr, u8 is_ip4) hdr->dst_port, hdr->src_port, TRANSPORT_PROTO_UDP); } - return s ? 1 : 0; + return s; } always_inline transport_connection_t * @@ -120,10 +150,11 @@ hsi_tcp_lookup (vlib_buffer_t *b, void *ip_hdr, tcp_header_t **rhdr, u8 is_ip4) always_inline void hsi_lookup_and_update (vlib_buffer_t *b, u32 *next, u8 is_ip4, u8 is_input) { - u8 proto, state, have_udp; + u8 proto, state; tcp_header_t *tcp_hdr = 0; tcp_connection_t *tc; u32 rw_len = 0; + session_t *s; void *ip_hdr; if (is_input) @@ -176,23 +207,49 @@ hsi_lookup_and_update (vlib_buffer_t *b, u32 *next, u8 is_ip4, u8 is_input) } else { - vnet_feature_next (next, b); + u32 error = 0; + + if (!hsi_have_intercept_proto (TRANSPORT_PROTO_TCP, is_ip4) || + !tcp_syn (tcp_hdr)) + { + vnet_feature_next (next, b); + break; + } + + /* force parsing of buffer in preparation for tcp-listen */ + tcp_input_lookup_buffer (b, vlib_get_thread_index (), &error, is_ip4, + 1 /* is_nolookup*/); + if (error) + { + vnet_feature_next (next, b); + break; + } + + vnet_buffer (b)->tcp.connection_index = + hsi_main.intercept_listeners[!is_ip4][TRANSPORT_PROTO_TCP]; + vnet_buffer (b)->tcp.flags = TCP_STATE_LISTEN; + + *next = HSI_INPUT_NEXT_TCP_LISTEN; } break; case IP_PROTOCOL_UDP: - have_udp = hsi_udp_lookup (b, ip_hdr, is_ip4); - if (have_udp) + s = hsi_udp_lookup (b, ip_hdr, is_ip4); + if (!s) { - *next = HSI_INPUT_NEXT_UDP_INPUT; - /* Emulate udp-local and consume headers up to udp payload */ - rw_len += is_ip4 ? sizeof (ip4_header_t) : sizeof (ip6_header_t); - rw_len += sizeof (udp_header_t); - vlib_buffer_advance (b, rw_len); - } - else - { - vnet_feature_next (next, b); + if (!hsi_have_intercept_proto (TRANSPORT_PROTO_UDP, is_ip4)) + { + vnet_feature_next (next, b); + break; + } + s = session_get_from_handle ( + hsi_main.intercept_listeners[!is_ip4][TRANSPORT_PROTO_UDP]); } + *next = HSI_INPUT_NEXT_UDP_INPUT_NOLOOKUP; + /* Emulate udp-local and consume headers up to udp payload */ + rw_len += is_ip4 ? sizeof (ip4_header_t) : sizeof (ip6_header_t); + rw_len += sizeof (udp_header_t); + vlib_buffer_advance (b, rw_len); + vnet_buffer (b)->udp.session_handle = s->handle; break; default: vnet_feature_next (next, b); @@ -389,6 +446,72 @@ VNET_FEATURE_INIT (hsi6_out_feature, static) = { .runs_before = VNET_FEATURES ("interface-output"), }; +void +hsi_intercept_proto (transport_proto_t proto, u8 is_ip4) +{ + hsi_main_t *hm = &hsi_main; + session_endpoint_t sep = { .transport_proto = proto, .is_ip4 = is_ip4 }; + session_t *ls; + + ls = session_lookup_listener_wildcard (0, &sep); + if (ls) + { + if (proto == TRANSPORT_PROTO_TCP) + hm->intercept_listeners[!is_ip4][proto] = ls->connection_index; + else + hm->intercept_listeners[!is_ip4][proto] = ls->handle; + hm->intercept_type |= hsi_intercept_proto_flag (proto, is_ip4); + } +} + +static clib_error_t * +hsi_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 *error = 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, "intercept tcp")) + { + hsi_intercept_proto (TRANSPORT_PROTO_TCP, 1); + hsi_intercept_proto (TRANSPORT_PROTO_TCP, 0); + } + else if (unformat (line_input, "intercept udp")) + { + hsi_intercept_proto (TRANSPORT_PROTO_UDP, 1); + hsi_intercept_proto (TRANSPORT_PROTO_UDP, 0); + } + else if (unformat (line_input, "intercept all")) + { + hsi_intercept_proto (TRANSPORT_PROTO_TCP, 1); + hsi_intercept_proto (TRANSPORT_PROTO_TCP, 0); + hsi_intercept_proto (TRANSPORT_PROTO_UDP, 1); + hsi_intercept_proto (TRANSPORT_PROTO_UDP, 0); + } + else + { + error = clib_error_return (0, "unknown input `%U'", + format_unformat_error, line_input); + goto done; + } + } + +done: + unformat_free (line_input); + return error; +} + +VLIB_CLI_COMMAND (hsi_command, static) = { + .path = "hsi", + .short_help = "hsi [intercept [tcp | udp | all]]", + .function = hsi_command_fn, +}; + VLIB_PLUGIN_REGISTER () = { .version = VPP_BUILD_VER, .description = "Host Stack Intercept (HSI)", diff --git a/src/plugins/hsi/hsi.h b/src/plugins/hsi/hsi.h index 1eee1565ef1..89172635d30 100644 --- a/src/plugins/hsi/hsi.h +++ b/src/plugins/hsi/hsi.h @@ -26,4 +26,6 @@ typedef enum _hsi_error HSI_N_ERROR, } hsi_error_t; +__clib_export void hsi_intercept_proto (transport_proto_t proto, u8 is_ip4); + #endif /* SRC_PLUGINS_HSI_HSI_H_ */ diff --git a/src/vnet/tcp/tcp_input.c b/src/vnet/tcp/tcp_input.c index b8b4c3432a5..cbb589d472a 100644 --- a/src/vnet/tcp/tcp_input.c +++ b/src/vnet/tcp/tcp_input.c @@ -2627,7 +2627,7 @@ tcp46_listen_inline (vlib_main_t *vm, vlib_node_runtime_t *node, /* Make sure connection wasn't just created */ child = tcp_lookup_connection (lc->c_fib_index, b[0], thread_index, is_ip4); - if (PREDICT_FALSE (child->state != TCP_STATE_LISTEN)) + if (PREDICT_FALSE (child && child->state != TCP_STATE_LISTEN)) { tcp_inc_counter (listen, TCP_ERROR_CREATE_EXISTS, 1); goto done; -- 2.16.6