snort: API functions for plugin 67/41567/15
authorAlexander Skorichenko <[email protected]>
Thu, 12 Sep 2024 07:32:46 +0000 (09:32 +0200)
committerMatthew Smith <[email protected]>
Fri, 27 Sep 2024 12:30:16 +0000 (12:30 +0000)
Also, made disconnect-instance and delete-instance functions
available via cli.

Type: feature

Change-Id: I7939d27867959cb871b1cc7205b94410b53906fd
Signed-off-by: Alexander Skorichenko <[email protected]>
src/plugins/snort/CMakeLists.txt
src/plugins/snort/cli.c
src/plugins/snort/dequeue.c
src/plugins/snort/enqueue.c
src/plugins/snort/main.c
src/plugins/snort/snort.api [new file with mode: 0644]
src/plugins/snort/snort.h
src/plugins/snort/snort_api.c [new file with mode: 0644]
test/test_snort.py

index bd9dcdc..3fc2bd6 100644 (file)
@@ -7,6 +7,10 @@ add_vpp_plugin(snort
   dequeue.c
   main.c
   cli.c
+  snort_api.c
+
+  API_FILES
+  snort.api
 
   MULTIARCH_SOURCES
   enqueue.c
index 08740f4..4b6dbc7 100644 (file)
@@ -25,6 +25,7 @@ snort_create_instance_command_fn (vlib_main_t *vm, unformat_input_t *input,
   u8 *name = 0;
   u32 queue_size = 1024;
   u8 drop_on_diconnect = 1;
+  int rv = 0;
 
   /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
@@ -60,8 +61,30 @@ snort_create_instance_command_fn (vlib_main_t *vm, unformat_input_t *input,
       goto done;
     }
 
-  err = snort_instance_create (vm, (char *) name, min_log2 (queue_size),
-                              drop_on_diconnect);
+  rv = snort_instance_create (vm, (char *) name, min_log2 (queue_size),
+                             drop_on_diconnect);
+
+  switch (rv)
+    {
+    case 0:
+      break;
+    case VNET_API_ERROR_ENTRY_ALREADY_EXISTS:
+      err = clib_error_return (0, "instance '%s' already exists", name);
+      break;
+    case VNET_API_ERROR_SYSCALL_ERROR_1:
+      err = clib_error_return (0, "memory fd failure: %U", format_clib_error,
+                              clib_mem_get_last_error ());
+      break;
+    case VNET_API_ERROR_SYSCALL_ERROR_2:
+      err = clib_error_return (0, "ftruncate failure");
+      break;
+    case VNET_API_ERROR_SYSCALL_ERROR_3:
+      err = clib_error_return (0, "mmap failure");
+      break;
+    default:
+      err = clib_error_return (0, "snort_instance_create returned %d", rv);
+      break;
+    }
 
 done:
   vec_free (name);
@@ -76,6 +99,118 @@ VLIB_CLI_COMMAND (snort_create_instance_command, static) = {
   .function = snort_create_instance_command_fn,
 };
 
+static clib_error_t *
+snort_disconnect_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;
+  snort_instance_t *si;
+  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 (!name)
+    {
+      err = clib_error_return (0, "please specify instance 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);
+
+  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");
+      break;
+    default:
+      err = clib_error_return (0, "snort_instance_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,
+};
+
+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 (!name)
+    {
+      err = clib_error_return (0, "please specify instance name");
+      goto done;
+    }
+
+  snort_instance_t *si = snort_get_instance_by_name ((char *) name);
+  if (!si)
+    err = clib_error_return (0, "unknown instance '%s' requested", name);
+  else
+    rv = snort_instance_delete (vm, si->index);
+
+  switch (rv)
+    {
+    case 0:
+      break;
+    case VNET_API_ERROR_NO_SUCH_ENTRY:
+      err = clib_error_return (0, "instance '%s' deletion failure", name);
+      break;
+    case VNET_API_ERROR_INSTANCE_IN_USE:
+      err = clib_error_return (0, "instance '%s' has connected client", name);
+      break;
+    default:
+      err = clib_error_return (0, "snort_instance_delete returned %d", rv);
+      break;
+    }
+
+done:
+  vec_free (name);
+  unformat_free (line_input);
+  return err;
+}
+
+VLIB_CLI_COMMAND (snort_delete_instance_command, static) = {
+  .path = "snort delete instance",
+  .short_help = "snort delete instance <name>",
+  .function = snort_delete_instance_command_fn,
+};
+
 static clib_error_t *
 snort_attach_command_fn (vlib_main_t *vm, unformat_input_t *input,
                         vlib_cli_command_t *cmd)
@@ -86,6 +221,7 @@ snort_attach_command_fn (vlib_main_t *vm, unformat_input_t *input,
   u8 *name = 0;
   u32 sw_if_index = ~0;
   snort_attach_dir_t dir = SNORT_INOUT;
+  int rv = 0;
 
   /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
@@ -124,8 +260,29 @@ snort_attach_command_fn (vlib_main_t *vm, unformat_input_t *input,
       goto done;
     }
 
-  err =
-    snort_interface_enable_disable (vm, (char *) name, sw_if_index, 1, dir);
+  rv = snort_interface_enable_disable (vm, (char *) name, sw_if_index, 1, dir);
+
+  switch (rv)
+    {
+    case 0:
+      break;
+    case VNET_API_ERROR_FEATURE_ALREADY_ENABLED:
+      /* already attached to same instance */
+      break;
+    case VNET_API_ERROR_INSTANCE_IN_USE:
+      err = clib_error_return (0,
+                              "interface %U already assigned to "
+                              "an instance",
+                              format_vnet_sw_if_index_name, vnm, sw_if_index);
+      break;
+    case VNET_API_ERROR_NO_SUCH_ENTRY:
+      err = clib_error_return (0, "unknown instance '%s'", name);
+      break;
+    default:
+      err = clib_error_return (0, "snort_interface_enable_disable returned %d",
+                              rv);
+      break;
+    }
 
 done:
   vec_free (name);
@@ -148,6 +305,7 @@ snort_detach_command_fn (vlib_main_t *vm, unformat_input_t *input,
   vnet_main_t *vnm = vnet_get_main ();
   clib_error_t *err = 0;
   u32 sw_if_index = ~0;
+  int rv = 0;
 
   /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
@@ -172,7 +330,23 @@ snort_detach_command_fn (vlib_main_t *vm, unformat_input_t *input,
       goto done;
     }
 
-  err = snort_interface_enable_disable (vm, 0, sw_if_index, 0, SNORT_INOUT);
+  rv = snort_interface_enable_disable (vm, 0, sw_if_index, 0, SNORT_INOUT);
+
+  switch (rv)
+    {
+    case 0:
+      break;
+    case VNET_API_ERROR_INVALID_INTERFACE:
+      err = clib_error_return (0,
+                              "interface %U is not assigned to snort "
+                              "instance!",
+                              format_vnet_sw_if_index_name, vnm, sw_if_index);
+      break;
+    default:
+      err = clib_error_return (0, "snort_interface_enable_disable returned %d",
+                              rv);
+      break;
+    }
 
 done:
   unformat_free (line_input);
@@ -213,7 +387,7 @@ snort_show_interfaces_command_fn (vlib_main_t *vm, unformat_input_t *input,
   snort_instance_t *si;
   u32 *index;
 
-  vlib_cli_output (vm, "interface\tsnort instance");
+  vlib_cli_output (vm, "interface\t\tsnort instance");
   vec_foreach (index, sm->instance_by_sw_if_index)
     {
       if (index[0] != ~0)
@@ -237,7 +411,18 @@ snort_show_clients_command_fn (vlib_main_t *vm, unformat_input_t *input,
                               vlib_cli_command_t *cmd)
 {
   snort_main_t *sm = &snort_main;
-  vlib_cli_output (vm, "number of clients: %d", pool_elts (sm->clients));
+  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");
+  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);
+    }
   return 0;
 }
 
@@ -251,14 +436,16 @@ static clib_error_t *
 snort_mode_polling_command_fn (vlib_main_t *vm, unformat_input_t *input,
                               vlib_cli_command_t *cmd)
 {
-  return snort_set_node_mode (vm, VLIB_NODE_STATE_POLLING);
+  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)
 {
-  return snort_set_node_mode (vm, VLIB_NODE_STATE_INTERRUPT);
+  snort_set_node_mode (vm, VLIB_NODE_STATE_INTERRUPT);
+  return 0;
 }
 
 VLIB_CLI_COMMAND (snort_mode_polling_command, static) = {
index 31745de..bc301f6 100644 (file)
@@ -307,7 +307,7 @@ snort_deq_node_polling (vlib_main_t *vm, vlib_node_runtime_t *node,
   snort_qpair_t *qp;
   snort_instance_t *si;
 
-  vec_foreach (si, sm->instances)
+  pool_foreach (si, sm->instances)
     {
       qp = vec_elt_at_index (si->qpairs, vm->thread_index);
       u32 ready = __atomic_load_n (&qp->ready, __ATOMIC_ACQUIRE);
index 409c0e4..ce4f344 100644 (file)
@@ -133,7 +133,7 @@ snort_enq_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
                                   nexts, n_processed);
     }
 
-  vec_foreach (si, sm->instances)
+  pool_foreach (si, sm->instances)
     {
       u32 head, freelist_len, n_pending, n_enq, mask;
       u64 ctr = 1;
index 2430fcd..50bff02 100644 (file)
@@ -3,10 +3,24 @@
  */
 
 #include <vlib/vlib.h>
+#include <vlibapi/api_types.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;
@@ -18,6 +32,12 @@ VLIB_REGISTER_LOG_CLASS (snort_log, static) = {
 #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)
 {
@@ -45,7 +65,38 @@ snort_client_disconnect (clib_file_t *uf)
   pool_put (sm->clients, c);
 }
 
-static snort_instance_t *
+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;
+}
+
+snort_instance_t *
 snort_get_instance_by_name (char *name)
 {
   snort_main_t *sm = &snort_main;
@@ -54,7 +105,16 @@ snort_get_instance_by_name (char *name)
     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 *
@@ -110,6 +170,8 @@ snort_conn_fd_read_ready (clib_file_t *uf)
          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;
@@ -281,14 +343,13 @@ snort_listener_init (vlib_main_t *vm)
   return 0;
 }
 
-clib_error_t *
+int
 snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz,
                       u8 drop_on_disconnect)
 {
   vlib_thread_main_t *tm = vlib_get_thread_main ();
   snort_main_t *sm = &snort_main;
   snort_instance_t *si;
-  clib_error_t *err = 0;
   u32 index, i;
   u8 *base = CLIB_MEM_VM_MAP_FAILED;
   u32 size;
@@ -296,9 +357,10 @@ snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz,
   u32 qpair_mem_sz = 0;
   u32 qsz = 1 << log2_queue_sz;
   u8 align = CLIB_CACHE_LINE_BYTES;
+  int rv = 0;
 
   if (snort_get_instance_by_name (name))
-    return clib_error_return (0, "instance already exists");
+    return VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
 
   /* descriptor table */
   qpair_mem_sz += round_pow2 (qsz * sizeof (daq_vpp_desc_t), align);
@@ -316,14 +378,13 @@ snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz,
 
   if (fd == -1)
     {
-      err = clib_error_return (0, "memory fd failure: %U", format_clib_error,
-                              clib_mem_get_last_error ());
+      rv = VNET_API_ERROR_SYSCALL_ERROR_1;
       goto done;
     }
 
   if ((ftruncate (fd, size)) == -1)
     {
-      err = clib_error_return (0, "ftruncate failure");
+      rv = VNET_API_ERROR_SYSCALL_ERROR_2;
       goto done;
     }
 
@@ -331,7 +392,7 @@ snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz,
 
   if (base == CLIB_MEM_VM_MAP_FAILED)
     {
-      err = clib_error_return (0, "mmap failure");
+      rv = VNET_API_ERROR_SYSCALL_ERROR_3;
       goto done;
     }
 
@@ -399,17 +460,17 @@ snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz,
                         sm->input_mode);
 
 done:
-  if (err)
+  if (rv)
     {
       if (base != CLIB_MEM_VM_MAP_FAILED)
        clib_mem_vm_unmap (base);
       if (fd != -1)
        close (fd);
     }
-  return err;
+  return rv;
 }
 
-clib_error_t *
+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)
@@ -417,16 +478,16 @@ snort_interface_enable_disable (vlib_main_t *vm, char *instance_name,
   snort_main_t *sm = &snort_main;
   vnet_main_t *vnm = vnet_get_main ();
   snort_instance_t *si;
-  clib_error_t *err = 0;
   u64 fa_data;
   u32 index;
+  int rv = 0;
 
   if (is_enable)
     {
       if ((si = snort_get_instance_by_name (instance_name)) == 0)
        {
-         err = clib_error_return (0, "unknown instance '%s'", instance_name);
-         goto done;
+         log_err ("unknown instance '%s'", instance_name);
+         return VNET_API_ERROR_NO_SUCH_ENTRY;
        }
 
       vec_validate_init_empty (sm->instance_by_sw_if_index, sw_if_index, ~0);
@@ -434,12 +495,13 @@ snort_interface_enable_disable (vlib_main_t *vm, char *instance_name,
       index = sm->instance_by_sw_if_index[sw_if_index];
       if (index != ~0)
        {
+         if (index == si->index)
+           rv = VNET_API_ERROR_FEATURE_ALREADY_ENABLED;
+         else
+           rv = VNET_API_ERROR_INSTANCE_IN_USE;
          si = vec_elt_at_index (sm->instances, index);
-         err = clib_error_return (0,
-                                  "interface %U already assgined to "
-                                  "instance '%s'",
-                                  format_vnet_sw_if_index_name, vnm,
-                                  sw_if_index, si->name);
+         log_err ("interface %U already assgined to instance '%s'",
+                  format_vnet_sw_if_index_name, vnm, sw_if_index, si->name);
          goto done;
        }
 
@@ -462,11 +524,9 @@ snort_interface_enable_disable (vlib_main_t *vm, char *instance_name,
       if (sw_if_index >= vec_len (sm->instance_by_sw_if_index) ||
          sm->instance_by_sw_if_index[sw_if_index] == ~0)
        {
-         err =
-           clib_error_return (0,
-                              "interface %U is not assigned to snort "
-                              "instance!",
-                              format_vnet_sw_if_index_name, vnm, sw_if_index);
+         rv = VNET_API_ERROR_INVALID_INTERFACE;
+         log_err ("interface %U is not assigned to snort instance!",
+                  format_vnet_sw_if_index_name, vnm, sw_if_index);
          goto done;
        }
       index = sm->instance_by_sw_if_index[sw_if_index];
@@ -488,12 +548,66 @@ snort_interface_enable_disable (vlib_main_t *vm, char *instance_name,
     }
 
 done:
-  if (err)
-    log_err ("%U", format_clib_error, err);
-  return 0;
+  return rv;
 }
 
-clib_error_t *
+static int
+snort_strip_instance_interfaces (vlib_main_t *vm, u32 instance_index)
+{
+  snort_main_t *sm = &snort_main;
+  u32 *index;
+  int rv = 0;
+
+  vec_foreach (index, sm->instance_by_sw_if_index)
+    {
+      if (*index == instance_index)
+       rv = snort_interface_enable_disable (
+         vm, NULL, index - sm->instance_by_sw_if_index, 0, 0);
+      if (rv)
+       break;
+    }
+
+  return rv;
+}
+
+int
+snort_instance_delete (vlib_main_t *vm, u32 instance_index)
+{
+  snort_main_t *sm = &snort_main;
+  snort_instance_t *si;
+  snort_qpair_t *qp;
+  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;
+
+  if ((rv = snort_strip_instance_interfaces (vm, si->index)))
+    return rv;
+
+  hash_unset_mem (sm->instance_by_name, si->name);
+
+  clib_mem_vm_unmap (si->shm_base);
+  close (si->shm_fd);
+
+  vec_foreach (qp, si->qpairs)
+    {
+      clib_file_del_by_index (&file_main, qp->deq_fd_file_index);
+    }
+
+  log_debug ("deleting instance '%s'", si->name);
+
+  vec_free (si->qpairs);
+  vec_free (si->name);
+  pool_put (sm->instances, si);
+
+  return rv;
+}
+
+int
 snort_set_node_mode (vlib_main_t *vm, u32 mode)
 {
   int i;
diff --git a/src/plugins/snort/snort.api b/src/plugins/snort/snort.api
new file mode 100644 (file)
index 0000000..5c65f79
--- /dev/null
@@ -0,0 +1,226 @@
+option version = "1.0.0";
+
+import "vnet/interface_types.api";
+import "vnet/ip/ip_types.api";
+
+define snort_instance_create {
+        u32 client_index;
+        u32 context;
+       u32 queue_size;
+       u8 drop_on_disconnect;
+       string name[];
+};
+
+define snort_instance_create_reply {
+        u32 context;
+        i32 retval;
+       u32 instance_index;
+};
+
+define snort_instance_delete {
+        u32 client_index;
+        u32 context;
+       u32 instance_index;
+};
+
+define snort_instance_delete_reply {
+        u32 context;
+       i32 retval;
+};
+
+define snort_client_disconnect {
+        u32 client_index;
+        u32 context;
+       u32 snort_client_index;
+};
+
+define snort_client_disconnect_reply {
+        u32 context;
+       i32 retval;
+};
+
+define snort_instance_disconnect {
+        u32 client_index;
+        u32 context;
+       u32 instance_index;
+};
+
+define snort_instance_disconnect_reply {
+        u32 context;
+       i32 retval;
+};
+
+define snort_interface_attach {
+        u32 client_index;
+        u32 context;
+       u32 instance_index;
+       u32 sw_if_index;
+       u8 snort_dir;
+};
+
+define snort_interface_attach_reply {
+       u32 context;
+       i32 retval;
+};
+
+define snort_interface_detach {
+        u32 client_index;
+        u32 context;
+       u32 sw_if_index;
+};
+
+define snort_interface_detach_reply {
+       u32 context;
+       i32 retval;
+};
+
+define snort_input_mode_get {
+        u32 client_index;
+        u32 context;
+};
+
+define snort_input_mode_get_reply {
+        u32 context;
+        i32 retval;
+       u32 snort_mode;
+};
+
+define snort_input_mode_set {
+        u32 client_index;
+        u32 context;
+       u8 input_mode;
+};
+
+define snort_input_mode_set_reply {
+        u32 context;
+        i32 retval;
+};
+
+service {
+  rpc snort_instance_get returns snort_instance_get_reply
+    stream snort_instance_details;
+};
+
+/** \brief Get snort instance(s).
+    @param client_index - opaque cookie to identify the sender.
+    @param context - sender context
+    @param cursor - current iterator value (all requested).
+    @param instance_index - instance index (~0 for all).
+*/
+define snort_instance_get
+{
+       u32 client_index;
+       u32 context;
+       u32 cursor;
+       u32 instance_index;
+};
+
+/** \brief Reply for snort instance(s).
+    @param context - sender context
+    @param retval - return code for the request.
+    @param cursor - iterator value to continue with (if there is more).
+*/
+define snort_instance_get_reply
+{
+       u32 context;
+       i32 retval;
+       u32 cursor;
+};
+
+/** \brief Details of a snort instance.
+    @param context - sender context
+    @param instance - snort instance info.
+*/
+define snort_instance_details {
+       u32 context;
+       u32 instance_index;
+       u32 shm_size;
+       u32 shm_fd;
+       u8 drop_on_disconnect;
+       u32 snort_client_index;
+       string name[];
+};
+
+service {
+  rpc snort_interface_get returns snort_interface_get_reply
+    stream snort_interface_details;
+};
+
+/** \brief Get snort interface(s).
+    @param client_index - opaque cookie to identify the sender.
+    @param context - sender context
+    @param cursor - current iterator value (all requested).
+    @param sw_if_index - sw if index (~0 for all).
+*/
+define snort_interface_get
+{
+       u32 client_index;
+       u32 context;
+       u32 cursor;
+       u32 sw_if_index;
+};
+
+/** \brief Reply for snort interface(s).
+    @param context - sender context
+    @param retval - return code for the request.
+    @param cursor - iterator value to continue with (if there is more).
+*/
+define snort_interface_get_reply
+{
+       u32 context;
+       i32 retval;
+       u32 cursor;
+};
+
+/** \brief Details of a snort interface.
+    @param context - sender context
+    @param sw_if_index - interface index
+    @param instance_index - snort instance the interface is attached to.
+*/
+define snort_interface_details {
+       u32 context;
+       u32 sw_if_index;
+       u32 instance_index;
+};
+
+service {
+  rpc snort_client_get returns snort_client_get_reply
+    stream snort_client_details;
+};
+
+/** \brief Get snort clients.
+    @param client_index - opaque cookie to identify the sender.
+    @param context - sender context
+    @param cursor - current iterator value (all requested).
+    @param client_index (~0 for all).
+*/
+define snort_client_get
+{
+       u32 client_index;
+       u32 context;
+       u32 cursor;
+       u32 snort_client_index;
+};
+
+/** \brief Reply for snort clients.
+    @param context - sender context
+    @param retval - return code for the request.
+    @param cursor - iterator value to continue with (if there is more).
+*/
+define snort_client_get_reply
+{
+       u32 context;
+       i32 retval;
+       u32 cursor;
+};
+
+/** \brief Details of a snort client.
+    @param context - sender context
+    @param client index
+    @param instance_index - snort instance of the client.
+*/
+define snort_client_details {
+       u32 context;
+       u32 client_index;
+       u32 instance_index;
+};
index 79299aa..c7e856c 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <vppinfra/error.h>
 #include <vppinfra/socket.h>
+#include <vppinfra/file.h>
 #include <vlib/vlib.h>
 #include <snort/daq_vpp.h>
 
@@ -78,8 +79,11 @@ typedef struct
   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;
@@ -103,13 +107,17 @@ typedef enum
   }
 
 /* functions */
-clib_error_t *snort_instance_create (vlib_main_t *vm, char *name,
-                                    u8 log2_queue_sz, u8 drop_on_disconnect);
-clib_error_t *snort_interface_enable_disable (vlib_main_t *vm,
-                                             char *instance_name,
-                                             u32 sw_if_index, int is_enable,
-                                             snort_attach_dir_t dir);
-clib_error_t *snort_set_node_mode (vlib_main_t *vm, u32 mode);
+snort_main_t *snort_get_main ();
+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);
+int snort_interface_enable_disable (vlib_main_t *vm, char *instance_name,
+                                   u32 sw_if_index, int is_enable,
+                                   snort_attach_dir_t dir);
+int snort_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);
 
 always_inline void
 snort_freelist_init (u32 *fl)
diff --git a/src/plugins/snort/snort_api.c b/src/plugins/snort/snort_api.c
new file mode 100644 (file)
index 0000000..334a84b
--- /dev/null
@@ -0,0 +1,398 @@
+#include <vlib/vlib.h>
+#include <vnet/plugin/plugin.h>
+#include <snort/snort.h>
+#include <vlibapi/api_types.h>
+
+#include <snort/snort.api_enum.h>
+#include <snort/snort.api_types.h>
+
+#include <vlibmemory/api.h>
+#include <vnet/ip/ip_types_api.h>
+#include <vnet/format_fns.h>
+#include <vnet/api_errno.h>
+
+/**
+ * Base message ID fot the plugin
+ */
+static u32 snort_base_msg_id;
+#define REPLY_MSG_ID_BASE snort_base_msg_id
+
+#include <vlibapi/api_helper_macros.h>
+
+#include <vnet/vnet.h>
+
+#include <vlibapi/api.h>
+#include <sys/eventfd.h>
+
+VLIB_REGISTER_LOG_CLASS (snort_log, static) = {
+  .class_name = "snort",
+};
+
+#define log_debug(fmt, ...) vlib_log_debug (snort_log.class, fmt, __VA_ARGS__)
+#define log_err(fmt, ...)   vlib_log_err (snort_log.class, fmt, __VA_ARGS__)
+
+static void
+vl_api_snort_instance_create_t_handler (vl_api_snort_instance_create_t *mp)
+{
+  vlib_main_t *vm = vlib_get_main ();
+  vl_api_snort_instance_create_reply_t *rmp;
+  char *name = vl_api_from_api_to_new_c_string (&mp->name);
+  u32 queue_sz = clib_net_to_host_u32 (mp->queue_size);
+  u8 drop_on_disconnect = mp->drop_on_disconnect;
+  int rv = 0;
+  u32 instance_index = ~0;
+  snort_instance_t *si;
+
+  rv =
+    snort_instance_create (vm, name, min_log2 (queue_sz), drop_on_disconnect);
+
+  if ((si = snort_get_instance_by_name (name)))
+    {
+      instance_index = si->index;
+    }
+
+  REPLY_MACRO2 (VL_API_SNORT_INSTANCE_CREATE_REPLY, ({
+                 rmp->instance_index = clib_host_to_net_u32 (instance_index);
+               }));
+}
+
+static void
+vl_api_snort_instance_delete_t_handler (vl_api_snort_instance_delete_t *mp)
+{
+  vlib_main_t *vm = vlib_get_main ();
+  vl_api_snort_instance_delete_reply_t *rmp;
+  u32 instance_index = clib_net_to_host_u32 (mp->instance_index);
+  int rv;
+
+  rv = snort_instance_delete (vm, instance_index);
+
+  REPLY_MACRO (VL_API_SNORT_INSTANCE_DELETE_REPLY);
+}
+
+static void
+vl_api_snort_interface_attach_t_handler (vl_api_snort_interface_attach_t *mp)
+{
+  vlib_main_t *vm = vlib_get_main ();
+  vl_api_snort_interface_attach_reply_t *rmp;
+  u32 instance_index = clib_net_to_host_u32 (mp->instance_index);
+  snort_instance_t *instance = 0;
+  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;
+
+  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);
+
+  REPLY_MACRO (VL_API_SNORT_INTERFACE_ATTACH_REPLY);
+}
+
+static void
+send_snort_instance_details (const snort_instance_t *instance,
+                            vl_api_registration_t *rp, u32 context)
+{
+  vl_api_snort_instance_details_t *rmp;
+  u32 name_len = vec_len (instance->name);
+
+  REPLY_MACRO_DETAILS5 (
+    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->shm_size = clib_host_to_net_u32 (instance->shm_size);
+      rmp->shm_fd = clib_host_to_net_u32 (instance->shm_fd);
+      rmp->drop_on_disconnect = instance->drop_on_disconnect;
+    }));
+}
+
+static void
+vl_api_snort_instance_get_t_handler (vl_api_snort_instance_get_t *mp)
+{
+  snort_main_t *sm = snort_get_main ();
+  snort_instance_t *instance = 0;
+  vl_api_snort_instance_get_reply_t *rmp;
+  u32 instance_index;
+  int rv = 0;
+
+  instance_index = clib_net_to_host_u32 (mp->instance_index);
+
+  if (instance_index == INDEX_INVALID)
+    {
+      /* clang-format off */
+      REPLY_AND_DETAILS_MACRO (
+        VL_API_SNORT_INSTANCE_GET_REPLY, sm->instances, ({
+         instance = pool_elt_at_index (sm->instances, cursor);
+          send_snort_instance_details (instance, rp, mp->context);
+        }));
+      /* clang-format on */
+    }
+  else
+    {
+      instance = snort_get_instance_by_index (instance_index);
+
+      if (instance)
+       {
+         vl_api_registration_t *rp =
+           vl_api_client_index_to_registration (mp->client_index);
+
+         if (rp == NULL)
+           {
+             return;
+           }
+
+         send_snort_instance_details (instance, rp, mp->context);
+       }
+      else
+       {
+         rv = VNET_API_ERROR_NO_SUCH_ENTRY;
+       }
+
+      /* clang-format off */
+      REPLY_MACRO2 (VL_API_SNORT_INSTANCE_GET_REPLY, ({
+        rmp->cursor = INDEX_INVALID;
+      }));
+      /* clang-format on */
+    }
+}
+
+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 *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->instance_by_sw_if_index) == 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->instance_by_sw_if_index,
+       mp, rmp, rv, ({
+          index = vec_elt_at_index (sm->instance_by_sw_if_index, cursor);
+          send_snort_interface_details (cursor, *index, rp, mp->context);
+       }))
+      /* clang-format on */
+    }
+  else
+    {
+      index = vec_elt_at_index (sm->instance_by_sw_if_index, sw_if_index);
+      if (snort_get_instance_by_index (index[0]))
+       {
+         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, *index, 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 */
+    }
+}
+
+static void
+send_snort_client_details (const snort_client_t *client,
+                          vl_api_registration_t *rp, u32 context)
+{
+  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);
+                           }));
+    }
+}
+
+static void
+vl_api_snort_client_get_t_handler (vl_api_snort_client_get_t *mp)
+{
+  snort_main_t *sm = snort_get_main ();
+  snort_client_t *client;
+  vl_api_snort_client_get_reply_t *rmp;
+  u32 client_index;
+  int rv = 0;
+
+  client_index = clib_net_to_host_u32 (mp->snort_client_index);
+
+  if (client_index == INDEX_INVALID)
+    {
+      /* clang-format off */
+      REPLY_AND_DETAILS_MACRO (
+        VL_API_SNORT_CLIENT_GET_REPLY, sm->clients, ({
+          client = pool_elt_at_index (sm->clients, cursor);
+          send_snort_client_details (client, rp, mp->context);
+        }));
+      /* clang-format on */
+    }
+  else
+    {
+      client = pool_elt_at_index (sm->clients, client_index);
+
+      if (client)
+       {
+         vl_api_registration_t *rp =
+           vl_api_client_index_to_registration (mp->client_index);
+
+         if (rp == NULL)
+           {
+             return;
+           }
+
+         send_snort_client_details (client, rp, mp->context);
+       }
+      else
+       {
+         rv = VNET_API_ERROR_NO_SUCH_ENTRY;
+       }
+
+      /* clang-format off */
+      REPLY_MACRO2 (VL_API_SNORT_CLIENT_GET_REPLY, ({
+        rmp->cursor = INDEX_INVALID;
+      }));
+      /* clang-format on */
+    }
+}
+
+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);
+    }
+
+  REPLY_MACRO (VL_API_SNORT_CLIENT_DISCONNECT_REPLY);
+}
+
+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);
+
+  REPLY_MACRO (VL_API_SNORT_INSTANCE_DISCONNECT_REPLY);
+}
+
+static void
+vl_api_snort_interface_detach_t_handler (vl_api_snort_interface_detach_t *mp)
+{
+  vlib_main_t *vm = vlib_get_main ();
+  vl_api_snort_interface_detach_reply_t *rmp;
+  u32 sw_if_index = clib_net_to_host_u32 (mp->sw_if_index);
+  int rv;
+
+  rv = snort_interface_enable_disable (vm, NULL, sw_if_index,
+                                      0 /* is_enable */, 0);
+
+  REPLY_MACRO (VL_API_SNORT_INTERFACE_DETACH_REPLY);
+}
+
+static void
+vl_api_snort_input_mode_get_t_handler (vl_api_snort_input_mode_get_t *mp)
+{
+  snort_main_t *sm = &snort_main;
+  vl_api_snort_input_mode_get_reply_t *rmp;
+  int rv = 0;
+
+  REPLY_MACRO2 (VL_API_SNORT_INPUT_MODE_GET_REPLY, ({
+                 rmp->snort_mode = clib_host_to_net_u32 (sm->input_mode);
+               }));
+}
+
+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);
+
+  REPLY_MACRO (VL_API_SNORT_INPUT_MODE_SET_REPLY);
+}
+
+/* API definitions */
+#include <snort/snort.api.c>
+
+clib_error_t *
+snort_init_api (vlib_main_t *vm)
+{
+  /* Add our API messages to the global name_crc hash table */
+  snort_base_msg_id = setup_message_id_table ();
+
+  return NULL;
+}
+
+VLIB_INIT_FUNCTION (snort_init_api);
index 352eaa3..19401cb 100644 (file)
@@ -30,6 +30,7 @@ class TestSnort(VppTestCase):
     def test_snort_cli(self):
         # TODO: add a test with packets
         # { cli command : part of the expected reply }
