snort: plugin rework 33/42933/27
authorDamjan Marion <[email protected]>
Tue, 6 May 2025 15:45:16 +0000 (17:45 +0200)
committerDamjan Marion <[email protected]>
Fri, 5 Sep 2025 10:33:41 +0000 (10:33 +0000)
Changes:
  - support for multiple qpairs per thread
  - ability for daq to connect to multiple instances
  - ability for daq to connect to specific qpairs
  - added ownership to each qpair to avoid multiple clients using same one
  - enabled packet rewrite
  - enqueue and dequeue nodes are decoupled from feature arcs so they
    can be used in different datapaths
  - feature arc doesn't support multiple-instances anymore, use multiple
    qpairs for load-balancing
  - per-instance drop decisions based on drop bitmap

sample snort invocation:

snort \
  <standard args> \
  --max-packet-threads 3 \  # number of threads snort will launch
  --daq-dir <daq dir> \     # path to dir with libdaq_bpp.so
  --daq vpp \               # enable VPP daq
  --daq-var <path> \        # optional, path to snort.sock
  --daq-var debug \         # optional, turn on debug prints
  -i vpp0:1 \               # attach instance to vpp0 instance qpair 1.0
  -i vpp0:1.1 \             # attach instance to vpp0 instance qpair 1.1
  -i vpp0:2.0,2.1,0 \       # attach instance to vpp0 instance qpairs 2.0, 2.1 and 0.0

qpair is identified as x.y where x is thread_id and y is queue_id, .y
part can be ommited if queue_id is 0.

Type: feature

Change-Id: I4f65b9a12ec71b115b9cb2da814c7918ddf6191d
Signed-off-by: Damjan Marion <[email protected]>
Signed-off-by: Mohsin Kazmi <[email protected]>
21 files changed:
build/external/Makefile
build/external/packages/daq.mk [new file with mode: 0644]
src/plugins/snort/CMakeLists.txt
src/plugins/snort/cli.c
src/plugins/snort/daq/config.c [new file with mode: 0644]
src/plugins/snort/daq/daq_vpp.h [new file with mode: 0644]
src/plugins/snort/daq/daq_vpp_shared.h [new file with mode: 0644]
src/plugins/snort/daq/main.c [new file with mode: 0644]
src/plugins/snort/daq/socket.c [new file with mode: 0644]
src/plugins/snort/daq_vpp.c [deleted file]
src/plugins/snort/daq_vpp.h [deleted file]
src/plugins/snort/dequeue.c
src/plugins/snort/enqueue.c
src/plugins/snort/export.h [new file with mode: 0644]
src/plugins/snort/format.c [new file with mode: 0644]
src/plugins/snort/interface.c [new file with mode: 0644]
src/plugins/snort/main.c
src/plugins/snort/snort.h
src/plugins/snort/snort_api.c
src/plugins/snort/socket.c [new file with mode: 0644]
test/test_snort.py

index 5394ecc..51064ce 100644 (file)
@@ -16,6 +16,7 @@ PKG_SUFFIX ?= $(shell git log --oneline v$(PKG_VERSION)-rc0.. . | wc -l)
 include ../build_common.mk
 include ../packages_common.mk
 
+include packages/daq.mk
 include packages/ipsec-mb.mk
 include packages/quicly.mk
 ifneq ($(shell uname), FreeBSD)
@@ -31,16 +32,16 @@ clean:
 
 .PHONY: install
 ifeq ($(shell uname), FreeBSD)
-install: $(if $(ARCH_X86_64), ipsec-mb-install) dpdk-install quicly-install
+install: $(if $(ARCH_X86_64), ipsec-mb-install) daq-install dpdk-install quicly-install
 else
-install: $(if $(ARCH_X86_64), ipsec-mb-install) dpdk-install rdma-core-install quicly-install xdp-tools-install $(if $(AARCH64), octeon-roc-install)
+install: $(if $(ARCH_X86_64), ipsec-mb-install) daq-install dpdk-install rdma-core-install quicly-install xdp-tools-install $(if $(AARCH64), octeon-roc-install)
 endif  # FreeBSD
 
 .PHONY: config
 ifeq ($(shell uname), FreeBSD)
-config: $(if $(ARCH_X86_64), ipsec-mb-config) dpdk-config quicly-build
+config: daq-config $(if $(ARCH_X86_64), ipsec-mb-config) dpdk-config quicly-build
 else
-config: $(if $(ARCH_X86_64), ipsec-mb-config) dpdk-config rdma-core-config quicly-build
+config: daq-config $(if $(ARCH_X86_64), ipsec-mb-config) dpdk-config rdma-core-config quicly-build
 endif  # FreeBSD
 
 ##############################################################################
diff --git a/build/external/packages/daq.mk b/build/external/packages/daq.mk
new file mode 100644 (file)
index 0000000..f1f5a82
--- /dev/null
@@ -0,0 +1,40 @@
+# Copyright (c) 2025 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+daq_version             := 3.0.21
+daq_tarball             := daq-$(daq_version).tar.gz
+daq_tarball_sha256sum_3.0.21 := 60ad9405c1c6b75955e0784511b173570a601491ccdb6399da53ca811c446a96
+daq_tarball_sha256sum      := $(daq_tarball_sha256sum_$(daq_version))
+daq_tarball_strip_dirs  := 1
+daq_url                 := https://github.com/snort3/libdaq/archive/refs/tags/v$(daq_version).tar.gz
+
+define  daq_config_cmds
+               @rm -f $(daq_config_log)
+        @cd ${daq_src_dir} && ./bootstrap > $(daq_config_log) 2>&1
+       @cd ${daq_src_dir} && ./configure --prefix='$(daq_install_dir)' --enable-shared --enable-static \
+                --disable-bundled-modules \
+               CFLAGS='' \
+               >> $(daq_config_log) 2>&1
+endef
+
+define  daq_build_cmds
+       @cd ${daq_src_dir} && $(MAKE) V=1 > $(daq_build_log)
+endef
+
+define  daq_install_cmds
+       @rm -f $(daq_install_log)
+       @cd ${daq_src_dir} && \
+               $(MAKE) install V=1 PREFIX='$(daq_install_dir)' >> $(daq_install_log)
+endef
+
+$(eval $(call package,daq))
index 3fc2bd6..895fdd0 100644 (file)
@@ -1,31 +1,11 @@
 # SPDX-License-Identifier: Apache-2.0
 # Copyright(c) 2021 Cisco Systems, Inc.
 
-add_vpp_plugin(snort
-  SOURCES
-  enqueue.c
-  dequeue.c
-  main.c
-  cli.c
-  snort_api.c
-
-  API_FILES
-  snort.api
-
-  MULTIARCH_SOURCES
-  enqueue.c
-  dequeue.c
-
-  COMPONENT
-  vpp-plugin-snort
-)
-
-# DAQ
-
 find_path(LIBDAQ_INCLUDE_DIR NAMES daq_module_api.h daq_dlt.h daq_version.h)
+find_library(LIBDAQ_LINK_LIBRARY NAMES libdaq.a)
 
 if (NOT LIBDAQ_INCLUDE_DIR)
-  message(WARNING "-- libdaq headers not found - snort3 DAQ disabled")
+  message(WARNING "-- libdaq headers not found - snort plugin disabled")
   return()
 endif()
 
@@ -40,14 +20,41 @@ foreach(l ${daq_version})
 endforeach()
 
 set(DAQ_VER "${DAQ_VERSION_MAJOR}.${DAQ_VERSION_MINOR}.${DAQ_VERSION_PATCH}")
-message(STATUS "libdaq ${DAQ_VER} include files found at ${LIBDAQ_INCLUDE_DIR}")
-
 if (NOT DAQ_VERSION_MAJOR MATCHES 3)
   message(WARNING "-- libdaq version not supported - snort3 DAQ disabled")
   return()
 endif()
 
-add_library(daq_vpp SHARED daq_vpp.c)
+message(STATUS "snort plugin needs libdaq ${DAQ_VER} - found at ${LIBDAQ_LINK_LIBRARY}")
+
+include_directories (${LIBDAQ_INCLUDE_DIR})
+
+add_vpp_plugin(snort
+  SOURCES
+  cli.c
+  dequeue.c
+  enqueue.c
+  format.c
+  interface.c
+  main.c
+  snort_api.c
+  socket.c
+
+  API_FILES
+  snort.api
+
+  INSTALL_HEADERS
+  export.h
+
+  MULTIARCH_SOURCES
+  enqueue.c
+  dequeue.c
+
+  COMPONENT
+  vpp-plugin-snort
+)
+
+add_library(daq_vpp SHARED daq/main.c daq/config.c daq/socket.c)
 set_target_properties(daq_vpp PROPERTIES SOVERSION ${VPP_LIB_VERSION})
 target_compile_options (daq_vpp PRIVATE "-fvisibility=hidden")
 target_compile_options (daq_vpp PRIVATE "-DHAVE_VISIBILITY")
index d4b69ad..d04dd39 100644 (file)
@@ -7,28 +7,17 @@
 #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;
@@ -55,56 +44,28 @@ snort_attach_detach_instance (vlib_main_t *vm, vnet_main_t *vnm,
   return err;
 }
 
