summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAgathiyan Bragadeesh <agathiyan.bragadeesh2@arm.com>2024-11-06 14:33:12 +0000
committerDamjan Marion <dmarion@0xa5.net>2025-01-17 17:27:06 +0000
commit102575492c9199259aa5e468f21b46936d7a1ac4 (patch)
treef7fbad6cfa602d8a56fd3e68a9d4a4611e062b5d
parentb33331925583a83c36aed67521b78e1f3db12a8c (diff)
snort: support multiple instances per interface
Implements load balancing between snort instances via flow hash. New CLI commands have been made to support these changes: snort attach instance <name1> [instance <name2> ... ] interface <ifname> [input|output|inout] snort attach all-instances interface <ifname> [input|output|inout] snort detach instance <name1> interface <ifname> snort detach all-instances interface <ifname> The output of "show snort interfaces" has an extra column to show the direction of each attachment: interface instances direction Ethernet0: snort1 inout snort2 inout snort3 inout Ethernet1: snort1 input snort3 output To maintain backwards compatibility for the snort api, the snort_interface_get api endpoint only returns one of the attached instances and the snort_interface_detach endpoint detaches all attached instances. Type: improvement Signed-off-by: Agathiyan Bragadeesh <agathiyan.bragadeesh2@arm.com> Change-Id: I6b7c26c203496d6a1dba244620907f28c04bb478
-rw-r--r--src/plugins/snort/cli.c258
-rw-r--r--src/plugins/snort/enqueue.c65
-rw-r--r--src/plugins/snort/main.c295
-rw-r--r--src/plugins/snort/snort.h23
-rw-r--r--src/plugins/snort/snort_api.c43
-rw-r--r--test/test_snort.py38
6 files changed, 554 insertions, 168 deletions
diff --git a/src/plugins/snort/cli.c b/src/plugins/snort/cli.c
index 4b6dbc742a7..d4b69adae7d 100644
--- a/src/plugins/snort/cli.c
+++ b/src/plugins/snort/cli.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright(c) 2021 Cisco Systems, Inc.
+ * Copyright(c) 2024 Arm Limited
*/
#include <vlib/vlib.h>
@@ -17,6 +18,70 @@ format_snort_instance (u8 *s, va_list *args)
}
static clib_error_t *
+snort_attach_detach_instance (vlib_main_t *vm, vnet_main_t *vnm,
+ char *instance_name, u32 sw_if_index,
+ int is_enable, snort_attach_dir_t dir)
+{
+ clib_error_t *err = NULL;
+ int rv = snort_interface_enable_disable (vm, instance_name, sw_if_index,
+ is_enable, dir);
+ switch (rv)
+ {
+ case 0:
+ break;
+ case VNET_API_ERROR_FEATURE_ALREADY_ENABLED:
+ /* already attached to same instance */
+ break;
+ case VNET_API_ERROR_INVALID_INTERFACE:
+ err = clib_error_return (0,
+ "interface %U is not assigned to snort "
+ "instance %s!",
+ format_vnet_sw_if_index_name, vnm, sw_if_index,
+ instance_name);
+ break;
+ case VNET_API_ERROR_NO_SUCH_ENTRY:
+ err = clib_error_return (0, "unknown instance '%s'", instance_name);
+ break;
+ case VNET_API_ERROR_INSTANCE_IN_USE:
+ err = clib_error_return (
+ 0, "interface %U is currently up, set state down first",
+ format_vnet_sw_if_index_name, vnm, sw_if_index);
+ break;
+ default:
+ err = clib_error_return (0, "snort_interface_enable_disable returned %d",
+ rv);
+ break;
+ }
+ return err;
+}
+
+static clib_error_t *
+snort_detach_all_instance (vlib_main_t *vm, vnet_main_t *vnm, u32 sw_if_index)
+{
+ clib_error_t *err = NULL;
+ int rv = snort_interface_disable_all (vm, sw_if_index);
+ switch (rv)
+ {
+ case 0:
+ break;
+ case VNET_API_ERROR_INSTANCE_IN_USE:
+ err = clib_error_return (
+ 0, "interface %U is currently up, set state down first",
+ format_vnet_sw_if_index_name, vnm, sw_if_index);
+ break;
+ case VNET_API_ERROR_INVALID_INTERFACE:
+ err = clib_error_return (0, "interface %U has no attached instances",
+ format_vnet_sw_if_index_name, vnm, sw_if_index);
+ break;
+ default:
+ err =
+ clib_error_return (0, "snort_interface_disable_all returned %d", rv);
+ break;
+ }
+ return err;
+}
+
+static clib_error_t *
snort_create_instance_command_fn (vlib_main_t *vm, unformat_input_t *input,
vlib_cli_command_t *cmd)
{
@@ -94,7 +159,7 @@ done:
VLIB_CLI_COMMAND (snort_create_instance_command, static) = {
.path = "snort create-instance",
- .short_help = "snort create-instaince name <name> [queue-size <size>] "
+ .short_help = "snort create-instance name <name> [queue-size <size>] "
"[on-disconnect drop|pass]",
.function = snort_create_instance_command_fn,
};
@@ -217,11 +282,15 @@ snort_attach_command_fn (vlib_main_t *vm, unformat_input_t *input,
{
unformat_input_t _line_input, *line_input = &_line_input;
vnet_main_t *vnm = vnet_get_main ();
- clib_error_t *err = 0;
- u8 *name = 0;
+ snort_main_t *sm = &snort_main;
+ snort_instance_t *si;
+ clib_error_t *err = NULL;
+ u8 *name = NULL;
+ u8 **names = NULL;
u32 sw_if_index = ~0;
- snort_attach_dir_t dir = SNORT_INOUT;
- int rv = 0;
+ snort_attach_dir_t direction = SNORT_INOUT;
+ u8 is_all_instances = 0;
+ int i;
/* Get a line of input. */
if (!unformat_user (input, unformat_line_input, line_input))
@@ -233,13 +302,15 @@ snort_attach_command_fn (vlib_main_t *vm, unformat_input_t *input,
vnm, &sw_if_index))
;
else if (unformat (line_input, "instance %s", &name))
- ;
+ vec_add1 (names, name);
+ else if (unformat (line_input, "all-instances"))
+ is_all_instances = 1;
else if (unformat (line_input, "input"))
- dir = SNORT_INPUT;
+ direction = SNORT_INPUT;
else if (unformat (line_input, "output"))
- dir = SNORT_OUTPUT;
+ direction = SNORT_OUTPUT;
else if (unformat (line_input, "inout"))
- dir = SNORT_INOUT;
+ direction = SNORT_INOUT;
else
{
err = clib_error_return (0, "unknown input `%U'",
@@ -254,46 +325,53 @@ snort_attach_command_fn (vlib_main_t *vm, unformat_input_t *input,
goto done;
}
- if (!name)
+ if (vec_len (names) == 0 && is_all_instances == 0)
{
- err = clib_error_return (0, "please specify instance name");
+ err = clib_error_return (0, "please specify instances");
goto done;
}
- rv = snort_interface_enable_disable (vm, (char *) name, sw_if_index, 1, dir);
+ if (is_all_instances)
+ {
+ if (vec_len (sm->instances) == 0)
+ {
+ err = clib_error_return (0, "no snort instances have been created");
+ goto done;
+ }
- switch (rv)
+ pool_foreach (si, sm->instances)
+ {
+ snort_attach_detach_instance (vm, vnm, (char *) si->name,
+ sw_if_index, 1 /* is_enable */,
+ direction);
+ }
+ }
+ else
{
- case 0:
- break;
- case VNET_API_ERROR_FEATURE_ALREADY_ENABLED:
- /* already attached to same instance */
- break;
- case VNET_API_ERROR_INSTANCE_IN_USE:
- err = clib_error_return (0,
- "interface %U already assigned to "
- "an instance",
- format_vnet_sw_if_index_name, vnm, sw_if_index);
- break;
- case VNET_API_ERROR_NO_SUCH_ENTRY:
- err = clib_error_return (0, "unknown instance '%s'", name);
- break;
- default:
- err = clib_error_return (0, "snort_interface_enable_disable returned %d",
- rv);
- break;
+ vec_foreach_index (i, names)
+ {
+ snort_attach_detach_instance (vm, vnm, (char *) names[i],
+ sw_if_index, 1 /* is_enable */,
+ direction);
+ }
}
done:
- vec_free (name);
+ vec_foreach_index (i, names)
+ {
+ vec_free (names[i]);
+ }
+ vec_free (names);
unformat_free (line_input);
return err;
}
VLIB_CLI_COMMAND (snort_attach_command, static) = {
.path = "snort attach",
- .short_help = "snort attach instance <name> interface <if-name> "
- "[input|ouput|inout]",
+ .short_help =
+ "snort attach all-instances|(instance <name> [instance <name> [...]]) "
+ "interface <if-name> "
+ "[input|output|inout]",
.function = snort_attach_command_fn,
};
@@ -303,9 +381,12 @@ snort_detach_command_fn (vlib_main_t *vm, unformat_input_t *input,
{
unformat_input_t _line_input, *line_input = &_line_input;
vnet_main_t *vnm = vnet_get_main ();
- clib_error_t *err = 0;
+ clib_error_t *err = NULL;
+ u8 *name = NULL;
+ u8 **names = NULL;
u32 sw_if_index = ~0;
- int rv = 0;
+ u8 is_all_instances = 0;
+ int i = 0;
/* Get a line of input. */
if (!unformat_user (input, unformat_line_input, line_input))
@@ -313,8 +394,12 @@ snort_detach_command_fn (vlib_main_t *vm, unformat_input_t *input,
while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
{
- if (unformat (line_input, "interface %U", unformat_vnet_sw_interface,
- vnm, &sw_if_index))
+ if (unformat (line_input, "instance %s", &name))
+ vec_add1 (names, name);
+ else if (unformat (line_input, "all-instances"))
+ is_all_instances = 1;
+ else if (unformat (line_input, "interface %U",
+ unformat_vnet_sw_interface, vnm, &sw_if_index))
;
else
{
@@ -330,32 +415,41 @@ snort_detach_command_fn (vlib_main_t *vm, unformat_input_t *input,
goto done;
}
- rv = snort_interface_enable_disable (vm, 0, sw_if_index, 0, SNORT_INOUT);
+ if (vec_len (names) == 0)
+ {
+ /* To maintain backwards compatibility */
+ is_all_instances = 1;
+ }
- switch (rv)
+ if (is_all_instances)
{
- case 0:
- break;
- case VNET_API_ERROR_INVALID_INTERFACE:
- err = clib_error_return (0,
- "interface %U is not assigned to snort "
- "instance!",
- format_vnet_sw_if_index_name, vnm, sw_if_index);
- break;
- default:
- err = clib_error_return (0, "snort_interface_enable_disable returned %d",
- rv);
- break;
+ err = snort_detach_all_instance (vm, vnm, sw_if_index);
+ }
+ else
+ {
+ vec_foreach_index (i, names)
+ {
+ snort_attach_detach_instance (vm, vnm, (char *) names[i],
+ sw_if_index, 0 /* is_enable */,
+ SNORT_INOUT);
+ }
}
done:
+ vec_foreach_index (i, names)
+ {
+ vec_free (names[i]);
+ }
+ vec_free (names);
unformat_free (line_input);
return err;
}
VLIB_CLI_COMMAND (snort_detach_command, static) = {
.path = "snort detach",
- .short_help = "snort detach interface <if-name>",
+ .short_help =
+ "snort detach all-instances|(instance <name> [instance <name> [...]]) "
+ "interface <if-name> ",
.function = snort_detach_command_fn,
};
@@ -384,17 +478,57 @@ snort_show_interfaces_command_fn (vlib_main_t *vm, unformat_input_t *input,
{
snort_main_t *sm = &snort_main;
vnet_main_t *vnm = vnet_get_main ();
- snort_instance_t *si;
- u32 *index;
-
- vlib_cli_output (vm, "interface\t\tsnort instance");
- vec_foreach (index, sm->instance_by_sw_if_index)
+ snort_interface_data_t *interface;
+ snort_instance_t *instance;
+ snort_attach_dir_t direction;
+ u32 instance_index;
+ u32 sw_if_index;
+ u8 is_input;
+ int i, j;
+
+ vlib_cli_output (vm, "interface\tinstances\tdirection");
+ vec_foreach_index (sw_if_index, sm->interfaces)
{
- if (index[0] != ~0)
+ interface = vec_elt_at_index (sm->interfaces, sw_if_index);
+
+ /* Loop over input instances and prints all of them (with direction
+ * indicated), then continues over output instances while ignoring
+ * previously printed input instances */
+ for (i = 0; i < vec_len (interface->input_instance_indices) +
+ vec_len (interface->output_instance_indices);
+ i++)
{
- si = vec_elt_at_index (sm->instances, index[0]);
- vlib_cli_output (vm, "%U:\t%s", format_vnet_sw_if_index_name, vnm,
- index - sm->instance_by_sw_if_index, si->name);
+ is_input = i < vec_len (interface->input_instance_indices);
+
+ instance_index =
+ is_input ? interface->input_instance_indices[i] :
+ interface->output_instance_indices
+ [i - vec_len (interface->input_instance_indices)];
+
+ /* When printing the output instances ignore the ones present in
+ * input instances as we have already printed them */
+ if (!is_input)
+ {
+ j =
+ vec_search (interface->input_instance_indices, instance_index);
+ if (j != ~0)
+ continue;
+ }
+
+ instance = snort_get_instance_by_index (instance_index);
+ direction = snort_get_instance_direction (instance_index, interface);
+ if (i == 0)
+ {
+ vlib_cli_output (vm, "%U:\t%s\t\t%s",
+ format_vnet_sw_if_index_name, vnm, sw_if_index,
+ instance->name,
+ snort_get_direction_name_by_enum (direction));
+ }
+ else
+ {
+ vlib_cli_output (vm, "\t\t%s\t\t%s", instance->name,
+ snort_get_direction_name_by_enum (direction));
+ }
}
}
return 0;
diff --git a/src/plugins/snort/enqueue.c b/src/plugins/snort/enqueue.c
index ce4f34491ec..84efb4d432f 100644
--- a/src/plugins/snort/enqueue.c
+++ b/src/plugins/snort/enqueue.c
@@ -1,7 +1,10 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright(c) 2021 Cisco Systems, Inc.
+ * Copyright(c) 2024 Arm Limited
*/
+#include <vnet/ip/ip4_inlines.h>
+#include <vnet/ip/ip4_packet.h>
#include <vlib/vlib.h>
#include <vnet/feature/feature.h>
#include <snort/snort.h>
@@ -56,6 +59,33 @@ static char *snort_enq_error_strings[] = {
#undef _
};
+static_always_inline u32
+get_snort_instance_index_ip4 (snort_main_t *sm, vlib_buffer_t *b, u32 fa_data)
+{
+ u32 hash;
+ u32 sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
+ ip4_header_t *ip = NULL;
+ u32 *instances = (fa_data == SNORT_INPUT) ?
+ sm->interfaces[sw_if_index].input_instance_indices :
+ sm->interfaces[sw_if_index].output_instance_indices;
+ int n_instances = vec_len (instances);
+
+ if (n_instances == 1)
+ {
+ return instances[0];
+ }
+ ip = vlib_buffer_get_current (b);
+ hash = ip4_compute_flow_hash (ip, IP_FLOW_HASH_DEFAULT);
+ return instances[hash % n_instances];
+}
+
+static_always_inline snort_instance_t *
+get_snort_instance (snort_main_t *sm, vlib_buffer_t *b, u32 fa_data)
+{
+ u32 instance_index = get_snort_instance_index_ip4 (sm, b, fa_data);
+ return snort_get_instance_by_index (instance_index);
+}
+
static_always_inline uword
snort_enq_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
vlib_frame_t *frame, int with_trace)
@@ -66,26 +96,24 @@ snort_enq_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
u32 thread_index = vm->thread_index;
u32 n_left = frame->n_vectors;
u32 n_trace = 0;
- u32 total_enq = 0, n_processed = 0;
+ u32 total_enq = 0, n_unprocessed = 0;
u32 *from = vlib_frame_vector_args (frame);
vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
u16 nexts[VLIB_FRAME_SIZE], *next = nexts;
+ u32 unprocessed_bufs[VLIB_FRAME_SIZE];
vlib_get_buffers (vm, from, bufs, n_left);
while (n_left)
{
- u64 fa_data;
- u32 instance_index, next_index, n;
- u32 l3_offset;
-
- fa_data =
- *(u64 *) vnet_feature_next_with_data (&next_index, b[0], sizeof (u64));
-
- instance_index = (u32) (fa_data & 0xffffffff);
- l3_offset =
- (fa_data >> 32) ? vnet_buffer (b[0])->ip.save_rewrite_length : 0;
- si = vec_elt_at_index (sm->instances, instance_index);
+ u32 next_index, n;
+ /* fa_data is either SNORT_INPUT or SNORT_OUTPUT */
+ u32 fa_data =
+ *(u32 *) vnet_feature_next_with_data (&next_index, b[0], sizeof (u32));
+ u32 l3_offset = (fa_data == SNORT_INPUT) ?
+ 0 :
+ vnet_buffer (b[0])->ip.save_rewrite_length;
+ si = get_snort_instance (sm, b[0], fa_data);
/* if client isn't connected skip enqueue and take default action */
if (PREDICT_FALSE (si->client_index == ~0))
@@ -95,7 +123,8 @@ snort_enq_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
else
next[0] = next_index;
next++;
- n_processed++;
+ unprocessed_bufs[n_unprocessed] = from[0];
+ n_unprocessed++;
}
else
{
@@ -108,7 +137,7 @@ snort_enq_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
vlib_buffer_chain_linearize (vm, b[0]);
- /* If this pkt is traced, snapshoot the data */
+ /* If this pkt is traced, snapshot the data */
if (with_trace && b[0]->flags & VLIB_BUFFER_IS_TRACED)
n_trace++;
@@ -125,12 +154,12 @@ snort_enq_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
b++;
}
- if (n_processed)
+ if (n_unprocessed)
{
vlib_node_increment_counter (vm, snort_enq_node.index,
- SNORT_ENQ_ERROR_NO_INSTANCE, n_processed);
- vlib_buffer_enqueue_to_next (vm, node, vlib_frame_vector_args (frame),
- nexts, n_processed);
+ SNORT_ENQ_ERROR_NO_INSTANCE, n_unprocessed);
+ vlib_buffer_enqueue_to_next (vm, node, unprocessed_bufs, nexts,
+ n_unprocessed);
}
pool_foreach (si, sm->instances)
diff --git a/src/plugins/snort/main.c b/src/plugins/snort/main.c
index 50bff027a13..9bab1185b60 100644
--- a/src/plugins/snort/main.c
+++ b/src/plugins/snort/main.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright(c) 2021 Cisco Systems, Inc.
+ * Copyright(c) 2024 Arm Limited
*/
#include <vlib/vlib.h>
@@ -96,6 +97,38 @@ snort_instance_disconnect (vlib_main_t *vm, u32 instance_index)
return rv;
}
+const char *
+snort_get_direction_name_by_enum (snort_attach_dir_t dir)
+{
+ switch (dir)
+ {
+ case SNORT_INPUT:
+ return "input";
+ case SNORT_OUTPUT:
+ return "output";
+ case SNORT_INOUT:
+ return "inout";
+ default:
+ return "none";
+ }
+}
+
+/* Returns SNORT_INVALID if the instance is not attached */
+snort_attach_dir_t
+snort_get_instance_direction (u32 instance_index,
+ snort_interface_data_t *interface)
+{
+ snort_attach_dir_t direction = SNORT_INVALID;
+ int i;
+ i = vec_search (interface->input_instance_indices, instance_index);
+ if (i != ~0)
+ direction = direction | SNORT_INPUT;
+ i = vec_search (interface->output_instance_indices, instance_index);
+ if (i != ~0)
+ direction = direction | SNORT_OUTPUT;
+ return direction;
+}
+
snort_instance_t *
snort_get_instance_by_name (char *name)
{
@@ -470,6 +503,30 @@ done:
return rv;
}
+static void
+snort_vnet_feature_enable_disable (snort_attach_dir_t snort_dir,
+ u32 sw_if_index, int is_enable)
+{
+ u32 fa_data;
+ switch (snort_dir)
+ {
+ case SNORT_INPUT:
+ fa_data = SNORT_INPUT;
+ vnet_feature_enable_disable ("ip4-unicast", "snort-enq", sw_if_index,
+ is_enable, &fa_data, sizeof (fa_data));
+ break;
+ case SNORT_OUTPUT:
+ fa_data = SNORT_OUTPUT;
+ vnet_feature_enable_disable ("ip4-output", "snort-enq", sw_if_index,
+ is_enable, &fa_data, sizeof (fa_data));
+ break;
+ default:
+ vlib_log_err (snort_log.class,
+ "Invalid direction given to enable/disable snort");
+ break;
+ }
+}
+
int
snort_interface_enable_disable (vlib_main_t *vm, char *instance_name,
u32 sw_if_index, int is_enable,
@@ -477,92 +534,216 @@ snort_interface_enable_disable (vlib_main_t *vm, char *instance_name,
{
snort_main_t *sm = &snort_main;
vnet_main_t *vnm = vnet_get_main ();
- snort_instance_t *si;
- u64 fa_data;
- u32 index;
+ vnet_sw_interface_t *software_interface =
+ vnet_get_sw_interface (vnm, sw_if_index);
+ snort_interface_data_t *interface_data;
+ snort_instance_t *instance;
+ u32 **instance_indices;
+ u32 instance_index;
+ const snort_attach_dir_t dirs[2] = { SNORT_INPUT, SNORT_OUTPUT };
int rv = 0;
+ int index, i;
- if (is_enable)
+ /* If interface is up, do not allow modifying attached instances */
+ if (software_interface->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
{
- if ((si = snort_get_instance_by_name (instance_name)) == 0)
- {
- log_err ("unknown instance '%s'", instance_name);
- return VNET_API_ERROR_NO_SUCH_ENTRY;
- }
+ rv = VNET_API_ERROR_INSTANCE_IN_USE;
+ log_err ("interface '%U' is currently up", format_vnet_sw_if_index_name,
+ vnm, sw_if_index);
+ goto done;
+ }
+
+ /* Check if provided instance name exists */
+ instance = snort_get_instance_by_name (instance_name);
+ if (instance == NULL)
+ {
+ rv = VNET_API_ERROR_NO_SUCH_ENTRY;
+ log_err ("unknown instance '%s'", instance_name);
+ goto done;
+ }
+
+ /* Check if interface is attached before unnecessarily increasing size of
+ * vector */
+ if (!is_enable && vec_len (sm->interfaces) <= sw_if_index)
+ {
+ rv = VNET_API_ERROR_INVALID_INTERFACE;
+ log_err ("interface %U is not assigned to snort instance %s!",
+ format_vnet_sw_if_index_name, vnm, sw_if_index, instance->name);
+ goto done;
+ }
- vec_validate_init_empty (sm->instance_by_sw_if_index, sw_if_index, ~0);
+ /* vec_validate initialises empty space to 0s, which corresponds to null
+ * pointers (i.e. empty vectors) in the snort_interface_data_t structs which
+ * is precisely what we need */
+ vec_validate (sm->interfaces, sw_if_index);
- index = sm->instance_by_sw_if_index[sw_if_index];
- if (index != ~0)
+ interface_data = vec_elt_at_index (sm->interfaces, sw_if_index);
+ instance_index = instance->index;
+
+ /* When detaching with direction SNORT_INOUT choose currently attached
+ * directions */
+ if (!is_enable)
+ {
+ snort_dir =
+ snort_get_instance_direction (instance_index, interface_data);
+ /* If snort_dir is SNORT_INVALID then the instance is not attached */
+ if (snort_dir == SNORT_INVALID)
{
- if (index == si->index)
- rv = VNET_API_ERROR_FEATURE_ALREADY_ENABLED;
- else
- rv = VNET_API_ERROR_INSTANCE_IN_USE;
- si = vec_elt_at_index (sm->instances, index);
- log_err ("interface %U already assgined to instance '%s'",
- format_vnet_sw_if_index_name, vnm, sw_if_index, si->name);
+ rv = VNET_API_ERROR_INVALID_INTERFACE;
+ log_err ("interface %U is not assigned to snort instance %s!",
+ format_vnet_sw_if_index_name, vnm, sw_if_index,
+ instance->name);
goto done;
}
+ }
- index = sm->instance_by_sw_if_index[sw_if_index] = si->index;
- if (snort_dir & SNORT_INPUT)
- {
- fa_data = (u64) index;
- vnet_feature_enable_disable ("ip4-unicast", "snort-enq", sw_if_index,
- 1, &fa_data, sizeof (fa_data));
- }
- if (snort_dir & SNORT_OUTPUT)
- {
- fa_data = (1LL << 32 | index);
- vnet_feature_enable_disable ("ip4-output", "snort-enq", sw_if_index,
- 1, &fa_data, sizeof (fa_data));
- }
+ /* Error if direction is invalid */
+ if (snort_dir == SNORT_INVALID)
+ {
+ rv = VNET_API_ERROR_INVALID_ARGUMENT;
+ vlib_log_err (snort_log.class,
+ "cannot attach/detach with invalid direction ");
+ goto done;
}
- else
+
+ /* Loop evaluates input instances and then output instances */
+ for (i = 0; i < 2; i++)
{
- if (sw_if_index >= vec_len (sm->instance_by_sw_if_index) ||
- sm->instance_by_sw_if_index[sw_if_index] == ~0)
+ if (!(snort_dir & dirs[i]))
+ continue;
+
+ instance_indices = (dirs[i] == SNORT_INPUT) ?
+ &(interface_data->input_instance_indices) :
+ &(interface_data->output_instance_indices);
+ index = vec_search (*instance_indices, instance_index);
+
+ if (is_enable)
{
- rv = VNET_API_ERROR_INVALID_INTERFACE;
- log_err ("interface %U is not assigned to snort instance!",
- format_vnet_sw_if_index_name, vnm, sw_if_index);
- goto done;
+ /* Error if instance is already attached when trying to attach */
+ if (index != ~0)
+ {
+ rv = VNET_API_ERROR_FEATURE_ALREADY_ENABLED;
+ log_err ("interface %U already assgined to instance '%s' on "
+ "direction '%s'",
+ format_vnet_sw_if_index_name, vnm, sw_if_index,
+ instance->name,
+ snort_get_direction_name_by_enum (dirs[i]));
+ goto done;
+ }
+ }
+ else
+ {
+ /* Error if instance is not attached when trying to detach */
+ if (index == ~0)
+ {
+ rv = VNET_API_ERROR_INVALID_INTERFACE;
+ log_err ("interface %U is not assigned to snort instance %s on "
+ "direction '%s'!",
+ format_vnet_sw_if_index_name, vnm, sw_if_index,
+ instance->name,
+ snort_get_direction_name_by_enum (dirs[i]));
+ goto done;
+ }
}
- index = sm->instance_by_sw_if_index[sw_if_index];
- si = vec_elt_at_index (sm->instances, index);
- sm->instance_by_sw_if_index[sw_if_index] = ~0;
- if (snort_dir & SNORT_INPUT)
+ if (is_enable)
{
- fa_data = (u64) index;
- vnet_feature_enable_disable ("ip4-unicast", "snort-enq", sw_if_index,
- 0, &fa_data, sizeof (fa_data));
+ /* Enable feature if not previously enabled */
+ if (vec_len (*instance_indices) == 0)
+ {
+ snort_vnet_feature_enable_disable (dirs[i], sw_if_index,
+ 1 /* is_enable */);
+ }
+ vec_add1 (*instance_indices, instance_index);
}
- if (snort_dir & SNORT_OUTPUT)
+ else
{
- fa_data = (1LL << 32 | index);
- vnet_feature_enable_disable ("ip4-output", "snort-enq", sw_if_index,
- 0, &fa_data, sizeof (fa_data));
+ /* Disable feature when removing last instance */
+ if (vec_len (*instance_indices) == 1)
+ {
+ snort_vnet_feature_enable_disable (dirs[i], sw_if_index,
+ 0 /* is_enable */);
+ }
+ vec_del1 (*instance_indices, index);
}
}
+done:
+ return rv;
+}
+
+int
+snort_interface_disable_all (vlib_main_t *vm, u32 sw_if_index)
+{
+ snort_main_t *sm = &snort_main;
+ vnet_main_t *vnm = vnet_get_main ();
+ vnet_sw_interface_t *software_interface =
+ vnet_get_sw_interface (vnm, sw_if_index);
+ snort_interface_data_t *interface_data;
+ int rv = 0;
+
+ if (software_interface->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
+ {
+ rv = VNET_API_ERROR_INSTANCE_IN_USE;
+ log_err ("interface '%U' is currently up", format_vnet_sw_if_index_name,
+ vnm, sw_if_index);
+ goto done;
+ }
+
+ if (vec_len (sm->interfaces) <= sw_if_index)
+ {
+ rv = VNET_API_ERROR_INVALID_INTERFACE;
+ log_err ("no instances attached to interface %U",
+ format_vnet_sw_if_index_name, vnm, sw_if_index);
+ goto done;
+ }
+
+ interface_data = vec_elt_at_index (sm->interfaces, sw_if_index);
+
+ if (vec_len (interface_data->input_instance_indices) == 0 &&
+ vec_len (interface_data->output_instance_indices) == 0)
+ {
+ rv = VNET_API_ERROR_INVALID_INTERFACE;
+ log_err ("no instances attached to interface %U",
+ format_vnet_sw_if_index_name, vnm, sw_if_index);
+ goto done;
+ }
+
+ if (vec_len (interface_data->input_instance_indices) > 0)
+ {
+ snort_vnet_feature_enable_disable (SNORT_INPUT, sw_if_index,
+ 0 /* is_enable */);
+ vec_free (interface_data->input_instance_indices);
+ }
+ if (vec_len (interface_data->output_instance_indices) > 0)
+ {
+ snort_vnet_feature_enable_disable (SNORT_OUTPUT, sw_if_index,
+ 0 /* is_enable */);
+ vec_free (interface_data->output_instance_indices);
+ }
done:
return rv;
}
static int
-snort_strip_instance_interfaces (vlib_main_t *vm, u32 instance_index)
+snort_strip_instance_interfaces (vlib_main_t *vm, snort_instance_t *instance)
{
snort_main_t *sm = &snort_main;
- u32 *index;
+ snort_interface_data_t *interface;
+ snort_attach_dir_t direction;
+ int i;
int rv = 0;
- vec_foreach (index, sm->instance_by_sw_if_index)
+ /* Find all interfaces containing the given snort instance to disable */
+ vec_foreach_index (i, sm->interfaces)
{
- if (*index == instance_index)
- rv = snort_interface_enable_disable (
- vm, NULL, index - sm->instance_by_sw_if_index, 0, 0);
+ /* Check if the snort_instance is attached by checking if the direction
+ * is SNORT_INVALID */
+ interface = vec_elt_at_index (sm->interfaces, i);
+ direction = snort_get_instance_direction (instance->index, interface);
+ if (direction != SNORT_INVALID)
+ rv = snort_interface_enable_disable (vm, (char *) instance->name, i,
+ 0 /* is_enable */, direction);
if (rv)
break;
}
@@ -585,7 +766,7 @@ snort_instance_delete (vlib_main_t *vm, u32 instance_index)
if (si->client_index != ~0)
return VNET_API_ERROR_INSTANCE_IN_USE;
- if ((rv = snort_strip_instance_interfaces (vm, si->index)))
+ if ((rv = snort_strip_instance_interfaces (vm, si)))
return rv;
hash_unset_mem (sm->instance_by_name, si->name);
diff --git a/src/plugins/snort/snort.h b/src/plugins/snort/snort.h
index c7e856c0127..76f0652df10 100644
--- a/src/plugins/snort/snort.h
+++ b/src/plugins/snort/snort.h
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright(c) 2021 Cisco Systems, Inc.
+ * Copyright(c) 2024 Arm Limited
*/
#ifndef __snort_snort_h__
@@ -68,13 +69,20 @@ typedef struct
void *interrupts;
} snort_per_thread_data_t;
+/* Holds snort plugin related information for an interface */
+typedef struct
+{
+ u32 *input_instance_indices;
+ u32 *output_instance_indices;
+} snort_interface_data_t;
+
typedef struct
{
clib_socket_t *listener;
snort_client_t *clients;
snort_instance_t *instances;
uword *instance_by_name;
- u32 *instance_by_sw_if_index;
+ snort_interface_data_t *interfaces;
u8 **buffer_pool_base_addrs;
snort_per_thread_data_t *per_thread_data;
u32 input_mode;
@@ -96,9 +104,11 @@ typedef enum
typedef enum
{
- SNORT_INPUT = 1,
- SNORT_OUTPUT = 2,
- SNORT_INOUT = 3
+ SNORT_INVALID = 0x00,
+ SNORT_INPUT = 0x01,
+ SNORT_OUTPUT = 0x02,
+ /* SNORT_INOUT === SNORT_INPUT | SNORT_OUTPUT */
+ SNORT_INOUT = 0x03
} snort_attach_dir_t;
#define SNORT_ENQ_NEXT_NODES \
@@ -108,6 +118,10 @@ typedef enum
/* functions */
snort_main_t *snort_get_main ();
+const char *snort_get_direction_name_by_enum (snort_attach_dir_t dir);
+snort_attach_dir_t
+snort_get_instance_direction (u32 instance_index,
+ snort_interface_data_t *interface);
snort_instance_t *snort_get_instance_by_index (u32 instance_index);
snort_instance_t *snort_get_instance_by_name (char *name);
int snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz,
@@ -115,6 +129,7 @@ int snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz,
int snort_interface_enable_disable (vlib_main_t *vm, char *instance_name,
u32 sw_if_index, int is_enable,
snort_attach_dir_t dir);
+int snort_interface_disable_all (vlib_main_t *vm, u32 sw_if_index);
int snort_set_node_mode (vlib_main_t *vm, u32 mode);
int snort_instance_delete (vlib_main_t *vm, u32 instance_index);
int snort_instance_disconnect (vlib_main_t *vm, u32 instance_index);
diff --git a/src/plugins/snort/snort_api.c b/src/plugins/snort/snort_api.c
index adad0d8763f..4016dfad63f 100644
--- a/src/plugins/snort/snort_api.c
+++ b/src/plugins/snort/snort_api.c
@@ -185,7 +185,8 @@ vl_api_snort_interface_get_t_handler (vl_api_snort_interface_get_t *mp)
snort_main_t *sm = snort_get_main ();
vl_api_snort_interface_get_reply_t *rmp;
u32 sw_if_index;
- u32 *index;
+ u32 *instances;
+ u32 index;
int rv = 0;
sw_if_index = clib_net_to_host_u32 (mp->sw_if_index);
@@ -193,7 +194,7 @@ vl_api_snort_interface_get_t_handler (vl_api_snort_interface_get_t *mp)
if (sw_if_index == INDEX_INVALID)
{
/* clang-format off */
- if (vec_len (sm->instance_by_sw_if_index) == 0)
+ if (vec_len (sm->interfaces) == 0)
{
REPLY_MACRO2 (VL_API_SNORT_INTERFACE_GET_REPLY, ({ rmp->cursor = ~0; }));
return;
@@ -201,17 +202,36 @@ vl_api_snort_interface_get_t_handler (vl_api_snort_interface_get_t *mp)
REPLY_AND_DETAILS_VEC_MACRO(
VL_API_SNORT_INTERFACE_GET_REPLY,
- sm->instance_by_sw_if_index,
+ sm->interfaces,
mp, rmp, rv, ({
- index = vec_elt_at_index (sm->instance_by_sw_if_index, cursor);
- send_snort_interface_details (cursor, *index, rp, mp->context);
+ instances = vec_len(sm->interfaces[cursor].input_instance_indices) ?
+ sm->interfaces[cursor].input_instance_indices : sm->interfaces[cursor].output_instance_indices;
+ if (vec_len(instances) == 0)
+ {
+ index = ~0;
+ }
+ else {
+ index = instances[0];
+ }
+ send_snort_interface_details (cursor, index, rp, mp->context);
}))
/* clang-format on */
}
else
{
- index = vec_elt_at_index (sm->instance_by_sw_if_index, sw_if_index);
- if (snort_get_instance_by_index (index[0]))
+ instances =
+ vec_len (sm->interfaces[sw_if_index].input_instance_indices) ?
+ sm->interfaces[sw_if_index].input_instance_indices :
+ sm->interfaces[sw_if_index].output_instance_indices;
+ if (vec_len (instances) == 0)
+ {
+ index = ~0;
+ }
+ else
+ {
+ index = instances[0];
+ }
+ if (snort_get_instance_by_index (index))
{
vl_api_registration_t *rp =
vl_api_client_index_to_registration (mp->client_index);
@@ -221,7 +241,8 @@ vl_api_snort_interface_get_t_handler (vl_api_snort_interface_get_t *mp)
return;
}
- send_snort_interface_details (sw_if_index, *index, rp, mp->context);
+ send_snort_interface_details (sw_if_index, *instances, rp,
+ mp->context);
}
else
{
@@ -352,11 +373,9 @@ vl_api_snort_interface_detach_t_handler (vl_api_snort_interface_detach_t *mp)
vlib_main_t *vm = vlib_get_main ();
vl_api_snort_interface_detach_reply_t *rmp;
u32 sw_if_index = clib_net_to_host_u32 (mp->sw_if_index);
- int rv = VNET_API_ERROR_NO_MATCHING_INTERFACE;
+ int rv;
- if (sw_if_index != INDEX_INVALID)
- rv = snort_interface_enable_disable (vm, NULL, sw_if_index,
- 0 /* is_enable */, SNORT_INOUT);
+ rv = snort_interface_disable_all (vm, sw_if_index);
REPLY_MACRO (VL_API_SNORT_INTERFACE_DETACH_REPLY);
}
diff --git a/test/test_snort.py b/test/test_snort.py
index 19401cb7b85..c25c0e65145 100644
--- a/test/test_snort.py
+++ b/test/test_snort.py
@@ -12,10 +12,10 @@ class TestSnort(VppTestCase):
def setUpClass(cls):
super(TestSnort, cls).setUpClass()
try:
- cls.create_pg_interfaces(range(2))
+ cls.create_pg_interfaces(range(4))
for i in cls.pg_interfaces:
i.config_ip4().resolve_arp()
- i.admin_up()
+ i.admin_down()
except Exception:
cls.tearDownClass()
raise
@@ -24,7 +24,6 @@ class TestSnort(VppTestCase):
def tearDownClass(cls):
for i in cls.pg_interfaces:
i.unconfig_ip4()
- i.admin_down()
super(TestSnort, cls).tearDownClass()
def test_snort_cli(self):
@@ -36,14 +35,18 @@ class TestSnort(VppTestCase):
"snort create-instance name snortTest2 queue-size 16 on-disconnect pass": "",
"snort attach instance snortTest interface pg0 output": "",
"snort attach instance snortTest2 interface pg1 input": "",
+ "snort attach all-instances interface pg2 inout": "",
+ "snort attach instance snortTest instance snortTest2 interface pg3 inout": "",
"show snort instances": "snortTest",
"show snort interfaces": "pg0",
"show snort clients": "number of clients",
"show snort mode": "input mode: interrupt",
"snort mode polling": "",
"snort mode interrupt": "",
- "snort detach interface pg0": "",
- "snort detach interface pg1": "",
+ "snort detach instance snortTest interface pg0": "",
+ "snort detach instance snortTest2 interface pg1": "",
+ "snort detach all-instances interface pg2": "",
+ "snort detach instance snortTest instance snortTest2 interface pg3": "",
"snort delete instance snortTest": "",
}
@@ -64,7 +67,7 @@ class TestSnortVapi(VppTestCase):
for i in cls.pg_interfaces:
i.config_ip4()
i.resolve_arp()
- i.admin_up()
+ i.admin_down()
except Exception:
cls.tearDownClass()
raise
@@ -73,7 +76,6 @@ class TestSnortVapi(VppTestCase):
def tearDownClass(cls):
for i in cls.pg_interfaces:
i.unconfig_ip4()
- i.admin_down()
super(TestSnortVapi, cls).tearDownClass()
def test_snort_01_modes_set_interrupt(self):
@@ -109,20 +111,27 @@ class TestSnortVapi(VppTestCase):
reply = self.vapi.snort_interface_attach(
instance_index=0, sw_if_index=1, snort_dir=1
)
+ reply = self.vapi.snort_interface_attach(
+ instance_index=0, sw_if_index=2, snort_dir=2
+ )
+ reply = self.vapi.snort_interface_attach(
+ instance_index=1, sw_if_index=2, snort_dir=3
+ )
+ reply = self.vapi.cli("show snort interfaces")
+ self.assertIn("snortTest0", reply)
+ self.assertIn("snortTest1", reply)
+ self.assertIn("input", reply)
+ self.assertIn("inout", reply)
+ self.assertIn("output", reply)
try:
reply = self.vapi.snort_interface_attach(
- instance_index=1, sw_if_index=1, snort_dir=1
+ instance_index=1, sw_if_index=2, snort_dir=2
)
except:
pass
else:
self.assertNotEqual(reply.retval, 0)
-
- reply = self.vapi.snort_interface_attach(
- instance_index=1, sw_if_index=2, snort_dir=3
- )
reply = self.vapi.cli("show snort interfaces")
- self.assertIn("snortTest0", reply)
self.assertIn("snortTest1", reply)
def test_snort_05_delete_instance(self):
@@ -131,14 +140,13 @@ class TestSnortVapi(VppTestCase):
reply = self.vapi.cli("show snort interfaces")
self.assertNotIn("snortTest0", reply)
self.assertIn("snortTest1", reply)
- reply = self.vapi.cli("show snort interfaces")
self.assertNotIn("pg0", reply)
self.assertIn("pg1", reply)
def test_snort_06_detach_if(self):
"""Interfaces can be detached"""
try:
- reply = self.vapi.snort_interface_detach(sw_if_index=1)
+ reply = self.vapi.snort_interface_detach(sw_if_index=3)
except:
pass
else: