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)
.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
##############################################################################
--- /dev/null
+# 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))
# 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()
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")
#include <vnet/vnet.h>
#include <snort/snort.h>
-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;
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
{
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)
{
done:
vec_free (name);
- unformat_free (line_input);
return err;
}
};
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 <name>",
- .function = snort_disconnect_instance_command_fn,
+VLIB_CLI_COMMAND (snort_disconnect_client_command, static) = {
+ .path = "snort disconnect client",
+ .short_help = "snort disconnect client <index>",
+ .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)
{
done:
vec_free (name);
- unformat_free (line_input);
return err;
}
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'",
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 <name> [instance <name> [...]]) "
- "interface <if-name> "
- "[input|output|inout]",
+ .short_help = "snort attach instance <name> [instance <name> [...]] "
+ "interface <if-name> "
+ "[input|output|inout]",
.function = snort_attach_command_fn,
};
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
{
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 <name> [instance <name> [...]]) "
- "interface <if-name> ",
+ .short_help = "snort detach instance <name> [instance <name> [...]] "
+ "interface <if-name> ",
.function = snort_detach_command_fn,
};
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;
}
{
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;
}
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;
}
.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,
-};
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+#define _GNU_SOURCE
+#include <stdbool.h>
+#include <assert.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/epoll.h>
+
+#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;
+}
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+#ifndef __DAQ_VPP_H__
+#define __DAQ_VPP_H__
+
+#include <stdint.h>
+#include <daq_module_api.h>
+#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__ */
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2021 Cisco Systems, Inc.
+ */
+
+#ifndef __DAQ_VPP_SHARED_H__
+#define __DAQ_VPP_SHARED_H__
+
+#include <stdint.h>
+
+#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__ */
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+
+#include <daq_dlt.h>
+#include <daq_module_api.h>
+
+#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,
+};
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/epoll.h>
+
+#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;
+}
+++ /dev/null
-/* SPDX-License-Identifier: Apache-2.0
- * Copyright(c) 2021 Cisco Systems, Inc.
- */
-
-#define _GNU_SOURCE
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/mman.h>
-#include <sys/time.h>
-#include <errno.h>
-#include <sys/epoll.h>
-
-#include <vppinfra/cache.h>
-#include <daq_dlt.h>
-#include <daq_module_api.h>
-
-#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,
-};
+++ /dev/null
-/* SPDX-License-Identifier: Apache-2.0
- * Copyright(c) 2021 Cisco Systems, Inc.
- */
-
-#ifndef __DAQ_VPP_H__
-#define __DAQ_VPP_H__
-
-#include <stdint.h>
-
-#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__ */
#include <vnet/feature/feature.h>
#include <snort/snort.h>
-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",
};
#include <vnet/ip/ip4_inlines.h>
#include <vnet/ip/ip4_packet.h>
#include <vlib/vlib.h>
-#include <vnet/feature/feature.h>
+#include <vppinfra/vector/array_mask.h>
#include <snort/snort.h>
-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,
.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,
+};
--- /dev/null
+/* 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 <vppinfra/error.h>
+#include <vppinfra/socket.h>
+#include <vppinfra/file.h>
+#include <vlib/vlib.h>
+
+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__ */
--- /dev/null
+
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vnet/ip/ip4_inlines.h>
+#include <vnet/ip/ip4_packet.h>
+#include <vlib/vlib.h>
+#include <vnet/feature/feature.h>
+#include <snort/snort.h>
+
+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]);
+}
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ * Copyright(c) 2024 Arm Limited
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+#include <snort/snort.h>
+#include <vnet/vnet.h>
+
+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;
+}
/* SPDX-License-Identifier: Apache-2.0
- * Copyright(c) 2021 Cisco Systems, Inc.
+ * Copyright(c) 2025 Cisco Systems, Inc.
* Copyright(c) 2024 Arm Limited
*/
#include <vlib/vlib.h>
-#include <vlibapi/api_types.h>
+#include <vlib/file.h>
#include <vnet/plugin/plugin.h>
#include <vpp/app/version.h>
#include <snort/snort.h>
-
-#include <snort/snort.api_enum.h>
-#include <snort/snort.api_types.h>
-
-#include <vnet/ip/ip_types_api.h>
-#include <vnet/format_fns.h>
-
-#include <vlibapi/api_helper_macros.h>
-
#include <vnet/vnet.h>
-#include <vlibapi/api.h>
-#include <vlibmemory/api.h>
-
#include <sys/eventfd.h>
snort_main_t snort_main;
.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);
}
}
- 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);
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)
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);
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;
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;
}
.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"),
-};
#include <vppinfra/socket.h>
#include <vppinfra/file.h>
#include <vlib/vlib.h>
-#include <snort/daq_vpp.h>
+#include <vnet/vnet.h>
+#include <snort/export.h>
+#include <snort/daq/daq_vpp_shared.h>
+
+#include <daq_common.h>
+
+#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
{
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", \
/* 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__ */
#include <vlibapi/api.h>
#include <sys/eventfd.h>
-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)
{
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)))
{
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;
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);
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;
}
}
-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
{
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
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);
}
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);
}
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);
}
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <vlib/file.h>
+#include <snort/snort.h>
+#include <vnet/vnet.h>
+#include <sys/eventfd.h>
+
+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;
+}
"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():
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"""
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(
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:
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)
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__":