-static clib_error_t *
-snort_detach_all_instance (vlib_main_t *vm, vnet_main_t *vnm, u32 sw_if_index)
-{
-  clib_error_t *err = NULL;
-  int rv = snort_interface_disable_all (vm, sw_if_index);
-  switch (rv)
-    {
-    case 0:
-      break;
-    case VNET_API_ERROR_INSTANCE_IN_USE:
-      err = clib_error_return (
-       0, "interface %U is currently up, set state down first",
-       format_vnet_sw_if_index_name, vnm, sw_if_index);
-      break;
-    case VNET_API_ERROR_INVALID_INTERFACE:
-      err = clib_error_return (0, "interface %U has no attached instances",
-                              format_vnet_sw_if_index_name, vnm, sw_if_index);
-      break;
-    default:
-      err =
-       clib_error_return (0, "snort_interface_disable_all returned %d", rv);
-      break;
-    }
-  return err;
-}
-
 static clib_error_t *
 snort_create_instance_command_fn (vlib_main_t *vm, unformat_input_t *input,
                                  vlib_cli_command_t *cmd)
 {
-  unformat_input_t _line_input, *line_input = &_line_input;
   clib_error_t *err = 0;
   u8 *name = 0;
   u32 queue_size = 1024;
-  u8 drop_on_diconnect = 1;
+  u32 qpairs_per_thread = 1;
+  u8 drop_on_disconnect = 1;
   int rv = 0;
 
-  /* Get a line of input. */
-  if (!unformat_user (input, unformat_line_input, line_input))
-    return 0;
-
-  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     {
-      if (unformat (line_input, "queue-size %u", &queue_size))
+      if (unformat (input, "queue-size %u", &queue_size))
        ;
-      else if (unformat (line_input, "on-disconnect drop"))
-       drop_on_diconnect = 1;
-      else if (unformat (line_input, "on-disconnect pass"))
-       drop_on_diconnect = 0;
-      else if (unformat (line_input, "name %s", &name))
+      else if (unformat (input, "queues-per-thread %u", &qpairs_per_thread))
+       ;
+      else if (unformat (input, "on-disconnect drop"))
+       drop_on_disconnect = 1;
+      else if (unformat (input, "on-disconnect pass"))
+       drop_on_disconnect = 0;
+      else if (unformat (input, "name %s", &name))
        ;
       else
        {
@@ -126,8 +87,15 @@ snort_create_instance_command_fn (vlib_main_t *vm, unformat_input_t *input,
       goto done;
     }
 
-  rv = snort_instance_create (vm, (char *) name, min_log2 (queue_size),
-                             drop_on_diconnect);
+  rv = snort_instance_create (
+    vm,
+    &(snort_instance_create_args_t){
+      .log2_queue_sz = min_log2 (queue_size),
+      .drop_on_disconnect = drop_on_disconnect,
+      .drop_bitmap = 1 << DAQ_VERDICT_BLOCK | 1 << DAQ_VERDICT_BLACKLIST,
+      .qpairs_per_thread = qpairs_per_thread,
+    },
+    "%s", name);
 
   switch (rv)
     {
@@ -153,7 +121,6 @@ snort_create_instance_command_fn (vlib_main_t *vm, unformat_input_t *input,
 
 done:
   vec_free (name);
-  unformat_free (line_input);
   return err;
 }
 
@@ -165,77 +132,57 @@ VLIB_CLI_COMMAND (snort_create_instance_command, static) = {
 };
 
 static clib_error_t *
-snort_disconnect_instance_command_fn (vlib_main_t *vm, unformat_input_t *input,
-                                     vlib_cli_command_t *cmd)
+snort_disconnect_client_command_fn (vlib_main_t *vm, unformat_input_t *input,
+                                   vlib_cli_command_t *cmd)
 {
-  unformat_input_t _line_input, *line_input = &_line_input;
   clib_error_t *err = 0;
   u8 *name = 0;
-  snort_instance_t *si;
   int rv = 0;
+  u32 client_index = SNORT_INVALID_CLIENT_INDEX;
 
-  if (!unformat_user (input, unformat_line_input, line_input))
-    return clib_error_return (0, "please specify instance name");
-
-  if (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
-    unformat (line_input, "%s", &name);
+  unformat (input, "%s", &name);
 
   if (!name)
     {
-      err = clib_error_return (0, "please specify instance name");
+      err = clib_error_return (0, "please specify client name");
       goto done;
     }
 
-  si = snort_get_instance_by_name ((char *) name);
-  if (!si)
-    rv = VNET_API_ERROR_NO_SUCH_ENTRY;
-  else
-    rv = snort_instance_disconnect (vm, si->index);
+  rv = snort_client_disconnect (vm, client_index);
 
   switch (rv)
     {
     case 0:
       break;
     case VNET_API_ERROR_NO_SUCH_ENTRY:
-      err = clib_error_return (0, "unknown instance '%s'", name);
-      break;
-    case VNET_API_ERROR_FEATURE_DISABLED:
-      err = clib_error_return (0, "instance '%s' is not connected", name);
-      break;
-    case VNET_API_ERROR_INVALID_VALUE:
-      err = clib_error_return (0, "failed to disconnect a broken client");
+      err = clib_error_return (0, "unknown client '%s'", name);
       break;
     default:
-      err = clib_error_return (0, "snort_instance_disconnect returned %d", rv);
+      err = clib_error_return (0, "snort_client_disconnect returned %d", rv);
       break;
     }
 
 done:
   vec_free (name);
-  unformat_free (line_input);
   return err;
 }
 
-VLIB_CLI_COMMAND (snort_disconnect_instance_command, static) = {
-  .path = "snort disconnect instance",
-  .short_help = "snort disconnect instance <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)
     {
@@ -266,7 +213,6 @@ snort_delete_instance_command_fn (vlib_main_t *vm, unformat_input_t *input,
 
 done:
   vec_free (name);
-  unformat_free (line_input);
   return err;
 }
 
@@ -280,37 +226,25 @@ static clib_error_t *
 snort_attach_command_fn (vlib_main_t *vm, unformat_input_t *input,
                         vlib_cli_command_t *cmd)
 {
-  unformat_input_t _line_input, *line_input = &_line_input;
   vnet_main_t *vnm = vnet_get_main ();
-  snort_main_t *sm = &snort_main;
-  snort_instance_t *si;
   clib_error_t *err = NULL;
   u8 *name = NULL;
-  u8 **names = NULL;
   u32 sw_if_index = ~0;
-  snort_attach_dir_t direction = SNORT_INOUT;
-  u8 is_all_instances = 0;
-  int i;
+  int in = 0, out = 0;
 
-  /* Get a line of input. */
-  if (!unformat_user (input, unformat_line_input, line_input))
-    return 0;
-
-  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     {
-      if (unformat (line_input, "interface %U", unformat_vnet_sw_interface,
-                   vnm, &sw_if_index))
+      if (unformat (input, "interface %U", unformat_vnet_sw_interface, vnm,
+                   &sw_if_index))
+       ;
+      else if (unformat (input, "instance %s", &name))
        ;
-      else if (unformat (line_input, "instance %s", &name))
-       vec_add1 (names, name);
-      else if (unformat (line_input, "all-instances"))
-       is_all_instances = 1;
-      else if (unformat (line_input, "input"))
-       direction = SNORT_INPUT;
-      else if (unformat (line_input, "output"))
-       direction = SNORT_OUTPUT;
-      else if (unformat (line_input, "inout"))
-       direction = SNORT_INOUT;
+      else if (unformat (input, "input"))
+       in = 1;
+      else if (unformat (input, "output"))
+       out = 1;
+      else if (unformat (input, "inout"))
+       in = out = 1;
       else
        {
          err = clib_error_return (0, "unknown input `%U'",
@@ -325,53 +259,25 @@ snort_attach_command_fn (vlib_main_t *vm, unformat_input_t *input,
       goto done;
     }
 
-  if (vec_len (names) == 0 && is_all_instances == 0)
+  if (!name)
     {
-      err = clib_error_return (0, "please specify instances");
+      err = clib_error_return (0, "please specify instance");
       goto done;
     }
 
-  if (is_all_instances)
-    {
-      if (vec_len (sm->instances) == 0)
-       {
-         err = clib_error_return (0, "no snort instances have been created");
-         goto done;
-       }
-
-      pool_foreach (si, sm->instances)
-       {
-         snort_attach_detach_instance (vm, vnm, (char *) si->name,
-                                       sw_if_index, 1 /* is_enable */,
-                                       direction);
-       }
-    }
-  else
-    {
-      vec_foreach_index (i, names)
-       {
-         snort_attach_detach_instance (vm, vnm, (char *) names[i],
-                                       sw_if_index, 1 /* is_enable */,
-                                       direction);
-       }
-    }
+  snort_attach_detach_instance (vm, vnm, (char *) name, sw_if_index,
+                               1 /* is_enable */, in, out);
 
 done:
-  vec_foreach_index (i, names)
-    {
-      vec_free (names[i]);
-    }
-  vec_free (names);
-  unformat_free (line_input);
+  vec_free (name);
   return err;
 }
 
 VLIB_CLI_COMMAND (snort_attach_command, static) = {
   .path = "snort attach",
-  .short_help =
-    "snort attach all-instances|(instance <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,
 };
 
@@ -379,27 +285,17 @@ static clib_error_t *
 snort_detach_command_fn (vlib_main_t *vm, unformat_input_t *input,
                         vlib_cli_command_t *cmd)
 {
-  unformat_input_t _line_input, *line_input = &_line_input;
   vnet_main_t *vnm = vnet_get_main ();
   clib_error_t *err = NULL;
   u8 *name = NULL;
-  u8 **names = NULL;
   u32 sw_if_index = ~0;
-  u8 is_all_instances = 0;
-  int i = 0;
-
-  /* Get a line of input. */
-  if (!unformat_user (input, unformat_line_input, line_input))
-    return 0;
 
-  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     {
-      if (unformat (line_input, "instance %s", &name))
-       vec_add1 (names, name);
-      else if (unformat (line_input, "all-instances"))
-       is_all_instances = 1;
-      else if (unformat (line_input, "interface %U",
-                        unformat_vnet_sw_interface, vnm, &sw_if_index))
+      if (unformat (input, "instance %s", &name))
+       ;
+      else if (unformat (input, "interface %U", unformat_vnet_sw_interface,
+                        vnm, &sw_if_index))
        ;
       else
        {
@@ -415,41 +311,18 @@ snort_detach_command_fn (vlib_main_t *vm, unformat_input_t *input,
       goto done;
     }
 
-  if (vec_len (names) == 0)
-    {
-      /* To maintain backwards compatibility */
-      is_all_instances = 1;
-    }
-
-  if (is_all_instances)
-    {
-      err = snort_detach_all_instance (vm, vnm, sw_if_index);
-    }
-  else
-    {
-      vec_foreach_index (i, names)
-       {
-         snort_attach_detach_instance (vm, vnm, (char *) names[i],
-                                       sw_if_index, 0 /* is_enable */,
-                                       SNORT_INOUT);
-       }
-    }
+  snort_attach_detach_instance (vm, vnm, (char *) name, sw_if_index,
+                               0 /* is_enable */, 1, 1);
 
 done:
-  vec_foreach_index (i, names)
-    {
-      vec_free (names[i]);
-    }
-  vec_free (names);
-  unformat_free (line_input);
+  vec_free (name);
   return err;
 }
 
 VLIB_CLI_COMMAND (snort_detach_command, static) = {
   .path = "snort detach",
-  .short_help =
-    "snort detach all-instances|(instance <name> [instance <name> [...]]) "
-    "interface <if-name> ",
+  .short_help = "snort detach instance <name> [instance <name> [...]] "
+               "interface <if-name> ",
   .function = snort_detach_command_fn,
 };
 
@@ -461,7 +334,47 @@ snort_show_instances_command_fn (vlib_main_t *vm, unformat_input_t *input,
   snort_instance_t *si;
 
   pool_foreach (si, sm->instances)
-    vlib_cli_output (vm, "%U", format_snort_instance, si);
+    {
+      vlib_cli_output (vm, "instance: %s (%u)", si->name, si->index);
+      vlib_cli_output (vm, "  shared memory: size %u fd %d", si->shm_size,
+                      si->shm_fd);
+      vlib_cli_output (vm, "  drop on disconnect: %u", si->drop_on_disconnect);
+
+      vec_foreach_pointer (qp, si->qpairs)
+       {
+         u64 n = 0;
+         u8 *s = 0;
+         vlib_cli_output (vm, "  qpair: %u.%u", qp->qpair_id.thread_id,
+                          qp->qpair_id.queue_id);
+         vlib_cli_output (vm, "    descriptors: total %u free %u",
+                          1 << qp->log2_queue_size, qp->n_free_descs);
+         vlib_cli_output (vm, "    client: %d", qp->client_index);
+         vlib_cli_output (vm, "    cleanup needed: %d", qp->cleanup_needed);
+         vlib_cli_output (vm,
+                          "    enqueue: ring_offset %u event_fd %d head %lu",
+                          (u8 *) qp->enq_ring - (u8 *) si->shm_base,
+                          qp->enq_fd, qp->hdr->enq.head);
+         vlib_cli_output (
+           vm, "    dequeue: ring_offset %u event_fd %d head %lu tail %lu",
+           (u8 *) qp->deq_ring - (u8 *) si->shm_base, qp->deq_fd,
+           qp->hdr->deq.head, qp->deq_tail);
+
+         for (u32 i = 0; i < MAX_DAQ_VERDICT; i++)
+           if (qp->n_packets_by_verdict[i])
+             {
+               n += qp->n_packets_by_verdict[i];
+               s =
+                 format (s, "%s%U: %lu", s ? ", " : "", format_snort_verdict,
+                         i, qp->n_packets_by_verdict[i]);
+             }
+
+         if (s)
+           vlib_cli_output (vm, "    packets processed: %lu (%v)", n, s);
+         else
+           vlib_cli_output (vm, "    packets processed: 0");
+         vec_free (s);
+       }
+    }
 
   return 0;
 }
@@ -478,59 +391,36 @@ snort_show_interfaces_command_fn (vlib_main_t *vm, unformat_input_t *input,
 {
   snort_main_t *sm = &snort_main;
   vnet_main_t *vnm = vnet_get_main ();
-  snort_interface_data_t *interface;
-  snort_instance_t *instance;
-  snort_attach_dir_t direction;
-  u32 instance_index;
+  snort_instance_t *si;
   u32 sw_if_index;
-  u8 is_input;
-  int i, j;
+  u8 *s = 0;
 
-  vlib_cli_output (vm, "interface\tinstances\tdirection");
-  vec_foreach_index (sw_if_index, sm->interfaces)
+  vec_foreach_index (sw_if_index, sm->input_instance_by_interface)
     {
-      interface = vec_elt_at_index (sm->interfaces, sw_if_index);
-
-      /* Loop over input instances and prints all of them (with direction
-       * indicated), then continues over output instances while ignoring
-       * previously printed input instances */
-      for (i = 0; i < vec_len (interface->input_instance_indices) +
-                       vec_len (interface->output_instance_indices);
-          i++)
-       {
-         is_input = i < vec_len (interface->input_instance_indices);
-
-         instance_index =
-           is_input ? interface->input_instance_indices[i] :
-                      interface->output_instance_indices
-                        [i - vec_len (interface->input_instance_indices)];
-
-         /* When printing the output instances ignore the ones present in
-          * input instances as we have already printed them */
-         if (!is_input)
-           {
-             j =
-               vec_search (interface->input_instance_indices, instance_index);
-             if (j != ~0)
-               continue;
-           }
-
-         instance = snort_get_instance_by_index (instance_index);
-         direction = snort_get_instance_direction (instance_index, interface);
-         if (i == 0)
-           {
-             vlib_cli_output (vm, "%U:\t%s\t\t%s",
-                              format_vnet_sw_if_index_name, vnm, sw_if_index,
-                              instance->name,
-                              snort_get_direction_name_by_enum (direction));
-           }
-         else
-           {
-             vlib_cli_output (vm, "\t\t%s\t\t%s", instance->name,
-                              snort_get_direction_name_by_enum (direction));
-           }
-       }
+      si = snort_get_instance_by_index (
+       sm->input_instance_by_interface[sw_if_index]);
+      if (si)
+       s = format (s, "%U:\t%s\n", format_vnet_sw_if_index_name, vnm,
+                   sw_if_index, si->name);
     }
+  if (vec_len (s))
+    vlib_cli_output (vm, "input instances:\n%v", s);
+
+  vec_reset_length (s);
+
+  vec_foreach_index (sw_if_index, sm->output_instance_by_interface)
+    {
+      si = snort_get_instance_by_index (
+       sm->output_instance_by_interface[sw_if_index]);
+      if (si)
+       s = format (s, "%U:\t%s\n", format_vnet_sw_if_index_name, vnm,
+                   sw_if_index, si->name);
+    }
+  if (vec_len (s))
+    vlib_cli_output (vm, "output instances:\n%v", s);
+
+  vec_free (s);
+
   return 0;
 }
 
@@ -545,17 +435,27 @@ snort_show_clients_command_fn (vlib_main_t *vm, unformat_input_t *input,
                               vlib_cli_command_t *cmd)
 {
   snort_main_t *sm = &snort_main;
-  u32 n_clients = pool_elts (sm->clients);
   snort_client_t *c;
-  snort_instance_t *si;
 
-  vlib_cli_output (vm, "number of clients: %d", n_clients);
-  if (n_clients)
-    vlib_cli_output (vm, "client  snort instance");
+  vlib_cli_output (vm, "number of clients: %d", pool_elts (sm->clients));
   pool_foreach (c, sm->clients)
     {
-      si = vec_elt_at_index (sm->instances, c->instance_index);
-      vlib_cli_output (vm, "%6d  %s", c - sm->clients, si->name);
+      snort_client_qpair_t *cqp;
+      vlib_cli_output (vm, "Client %u", c - sm->clients);
+      vlib_cli_output (vm, "  DAQ version: %U", format_snort_daq_version,
+                      c->daq_version);
+      vlib_cli_output (vm, "  number of intstances: %u", c->n_instances);
+      vlib_cli_output (vm, "  mode: %U", format_snort_mode, c->mode);
+      vlib_cli_output (vm, "  inputs:");
+      vec_foreach (cqp, c->qpairs)
+       {
+         snort_instance_t *si;
+         snort_qpair_t *qp;
+         si = snort_get_instance_by_index (cqp->instance_index);
+         qp = *vec_elt_at_index (si->qpairs, cqp->qpair_index);
+         vlib_cli_output (vm, "    %s:%u.%u", si->name,
+                          qp->qpair_id.thread_id, qp->qpair_id.queue_id);
+       }
     }
   return 0;
 }
@@ -565,48 +465,3 @@ VLIB_CLI_COMMAND (snort_show_clients_command, static) = {
   .short_help = "show snort clients",
   .function = snort_show_clients_command_fn,
 };
-
-static clib_error_t *
-snort_mode_polling_command_fn (vlib_main_t *vm, unformat_input_t *input,
-                              vlib_cli_command_t *cmd)
-{
-  snort_set_node_mode (vm, VLIB_NODE_STATE_POLLING);
-  return 0;
-}
-
-static clib_error_t *
-snort_mode_interrupt_command_fn (vlib_main_t *vm, unformat_input_t *input,
-                                vlib_cli_command_t *cmd)
-{
-  snort_set_node_mode (vm, VLIB_NODE_STATE_INTERRUPT);
-  return 0;
-}
-
-VLIB_CLI_COMMAND (snort_mode_polling_command, static) = {
-  .path = "snort mode polling",
-  .short_help = "snort mode polling|interrupt",
-  .function = snort_mode_polling_command_fn,
-};
-
-VLIB_CLI_COMMAND (snort_mode_interrupt_command, static) = {
-  .path = "snort mode interrupt",
-  .short_help = "snort mode polling|interrupt",
-  .function = snort_mode_interrupt_command_fn,
-};
-
-static clib_error_t *
-snort_show_mode_command_fn (vlib_main_t *vm, unformat_input_t *input,
-                           vlib_cli_command_t *cmd)
-{
-  snort_main_t *sm = &snort_main;
-  char *mode =
-    sm->input_mode == VLIB_NODE_STATE_POLLING ? "polling" : "interrupt";
-  vlib_cli_output (vm, "input mode: %s", mode);
-  return 0;
-}
-
-VLIB_CLI_COMMAND (snort_show_mode_command, static) = {
-  .path = "show snort mode",
-  .short_help = "show snort mode",
-  .function = snort_show_mode_command_fn,
-};
diff --git a/src/plugins/snort/daq/config.c b/src/plugins/snort/daq/config.c
new file mode 100644 (file)
index 0000000..ba0fba1
--- /dev/null
@@ -0,0 +1,54 @@
+/* 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;
+}
diff --git a/src/plugins/snort/daq/daq_vpp.h b/src/plugins/snort/daq/daq_vpp.h
new file mode 100644 (file)
index 0000000..720fedd
--- /dev/null
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+#ifndef __DAQ_VPP_H__
+#define __DAQ_VPP_H__
+
+#include <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__ */
diff --git a/src/plugins/snort/daq/daq_vpp_shared.h b/src/plugins/snort/daq/daq_vpp_shared.h
new file mode 100644 (file)
index 0000000..783cc50
--- /dev/null
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2021 Cisco Systems, Inc.
+ */
+
+#ifndef __DAQ_VPP_SHARED_H__
+#define __DAQ_VPP_SHARED_H__
+
+#include <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__ */
diff --git a/src/plugins/snort/daq/main.c b/src/plugins/snort/daq/main.c
new file mode 100644 (file)
index 0000000..6d7c7b6
--- /dev/null
@@ -0,0 +1,763 @@
+/* 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,
+};
diff --git a/src/plugins/snort/daq/socket.c b/src/plugins/snort/daq/socket.c
new file mode 100644 (file)
index 0000000..b2c7126
--- /dev/null
@@ -0,0 +1,302 @@
+/* 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;
+}
diff --git a/src/plugins/snort/daq_vpp.c b/src/plugins/snort/daq_vpp.c
deleted file mode 100644 (file)
index 6fc0bf5..0000000
+++ /dev/null
@@ -1,723 +0,0 @@
-/* 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,
-};
diff --git a/src/plugins/snort/daq_vpp.h b/src/plugins/snort/daq_vpp.h
deleted file mode 100644 (file)
index ebec554..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/* SPDX-License-Identifier: Apache-2.0
- * Copyright(c) 2021 Cisco Systems, Inc.
- */
-
-#ifndef __DAQ_VPP_H__
-#define __DAQ_VPP_H__
-
-#include <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__ */
index bc301f6..8511127 100644 (file)
 #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",
 };
index ae04c58..03b0d64 100644 (file)
 #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,
@@ -257,3 +289,141 @@ VLIB_REGISTER_NODE (snort_enq_node) = {
   .n_errors = ARRAY_LEN (snort_enq_error_strings),
   .error_strings = snort_enq_error_strings,
 };
+
+static_always_inline uword
+snort_arc_input (vlib_main_t *vm, vlib_node_runtime_t *node,
+                vlib_frame_t *frame, int is_output)
+{
+  snort_main_t *sm = &snort_main;
+  u16 *instance_by_interface = is_output ? sm->output_instance_by_interface :
+                                          sm->input_instance_by_interface;
+  u32 *buffer_indices = vlib_frame_vector_args (frame), *bi = buffer_indices;
+  u32 n_pkts = frame->n_vectors, n_left = n_pkts, n_total_left = n_pkts;
+  u16 instance_indices[VLIB_FRAME_SIZE], *ii = instance_indices;
+  daq_vpp_pkt_metadata_t metadata = {
+    .flags = is_output ? 0 : DAQ_PKT_FLAG_PRE_ROUTING,
+  };
+
+  for (; n_left >= 8; n_left -= 4, bi += 4, ii += 4)
+    {
+      vlib_buffer_t *b0, *b1, *b2, *b3;
+
+      clib_prefetch_load (vlib_get_buffer (vm, bi[4]));
+      b0 = vlib_get_buffer (vm, bi[0]);
+      clib_prefetch_load (vlib_get_buffer (vm, bi[5]));
+      b1 = vlib_get_buffer (vm, bi[1]);
+      clib_prefetch_load (vlib_get_buffer (vm, bi[6]));
+      b2 = vlib_get_buffer (vm, bi[2]);
+      clib_prefetch_load (vlib_get_buffer (vm, bi[7]));
+      b3 = vlib_get_buffer (vm, bi[3]);
+
+      ii[0] = instance_by_interface[vnet_buffer (b0)->sw_if_index[VLIB_RX]];
+      *snort_get_buffer_metadata (b0) = metadata;
+      ii[1] = instance_by_interface[vnet_buffer (b1)->sw_if_index[VLIB_RX]];
+      *snort_get_buffer_metadata (b1) = metadata;
+      ii[2] = instance_by_interface[vnet_buffer (b2)->sw_if_index[VLIB_RX]];
+      *snort_get_buffer_metadata (b2) = metadata;
+      ii[3] = instance_by_interface[vnet_buffer (b3)->sw_if_index[VLIB_RX]];
+      *snort_get_buffer_metadata (b3) = metadata;
+    }
+
+  for (; n_left; n_left -= 1, bi += 1, ii += 1)
+    {
+      vlib_buffer_t *b0;
+
+      b0 = vlib_get_buffer (vm, bi[0]);
+      ii[0] = instance_by_interface[vnet_buffer (b0)->sw_if_index[VLIB_RX]];
+      *snort_get_buffer_metadata (b0) = metadata;
+    }
+
+  if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
+    {
+      for (u32 i = 0; i < n_pkts; i++)
+       {
+         vlib_buffer_t *b = vlib_get_buffer (vm, buffer_indices[i]);
+         if (b->flags & VLIB_BUFFER_IS_TRACED)
+           {
+             snort_arc_input_trace_t *t =
+               vlib_add_trace (vm, node, b, sizeof (*t));
+             t->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
+             t->instance = instance_indices[i];
+           }
+       }
+    }
+
+  while (n_total_left)
+    {
+      vlib_next_frame_t *nf;
+      vlib_frame_t *f;
+      snort_enq_scalar_args_t *sa;
+      u32 *to_next, n_left_to_next, *not_now = buffer_indices;
+      u16 next_index = 0; /* snort_enq */
+      u16 instance_index = instance_indices[0];
+      snort_instance_t *si = pool_elt_at_index (sm->instances, instance_index);
+      u16 n_enq;
+
+      vlib_get_new_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      to_next++[0] = buffer_indices[0];
+      n_enq = 1;
+
+      for (u32 i = 1; i < n_total_left; i++)
+       if (instance_indices[i] == instance_index)
+         {
+           to_next++[0] = buffer_indices[i];
+           n_enq++;
+         }
+       else
+         not_now++[0] = buffer_indices[i];
+
+      nf = vlib_node_runtime_get_next_frame (vm, node, next_index);
+      f = vlib_get_frame (vm, nf->frame);
+      sa = vlib_frame_scalar_args (f);
+      *sa = (snort_enq_scalar_args_t){
+       .instance_index = instance_index,
+       .dequeue_node_next_index = is_output ?
+                                    si->ip4_output_dequeue_node_next_index :
+                                    si->ip4_input_dequeue_node_next_index,
+       .use_rewrite_length_offset = is_output ? 1 : 0,
+      };
+      vlib_frame_no_append (f);
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next - n_enq);
+      n_total_left -= n_enq;
+    }
+
+  return n_pkts;
+}
+
+VLIB_NODE_FN (snort_ip4_input_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+  return snort_arc_input (vm, node, frame, 0 /* is_output */);
+}
+
+VLIB_REGISTER_NODE (snort_ip4_input_node) = {
+  .name = "snort-ip4-input",
+  .vector_size = sizeof (u32),
+  .format_trace = format_snort_arc_input_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .next_nodes = {
+      [0] = "snort-enq",
+  },
+  .n_next_nodes = 1,
+};
+
+VLIB_NODE_FN (snort_ip4_output_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+  return snort_arc_input (vm, node, frame, 1 /* is_output */);
+}
+
+VLIB_REGISTER_NODE (snort_ip4_output_node) = {
+  .name = "snort-ip4-output",
+  .vector_size = sizeof (u32),
+  .format_trace = format_snort_arc_input_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .next_nodes = {
+      [0] = "snort-enq",
+  },
+  .n_next_nodes = 1,
+};
diff --git a/src/plugins/snort/export.h b/src/plugins/snort/export.h
new file mode 100644 (file)
index 0000000..e1f3edb
--- /dev/null
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2021 Cisco Systems, Inc.
+ * Copyright(c) 2024 Arm Limited
+ */
+
+#ifndef __snort_export_h__
+#define __snort_export_h__
+
+#include <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__ */
diff --git a/src/plugins/snort/format.c b/src/plugins/snort/format.c
new file mode 100644 (file)
index 0000000..9228007
--- /dev/null
@@ -0,0 +1,114 @@
+
+/* 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]);
+}
diff --git a/src/plugins/snort/interface.c b/src/plugins/snort/interface.c
new file mode 100644 (file)
index 0000000..1cd01a1
--- /dev/null
@@ -0,0 +1,217 @@
+/* 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;
+}
index c87ecfd..583c555 100644 (file)
@@ -1,27 +1,15 @@
 /* SPDX-License-Identifier: Apache-2.0
- * Copyright(c) 2021 Cisco Systems, Inc.
+ * Copyright(c) 2025 Cisco Systems, Inc.
  * Copyright(c) 2024 Arm Limited
  */
 
 #include <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;
@@ -30,372 +18,62 @@ VLIB_REGISTER_LOG_CLASS (snort_log, static) = {
   .class_name = "snort",
 };
 
-#define log_debug(fmt, ...) vlib_log_debug (snort_log.class, fmt, __VA_ARGS__)
-#define log_err(fmt, ...)   vlib_log_err (snort_log.class, fmt, __VA_ARGS__)
-
-snort_main_t *
-snort_get_main ()
-{
-  return &snort_main;
-}
-
-static void
-snort_client_disconnect (clib_file_t *uf)
-{
-  vlib_main_t *vm = vlib_get_main ();
-  snort_qpair_t *qp;
-  snort_main_t *sm = &snort_main;
-  snort_client_t *c = pool_elt_at_index (sm->clients, uf->private_data);
-
-  if (c->instance_index != ~0)
-    {
-      snort_per_thread_data_t *ptd =
-       vec_elt_at_index (sm->per_thread_data, vm->thread_index);
-      snort_instance_t *si =
-       pool_elt_at_index (sm->instances, c->instance_index);
-      vec_foreach (qp, si->qpairs)
-       __atomic_store_n (&qp->ready, 1, __ATOMIC_RELEASE);
-
-      si->client_index = ~0;
-      clib_interrupt_set (ptd->interrupts, uf->private_data);
-      vlib_node_set_interrupt_pending (vm, snort_deq_node.index);
-    }
-
-  clib_file_del (&file_main, uf);
-  clib_socket_close (&c->socket);
-  pool_put (sm->clients, c);
-}
-
-int
-snort_instance_disconnect (vlib_main_t *vm, u32 instance_index)
-{
-  snort_main_t *sm = &snort_main;
-  snort_instance_t *si;
-  snort_client_t *client;
-  clib_file_main_t *fm = &file_main;
-  clib_file_t *uf = 0;
-  int rv = 0;
-
-  si = snort_get_instance_by_index (instance_index);
-  if (!si)
-    return VNET_API_ERROR_NO_SUCH_ENTRY;
-  if (si->client_index == ~0)
-    return VNET_API_ERROR_FEATURE_DISABLED;
-
-  client = pool_elt_at_index (sm->clients, si->client_index);
-  uf = clib_file_get (fm, client->file_index);
-  if (uf)
-    snort_client_disconnect (uf);
-  else
-    {
-      log_err ("failed to disconnect a broken client from"
-              "instance '%s'",
-              si->name);
-      rv = VNET_API_ERROR_INVALID_VALUE;
-    }
-
-  return rv;
-}
-
-const char *
-snort_get_direction_name_by_enum (snort_attach_dir_t dir)
-{
-  switch (dir)
-    {
-    case SNORT_INPUT:
-      return "input";
-    case SNORT_OUTPUT:
-      return "output";
-    case SNORT_INOUT:
-      return "inout";
-    default:
-      return "none";
-    }
-}
-
-/* Returns SNORT_INVALID if the instance is not attached */
-snort_attach_dir_t
-snort_get_instance_direction (u32 instance_index,
-                             snort_interface_data_t *interface)
-{
-  snort_attach_dir_t direction = SNORT_INVALID;
-  int i;
-  i = vec_search (interface->input_instance_indices, instance_index);
-  if (i != ~0)
-    direction = direction | SNORT_INPUT;
-  i = vec_search (interface->output_instance_indices, instance_index);
-  if (i != ~0)
-    direction = direction | SNORT_OUTPUT;
-  return direction;
-}
-
-snort_instance_t *
-snort_get_instance_by_name (char *name)
-{
-  snort_main_t *sm = &snort_main;
-  uword *p;
-  if ((p = hash_get_mem (sm->instance_by_name, name)) == 0)
-    return 0;
-
-  return vec_elt_at_index (sm->instances, p[0]);
-}
-
-snort_instance_t *
-snort_get_instance_by_index (u32 instance_index)
-{
-  snort_main_t *sm = &snort_main;
-
-  if (pool_is_free_index (sm->instances, instance_index))
-    return 0;
-  return pool_elt_at_index (sm->instances, instance_index);
-}
-
-static clib_error_t *
-snort_conn_fd_read_ready (clib_file_t *uf)
-{
-  vlib_main_t *vm = vlib_get_main ();
-  snort_main_t *sm = &snort_main;
-  snort_client_t *c = pool_elt_at_index (sm->clients, uf->private_data);
-  vlib_buffer_pool_t *bp;
-  snort_instance_t *si;
-  snort_qpair_t *qp;
-  snort_client_msg_queue_elt *e;
-  clib_error_t *err;
-  daq_vpp_msg_t msg;
-  char *name;
-  u8 *base;
-
-  log_debug ("fd_read_ready: client %u", uf->private_data);
-
-  if ((err = clib_socket_recvmsg (&c->socket, &msg, sizeof (msg), 0, 0)))
-    {
-      log_err ("client recvmsg error: %U", format_clib_error, err);
-      snort_client_disconnect (uf);
-      clib_error_free (err);
-      return 0;
-    }
-
-  if (msg.type != DAQ_VPP_MSG_TYPE_HELLO)
-    {
-      log_err ("unexpeced message recieved from client", 0);
-      snort_client_disconnect (uf);
-      return 0;
-    }
-
-  msg.hello.inst_name[DAQ_VPP_INST_NAME_LEN - 1] = 0;
-  name = msg.hello.inst_name;
-
-  log_debug ("fd_read_ready: connect instance %s", name);
-
-  if ((si = snort_get_instance_by_name (name)) == 0)
-    {
-      log_err ("unknown instance '%s' requested by client", name);
-      snort_client_disconnect (uf);
-      return 0;
-    }
-
-  vec_foreach (qp, si->qpairs)
-    {
-      u32 ready = __atomic_load_n (&qp->ready, __ATOMIC_ACQUIRE);
-      if (!ready)
-       {
-         log_err ("instance '%s' is not ready to accept connections", name);
-         snort_client_disconnect (uf);
-         return 0;
-       }
-      snort_freelist_init (qp->freelist);
-      *qp->enq_head = *qp->deq_head = qp->next_desc = 0;
-    }
-
-  base = (u8 *) si->shm_base;
-
-  if (si->client_index != ~0)
-    {
-      log_err ("client already connected to instance '%s'", name);
-      snort_client_disconnect (uf);
-      return 0;
-    }
-  si->client_index = uf->private_data;
-  c->instance_index = si->index;
-
-  log_debug ("fd_read_ready: connect instance index %u", si->index);
-
-  clib_fifo_add2 (c->msg_queue, e);
-  e->msg.type = DAQ_VPP_MSG_TYPE_CONFIG;
-  e->msg.config.num_bpools = vec_len (vm->buffer_main->buffer_pools);
-  e->msg.config.num_qpairs = vec_len (si->qpairs);
-  e->msg.config.shm_size = si->shm_size;
-  e->fds[0] = si->shm_fd;
-  e->n_fds = 1;
-
-  vec_foreach (bp, vm->buffer_main->buffer_pools)
-    {
-      vlib_physmem_map_t *pm;
-      pm = vlib_physmem_get_map (vm, bp->physmem_map_index);
-      clib_fifo_add2 (c->msg_queue, e);
-      e->msg.type = DAQ_VPP_MSG_TYPE_BPOOL;
-      e->msg.bpool.size = pm->n_pages << pm->log2_page_size;
-      e->fds[0] = pm->fd;
-      e->n_fds = 1;
-    }
-
-  vec_foreach (qp, si->qpairs)
-    {
-      clib_fifo_add2 (c->msg_queue, e);
-      e->msg.type = DAQ_VPP_MSG_TYPE_QPAIR;
-      e->msg.qpair.log2_queue_size = qp->log2_queue_size;
-      e->msg.qpair.desc_table_offset = (u8 *) qp->descriptors - base;
-      e->msg.qpair.enq_ring_offset = (u8 *) qp->enq_ring - base;
-      e->msg.qpair.deq_ring_offset = (u8 *) qp->deq_ring - base;
-      e->msg.qpair.enq_head_offset = (u8 *) qp->enq_head - base;
-      e->msg.qpair.deq_head_offset = (u8 *) qp->deq_head - base;
-      e->fds[0] = qp->enq_fd;
-      e->fds[1] = qp->deq_fd;
-      e->n_fds = 2;
-    }
-
-  clib_file_set_data_available_to_write (&file_main, c->file_index, 1);
-  return 0;
-}
-
-static clib_error_t *
-snort_conn_fd_write_ready (clib_file_t *uf)
-{
-  snort_main_t *sm = &snort_main;
-  snort_client_t *c = pool_elt_at_index (sm->clients, uf->private_data);
-  snort_client_msg_queue_elt *e;
-
-  log_debug ("fd_write_ready: client %u", uf->private_data);
-  clib_fifo_sub2 (c->msg_queue, e);
-
-  if (clib_fifo_elts (c->msg_queue) == 0)
-    clib_file_set_data_available_to_write (&file_main, c->file_index, 0);
-
-  return clib_socket_sendmsg (&c->socket, &e->msg, sizeof (*e), e->fds,
-                             e->n_fds);
-}
-
-clib_error_t *
-snort_conn_fd_error (clib_file_t *uf)
-{
-  log_debug ("fd_error: client %u", uf->private_data);
-  return 0;
-}
+vlib_node_registration_t snort_deq_node;
 
 static clib_error_t *
 snort_deq_ready (clib_file_t *uf)
 {
   vlib_main_t *vm = vlib_get_main ();
-  snort_main_t *sm = &snort_main;
-  snort_per_thread_data_t *ptd =
-    vec_elt_at_index (sm->per_thread_data, vm->thread_index);
+  snort_qpair_t *qp = (snort_qpair_t *) uf->private_data;
   u64 counter;
-  ssize_t bytes_read;
-
-  bytes_read = read (uf->file_descriptor, &counter, sizeof (counter));
-  if (bytes_read < 0)
-    {
-      return clib_error_return (0, "client closed socket");
-    }
-
-  if (bytes_read < sizeof (counter))
-    {
-      return clib_error_return (0, "unexpected truncated read");
-    }
+  ssize_t __clib_unused bytes_read;
 
-  clib_interrupt_set (ptd->interrupts, uf->private_data);
-  vlib_node_set_interrupt_pending (vm, snort_deq_node.index);
+  bytes_read = read ((int) uf->file_descriptor, &counter, sizeof (counter));
+  __atomic_store_n (&qp->hdr->deq.interrupt_pending, 0, __ATOMIC_RELAXED);
+  vlib_node_set_interrupt_pending (vm, qp->dequeue_node_index);
   return 0;
 }
 
-static clib_error_t *
-snort_conn_fd_accept_ready (clib_file_t *uf)
-{
-  snort_main_t *sm = &snort_main;
-  snort_client_t *c;
-  clib_socket_t *s;
-  clib_error_t *err = 0;
-  clib_file_t t = { 0 };
-
-  pool_get_zero (sm->clients, c);
-  c->instance_index = ~0;
-  s = &c->socket;
-
-  if ((err = clib_socket_accept (sm->listener, s)))
-    {
-      log_err ("%U", format_clib_error, err);
-      pool_put (sm->clients, c);
-      return err;
-    }
-
-  t.read_function = snort_conn_fd_read_ready;
-  t.write_function = snort_conn_fd_write_ready;
-  t.error_function = snort_conn_fd_error;
-  t.file_descriptor = s->fd;
-  t.private_data = c - sm->clients;
-  t.description = format (0, "snort client");
-  c->file_index = clib_file_add (&file_main, &t);
-
-  log_debug ("snort_conn_fd_accept_ready: client %u", t.private_data);
-  return 0;
-}
+static char *snort_deq_error_strings[] = {
+#define _(sym, string) string,
+  foreach_snort_deq_error
+#undef _
+};
 
-static clib_error_t *
-snort_listener_init (vlib_main_t *vm)
+__clib_export int
+snort_instance_get_index_by_name (vlib_main_t *vm, const char *name,
+                                 snort_instance_index_t *instance_index)
 {
-  snort_main_t *sm = &snort_main;
-  clib_error_t *err;
-  clib_file_t t = { 0 };
-  clib_socket_t *s;
-
-  if (sm->listener)
-    return 0;
-
-  s = clib_mem_alloc (sizeof (clib_socket_t));
-  clib_memset (s, 0, sizeof (clib_socket_t));
-  s->config = (char *) sm->socket_name;
-  s->is_server = 1;
-  s->allow_group_write = 1;
-  s->is_seqpacket = 1;
-  s->passcred = 1;
-
-  if ((err = clib_socket_init (s)))
-    {
-      clib_mem_free (s);
-      return err;
-    }
+  snort_instance_t *si = snort_get_instance_by_name ((char *) name);
 
-  t.read_function = snort_conn_fd_accept_ready;
-  t.file_descriptor = s->fd;
-  t.description = format (0, "snort listener %s", s->config);
-  log_debug ("%v", t.description);
-  clib_file_add (&file_main, &t);
+  if (!si)
+    return VNET_API_ERROR_NO_SUCH_ENTRY;
 
-  sm->listener = s;
+  *instance_index = si->index;
 
   return 0;
 }
 
-int
-snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz,
-                      u8 drop_on_disconnect)
+__clib_export int
+snort_instance_create (vlib_main_t *vm, snort_instance_create_args_t *args,
+                      char *fmt, ...)
+
 {
   vlib_thread_main_t *tm = vlib_get_thread_main ();
   snort_main_t *sm = &snort_main;
   snort_instance_t *si;
-  u32 index, i;
-  u8 *base = CLIB_MEM_VM_MAP_FAILED;
-  u32 size;
-  int fd = -1;
-  u32 qpair_mem_sz = 0;
-  u32 qsz = 1 << log2_queue_sz;
+  va_list va;
+  u32 size, index, qpair_mem_sz;
+  u8 *base = CLIB_MEM_VM_MAP_FAILED, *name;
+  int rv = 0, fd = -1;
+  u32 qsz = 1 << args->log2_queue_sz;
+  u32 qpairs_per_thread, total_qpairs, n_threads = tm->n_vlib_mains;
   u8 align = CLIB_CACHE_LINE_BYTES;
-  int rv = 0;
 
   if (sm->listener == 0)
     {
       clib_error_t *err;
-      err = snort_listener_init (vm);
+      err = snort_listener_init ();
       if (err)
        {
          log_err ("listener init failed: %U", format_clib_error, err);
@@ -404,20 +82,28 @@ snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz,
        }
     }
 
-  if (snort_get_instance_by_name (name))
+  va_start (va, fmt);
+  name = va_format (0, fmt, &va);
+  va_end (va);
+  vec_add1 (name, 0);
+
+  if (snort_get_instance_by_name ((char *) name))
     return VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
 
-  /* descriptor table */
-  qpair_mem_sz += round_pow2 (qsz * sizeof (daq_vpp_desc_t), align);
+  qpairs_per_thread = clib_max (1, args->qpairs_per_thread);
+  total_qpairs = qpairs_per_thread * n_threads;
 
-  /* enq and deq ring */
-  qpair_mem_sz += 2 * round_pow2 (qsz * sizeof (u32), align);
+  /* header and descriptor table */
+  qpair_mem_sz = round_pow2 (
+    sizeof (daq_vpp_qpair_header_t) + qsz * sizeof (daq_vpp_desc_t), align);
 
-  /* enq and deq head pointer */
-  qpair_mem_sz += 2 * round_pow2 (sizeof (u32), align);
+  /* enq and deq ring */
+  qpair_mem_sz += 2 * round_pow2 (qsz * sizeof (daq_vpp_desc_index_t), align);
 
-  size = round_pow2 ((uword) tm->n_vlib_mains * qpair_mem_sz,
+  /* total size of shared memory */
+  size = round_pow2 ((uword) total_qpairs * qpair_mem_sz,
                     clib_mem_get_page_size ());
+
   fd = clib_mem_vm_create_fd (CLIB_MEM_PAGE_SZ_DEFAULT, "snort instance %s",
                              name);
 
@@ -443,66 +129,125 @@ snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz,
 
   pool_get_zero (sm->instances, si);
   si->index = si - sm->instances;
-  si->client_index = ~0;
   si->shm_base = base;
+  si->drop_bitmap = args->drop_bitmap;
   si->shm_fd = fd;
   si->shm_size = size;
-  si->name = format (0, "%s%c", name, 0);
-  si->drop_on_disconnect = drop_on_disconnect;
+  si->name = name;
+  si->drop_on_disconnect = args->drop_on_disconnect;
+  si->qpairs_per_thread = qpairs_per_thread;
+  si->ip4_hash_fn = vnet_hash_default_function (VNET_HASH_FN_TYPE_IP4);
+
   index = si - sm->instances;
   hash_set_mem (sm->instance_by_name, si->name, index);
 
-  log_debug ("instnce '%s' createed with fd %d at %p, len %u", name, fd, base,
-            size);
-
-  vec_validate_aligned (sm->per_thread_data, tm->n_vlib_mains - 1,
-                       CLIB_CACHE_LINE_BYTES);
-  vec_validate_aligned (si->qpairs, tm->n_vlib_mains - 1,
-                       CLIB_CACHE_LINE_BYTES);
-
-  for (int i = 0; i < tm->n_vlib_mains; i++)
+  if (vec_len (sm->snort_deleted_deq_nodes) > 0)
     {
-      snort_qpair_t *qp = vec_elt_at_index (si->qpairs, i);
-      snort_per_thread_data_t *ptd = vec_elt_at_index (sm->per_thread_data, i);
-      clib_file_t t = { 0 };
-
-      qp->log2_queue_size = log2_queue_sz;
-      qp->descriptors = (void *) base;
-      base += round_pow2 (qsz * sizeof (daq_vpp_desc_t), align);
-      qp->enq_ring = (void *) base;
-      base += round_pow2 (qsz * sizeof (u32), align);
-      qp->deq_ring = (void *) base;
-      base += round_pow2 (qsz * sizeof (u32), align);
-      qp->enq_head = (void *) base;
-      base += round_pow2 (sizeof (u32), align);
-      qp->deq_head = (void *) base;
-      base += round_pow2 (sizeof (u32), align);
-      qp->enq_fd = eventfd (0, EFD_NONBLOCK);
-      qp->deq_fd = eventfd (0, EFD_NONBLOCK);
-      vec_validate_aligned (qp->buffer_indices, qsz - 1,
-                           CLIB_CACHE_LINE_BYTES);
-      vec_validate_aligned (qp->next_indices, qsz - 1, CLIB_CACHE_LINE_BYTES);
-      clib_memset_u32 (qp->buffer_indices, ~0, qsz);
-
-      /* pre-populate freelist */
-      vec_validate_aligned (qp->freelist, qsz - 1, CLIB_CACHE_LINE_BYTES);
-      snort_freelist_init (qp->freelist);
-
-      /* listen on dequeue events */
-      t.read_function = snort_deq_ready;
-      t.file_descriptor = qp->deq_fd;
-      t.private_data = si->index;
-      t.description =
-       format (0, "snort dequeue for instance '%s' qpair %u", si->name, i);
-      qp->deq_fd_file_index = clib_file_add (&file_main, &t);
-      qp->ready = 1;
-      clib_file_set_polling_thread (&file_main, qp->deq_fd_file_index, i);
-      clib_interrupt_resize (&ptd->interrupts, vec_len (sm->instances));
+      snort_deleted_deq_node_t *dn = vec_end (sm->snort_deleted_deq_nodes) - 1;
+      si->dequeue_node_index = dn->dequeue_node_index;
+      vlib_node_rename (vm, si->dequeue_node_index, "snort-deq-%s", si->name);
+      foreach_vlib_main ()
+       {
+         vlib_node_runtime_t *nrt;
+         snort_deq_runtime_data_t *rt;
+         nrt = vlib_node_get_runtime (this_vlib_main, dn->dequeue_node_index);
+         vlib_node_runtime_perf_counter (this_vlib_main, nrt, 0, 0, 0,
+                                         VLIB_NODE_RUNTIME_PERF_RESET);
+         rt = vlib_node_get_runtime_data (this_vlib_main,
+                                          dn->dequeue_node_index);
+         ASSERT (rt->is_deleted == 1);
+         rt->is_deleted = 0;
+         rt->instance_index = index;
+       }
+
+      vlib_node_set_state (vm, si->dequeue_node_index,
+                          VLIB_NODE_STATE_INTERRUPT);
+      vec_dec_len (sm->snort_deleted_deq_nodes, 1);
     }
+  else
+    {
+      snort_deq_runtime_data_t rt = {};
+      rt.instance_index = index;
+      rt.is_deleted = 0;
+
+      vlib_node_registration_t snort_deq_reg = {
+       .sibling_of = "snort-enq",
+       .type = VLIB_NODE_TYPE_SCHED,
+       .state = VLIB_NODE_STATE_INTERRUPT,
+       .vector_size = sizeof (u32),
+       .runtime_data = &rt,
+       .runtime_data_bytes = sizeof (snort_deq_runtime_data_t),
+       .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED,
+       .node_fn_registrations = snort_deq_node.node_fn_registrations,
+       .format_trace = format_snort_deq_trace,
+       .error_strings = snort_deq_error_strings,
+       .n_errors = ARRAY_LEN (snort_deq_error_strings),
+      };
+
+      si->dequeue_node_index =
+       vlib_register_node (vm, &snort_deq_reg, "snort-deq-%s", si->name);
+    }
+
+  si->ip4_input_dequeue_node_next_index = vlib_node_add_named_next (
+    vm, si->dequeue_node_index, "snort-ip4-input-next");
+  si->ip4_output_dequeue_node_next_index = vlib_node_add_named_next (
+    vm, si->dequeue_node_index, "snort-ip4-output-next");
+  vlib_worker_thread_node_runtime_update ();
+
+  log_debug ("instnce '%s' created with fd %d at %p, len %u", name, fd, base,
+            size);
 
-  for (i = 0; i < vlib_get_n_threads (); i++)
-    vlib_node_set_state (vlib_get_main_by_index (i), snort_deq_node.index,
-                        sm->input_mode);
+  vec_validate_aligned (si->qpairs, total_qpairs - 1, CLIB_CACHE_LINE_BYTES);
+
+  for (int thread_id = 0; thread_id < n_threads; thread_id++)
+    for (int queue_id = 0; queue_id < qpairs_per_thread; queue_id++)
+      {
+       snort_qpair_t *qp;
+       u32 sz = sizeof (snort_qpair_t) + qsz * sizeof (snort_qpair_entry_t);
+
+       qp = clib_mem_alloc_aligned (sz, CLIB_CACHE_LINE_BYTES);
+       si->qpairs[thread_id * qpairs_per_thread + queue_id] = qp;
+
+       *qp = (snort_qpair_t){
+         .client_index = SNORT_INVALID_CLIENT_INDEX,
+         .dequeue_node_index = si->dequeue_node_index,
+         .log2_queue_size = args->log2_queue_sz,
+         .qpair_id.thread_id = thread_id,
+         .qpair_id.queue_id = queue_id,
+         .enq_fd = eventfd (0, EFD_NONBLOCK),
+         .deq_fd = eventfd (0, EFD_NONBLOCK),
+       };
+
+       qp->hdr = (void *) base;
+       base += round_pow2 (sizeof (daq_vpp_qpair_header_t) +
+                             qsz * sizeof (daq_vpp_desc_t),
+                           align);
+       qp->enq_ring = (void *) base;
+       base += round_pow2 (qsz * sizeof (daq_vpp_desc_index_t), align);
+       qp->deq_ring = (void *) base;
+       base += round_pow2 (qsz * sizeof (daq_vpp_desc_index_t), align);
+
+       for (u32 i = 0; i < qsz; i++)
+         qp->entries[i].buffer_index = VLIB_BUFFER_INVALID_INDEX;
+
+       qp->hdr->enq.cookie = DAQ_VPP_COOKIE;
+       snort_qpair_init (qp);
+
+       /* listen on dequeue events */
+       qp->deq_fd_file_index = clib_file_add (
+         &file_main, &(clib_file_t){
+                       .read_function = snort_deq_ready,
+                       .file_descriptor = qp->deq_fd,
+                       .description = format (
+                         0, "snort dequeue for instance '%s' qpair %u.%u",
+                         si->name, thread_id, queue_id),
+                       .polling_thread_index = thread_id,
+                       .private_data = (u64) qp,
+                     });
+
+       log_debug ("qpair %u.%u created at %p size %u enq_fd %u deq_fd %d",
+                  thread_id, queue_id, qp, qsz, qp->enq_fd, qp->deq_fd);
+      }
 
 done:
   if (rv)
@@ -511,284 +256,59 @@ done:
        clib_mem_vm_unmap (base);
       if (fd != -1)
        close (fd);
+      vec_free (name);
     }
   return rv;
 }
 
-static void
-snort_vnet_feature_enable_disable (snort_attach_dir_t snort_dir,
-                                  u32 sw_if_index, int is_enable)
-{
-  u32 fa_data;
-  switch (snort_dir)
-    {
-    case SNORT_INPUT:
-      fa_data = SNORT_INPUT;
-      vnet_feature_enable_disable ("ip4-unicast", "snort-enq", sw_if_index,
-                                  is_enable, &fa_data, sizeof (fa_data));
-      break;
-    case SNORT_OUTPUT:
-      fa_data = SNORT_OUTPUT;
-      vnet_feature_enable_disable ("ip4-output", "snort-enq", sw_if_index,
-                                  is_enable, &fa_data, sizeof (fa_data));
-      break;
-    default:
-      vlib_log_err (snort_log.class,
-                   "Invalid direction given to enable/disable snort");
-      break;
-    }
-}
-
-int
-snort_interface_enable_disable (vlib_main_t *vm, char *instance_name,
-                               u32 sw_if_index, int is_enable,
-                               snort_attach_dir_t snort_dir)
-{
-  snort_main_t *sm = &snort_main;
-  vnet_main_t *vnm = vnet_get_main ();
-  vnet_sw_interface_t *software_interface =
-    vnet_get_sw_interface (vnm, sw_if_index);
-  snort_interface_data_t *interface_data;
-  snort_instance_t *instance;
-  u32 **instance_indices;
-  u32 instance_index;
-  const snort_attach_dir_t dirs[2] = { SNORT_INPUT, SNORT_OUTPUT };
-  int rv = 0;
-  int index, i;
-
-  /* If interface is up, do not allow modifying attached instances */
-  if (software_interface->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
-    {
-      rv = VNET_API_ERROR_INSTANCE_IN_USE;
-      log_err ("interface '%U' is currently up", format_vnet_sw_if_index_name,
-              vnm, sw_if_index);
-      goto done;
-    }
-
-  /* Check if provided instance name exists */
-  instance = snort_get_instance_by_name (instance_name);
-  if (instance == NULL)
-    {
-      rv = VNET_API_ERROR_NO_SUCH_ENTRY;
-      log_err ("unknown instance '%s'", instance_name);
-      goto done;
-    }
-
-  /* Check if interface is attached before unnecessarily increasing size of
-   * vector */
-  if (!is_enable && vec_len (sm->interfaces) <= sw_if_index)
-    {
-      rv = VNET_API_ERROR_INVALID_INTERFACE;
-      log_err ("interface %U is not assigned to snort instance %s!",
-              format_vnet_sw_if_index_name, vnm, sw_if_index, instance->name);
-      goto done;
-    }
-
-  /* vec_validate initialises empty space to 0s, which corresponds to null
-   * pointers (i.e. empty vectors) in the snort_interface_data_t structs which
-   * is precisely what we need */
-  vec_validate (sm->interfaces, sw_if_index);
-
-  interface_data = vec_elt_at_index (sm->interfaces, sw_if_index);
-  instance_index = instance->index;
-
-  /* When detaching with direction SNORT_INOUT choose currently attached
-   * directions */
-  if (!is_enable)
-    {
-      snort_dir =
-       snort_get_instance_direction (instance_index, interface_data);
-      /* If snort_dir is SNORT_INVALID then the instance is not attached */
-      if (snort_dir == SNORT_INVALID)
-       {
-         rv = VNET_API_ERROR_INVALID_INTERFACE;
-         log_err ("interface %U is not assigned to snort instance %s!",
-                  format_vnet_sw_if_index_name, vnm, sw_if_index,
-                  instance->name);
-         goto done;
-       }
-    }
-
-  /* Error if direction is invalid */
-  if (snort_dir == SNORT_INVALID)
-    {
-      rv = VNET_API_ERROR_INVALID_ARGUMENT;
-      vlib_log_err (snort_log.class,
-                   "cannot attach/detach with invalid direction ");
-      goto done;
-    }
-
-  /* Loop evaluates input instances and then output instances */
-  for (i = 0; i < 2; i++)
-    {
-      if (!(snort_dir & dirs[i]))
-       continue;
-
-      instance_indices = (dirs[i] == SNORT_INPUT) ?
-                          &(interface_data->input_instance_indices) :
-                          &(interface_data->output_instance_indices);
-      index = vec_search (*instance_indices, instance_index);
-
-      if (is_enable)
-       {
-         /* Error if instance is already attached when trying to attach */
-         if (index != ~0)
-           {
-             rv = VNET_API_ERROR_FEATURE_ALREADY_ENABLED;
-             log_err ("interface %U already assgined to instance '%s' on "
-                      "direction '%s'",
-                      format_vnet_sw_if_index_name, vnm, sw_if_index,
-                      instance->name,
-                      snort_get_direction_name_by_enum (dirs[i]));
-             goto done;
-           }
-       }
-      else
-       {
-         /* Error if instance is not attached when trying to detach */
-         if (index == ~0)
-           {
-             rv = VNET_API_ERROR_INVALID_INTERFACE;
-             log_err ("interface %U is not assigned to snort instance %s on "
-                      "direction '%s'!",
-                      format_vnet_sw_if_index_name, vnm, sw_if_index,
-                      instance->name,
-                      snort_get_direction_name_by_enum (dirs[i]));
-             goto done;
-           }
-       }
-
-      if (is_enable)
-       {
-         /* Enable feature if not previously enabled */
-         if (vec_len (*instance_indices) == 0)
-           {
-             snort_vnet_feature_enable_disable (dirs[i], sw_if_index,
-                                                1 /* is_enable */);
-           }
-         vec_add1 (*instance_indices, instance_index);
-       }
-      else
-       {
-         /* Disable feature when removing last instance */
-         if (vec_len (*instance_indices) == 1)
-           {
-             snort_vnet_feature_enable_disable (dirs[i], sw_if_index,
-                                                0 /* is_enable */);
-           }
-         vec_del1 (*instance_indices, index);
-       }
-    }
-done:
-  return rv;
-}
-
-int
-snort_interface_disable_all (vlib_main_t *vm, u32 sw_if_index)
-{
-  snort_main_t *sm = &snort_main;
-  vnet_main_t *vnm = vnet_get_main ();
-  vnet_sw_interface_t *software_interface =
-    vnet_get_sw_interface (vnm, sw_if_index);
-  snort_interface_data_t *interface_data;
-  int rv = 0;
-
-  if (software_interface->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
-    {
-      rv = VNET_API_ERROR_INSTANCE_IN_USE;
-      log_err ("interface '%U' is currently up", format_vnet_sw_if_index_name,
-              vnm, sw_if_index);
-      goto done;
-    }
-
-  if (vec_len (sm->interfaces) <= sw_if_index)
-    {
-      rv = VNET_API_ERROR_INVALID_INTERFACE;
-      log_err ("no instances attached to interface %U",
-              format_vnet_sw_if_index_name, vnm, sw_if_index);
-      goto done;
-    }
-
-  interface_data = vec_elt_at_index (sm->interfaces, sw_if_index);
-
-  if (vec_len (interface_data->input_instance_indices) == 0 &&
-      vec_len (interface_data->output_instance_indices) == 0)
-    {
-      rv = VNET_API_ERROR_INVALID_INTERFACE;
-      log_err ("no instances attached to interface %U",
-              format_vnet_sw_if_index_name, vnm, sw_if_index);
-      goto done;
-    }
-
-  if (vec_len (interface_data->input_instance_indices) > 0)
-    {
-      snort_vnet_feature_enable_disable (SNORT_INPUT, sw_if_index,
-                                        0 /* is_enable */);
-      vec_free (interface_data->input_instance_indices);
-    }
-  if (vec_len (interface_data->output_instance_indices) > 0)
-    {
-      snort_vnet_feature_enable_disable (SNORT_OUTPUT, sw_if_index,
-                                        0 /* is_enable */);
-      vec_free (interface_data->output_instance_indices);
-    }
-
-done:
-  return rv;
-}
-
-static int
-snort_strip_instance_interfaces (vlib_main_t *vm, snort_instance_t *instance)
-{
-  snort_main_t *sm = &snort_main;
-  snort_interface_data_t *interface;
-  snort_attach_dir_t direction;
-  int i;
-  int rv = 0;
-
-  /* Find all interfaces containing the given snort instance to disable */
-  vec_foreach_index (i, sm->interfaces)
-    {
-      /* Check if the snort_instance is attached by checking if the direction
-       * is SNORT_INVALID */
-      interface = vec_elt_at_index (sm->interfaces, i);
-      direction = snort_get_instance_direction (instance->index, interface);
-      if (direction != SNORT_INVALID)
-       rv = snort_interface_enable_disable (vm, (char *) instance->name, i,
-                                            0 /* is_enable */, direction);
-      if (rv)
-       break;
-    }
-
-  return rv;
-}
-
-int
-snort_instance_delete (vlib_main_t *vm, u32 instance_index)
+__clib_export int
+snort_instance_delete (vlib_main_t *vm, snort_instance_index_t instance_index)
 {
   snort_main_t *sm = &snort_main;
   snort_instance_t *si;
-  snort_qpair_t *qp;
+  snort_deleted_deq_node_t *dn;
   int rv = 0;
 
   si = snort_get_instance_by_index (instance_index);
   if (!si)
     return VNET_API_ERROR_NO_SUCH_ENTRY;
 
-  if (si->client_index != ~0)
-    return VNET_API_ERROR_INSTANCE_IN_USE;
+  vec_foreach_pointer (qp, si->qpairs)
+    if (qp->client_index != SNORT_INVALID_CLIENT_INDEX)
+      return VNET_API_ERROR_INSTANCE_IN_USE;
 
   if ((rv = snort_strip_instance_interfaces (vm, si)))
     return rv;
 
   hash_unset_mem (sm->instance_by_name, si->name);
 
+  // disable deq node and put it on recycle list
+  vlib_node_set_state (vm, si->dequeue_node_index, VLIB_NODE_STATE_DISABLED);
+  foreach_vlib_main ()
+    {
+      snort_deq_runtime_data_t *rt =
+       vlib_node_get_runtime_data (this_vlib_main, si->dequeue_node_index);
+
+      /* Mark node runtime as deleted so (if called)
+       * will drop packets. */
+      rt->is_deleted = 1;
+      rt->instance_index = ~0;
+    }
+  vlib_node_rename (vm, si->dequeue_node_index, "snort-deq-%s-deleted",
+                   si->name);
+  vlib_unregister_errors (vm, si->dequeue_node_index);
+  vec_add2 (sm->snort_deleted_deq_nodes, dn, 1);
+  dn->dequeue_node_index = si->dequeue_node_index;
+
   clib_mem_vm_unmap (si->shm_base);
   close (si->shm_fd);
 
-  vec_foreach (qp, si->qpairs)
+  vec_foreach_pointer (qp, si->qpairs)
     {
       clib_file_del_by_index (&file_main, qp->deq_fd_file_index);
+      close (qp->enq_fd);
+      close (qp->deq_fd);
+      clib_mem_free (qp);
     }
 
   log_debug ("deleting instance '%s'", si->name);
@@ -800,36 +320,10 @@ snort_instance_delete (vlib_main_t *vm, u32 instance_index)
   return rv;
 }
 
-int
-snort_set_node_mode (vlib_main_t *vm, u32 mode)
-{
-  int i;
-  snort_main.input_mode = mode;
-  for (i = 0; i < vlib_get_n_threads (); i++)
-    vlib_node_set_state (vlib_get_main_by_index (i), snort_deq_node.index,
-                        mode);
-  return 0;
-}
-
-static void
-snort_set_default_socket (snort_main_t *sm, u8 *socket_name)
-{
-  if (sm->socket_name)
-    return;
-
-  if (!socket_name)
-    socket_name = (u8 *) DAQ_VPP_DEFAULT_SOCKET_FILE;
-
-  sm->socket_name =
-    format (0, "%s/%s", vlib_unix_get_runtime_dir (), socket_name);
-  vec_terminate_c_string (sm->socket_name);
-}
-
 static clib_error_t *
 snort_init (vlib_main_t *vm)
 {
   snort_main_t *sm = &snort_main;
-  sm->input_mode = VLIB_NODE_STATE_INTERRUPT;
   sm->instance_by_name = hash_create_string (0, sizeof (uword));
   vlib_buffer_pool_t *bp;
 
@@ -840,8 +334,8 @@ snort_init (vlib_main_t *vm)
       vec_add1 (sm->buffer_pool_base_addrs, pm->base);
     }
 
-  if (!sm->socket_name)
-    snort_set_default_socket (sm, 0);
+  sm->socket_name = format (0, "%s/%s%c", vlib_unix_get_runtime_dir (),
+                           DAQ_VPP_DEFAULT_SOCKET_FILE, 0);
 
   return 0;
 }
@@ -852,15 +346,3 @@ VLIB_PLUGIN_REGISTER () = {
   .version = VPP_BUILD_VER,
   .description = "Snort",
 };
-
-VNET_FEATURE_INIT (snort_enq, static) = {
-  .arc_name = "ip4-unicast",
-  .node_name = "snort-enq",
-  .runs_before = VNET_FEATURES ("ip4-lookup"),
-};
-
-VNET_FEATURE_INIT (snort_enq_out, static) = {
-  .arc_name = "ip4-output",
-  .node_name = "snort-enq",
-  .runs_before = VNET_FEATURES ("interface-output"),
-};
index 76f0652..a5e3300 100644 (file)
 #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
 {
@@ -102,15 +122,6 @@ typedef enum
   SNORT_ENQ_N_NEXT_NODES,
 } snort_enq_next_t;
 
-typedef enum
-{
-  SNORT_INVALID = 0x00,
-  SNORT_INPUT = 0x01,
-  SNORT_OUTPUT = 0x02,
-  /* SNORT_INOUT === SNORT_INPUT | SNORT_OUTPUT */
-  SNORT_INOUT = 0x03
-} snort_attach_dir_t;
-
 #define SNORT_ENQ_NEXT_NODES                                                  \
   {                                                                           \
     [SNORT_ENQ_NEXT_DROP] = "error-drop",                                     \
@@ -118,27 +129,138 @@ typedef enum
 
 /* functions */
 snort_main_t *snort_get_main ();
-const char *snort_get_direction_name_by_enum (snort_attach_dir_t dir);
-snort_attach_dir_t
-snort_get_instance_direction (u32 instance_index,
-                             snort_interface_data_t *interface);
-snort_instance_t *snort_get_instance_by_index (u32 instance_index);
-snort_instance_t *snort_get_instance_by_name (char *name);
-int snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz,
-                          u8 drop_on_disconnect);
+
+/* interface.c */
 int snort_interface_enable_disable (vlib_main_t *vm, char *instance_name,
-                                   u32 sw_if_index, int is_enable,
-                                   snort_attach_dir_t dir);
+                                   u32 sw_if_index, int is_enable, int in,
+                                   int out);
 int snort_interface_disable_all (vlib_main_t *vm, u32 sw_if_index);
-int snort_set_node_mode (vlib_main_t *vm, u32 mode);
-int snort_instance_delete (vlib_main_t *vm, u32 instance_index);
-int snort_instance_disconnect (vlib_main_t *vm, u32 instance_index);
+int snort_strip_instance_interfaces (vlib_main_t *vm,
+                                    snort_instance_t *instance);
+
+/* socket.c */
+int snort_client_disconnect (vlib_main_t *vm, u32 client_index);
+clib_error_t *snort_listener_init ();
+
+/* format.c */
+format_function_t format_snort_enq_trace;
+format_function_t format_snort_arc_input_trace;
+format_function_t format_snort_arc_next_trace;
+format_function_t format_snort_deq_trace;
+format_function_t format_snort_daq_version;
+format_function_t format_snort_verdict;
+format_function_t format_snort_mode;
+
+/* enqueue.c */
+typedef struct
+{
+  u32 next_index;
+  u32 sw_if_index;
+  u16 instance;
+  daq_vpp_qpair_id_t qpair_id;
+  daq_vpp_desc_t desc;
+} snort_enq_trace_t;
+
+typedef struct
+{
+  u32 sw_if_index;
+  u16 instance;
+} snort_arc_input_trace_t;
 
+#define foreach_snort_enq_error                                               \
+  _ (SOCKET_ERROR, "write socket error")                                      \
+  _ (NO_CLIENT, "no snort client attached")                                   \
+  _ (NO_ENQ_SLOTS, "no enqueue slots (packet dropped)")
+
+typedef enum
+{
+#define _(sym, str) SNORT_ENQ_ERROR_##sym,
+  foreach_snort_enq_error
+#undef _
+    SNORT_ENQ_N_ERROR,
+} snort_enq_error_t;
+
+/* dequeue.c */
+typedef struct
+{
+  u32 next_index;
+  u32 sw_if_index;
+  u32 buffer_index;
+  u8 verdict;
+} snort_deq_trace_t;
+
+typedef struct
+{
+  u32 buffer_index;
+  u32 next_index;
+} snort_arc_next_trace_t;
+
+#define foreach_snort_deq_error                                               \
+  _ (BAD_DESC, "bad descriptor")                                              \
+  _ (BAD_DESC_INDEX, "bad descriptor index")                                  \
+  _ (NO_CLIENT_FREE, "packets freed on client dissapear")
+
+typedef enum
+{
+#define _(sym, str) SNORT_DEQ_ERROR_##sym,
+  foreach_snort_deq_error
+#undef _
+    SNORT_DEQ_N_ERROR,
+} snort_deq_error_t;
+
+/* inlines */
 always_inline void
-snort_freelist_init (u32 *fl)
+snort_qpair_init (snort_qpair_t *qp)
 {
-  for (int j = 0; j < vec_len (fl); j++)
-    fl[j] = j;
+  u16 qsz = 1 << qp->log2_queue_size;
+  for (int j = 0; j < qsz - 1; j++)
+    {
+      qp->entries[j].freelist_next = j + 1;
+      qp->entries[j].buffer_index = VLIB_BUFFER_INVALID_INDEX;
+    }
+  qp->entries[qsz - 1].freelist_next = ~0;
+  qp->next_free_desc = 0;
+  qp->hdr->enq.head = qp->hdr->deq.head = 0;
+  qp->hdr->enq.interrupt_pending = qp->hdr->deq.interrupt_pending = 0;
+  qp->deq_tail = 0;
+  qp->n_free_descs = qsz;
 }
 
+static_always_inline snort_qpair_t **
+snort_get_qpairs (snort_instance_t *si, clib_thread_index_t thread_index)
+{
+  return si->qpairs + (uword) thread_index * si->qpairs_per_thread;
+}
+
+static_always_inline snort_instance_t *
+snort_get_instance_by_name (char *name)
+{
+  snort_main_t *sm = &snort_main;
+  uword *p;
+  if ((p = hash_get_mem (sm->instance_by_name, name)) == 0)
+    return 0;
+
+  return vec_elt_at_index (sm->instances, p[0]);
+}
+
+static_always_inline snort_instance_t *
+snort_get_instance_by_index (u32 instance_index)
+{
+  snort_main_t *sm = &snort_main;
+
+  if (pool_is_free_index (sm->instances, instance_index))
+    return 0;
+  return pool_elt_at_index (sm->instances, instance_index);
+}
+
+static_always_inline daq_vpp_pkt_metadata_t *
+snort_get_buffer_metadata (vlib_buffer_t *b)
+{
+  return vnet_buffer_get_opaque (b);
+}
+
+#define log_debug(fmt, ...)                                                   \
+  vlib_log_debug (snort_log.class, "%s: " fmt, __func__, __VA_ARGS__)
+#define log_err(fmt, ...) vlib_log_err (snort_log.class, fmt, __VA_ARGS__)
+
 #endif /* __snort_snort_h__ */
index 00b3c3a..5de0194 100644 (file)
@@ -27,13 +27,6 @@ static u32 snort_base_msg_id;
 #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)
 {
@@ -46,8 +39,12 @@ vl_api_snort_instance_create_t_handler (vl_api_snort_instance_create_t *mp)
   u32 instance_index = ~0;
   snort_instance_t *si;
 
-  rv =
-    snort_instance_create (vm, name, min_log2 (queue_sz), drop_on_disconnect);
+  rv = snort_instance_create (vm,
+                             &(snort_instance_create_args_t){
+                               .drop_on_disconnect = drop_on_disconnect,
+                               .log2_queue_sz = min_log2 (queue_sz),
+                             },
+                             "%s", name);
 
   if ((si = snort_get_instance_by_name (name)))
     {
@@ -82,13 +79,19 @@ vl_api_snort_interface_attach_t_handler (vl_api_snort_interface_attach_t *mp)
   u32 sw_if_index = clib_net_to_host_u32 (mp->sw_if_index);
   u8 snort_dir = mp->snort_dir;
   int rv = VNET_API_ERROR_NO_SUCH_ENTRY;
+  int in = 0, out = 0;
 
   VALIDATE_SW_IF_INDEX (mp);
   switch (snort_dir)
     {
-    case SNORT_INPUT:
-    case SNORT_OUTPUT:
-    case SNORT_INOUT:
+    case 1:
+      in = 1;
+      break;
+    case 2:
+      out = 1;
+      break;
+    case 3:
+      in = out = 1;
       break;
     default:
       rv = VNET_API_ERROR_INVALID_ARGUMENT;
@@ -97,9 +100,8 @@ vl_api_snort_interface_attach_t_handler (vl_api_snort_interface_attach_t *mp)
   instance = snort_get_instance_by_index (instance_index);
   if (instance)
     {
-      rv = snort_interface_enable_disable (vm, (char *) instance->name,
-                                          sw_if_index, 1 /* is_enable */,
-                                          snort_dir);
+      rv = snort_interface_enable_disable (
+       vm, (char *) instance->name, sw_if_index, 1 /* is_enable */, in, out);
     }
   BAD_SW_IF_INDEX_LABEL;
   REPLY_MACRO (VL_API_SNORT_INTERFACE_ATTACH_REPLY);
@@ -116,7 +118,7 @@ send_snort_instance_details (const snort_instance_t *instance,
     VL_API_SNORT_INSTANCE_DETAILS, name_len, rp, context, ({
       rmp->instance_index = clib_host_to_net_u32 (instance->index);
       vl_api_vec_to_api_string (instance->name, &rmp->name);
-      rmp->snort_client_index = clib_host_to_net_u32 (instance->client_index);
+      rmp->snort_client_index = clib_host_to_net_u32 (0);
       rmp->shm_size = clib_host_to_net_u32 (instance->shm_size);
       rmp->shm_fd = clib_host_to_net_u32 (instance->shm_fd);
       rmp->drop_on_disconnect = instance->drop_on_disconnect;
@@ -173,99 +175,13 @@ vl_api_snort_instance_get_t_handler (vl_api_snort_instance_get_t *mp)
     }
 }
 
-static void
-send_snort_interface_details (u32 sw_if_index, u32 instance_index,
-                             vl_api_registration_t *rp, u32 context)
-{
-  vl_api_snort_interface_details_t *rmp;
-
-  if (instance_index != ~0)
-    {
-      REPLY_MACRO_DETAILS4 (VL_API_SNORT_INTERFACE_DETAILS, rp, context, ({
-                             rmp->instance_index =
-                               clib_host_to_net_u32 (instance_index);
-                             rmp->sw_if_index =
-                               clib_host_to_net_u32 (sw_if_index);
-                           }));
-    }
-}
-
 static void
 vl_api_snort_interface_get_t_handler (vl_api_snort_interface_get_t *mp)
 {
-  snort_main_t *sm = snort_get_main ();
   vl_api_snort_interface_get_reply_t *rmp;
-  u32 sw_if_index;
-  u32 *instances;
-  u32 index;
   int rv = 0;
 
-  sw_if_index = clib_net_to_host_u32 (mp->sw_if_index);
-
-  if (sw_if_index == INDEX_INVALID)
-    {
-      /* clang-format off */
-      if (vec_len (sm->interfaces) == 0)
-       {
-         REPLY_MACRO2 (VL_API_SNORT_INTERFACE_GET_REPLY, ({ rmp->cursor = ~0; }));
-         return;
-       }
-
-      REPLY_AND_DETAILS_VEC_MACRO(
-       VL_API_SNORT_INTERFACE_GET_REPLY,
-       sm->interfaces,
-       mp, rmp, rv, ({
-          instances = vec_len(sm->interfaces[cursor].input_instance_indices) ?
-           sm->interfaces[cursor].input_instance_indices : sm->interfaces[cursor].output_instance_indices;
-          if (vec_len(instances) == 0)
-          {
-            index = ~0;
-          }
-          else {
-            index = instances[0];
-          }
-          send_snort_interface_details (cursor, index, rp, mp->context);
-       }))
-      /* clang-format on */
-    }
-  else
-    {
-      instances =
-       vec_len (sm->interfaces[sw_if_index].input_instance_indices) ?
-         sm->interfaces[sw_if_index].input_instance_indices :
-         sm->interfaces[sw_if_index].output_instance_indices;
-      if (vec_len (instances) == 0)
-       {
-         index = ~0;
-       }
-      else
-       {
-         index = instances[0];
-       }
-      if (snort_get_instance_by_index (index))
-       {
-         vl_api_registration_t *rp =
-           vl_api_client_index_to_registration (mp->client_index);
-
-         if (rp == NULL)
-           {
-             return;
-           }
-
-         send_snort_interface_details (sw_if_index, *instances, rp,
-                                       mp->context);
-       }
-      else
-       {
-         rv = VNET_API_ERROR_NO_SUCH_ENTRY;
-       }
-
-      /* clang-format off */
-      REPLY_MACRO2 (VL_API_SNORT_INTERFACE_GET_REPLY, ({
-        rmp->cursor = INDEX_INVALID;
-      }));
-      /* clang-format on */
-    }
+  REPLY_MACRO2 (VL_API_SNORT_INTERFACE_GET_REPLY, ({ rmp->cursor = ~0; }));
 }
 
 static void
@@ -274,23 +190,12 @@ send_snort_client_details (const snort_client_t *client,
 {
   snort_main_t *sm = snort_get_main ();
   vl_api_snort_client_details_t *rmp;
-  snort_instance_t *instance;
 
-  if (client->instance_index == ~0)
-    {
-      return;
-    }
-
-  instance = pool_elt_at_index (sm->instances, client->instance_index);
-  if (instance)
-    {
-      REPLY_MACRO_DETAILS4 (VL_API_SNORT_CLIENT_DETAILS, rp, context, ({
-                             rmp->instance_index =
-                               clib_host_to_net_u32 (client->instance_index);
-                             rmp->client_index =
-                               clib_host_to_net_u32 (client - sm->clients);
-                           }));
-    }
+  REPLY_MACRO_DETAILS4 (VL_API_SNORT_CLIENT_DETAILS, rp, context, ({
+                         rmp->instance_index = 0;
+                         rmp->client_index =
+                           clib_host_to_net_u32 (client - sm->clients);
+                       }));
 }
 
 static void
@@ -347,21 +252,11 @@ static void
 vl_api_snort_client_disconnect_t_handler (vl_api_snort_client_disconnect_t *mp)
 {
   vlib_main_t *vm = vlib_get_main ();
-  snort_main_t *sm = snort_get_main ();
-  snort_client_t *client;
   vl_api_snort_client_disconnect_reply_t *rmp;
   u32 client_index = clib_net_to_host_u32 (mp->snort_client_index);
   int rv = 0;
 
-  if (pool_is_free_index (sm->clients, client_index))
-    {
-      rv = VNET_API_ERROR_NO_SUCH_ENTRY;
-    }
-  else
-    {
-      client = pool_elt_at_index (sm->clients, client_index);
-      rv = snort_instance_disconnect (vm, client->instance_index);
-    }
+  rv = snort_client_disconnect (vm, client_index);
 
   REPLY_MACRO (VL_API_SNORT_CLIENT_DISCONNECT_REPLY);
 }
@@ -370,10 +265,8 @@ static void
 vl_api_snort_instance_disconnect_t_handler (
   vl_api_snort_instance_disconnect_t *mp)
 {
-  vlib_main_t *vm = vlib_get_main ();
   vl_api_snort_instance_disconnect_reply_t *rmp;
-  u32 instance_index = clib_net_to_host_u32 (mp->instance_index);
-  int rv = snort_instance_disconnect (vm, instance_index);
+  int rv = VNET_API_ERROR_UNSUPPORTED;
 
   REPLY_MACRO (VL_API_SNORT_INSTANCE_DISCONNECT_REPLY);
 }
@@ -396,28 +289,17 @@ vl_api_snort_interface_detach_t_handler (vl_api_snort_interface_detach_t *mp)
 static void
 vl_api_snort_input_mode_get_t_handler (vl_api_snort_input_mode_get_t *mp)
 {
-  snort_main_t *sm = &snort_main;
   vl_api_snort_input_mode_get_reply_t *rmp;
-  int rv = 0;
+  int rv = VNET_API_ERROR_UNSUPPORTED;
 
-  REPLY_MACRO2 (VL_API_SNORT_INPUT_MODE_GET_REPLY, ({
-                 rmp->snort_mode = clib_host_to_net_u32 (sm->input_mode);
-               }));
+  REPLY_MACRO (VL_API_SNORT_INPUT_MODE_GET_REPLY);
 }
 
 static void
 vl_api_snort_input_mode_set_t_handler (vl_api_snort_input_mode_set_t *mp)
 {
-  vlib_main_t *vm = vlib_get_main ();
   vl_api_snort_input_mode_set_reply_t *rmp;
-  u8 mode = mp->input_mode;
-  int rv = 0;
-
-  if (mode != VLIB_NODE_STATE_INTERRUPT && mode != VLIB_NODE_STATE_POLLING)
-    {
-      clib_error_return (0, "invalid input mode %u", mode);
-    }
-  snort_set_node_mode (vm, mode);
+  int rv = VNET_API_ERROR_UNSUPPORTED;
 
   REPLY_MACRO (VL_API_SNORT_INPUT_MODE_SET_REPLY);
 }
diff --git a/src/plugins/snort/socket.c b/src/plugins/snort/socket.c
new file mode 100644 (file)
index 0000000..119435c
--- /dev/null
@@ -0,0 +1,339 @@
+/* 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;
+}
index 5335091..090d68f 100644 (file)
@@ -34,19 +34,15 @@ class TestSnort(VppTestCase):
             "snort create-instance name snortTest2 queue-size 16 on-disconnect pass": "",
             "snort attach instance snortTest interface pg0 output": "",
             "snort attach instance snortTest2 interface pg1 input": "",
-            "snort attach all-instances interface pg2 inout": "",
             "snort attach instance snortTest instance snortTest2 interface pg3 inout": "",
             "show snort instances": "snortTest",
             "show snort interfaces": "pg0",
             "show snort clients": "number of clients",
-            "show snort mode": "input mode: interrupt",
-            "snort mode polling": "",
-            "snort mode interrupt": "",
             "snort detach instance snortTest interface pg0": "",
             "snort detach instance snortTest2 interface pg1": "",
-            "snort detach all-instances interface pg2": "",
             "snort detach instance snortTest instance snortTest2 interface pg3": "",
             "snort delete instance snortTest": "",
+            "snort delete instance snortTest2": "",
         }
 
         for command, reply in commands_replies.items():
@@ -79,17 +75,21 @@ class TestSnortVapi(VppTestCase):
 
     def test_snort_01_modes_set_interrupt(self):
         """Set mode to interrupt"""
-        self.vapi.snort_input_mode_set(input_mode=1)
-        reply = self.vapi.snort_input_mode_get()
-        self.assertEqual(reply.snort_mode, 1)
-        reply = self.vapi.cli("show snort mode")
-        self.assertIn("interrupt", reply)
+        with self.vapi.assert_negative_api_retval():
+            reply = self.vapi.snort_input_mode_set(input_mode=1)
+            self.assertEqual(reply.retval, -126)
+        with self.vapi.assert_negative_api_retval():
+            reply = self.vapi.snort_input_mode_get()
+            self.assertEqual(reply.retval, -126)
 
     def test_snort_02_modes_set_polling(self):
         """Set mode to polling"""
-        self.vapi.snort_input_mode_set(input_mode=0)
-        reply = self.vapi.snort_input_mode_get()
-        self.assertEqual(reply.snort_mode, 0)
+        with self.vapi.assert_negative_api_retval():
+            reply = self.vapi.snort_input_mode_set(input_mode=0)
+            self.assertEqual(reply.retval, -126)
+        with self.vapi.assert_negative_api_retval():
+            reply = self.vapi.snort_input_mode_get()
+            self.assertEqual(reply.retval, -126)
 
     def test_snort_03_create(self):
         """Create two snort instances"""
@@ -104,15 +104,19 @@ class TestSnortVapi(VppTestCase):
         reply = self.vapi.cli("show snort instances")
         self.assertIn("snortTest0", reply)
         self.assertIn("snortTest1", reply)
+        self.vapi.snort_instance_delete(instance_index=1)
+        self.vapi.snort_instance_delete(instance_index=0)
 
     def test_snort_04_attach_if(self):
         """Interfaces can be attached"""
-        reply = self.vapi.snort_interface_attach(
-            instance_index=0, sw_if_index=1, snort_dir=1
+        self.vapi.snort_instance_create(
+            queue_size=8, drop_on_disconnect=0, name="snortTest0"
         )
-        reply = self.vapi.snort_interface_attach(
-            instance_index=0, sw_if_index=2, snort_dir=2
+        self.vapi.snort_instance_create(
+            queue_size=32, drop_on_disconnect=1, name="snortTest1"
         )
+        self.vapi.snort_interface_attach(instance_index=0, sw_if_index=1, snort_dir=1)
+        self.vapi.snort_interface_attach(instance_index=0, sw_if_index=2, snort_dir=2)
         # verify attaching with an invalid direction is rejected
         try:
             reply = self.vapi.snort_interface_attach(
@@ -122,18 +126,13 @@ class TestSnortVapi(VppTestCase):
             pass
         else:
             self.assertNotEqual(reply.retval, 0)
+        self.vapi.snort_interface_attach(instance_index=1, sw_if_index=2, snort_dir=3)
         reply = self.vapi.cli("show snort interfaces")
-        self.assertNotIn("snortTest1", reply)
-
-        reply = self.vapi.snort_interface_attach(
-            instance_index=1, sw_if_index=2, snort_dir=3
-        )
-        reply = self.vapi.cli("show snort interfaces")
-        self.assertIn("snortTest0", reply)
-        self.assertIn("snortTest1", reply)
-        self.assertIn("input", reply)
-        self.assertIn("inout", reply)
-        self.assertIn("output", reply)
+        self.assertIn("input instances:", reply)
+        self.assertIn("pg0:    snortTest0", reply)
+        self.assertIn("pg1:    snortTest1", reply)
+        self.assertIn("output instances:", reply)
+        self.assertIn("pg1:    snortTest1", reply)
 
         # verify attaching a previously attached interface is rejected
         try:
@@ -156,18 +155,39 @@ class TestSnortVapi(VppTestCase):
             self.assertNotEqual(reply.retval, 0)
         reply = self.vapi.cli("show snort interfaces")
         self.assertIn("snortTest1", reply)
+        self.vapi.snort_instance_delete(instance_index=1)
+        self.vapi.snort_instance_delete(instance_index=0)
 
     def test_snort_05_delete_instance(self):
         """Instances can be deleted"""
+        reply = self.vapi.snort_instance_create(
+            queue_size=8, drop_on_disconnect=0, name="snortTest0"
+        )
+        self.assertEqual(reply.instance_index, 0)
         reply = self.vapi.snort_instance_delete(instance_index=0)
+        self.assertEqual(reply.retval, 0)
         reply = self.vapi.cli("show snort interfaces")
         self.assertNotIn("snortTest0", reply)
-        self.assertIn("snortTest1", reply)
-        self.assertNotIn("pg0", reply)
-        self.assertIn("pg1", reply)
 
     def test_snort_06_detach_if(self):
         """Interfaces can be detached"""
+        reply = self.vapi.snort_instance_create(
+            queue_size=8, drop_on_disconnect=0, name="snortTest0"
+        )
+        self.assertEqual(reply.instance_index, 0)
+        reply = self.vapi.snort_instance_create(
+            queue_size=128, drop_on_disconnect=1, name="snortTest1"
+        )
+        self.assertEqual(reply.instance_index, 1)
+        reply = self.vapi.snort_interface_attach(
+            instance_index=0, sw_if_index=1, snort_dir=1
+        )
+        reply = self.vapi.snort_interface_attach(
+            instance_index=1, sw_if_index=1, snort_dir=2
+        )
+        reply = self.vapi.snort_interface_attach(
+            instance_index=1, sw_if_index=2, snort_dir=3
+        )
         # verify detaching an invalid sw_if_index is rejected
         try:
             reply = self.vapi.snort_interface_detach(sw_if_index=3)
@@ -175,9 +195,14 @@ class TestSnortVapi(VppTestCase):
             pass
         else:
             self.assertNotEqual(reply.retval, 0)
+        reply = self.vapi.snort_interface_detach(sw_if_index=1)
+        self.assertEqual(reply.retval, 0)
         reply = self.vapi.snort_interface_detach(sw_if_index=2)
-        reply = self.vapi.cli("show snort interfaces")
+        self.assertEqual(reply.retval, 0)
+        self.assertNotIn("pg0", reply)
         self.assertNotIn("pg1", reply)
+        self.vapi.snort_instance_delete(instance_index=1)
+        self.vapi.snort_instance_delete(instance_index=0)
 
 
 if __name__ == "__main__":