diff options
Diffstat (limited to 'src/plugins/snort/main.c')
-rw-r--r-- | src/plugins/snort/main.c | 295 |
1 files changed, 238 insertions, 57 deletions
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); |