+        print("TEST SNORT CLI")
         commands_replies = {
             "snort create-instance name snortTest queue-size 16 on-disconnect drop": "",
             "snort create-instance name snortTest2 queue-size 16 on-disconnect pass": "",
@@ -43,6 +44,7 @@ class TestSnort(VppTestCase):
             "snort mode interrupt": "",
             "snort detach interface pg0": "",
             "snort detach interface pg1": "",
+            "snort delete instance snortTest": "",
         }
 
         for command, reply in commands_replies.items():
@@ -50,5 +52,101 @@ class TestSnort(VppTestCase):
             self.assertIn(reply, actual_reply)
 
 
[email protected]("snort" in config.excluded_plugins, "Exclude snort plugin test")
+class TestSnortVapi(VppTestCase):
+    """Snort plugin test [VAPI]"""
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestSnortVapi, cls).setUpClass()
+        try:
+            cls.create_pg_interfaces(range(2))
+            for i in cls.pg_interfaces:
+                i.config_ip4()
+                i.resolve_arp()
+                i.admin_up()
+        except Exception:
+            cls.tearDownClass()
+            raise
+
+    @classmethod
+    def tearDownClass(cls):
+        for i in cls.pg_interfaces:
+            i.unconfig_ip4()
+            i.admin_down()
+        super(TestSnortVapi, cls).tearDownClass()
+
+    def test_snort_01_modes_set_interrupt(self):
+        """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)
+
+    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)
+
+    def test_snort_03_create(self):
+        """Create two snort instances"""
+        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=32, drop_on_disconnect=1, name="snortTest1"
+        )
+        self.assertEqual(reply.instance_index, 1)
+        reply = self.vapi.cli("show snort instances")
+        self.assertIn("snortTest0", reply)
+        self.assertIn("snortTest1", reply)
+
+    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
+        )
+        try:
+            reply = self.vapi.snort_interface_attach(
+                instance_index=1, sw_if_index=1, snort_dir=1
+            )
+        except:
+            pass
+        else:
+            self.assertNotEqual(reply.retval, 0)
+
+        reply = self.vapi.snort_interface_attach(
+            instance_index=1, sw_if_index=2, snort_dir=3
+        )
+        reply = self.vapi.cli("show snort interfaces")
+        self.assertIn("snortTest0", reply)
+        self.assertIn("snortTest1", reply)
+
+    def test_snort_05_delete_instance(self):
+        """Instances can be deleted"""
+        reply = self.vapi.snort_instance_delete(instance_index=0)
+        reply = self.vapi.cli("show snort interfaces")
+        self.assertNotIn("snortTest0", reply)
+        self.assertIn("snortTest1", reply)
+        reply = self.vapi.cli("show snort interfaces")
+        self.assertNotIn("pg0", reply)
+        self.assertIn("pg1", reply)
+
+    def test_snort_06_detach_if(self):
+        """Interfaces can be detached"""
+        try:
+            reply = self.vapi.snort_interface_detach(sw_if_index=1)
+        except:
+            pass
+        else:
+            self.assertNotEqual(reply.retval, 0)
+        reply = self.vapi.snort_interface_detach(sw_if_index=2)
+        reply = self.vapi.cli("show snort interfaces")
+        self.assertNotIn("pg1", reply)
+
+
 if __name__ == "__main__":
     unittest.main(testRunner=VppTestRunner)