From: Damjan Marion Date: Tue, 6 May 2025 15:45:16 +0000 (+0200) Subject: snort: plugin rework X-Git-Tag: v26.02-rc0~45 X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=e070068b768cbd72173a945d9954d1200c69bb08;p=vpp.git snort: plugin rework Changes: - support for multiple qpairs per thread - ability for daq to connect to multiple instances - ability for daq to connect to specific qpairs - added ownership to each qpair to avoid multiple clients using same one - enabled packet rewrite - enqueue and dequeue nodes are decoupled from feature arcs so they can be used in different datapaths - feature arc doesn't support multiple-instances anymore, use multiple qpairs for load-balancing - per-instance drop decisions based on drop bitmap sample snort invocation: snort \ \ --max-packet-threads 3 \ # number of threads snort will launch --daq-dir \ # path to dir with libdaq_bpp.so --daq vpp \ # enable VPP daq --daq-var \ # optional, path to snort.sock --daq-var debug \ # optional, turn on debug prints -i vpp0:1 \ # attach instance to vpp0 instance qpair 1.0 -i vpp0:1.1 \ # attach instance to vpp0 instance qpair 1.1 -i vpp0:2.0,2.1,0 \ # attach instance to vpp0 instance qpairs 2.0, 2.1 and 0.0 qpair is identified as x.y where x is thread_id and y is queue_id, .y part can be ommited if queue_id is 0. Type: feature Change-Id: I4f65b9a12ec71b115b9cb2da814c7918ddf6191d Signed-off-by: Damjan Marion Signed-off-by: Mohsin Kazmi --- diff --git a/build/external/Makefile b/build/external/Makefile index 5394eccc3cb..51064ce8f06 100644 --- a/build/external/Makefile +++ b/build/external/Makefile @@ -16,6 +16,7 @@ PKG_SUFFIX ?= $(shell git log --oneline v$(PKG_VERSION)-rc0.. . | wc -l) include ../build_common.mk include ../packages_common.mk +include packages/daq.mk include packages/ipsec-mb.mk include packages/quicly.mk ifneq ($(shell uname), FreeBSD) @@ -31,16 +32,16 @@ clean: .PHONY: install ifeq ($(shell uname), FreeBSD) -install: $(if $(ARCH_X86_64), ipsec-mb-install) dpdk-install quicly-install +install: $(if $(ARCH_X86_64), ipsec-mb-install) daq-install dpdk-install quicly-install else -install: $(if $(ARCH_X86_64), ipsec-mb-install) dpdk-install rdma-core-install quicly-install xdp-tools-install $(if $(AARCH64), octeon-roc-install) +install: $(if $(ARCH_X86_64), ipsec-mb-install) daq-install dpdk-install rdma-core-install quicly-install xdp-tools-install $(if $(AARCH64), octeon-roc-install) endif # FreeBSD .PHONY: config ifeq ($(shell uname), FreeBSD) -config: $(if $(ARCH_X86_64), ipsec-mb-config) dpdk-config quicly-build +config: daq-config $(if $(ARCH_X86_64), ipsec-mb-config) dpdk-config quicly-build else -config: $(if $(ARCH_X86_64), ipsec-mb-config) dpdk-config rdma-core-config quicly-build +config: daq-config $(if $(ARCH_X86_64), ipsec-mb-config) dpdk-config rdma-core-config quicly-build endif # FreeBSD ############################################################################## diff --git a/build/external/packages/daq.mk b/build/external/packages/daq.mk new file mode 100644 index 00000000000..f1f5a827abc --- /dev/null +++ b/build/external/packages/daq.mk @@ -0,0 +1,40 @@ +# Copyright (c) 2025 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. + +daq_version := 3.0.21 +daq_tarball := daq-$(daq_version).tar.gz +daq_tarball_sha256sum_3.0.21 := 60ad9405c1c6b75955e0784511b173570a601491ccdb6399da53ca811c446a96 +daq_tarball_sha256sum := $(daq_tarball_sha256sum_$(daq_version)) +daq_tarball_strip_dirs := 1 +daq_url := https://github.com/snort3/libdaq/archive/refs/tags/v$(daq_version).tar.gz + +define daq_config_cmds + @rm -f $(daq_config_log) + @cd ${daq_src_dir} && ./bootstrap > $(daq_config_log) 2>&1 + @cd ${daq_src_dir} && ./configure --prefix='$(daq_install_dir)' --enable-shared --enable-static \ + --disable-bundled-modules \ + CFLAGS='' \ + >> $(daq_config_log) 2>&1 +endef + +define daq_build_cmds + @cd ${daq_src_dir} && $(MAKE) V=1 > $(daq_build_log) +endef + +define daq_install_cmds + @rm -f $(daq_install_log) + @cd ${daq_src_dir} && \ + $(MAKE) install V=1 PREFIX='$(daq_install_dir)' >> $(daq_install_log) +endef + +$(eval $(call package,daq)) diff --git a/src/plugins/snort/CMakeLists.txt b/src/plugins/snort/CMakeLists.txt index 3fc2bd625a4..895fdd04c58 100644 --- a/src/plugins/snort/CMakeLists.txt +++ b/src/plugins/snort/CMakeLists.txt @@ -1,31 +1,11 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright(c) 2021 Cisco Systems, Inc. -add_vpp_plugin(snort - SOURCES - enqueue.c - dequeue.c - main.c - cli.c - snort_api.c - - API_FILES - snort.api - - MULTIARCH_SOURCES - enqueue.c - dequeue.c - - COMPONENT - vpp-plugin-snort -) - -# DAQ - find_path(LIBDAQ_INCLUDE_DIR NAMES daq_module_api.h daq_dlt.h daq_version.h) +find_library(LIBDAQ_LINK_LIBRARY NAMES libdaq.a) if (NOT LIBDAQ_INCLUDE_DIR) - message(WARNING "-- libdaq headers not found - snort3 DAQ disabled") + message(WARNING "-- libdaq headers not found - snort plugin disabled") return() endif() @@ -40,14 +20,41 @@ foreach(l ${daq_version}) endforeach() set(DAQ_VER "${DAQ_VERSION_MAJOR}.${DAQ_VERSION_MINOR}.${DAQ_VERSION_PATCH}") -message(STATUS "libdaq ${DAQ_VER} include files found at ${LIBDAQ_INCLUDE_DIR}") - if (NOT DAQ_VERSION_MAJOR MATCHES 3) message(WARNING "-- libdaq version not supported - snort3 DAQ disabled") return() endif() -add_library(daq_vpp SHARED daq_vpp.c) +message(STATUS "snort plugin needs libdaq ${DAQ_VER} - found at ${LIBDAQ_LINK_LIBRARY}") + +include_directories (${LIBDAQ_INCLUDE_DIR}) + +add_vpp_plugin(snort + SOURCES + cli.c + dequeue.c + enqueue.c + format.c + interface.c + main.c + snort_api.c + socket.c + + API_FILES + snort.api + + INSTALL_HEADERS + export.h + + MULTIARCH_SOURCES + enqueue.c + dequeue.c + + COMPONENT + vpp-plugin-snort +) + +add_library(daq_vpp SHARED daq/main.c daq/config.c daq/socket.c) set_target_properties(daq_vpp PROPERTIES SOVERSION ${VPP_LIB_VERSION}) target_compile_options (daq_vpp PRIVATE "-fvisibility=hidden") target_compile_options (daq_vpp PRIVATE "-DHAVE_VISIBILITY") diff --git a/src/plugins/snort/cli.c b/src/plugins/snort/cli.c index d4b69adae7d..d04dd392f38 100644 --- a/src/plugins/snort/cli.c +++ b/src/plugins/snort/cli.c @@ -7,28 +7,17 @@ #include #include -static u8 * -format_snort_instance (u8 *s, va_list *args) -{ - snort_instance_t *i = va_arg (*args, snort_instance_t *); - s = format (s, "%s [idx:%d sz:%d fd:%d]", i->name, i->index, i->shm_size, - i->shm_fd); - - return s; -} - 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) + int is_enable, int in, int out) { clib_error_t *err = NULL; int rv = snort_interface_enable_disable (vm, instance_name, sw_if_index, - is_enable, dir); + is_enable, in, out); switch (rv) { case 0: - break; case VNET_API_ERROR_FEATURE_ALREADY_ENABLED: /* already attached to same instance */ break; @@ -55,56 +44,28 @@ snort_attach_detach_instance (vlib_main_t *vm, vnet_main_t *vnm, 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) { - unformat_input_t _line_input, *line_input = &_line_input; clib_error_t *err = 0; u8 *name = 0; u32 queue_size = 1024; - u8 drop_on_diconnect = 1; + u32 qpairs_per_thread = 1; + u8 drop_on_disconnect = 1; int rv = 0; - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { - if (unformat (line_input, "queue-size %u", &queue_size)) + if (unformat (input, "queue-size %u", &queue_size)) ; - else if (unformat (line_input, "on-disconnect drop")) - drop_on_diconnect = 1; - else if (unformat (line_input, "on-disconnect pass")) - drop_on_diconnect = 0; - else if (unformat (line_input, "name %s", &name)) + else if (unformat (input, "queues-per-thread %u", &qpairs_per_thread)) + ; + else if (unformat (input, "on-disconnect drop")) + drop_on_disconnect = 1; + else if (unformat (input, "on-disconnect pass")) + drop_on_disconnect = 0; + else if (unformat (input, "name %s", &name)) ; else { @@ -126,8 +87,15 @@ snort_create_instance_command_fn (vlib_main_t *vm, unformat_input_t *input, goto done; } - rv = snort_instance_create (vm, (char *) name, min_log2 (queue_size), - drop_on_diconnect); + rv = snort_instance_create ( + vm, + &(snort_instance_create_args_t){ + .log2_queue_sz = min_log2 (queue_size), + .drop_on_disconnect = drop_on_disconnect, + .drop_bitmap = 1 << DAQ_VERDICT_BLOCK | 1 << DAQ_VERDICT_BLACKLIST, + .qpairs_per_thread = qpairs_per_thread, + }, + "%s", name); switch (rv) { @@ -153,7 +121,6 @@ snort_create_instance_command_fn (vlib_main_t *vm, unformat_input_t *input, done: vec_free (name); - unformat_free (line_input); return err; } @@ -165,77 +132,57 @@ VLIB_CLI_COMMAND (snort_create_instance_command, static) = { }; static clib_error_t * -snort_disconnect_instance_command_fn (vlib_main_t *vm, unformat_input_t *input, - vlib_cli_command_t *cmd) +snort_disconnect_client_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; u8 *name = 0; - snort_instance_t *si; int rv = 0; + u32 client_index = SNORT_INVALID_CLIENT_INDEX; - if (!unformat_user (input, unformat_line_input, line_input)) - return clib_error_return (0, "please specify instance name"); - - if (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - unformat (line_input, "%s", &name); + unformat (input, "%s", &name); if (!name) { - err = clib_error_return (0, "please specify instance name"); + err = clib_error_return (0, "please specify client name"); goto done; } - si = snort_get_instance_by_name ((char *) name); - if (!si) - rv = VNET_API_ERROR_NO_SUCH_ENTRY; - else - rv = snort_instance_disconnect (vm, si->index); + rv = snort_client_disconnect (vm, client_index); switch (rv) { case 0: break; case VNET_API_ERROR_NO_SUCH_ENTRY: - err = clib_error_return (0, "unknown instance '%s'", name); - break; - case VNET_API_ERROR_FEATURE_DISABLED: - err = clib_error_return (0, "instance '%s' is not connected", name); - break; - case VNET_API_ERROR_INVALID_VALUE: - err = clib_error_return (0, "failed to disconnect a broken client"); + err = clib_error_return (0, "unknown client '%s'", name); break; default: - err = clib_error_return (0, "snort_instance_disconnect returned %d", rv); + err = clib_error_return (0, "snort_client_disconnect returned %d", rv); break; } done: vec_free (name); - unformat_free (line_input); return err; } -VLIB_CLI_COMMAND (snort_disconnect_instance_command, static) = { - .path = "snort disconnect instance", - .short_help = "snort disconnect instance ", - .function = snort_disconnect_instance_command_fn, +VLIB_CLI_COMMAND (snort_disconnect_client_command, static) = { + .path = "snort disconnect client", + .short_help = "snort disconnect client ", + .function = snort_disconnect_client_command_fn, }; static clib_error_t * snort_delete_instance_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; u8 *name = 0; int rv = 0; - if (!unformat_user (input, unformat_line_input, line_input)) - return clib_error_return (0, "please specify instance name"); - - if (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - unformat (line_input, "%s", &name); + if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + unformat (input, "%s", &name); if (!name) { @@ -266,7 +213,6 @@ snort_delete_instance_command_fn (vlib_main_t *vm, unformat_input_t *input, done: vec_free (name); - unformat_free (line_input); return err; } @@ -280,37 +226,25 @@ static clib_error_t * snort_attach_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd) { - unformat_input_t _line_input, *line_input = &_line_input; vnet_main_t *vnm = vnet_get_main (); - 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 direction = SNORT_INOUT; - u8 is_all_instances = 0; - int i; + int in = 0, out = 0; - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { - if (unformat (line_input, "interface %U", unformat_vnet_sw_interface, - vnm, &sw_if_index)) + if (unformat (input, "interface %U", unformat_vnet_sw_interface, vnm, + &sw_if_index)) + ; + else if (unformat (input, "instance %s", &name)) ; - 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")) - direction = SNORT_INPUT; - else if (unformat (line_input, "output")) - direction = SNORT_OUTPUT; - else if (unformat (line_input, "inout")) - direction = SNORT_INOUT; + else if (unformat (input, "input")) + in = 1; + else if (unformat (input, "output")) + out = 1; + else if (unformat (input, "inout")) + in = out = 1; else { err = clib_error_return (0, "unknown input `%U'", @@ -325,53 +259,25 @@ snort_attach_command_fn (vlib_main_t *vm, unformat_input_t *input, goto done; } - if (vec_len (names) == 0 && is_all_instances == 0) + if (!name) { - err = clib_error_return (0, "please specify instances"); + err = clib_error_return (0, "please specify instance"); goto done; } - if (is_all_instances) - { - if (vec_len (sm->instances) == 0) - { - err = clib_error_return (0, "no snort instances have been created"); - goto done; - } - - pool_foreach (si, sm->instances) - { - snort_attach_detach_instance (vm, vnm, (char *) si->name, - sw_if_index, 1 /* is_enable */, - direction); - } - } - else - { - vec_foreach_index (i, names) - { - snort_attach_detach_instance (vm, vnm, (char *) names[i], - sw_if_index, 1 /* is_enable */, - direction); - } - } + snort_attach_detach_instance (vm, vnm, (char *) name, sw_if_index, + 1 /* is_enable */, in, out); done: - vec_foreach_index (i, names) - { - vec_free (names[i]); - } - vec_free (names); - unformat_free (line_input); + vec_free (name); return err; } VLIB_CLI_COMMAND (snort_attach_command, static) = { .path = "snort attach", - .short_help = - "snort attach all-instances|(instance [instance [...]]) " - "interface " - "[input|output|inout]", + .short_help = "snort attach instance [instance [...]] " + "interface " + "[input|output|inout]", .function = snort_attach_command_fn, }; @@ -379,27 +285,17 @@ static clib_error_t * snort_detach_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd) { - unformat_input_t _line_input, *line_input = &_line_input; vnet_main_t *vnm = vnet_get_main (); clib_error_t *err = NULL; u8 *name = NULL; - u8 **names = NULL; u32 sw_if_index = ~0; - u8 is_all_instances = 0; - int i = 0; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { - 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)) + if (unformat (input, "instance %s", &name)) + ; + else if (unformat (input, "interface %U", unformat_vnet_sw_interface, + vnm, &sw_if_index)) ; else { @@ -415,41 +311,18 @@ snort_detach_command_fn (vlib_main_t *vm, unformat_input_t *input, goto done; } - if (vec_len (names) == 0) - { - /* To maintain backwards compatibility */ - is_all_instances = 1; - } - - if (is_all_instances) - { - 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); - } - } + snort_attach_detach_instance (vm, vnm, (char *) name, sw_if_index, + 0 /* is_enable */, 1, 1); done: - vec_foreach_index (i, names) - { - vec_free (names[i]); - } - vec_free (names); - unformat_free (line_input); + vec_free (name); return err; } VLIB_CLI_COMMAND (snort_detach_command, static) = { .path = "snort detach", - .short_help = - "snort detach all-instances|(instance [instance [...]]) " - "interface ", + .short_help = "snort detach instance [instance [...]] " + "interface ", .function = snort_detach_command_fn, }; @@ -461,7 +334,47 @@ snort_show_instances_command_fn (vlib_main_t *vm, unformat_input_t *input, snort_instance_t *si; pool_foreach (si, sm->instances) - vlib_cli_output (vm, "%U", format_snort_instance, si); + { + vlib_cli_output (vm, "instance: %s (%u)", si->name, si->index); + vlib_cli_output (vm, " shared memory: size %u fd %d", si->shm_size, + si->shm_fd); + vlib_cli_output (vm, " drop on disconnect: %u", si->drop_on_disconnect); + + vec_foreach_pointer (qp, si->qpairs) + { + u64 n = 0; + u8 *s = 0; + vlib_cli_output (vm, " qpair: %u.%u", qp->qpair_id.thread_id, + qp->qpair_id.queue_id); + vlib_cli_output (vm, " descriptors: total %u free %u", + 1 << qp->log2_queue_size, qp->n_free_descs); + vlib_cli_output (vm, " client: %d", qp->client_index); + vlib_cli_output (vm, " cleanup needed: %d", qp->cleanup_needed); + vlib_cli_output (vm, + " enqueue: ring_offset %u event_fd %d head %lu", + (u8 *) qp->enq_ring - (u8 *) si->shm_base, + qp->enq_fd, qp->hdr->enq.head); + vlib_cli_output ( + vm, " dequeue: ring_offset %u event_fd %d head %lu tail %lu", + (u8 *) qp->deq_ring - (u8 *) si->shm_base, qp->deq_fd, + qp->hdr->deq.head, qp->deq_tail); + + for (u32 i = 0; i < MAX_DAQ_VERDICT; i++) + if (qp->n_packets_by_verdict[i]) + { + n += qp->n_packets_by_verdict[i]; + s = + format (s, "%s%U: %lu", s ? ", " : "", format_snort_verdict, + i, qp->n_packets_by_verdict[i]); + } + + if (s) + vlib_cli_output (vm, " packets processed: %lu (%v)", n, s); + else + vlib_cli_output (vm, " packets processed: 0"); + vec_free (s); + } + } return 0; } @@ -478,59 +391,36 @@ 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_interface_data_t *interface; - snort_instance_t *instance; - snort_attach_dir_t direction; - u32 instance_index; + snort_instance_t *si; u32 sw_if_index; - u8 is_input; - int i, j; + u8 *s = 0; - vlib_cli_output (vm, "interface\tinstances\tdirection"); - vec_foreach_index (sw_if_index, sm->interfaces) + vec_foreach_index (sw_if_index, sm->input_instance_by_interface) { - 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++) - { - 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)); - } - } + si = snort_get_instance_by_index ( + sm->input_instance_by_interface[sw_if_index]); + if (si) + s = format (s, "%U:\t%s\n", format_vnet_sw_if_index_name, vnm, + sw_if_index, si->name); } + if (vec_len (s)) + vlib_cli_output (vm, "input instances:\n%v", s); + + vec_reset_length (s); + + vec_foreach_index (sw_if_index, sm->output_instance_by_interface) + { + si = snort_get_instance_by_index ( + sm->output_instance_by_interface[sw_if_index]); + if (si) + s = format (s, "%U:\t%s\n", format_vnet_sw_if_index_name, vnm, + sw_if_index, si->name); + } + if (vec_len (s)) + vlib_cli_output (vm, "output instances:\n%v", s); + + vec_free (s); + return 0; } @@ -545,17 +435,27 @@ snort_show_clients_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd) { snort_main_t *sm = &snort_main; - u32 n_clients = pool_elts (sm->clients); snort_client_t *c; - snort_instance_t *si; - vlib_cli_output (vm, "number of clients: %d", n_clients); - if (n_clients) - vlib_cli_output (vm, "client snort instance"); + vlib_cli_output (vm, "number of clients: %d", pool_elts (sm->clients)); pool_foreach (c, sm->clients) { - si = vec_elt_at_index (sm->instances, c->instance_index); - vlib_cli_output (vm, "%6d %s", c - sm->clients, si->name); + snort_client_qpair_t *cqp; + vlib_cli_output (vm, "Client %u", c - sm->clients); + vlib_cli_output (vm, " DAQ version: %U", format_snort_daq_version, + c->daq_version); + vlib_cli_output (vm, " number of intstances: %u", c->n_instances); + vlib_cli_output (vm, " mode: %U", format_snort_mode, c->mode); + vlib_cli_output (vm, " inputs:"); + vec_foreach (cqp, c->qpairs) + { + snort_instance_t *si; + snort_qpair_t *qp; + si = snort_get_instance_by_index (cqp->instance_index); + qp = *vec_elt_at_index (si->qpairs, cqp->qpair_index); + vlib_cli_output (vm, " %s:%u.%u", si->name, + qp->qpair_id.thread_id, qp->qpair_id.queue_id); + } } return 0; } @@ -565,48 +465,3 @@ VLIB_CLI_COMMAND (snort_show_clients_command, static) = { .short_help = "show snort clients", .function = snort_show_clients_command_fn, }; - -static clib_error_t * -snort_mode_polling_command_fn (vlib_main_t *vm, unformat_input_t *input, - vlib_cli_command_t *cmd) -{ - snort_set_node_mode (vm, VLIB_NODE_STATE_POLLING); - return 0; -} - -static clib_error_t * -snort_mode_interrupt_command_fn (vlib_main_t *vm, unformat_input_t *input, - vlib_cli_command_t *cmd) -{ - snort_set_node_mode (vm, VLIB_NODE_STATE_INTERRUPT); - return 0; -} - -VLIB_CLI_COMMAND (snort_mode_polling_command, static) = { - .path = "snort mode polling", - .short_help = "snort mode polling|interrupt", - .function = snort_mode_polling_command_fn, -}; - -VLIB_CLI_COMMAND (snort_mode_interrupt_command, static) = { - .path = "snort mode interrupt", - .short_help = "snort mode polling|interrupt", - .function = snort_mode_interrupt_command_fn, -}; - -static clib_error_t * -snort_show_mode_command_fn (vlib_main_t *vm, unformat_input_t *input, - vlib_cli_command_t *cmd) -{ - snort_main_t *sm = &snort_main; - char *mode = - sm->input_mode == VLIB_NODE_STATE_POLLING ? "polling" : "interrupt"; - vlib_cli_output (vm, "input mode: %s", mode); - return 0; -} - -VLIB_CLI_COMMAND (snort_show_mode_command, static) = { - .path = "show snort mode", - .short_help = "show snort mode", - .function = snort_show_mode_command_fn, -}; diff --git a/src/plugins/snort/daq/config.c b/src/plugins/snort/daq/config.c new file mode 100644 index 00000000000..ba0fba10cf3 --- /dev/null +++ b/src/plugins/snort/daq/config.c @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2025 Cisco Systems, Inc. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include "daq_vpp.h" + +static DAQ_VariableDesc_t vpp_variable_descriptions[] = { + { .name = "debug", + .description = "Enable debugging output to stdout", + .flags = DAQ_VAR_DESC_FORBIDS_ARGUMENT }, + { .name = "socket", + .description = "Path to VPP unix domain socket", + .flags = DAQ_VAR_DESC_REQUIRES_ARGUMENT }, +}; + +int +daq_vpp_get_variable_descs (const DAQ_VariableDesc_t **var_desc_table) +{ + *var_desc_table = vpp_variable_descriptions; + + return sizeof (vpp_variable_descriptions) / sizeof (DAQ_VariableDesc_t); +} + +int +daq_vpp_parse_config (daq_vpp_ctx_t *ctx, DAQ_ModuleConfig_h modcfg) +{ + daq_vpp_main_t *vdm = &daq_vpp_main; + + const char *varKey, *varValue; + vdm->daq_base_api.config_first_variable (modcfg, &varKey, &varValue); + while (varKey) + { + if (!strcmp (varKey, "debug")) + vdm->debug = true; + else if (!strcmp (varKey, "socket")) + { + vdm->socket_name = varValue; + } + else + return daq_vpp_err (ctx, "unknown config key '%s'", varKey); + + vdm->daq_base_api.config_next_variable (modcfg, &varKey, &varValue); + } + return DAQ_SUCCESS; +} diff --git a/src/plugins/snort/daq/daq_vpp.h b/src/plugins/snort/daq/daq_vpp.h new file mode 100644 index 00000000000..720fedd7636 --- /dev/null +++ b/src/plugins/snort/daq/daq_vpp.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2025 Cisco Systems, Inc. + */ + +#ifndef __DAQ_VPP_H__ +#define __DAQ_VPP_H__ + +#include +#include +#include "daq_vpp_shared.h" + +#define __unused __attribute__ ((unused)) +#define __aligned(x) __attribute__ ((__aligned__ (x))) +#if __x86_64__ +#define VPP_DAQ_PAUSE() __builtin_ia32_pause () +#elif defined(__aarch64__) || defined(__arm__) +#define VPP_DAQ_PAUSE() __asm__ ("yield") +#else +#define VPP_DAQ_PAUSE() +#endif +#define ARRAY_LEN(x) (sizeof (x) / sizeof (x[0])) + +#define DEBUG(fmt, ...) \ + if (daq_vpp_main.debug) \ + printf ("%s: " fmt "\n", "daq_vpp", ##__VA_ARGS__); + +#define SET_ERROR(modinst, ...) \ + daq_vpp_main.daq_base_api.set_errbuf (modinst, __VA_ARGS__) + +typedef struct +{ + int fd; + uint64_t size; + void *base; +} daq_vpp_buffer_pool_t; + +typedef struct _vpp_qpair +{ + daq_vpp_qpair_header_t *hdr; + daq_vpp_desc_index_t *enq_ring; + daq_vpp_desc_index_t *deq_ring; + daq_vpp_head_tail_t tail; + uint16_t queue_size; + int enq_fd; + int deq_fd; + daq_vpp_input_index_t input_index; + uint16_t used_by_instance; + daq_vpp_qpair_id_t qpair_id; +} __aligned (64) daq_vpp_qpair_t; + +typedef struct daq_vpp_msg_pool_entry +{ + DAQ_Msg_t msg; + DAQ_PktHdr_t pkthdr; + daq_vpp_desc_index_t index; + union + { + struct daq_vpp_msg_pool_entry *freelist_next; + daq_vpp_qpair_t *qpair; + }; +} daq_vpp_msg_pool_entry_t; + +typedef struct daq_vpp_input_t +{ + /* shared memory */ + uint64_t shm_size; + void *shm_base; + int shm_fd; + + /* queue pairs */ + uint16_t num_qpairs; + + char name[DAQ_VPP_MAX_INST_NAME_LEN]; + daq_vpp_qpair_t qpairs[]; +} daq_vpp_input_t; + +typedef struct _vpp_context +{ + /* state */ + DAQ_ModuleInstance_h modinst; + uint16_t instance_id; + uint8_t interrupted; + int timeout; + + /* stats */ + DAQ_Stats_t stats; + + /* epoll and eventfd */ + int epoll_fd; + int wakeup_fd; + + uint16_t num_qpairs; + daq_vpp_qpair_index_t next_qpair; + daq_vpp_qpair_t **qpairs; + + /* msg pool */ + DAQ_MsgPoolInfo_t msg_pool_info; + daq_vpp_msg_pool_entry_t *msg_pool_freelist; + daq_vpp_msg_pool_entry_t msg_pool[]; +} daq_vpp_ctx_t; + +typedef struct +{ + uint32_t debug : 1; + uint32_t config_parsed : 1; + uint32_t connected : 1; + uint32_t buffer_pools_initialized : 1; + uint32_t hangup : 1; + + /* configured */ + uint32_t default_msg_pool_size; + DAQ_BaseAPI_t daq_base_api; + + /* buffer pools */ + uint8_t num_bpools; + daq_vpp_buffer_pool_t *bpools; + + /* socket */ + int socket_fd; + const char *socket_name; + + /* inputs */ + daq_vpp_input_t **inputs; + uint16_t n_inputs; + + /* instances */ + uint16_t n_instances; +} daq_vpp_main_t; + +extern daq_vpp_main_t daq_vpp_main; + +/* main.c */ +int daq_vpp_err (daq_vpp_ctx_t *ctx, char *fmt, ...); + +/* config.c */ +int daq_vpp_get_variable_descs (const DAQ_VariableDesc_t **var_desc_table); +int daq_vpp_parse_config (daq_vpp_ctx_t *ctx, DAQ_ModuleConfig_h modcfg); + +/* socket.c */ +int daq_vpp_connect (daq_vpp_ctx_t *ctx, uint16_t num_instances, + DAQ_Mode mode); +int daq_vpp_find_or_add_input (daq_vpp_ctx_t *ctx, char *name, + daq_vpp_input_t **inp); + +#endif /* __DAQ_VPP_H__ */ diff --git a/src/plugins/snort/daq/daq_vpp_shared.h b/src/plugins/snort/daq/daq_vpp_shared.h new file mode 100644 index 00000000000..783cc50c341 --- /dev/null +++ b/src/plugins/snort/daq/daq_vpp_shared.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2021 Cisco Systems, Inc. + */ + +#ifndef __DAQ_VPP_SHARED_H__ +#define __DAQ_VPP_SHARED_H__ + +#include + +#define DAQ_VPP_VERSION 2 +#define DAQ_VPP_DEFAULT_SOCKET_FILE "snort.sock" +#define DAQ_VPP_DEFAULT_SOCKET_PATH "/run/vpp/" DAQ_VPP_DEFAULT_SOCKET_FILE +#define DAQ_VPP_MAX_INST_NAME_LEN 32 +#define DAQ_VPP_COOKIE 0xa196c3e82a4bc68f + +typedef uint8_t daq_vpp_buffer_pool_index_t; +typedef uint16_t daq_vpp_input_index_t; +typedef uint16_t daq_vpp_qpair_index_t; +typedef uint16_t daq_vpp_desc_index_t; +typedef uint32_t daq_vpp_offset_t; +typedef uint64_t daq_vpp_head_tail_t; + +typedef enum +{ + DAQ_VPP_MSG_TYPE_UNKNOWN = 0, + DAQ_VPP_MSG_TYPE_CONNECT = 1, + DAQ_VPP_MSG_TYPE_GET_BUFFER_POOL = 2, + DAQ_VPP_MSG_TYPE_GET_INPUT = 3, + DAQ_VPP_MSG_TYPE_ATTACH_QPAIR = 4, +} __attribute__ ((packed)) daq_vpp_msg_type_t; + +typedef enum +{ + DAQ_VPP_OK = 0, + DAQ_VPP_ERR_SOCKET = 1, + DAQ_VPP_ERR_MALLOC_FAIL = 2, + DAQ_VPP_ERR_UNSUPPORTED_VERSION = 3, + DAQ_VPP_ERR_INVALID_MESSAGE = 4, + DAQ_VPP_ERR_INVALID_INDEX = 5, + DAQ_VPP_ERR_UNKNOWN_INPUT = 6, + DAQ_VPP_ERR_QPAIR_IN_USE = 7, + DAQ_VPP_ERR_QPAIR_NOT_READY = 8, + DAQ_VPP_ERR_INVALID_MODE = 9, +} __attribute__ ((packed)) daq_vpp_rv_t; + +static inline __attribute__ ((__always_inline__)) const char * +daq_vpp_rv_string (daq_vpp_rv_t err) +{ + const char *msg_errors[] = { + [DAQ_VPP_OK] = "no error", + [DAQ_VPP_ERR_SOCKET] = "socket error", + [DAQ_VPP_ERR_MALLOC_FAIL] = "memory allocation error", + [DAQ_VPP_ERR_UNSUPPORTED_VERSION] = "unsuported version", + [DAQ_VPP_ERR_INVALID_MESSAGE] = "invalid message", + [DAQ_VPP_ERR_INVALID_INDEX] = "invalid index", + [DAQ_VPP_ERR_UNKNOWN_INPUT] = "unknown input", + [DAQ_VPP_ERR_QPAIR_IN_USE] = "qpair alredy in use", + [DAQ_VPP_ERR_QPAIR_NOT_READY] = "qpair not ready", + [DAQ_VPP_ERR_INVALID_MODE] = "invalid mode", + }; + if (err >= (sizeof (msg_errors) / sizeof (msg_errors[0]))) + return 0; + return msg_errors[err]; +} + +typedef struct +{ + uint16_t thread_id; + uint16_t queue_id; +} daq_vpp_qpair_id_t; + +typedef struct +{ + uint32_t daq_version; + uint16_t num_snort_instances; + uint8_t mode; /* DAQ_Mode */ +} daq_vpp_msg_req_connect_t; + +typedef struct +{ + uint16_t num_bpools; +} daq_vpp_msg_reply_connect_t; + +typedef struct +{ + daq_vpp_buffer_pool_index_t buffer_pool_index; +} daq_vpp_msg_req_get_buffer_pool_t; + +typedef struct +{ + daq_vpp_buffer_pool_index_t buffer_pool_index; + uint64_t size; +} daq_vpp_msg_reply_get_buffer_pool_t; + +typedef struct +{ + char input_name[DAQ_VPP_MAX_INST_NAME_LEN]; +} daq_vpp_msg_req_get_input_t; + +typedef struct +{ + daq_vpp_input_index_t input_index; + uint64_t shm_size; + uint16_t num_qpairs; +} daq_vpp_msg_reply_get_input_t; + +typedef struct +{ + daq_vpp_input_index_t input_index; + daq_vpp_qpair_index_t qpair_index; +} daq_vpp_msg_req_attach_qpair_t; + +typedef struct +{ + daq_vpp_qpair_id_t qpair_id; + daq_vpp_input_index_t input_index; + daq_vpp_qpair_index_t qpair_index; + uint8_t log2_queue_size; + daq_vpp_offset_t qpair_header_offset; + daq_vpp_offset_t enq_ring_offset; + daq_vpp_offset_t deq_ring_offset; +} daq_vpp_msg_reply_attach_qpair_t; + +typedef struct +{ + daq_vpp_msg_type_t type; + union + { + daq_vpp_msg_req_connect_t connect; + daq_vpp_msg_req_get_buffer_pool_t get_buffer_pool; + daq_vpp_msg_req_get_input_t get_input; + daq_vpp_msg_req_attach_qpair_t attach_qpair; + }; +} daq_vpp_msg_req_t; + +typedef struct +{ + daq_vpp_msg_type_t type; + daq_vpp_rv_t err; + union + { + daq_vpp_msg_reply_connect_t connect; + daq_vpp_msg_reply_get_buffer_pool_t get_buffer_pool; + daq_vpp_msg_reply_get_input_t get_input; + daq_vpp_msg_reply_attach_qpair_t attach_qpair; + }; +} daq_vpp_msg_reply_t; + +typedef struct +{ + union + { + struct + { + uint32_t flags; /* DAQ_PKT_FLAG_* */ + uint32_t flow_id; + int32_t ingress_index; + uint16_t address_space_id; + }; + struct + { + uint8_t verdict; /* DAQ_Verdict */ + }; + uint32_t data[4]; + }; +} daq_vpp_pkt_metadata_t; + +_Static_assert (sizeof (daq_vpp_pkt_metadata_t) == 16, + "let it be 128-bits, so it fits into single load/store"); + +typedef struct +{ + daq_vpp_offset_t offset; + uint16_t length; + uint8_t buffer_pool; + daq_vpp_pkt_metadata_t metadata; +} daq_vpp_desc_t; + +typedef struct +{ + /* enqueue */ + struct + { + daq_vpp_head_tail_t head; + uint8_t interrupt_pending; + uint64_t cookie; + } __attribute__ ((__aligned__ (64))) enq; + + /* dequeue */ + struct + { + daq_vpp_head_tail_t head; + uint8_t interrupt_pending; + } __attribute__ ((__aligned__ (64))) deq; + + /* descriptors */ + daq_vpp_desc_t __attribute__ ((__aligned__ (64))) descs[]; +} daq_vpp_qpair_header_t; + +_Static_assert (sizeof (daq_vpp_qpair_header_t) == 128, + "size must be two achelines"); + +#endif /* __DAQ_VPP_SHARED_H__ */ diff --git a/src/plugins/snort/daq/main.c b/src/plugins/snort/daq/main.c new file mode 100644 index 00000000000..6d7c7b67113 --- /dev/null +++ b/src/plugins/snort/daq/main.c @@ -0,0 +1,763 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2025 Cisco Systems, Inc. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "daq_vpp.h" + +daq_vpp_main_t daq_vpp_main = { + .socket_name = DAQ_VPP_DEFAULT_SOCKET_PATH, + .socket_fd = -1, + .default_msg_pool_size = 256, +}; + +int +daq_vpp_err (daq_vpp_ctx_t *ctx, char *fmt, ...) +{ + char buffer[256]; + va_list va; + + va_start (va, fmt); + vsnprintf (buffer, sizeof (buffer), fmt, va); + va_end (va); + + daq_vpp_main.daq_base_api.set_errbuf (ctx->modinst, "%s", buffer); + return DAQ_ERROR; +} +static inline __attribute__ ((always_inline)) void +daq_vpp_prefetch_read (void *p) +{ + __builtin_prefetch (p, 0 /* read */, 3 /* closest to the cpu */); +} + +static int +daq_vpp_module_load (const DAQ_BaseAPI_t *base_api) +{ + daq_vpp_main_t *vdm = &daq_vpp_main; + if (base_api->api_version != DAQ_BASE_API_VERSION || + base_api->api_size != sizeof (DAQ_BaseAPI_t)) + return DAQ_ERROR; + + vdm->daq_base_api = *base_api; + + return DAQ_SUCCESS; +} + +static int +daq_vpp_module_unload (void) +{ + return DAQ_SUCCESS; +} + +static void +daq_vpp_destroy (void *handle) +{ + daq_vpp_main_t *vdm = &daq_vpp_main; + daq_vpp_ctx_t *ctx = (daq_vpp_ctx_t *) handle; + + free (ctx->qpairs); + if (ctx->epoll_fd != -1) + close (ctx->epoll_fd); + free (ctx); + + if (--vdm->n_instances == 0) + { + /* free inputs and their qpairs */ + if (vdm->inputs) + { + for (daq_vpp_input_index_t ii = 0; ii < vdm->n_inputs; ii++) + { + daq_vpp_input_t *in = vdm->inputs[ii]; + for (daq_vpp_qpair_index_t qi = 0; qi < in->num_qpairs; qi++) + { + daq_vpp_qpair_t *qp = in->qpairs + qi; + close (qp->enq_fd); + close (qp->deq_fd); + } + if (in->shm_base) + munmap (in->shm_base, in->shm_size); + } + free (vdm->inputs); + } + + /* free buffer pools */ + if (vdm->bpools) + { + for (daq_vpp_buffer_pool_index_t bpi = 0; bpi < vdm->num_bpools; + bpi++) + { + daq_vpp_buffer_pool_t *bp = vdm->bpools + bpi; + munmap (bp->base, bp->size); + close (bp->fd); + } + free (vdm->bpools); + } + } +} + +daq_vpp_qpair_t * +daq_vpp_find_qpair (daq_vpp_input_t *in, daq_vpp_qpair_id_t id) +{ + for (uint16_t i = 0; i < in->num_qpairs; i++) + { + if (in->qpairs[i].qpair_id.thread_id == id.thread_id && + in->qpairs[i].qpair_id.queue_id == id.queue_id) + return in->qpairs + i; + } + return 0; +} + +static int +daq_vpp_add_qpair_to_instance (daq_vpp_ctx_t *ctx, daq_vpp_qpair_t *qp) +{ + struct epoll_event ev = { .events = EPOLLIN, .data.ptr = qp }; + + if (qp->used_by_instance) + return daq_vpp_err (ctx, "%s: qpair %u.%u already used by instance %u", + __func__, qp->qpair_id.thread_id, + qp->qpair_id.queue_id, qp->used_by_instance); + + if (epoll_ctl (ctx->epoll_fd, EPOLL_CTL_ADD, qp->enq_fd, &ev) == -1) + return daq_vpp_err (ctx, "%s: failed to add dequeue fd to epoll instance", + __func__); + + qp->used_by_instance = ctx->instance_id; + ctx->qpairs = reallocarray (ctx->qpairs, ctx->num_qpairs + 1, + sizeof (daq_vpp_qpair_t *)); + ctx->qpairs[ctx->num_qpairs++] = qp; + + return DAQ_SUCCESS; +} + +static int +daq_vpp_parse_qpair_ids (daq_vpp_ctx_t *ctx, char *s, daq_vpp_qpair_id_t **qip, + uint16_t *n_qpair_ids_ptr) +{ + daq_vpp_qpair_id_t *v = 0; + uint16_t n_qpair_ids = 0; + + if (*s != ':') + return 0; + + const char *p = s + 1; + uint16_t a = 0, b = 0, parsing_b = 0; + + for (; *p != '\0'; ++p) + { + switch (*p) + { + case '.': + parsing_b = 1; + break; + case ',': + v = reallocarray (v, n_qpair_ids + 1, sizeof (daq_vpp_qpair_id_t)); + v[n_qpair_ids++] = + (daq_vpp_qpair_id_t){ .thread_id = a, .queue_id = b }; + + a = b = 0; + parsing_b = 0; + break; + case '0' ... '9': + if (!parsing_b) + a = a * 10 + (*p - '0'); + else + b = b * 10 + (*p - '0'); + break; + default: + if (v) + free (v); + return daq_vpp_err (ctx, "unable to parse '%s'", p); + } + } + + v = reallocarray (v, n_qpair_ids + 1, sizeof (daq_vpp_qpair_id_t)); + v[n_qpair_ids++] = (daq_vpp_qpair_id_t){ .thread_id = a, .queue_id = b }; + + *qip = v; + *n_qpair_ids_ptr = n_qpair_ids; + return DAQ_SUCCESS; +} + +static int +daq_vpp_instantiate (DAQ_ModuleConfig_h modcfg, DAQ_ModuleInstance_h modinst, + void **ctxt_ptr) +{ + daq_vpp_main_t *vdm = &daq_vpp_main; + int rv; + daq_vpp_input_t *in; + daq_vpp_ctx_t *ctx = 0, *oldctx; + unsigned n_instances, instance_id; + char name[DAQ_VPP_MAX_INST_NAME_LEN], *end_of_name; + unsigned input_name_len; + const char *input_name; + daq_vpp_qpair_id_t *qpair_ids = 0; + uint16_t n_qpair_ids = 0; + uint32_t msg_pool_size; + + n_instances = vdm->daq_base_api.config_get_total_instances (modcfg); + instance_id = vdm->daq_base_api.config_get_instance_id (modcfg); + input_name = vdm->daq_base_api.config_get_input (modcfg); + + vdm->n_instances++; + + end_of_name = strchrnul (input_name, ':'); + input_name_len = end_of_name - input_name; + ctx = calloc (1, sizeof (daq_vpp_ctx_t)); + ctx->modinst = modinst; + ctx->timeout = (int) vdm->daq_base_api.config_get_timeout (modcfg); + ctx->instance_id = instance_id; + + if (input_name_len >= DAQ_VPP_MAX_INST_NAME_LEN) + return daq_vpp_err (ctx, "input name '%s' too long", input_name); + + strncpy (name, input_name, input_name_len); + name[input_name_len] = 0; + + rv = daq_vpp_parse_qpair_ids (ctx, end_of_name, &qpair_ids, &n_qpair_ids); + if (rv != DAQ_SUCCESS) + goto err; + + if (!vdm->config_parsed) + { + rv = daq_vpp_parse_config (ctx, modcfg); + if (rv != DAQ_SUCCESS) + goto err; + vdm->config_parsed = 1; + } + + DEBUG ("creating instance %u out of %u with input %s", instance_id, + n_instances, name); + + msg_pool_size = vdm->daq_base_api.config_get_msg_pool_size (modcfg); + msg_pool_size = msg_pool_size ? msg_pool_size : vdm->default_msg_pool_size; + + oldctx = ctx; + ctx = realloc (ctx, sizeof (daq_vpp_ctx_t) + + msg_pool_size * sizeof (daq_vpp_msg_pool_entry_t)); + if (ctx == 0) + { + free (oldctx); + rv = daq_vpp_err (ctx, "failed to realloc"); + goto err; + } + + if (!vdm->connected) + { + rv = daq_vpp_connect (ctx, n_instances, + vdm->daq_base_api.config_get_mode (modcfg)); + + if (rv != DAQ_SUCCESS) + goto err; + vdm->connected = 1; + } + + rv = daq_vpp_find_or_add_input (ctx, name, &in); + if (rv != DAQ_SUCCESS) + goto err; + + ctx->modinst = modinst; + + ctx->epoll_fd = epoll_create (1); + if (ctx->epoll_fd < 0) + { + rv = daq_vpp_err (ctx, "failed to create epoll instance"); + goto err; + } + + ctx->wakeup_fd = eventfd (0, EFD_NONBLOCK); + if (ctx->wakeup_fd < 0) + { + rv = daq_vpp_err (ctx, "failed to create epoll instance"); + goto err; + } + + rv = epoll_ctl (ctx->epoll_fd, EPOLL_CTL_ADD, ctx->wakeup_fd, + &(struct epoll_event){ .events = EPOLLIN }); + if (rv == -1) + { + rv = daq_vpp_err (ctx, "failed to add dequeue fd to epoll instance"); + goto err; + } + + /* monitor sock_fd from first instance only, to be notified that remote + * dissapears */ + if (ctx->instance_id == 1) + { + rv = epoll_ctl ( + ctx->epoll_fd, EPOLL_CTL_ADD, vdm->socket_fd, + &(struct epoll_event){ .events = EPOLLIN, .data.ptr = (void *) 1 }); + if (rv == -1) + { + rv = daq_vpp_err (ctx, "failed to add dequeue fd to epoll instance"); + goto err; + } + } + + /* assign qpair to ths instance */ + if (n_qpair_ids) + { + for (uint32_t i = 0; i < n_qpair_ids; i++) + { + daq_vpp_qpair_t *qp; + qp = daq_vpp_find_qpair (in, qpair_ids[i]); + if (!qp) + { + rv = daq_vpp_err (ctx, "cannot find qpair %u.%u", + qpair_ids[i].thread_id, qpair_ids[i].queue_id, + ctx->instance_id); + goto err; + } + rv = daq_vpp_add_qpair_to_instance (ctx, qp); + if (rv != DAQ_SUCCESS) + goto err; + } + free (qpair_ids); + qpair_ids = 0; + } + else + /* add all qpairs to this instance */ + for (daq_vpp_qpair_index_t i = 0; i < in->num_qpairs; i++) + { + rv = daq_vpp_add_qpair_to_instance (ctx, in->qpairs + i); + if (rv != DAQ_SUCCESS) + goto err; + } + + /* init msg pool */ + daq_vpp_msg_pool_entry_t *freelist_next = 0; + for (uint32_t i = 0; i < msg_pool_size; i++) + { + daq_vpp_msg_pool_entry_t *pe = ctx->msg_pool + i; + pe->pkthdr = (DAQ_PktHdr_t){ + .egress_index = DAQ_PKTHDR_UNKNOWN, + .ingress_group = DAQ_PKTHDR_UNKNOWN, + .egress_group = DAQ_PKTHDR_UNKNOWN, + }; + pe->msg = (DAQ_Msg_t){ + .type = DAQ_MSG_TYPE_PACKET, + .hdr_len = sizeof (DAQ_PktHdr_t), + .hdr = &ctx->msg_pool[i].pkthdr, + .priv = pe, + }; + pe->freelist_next = freelist_next; + freelist_next = pe; + } + ctx->msg_pool_freelist = freelist_next; + ctx->msg_pool_info.available = msg_pool_size; + ctx->msg_pool_info.size = msg_pool_size; + ctx->msg_pool_info.mem_size = + msg_pool_size * sizeof (daq_vpp_msg_pool_entry_t); + + *ctxt_ptr = ctx; + + if (instance_id == n_instances) + { + /* run checks on last instance */ + for (daq_vpp_input_index_t ii = 0; ii < vdm->n_inputs; ii++) + { + daq_vpp_input_t *in = vdm->inputs[ii]; + for (daq_vpp_qpair_index_t qi = 0; qi < in->num_qpairs; qi++) + { + daq_vpp_qpair_t *qp = in->qpairs + qi; + if (qp->used_by_instance == 0) + fprintf ( + stderr, + "WARNING: input %s:%u.%u is not assigned to any instance\n", + in->name, qp->qpair_id.thread_id, qp->qpair_id.queue_id); + } + } + } + + return DAQ_SUCCESS; + +err: + if (qpair_ids) + free (qpair_ids); + daq_vpp_destroy (ctx); + return rv; +} + +static int +daq_vpp_start (void __unused *handle) +{ + return DAQ_SUCCESS; +} + +static int +daq_vpp_interrupt (void *handle) +{ + daq_vpp_ctx_t *ctx = (daq_vpp_ctx_t *) handle; + ssize_t __unused rv; + + ctx->interrupted = 1; + rv = write (ctx->wakeup_fd, &(uint64_t){ 1 }, sizeof (uint64_t)); + + return DAQ_SUCCESS; +} + +const DAQ_Msg_t * +daq_vpp_fill_msg (daq_vpp_ctx_t *ctx, daq_vpp_qpair_t *qp, uint32_t desc_index, + struct timeval tv) +{ + daq_vpp_main_t *vdm = &daq_vpp_main; + daq_vpp_msg_pool_entry_t *pe; + daq_vpp_desc_t *d; + uint8_t *data; + + d = qp->hdr->descs + desc_index; + data = vdm->bpools[d->buffer_pool].base + d->offset; + + daq_vpp_prefetch_read (data); + pe = ctx->msg_pool_freelist; + ctx->msg_pool_freelist = pe->freelist_next; + daq_vpp_prefetch_read (pe->freelist_next); + pe->qpair = qp; + pe->index = desc_index; + pe->pkthdr.ts = tv; + pe->pkthdr.pktlen = d->length; + pe->pkthdr.address_space_id = d->metadata.address_space_id; + pe->pkthdr.flow_id = d->metadata.flow_id; + pe->pkthdr.flags = d->metadata.flags; + pe->pkthdr.ingress_index = d->metadata.ingress_index; + pe->msg.data = data; + pe->msg.data_len = d->length; + + return &pe->msg; +} + +uint32_t +daq_vpp_msg_receive_one (daq_vpp_ctx_t *ctx, daq_vpp_qpair_t *qp, + const DAQ_Msg_t *msgs[], unsigned max_recv) +{ + uint32_t n_recv, n_left, desc_index, next_desc_index; + daq_vpp_head_tail_t head, tail, mask = qp->queue_size - 1; + struct timeval tv; + + if (max_recv == 0) + return 0; + + tail = qp->tail; + head = __atomic_load_n (&qp->hdr->enq.head, __ATOMIC_ACQUIRE); + n_recv = head - tail; + + if (n_recv == 0) + return 0; + + if (n_recv > max_recv) + n_recv = max_recv; + + next_desc_index = qp->enq_ring[tail++ & mask]; + + gettimeofday (&tv, NULL); + for (n_left = n_recv; n_left > 1; n_left--, msgs++) + { + desc_index = next_desc_index; + + msgs[0] = daq_vpp_fill_msg (ctx, qp, desc_index, tv); + next_desc_index = qp->enq_ring[tail++ & mask]; + } + + /* last packet */ + msgs[0] = daq_vpp_fill_msg (ctx, qp, next_desc_index, tv); + + qp->tail = tail; + + return n_recv; +} + +static inline void +daq_vpp_flush_eventfd (int fd) +{ + uint64_t ctr; + ssize_t __unused size = read (fd, &ctr, sizeof (ctr)); +} + +static unsigned +daq_vpp_msg_receive (void *handle, unsigned max_recv, const DAQ_Msg_t *msgs[], + DAQ_RecvStatus *rstatp) +{ + daq_vpp_main_t *dvm = &daq_vpp_main; + daq_vpp_ctx_t *ctx = (daq_vpp_ctx_t *) handle; + struct epoll_event epoll_events[32]; + uint32_t n_recv = 0; + int32_t n_events; + DAQ_RecvStatus rstat = DAQ_RSTAT_OK; + + if (ctx->interrupted) + { + rstat = DAQ_RSTAT_INTERRUPTED; + ctx->interrupted = 0; + goto done; + } + + if (dvm->hangup) + { + daq_vpp_err (ctx, "hangup received"); + rstat = DAQ_RSTAT_ERROR; + goto done; + } + + if (ctx->msg_pool_info.available < max_recv) + max_recv = ctx->msg_pool_info.available; + + if (ctx->num_qpairs == 1) + { + daq_vpp_qpair_t *qp = ctx->qpairs[0]; + uint32_t n; + + n = daq_vpp_msg_receive_one (ctx, qp, msgs, max_recv); + if (n) + { + msgs += n; + n_recv += n; + } + } + else + { + /* first, we visit all qpairs. If we find any work there then we can give + * it back immediatelly. To avoid bias towards qpair 0 we remeber what + * next qpair */ + uint16_t num_qpairs = ctx->num_qpairs; + uint16_t next_qp = ctx->next_qpair; + for (uint32_t n, left = num_qpairs; left; left--) + { + daq_vpp_qpair_t *qp = ctx->qpairs[next_qp]; + + n = daq_vpp_msg_receive_one (ctx, qp, msgs, max_recv - n_recv); + if (n) + { + msgs += n; + n_recv += n; + } + + /* next */ + next_qp = next_qp + 1 < num_qpairs ? next_qp + 1 : 0; + } + ctx->next_qpair = + ctx->next_qpair + 1 < num_qpairs ? ctx->next_qpair + 1 : 0; + } + + if (n_recv) + goto done; + + n_events = epoll_wait (ctx->epoll_fd, epoll_events, ARRAY_LEN (epoll_events), + ctx->timeout); + + if (n_events < 1) + { + if (n_events == 0 || errno == EINTR) + rstat = DAQ_RSTAT_TIMEOUT; + else + rstat = DAQ_RSTAT_ERROR; + goto done; + } + + for (struct epoll_event *e = epoll_events; e - epoll_events < n_events; e++) + { + if (e->events & EPOLLERR) + { + daq_vpp_err (ctx, "socket error"); + rstat = DAQ_RSTAT_ERROR; + } + else if (e->events & EPOLLHUP) + { + daq_vpp_err (ctx, "hangup received"); + dvm->hangup = 1; + rstat = DAQ_RSTAT_EOF; + } + else if (e->events & EPOLLIN) + { + if (e->data.ptr == 0) + { + rstat = DAQ_RSTAT_INTERRUPTED; + ctx->interrupted = 0; + daq_vpp_flush_eventfd (ctx->wakeup_fd); + } + else if (e->data.ptr == (void *) 1) + { + rstat = DAQ_RSTAT_ERROR; + daq_vpp_flush_eventfd (ctx->wakeup_fd); + } + } + } + + if (rstat != DAQ_RSTAT_OK) + goto done; + + for (struct epoll_event *e = epoll_events; e - epoll_events < n_events; e++) + if (e->data.ptr > (void *) 1) + { + daq_vpp_qpair_t *qp = e->data.ptr; + uint32_t n; + + __atomic_store_n (&qp->hdr->enq.interrupt_pending, 0, + __ATOMIC_RELAXED); + + n = daq_vpp_msg_receive_one (ctx, qp, msgs, max_recv - n_recv); + if (n) + { + msgs += n; + n_recv += n; + } + daq_vpp_flush_eventfd (qp->enq_fd); + } + +done: + if (n_recv) + ctx->msg_pool_info.available -= n_recv; + *rstatp = rstat; + return n_recv; +} + +static int +daq_vpp_msg_finalize (void *handle, const DAQ_Msg_t *msg, DAQ_Verdict verdict) +{ + daq_vpp_ctx_t *ctx = (daq_vpp_ctx_t *) handle; + daq_vpp_msg_pool_entry_t *pe = msg->priv; + daq_vpp_qpair_t *qp = pe->qpair; + daq_vpp_head_tail_t head, mask; + daq_vpp_qpair_header_t *h = qp->hdr; + daq_vpp_desc_t *d; + + if (verdict >= MAX_DAQ_VERDICT) + verdict = DAQ_VERDICT_PASS; + ctx->stats.verdicts[verdict]++; + + mask = qp->queue_size - 1; + head = __atomic_load_n (&h->deq.head, __ATOMIC_RELAXED); + d = h->descs + pe->index; + + d->metadata.verdict = verdict; + qp->deq_ring[head & mask] = pe->index; + head = head + 1; + __atomic_store_n (&h->deq.head, head, __ATOMIC_RELEASE); + + /* put back to freelist */ + pe->freelist_next = ctx->msg_pool_freelist; + ctx->msg_pool_freelist = pe; + ctx->msg_pool_info.available++; + + if (!__atomic_exchange_n (&qp->hdr->deq.interrupt_pending, 1, + __ATOMIC_RELAXED)) + { + ssize_t __unused rv; + rv = write (qp->deq_fd, &(uint64_t){ 1 }, sizeof (uint64_t)); + } + + return DAQ_SUCCESS; +} + +static int +daq_vpp_get_msg_pool_info (void *handle, DAQ_MsgPoolInfo_t *info) +{ + daq_vpp_ctx_t *ctx = (daq_vpp_ctx_t *) handle; + *info = ctx->msg_pool_info; + return DAQ_SUCCESS; +} + +static int +daq_vpp_get_stats (void __unused *handle, DAQ_Stats_t *stats) +{ + daq_vpp_ctx_t *ctx = (daq_vpp_ctx_t *) handle; + *stats = ctx->stats; + return DAQ_SUCCESS; +} + +static void +daq_vpp_reset_stats (void *handle) +{ + daq_vpp_ctx_t *ctx = (daq_vpp_ctx_t *) handle; + ctx->stats = (DAQ_Stats_t){}; +} + +static uint32_t +daq_vpp_get_capabilities (void __unused *handle) +{ + return DAQ_CAPA_BLOCK | DAQ_CAPA_UNPRIV_START | DAQ_CAPA_INTERRUPT | + DAQ_CAPA_DEVICE_INDEX; +} + +static int +daq_vpp_get_datalink_type (void __unused *handle) +{ + return DLT_IPV4; +} + +static int +daq_vpp_ioctl (void *handle, DAQ_IoctlCmd cmd, void *arg, size_t arglen) +{ + daq_vpp_main_t *vdm = &daq_vpp_main; + daq_vpp_ctx_t *ctx = (daq_vpp_ctx_t *) handle; + DIOCTL_QueryDeviceIndex *qdi = (DIOCTL_QueryDeviceIndex *) arg; + + if (cmd == DIOCTL_GET_DEVICE_INDEX) + { + char name[DAQ_VPP_MAX_INST_NAME_LEN], *colon; + + if (arglen != sizeof (DIOCTL_QueryDeviceIndex)) + return DAQ_ERROR_NOTSUP; + + if (qdi->device == 0) + { + daq_vpp_err (ctx, "no device name in IOCTL_GET_DEVICE_INDEX"); + return DAQ_ERROR_INVAL; + } + snprintf (name, sizeof (name), "%s", qdi->device); + colon = strchr (name, ':'); + if (colon) + colon[0] = 0; + + for (daq_vpp_input_index_t ii = 0; ii < vdm->n_inputs; ii++) + if (strcmp (name, vdm->inputs[ii]->name) == 0) + { + qdi->index = ii + 1; + return DAQ_SUCCESS; + } + + return DAQ_ERROR_NODEV; + } + + return DAQ_ERROR_NOTSUP; +} + +DAQ_SO_PUBLIC +const DAQ_ModuleAPI_t DAQ_MODULE_DATA = { + .name = "vpp", + .api_version = DAQ_MODULE_API_VERSION, + .api_size = sizeof (DAQ_ModuleAPI_t), + .module_version = DAQ_VPP_VERSION, + .type = + DAQ_TYPE_INTF_CAPABLE | DAQ_TYPE_MULTI_INSTANCE | DAQ_TYPE_INLINE_CAPABLE, + .load = daq_vpp_module_load, + .unload = daq_vpp_module_unload, + .get_variable_descs = daq_vpp_get_variable_descs, + .instantiate = daq_vpp_instantiate, + .destroy = daq_vpp_destroy, + .start = daq_vpp_start, + .interrupt = daq_vpp_interrupt, + .ioctl = daq_vpp_ioctl, + .get_stats = daq_vpp_get_stats, + .reset_stats = daq_vpp_reset_stats, + .get_snaplen = NULL, + .get_datalink_type = daq_vpp_get_datalink_type, + .msg_receive = daq_vpp_msg_receive, + .msg_finalize = daq_vpp_msg_finalize, + .get_msg_pool_info = daq_vpp_get_msg_pool_info, + .get_capabilities = daq_vpp_get_capabilities, +}; diff --git a/src/plugins/snort/daq/socket.c b/src/plugins/snort/daq/socket.c new file mode 100644 index 00000000000..b2c7126ba74 --- /dev/null +++ b/src/plugins/snort/daq/socket.c @@ -0,0 +1,302 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2025 Cisco Systems, Inc. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include "daq.h" +#include "daq_vpp.h" + +static daq_vpp_rv_t +daq_vpp_request (daq_vpp_msg_req_t *req, daq_vpp_msg_reply_t *reply, int n_fds, + int fds[]) +{ + daq_vpp_main_t *vdm = &daq_vpp_main; + const ssize_t req_msg_sz = sizeof (daq_vpp_msg_req_t); + const int ctl_sz = + CMSG_SPACE (sizeof (int) * n_fds) + CMSG_SPACE (sizeof (struct ucred)); + char ctl[ctl_sz]; + struct msghdr mh = {}; + struct iovec iov[1]; + struct cmsghdr *cmsg; + ssize_t rv; + + if (send (vdm->socket_fd, req, req_msg_sz, 0) != req_msg_sz) + return DAQ_VPP_ERR_SOCKET; + + iov[0].iov_base = (void *) reply; + iov[0].iov_len = sizeof (daq_vpp_msg_reply_t); + mh.msg_iov = iov; + mh.msg_iovlen = 1; + mh.msg_control = ctl; + mh.msg_controllen = ctl_sz; + + memset (ctl, 0, ctl_sz); + + rv = recvmsg (vdm->socket_fd, &mh, 0); + if (rv != sizeof (daq_vpp_msg_reply_t)) + return DAQ_VPP_ERR_SOCKET; + + cmsg = CMSG_FIRSTHDR (&mh); + while (cmsg) + { + if (cmsg->cmsg_level == SOL_SOCKET) + { + if (cmsg->cmsg_type == SCM_CREDENTIALS) + /* Do nothing */; + else if (cmsg->cmsg_type == SCM_RIGHTS) + memcpy (fds, CMSG_DATA (cmsg), n_fds * sizeof (int)); + } + cmsg = CMSG_NXTHDR (&mh, cmsg); + } + + return reply->err; +} + +void +daq_vpp_socket_disconnect () +{ + daq_vpp_main_t *vdm = &daq_vpp_main; + + if (vdm->bpools) + free (vdm->bpools); + + if (vdm->socket_fd > -1) + { + close (vdm->socket_fd); + vdm->socket_fd = -1; + } + vdm->connected = 1; +} + +daq_vpp_rv_t +daq_vpp_socket_connect () +{ + daq_vpp_main_t *vdm = &daq_vpp_main; + + struct sockaddr_un sun = { .sun_family = AF_UNIX }; + int fd; + + fd = socket (AF_UNIX, SOCK_SEQPACKET, 0); + + if (fd < 0) + return DAQ_VPP_ERR_SOCKET; + + strncpy (sun.sun_path, vdm->socket_name, sizeof (sun.sun_path) - 1); + + if (connect (fd, (struct sockaddr *) &sun, sizeof (struct sockaddr_un)) != 0) + { + close (fd); + return DAQ_VPP_ERR_SOCKET; + } + + vdm->socket_fd = fd; + return DAQ_VPP_OK; +} + +int +daq_vpp_connect (daq_vpp_ctx_t *ctx, uint16_t num_instances, DAQ_Mode mode) +{ + daq_vpp_main_t *vdm = &daq_vpp_main; + daq_vpp_msg_reply_t reply = {}; + daq_vpp_rv_t vrv; + int rv, fd; + + vrv = daq_vpp_socket_connect (); + if (vrv != DAQ_VPP_OK) + return daq_vpp_err (ctx, "socket connect error"); + + vrv = daq_vpp_request ( + &(daq_vpp_msg_req_t){ + .type = DAQ_VPP_MSG_TYPE_CONNECT, + .connect = { + .num_snort_instances = num_instances, + .daq_version = daq_version_number(), + .mode = mode, + }, + }, + &reply, 0, 0); + + if (vrv != DAQ_VPP_OK) + { + rv = daq_vpp_err (ctx, "CONNECT request failed, %s", + daq_vpp_rv_string (vrv)); + goto err; + } + + vdm->num_bpools = reply.connect.num_bpools; + vdm->bpools = calloc (vdm->num_bpools, sizeof (daq_vpp_buffer_pool_t)); + + if (vdm->bpools == NULL) + { + rv = daq_vpp_err (ctx, "buffer pool memory allocation error"); + goto err; + } + + for (daq_vpp_buffer_pool_index_t i = 0; i < vdm->num_bpools; i++) + { + daq_vpp_buffer_pool_t *bp = vdm->bpools + i; + + vrv = daq_vpp_request ( + &(daq_vpp_msg_req_t){ + .type = DAQ_VPP_MSG_TYPE_GET_BUFFER_POOL, + .get_buffer_pool = { .buffer_pool_index = i }, + }, + &reply, 1, &fd); + + if (vrv != DAQ_VPP_OK) + { + rv = daq_vpp_err (ctx, "GET_BUFFER_POOL request failed, %s", + daq_vpp_rv_string (vrv)); + goto err; + } + + bp->base = mmap (0, reply.get_buffer_pool.size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + + if (bp->base == MAP_FAILED) + { + rv = daq_vpp_err (ctx, "buffer pool mmap failed"); + goto err; + } + + bp->fd = fd; + bp->size = reply.get_buffer_pool.size; + } + + return DAQ_SUCCESS; + +err: + if (rv != DAQ_SUCCESS) + daq_vpp_socket_disconnect (); + return rv; +} + +int +daq_vpp_find_or_add_input (daq_vpp_ctx_t *ctx, char *name, + daq_vpp_input_t **inp) +{ + daq_vpp_main_t *vdm = &daq_vpp_main; + daq_vpp_input_t *in = 0; + daq_vpp_msg_req_t req = {}; + daq_vpp_msg_reply_t reply; + daq_vpp_input_index_t ii; + daq_vpp_rv_t vrv; + uint64_t shm_size; + uint16_t n_qpairs; + int rv, fd; + uint8_t *base = 0; + + /* search for existing input */ + for (daq_vpp_input_index_t i = 0; i < vdm->n_inputs; i++) + { + daq_vpp_input_t *in = vdm->inputs[i]; + if (strcmp (in->name, name) == 0) + { + *inp = in; + return DAQ_VPP_OK; + } + } + + req.type = DAQ_VPP_MSG_TYPE_GET_INPUT; + strcpy (req.get_input.input_name, name); + vrv = daq_vpp_request (&req, &reply, 1, &fd); + + if (vrv != DAQ_VPP_OK) + { + rv = daq_vpp_err (ctx, "GET_INPUT request failed, %s", + daq_vpp_rv_string (vrv)); + goto err; + } + + ii = reply.get_input.input_index; + n_qpairs = reply.get_input.num_qpairs; + shm_size = reply.get_input.shm_size; + + base = mmap (0, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + if (base == MAP_FAILED) + { + rv = daq_vpp_err (ctx, "input shared memory mmap failed"); + goto err; + } + + in = + calloc (1, sizeof (daq_vpp_input_t) + n_qpairs * sizeof (daq_vpp_qpair_t)); + if (!in) + { + rv = daq_vpp_err (ctx, "input memory alloc failed"); + goto err; + } + + strcpy (in->name, name); + in->shm_size = shm_size; + in->num_qpairs = n_qpairs; + in->shm_fd = fd; + in->shm_base = base; + + for (daq_vpp_qpair_index_t qi = 0; qi < in->num_qpairs; qi++) + { + daq_vpp_qpair_t *qp = in->qpairs + qi; + daq_vpp_msg_reply_attach_qpair_t *g = &reply.attach_qpair; + daq_vpp_msg_req_t req = { + .type = DAQ_VPP_MSG_TYPE_ATTACH_QPAIR, + .attach_qpair = { + .input_index = ii, + .qpair_index = qi, + }, + }; + int fds[2]; + + vrv = daq_vpp_request (&req, &reply, 2, fds); + + if (vrv != DAQ_VPP_OK) + { + rv = daq_vpp_err (ctx, "ATTACH_QPAIR request failed, %s", + daq_vpp_rv_string (vrv)); + goto err; + } + + qp->qpair_id = g->qpair_id; + qp->queue_size = 1 << g->log2_queue_size; + qp->hdr = (daq_vpp_qpair_header_t *) (base + g->qpair_header_offset); + qp->enq_ring = (daq_vpp_desc_index_t *) (base + g->enq_ring_offset); + qp->deq_ring = (daq_vpp_desc_index_t *) (base + g->deq_ring_offset); + qp->enq_fd = fds[0]; + qp->deq_fd = fds[1]; + qp->input_index = ii; + + if (qp->hdr->enq.cookie != DAQ_VPP_COOKIE) + { + rv = daq_vpp_err (ctx, "invalid cookie for qpair %u.%u", + qp->qpair_id.thread_id, qp->qpair_id.queue_id); + goto err; + } + + DEBUG ("qpair %u.%u added, size %u", qp->qpair_id.thread_id, + qp->qpair_id.queue_id, qp->queue_size); + } + + vdm->inputs = + reallocarray (vdm->inputs, vdm->n_inputs + 1, sizeof (daq_vpp_input_t *)); + vdm->inputs[vdm->n_inputs++] = in; + + *inp = in; + + return DAQ_VPP_OK; + +err: + if (base) + munmap (base, shm_size); + if (in) + free (in); + return rv; +} diff --git a/src/plugins/snort/daq_vpp.c b/src/plugins/snort/daq_vpp.c deleted file mode 100644 index 6fc0bf5506a..00000000000 --- a/src/plugins/snort/daq_vpp.c +++ /dev/null @@ -1,723 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * Copyright(c) 2021 Cisco Systems, Inc. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "daq_vpp.h" - -#define DAQ_VPP_VERSION 1 - -#if __x86_64__ -#define VPP_DAQ_PAUSE() __builtin_ia32_pause () -#elif defined(__aarch64__) || defined(__arm__) -#define VPP_DAQ_PAUSE() __asm__("yield") -#else -#define VPP_DAQ_PAUSE() -#endif - -static DAQ_VariableDesc_t vpp_variable_descriptions[] = { - { "debug", "Enable debugging output to stdout", - DAQ_VAR_DESC_FORBIDS_ARGUMENT }, -}; - -static DAQ_BaseAPI_t daq_base_api; - -#define SET_ERROR(modinst, ...) daq_base_api.set_errbuf (modinst, __VA_ARGS__) - -typedef struct _vpp_msg_pool -{ - DAQ_MsgPoolInfo_t info; -} VPPMsgPool; - -typedef struct _vpp_desc_data -{ - uint32_t index; - uint32_t qpair_index; - DAQ_Msg_t msg; - DAQ_PktHdr_t pkthdr; -} VPPDescData; - -typedef struct _vpp_bpool -{ - int fd; - uint32_t size; - void *base; -} VPPBufferPool; - -typedef struct _vpp_qpair -{ - CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); - uint32_t queue_size; - daq_vpp_desc_t *descs; - uint32_t *enq_ring; - uint32_t *deq_ring; - volatile uint32_t *enq_head; - volatile uint32_t *deq_head; - uint32_t next_desc; - int enq_fd; - int deq_fd; - VPPDescData *desc_data; - volatile int lock; -} VPPQueuePair; - -typedef enum -{ - DAQ_VPP_INPUT_MODE_INTERRUPT = 0, - DAQ_VPP_INPUT_MODE_POLLING, -} daq_vpp_input_mode_t; - -typedef struct _vpp_context -{ - /* config */ - bool debug; - - /* state */ - uint32_t intf_count; - DAQ_ModuleInstance_h modinst; - VPPMsgPool pool; - - /* socket */ - int sock_fd; - - /* shared memory */ - uint32_t shm_size; - void *shm_base; - int shm_fd; - - /* queue pairs */ - uint8_t num_qpairs; - VPPQueuePair *qpairs; - uint32_t next_qpair; - - /* epoll */ - struct epoll_event *epoll_events; - int epoll_fd; - - /* buffer pools */ - uint8_t num_bpools; - VPPBufferPool *bpools; - - daq_vpp_input_mode_t input_mode; - const char *socket_name; - volatile bool interrupted; -} VPP_Context_t; - -static VPP_Context_t *global_vpp_ctx = 0; - -static int -vpp_daq_qpair_lock (VPPQueuePair *p) -{ - int free = 0; - while (!__atomic_compare_exchange_n (&p->lock, &free, 1, 0, __ATOMIC_ACQUIRE, - __ATOMIC_RELAXED)) - { - while (__atomic_load_n (&p->lock, __ATOMIC_RELAXED)) - VPP_DAQ_PAUSE (); - free = 0; - } - return 0; -} - -static void -vpp_daq_qpair_unlock (VPPQueuePair *p) -{ - __atomic_store_n (&p->lock, 0, __ATOMIC_RELEASE); -} - -static int -vpp_daq_module_load (const DAQ_BaseAPI_t *base_api) -{ - if (base_api->api_version != DAQ_BASE_API_VERSION || - base_api->api_size != sizeof (DAQ_BaseAPI_t)) - return DAQ_ERROR; - - daq_base_api = *base_api; - - return DAQ_SUCCESS; -} - -static int -vpp_daq_module_unload (void) -{ - memset (&daq_base_api, 0, sizeof (daq_base_api)); - return DAQ_SUCCESS; -} - -static int -vpp_daq_get_variable_descs (const DAQ_VariableDesc_t **var_desc_table) -{ - *var_desc_table = vpp_variable_descriptions; - - return sizeof (vpp_variable_descriptions) / sizeof (DAQ_VariableDesc_t); -} - -static int -vpp_daq_recvmsg (int fd, daq_vpp_msg_t *msg, int n_fds, int *fds) -{ - const int ctl_sz = - CMSG_SPACE (sizeof (int) * n_fds) + CMSG_SPACE (sizeof (struct ucred)); - char ctl[ctl_sz]; - struct msghdr mh = {}; - struct iovec iov[1]; - struct cmsghdr *cmsg; - - iov[0].iov_base = (void *) msg; - iov[0].iov_len = sizeof (daq_vpp_msg_t); - mh.msg_iov = iov; - mh.msg_iovlen = 1; - mh.msg_control = ctl; - mh.msg_controllen = ctl_sz; - - memset (ctl, 0, ctl_sz); - - int rv; - if ((rv = recvmsg (fd, &mh, 0)) != sizeof (daq_vpp_msg_t)) - return DAQ_ERROR_NODEV; - - cmsg = CMSG_FIRSTHDR (&mh); - while (cmsg) - { - if (cmsg->cmsg_level == SOL_SOCKET) - { - if (cmsg->cmsg_type == SCM_CREDENTIALS) - { - /* Do nothing */; - } - else if (cmsg->cmsg_type == SCM_RIGHTS) - { - memcpy (fds, CMSG_DATA (cmsg), n_fds * sizeof (int)); - } - } - cmsg = CMSG_NXTHDR (&mh, cmsg); - } - - return DAQ_SUCCESS; -} - -static void -vpp_daq_destroy (void *handle) -{ - VPP_Context_t *vc = (VPP_Context_t *) handle; - - if (vc->shm_base != MAP_FAILED) - munmap (vc->shm_base, vc->shm_size); - - if (vc->shm_fd != -1) - close (vc->shm_fd); - - if (vc->bpools) - { - for (int i = 0; i < vc->num_bpools; i++) - { - VPPBufferPool *bp = vc->bpools + i; - if (bp->fd != -1) - close (bp->fd); - if (bp->base && bp->base != MAP_FAILED) - munmap (bp->base, bp->size); - } - free (vc->bpools); - } - - if (vc->qpairs) - { - for (int i = 0; i < vc->num_qpairs; i++) - { - VPPQueuePair *qp = vc->qpairs + i; - if (qp->enq_fd != -1) - close (qp->enq_fd); - if (qp->deq_fd != -1) - close (qp->deq_fd); - if (qp->desc_data) - free (qp->desc_data); - } - free (vc->qpairs); - } - - free (vc->epoll_events); - close (vc->sock_fd); - if (vc->epoll_fd != -1) - close (vc->epoll_fd); - free (vc); -} - -#define ERR(rv, ...) \ - { \ - SET_ERROR (modinst, __VA_ARGS__); \ - rval = rv; \ - goto err; \ - } - -static int -vpp_daq_instantiate (const DAQ_ModuleConfig_h modcfg, - DAQ_ModuleInstance_h modinst, void **ctxt_ptr) -{ - VPP_Context_t *vc = 0; - int rval = DAQ_ERROR; - daq_vpp_msg_t msg; - struct sockaddr_un sun = { .sun_family = AF_UNIX }; - int i, fd = -1, shm_fd = -1; - const char *input; - uint8_t *base; - - if (global_vpp_ctx) - { - *ctxt_ptr = global_vpp_ctx; - return DAQ_SUCCESS; - } - - vc = calloc (1, sizeof (VPP_Context_t)); - - if (!vc) - ERR (DAQ_ERROR_NOMEM, - "%s: Couldn't allocate memory for the new VPP context!", __func__); - - const char *varKey, *varValue; - daq_base_api.config_first_variable (modcfg, &varKey, &varValue); - while (varKey) - { - if (!strcmp (varKey, "debug")) - vc->debug = true; - else if (!strcmp (varKey, "input_mode")) - { - if (!strcmp (varValue, "interrupt")) - vc->input_mode = DAQ_VPP_INPUT_MODE_INTERRUPT; - else if (!strcmp (varValue, "polling")) - vc->input_mode = DAQ_VPP_INPUT_MODE_POLLING; - } - else if (!strcmp (varKey, "socket_name")) - { - vc->socket_name = varValue; - } - daq_base_api.config_next_variable (modcfg, &varKey, &varValue); - } - - input = daq_base_api.config_get_input (modcfg); - - if (!vc->socket_name) - /* try to use default socket path */ - vc->socket_name = DAQ_VPP_DEFAULT_SOCKET_PATH; - - if ((fd = socket (AF_UNIX, SOCK_SEQPACKET, 0)) < 0) - ERR (DAQ_ERROR_NODEV, "%s: Couldn't create socket!", __func__); - - strncpy (sun.sun_path, vc->socket_name, sizeof (sun.sun_path) - 1); - - if (connect (fd, (struct sockaddr *) &sun, sizeof (struct sockaddr_un)) != 0) - ERR (DAQ_ERROR_NODEV, "%s: Couldn't connect to socket! '%s'", __func__, - vc->socket_name); - - /* craft and send connect message */ - msg.type = DAQ_VPP_MSG_TYPE_HELLO; - snprintf ((char *) &msg.hello.inst_name, DAQ_VPP_INST_NAME_LEN - 1, "%s", - input); - - if (send (fd, &msg, sizeof (msg), 0) != sizeof (msg)) - ERR (DAQ_ERROR_NODEV, "%s: Couldn't send connect message!", __func__); - - /* receive config message */ - rval = vpp_daq_recvmsg (fd, &msg, 1, &shm_fd); - - if (rval != DAQ_SUCCESS || msg.type != DAQ_VPP_MSG_TYPE_CONFIG || - shm_fd == -1) - ERR (DAQ_ERROR_NODEV, "%s: Couldn't receive config message!", __func__); - - vc->modinst = modinst; - vc->sock_fd = fd; - vc->epoll_fd = -1; - vc->intf_count = 1; - vc->num_bpools = msg.config.num_bpools; - vc->num_qpairs = msg.config.num_qpairs; - vc->shm_size = msg.config.shm_size; - vc->shm_fd = shm_fd; - - vc->bpools = calloc (vc->num_bpools, sizeof (VPPBufferPool)); - vc->qpairs = calloc (vc->num_qpairs, sizeof (VPPQueuePair)); - vc->epoll_events = calloc (vc->num_qpairs, sizeof (struct epoll_event)); - - if (vc->bpools == 0 || vc->qpairs == 0) - ERR (DAQ_ERROR_NOMEM, - "%s: Couldn't allocate memory for the new VPP context!", __func__); - - for (i = 0; i < vc->num_bpools; i++) - vc->bpools[i].fd = -1; - - base = - mmap (0, vc->shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, vc->shm_fd, 0); - - if (base == MAP_FAILED) - ERR (DAQ_ERROR_NOMEM, - "%s: Couldn't map shared memory for the new VPP context!", __func__); - - vc->shm_base = base; - - if (vc->debug) - { - printf ("[%s]\n", input); - printf (" Shared memory size: %u\n", vc->shm_size); - printf (" Number of buffer pools: %u\n", vc->num_bpools); - printf (" Number of queue pairs: %u\n", vc->num_qpairs); - } - - /* receive buffer pools */ - for (int i = 0; i < vc->num_bpools; i++) - { - VPPBufferPool *bp = vc->bpools + i; - rval = vpp_daq_recvmsg (fd, &msg, 1, &bp->fd); - if (rval != DAQ_SUCCESS || msg.type != DAQ_VPP_MSG_TYPE_BPOOL || - bp->fd == -1) - ERR (DAQ_ERROR_NODEV, - "%s: Failed to receive buffer pool message for the new " - "VPP context!", - __func__); - bp->size = msg.bpool.size; - bp->base = mmap (0, bp->size, PROT_READ, MAP_SHARED, bp->fd, 0); - - if (bp->base == MAP_FAILED) - ERR (DAQ_ERROR_NOMEM, - "%s: Couldn't map shared memory for the new VPP context!", - __func__); - if (vc->debug) - printf (" Buffer pool %u size: %u\n", i, bp->size); - } - - if ((vc->epoll_fd = epoll_create (1)) == -1) - ERR (DAQ_ERROR_NODEV, - "%s: Couldn't create epoll fd for the new VPP context!", __func__); - - /* receive queue pairs */ - for (int i = 0; i < vc->num_qpairs; i++) - { - struct epoll_event ev = { .events = EPOLLIN }; - int fds[2] = { -1, -1 }; - uint32_t qsz; - VPPQueuePair *qp = vc->qpairs + i; - rval = vpp_daq_recvmsg (fd, &msg, 2, fds); - if (rval != DAQ_SUCCESS || msg.type != DAQ_VPP_MSG_TYPE_QPAIR || - fds[0] == -1 || fds[1] == -1) - ERR (DAQ_ERROR_NODEV, - "%s: Failed to receive queu pair message for the new " - "VPP context!", - __func__); - qp->queue_size = 1 << msg.qpair.log2_queue_size; - qp->descs = (daq_vpp_desc_t *) (base + msg.qpair.desc_table_offset); - qp->enq_ring = (uint32_t *) (base + msg.qpair.enq_ring_offset); - qp->deq_ring = (uint32_t *) (base + msg.qpair.deq_ring_offset); - qp->enq_head = (uint32_t *) (base + msg.qpair.enq_head_offset); - qp->deq_head = (uint32_t *) (base + msg.qpair.deq_head_offset); - qp->enq_fd = fds[0]; - qp->deq_fd = fds[1]; - ev.data.u32 = i; - - if (epoll_ctl (vc->epoll_fd, EPOLL_CTL_ADD, qp->enq_fd, &ev) == -1) - ERR (DAQ_ERROR_NODEV, - "%s: Failed to dequeue fd to epoll instance for the new " - "VPP context!", - __func__); - - qsz = qp->queue_size; - - qp->desc_data = calloc (qsz, sizeof (VPPDescData)); - if (!qp->desc_data) - ERR (DAQ_ERROR_NOMEM, - "%s: Couldn't allocate memory for the new VPP context!", - __func__); - - for (int j = 0; j < qsz; j++) - { - VPPDescData *dd = qp->desc_data + j; - DAQ_PktHdr_t *pkthdr = &dd->pkthdr; - DAQ_Msg_t *msg = &dd->msg; - - dd->index = j; - dd->qpair_index = i; - - pkthdr->ingress_group = DAQ_PKTHDR_UNKNOWN; - pkthdr->egress_group = DAQ_PKTHDR_UNKNOWN; - - msg->type = DAQ_MSG_TYPE_PACKET; - msg->hdr_len = sizeof (DAQ_PktHdr_t); - msg->hdr = pkthdr; - msg->owner = vc->modinst; - msg->priv = dd; - } - - if (vc->debug) - { - printf (" Queue pair %u:\n", i); - printf (" Size: %u\n", qp->queue_size); - printf (" Enqueue fd: %u\n", qp->enq_fd); - printf (" Dequeue fd: %u\n", qp->deq_fd); - } - } - - *ctxt_ptr = global_vpp_ctx = vc; - return DAQ_SUCCESS; -err: - if (vc) - vpp_daq_destroy (vc); - else if (fd != -1) - close (fd); - return rval; -} - -static int -vpp_daq_start (void *handle) -{ - return DAQ_SUCCESS; -} - -static int -vpp_daq_interrupt (void *handle) -{ - VPP_Context_t *vc = (VPP_Context_t *) handle; - - vc->interrupted = true; - - return DAQ_SUCCESS; -} - -static int -vpp_daq_get_stats (void *handle, DAQ_Stats_t *stats) -{ - memset (stats, 0, sizeof (DAQ_Stats_t)); - return DAQ_SUCCESS; -} - -static void -vpp_daq_reset_stats (void *handle) -{ -} - -static uint32_t -vpp_daq_get_capabilities (void *handle) -{ - uint32_t capabilities = DAQ_CAPA_BLOCK | DAQ_CAPA_UNPRIV_START; - return capabilities; -} - -static int -vpp_daq_get_datalink_type (void *handle) -{ - return DLT_IPV4; -} - -static inline uint32_t -vpp_daq_msg_receive_one (VPP_Context_t *vc, VPPQueuePair *qp, - const DAQ_Msg_t *msgs[], unsigned max_recv) -{ - uint32_t n_recv, n_left; - uint32_t head, next, mask = qp->queue_size - 1; - struct timeval tv; - - if (max_recv == 0) - return 0; - - vpp_daq_qpair_lock (qp); - next = qp->next_desc; - head = __atomic_load_n (qp->enq_head, __ATOMIC_ACQUIRE); - n_recv = n_left = head - next; - - if (n_left > max_recv) - { - n_left = n_recv = max_recv; - } - - gettimeofday (&tv, NULL); - while (n_left--) - { - uint32_t desc_index = qp->enq_ring[next & mask]; - daq_vpp_desc_t *d = qp->descs + desc_index; - VPPDescData *dd = qp->desc_data + desc_index; - dd->pkthdr.ts.tv_sec = tv.tv_sec; - dd->pkthdr.ts.tv_usec = tv.tv_usec; - dd->pkthdr.pktlen = d->length; - dd->pkthdr.address_space_id = d->address_space_id; - dd->msg.data = vc->bpools[d->buffer_pool].base + d->offset; - dd->msg.data_len = d->length; - next = next + 1; - - msgs[0] = &dd->msg; - msgs++; - } - - qp->next_desc = next; - vpp_daq_qpair_unlock (qp); - - return n_recv; -} - -static unsigned -vpp_daq_msg_receive (void *handle, const unsigned max_recv, - const DAQ_Msg_t *msgs[], DAQ_RecvStatus *rstat) -{ - VPP_Context_t *vc = (VPP_Context_t *) handle; - uint32_t n_qpairs_left = vc->num_qpairs; - uint32_t n, n_recv = 0; - int32_t n_events; - - /* If the receive has been interrupted, break out of loop and return. */ - if (vc->interrupted) - { - vc->interrupted = false; - *rstat = DAQ_RSTAT_INTERRUPTED; - return 0; - } - - /* first, we visit all qpairs. If we find any work there then we can give - * it back immediatelly. To avoid bias towards qpair 0 we remeber what - * next qpair */ - while (n_qpairs_left) - { - VPPQueuePair *qp = vc->qpairs + vc->next_qpair; - - if ((n = vpp_daq_msg_receive_one (vc, qp, msgs, max_recv - n_recv))) - { - msgs += n; - n_recv += n; - } - - /* next */ - vc->next_qpair++; - if (vc->next_qpair == vc->num_qpairs) - vc->next_qpair = 0; - n_qpairs_left--; - } - - if (vc->input_mode == DAQ_VPP_INPUT_MODE_POLLING) - { - *rstat = DAQ_RSTAT_OK; - return n_recv; - } - - if (n_recv) - { - *rstat = DAQ_RSTAT_OK; - return n_recv; - } - - n_events = epoll_wait (vc->epoll_fd, vc->epoll_events, vc->num_qpairs, 1000); - - if (n_events == 0) - { - *rstat = DAQ_RSTAT_TIMEOUT; - return 0; - } - if (n_events < 0) - { - *rstat = errno == EINTR ? DAQ_RSTAT_TIMEOUT : DAQ_RSTAT_ERROR; - return 0; - } - - for (int i = 0; i < n_events; i++) - { - uint64_t ctr; - VPPQueuePair *qp = vc->qpairs + vc->epoll_events[i].data.u32; - - if ((n = vpp_daq_msg_receive_one (vc, qp, msgs, max_recv - n_recv))) - { - msgs += n; - n_recv += n; - } - ssize_t __clib_unused size = read (qp->enq_fd, &ctr, sizeof (ctr)); - } - - *rstat = DAQ_RSTAT_OK; - return n_recv; -} - -static int -vpp_daq_msg_finalize (void *handle, const DAQ_Msg_t *msg, DAQ_Verdict verdict) -{ - VPP_Context_t *vc = (VPP_Context_t *) handle; - VPPDescData *dd = msg->priv; - VPPQueuePair *qp = vc->qpairs + dd->qpair_index; - daq_vpp_desc_t *d; - uint32_t mask, head; - uint64_t counter_increment = 1; - int rv, retv = DAQ_SUCCESS; - - vpp_daq_qpair_lock (qp); - mask = qp->queue_size - 1; - head = *qp->deq_head; - d = qp->descs + dd->index; - if (verdict == DAQ_VERDICT_PASS) - d->action = DAQ_VPP_ACTION_FORWARD; - else - d->action = DAQ_VPP_ACTION_DROP; - - qp->deq_ring[head & mask] = dd->index; - head = head + 1; - __atomic_store_n (qp->deq_head, head, __ATOMIC_RELEASE); - - if (vc->input_mode == DAQ_VPP_INPUT_MODE_INTERRUPT) - { - rv = write (qp->deq_fd, &counter_increment, sizeof (counter_increment)); - - if (rv != sizeof (counter_increment)) - retv = DAQ_ERROR; - } - - vpp_daq_qpair_unlock (qp); - return retv; -} - -static int -vpp_daq_get_msg_pool_info (void *handle, DAQ_MsgPoolInfo_t *info) -{ - VPP_Context_t *vc = (VPP_Context_t *) handle; - - vc->pool.info.available = 128; - vc->pool.info.size = 256; - - *info = vc->pool.info; - - return DAQ_SUCCESS; -} - -DAQ_SO_PUBLIC -const DAQ_ModuleAPI_t DAQ_MODULE_DATA = { - /* .api_version = */ DAQ_MODULE_API_VERSION, - /* .api_size = */ sizeof (DAQ_ModuleAPI_t), - /* .module_version = */ DAQ_VPP_VERSION, - /* .name = */ "vpp", - /* .type = */ DAQ_TYPE_INTF_CAPABLE | DAQ_TYPE_INLINE_CAPABLE | - DAQ_TYPE_MULTI_INSTANCE, - /* .load = */ vpp_daq_module_load, - /* .unload = */ vpp_daq_module_unload, - /* .get_variable_descs = */ vpp_daq_get_variable_descs, - /* .instantiate = */ vpp_daq_instantiate, - /* .destroy = */ vpp_daq_destroy, - /* .set_filter = */ NULL, - /* .start = */ vpp_daq_start, - /* .inject = */ NULL, - /* .inject_relative = */ NULL, - /* .interrupt = */ vpp_daq_interrupt, - /* .stop = */ NULL, - /* .ioctl = */ NULL, - /* .get_stats = */ vpp_daq_get_stats, - /* .reset_stats = */ vpp_daq_reset_stats, - /* .get_snaplen = */ NULL, - /* .get_capabilities = */ vpp_daq_get_capabilities, - /* .get_datalink_type = */ vpp_daq_get_datalink_type, - /* .config_load = */ NULL, - /* .config_swap = */ NULL, - /* .config_free = */ NULL, - /* .msg_receive = */ vpp_daq_msg_receive, - /* .msg_finalize = */ vpp_daq_msg_finalize, - /* .get_msg_pool_info = */ vpp_daq_get_msg_pool_info, -}; diff --git a/src/plugins/snort/daq_vpp.h b/src/plugins/snort/daq_vpp.h deleted file mode 100644 index ebec55435f3..00000000000 --- a/src/plugins/snort/daq_vpp.h +++ /dev/null @@ -1,77 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * Copyright(c) 2021 Cisco Systems, Inc. - */ - -#ifndef __DAQ_VPP_H__ -#define __DAQ_VPP_H__ - -#include - -#define DAQ_VPP_DEFAULT_SOCKET_FILE "snort.sock" -#define DAQ_VPP_DEFAULT_SOCKET_PATH "/run/vpp/" DAQ_VPP_DEFAULT_SOCKET_FILE -#define DAQ_VPP_INST_NAME_LEN 32 - -typedef enum memif_msg_type -{ - DAQ_VPP_MSG_TYPE_NONE = 0, - DAQ_VPP_MSG_TYPE_HELLO = 1, - DAQ_VPP_MSG_TYPE_CONFIG = 2, - DAQ_VPP_MSG_TYPE_BPOOL = 3, - DAQ_VPP_MSG_TYPE_QPAIR = 4, -} daq_vpp_msg_type_t; - -typedef struct -{ - char inst_name[DAQ_VPP_INST_NAME_LEN]; -} daq_vpp_msg_hello_t; - -typedef struct -{ - uint32_t shm_size; - uint16_t num_bpools; - uint16_t num_qpairs; -} daq_vpp_msg_config_t; - -typedef struct -{ - uint32_t size; -} daq_vpp_msg_bpool_t; - -typedef struct -{ - uint8_t log2_queue_size; - uint32_t desc_table_offset; - uint32_t enq_head_offset; - uint32_t deq_head_offset; - uint32_t enq_ring_offset; - uint32_t deq_ring_offset; -} daq_vpp_msg_qpair_t; - -typedef struct -{ - daq_vpp_msg_type_t type : 8; - union - { - daq_vpp_msg_hello_t hello; - daq_vpp_msg_config_t config; - daq_vpp_msg_bpool_t bpool; - daq_vpp_msg_qpair_t qpair; - }; -} daq_vpp_msg_t; - -typedef enum -{ - DAQ_VPP_ACTION_DROP, - DAQ_VPP_ACTION_FORWARD, -} daq_vpp_action_t; - -typedef struct -{ - uint64_t offset; - uint16_t length; - uint16_t address_space_id; - uint8_t buffer_pool; - daq_vpp_action_t action : 8; -} daq_vpp_desc_t; - -#endif /* __DAQ_VPP_H__ */ diff --git a/src/plugins/snort/dequeue.c b/src/plugins/snort/dequeue.c index bc301f6888b..85111278423 100644 --- a/src/plugins/snort/dequeue.c +++ b/src/plugins/snort/dequeue.c @@ -6,361 +6,274 @@ #include #include -typedef struct -{ - u32 next_index; - u32 sw_if_index; -} snort_deq_trace_t; - -static u8 * -format_snort_deq_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 *); - snort_deq_trace_t *t = va_arg (*args, snort_deq_trace_t *); - - s = format (s, "snort-deq: sw_if_index %d, next index %d\n", t->sw_if_index, - t->next_index); - - return s; -} - -#define foreach_snort_deq_error \ - _ (BAD_DESC, "bad descriptor") \ - _ (BAD_DESC_INDEX, "bad descriptor index") - -typedef enum -{ -#define _(sym, str) SNORT_DEQ_ERROR_##sym, - foreach_snort_deq_error -#undef _ - SNORT_DEQ_N_ERROR, -} snort_deq_error_t; - -static char *snort_deq_error_strings[] = { -#define _(sym, string) string, - foreach_snort_deq_error -#undef _ -}; - -static_always_inline uword -snort_deq_instance (vlib_main_t *vm, u32 instance_index, snort_qpair_t *qp, - u32 *buffer_indices, u16 *nexts, u32 max_recv) +static u32 +snort_deq_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + snort_instance_t *si, snort_qpair_t *qp) { - snort_main_t *sm = &snort_main; - snort_per_thread_data_t *ptd = - vec_elt_at_index (sm->per_thread_data, vm->thread_index); - u32 mask = pow2_mask (qp->log2_queue_size); - u32 head, next, n_recv = 0, n_left; + u32 buffer_indices[VLIB_FRAME_SIZE], *from; + u16 next_indices[VLIB_FRAME_SIZE], *nexts; + u32 n_left, n_deq, error = 0, n_total = 0; + daq_vpp_head_tail_t head, tail, old_tail; + daq_vpp_desc_index_t next_free, mask = pow2_mask (qp->log2_queue_size); + u32 drop_bitmap = si->drop_bitmap; + u16 n_verdicsts[MAX_DAQ_VERDICT] = {}; + + if (PREDICT_FALSE (qp->cleanup_needed)) + { + u32 qsz = 1 << qp->log2_queue_size; + if (qp->n_free_descs != qsz) + { + u32 n_free = 0; + n_total = 0; + for (u32 i = 0; i < qsz; i++) + { + snort_qpair_entry_t *qpe = qp->entries + i; + + if (qpe->buffer_index == ~0) + continue; + + buffer_indices[n_free++] = qpe->buffer_index; + + if (n_free == VLIB_FRAME_SIZE) + { + vlib_buffer_free (vm, buffer_indices, VLIB_FRAME_SIZE); + n_total += VLIB_FRAME_SIZE; + n_free = 0; + } + } + if (n_free) + vlib_buffer_free (vm, buffer_indices, n_free); + n_total += n_free; + + if (n_total) + vlib_node_increment_counter ( + vm, node->node_index, SNORT_DEQ_ERROR_NO_CLIENT_FREE, n_total); + } - head = __atomic_load_n (qp->deq_head, __ATOMIC_ACQUIRE); - next = qp->next_desc; + snort_qpair_init (qp); + __atomic_store_n (&qp->cleanup_needed, 0, __ATOMIC_RELEASE); + return 0; + } - n_left = head - next; + tail = qp->deq_tail; + head = __atomic_load_n (&qp->hdr->deq.head, __ATOMIC_ACQUIRE); + next_free = qp->next_free_desc; - if (n_left == 0) + if (head == tail) return 0; - if (n_left > max_recv) - { - n_left = max_recv; - clib_interrupt_set (ptd->interrupts, instance_index); - vlib_node_set_interrupt_pending (vm, snort_deq_node.index); - } +more: + old_tail = tail; + + n_left = clib_min (VLIB_FRAME_SIZE, head - tail); - while (n_left) + for (from = buffer_indices, nexts = next_indices; n_left; + from++, nexts++, n_left--) { - u32 desc_index, bi; + u32 desc_index = qp->deq_ring[tail & mask]; + snort_qpair_entry_t *qpe = qp->entries + desc_index; daq_vpp_desc_t *d; + vlib_buffer_t *b; + u32 bi; + u8 verdict; /* check if descriptor index taken from dequqe ring is valid */ - if ((desc_index = qp->deq_ring[next & mask]) & ~mask) + if (desc_index & ~mask) { - vlib_node_increment_counter (vm, snort_deq_node.index, + error = 1; + vlib_node_increment_counter (vm, node->node_index, SNORT_DEQ_ERROR_BAD_DESC_INDEX, 1); - goto next; + break; } - /* check if descriptor index taken from dequeue ring points to enqueued - * buffer */ - if ((bi = qp->buffer_indices[desc_index]) == ~0) + /* check if descriptor index taken from dequeue ring points to + * enqueued buffer */ + bi = qpe->buffer_index; + if (bi == ~0) { - vlib_node_increment_counter (vm, snort_deq_node.index, + error = 1; + vlib_node_increment_counter (vm, node->node_index, SNORT_DEQ_ERROR_BAD_DESC, 1); - goto next; + break; } - /* put descriptor back to freelist */ - vec_add1 (qp->freelist, desc_index); - d = qp->descriptors + desc_index; - buffer_indices++[0] = bi; - if (d->action == DAQ_VPP_ACTION_FORWARD) - nexts[0] = qp->next_indices[desc_index]; - else + d = qp->hdr->descs + desc_index; + verdict = d->metadata.verdict; + from[0] = bi; + if ((1U << verdict) & drop_bitmap) nexts[0] = SNORT_ENQ_NEXT_DROP; - qp->buffer_indices[desc_index] = ~0; - nexts++; - n_recv++; + else + nexts[0] = qpe->next_index; + n_verdicsts[verdict]++; + qpe->buffer_index = ~0; + b = vlib_get_buffer (vm, bi); + *snort_get_buffer_metadata (b) = d->metadata; - /* next */ - next: - next = next + 1; - n_left--; - } + /* put descriptor back to freelist */ + qpe->freelist_next = next_free; + next_free = desc_index; - qp->next_desc = next; + /* next */ + tail++; - return n_recv; -} + if (b->flags & VLIB_BUFFER_IS_TRACED) + { + snort_deq_trace_t *t = vlib_add_trace (vm, node, b, sizeof (*t)); + t->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; + t->next_index = nexts[0]; + t->buffer_index = bi; + t->verdict = verdict; + } + } -static_always_inline u32 -snort_process_all_buffer_indices (snort_qpair_t *qp, u32 *b, u16 *nexts, - u32 max_recv, u8 drop_on_disconnect) -{ - u32 *bi, n_processed = 0; - u32 desc_index = 0; + n_deq = tail - old_tail; - vec_foreach (bi, qp->buffer_indices) + if (n_deq) { - if (n_processed >= max_recv) - break; - - if (bi[0] == ~0) - continue; - - desc_index = bi - qp->buffer_indices; - - b[0] = bi[0]; - if (drop_on_disconnect) - nexts[0] = SNORT_ENQ_NEXT_DROP; - else - nexts[0] = qp->next_indices[desc_index]; - qp->buffer_indices[desc_index] = ~0; + vlib_buffer_enqueue_to_next (vm, node, buffer_indices, next_indices, + n_deq); + old_tail = tail; + n_total += n_deq; - nexts += 1; - b += 1; - n_processed += 1; + if (!error && tail != head) + goto more; } - return n_processed; -} -static_always_inline uword -snort_deq_instance_all_interrupt (vlib_main_t *vm, u32 instance_index, - snort_qpair_t *qp, u32 *buffer_indices, - u16 *nexts, u32 max_recv, - u8 drop_on_disconnect) -{ - snort_main_t *sm = &snort_main; - snort_per_thread_data_t *ptd = - vec_elt_at_index (sm->per_thread_data, vm->thread_index); - u32 n_processed; + qp->deq_tail = tail; + qp->n_free_descs += n_total; + qp->next_free_desc = next_free; - n_processed = snort_process_all_buffer_indices ( - qp, buffer_indices, nexts, max_recv, drop_on_disconnect); + if (n_total) + for (u32 i = 0; i < MAX_DAQ_VERDICT; i++) + qp->n_packets_by_verdict[i] += n_verdicsts[i]; - if (n_processed == max_recv) - { - clib_interrupt_set (ptd->interrupts, instance_index); - vlib_node_set_interrupt_pending (vm, snort_deq_node.index); - } - else - { - *qp->enq_head = *qp->deq_head = qp->next_desc = 0; - snort_freelist_init (qp->freelist); - __atomic_store_n (&qp->ready, 1, __ATOMIC_RELEASE); - } + if (head != tail) + vlib_node_set_interrupt_pending (vm, node->node_index); - return n_processed; + return n_total; } -static u32 -snort_deq_node_interrupt (vlib_main_t *vm, vlib_node_runtime_t *node, - vlib_frame_t *frame) +VLIB_NODE_FN (snort_deq_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { snort_main_t *sm = &snort_main; - snort_per_thread_data_t *ptd = - vec_elt_at_index (sm->per_thread_data, vm->thread_index); - u32 buffer_indices[VLIB_FRAME_SIZE], *bi = buffer_indices; - u16 next_indices[VLIB_FRAME_SIZE], *nexts = next_indices; - u32 n_left = VLIB_FRAME_SIZE, n; - snort_qpair_t *qp; - snort_instance_t *si; - int inst = -1; - - while ((inst = clib_interrupt_get_next_and_clear (ptd->interrupts, inst)) != - -1) - { - si = vec_elt_at_index (sm->instances, inst); - qp = vec_elt_at_index (si->qpairs, vm->thread_index); - u32 ready = __atomic_load_n (&qp->ready, __ATOMIC_ACQUIRE); - if (!ready) - n = snort_deq_instance_all_interrupt (vm, inst, qp, bi, nexts, n_left, - si->drop_on_disconnect); - else - n = snort_deq_instance (vm, inst, qp, bi, nexts, n_left); + snort_deq_runtime_data_t *rt = + vlib_node_get_runtime_data (vm, node->node_index); + snort_instance_t *si = pool_elt_at_index (sm->instances, rt->instance_index); + u32 qpairs_per_thread = si->qpairs_per_thread; + snort_qpair_t **qp = snort_get_qpairs (si, vm->thread_index); + uword rv = 0; - n_left -= n; - bi += n; - nexts += n; - - if (n_left == 0) - goto enq; - } + while (qpairs_per_thread--) + rv += snort_deq_node_inline (vm, node, si, qp++[0]); - if (n_left == VLIB_FRAME_SIZE) - return 0; - -enq: - n = VLIB_FRAME_SIZE - n_left; - vlib_buffer_enqueue_to_next (vm, node, buffer_indices, next_indices, n); - return n; + return rv; } static_always_inline uword -snort_deq_instance_poll (vlib_main_t *vm, snort_qpair_t *qp, - u32 *buffer_indices, u16 *nexts, u32 max_recv) +snort_arc_next_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame) { - u32 mask = pow2_mask (qp->log2_queue_size); - u32 head, next, n_recv = 0, n_left; - - head = __atomic_load_n (qp->deq_head, __ATOMIC_ACQUIRE); - next = qp->next_desc; + u32 *buffer_indices = vlib_frame_vector_args (frame), *bi = buffer_indices; + u16 next_indices[VLIB_FRAME_SIZE], *ni = next_indices; + u32 n_pkts = frame->n_vectors, n_left = n_pkts; - n_left = head - next; - - if (n_left == 0) - return 0; - - if (n_left > max_recv) - n_left = max_recv; - - while (n_left) + for (; n_left >= 8; n_left -= 4, bi += 4, ni += 4) { - u32 desc_index, bi; - daq_vpp_desc_t *d; - - /* check if descriptor index taken from dequqe ring is valid */ - if ((desc_index = qp->deq_ring[next & mask]) & ~mask) + vlib_buffer_t *b0, *b1, *b2, *b3; + + clib_prefetch_load (vlib_get_buffer (vm, bi[4])); + b0 = vlib_get_buffer (vm, bi[0]); + clib_prefetch_load (vlib_get_buffer (vm, bi[5])); + b1 = vlib_get_buffer (vm, bi[1]); + clib_prefetch_load (vlib_get_buffer (vm, bi[6])); + b2 = vlib_get_buffer (vm, bi[2]); + clib_prefetch_load (vlib_get_buffer (vm, bi[7])); + b3 = vlib_get_buffer (vm, bi[3]); + + vnet_feature_next_u16 (ni + 0, b0); + vnet_feature_next_u16 (ni + 1, b1); + vnet_feature_next_u16 (ni + 2, b2); + vnet_feature_next_u16 (ni + 3, b3); + + if (b0->flags & VLIB_BUFFER_IS_TRACED) { - vlib_node_increment_counter (vm, snort_deq_node.index, - SNORT_DEQ_ERROR_BAD_DESC_INDEX, 1); - goto next; + snort_arc_next_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->buffer_index = bi[0]; + t->next_index = ni[0]; } - /* check if descriptor index taken from dequeue ring points to enqueued - * buffer */ - if ((bi = qp->buffer_indices[desc_index]) == ~0) + if (b1->flags & VLIB_BUFFER_IS_TRACED) { - vlib_node_increment_counter (vm, snort_deq_node.index, - SNORT_DEQ_ERROR_BAD_DESC, 1); - goto next; + snort_arc_next_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + t->buffer_index = bi[1]; + t->next_index = ni[1]; } - /* put descriptor back to freelist */ - vec_add1 (qp->freelist, desc_index); - d = qp->descriptors + desc_index; - buffer_indices++[0] = bi; - if (d->action == DAQ_VPP_ACTION_FORWARD) - nexts[0] = qp->next_indices[desc_index]; - else - nexts[0] = SNORT_ENQ_NEXT_DROP; - qp->buffer_indices[desc_index] = ~0; - nexts++; - n_recv++; - - /* next */ - next: - next = next + 1; - n_left--; - } - - qp->next_desc = next; - - return n_recv; -} + if (b2->flags & VLIB_BUFFER_IS_TRACED) + { + snort_arc_next_trace_t *t = + vlib_add_trace (vm, node, b2, sizeof (*t)); + t->buffer_index = bi[2]; + t->next_index = ni[2]; + } -static_always_inline uword -snort_deq_instance_all_poll (vlib_main_t *vm, snort_qpair_t *qp, - u32 *buffer_indices, u16 *nexts, u32 max_recv, - u8 drop_on_disconnect) -{ - u32 n_processed = snort_process_all_buffer_indices ( - qp, buffer_indices, nexts, max_recv, drop_on_disconnect); - if (n_processed < max_recv) - { - *qp->enq_head = *qp->deq_head = qp->next_desc = 0; - snort_freelist_init (qp->freelist); - __atomic_store_n (&qp->ready, 1, __ATOMIC_RELEASE); + if (b3->flags & VLIB_BUFFER_IS_TRACED) + { + snort_arc_next_trace_t *t = + vlib_add_trace (vm, node, b3, sizeof (*t)); + t->buffer_index = bi[3]; + t->next_index = ni[3]; + } } - return n_processed; -} - -static u32 -snort_deq_node_polling (vlib_main_t *vm, vlib_node_runtime_t *node, - vlib_frame_t *frame) -{ - snort_main_t *sm = &snort_main; - u32 buffer_indices[VLIB_FRAME_SIZE], *bi = buffer_indices; - u16 next_indices[VLIB_FRAME_SIZE], *nexts = next_indices; - u32 n_left = VLIB_FRAME_SIZE, n, n_total = 0; - snort_qpair_t *qp; - snort_instance_t *si; - - pool_foreach (si, sm->instances) + for (; n_left > 0; n_left -= 1, bi += 1, ni += 1) { - qp = vec_elt_at_index (si->qpairs, vm->thread_index); - u32 ready = __atomic_load_n (&qp->ready, __ATOMIC_ACQUIRE); - if (!ready) - n = snort_deq_instance_all_poll (vm, qp, bi, nexts, n_left, - si->drop_on_disconnect); - else - n = snort_deq_instance_poll (vm, qp, bi, nexts, n_left); - - n_left -= n; - bi += n; - nexts += n; + vlib_buffer_t *b0; - if (n_left == 0) + b0 = vlib_get_buffer (vm, bi[0]); + vnet_feature_next_u16 (ni + 0, b0); + if (b0->flags & VLIB_BUFFER_IS_TRACED) { - n = VLIB_FRAME_SIZE - n_left; - vlib_buffer_enqueue_to_next (vm, node, buffer_indices, next_indices, - n); - n_left = VLIB_FRAME_SIZE; - bi = buffer_indices; - nexts = next_indices; - n_total += n; + snort_arc_next_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->buffer_index = bi[0]; + t->next_index = ni[0]; } } - if (n_left < VLIB_FRAME_SIZE) - { - n = VLIB_FRAME_SIZE - n_left; - vlib_buffer_enqueue_to_next (vm, node, buffer_indices, next_indices, n); - n_total += n; - } - return n_total; + vlib_buffer_enqueue_to_next (vm, node, buffer_indices, next_indices, n_pkts); + return n_pkts; } -VLIB_NODE_FN (snort_deq_node) +VLIB_NODE_FN (snort_ip4_input_next_node) (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { - snort_main_t *sm = &snort_main; - if (sm->input_mode == VLIB_NODE_STATE_POLLING) - return snort_deq_node_polling (vm, node, frame); - return snort_deq_node_interrupt (vm, node, frame); + return snort_arc_next_node_inline (vm, node, frame); } -VLIB_REGISTER_NODE (snort_deq_node) = { - .name = "snort-deq", +VLIB_REGISTER_NODE (snort_ip4_input_next_node) = { + .name = "snort-ip4-input-next", .vector_size = sizeof (u32), - .format_trace = format_snort_deq_trace, - .type = VLIB_NODE_TYPE_INPUT, - .state = VLIB_NODE_STATE_DISABLED, - .sibling_of = "snort-enq", + .aux_size = sizeof (u16), + .format_trace = format_snort_arc_next_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .sibling_of = "snort-ip4-input", +}; - .n_errors = ARRAY_LEN (snort_deq_error_strings), - .error_strings = snort_deq_error_strings, +VLIB_NODE_FN (snort_ip4_output_next_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return snort_arc_next_node_inline (vm, node, frame); +} - .n_next_nodes = 0, +VLIB_REGISTER_NODE (snort_ip4_output_next_node) = { + .name = "snort-ip4-output-next", + .vector_size = sizeof (u32), + .aux_size = sizeof (u16), + .format_trace = format_snort_arc_next_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .sibling_of = "snort-ip4-output", }; diff --git a/src/plugins/snort/enqueue.c b/src/plugins/snort/enqueue.c index ae04c58bba0..03b0d64f97d 100644 --- a/src/plugins/snort/enqueue.c +++ b/src/plugins/snort/enqueue.c @@ -6,250 +6,282 @@ #include #include #include -#include +#include #include -typedef struct -{ - u32 next_index; - u32 sw_if_index; - u16 instance; - u16 qpair; - u32 enq_slot; - u32 desc_index; - daq_vpp_desc_t desc; -} snort_enq_trace_t; - -static u8 * -format_snort_enq_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 *); - snort_enq_trace_t *t = va_arg (*args, snort_enq_trace_t *); - u32 indent = format_get_indent (s); - - s = format (s, - "sw-if-index %u next-index %u\n" - "%Uinstance %u qpair %u desc-index %u slot %u\n" - "%Udesc: buffer-pool %u offset %u len %u address-space-id %u\n", - t->sw_if_index, t->next_index, format_white_space, indent, - t->instance, t->qpair, t->desc_index, t->enq_slot, - format_white_space, indent, t->desc.buffer_pool, t->desc.offset, - t->desc.length, t->desc.address_space_id); - - return s; -} - -#define foreach_snort_enq_error \ - _ (SOCKET_ERROR, "write socket error") \ - _ (NO_INSTANCE, "no snort instance") \ - _ (NO_ENQ_SLOTS, "no enqueue slots (packet dropped)") - -typedef enum -{ -#define _(sym, str) SNORT_ENQ_ERROR_##sym, - foreach_snort_enq_error -#undef _ - SNORT_ENQ_N_ERROR, -} snort_enq_error_t; - static char *snort_enq_error_strings[] = { #define _(sym, string) string, foreach_snort_enq_error #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) +typedef struct { - u32 instance_index = get_snort_instance_index_ip4 (sm, b, fa_data); - return snort_get_instance_by_index (instance_index); -} + u16 instance_index; + u16 dequeue_node_next_index; + u8 use_rewrite_length_offset; +} snort_enq_scalar_args_t; static_always_inline uword -snort_enq_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node, - vlib_frame_t *frame, int with_trace) +snort_enq_qpair (vlib_main_t *vm, vlib_node_runtime_t *node, + snort_instance_t *si, snort_qpair_t *qp, u16 qpi, u32 *from, + daq_vpp_desc_t *descs, u32 *hashes, u32 n_enq, u16 next_index, + int single_qpair) { - snort_main_t *sm = &snort_main; - snort_instance_t *si = 0; - snort_qpair_t *qp = 0; - clib_thread_index_t thread_index = vm->thread_index; - u32 n_left = frame->n_vectors; - u32 n_trace = 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); + u32 n_free_descs, old_n_free_descs; + daq_vpp_desc_index_t mask = pow2_mask (qp->log2_queue_size); + daq_vpp_head_tail_t head; + u32 i, to_be_freed[VLIB_FRAME_SIZE], n_free = 0; - while (n_left) + if (PREDICT_FALSE (qp->client_index == SNORT_INVALID_CLIENT_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)) + u32 *f; + if (si->drop_on_disconnect) + next_index = SNORT_ENQ_NEXT_DROP; + if (single_qpair) { - if (si->drop_on_disconnect) - next[0] = SNORT_ENQ_NEXT_DROP; - else - next[0] = next_index; - next++; - unprocessed_bufs[n_unprocessed] = from[0]; - n_unprocessed++; + n_free = n_enq; + f = from; } else { - qp = vec_elt_at_index (si->qpairs, thread_index); - n = qp->n_pending++; - daq_vpp_desc_t *d = qp->pending_descs + n; + f = to_be_freed; + n_free = 0; + + for (u32 i = 0; i < n_enq; i++) + if (hashes[i] == qpi) + to_be_freed[n_free++] = from[i]; + } - qp->pending_nexts[n] = next_index; - qp->pending_buffers[n] = from[0]; + vlib_buffer_enqueue_to_single_next (vm, node, f, next_index, n_free); + vlib_node_increment_counter (vm, node->node_index, + SNORT_ENQ_ERROR_NO_CLIENT, n_free); + return 0; + } - vlib_buffer_chain_linearize (vm, b[0]); + head = __atomic_load_n (&qp->hdr->enq.head, __ATOMIC_ACQUIRE); + daq_vpp_desc_index_t next_free_desc = qp->next_free_desc; + old_n_free_descs = n_free_descs = qp->n_free_descs; - /* If this pkt is traced, snapshot the data */ - if (with_trace && b[0]->flags & VLIB_BUFFER_IS_TRACED) - n_trace++; + for (i = 0; i < n_enq; i++) + if (single_qpair || hashes[i] == qpi) + { + u32 desc_index = next_free_desc; + snort_qpair_entry_t *qpe = qp->entries + desc_index; + daq_vpp_desc_t *d = qp->hdr->descs + desc_index; - /* fill descriptor */ - d->buffer_pool = b[0]->buffer_pool_index; - d->length = b[0]->current_length; - d->offset = (u8 *) b[0]->data + b[0]->current_data + l3_offset - - sm->buffer_pool_base_addrs[d->buffer_pool]; - d->address_space_id = vnet_buffer (b[0])->sw_if_index[VLIB_RX]; - } + if (n_free_descs == 0) + break; - n_left--; - from++; - b++; - } + /* take empty descriptor from freelist */ + next_free_desc = qpe->freelist_next; + n_free_descs--; + + *d = descs[i]; + qpe->buffer_index = from[i]; + qpe->next_index = next_index; + + /* enqueue */ + qp->enq_ring[head++ & mask] = desc_index; + } + + __atomic_store_n (&qp->hdr->enq.head, head, __ATOMIC_RELEASE); + qp->n_free_descs = n_free_descs; + qp->next_free_desc = next_free_desc; - if (n_unprocessed) + if (!__atomic_exchange_n (&qp->hdr->enq.interrupt_pending, 1, + __ATOMIC_RELAXED)) { - vlib_node_increment_counter (vm, snort_enq_node.index, - SNORT_ENQ_ERROR_NO_INSTANCE, n_unprocessed); - vlib_buffer_enqueue_to_next (vm, node, unprocessed_bufs, nexts, - n_unprocessed); + if (write (qp->enq_fd, &(u64){ 1 }, sizeof (u64)) < 0) + vlib_node_increment_counter (vm, node->node_index, + SNORT_ENQ_ERROR_SOCKET_ERROR, 1); } - pool_foreach (si, sm->instances) + for (; i < n_enq; i++) + if (single_qpair || hashes[i] == qpi) + to_be_freed[n_free++] = from[i]; + + if (n_free) { - u32 head, freelist_len, n_pending, n_enq, mask; - u64 ctr = 1; - qp = vec_elt_at_index (si->qpairs, thread_index); - mask = pow2_mask (qp->log2_queue_size); - n_pending = qp->n_pending; - qp->n_pending = 0; + vlib_buffer_free (vm, to_be_freed, n_free); + vlib_node_increment_counter (vm, node->node_index, + SNORT_ENQ_ERROR_NO_ENQ_SLOTS, n_free); + n_enq = n_free; + } - if (n_pending == 0) - continue; + if (n_free_descs != 1U << qp->log2_queue_size) + vlib_node_set_interrupt_pending (vm, si->dequeue_node_index); - freelist_len = vec_len (qp->freelist); + return old_n_free_descs - n_free_descs; +} - if (freelist_len < n_pending) +static_always_inline void +snort_enq_prepare_descs (vlib_main_t *vm, vlib_buffer_t **b, daq_vpp_desc_t *d, + void **iph, u32 n_left, int use_rewrite_length_offset, + int with_hash) +{ + snort_main_t *sm = &snort_main; + u8 bpi[4]; + u8 off[4] = { 0, 0, 0, 0 }; + u8 *p[4]; + + for (; n_left >= 8; b += 4, d += 4, iph += 4, n_left -= 4) + { + clib_prefetch_load (b[4]); + vlib_buffer_chain_linearize (vm, b[0]); + vlib_buffer_chain_linearize (vm, b[1]); + clib_prefetch_load (b[5]); + vlib_buffer_chain_linearize (vm, b[2]); + vlib_buffer_chain_linearize (vm, b[3]); + + clib_prefetch_load (b[6]); + d[0].buffer_pool = bpi[0] = b[0]->buffer_pool_index; + d[1].buffer_pool = bpi[1] = b[1]->buffer_pool_index; + d[2].buffer_pool = bpi[2] = b[2]->buffer_pool_index; + d[3].buffer_pool = bpi[3] = b[3]->buffer_pool_index; + clib_prefetch_load (b[7]); + + if (use_rewrite_length_offset) { - n_enq = freelist_len; - vlib_buffer_free (vm, qp->pending_buffers + n_enq, - n_pending - n_enq); - vlib_node_increment_counter (vm, snort_enq_node.index, - SNORT_ENQ_ERROR_NO_ENQ_SLOTS, - n_pending - n_enq); + off[0] = vnet_buffer (b[0])->ip.save_rewrite_length; + off[1] = vnet_buffer (b[1])->ip.save_rewrite_length; + off[2] = vnet_buffer (b[2])->ip.save_rewrite_length; + off[3] = vnet_buffer (b[3])->ip.save_rewrite_length; } - else - n_enq = n_pending; - if (n_enq == 0) - continue; + d[0].length = b[0]->current_length - off[0]; + d[1].length = b[1]->current_length - off[1]; + d[2].length = b[2]->current_length - off[2]; + d[3].length = b[3]->current_length - off[3]; - total_enq += n_enq; - head = *qp->enq_head; + p[0] = (u8 *) b[0]->data + b[0]->current_data; + p[1] = (u8 *) b[1]->data + b[1]->current_data; + p[2] = (u8 *) b[2]->data + b[2]->current_data; + p[3] = (u8 *) b[3]->data + b[3]->current_data; - for (u32 i = 0; i < n_enq; i++) - { - u32 desc_index = qp->freelist[--freelist_len]; - qp->next_indices[desc_index] = qp->pending_nexts[i]; - ASSERT (qp->buffer_indices[desc_index] == ~0); - qp->buffer_indices[desc_index] = qp->pending_buffers[i]; - clib_memcpy_fast (qp->descriptors + desc_index, - qp->pending_descs + i, sizeof (daq_vpp_desc_t)); - qp->enq_ring[head & mask] = desc_index; - - /* trace */ - if (with_trace && n_trace) - { - vlib_buffer_t *tb = vlib_get_buffer (vm, qp->pending_buffers[i]); - if (tb->flags & VLIB_BUFFER_IS_TRACED) - { - snort_enq_trace_t *t = - vlib_add_trace (vm, node, tb, sizeof (*t)); - t->sw_if_index = vnet_buffer (tb)->sw_if_index[VLIB_RX]; - t->next_index = qp->pending_nexts[i]; - t->instance = si->index; - t->qpair = qp - si->qpairs; - t->enq_slot = head & mask; - t->desc_index = desc_index; - clib_memcpy_fast (&t->desc, qp->pending_descs + i, - sizeof (daq_vpp_desc_t)); - } - } - head = head + 1; - } + d[0].offset = p[0] + off[0] - sm->buffer_pool_base_addrs[bpi[0]]; + d[1].offset = p[1] + off[1] - sm->buffer_pool_base_addrs[bpi[1]]; + d[2].offset = p[2] + off[2] - sm->buffer_pool_base_addrs[bpi[2]]; + d[3].offset = p[3] + off[3] - sm->buffer_pool_base_addrs[bpi[3]]; - __atomic_store_n (qp->enq_head, head, __ATOMIC_RELEASE); - vec_set_len (qp->freelist, freelist_len); - if (sm->input_mode == VLIB_NODE_STATE_INTERRUPT) + if (with_hash) { - if (write (qp->enq_fd, &ctr, sizeof (ctr)) < 0) - vlib_node_increment_counter (vm, snort_enq_node.index, - SNORT_ENQ_ERROR_SOCKET_ERROR, 1); + iph[0] = p[0]; + iph[1] = p[1]; + iph[2] = p[2]; + iph[3] = p[3]; } + + d[0].metadata = *snort_get_buffer_metadata (b[0]); + d[1].metadata = *snort_get_buffer_metadata (b[1]); + d[2].metadata = *snort_get_buffer_metadata (b[2]); + d[3].metadata = *snort_get_buffer_metadata (b[3]); + } + + for (; n_left; b++, d++, iph++, n_left--) + { + vlib_buffer_chain_linearize (vm, b[0]); + d[0].buffer_pool = bpi[0] = b[0]->buffer_pool_index; + + if (use_rewrite_length_offset) + off[0] = vnet_buffer (b[0])->ip.save_rewrite_length; + + d[0].length = b[0]->current_length - off[0]; + p[0] = (u8 *) b[0]->data + b[0]->current_data; + d[0].offset = p[0] + off[0] - sm->buffer_pool_base_addrs[bpi[0]]; + + if (with_hash) + iph[0] = p[0]; + + d[0].metadata = *snort_get_buffer_metadata (b[0]); } +} - return total_enq; +static_always_inline void +clib_array_hash_to_index_u32 (u32 *src, u32 n_indices, u32 n_elts) +{ + if (count_set_bits (n_indices) == 1) + { + clib_array_mask_u32 (src, n_indices - 1, n_elts); + return; + } + for (u32 i = 0; i < n_indices - 1; i++) + src[i] = ((u64) src[i] * n_indices) >> 32; } VLIB_NODE_FN (snort_enq_node) (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { - if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE)) - return snort_enq_node_inline (vm, node, frame, 1 /* is_trace*/); + snort_main_t *sm = &snort_main; + vlib_buffer_t *bufs[VLIB_FRAME_SIZE]; + u32 *from = vlib_frame_vector_args (frame); + u32 n_from = frame->n_vectors; + vlib_get_buffers (vm, from, bufs, n_from); + const snort_enq_scalar_args_t *sa = vlib_frame_scalar_args (frame); + snort_instance_t *si = pool_elt_at_index (sm->instances, sa->instance_index); + u16 qpairs_per_thread = si->qpairs_per_thread; + snort_qpair_t **qp; + u16 next_index = sa->dequeue_node_next_index; + daq_vpp_desc_t descs[VLIB_FRAME_SIZE]; + void *ip_hdrs[VLIB_FRAME_SIZE]; + u32 hashes[VLIB_FRAME_SIZE]; + uword rv = 0; + + /* first qpair for this thread */ + qp = snort_get_qpairs (si, vm->thread_index); + + if (qpairs_per_thread > 1) + { + if (sa->use_rewrite_length_offset) + snort_enq_prepare_descs (vm, bufs, descs, ip_hdrs, n_from, 1, 1); + else + snort_enq_prepare_descs (vm, bufs, descs, ip_hdrs, n_from, 0, 1); + + /* calculate hash out of pointers to ip headers */ + si->ip4_hash_fn (ip_hdrs, hashes, n_from); + /* convert hash to qpair index */ + clib_array_hash_to_index_u32 (hashes, qpairs_per_thread, n_from); + } else - return snort_enq_node_inline (vm, node, frame, 0 /* is_trace*/); + { + if (sa->use_rewrite_length_offset) + snort_enq_prepare_descs (vm, bufs, descs, 0, n_from, 1, 0); + else + snort_enq_prepare_descs (vm, bufs, descs, 0, n_from, 0, 0); + } + + if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE)) + { + for (u32 i = 0; i < n_from; i++) + { + if (bufs[i]->flags & VLIB_BUFFER_IS_TRACED) + { + snort_enq_trace_t *t = + vlib_add_trace (vm, node, bufs[i], sizeof (*t)); + t->sw_if_index = vnet_buffer (bufs[i])->sw_if_index[VLIB_RX]; + t->next_index = next_index; + t->instance = si->index; + t->desc = descs[i]; + t->qpair_id.thread_id = vm->thread_index; + t->qpair_id.queue_id = qpairs_per_thread > 1 ? hashes[i] : 0; + } + } + } + + if (qpairs_per_thread == 1) + return snort_enq_qpair (vm, node, si, qp[0], 0, from, descs, 0, n_from, + next_index, /* single_qpair */ 1); + + for (u32 qpi = 0; qpi < qpairs_per_thread; qpi++) + rv += snort_enq_qpair (vm, node, si, qp[qpi], qpi, from, descs, hashes, + n_from, next_index, /* single_qpair */ 0); + return rv; } VLIB_REGISTER_NODE (snort_enq_node) = { .name = "snort-enq", .vector_size = sizeof (u32), + .scalar_size = sizeof (snort_enq_scalar_args_t), .format_trace = format_snort_enq_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_next_nodes = SNORT_ENQ_N_NEXT_NODES, @@ -257,3 +289,141 @@ VLIB_REGISTER_NODE (snort_enq_node) = { .n_errors = ARRAY_LEN (snort_enq_error_strings), .error_strings = snort_enq_error_strings, }; + +static_always_inline uword +snort_arc_input (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame, int is_output) +{ + snort_main_t *sm = &snort_main; + u16 *instance_by_interface = is_output ? sm->output_instance_by_interface : + sm->input_instance_by_interface; + u32 *buffer_indices = vlib_frame_vector_args (frame), *bi = buffer_indices; + u32 n_pkts = frame->n_vectors, n_left = n_pkts, n_total_left = n_pkts; + u16 instance_indices[VLIB_FRAME_SIZE], *ii = instance_indices; + daq_vpp_pkt_metadata_t metadata = { + .flags = is_output ? 0 : DAQ_PKT_FLAG_PRE_ROUTING, + }; + + for (; n_left >= 8; n_left -= 4, bi += 4, ii += 4) + { + vlib_buffer_t *b0, *b1, *b2, *b3; + + clib_prefetch_load (vlib_get_buffer (vm, bi[4])); + b0 = vlib_get_buffer (vm, bi[0]); + clib_prefetch_load (vlib_get_buffer (vm, bi[5])); + b1 = vlib_get_buffer (vm, bi[1]); + clib_prefetch_load (vlib_get_buffer (vm, bi[6])); + b2 = vlib_get_buffer (vm, bi[2]); + clib_prefetch_load (vlib_get_buffer (vm, bi[7])); + b3 = vlib_get_buffer (vm, bi[3]); + + ii[0] = instance_by_interface[vnet_buffer (b0)->sw_if_index[VLIB_RX]]; + *snort_get_buffer_metadata (b0) = metadata; + ii[1] = instance_by_interface[vnet_buffer (b1)->sw_if_index[VLIB_RX]]; + *snort_get_buffer_metadata (b1) = metadata; + ii[2] = instance_by_interface[vnet_buffer (b2)->sw_if_index[VLIB_RX]]; + *snort_get_buffer_metadata (b2) = metadata; + ii[3] = instance_by_interface[vnet_buffer (b3)->sw_if_index[VLIB_RX]]; + *snort_get_buffer_metadata (b3) = metadata; + } + + for (; n_left; n_left -= 1, bi += 1, ii += 1) + { + vlib_buffer_t *b0; + + b0 = vlib_get_buffer (vm, bi[0]); + ii[0] = instance_by_interface[vnet_buffer (b0)->sw_if_index[VLIB_RX]]; + *snort_get_buffer_metadata (b0) = metadata; + } + + if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE)) + { + for (u32 i = 0; i < n_pkts; i++) + { + vlib_buffer_t *b = vlib_get_buffer (vm, buffer_indices[i]); + if (b->flags & VLIB_BUFFER_IS_TRACED) + { + snort_arc_input_trace_t *t = + vlib_add_trace (vm, node, b, sizeof (*t)); + t->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; + t->instance = instance_indices[i]; + } + } + } + + while (n_total_left) + { + vlib_next_frame_t *nf; + vlib_frame_t *f; + snort_enq_scalar_args_t *sa; + u32 *to_next, n_left_to_next, *not_now = buffer_indices; + u16 next_index = 0; /* snort_enq */ + u16 instance_index = instance_indices[0]; + snort_instance_t *si = pool_elt_at_index (sm->instances, instance_index); + u16 n_enq; + + vlib_get_new_next_frame (vm, node, next_index, to_next, n_left_to_next); + + to_next++[0] = buffer_indices[0]; + n_enq = 1; + + for (u32 i = 1; i < n_total_left; i++) + if (instance_indices[i] == instance_index) + { + to_next++[0] = buffer_indices[i]; + n_enq++; + } + else + not_now++[0] = buffer_indices[i]; + + nf = vlib_node_runtime_get_next_frame (vm, node, next_index); + f = vlib_get_frame (vm, nf->frame); + sa = vlib_frame_scalar_args (f); + *sa = (snort_enq_scalar_args_t){ + .instance_index = instance_index, + .dequeue_node_next_index = is_output ? + si->ip4_output_dequeue_node_next_index : + si->ip4_input_dequeue_node_next_index, + .use_rewrite_length_offset = is_output ? 1 : 0, + }; + vlib_frame_no_append (f); + vlib_put_next_frame (vm, node, next_index, n_left_to_next - n_enq); + n_total_left -= n_enq; + } + + return n_pkts; +} + +VLIB_NODE_FN (snort_ip4_input_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return snort_arc_input (vm, node, frame, 0 /* is_output */); +} + +VLIB_REGISTER_NODE (snort_ip4_input_node) = { + .name = "snort-ip4-input", + .vector_size = sizeof (u32), + .format_trace = format_snort_arc_input_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .next_nodes = { + [0] = "snort-enq", + }, + .n_next_nodes = 1, +}; + +VLIB_NODE_FN (snort_ip4_output_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return snort_arc_input (vm, node, frame, 1 /* is_output */); +} + +VLIB_REGISTER_NODE (snort_ip4_output_node) = { + .name = "snort-ip4-output", + .vector_size = sizeof (u32), + .format_trace = format_snort_arc_input_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .next_nodes = { + [0] = "snort-enq", + }, + .n_next_nodes = 1, +}; diff --git a/src/plugins/snort/export.h b/src/plugins/snort/export.h new file mode 100644 index 00000000000..e1f3edb2cd0 --- /dev/null +++ b/src/plugins/snort/export.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2021 Cisco Systems, Inc. + * Copyright(c) 2024 Arm Limited + */ + +#ifndef __snort_export_h__ +#define __snort_export_h__ + +#include +#include +#include +#include + +typedef struct +{ + u8 log2_queue_sz; + u8 drop_on_disconnect; + u8 qpairs_per_thread; + u8 drop_bitmap; /* bits indexed by verdict, 0 = pass, 1 = drop */ +} snort_instance_create_args_t; + +typedef u16 snort_instance_index_t; + +typedef int (snort_instance_create_fn_t) (vlib_main_t *vm, + snort_instance_create_args_t *args, + char *fmt, ...); +typedef int (snort_instance_delete_fn_t) ( + vlib_main_t *vm, snort_instance_index_t instance_index); + +typedef int (snort_instance_get_index_by_name_fn_t) ( + vlib_main_t *vm, const char *name, snort_instance_index_t *instance_index); + +snort_instance_create_fn_t snort_instance_create; +snort_instance_delete_fn_t snort_instance_delete; +snort_instance_get_index_by_name_fn_t snort_instance_get_index_by_name; + +#endif /* __snort_export_h__ */ diff --git a/src/plugins/snort/format.c b/src/plugins/snort/format.c new file mode 100644 index 00000000000..9228007a6c5 --- /dev/null +++ b/src/plugins/snort/format.c @@ -0,0 +1,114 @@ + +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include +#include +#include + +u8 * +format_snort_enq_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 *); + snort_enq_trace_t *t = va_arg (*args, snort_enq_trace_t *); + u32 indent = format_get_indent (s); + + s = + format (s, "sw-if-index %u next-index %u", t->sw_if_index, t->next_index); + s = format (s, "\n%Uinstance %u qpair %u.%u", format_white_space, indent, + t->instance, t->qpair_id.thread_id, t->qpair_id.queue_id); + s = + format (s, "\n%Udesc: buffer-pool %u offset %u len %u", format_white_space, + indent, t->desc.buffer_pool, t->desc.offset, t->desc.length); + s = + format (s, "\n%Umetadata: address-space-id %u flags 0x%x ingress_index %d", + format_white_space, indent, t->desc.metadata.address_space_id, + t->desc.metadata.flags, t->desc.metadata.ingress_index); + + return s; +} + +u8 * +format_snort_arc_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 *); + snort_arc_input_trace_t *t = va_arg (*args, snort_arc_input_trace_t *); + + return format (s, "sw-if-index %u instance %u", t->sw_if_index, t->instance); +} + +u8 * +format_snort_deq_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 *); + snort_deq_trace_t *t = va_arg (*args, snort_deq_trace_t *); + u32 indent = format_get_indent (s); + + s = format (s, "snort-deq: sw_if_index %d, next index %d\n", t->sw_if_index, + t->next_index); + s = format (s, "%U buffer index %d, verdict %U", format_white_space, indent, + t->buffer_index, format_snort_verdict, t->verdict); + + return s; +} + +u8 * +format_snort_arc_next_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 *); + snort_arc_next_trace_t *t = va_arg (*args, snort_arc_next_trace_t *); + + return format (s, "buffer-index %u next_index %u", t->buffer_index, + t->next_index); +} + +u8 * +format_snort_daq_version (u8 *s, va_list *args) +{ + u32 v = va_arg (*args, u32); + + return format (s, "%u.%u.%u", (u8) (v >> 24), (u8) (v >> 16), (u8) (v >> 8)); +} + +u8 * +format_snort_verdict (u8 *s, va_list *args) +{ + DAQ_Verdict v = va_arg (*args, DAQ_Verdict); + static char *strings[MAX_DAQ_VERDICT] = { + [DAQ_VERDICT_PASS] = "PASS", + [DAQ_VERDICT_BLOCK] = "BLOCK", + [DAQ_VERDICT_REPLACE] = "REPLACE", + [DAQ_VERDICT_WHITELIST] = "WHITELIST", + [DAQ_VERDICT_BLACKLIST] = "BLACKLIST", + [DAQ_VERDICT_IGNORE] = "IGNORE", + }; + + if (v >= MAX_DAQ_VERDICT || strings[v] == 0) + return format (s, "unknown (%d)", v); + + return format (s, "%s", strings[v]); +} + +u8 * +format_snort_mode (u8 *s, va_list *args) +{ + DAQ_Mode v = va_arg (*args, DAQ_Mode); + static char *strings[MAX_DAQ_MODE] = { + [DAQ_MODE_NONE] = "none", + [DAQ_MODE_PASSIVE] = "passive", + [DAQ_MODE_INLINE] = "inline", + [DAQ_MODE_READ_FILE] = "read-file", + }; + + if (v >= MAX_DAQ_MODE || strings[v] == 0) + return format (s, "unknown (%d)", v); + + return format (s, "%s", strings[v]); +} diff --git a/src/plugins/snort/interface.c b/src/plugins/snort/interface.c new file mode 100644 index 00000000000..1cd01a1421f --- /dev/null +++ b/src/plugins/snort/interface.c @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2025 Cisco Systems, Inc. + * Copyright(c) 2024 Arm Limited + */ + +#include +#include +#include +#include +#include + +VLIB_REGISTER_LOG_CLASS (snort_log, static) = { + .class_name = "snort", + .subclass_name = "interface", +}; + +VNET_FEATURE_INIT (snort_ip4_input, static) = { + .arc_name = "ip4-unicast", + .node_name = "snort-ip4-input", + .runs_before = VNET_FEATURES ("ip4-lookup"), +}; + +VNET_FEATURE_INIT (snort_ip4_output, static) = { + .arc_name = "ip4-output", + .node_name = "snort-ip4-output", + .runs_before = VNET_FEATURES ("interface-output"), +}; + +static void +snort_feature_enable_disable (u32 sw_if_index, int is_enable, int input, + int output) +{ + u32 fa_data = input ? 1 : 0 | output ? 1 : 0; + + if (input) + vnet_feature_enable_disable ("ip4-unicast", "snort-ip4-input", sw_if_index, + is_enable, &fa_data, sizeof (fa_data)); + + if (output) + vnet_feature_enable_disable ("ip4-output", "snort-ip4-output", sw_if_index, + is_enable, &fa_data, sizeof (fa_data)); +} + +int +snort_interface_enable_disable (vlib_main_t *vm, char *instance_name, + u32 sw_if_index, int is_enable, int input, + int output) +{ + 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_instance_t *instance; + u16 *input_instance, *output_instance, instance_index; + int rv = 0; + + /* If interface is up, do not allow modifying attached instances */ + 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; + } + + /* 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->input_instance_by_interface) <= 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 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_init_empty (sm->input_instance_by_interface, sw_if_index, ~0); + vec_validate_init_empty (sm->output_instance_by_interface, sw_if_index, ~0); + input_instance = sm->input_instance_by_interface + sw_if_index; + output_instance = sm->output_instance_by_interface + sw_if_index; + + instance_index = instance->index; + + /* When detaching with direction SNORT_INOUT choose currently attached + * directions */ + if (!is_enable) + { + if (input && *input_instance != instance_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; + } + + if (output && *output_instance != instance_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; + } + + if (input) + *input_instance = ~0; + if (output) + *output_instance = ~0; + } + else + { + if (input && *input_instance == instance_index) + { + rv = VNET_API_ERROR_FEATURE_ALREADY_ENABLED; + log_err ("interface %U already assgined to instance '%s' on " + "input direction", + format_vnet_sw_if_index_name, vnm, sw_if_index, + instance->name); + goto done; + } + if (output && *output_instance == instance_index) + { + rv = VNET_API_ERROR_FEATURE_ALREADY_ENABLED; + log_err ("interface %U already assgined to instance '%s' on " + "output direction", + format_vnet_sw_if_index_name, vnm, sw_if_index, + instance->name); + goto done; + } + + if (input) + *input_instance = instance_index; + if (output) + *output_instance = instance_index; + } + + snort_feature_enable_disable (sw_if_index, is_enable, input, output); + +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); + int rv = 0, in = 0, out = 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->input_instance_by_interface) <= 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; + } + in = sm->input_instance_by_interface[sw_if_index] != (u16) ~0; + out = sm->output_instance_by_interface[sw_if_index] != (u16) ~0; + + if (!in && !out) + { + 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; + } + + snort_feature_enable_disable (sw_if_index, 0 /* is_enable */, in, out); + sm->input_instance_by_interface[sw_if_index] = ~0; + sm->output_instance_by_interface[sw_if_index] = ~0; + +done: + return rv; +} + +int +snort_strip_instance_interfaces (vlib_main_t *vm, snort_instance_t *instance) +{ + snort_main_t *sm = &snort_main; + + int i; + int rv = 0; + + vec_foreach_index (i, sm->input_instance_by_interface) + if (sm->input_instance_by_interface[i] == instance->index) + rv = snort_interface_enable_disable (vm, (char *) instance->name, i, + 0 /* is_enable */, 1, 0); + + vec_foreach_index (i, sm->output_instance_by_interface) + if (sm->output_instance_by_interface[i] == instance->index) + rv = snort_interface_enable_disable (vm, (char *) instance->name, i, + 0 /* is_enable */, 0, 1); + + return rv; +} diff --git a/src/plugins/snort/main.c b/src/plugins/snort/main.c index c87ecfd7ebd..583c555581a 100644 --- a/src/plugins/snort/main.c +++ b/src/plugins/snort/main.c @@ -1,27 +1,15 @@ /* SPDX-License-Identifier: Apache-2.0 - * Copyright(c) 2021 Cisco Systems, Inc. + * Copyright(c) 2025 Cisco Systems, Inc. * Copyright(c) 2024 Arm Limited */ #include -#include +#include #include #include #include - -#include -#include - -#include -#include - -#include - #include -#include -#include - #include snort_main_t snort_main; @@ -30,372 +18,62 @@ VLIB_REGISTER_LOG_CLASS (snort_log, static) = { .class_name = "snort", }; -#define log_debug(fmt, ...) vlib_log_debug (snort_log.class, fmt, __VA_ARGS__) -#define log_err(fmt, ...) vlib_log_err (snort_log.class, fmt, __VA_ARGS__) - -snort_main_t * -snort_get_main () -{ - return &snort_main; -} - -static void -snort_client_disconnect (clib_file_t *uf) -{ - vlib_main_t *vm = vlib_get_main (); - snort_qpair_t *qp; - snort_main_t *sm = &snort_main; - snort_client_t *c = pool_elt_at_index (sm->clients, uf->private_data); - - if (c->instance_index != ~0) - { - snort_per_thread_data_t *ptd = - vec_elt_at_index (sm->per_thread_data, vm->thread_index); - snort_instance_t *si = - pool_elt_at_index (sm->instances, c->instance_index); - vec_foreach (qp, si->qpairs) - __atomic_store_n (&qp->ready, 1, __ATOMIC_RELEASE); - - si->client_index = ~0; - clib_interrupt_set (ptd->interrupts, uf->private_data); - vlib_node_set_interrupt_pending (vm, snort_deq_node.index); - } - - clib_file_del (&file_main, uf); - clib_socket_close (&c->socket); - pool_put (sm->clients, c); -} - -int -snort_instance_disconnect (vlib_main_t *vm, u32 instance_index) -{ - snort_main_t *sm = &snort_main; - snort_instance_t *si; - snort_client_t *client; - clib_file_main_t *fm = &file_main; - clib_file_t *uf = 0; - int rv = 0; - - si = snort_get_instance_by_index (instance_index); - if (!si) - return VNET_API_ERROR_NO_SUCH_ENTRY; - if (si->client_index == ~0) - return VNET_API_ERROR_FEATURE_DISABLED; - - client = pool_elt_at_index (sm->clients, si->client_index); - uf = clib_file_get (fm, client->file_index); - if (uf) - snort_client_disconnect (uf); - else - { - log_err ("failed to disconnect a broken client from" - "instance '%s'", - si->name); - rv = VNET_API_ERROR_INVALID_VALUE; - } - - 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) -{ - snort_main_t *sm = &snort_main; - uword *p; - if ((p = hash_get_mem (sm->instance_by_name, name)) == 0) - return 0; - - return vec_elt_at_index (sm->instances, p[0]); -} - -snort_instance_t * -snort_get_instance_by_index (u32 instance_index) -{ - snort_main_t *sm = &snort_main; - - if (pool_is_free_index (sm->instances, instance_index)) - return 0; - return pool_elt_at_index (sm->instances, instance_index); -} - -static clib_error_t * -snort_conn_fd_read_ready (clib_file_t *uf) -{ - vlib_main_t *vm = vlib_get_main (); - snort_main_t *sm = &snort_main; - snort_client_t *c = pool_elt_at_index (sm->clients, uf->private_data); - vlib_buffer_pool_t *bp; - snort_instance_t *si; - snort_qpair_t *qp; - snort_client_msg_queue_elt *e; - clib_error_t *err; - daq_vpp_msg_t msg; - char *name; - u8 *base; - - log_debug ("fd_read_ready: client %u", uf->private_data); - - if ((err = clib_socket_recvmsg (&c->socket, &msg, sizeof (msg), 0, 0))) - { - log_err ("client recvmsg error: %U", format_clib_error, err); - snort_client_disconnect (uf); - clib_error_free (err); - return 0; - } - - if (msg.type != DAQ_VPP_MSG_TYPE_HELLO) - { - log_err ("unexpeced message recieved from client", 0); - snort_client_disconnect (uf); - return 0; - } - - msg.hello.inst_name[DAQ_VPP_INST_NAME_LEN - 1] = 0; - name = msg.hello.inst_name; - - log_debug ("fd_read_ready: connect instance %s", name); - - if ((si = snort_get_instance_by_name (name)) == 0) - { - log_err ("unknown instance '%s' requested by client", name); - snort_client_disconnect (uf); - return 0; - } - - vec_foreach (qp, si->qpairs) - { - u32 ready = __atomic_load_n (&qp->ready, __ATOMIC_ACQUIRE); - if (!ready) - { - log_err ("instance '%s' is not ready to accept connections", name); - snort_client_disconnect (uf); - return 0; - } - snort_freelist_init (qp->freelist); - *qp->enq_head = *qp->deq_head = qp->next_desc = 0; - } - - base = (u8 *) si->shm_base; - - if (si->client_index != ~0) - { - log_err ("client already connected to instance '%s'", name); - snort_client_disconnect (uf); - return 0; - } - si->client_index = uf->private_data; - c->instance_index = si->index; - - log_debug ("fd_read_ready: connect instance index %u", si->index); - - clib_fifo_add2 (c->msg_queue, e); - e->msg.type = DAQ_VPP_MSG_TYPE_CONFIG; - e->msg.config.num_bpools = vec_len (vm->buffer_main->buffer_pools); - e->msg.config.num_qpairs = vec_len (si->qpairs); - e->msg.config.shm_size = si->shm_size; - e->fds[0] = si->shm_fd; - e->n_fds = 1; - - vec_foreach (bp, vm->buffer_main->buffer_pools) - { - vlib_physmem_map_t *pm; - pm = vlib_physmem_get_map (vm, bp->physmem_map_index); - clib_fifo_add2 (c->msg_queue, e); - e->msg.type = DAQ_VPP_MSG_TYPE_BPOOL; - e->msg.bpool.size = pm->n_pages << pm->log2_page_size; - e->fds[0] = pm->fd; - e->n_fds = 1; - } - - vec_foreach (qp, si->qpairs) - { - clib_fifo_add2 (c->msg_queue, e); - e->msg.type = DAQ_VPP_MSG_TYPE_QPAIR; - e->msg.qpair.log2_queue_size = qp->log2_queue_size; - e->msg.qpair.desc_table_offset = (u8 *) qp->descriptors - base; - e->msg.qpair.enq_ring_offset = (u8 *) qp->enq_ring - base; - e->msg.qpair.deq_ring_offset = (u8 *) qp->deq_ring - base; - e->msg.qpair.enq_head_offset = (u8 *) qp->enq_head - base; - e->msg.qpair.deq_head_offset = (u8 *) qp->deq_head - base; - e->fds[0] = qp->enq_fd; - e->fds[1] = qp->deq_fd; - e->n_fds = 2; - } - - clib_file_set_data_available_to_write (&file_main, c->file_index, 1); - return 0; -} - -static clib_error_t * -snort_conn_fd_write_ready (clib_file_t *uf) -{ - snort_main_t *sm = &snort_main; - snort_client_t *c = pool_elt_at_index (sm->clients, uf->private_data); - snort_client_msg_queue_elt *e; - - log_debug ("fd_write_ready: client %u", uf->private_data); - clib_fifo_sub2 (c->msg_queue, e); - - if (clib_fifo_elts (c->msg_queue) == 0) - clib_file_set_data_available_to_write (&file_main, c->file_index, 0); - - return clib_socket_sendmsg (&c->socket, &e->msg, sizeof (*e), e->fds, - e->n_fds); -} - -clib_error_t * -snort_conn_fd_error (clib_file_t *uf) -{ - log_debug ("fd_error: client %u", uf->private_data); - return 0; -} +vlib_node_registration_t snort_deq_node; static clib_error_t * snort_deq_ready (clib_file_t *uf) { vlib_main_t *vm = vlib_get_main (); - snort_main_t *sm = &snort_main; - snort_per_thread_data_t *ptd = - vec_elt_at_index (sm->per_thread_data, vm->thread_index); + snort_qpair_t *qp = (snort_qpair_t *) uf->private_data; u64 counter; - ssize_t bytes_read; - - bytes_read = read (uf->file_descriptor, &counter, sizeof (counter)); - if (bytes_read < 0) - { - return clib_error_return (0, "client closed socket"); - } - - if (bytes_read < sizeof (counter)) - { - return clib_error_return (0, "unexpected truncated read"); - } + ssize_t __clib_unused bytes_read; - clib_interrupt_set (ptd->interrupts, uf->private_data); - vlib_node_set_interrupt_pending (vm, snort_deq_node.index); + bytes_read = read ((int) uf->file_descriptor, &counter, sizeof (counter)); + __atomic_store_n (&qp->hdr->deq.interrupt_pending, 0, __ATOMIC_RELAXED); + vlib_node_set_interrupt_pending (vm, qp->dequeue_node_index); return 0; } -static clib_error_t * -snort_conn_fd_accept_ready (clib_file_t *uf) -{ - snort_main_t *sm = &snort_main; - snort_client_t *c; - clib_socket_t *s; - clib_error_t *err = 0; - clib_file_t t = { 0 }; - - pool_get_zero (sm->clients, c); - c->instance_index = ~0; - s = &c->socket; - - if ((err = clib_socket_accept (sm->listener, s))) - { - log_err ("%U", format_clib_error, err); - pool_put (sm->clients, c); - return err; - } - - t.read_function = snort_conn_fd_read_ready; - t.write_function = snort_conn_fd_write_ready; - t.error_function = snort_conn_fd_error; - t.file_descriptor = s->fd; - t.private_data = c - sm->clients; - t.description = format (0, "snort client"); - c->file_index = clib_file_add (&file_main, &t); - - log_debug ("snort_conn_fd_accept_ready: client %u", t.private_data); - return 0; -} +static char *snort_deq_error_strings[] = { +#define _(sym, string) string, + foreach_snort_deq_error +#undef _ +}; -static clib_error_t * -snort_listener_init (vlib_main_t *vm) +__clib_export int +snort_instance_get_index_by_name (vlib_main_t *vm, const char *name, + snort_instance_index_t *instance_index) { - snort_main_t *sm = &snort_main; - clib_error_t *err; - clib_file_t t = { 0 }; - clib_socket_t *s; - - if (sm->listener) - return 0; - - s = clib_mem_alloc (sizeof (clib_socket_t)); - clib_memset (s, 0, sizeof (clib_socket_t)); - s->config = (char *) sm->socket_name; - s->is_server = 1; - s->allow_group_write = 1; - s->is_seqpacket = 1; - s->passcred = 1; - - if ((err = clib_socket_init (s))) - { - clib_mem_free (s); - return err; - } + snort_instance_t *si = snort_get_instance_by_name ((char *) name); - t.read_function = snort_conn_fd_accept_ready; - t.file_descriptor = s->fd; - t.description = format (0, "snort listener %s", s->config); - log_debug ("%v", t.description); - clib_file_add (&file_main, &t); + if (!si) + return VNET_API_ERROR_NO_SUCH_ENTRY; - sm->listener = s; + *instance_index = si->index; return 0; } -int -snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz, - u8 drop_on_disconnect) +__clib_export int +snort_instance_create (vlib_main_t *vm, snort_instance_create_args_t *args, + char *fmt, ...) + { vlib_thread_main_t *tm = vlib_get_thread_main (); snort_main_t *sm = &snort_main; snort_instance_t *si; - u32 index, i; - u8 *base = CLIB_MEM_VM_MAP_FAILED; - u32 size; - int fd = -1; - u32 qpair_mem_sz = 0; - u32 qsz = 1 << log2_queue_sz; + va_list va; + u32 size, index, qpair_mem_sz; + u8 *base = CLIB_MEM_VM_MAP_FAILED, *name; + int rv = 0, fd = -1; + u32 qsz = 1 << args->log2_queue_sz; + u32 qpairs_per_thread, total_qpairs, n_threads = tm->n_vlib_mains; u8 align = CLIB_CACHE_LINE_BYTES; - int rv = 0; if (sm->listener == 0) { clib_error_t *err; - err = snort_listener_init (vm); + err = snort_listener_init (); if (err) { log_err ("listener init failed: %U", format_clib_error, err); @@ -404,20 +82,28 @@ snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz, } } - if (snort_get_instance_by_name (name)) + va_start (va, fmt); + name = va_format (0, fmt, &va); + va_end (va); + vec_add1 (name, 0); + + if (snort_get_instance_by_name ((char *) name)) return VNET_API_ERROR_ENTRY_ALREADY_EXISTS; - /* descriptor table */ - qpair_mem_sz += round_pow2 (qsz * sizeof (daq_vpp_desc_t), align); + qpairs_per_thread = clib_max (1, args->qpairs_per_thread); + total_qpairs = qpairs_per_thread * n_threads; - /* enq and deq ring */ - qpair_mem_sz += 2 * round_pow2 (qsz * sizeof (u32), align); + /* header and descriptor table */ + qpair_mem_sz = round_pow2 ( + sizeof (daq_vpp_qpair_header_t) + qsz * sizeof (daq_vpp_desc_t), align); - /* enq and deq head pointer */ - qpair_mem_sz += 2 * round_pow2 (sizeof (u32), align); + /* enq and deq ring */ + qpair_mem_sz += 2 * round_pow2 (qsz * sizeof (daq_vpp_desc_index_t), align); - size = round_pow2 ((uword) tm->n_vlib_mains * qpair_mem_sz, + /* total size of shared memory */ + size = round_pow2 ((uword) total_qpairs * qpair_mem_sz, clib_mem_get_page_size ()); + fd = clib_mem_vm_create_fd (CLIB_MEM_PAGE_SZ_DEFAULT, "snort instance %s", name); @@ -443,66 +129,125 @@ snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz, pool_get_zero (sm->instances, si); si->index = si - sm->instances; - si->client_index = ~0; si->shm_base = base; + si->drop_bitmap = args->drop_bitmap; si->shm_fd = fd; si->shm_size = size; - si->name = format (0, "%s%c", name, 0); - si->drop_on_disconnect = drop_on_disconnect; + si->name = name; + si->drop_on_disconnect = args->drop_on_disconnect; + si->qpairs_per_thread = qpairs_per_thread; + si->ip4_hash_fn = vnet_hash_default_function (VNET_HASH_FN_TYPE_IP4); + index = si - sm->instances; hash_set_mem (sm->instance_by_name, si->name, index); - log_debug ("instnce '%s' createed with fd %d at %p, len %u", name, fd, base, - size); - - vec_validate_aligned (sm->per_thread_data, tm->n_vlib_mains - 1, - CLIB_CACHE_LINE_BYTES); - vec_validate_aligned (si->qpairs, tm->n_vlib_mains - 1, - CLIB_CACHE_LINE_BYTES); - - for (int i = 0; i < tm->n_vlib_mains; i++) + if (vec_len (sm->snort_deleted_deq_nodes) > 0) { - snort_qpair_t *qp = vec_elt_at_index (si->qpairs, i); - snort_per_thread_data_t *ptd = vec_elt_at_index (sm->per_thread_data, i); - clib_file_t t = { 0 }; - - qp->log2_queue_size = log2_queue_sz; - qp->descriptors = (void *) base; - base += round_pow2 (qsz * sizeof (daq_vpp_desc_t), align); - qp->enq_ring = (void *) base; - base += round_pow2 (qsz * sizeof (u32), align); - qp->deq_ring = (void *) base; - base += round_pow2 (qsz * sizeof (u32), align); - qp->enq_head = (void *) base; - base += round_pow2 (sizeof (u32), align); - qp->deq_head = (void *) base; - base += round_pow2 (sizeof (u32), align); - qp->enq_fd = eventfd (0, EFD_NONBLOCK); - qp->deq_fd = eventfd (0, EFD_NONBLOCK); - vec_validate_aligned (qp->buffer_indices, qsz - 1, - CLIB_CACHE_LINE_BYTES); - vec_validate_aligned (qp->next_indices, qsz - 1, CLIB_CACHE_LINE_BYTES); - clib_memset_u32 (qp->buffer_indices, ~0, qsz); - - /* pre-populate freelist */ - vec_validate_aligned (qp->freelist, qsz - 1, CLIB_CACHE_LINE_BYTES); - snort_freelist_init (qp->freelist); - - /* listen on dequeue events */ - t.read_function = snort_deq_ready; - t.file_descriptor = qp->deq_fd; - t.private_data = si->index; - t.description = - format (0, "snort dequeue for instance '%s' qpair %u", si->name, i); - qp->deq_fd_file_index = clib_file_add (&file_main, &t); - qp->ready = 1; - clib_file_set_polling_thread (&file_main, qp->deq_fd_file_index, i); - clib_interrupt_resize (&ptd->interrupts, vec_len (sm->instances)); + snort_deleted_deq_node_t *dn = vec_end (sm->snort_deleted_deq_nodes) - 1; + si->dequeue_node_index = dn->dequeue_node_index; + vlib_node_rename (vm, si->dequeue_node_index, "snort-deq-%s", si->name); + foreach_vlib_main () + { + vlib_node_runtime_t *nrt; + snort_deq_runtime_data_t *rt; + nrt = vlib_node_get_runtime (this_vlib_main, dn->dequeue_node_index); + vlib_node_runtime_perf_counter (this_vlib_main, nrt, 0, 0, 0, + VLIB_NODE_RUNTIME_PERF_RESET); + rt = vlib_node_get_runtime_data (this_vlib_main, + dn->dequeue_node_index); + ASSERT (rt->is_deleted == 1); + rt->is_deleted = 0; + rt->instance_index = index; + } + + vlib_node_set_state (vm, si->dequeue_node_index, + VLIB_NODE_STATE_INTERRUPT); + vec_dec_len (sm->snort_deleted_deq_nodes, 1); } + else + { + snort_deq_runtime_data_t rt = {}; + rt.instance_index = index; + rt.is_deleted = 0; + + vlib_node_registration_t snort_deq_reg = { + .sibling_of = "snort-enq", + .type = VLIB_NODE_TYPE_SCHED, + .state = VLIB_NODE_STATE_INTERRUPT, + .vector_size = sizeof (u32), + .runtime_data = &rt, + .runtime_data_bytes = sizeof (snort_deq_runtime_data_t), + .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED, + .node_fn_registrations = snort_deq_node.node_fn_registrations, + .format_trace = format_snort_deq_trace, + .error_strings = snort_deq_error_strings, + .n_errors = ARRAY_LEN (snort_deq_error_strings), + }; + + si->dequeue_node_index = + vlib_register_node (vm, &snort_deq_reg, "snort-deq-%s", si->name); + } + + si->ip4_input_dequeue_node_next_index = vlib_node_add_named_next ( + vm, si->dequeue_node_index, "snort-ip4-input-next"); + si->ip4_output_dequeue_node_next_index = vlib_node_add_named_next ( + vm, si->dequeue_node_index, "snort-ip4-output-next"); + vlib_worker_thread_node_runtime_update (); + + log_debug ("instnce '%s' created with fd %d at %p, len %u", name, fd, base, + size); - for (i = 0; i < vlib_get_n_threads (); i++) - vlib_node_set_state (vlib_get_main_by_index (i), snort_deq_node.index, - sm->input_mode); + vec_validate_aligned (si->qpairs, total_qpairs - 1, CLIB_CACHE_LINE_BYTES); + + for (int thread_id = 0; thread_id < n_threads; thread_id++) + for (int queue_id = 0; queue_id < qpairs_per_thread; queue_id++) + { + snort_qpair_t *qp; + u32 sz = sizeof (snort_qpair_t) + qsz * sizeof (snort_qpair_entry_t); + + qp = clib_mem_alloc_aligned (sz, CLIB_CACHE_LINE_BYTES); + si->qpairs[thread_id * qpairs_per_thread + queue_id] = qp; + + *qp = (snort_qpair_t){ + .client_index = SNORT_INVALID_CLIENT_INDEX, + .dequeue_node_index = si->dequeue_node_index, + .log2_queue_size = args->log2_queue_sz, + .qpair_id.thread_id = thread_id, + .qpair_id.queue_id = queue_id, + .enq_fd = eventfd (0, EFD_NONBLOCK), + .deq_fd = eventfd (0, EFD_NONBLOCK), + }; + + qp->hdr = (void *) base; + base += round_pow2 (sizeof (daq_vpp_qpair_header_t) + + qsz * sizeof (daq_vpp_desc_t), + align); + qp->enq_ring = (void *) base; + base += round_pow2 (qsz * sizeof (daq_vpp_desc_index_t), align); + qp->deq_ring = (void *) base; + base += round_pow2 (qsz * sizeof (daq_vpp_desc_index_t), align); + + for (u32 i = 0; i < qsz; i++) + qp->entries[i].buffer_index = VLIB_BUFFER_INVALID_INDEX; + + qp->hdr->enq.cookie = DAQ_VPP_COOKIE; + snort_qpair_init (qp); + + /* listen on dequeue events */ + qp->deq_fd_file_index = clib_file_add ( + &file_main, &(clib_file_t){ + .read_function = snort_deq_ready, + .file_descriptor = qp->deq_fd, + .description = format ( + 0, "snort dequeue for instance '%s' qpair %u.%u", + si->name, thread_id, queue_id), + .polling_thread_index = thread_id, + .private_data = (u64) qp, + }); + + log_debug ("qpair %u.%u created at %p size %u enq_fd %u deq_fd %d", + thread_id, queue_id, qp, qsz, qp->enq_fd, qp->deq_fd); + } done: if (rv) @@ -511,284 +256,59 @@ done: clib_mem_vm_unmap (base); if (fd != -1) close (fd); + vec_free (name); } 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, - snort_attach_dir_t snort_dir) -{ - 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; - 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 interface is up, do not allow modifying attached instances */ - 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; - } - - /* 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 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); - - 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) - { - 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; - } - } - - /* 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; - } - - /* Loop evaluates input instances and then output instances */ - for (i = 0; i < 2; i++) - { - 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) - { - /* 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; - } - } - - if (is_enable) - { - /* 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); - } - else - { - /* 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, snort_instance_t *instance) -{ - snort_main_t *sm = &snort_main; - snort_interface_data_t *interface; - snort_attach_dir_t direction; - int i; - int rv = 0; - - /* Find all interfaces containing the given snort instance to disable */ - vec_foreach_index (i, sm->interfaces) - { - /* 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; - } - - return rv; -} - -int -snort_instance_delete (vlib_main_t *vm, u32 instance_index) +__clib_export int +snort_instance_delete (vlib_main_t *vm, snort_instance_index_t instance_index) { snort_main_t *sm = &snort_main; snort_instance_t *si; - snort_qpair_t *qp; + snort_deleted_deq_node_t *dn; int rv = 0; si = snort_get_instance_by_index (instance_index); if (!si) return VNET_API_ERROR_NO_SUCH_ENTRY; - if (si->client_index != ~0) - return VNET_API_ERROR_INSTANCE_IN_USE; + vec_foreach_pointer (qp, si->qpairs) + if (qp->client_index != SNORT_INVALID_CLIENT_INDEX) + return VNET_API_ERROR_INSTANCE_IN_USE; if ((rv = snort_strip_instance_interfaces (vm, si))) return rv; hash_unset_mem (sm->instance_by_name, si->name); + // disable deq node and put it on recycle list + vlib_node_set_state (vm, si->dequeue_node_index, VLIB_NODE_STATE_DISABLED); + foreach_vlib_main () + { + snort_deq_runtime_data_t *rt = + vlib_node_get_runtime_data (this_vlib_main, si->dequeue_node_index); + + /* Mark node runtime as deleted so (if called) + * will drop packets. */ + rt->is_deleted = 1; + rt->instance_index = ~0; + } + vlib_node_rename (vm, si->dequeue_node_index, "snort-deq-%s-deleted", + si->name); + vlib_unregister_errors (vm, si->dequeue_node_index); + vec_add2 (sm->snort_deleted_deq_nodes, dn, 1); + dn->dequeue_node_index = si->dequeue_node_index; + clib_mem_vm_unmap (si->shm_base); close (si->shm_fd); - vec_foreach (qp, si->qpairs) + vec_foreach_pointer (qp, si->qpairs) { clib_file_del_by_index (&file_main, qp->deq_fd_file_index); + close (qp->enq_fd); + close (qp->deq_fd); + clib_mem_free (qp); } log_debug ("deleting instance '%s'", si->name); @@ -800,36 +320,10 @@ snort_instance_delete (vlib_main_t *vm, u32 instance_index) return rv; } -int -snort_set_node_mode (vlib_main_t *vm, u32 mode) -{ - int i; - snort_main.input_mode = mode; - for (i = 0; i < vlib_get_n_threads (); i++) - vlib_node_set_state (vlib_get_main_by_index (i), snort_deq_node.index, - mode); - return 0; -} - -static void -snort_set_default_socket (snort_main_t *sm, u8 *socket_name) -{ - if (sm->socket_name) - return; - - if (!socket_name) - socket_name = (u8 *) DAQ_VPP_DEFAULT_SOCKET_FILE; - - sm->socket_name = - format (0, "%s/%s", vlib_unix_get_runtime_dir (), socket_name); - vec_terminate_c_string (sm->socket_name); -} - static clib_error_t * snort_init (vlib_main_t *vm) { snort_main_t *sm = &snort_main; - sm->input_mode = VLIB_NODE_STATE_INTERRUPT; sm->instance_by_name = hash_create_string (0, sizeof (uword)); vlib_buffer_pool_t *bp; @@ -840,8 +334,8 @@ snort_init (vlib_main_t *vm) vec_add1 (sm->buffer_pool_base_addrs, pm->base); } - if (!sm->socket_name) - snort_set_default_socket (sm, 0); + sm->socket_name = format (0, "%s/%s%c", vlib_unix_get_runtime_dir (), + DAQ_VPP_DEFAULT_SOCKET_FILE, 0); return 0; } @@ -852,15 +346,3 @@ VLIB_PLUGIN_REGISTER () = { .version = VPP_BUILD_VER, .description = "Snort", }; - -VNET_FEATURE_INIT (snort_enq, static) = { - .arc_name = "ip4-unicast", - .node_name = "snort-enq", - .runs_before = VNET_FEATURES ("ip4-lookup"), -}; - -VNET_FEATURE_INIT (snort_enq_out, static) = { - .arc_name = "ip4-output", - .node_name = "snort-enq", - .runs_before = VNET_FEATURES ("interface-output"), -}; diff --git a/src/plugins/snort/snort.h b/src/plugins/snort/snort.h index 76f0652df10..a5e33001603 100644 --- a/src/plugins/snort/snort.h +++ b/src/plugins/snort/snort.h @@ -10,91 +10,111 @@ #include #include #include -#include +#include +#include +#include + +#include + +#define SNORT_INVALID_CLIENT_INDEX CLIB_U32_MAX + +STATIC_ASSERT (VNET_BUFFER_OPAQUE_SIZE >= sizeof (daq_vpp_pkt_metadata_t), + "metadata must fit into vnet buffer opaque"); typedef struct { - CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); - u8 log2_queue_size; - daq_vpp_desc_t *descriptors; - volatile u32 *enq_head; - volatile u32 *deq_head; - volatile u32 *enq_ring; - volatile u32 *deq_ring; - u32 next_desc; + u32 buffer_index; + u16 next_index; + daq_vpp_desc_index_t freelist_next; +} snort_qpair_entry_t; + +typedef struct +{ + daq_vpp_qpair_header_t *hdr; + daq_vpp_desc_index_t *enq_ring; + daq_vpp_desc_index_t *deq_ring; int enq_fd, deq_fd; + u32 client_index; + daq_vpp_desc_index_t next_free_desc; + u16 n_free_descs; + daq_vpp_head_tail_t deq_tail; + u8 log2_queue_size; + u8 cleanup_needed; + daq_vpp_qpair_id_t qpair_id; u32 deq_fd_file_index; - u32 *buffer_indices; - u16 *next_indices; - u32 *freelist; - u32 ready; - - /* temporary storeage used by enqueue node */ - u32 n_pending; - u16 pending_nexts[VLIB_FRAME_SIZE]; - u32 pending_buffers[VLIB_FRAME_SIZE]; - daq_vpp_desc_t pending_descs[VLIB_FRAME_SIZE]; + u32 dequeue_node_index; + u64 n_packets_by_verdict[MAX_DAQ_VERDICT]; + snort_qpair_entry_t entries[]; } snort_qpair_t; typedef struct { - u32 index; - u32 client_index; + snort_instance_index_t index; + u8 drop_bitmap; + u8 drop_on_disconnect; + u32 dequeue_node_index; void *shm_base; u32 shm_size; int shm_fd; - snort_qpair_t *qpairs; + snort_qpair_t **qpairs; u8 *name; - u8 drop_on_disconnect; + vnet_hash_fn_t ip4_hash_fn; + u16 ip4_input_dequeue_node_next_index; + u16 ip4_output_dequeue_node_next_index; + u16 qpairs_per_thread; } snort_instance_t; typedef struct { - daq_vpp_msg_t msg; + daq_vpp_msg_reply_t msg; int fds[2]; int n_fds; } snort_client_msg_queue_elt; typedef struct { - clib_socket_t socket; u32 instance_index; + u32 qpair_index; +} snort_client_qpair_t; + +typedef struct +{ + clib_socket_t socket; u32 file_index; + u32 daq_version; + u16 n_instances; + u8 mode; snort_client_msg_queue_elt *msg_queue; + snort_client_qpair_t *qpairs; } snort_client_t; typedef struct { - /* per-instance dequeue interrupts */ - void *interrupts; -} snort_per_thread_data_t; + u16 instance_index; + u16 is_deleted; +} snort_deq_runtime_data_t; -/* Holds snort plugin related information for an interface */ typedef struct { - u32 *input_instance_indices; - u32 *output_instance_indices; -} snort_interface_data_t; + u32 dequeue_node_index; +} snort_deleted_deq_node_t; typedef struct { clib_socket_t *listener; snort_client_t *clients; snort_instance_t *instances; + snort_deleted_deq_node_t *snort_deleted_deq_nodes; uword *instance_by_name; - snort_interface_data_t *interfaces; + u16 *input_instance_by_interface; + u16 *output_instance_by_interface; u8 **buffer_pool_base_addrs; - snort_per_thread_data_t *per_thread_data; - u32 input_mode; u8 *socket_name; /* API message ID base */ u16 msg_id_base; } snort_main_t; -extern clib_file_main_t file_main; extern snort_main_t snort_main; -extern vlib_node_registration_t snort_enq_node; -extern vlib_node_registration_t snort_deq_node; typedef enum { @@ -102,15 +122,6 @@ typedef enum SNORT_ENQ_N_NEXT_NODES, } snort_enq_next_t; -typedef enum -{ - 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 \ { \ [SNORT_ENQ_NEXT_DROP] = "error-drop", \ @@ -118,27 +129,138 @@ 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, - u8 drop_on_disconnect); + +/* interface.c */ int snort_interface_enable_disable (vlib_main_t *vm, char *instance_name, - u32 sw_if_index, int is_enable, - snort_attach_dir_t dir); + u32 sw_if_index, int is_enable, int in, + int out); 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); +int snort_strip_instance_interfaces (vlib_main_t *vm, + snort_instance_t *instance); + +/* socket.c */ +int snort_client_disconnect (vlib_main_t *vm, u32 client_index); +clib_error_t *snort_listener_init (); + +/* format.c */ +format_function_t format_snort_enq_trace; +format_function_t format_snort_arc_input_trace; +format_function_t format_snort_arc_next_trace; +format_function_t format_snort_deq_trace; +format_function_t format_snort_daq_version; +format_function_t format_snort_verdict; +format_function_t format_snort_mode; + +/* enqueue.c */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u16 instance; + daq_vpp_qpair_id_t qpair_id; + daq_vpp_desc_t desc; +} snort_enq_trace_t; + +typedef struct +{ + u32 sw_if_index; + u16 instance; +} snort_arc_input_trace_t; +#define foreach_snort_enq_error \ + _ (SOCKET_ERROR, "write socket error") \ + _ (NO_CLIENT, "no snort client attached") \ + _ (NO_ENQ_SLOTS, "no enqueue slots (packet dropped)") + +typedef enum +{ +#define _(sym, str) SNORT_ENQ_ERROR_##sym, + foreach_snort_enq_error +#undef _ + SNORT_ENQ_N_ERROR, +} snort_enq_error_t; + +/* dequeue.c */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u32 buffer_index; + u8 verdict; +} snort_deq_trace_t; + +typedef struct +{ + u32 buffer_index; + u32 next_index; +} snort_arc_next_trace_t; + +#define foreach_snort_deq_error \ + _ (BAD_DESC, "bad descriptor") \ + _ (BAD_DESC_INDEX, "bad descriptor index") \ + _ (NO_CLIENT_FREE, "packets freed on client dissapear") + +typedef enum +{ +#define _(sym, str) SNORT_DEQ_ERROR_##sym, + foreach_snort_deq_error +#undef _ + SNORT_DEQ_N_ERROR, +} snort_deq_error_t; + +/* inlines */ always_inline void -snort_freelist_init (u32 *fl) +snort_qpair_init (snort_qpair_t *qp) { - for (int j = 0; j < vec_len (fl); j++) - fl[j] = j; + u16 qsz = 1 << qp->log2_queue_size; + for (int j = 0; j < qsz - 1; j++) + { + qp->entries[j].freelist_next = j + 1; + qp->entries[j].buffer_index = VLIB_BUFFER_INVALID_INDEX; + } + qp->entries[qsz - 1].freelist_next = ~0; + qp->next_free_desc = 0; + qp->hdr->enq.head = qp->hdr->deq.head = 0; + qp->hdr->enq.interrupt_pending = qp->hdr->deq.interrupt_pending = 0; + qp->deq_tail = 0; + qp->n_free_descs = qsz; } +static_always_inline snort_qpair_t ** +snort_get_qpairs (snort_instance_t *si, clib_thread_index_t thread_index) +{ + return si->qpairs + (uword) thread_index * si->qpairs_per_thread; +} + +static_always_inline snort_instance_t * +snort_get_instance_by_name (char *name) +{ + snort_main_t *sm = &snort_main; + uword *p; + if ((p = hash_get_mem (sm->instance_by_name, name)) == 0) + return 0; + + return vec_elt_at_index (sm->instances, p[0]); +} + +static_always_inline snort_instance_t * +snort_get_instance_by_index (u32 instance_index) +{ + snort_main_t *sm = &snort_main; + + if (pool_is_free_index (sm->instances, instance_index)) + return 0; + return pool_elt_at_index (sm->instances, instance_index); +} + +static_always_inline daq_vpp_pkt_metadata_t * +snort_get_buffer_metadata (vlib_buffer_t *b) +{ + return vnet_buffer_get_opaque (b); +} + +#define log_debug(fmt, ...) \ + vlib_log_debug (snort_log.class, "%s: " fmt, __func__, __VA_ARGS__) +#define log_err(fmt, ...) vlib_log_err (snort_log.class, fmt, __VA_ARGS__) + #endif /* __snort_snort_h__ */ diff --git a/src/plugins/snort/snort_api.c b/src/plugins/snort/snort_api.c index 00b3c3ace67..5de0194336d 100644 --- a/src/plugins/snort/snort_api.c +++ b/src/plugins/snort/snort_api.c @@ -27,13 +27,6 @@ static u32 snort_base_msg_id; #include #include -VLIB_REGISTER_LOG_CLASS (snort_log, static) = { - .class_name = "snort", -}; - -#define log_debug(fmt, ...) vlib_log_debug (snort_log.class, fmt, __VA_ARGS__) -#define log_err(fmt, ...) vlib_log_err (snort_log.class, fmt, __VA_ARGS__) - static void vl_api_snort_instance_create_t_handler (vl_api_snort_instance_create_t *mp) { @@ -46,8 +39,12 @@ vl_api_snort_instance_create_t_handler (vl_api_snort_instance_create_t *mp) u32 instance_index = ~0; snort_instance_t *si; - rv = - snort_instance_create (vm, name, min_log2 (queue_sz), drop_on_disconnect); + rv = snort_instance_create (vm, + &(snort_instance_create_args_t){ + .drop_on_disconnect = drop_on_disconnect, + .log2_queue_sz = min_log2 (queue_sz), + }, + "%s", name); if ((si = snort_get_instance_by_name (name))) { @@ -82,13 +79,19 @@ vl_api_snort_interface_attach_t_handler (vl_api_snort_interface_attach_t *mp) u32 sw_if_index = clib_net_to_host_u32 (mp->sw_if_index); u8 snort_dir = mp->snort_dir; int rv = VNET_API_ERROR_NO_SUCH_ENTRY; + int in = 0, out = 0; VALIDATE_SW_IF_INDEX (mp); switch (snort_dir) { - case SNORT_INPUT: - case SNORT_OUTPUT: - case SNORT_INOUT: + case 1: + in = 1; + break; + case 2: + out = 1; + break; + case 3: + in = out = 1; break; default: rv = VNET_API_ERROR_INVALID_ARGUMENT; @@ -97,9 +100,8 @@ vl_api_snort_interface_attach_t_handler (vl_api_snort_interface_attach_t *mp) instance = snort_get_instance_by_index (instance_index); if (instance) { - rv = snort_interface_enable_disable (vm, (char *) instance->name, - sw_if_index, 1 /* is_enable */, - snort_dir); + rv = snort_interface_enable_disable ( + vm, (char *) instance->name, sw_if_index, 1 /* is_enable */, in, out); } BAD_SW_IF_INDEX_LABEL; REPLY_MACRO (VL_API_SNORT_INTERFACE_ATTACH_REPLY); @@ -116,7 +118,7 @@ send_snort_instance_details (const snort_instance_t *instance, VL_API_SNORT_INSTANCE_DETAILS, name_len, rp, context, ({ rmp->instance_index = clib_host_to_net_u32 (instance->index); vl_api_vec_to_api_string (instance->name, &rmp->name); - rmp->snort_client_index = clib_host_to_net_u32 (instance->client_index); + rmp->snort_client_index = clib_host_to_net_u32 (0); rmp->shm_size = clib_host_to_net_u32 (instance->shm_size); rmp->shm_fd = clib_host_to_net_u32 (instance->shm_fd); rmp->drop_on_disconnect = instance->drop_on_disconnect; @@ -173,99 +175,13 @@ vl_api_snort_instance_get_t_handler (vl_api_snort_instance_get_t *mp) } } -static void -send_snort_interface_details (u32 sw_if_index, u32 instance_index, - vl_api_registration_t *rp, u32 context) -{ - vl_api_snort_interface_details_t *rmp; - - if (instance_index != ~0) - { - REPLY_MACRO_DETAILS4 (VL_API_SNORT_INTERFACE_DETAILS, rp, context, ({ - rmp->instance_index = - clib_host_to_net_u32 (instance_index); - rmp->sw_if_index = - clib_host_to_net_u32 (sw_if_index); - })); - } -} - static void 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 *instances; - u32 index; int rv = 0; - sw_if_index = clib_net_to_host_u32 (mp->sw_if_index); - - if (sw_if_index == INDEX_INVALID) - { - /* clang-format off */ - if (vec_len (sm->interfaces) == 0) - { - REPLY_MACRO2 (VL_API_SNORT_INTERFACE_GET_REPLY, ({ rmp->cursor = ~0; })); - return; - } - - REPLY_AND_DETAILS_VEC_MACRO( - VL_API_SNORT_INTERFACE_GET_REPLY, - sm->interfaces, - mp, rmp, rv, ({ - 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 - { - 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); - - if (rp == NULL) - { - return; - } - - send_snort_interface_details (sw_if_index, *instances, rp, - mp->context); - } - else - { - rv = VNET_API_ERROR_NO_SUCH_ENTRY; - } - - /* clang-format off */ - REPLY_MACRO2 (VL_API_SNORT_INTERFACE_GET_REPLY, ({ - rmp->cursor = INDEX_INVALID; - })); - /* clang-format on */ - } + REPLY_MACRO2 (VL_API_SNORT_INTERFACE_GET_REPLY, ({ rmp->cursor = ~0; })); } static void @@ -274,23 +190,12 @@ send_snort_client_details (const snort_client_t *client, { snort_main_t *sm = snort_get_main (); vl_api_snort_client_details_t *rmp; - snort_instance_t *instance; - if (client->instance_index == ~0) - { - return; - } - - instance = pool_elt_at_index (sm->instances, client->instance_index); - if (instance) - { - REPLY_MACRO_DETAILS4 (VL_API_SNORT_CLIENT_DETAILS, rp, context, ({ - rmp->instance_index = - clib_host_to_net_u32 (client->instance_index); - rmp->client_index = - clib_host_to_net_u32 (client - sm->clients); - })); - } + REPLY_MACRO_DETAILS4 (VL_API_SNORT_CLIENT_DETAILS, rp, context, ({ + rmp->instance_index = 0; + rmp->client_index = + clib_host_to_net_u32 (client - sm->clients); + })); } static void @@ -347,21 +252,11 @@ static void vl_api_snort_client_disconnect_t_handler (vl_api_snort_client_disconnect_t *mp) { vlib_main_t *vm = vlib_get_main (); - snort_main_t *sm = snort_get_main (); - snort_client_t *client; vl_api_snort_client_disconnect_reply_t *rmp; u32 client_index = clib_net_to_host_u32 (mp->snort_client_index); int rv = 0; - if (pool_is_free_index (sm->clients, client_index)) - { - rv = VNET_API_ERROR_NO_SUCH_ENTRY; - } - else - { - client = pool_elt_at_index (sm->clients, client_index); - rv = snort_instance_disconnect (vm, client->instance_index); - } + rv = snort_client_disconnect (vm, client_index); REPLY_MACRO (VL_API_SNORT_CLIENT_DISCONNECT_REPLY); } @@ -370,10 +265,8 @@ static void vl_api_snort_instance_disconnect_t_handler ( vl_api_snort_instance_disconnect_t *mp) { - vlib_main_t *vm = vlib_get_main (); vl_api_snort_instance_disconnect_reply_t *rmp; - u32 instance_index = clib_net_to_host_u32 (mp->instance_index); - int rv = snort_instance_disconnect (vm, instance_index); + int rv = VNET_API_ERROR_UNSUPPORTED; REPLY_MACRO (VL_API_SNORT_INSTANCE_DISCONNECT_REPLY); } @@ -396,28 +289,17 @@ vl_api_snort_interface_detach_t_handler (vl_api_snort_interface_detach_t *mp) static void vl_api_snort_input_mode_get_t_handler (vl_api_snort_input_mode_get_t *mp) { - snort_main_t *sm = &snort_main; vl_api_snort_input_mode_get_reply_t *rmp; - int rv = 0; + int rv = VNET_API_ERROR_UNSUPPORTED; - REPLY_MACRO2 (VL_API_SNORT_INPUT_MODE_GET_REPLY, ({ - rmp->snort_mode = clib_host_to_net_u32 (sm->input_mode); - })); + REPLY_MACRO (VL_API_SNORT_INPUT_MODE_GET_REPLY); } static void vl_api_snort_input_mode_set_t_handler (vl_api_snort_input_mode_set_t *mp) { - vlib_main_t *vm = vlib_get_main (); vl_api_snort_input_mode_set_reply_t *rmp; - u8 mode = mp->input_mode; - int rv = 0; - - if (mode != VLIB_NODE_STATE_INTERRUPT && mode != VLIB_NODE_STATE_POLLING) - { - clib_error_return (0, "invalid input mode %u", mode); - } - snort_set_node_mode (vm, mode); + int rv = VNET_API_ERROR_UNSUPPORTED; REPLY_MACRO (VL_API_SNORT_INPUT_MODE_SET_REPLY); } diff --git a/src/plugins/snort/socket.c b/src/plugins/snort/socket.c new file mode 100644 index 00000000000..119435c5048 --- /dev/null +++ b/src/plugins/snort/socket.c @@ -0,0 +1,339 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2025 Cisco Systems, Inc. + */ + +#include +#include +#include +#include +#include + +VLIB_REGISTER_LOG_CLASS (snort_log, static) = { + .class_name = "snort", + .subclass_name = "socket", +}; + +int +snort_client_disconnect (vlib_main_t *vm, u32 client_index) +{ + snort_main_t *sm = &snort_main; + snort_client_t *c = pool_elt_at_index (sm->clients, client_index); + snort_client_qpair_t *cqp; + + if (pool_is_free_index (sm->clients, client_index)) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + vec_foreach (cqp, c->qpairs) + { + snort_instance_t *si = snort_get_instance_by_index (cqp->instance_index); + snort_qpair_t *qp = *vec_elt_at_index (si->qpairs, cqp->qpair_index); + qp->client_index = SNORT_INVALID_CLIENT_INDEX; + vlib_node_set_interrupt_pending ( + vlib_get_main_by_index (qp->qpair_id.thread_id), + si->dequeue_node_index); + __atomic_store_n (&qp->cleanup_needed, 1, __ATOMIC_RELEASE); + } + + clib_file_del_by_index (&file_main, c->file_index); + clib_socket_close (&c->socket); + clib_fifo_free (c->msg_queue); + vec_free (c->qpairs); + pool_put (sm->clients, c); + return 0; +} + +static void +snort_msg_connect (vlib_main_t *vm, snort_client_t *c, daq_vpp_msg_req_t *req, + snort_client_msg_queue_elt *e) +{ + log_debug ("daq_version %U num_snort_instances %u mode %U", + format_snort_daq_version, req->connect.daq_version, + req->connect.num_snort_instances, format_snort_mode, + req->connect.mode); + + e->msg.connect.num_bpools = vec_len (vm->buffer_main->buffer_pools); + c->n_instances = req->connect.num_snort_instances; + c->daq_version = req->connect.daq_version; + c->mode = req->connect.mode; +} + +static void +snort_msg_get_buffer_pool (vlib_main_t *vm, daq_vpp_msg_req_t *req, + snort_client_msg_queue_elt *e) +{ + vlib_buffer_pool_t *bp; + vlib_physmem_map_t *pm; + daq_vpp_msg_reply_get_buffer_pool_t *r = &e->msg.get_buffer_pool; + + log_debug ("buffer_pool_index %u", req->get_buffer_pool.buffer_pool_index); + + u32 i = req->get_buffer_pool.buffer_pool_index; + + if (i >= vec_len (vm->buffer_main->buffer_pools)) + { + e->msg.err = DAQ_VPP_ERR_INVALID_MESSAGE; + return; + } + + bp = vec_elt_at_index (vm->buffer_main->buffer_pools, i); + pm = vlib_physmem_get_map (vm, bp->physmem_map_index); + r->buffer_pool_index = i; + r->size = pm->n_pages << pm->log2_page_size; + e->fds[0] = pm->fd; + e->n_fds = 1; +} + +static void +snort_msg_get_input (daq_vpp_msg_req_t *req, snort_client_msg_queue_elt *e) +{ + daq_vpp_msg_reply_get_input_t *r = &e->msg.get_input; + snort_instance_t *si; + + log_debug ("input name '%s'", req->get_input.input_name); + si = snort_get_instance_by_name (req->get_input.input_name); + + if (!si) + { + e->msg.err = DAQ_VPP_ERR_UNKNOWN_INPUT; + return; + } + + r->input_index = si->index; + r->shm_size = si->shm_size; + r->num_qpairs = vec_len (si->qpairs); + e->fds[0] = si->shm_fd; + e->n_fds = 1; + + log_debug ("input_index %u num_qpairs %u shm_size %u fd %d", r->input_index, + r->num_qpairs, r->shm_size, e->fds[0]); +} + +static void +snort_msg_attach_qpair (snort_client_t *c, daq_vpp_msg_req_t *req, + snort_client_msg_queue_elt *e) +{ + snort_main_t *sm = &snort_main; + daq_vpp_msg_reply_attach_qpair_t *r = &e->msg.attach_qpair; + u32 instance_index = req->attach_qpair.input_index; + u32 qpair_index = req->attach_qpair.qpair_index; + snort_instance_t *si; + snort_qpair_t *qp = 0; + u8 *base; + + snort_client_qpair_t cqp = { .instance_index = instance_index, + .qpair_index = qpair_index }; + + si = snort_get_instance_by_index (instance_index); + + if (!si) + { + e->msg.err = DAQ_VPP_ERR_UNKNOWN_INPUT; + log_err ("instance %u doesn't exist", instance_index); + return; + } + + if (qpair_index >= vec_len (si->qpairs)) + { + e->msg.err = DAQ_VPP_ERR_INVALID_INDEX; + log_err ("apair with index %u on instance %s doesn't exist", qpair_index, + si->name); + return; + } + + qp = *vec_elt_at_index (si->qpairs, qpair_index); + + if (qp->client_index != SNORT_INVALID_CLIENT_INDEX) + { + e->msg.err = DAQ_VPP_ERR_QPAIR_IN_USE; + log_err ("apair %u.%u is used by client %u", qp->qpair_id.thread_id, + qp->qpair_id.queue_id, qp->client_index); + return; + } + + if (qp->cleanup_needed) + { + e->msg.err = DAQ_VPP_ERR_QPAIR_NOT_READY; + log_err ("apair %u.%u is not ready", qp->qpair_id.thread_id, + qp->qpair_id.queue_id); + return; + } + + qp->client_index = c - sm->clients; + + base = (u8 *) si->shm_base; + r->qpair_id = qp->qpair_id; + r->input_index = si->index; + r->log2_queue_size = qp->log2_queue_size; + r->qpair_header_offset = (u8 *) qp->hdr - base; + r->enq_ring_offset = (u8 *) qp->enq_ring - base; + r->deq_ring_offset = (u8 *) qp->deq_ring - base; + e->fds[0] = qp->enq_fd; + e->fds[1] = qp->deq_fd; + e->n_fds = 2; + + vec_add1 (c->qpairs, cqp); + + log_debug ( + "qpair_id %u.%u input_index %u log2_queue_size %u qpair_header_offset %u " + "enq_ring_offset %u deq_ring_offset %u enq_fd %d deq_fd %d", + r->qpair_id.thread_id, r->qpair_id.queue_id, r->input_index, + r->log2_queue_size, r->qpair_header_offset, r->enq_ring_offset, + r->deq_ring_offset, e->fds[0], e->fds[1]); +} + +static clib_error_t * +snort_conn_fd_read_ready (clib_file_t *uf) +{ + vlib_main_t *vm = vlib_get_main (); + snort_main_t *sm = &snort_main; + u32 client_index = uf->private_data; + snort_client_t *c = pool_elt_at_index (sm->clients, client_index); + snort_client_msg_queue_elt *e; + clib_error_t *err; + daq_vpp_msg_req_t req; + + log_debug ("fd_read_ready: client %u", uf->private_data); + + err = clib_socket_recvmsg (&c->socket, &req, sizeof (req), 0, 0); + if (err) + { + log_err ("client recvmsg error: %U", format_clib_error, err); + clib_error_free (err); + snort_client_disconnect (vm, client_index); + return 0; + } + + clib_fifo_add2 (c->msg_queue, e); + *e = (snort_client_msg_queue_elt){ + .msg.type = req.type, + .msg.err = DAQ_VPP_OK, + }; + + switch (req.type) + { + case DAQ_VPP_MSG_TYPE_CONNECT: + snort_msg_connect (vm, c, &req, e); + break; + + case DAQ_VPP_MSG_TYPE_GET_BUFFER_POOL: + snort_msg_get_buffer_pool (vm, &req, e); + break; + + case DAQ_VPP_MSG_TYPE_GET_INPUT: + snort_msg_get_input (&req, e); + break; + + case DAQ_VPP_MSG_TYPE_ATTACH_QPAIR: + snort_msg_attach_qpair (c, &req, e); + break; + + default: + e->msg.err = DAQ_VPP_ERR_INVALID_MESSAGE; + break; + } + + clib_file_set_data_available_to_write (&file_main, c->file_index, 1); + return 0; +} + +static clib_error_t * +snort_conn_fd_write_ready (clib_file_t *uf) +{ + snort_main_t *sm = &snort_main; + snort_client_t *c; + snort_client_msg_queue_elt *e; + + log_debug ("fd_write_ready: client %u", uf->private_data); + + if (pool_is_free_index (sm->clients, uf->private_data)) + { + clib_file_set_data_available_to_write (&file_main, uf->index, 0); + return 0; + } + + c = pool_elt_at_index (sm->clients, uf->private_data); + clib_fifo_sub2 (c->msg_queue, e); + if (clib_fifo_elts (c->msg_queue) == 0) + clib_file_set_data_available_to_write (&file_main, uf->index, 0); + + return clib_socket_sendmsg (&c->socket, &e->msg, sizeof (*e), e->fds, + e->n_fds); +} + +clib_error_t * +snort_conn_fd_error (clib_file_t *uf) +{ + log_debug ("fd_error: client %u", uf->private_data); + return 0; +} + +static clib_error_t * +snort_conn_fd_accept_ready (clib_file_t __clib_unused *uf) +{ + snort_main_t *sm = &snort_main; + snort_client_t *c; + clib_socket_t *s; + clib_error_t *err = 0; + clib_file_t t = { 0 }; + u32 client_index; + + pool_get_zero (sm->clients, c); + client_index = c - sm->clients; + s = &c->socket; + + err = clib_socket_accept (sm->listener, s); + if (err) + { + log_err ("%U", format_clib_error, err); + pool_put (sm->clients, c); + return err; + } + + t.read_function = snort_conn_fd_read_ready; + t.write_function = snort_conn_fd_write_ready; + t.error_function = snort_conn_fd_error; + t.file_descriptor = s->fd; + t.private_data = client_index; + t.description = format (0, "snort client %u", client_index); + c->file_index = clib_file_add (&file_main, &t); + + log_debug ("snort_conn_fd_accept_ready: client %u", client_index); + return 0; +} + +clib_error_t * +snort_listener_init () +{ + snort_main_t *sm = &snort_main; + clib_error_t *err; + clib_file_t t = { 0 }; + clib_socket_t *s; + + if (sm->listener) + return 0; + + s = clib_mem_alloc (sizeof (clib_socket_t)); + clib_memset (s, 0, sizeof (clib_socket_t)); + s->config = (char *) sm->socket_name; + s->is_server = 1; + s->allow_group_write = 1; + s->is_seqpacket = 1; + s->passcred = 1; + + err = clib_socket_init (s); + if (err) + { + clib_mem_free (s); + return err; + } + + t.read_function = snort_conn_fd_accept_ready; + t.file_descriptor = s->fd; + t.description = format (0, "snort listener %s", s->config); + log_debug ("%v", t.description); + clib_file_add (&file_main, &t); + + sm->listener = s; + + return 0; +} diff --git a/test/test_snort.py b/test/test_snort.py index 5335091dba7..090d68ff3b9 100644 --- a/test/test_snort.py +++ b/test/test_snort.py @@ -34,19 +34,15 @@ 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 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": "", + "snort delete instance snortTest2": "", } for command, reply in commands_replies.items(): @@ -79,17 +75,21 @@ class TestSnortVapi(VppTestCase): def test_snort_01_modes_set_interrupt(self): """Set mode to interrupt""" - self.vapi.snort_input_mode_set(input_mode=1) - reply = self.vapi.snort_input_mode_get() - self.assertEqual(reply.snort_mode, 1) - reply = self.vapi.cli("show snort mode") - self.assertIn("interrupt", reply) + with self.vapi.assert_negative_api_retval(): + reply = self.vapi.snort_input_mode_set(input_mode=1) + self.assertEqual(reply.retval, -126) + with self.vapi.assert_negative_api_retval(): + reply = self.vapi.snort_input_mode_get() + self.assertEqual(reply.retval, -126) def test_snort_02_modes_set_polling(self): """Set mode to polling""" - self.vapi.snort_input_mode_set(input_mode=0) - reply = self.vapi.snort_input_mode_get() - self.assertEqual(reply.snort_mode, 0) + with self.vapi.assert_negative_api_retval(): + reply = self.vapi.snort_input_mode_set(input_mode=0) + self.assertEqual(reply.retval, -126) + with self.vapi.assert_negative_api_retval(): + reply = self.vapi.snort_input_mode_get() + self.assertEqual(reply.retval, -126) def test_snort_03_create(self): """Create two snort instances""" @@ -104,15 +104,19 @@ class TestSnortVapi(VppTestCase): reply = self.vapi.cli("show snort instances") self.assertIn("snortTest0", reply) self.assertIn("snortTest1", reply) + self.vapi.snort_instance_delete(instance_index=1) + self.vapi.snort_instance_delete(instance_index=0) def test_snort_04_attach_if(self): """Interfaces can be attached""" - reply = self.vapi.snort_interface_attach( - instance_index=0, sw_if_index=1, snort_dir=1 + self.vapi.snort_instance_create( + queue_size=8, drop_on_disconnect=0, name="snortTest0" ) - reply = self.vapi.snort_interface_attach( - instance_index=0, sw_if_index=2, snort_dir=2 + self.vapi.snort_instance_create( + queue_size=32, drop_on_disconnect=1, name="snortTest1" ) + self.vapi.snort_interface_attach(instance_index=0, sw_if_index=1, snort_dir=1) + self.vapi.snort_interface_attach(instance_index=0, sw_if_index=2, snort_dir=2) # verify attaching with an invalid direction is rejected try: reply = self.vapi.snort_interface_attach( @@ -122,18 +126,13 @@ class TestSnortVapi(VppTestCase): pass else: self.assertNotEqual(reply.retval, 0) + self.vapi.snort_interface_attach(instance_index=1, sw_if_index=2, snort_dir=3) reply = self.vapi.cli("show snort interfaces") - self.assertNotIn("snortTest1", reply) - - 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) + self.assertIn("input instances:", reply) + self.assertIn("pg0: snortTest0", reply) + self.assertIn("pg1: snortTest1", reply) + self.assertIn("output instances:", reply) + self.assertIn("pg1: snortTest1", reply) # verify attaching a previously attached interface is rejected try: @@ -156,18 +155,39 @@ class TestSnortVapi(VppTestCase): self.assertNotEqual(reply.retval, 0) reply = self.vapi.cli("show snort interfaces") self.assertIn("snortTest1", reply) + self.vapi.snort_instance_delete(instance_index=1) + self.vapi.snort_instance_delete(instance_index=0) def test_snort_05_delete_instance(self): """Instances can be deleted""" + reply = self.vapi.snort_instance_create( + queue_size=8, drop_on_disconnect=0, name="snortTest0" + ) + self.assertEqual(reply.instance_index, 0) reply = self.vapi.snort_instance_delete(instance_index=0) + self.assertEqual(reply.retval, 0) reply = self.vapi.cli("show snort interfaces") self.assertNotIn("snortTest0", reply) - self.assertIn("snortTest1", reply) - self.assertNotIn("pg0", reply) - self.assertIn("pg1", reply) def test_snort_06_detach_if(self): """Interfaces can be detached""" + reply = self.vapi.snort_instance_create( + queue_size=8, drop_on_disconnect=0, name="snortTest0" + ) + self.assertEqual(reply.instance_index, 0) + reply = self.vapi.snort_instance_create( + queue_size=128, drop_on_disconnect=1, name="snortTest1" + ) + self.assertEqual(reply.instance_index, 1) + reply = self.vapi.snort_interface_attach( + instance_index=0, sw_if_index=1, snort_dir=1 + ) + reply = self.vapi.snort_interface_attach( + instance_index=1, sw_if_index=1, snort_dir=2 + ) + reply = self.vapi.snort_interface_attach( + instance_index=1, sw_if_index=2, snort_dir=3 + ) # verify detaching an invalid sw_if_index is rejected try: reply = self.vapi.snort_interface_detach(sw_if_index=3) @@ -175,9 +195,14 @@ class TestSnortVapi(VppTestCase): pass else: self.assertNotEqual(reply.retval, 0) + reply = self.vapi.snort_interface_detach(sw_if_index=1) + self.assertEqual(reply.retval, 0) reply = self.vapi.snort_interface_detach(sw_if_index=2) - reply = self.vapi.cli("show snort interfaces") + self.assertEqual(reply.retval, 0) + self.assertNotIn("pg0", reply) self.assertNotIn("pg1", reply) + self.vapi.snort_instance_delete(instance_index=1) + self.vapi.snort_instance_delete(instance_index=0) if __name__ == "__main__":