summaryrefslogtreecommitdiffstats
path: root/src/plugins/snort/main.c
diff options
context:
space:
mode:
authorDamjan Marion <damarion@cisco.com>2021-01-27 21:17:48 +0100
committerDamjan Marion <dmarion@me.com>2021-07-16 11:36:32 +0000
commit839b1473e96800f577d06b14234aef9b573bb303 (patch)
tree7e87c52cafc49a1a66c2ba3e56af1e8e22f92144 /src/plugins/snort/main.c
parent0ec7dad7a00852663eb88554561347987f87bb53 (diff)
snort: snort3 plugin and DAQ
Zero copy interface which exposes VPP buffers to snort instance(s). Includes VPP DAQ which is compiled only if libdaq 3 API headers are available. Type: feature Change-Id: I96611b43f94fbae091e7391589e0454ae66de88b Signed-off-by: Damjan Marion <damarion@cisco.com> Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
Diffstat (limited to 'src/plugins/snort/main.c')
-rw-r--r--src/plugins/snort/main.c520
1 files changed, 520 insertions, 0 deletions
diff --git a/src/plugins/snort/main.c b/src/plugins/snort/main.c
new file mode 100644
index 00000000000..37b517215bc
--- /dev/null
+++ b/src/plugins/snort/main.c
@@ -0,0 +1,520 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2021 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+#include <snort/snort.h>
+
+#include <sys/eventfd.h>
+
+snort_main_t snort_main;
+
+VLIB_REGISTER_LOG_CLASS (snort_log, static) = {
+ .class_name = "snort",
+ .default_syslog_level = VLIB_LOG_LEVEL_DEBUG,
+};
+
+#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
+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);
+}
+
+static 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 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;
+ }
+ }
+
+ 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;
+}
+
+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);
+ u64 counter;
+
+ if (read (uf->file_descriptor, &counter, sizeof (counter)) < 0)
+ return clib_error_return (0, "client closed socket");
+
+ clib_interrupt_set (ptd->interrupts, uf->private_data);
+ vlib_node_set_interrupt_pending (vm, snort_deq_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 clib_error_t *
+snort_listener_init (vlib_main_t *vm)
+{
+ 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->flags = CLIB_SOCKET_F_IS_SERVER | CLIB_SOCKET_F_ALLOW_GROUP_WRITE |
+ CLIB_SOCKET_F_SEQPACKET | CLIB_SOCKET_F_PASSCRED;
+
+ if ((err = clib_socket_init (s)))
+ {
+ 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;
+}
+
+clib_error_t *
+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;
+ int fd = -1;
+ u32 qpair_mem_sz = 0;
+ u32 qsz = 1 << log2_queue_sz;
+ u8 align = CLIB_CACHE_LINE_BYTES;
+
+ if (snort_get_instance_by_name (name))
+ return clib_error_return (0, "instance already exists");
+
+ /* descriptor table */
+ qpair_mem_sz += round_pow2 (qsz * sizeof (daq_vpp_desc_t), align);
+
+ /* enq and deq ring */
+ qpair_mem_sz += 2 * round_pow2 (qsz * sizeof (u32), align);
+
+ /* enq and deq head pointer */
+ qpair_mem_sz += 2 * round_pow2 (sizeof (u32), align);
+
+ size =
+ round_pow2 (tm->n_vlib_mains * qpair_mem_sz, clib_mem_get_page_size ());
+ fd = clib_mem_vm_create_fd (CLIB_MEM_PAGE_SZ_DEFAULT, "snort instance %s",
+ name);
+
+ if (fd == -1)
+ {
+ err = clib_error_return (0, "memory fd failure: %U", format_clib_error,
+ clib_mem_get_last_error ());
+ goto done;
+ }
+
+ if ((ftruncate (fd, size)) == -1)
+ {
+ err = clib_error_return (0, "ftruncate failure");
+ goto done;
+ }
+
+ base = clib_mem_vm_map_shared (0, size, fd, 0, "snort instance %s", name);
+
+ if (base == CLIB_MEM_VM_MAP_FAILED)
+ {
+ err = clib_error_return (0, "mmap failure");
+ goto done;
+ }
+
+ pool_get_zero (sm->instances, si);
+ si->index = si - sm->instances;
+ si->client_index = ~0;
+ si->shm_base = base;
+ si->shm_fd = fd;
+ si->shm_size = size;
+ si->name = format (0, "%s%c", name, 0);
+ si->drop_on_disconnect = drop_on_disconnect;
+ 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++)
+ {
+ 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));
+ }
+
+ for (i = 0; i < vlib_get_n_threads (); i++)
+ vlib_node_set_state (vlib_get_main_by_index (i), snort_deq_node.index,
+ VLIB_NODE_STATE_INTERRUPT);
+
+done:
+ if (err)
+ {
+ if (base != CLIB_MEM_VM_MAP_FAILED)
+ clib_mem_vm_unmap (base);
+ if (fd != -1)
+ close (fd);
+ }
+ return err;
+}
+
+clib_error_t *
+snort_interface_enable_disable (vlib_main_t *vm, char *instance_name,
+ u32 sw_if_index, int is_enable)
+{
+ snort_main_t *sm = &snort_main;
+ vnet_main_t *vnm = vnet_get_main ();
+ snort_instance_t *si;
+ clib_error_t *err = 0;
+ u32 index;
+
+ 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;
+ }
+
+ vec_validate_init_empty (sm->instance_by_sw_if_index, sw_if_index, ~0);
+
+ index = sm->instance_by_sw_if_index[sw_if_index];
+ if (index != ~0)
+ {
+ 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);
+ goto done;
+ }
+
+ index = sm->instance_by_sw_if_index[sw_if_index] = si->index;
+ vnet_feature_enable_disable ("ip4-unicast", "snort-enq", sw_if_index, 1,
+ &index, sizeof (index));
+ }
+ else
+ {
+ 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);
+ goto done;
+ }
+ index = sm->instance_by_sw_if_index[sw_if_index];
+ si = vec_elt_at_index (sm->instances, index);
+
+ sm->instance_by_sw_if_index[sw_if_index] = ~0;
+ vnet_feature_enable_disable ("ip4-unicast", "snort-enq", sw_if_index, 0,
+ &index, sizeof (index));
+ }
+
+done:
+ if (err)
+ log_err ("%U", format_clib_error, err);
+ return 0;
+}
+
+clib_error_t *
+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->instance_by_name = hash_create_string (0, sizeof (uword));
+ vlib_buffer_pool_t *bp;
+
+ vec_foreach (bp, vm->buffer_main->buffer_pools)
+ {
+ vlib_physmem_map_t *pm =
+ vlib_physmem_get_map (vm, bp->physmem_map_index);
+ vec_add1 (sm->buffer_pool_base_addrs, pm->base);
+ }
+
+ if (!sm->socket_name)
+ snort_set_default_socket (sm, 0);
+
+ return snort_listener_init (vm);
+}
+
+VLIB_INIT_FUNCTION (snort_init);
+
+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"),
+};