1 /* SPDX-License-Identifier: Apache-2.0
2 * Copyright(c) 2021 Cisco Systems, Inc.
6 #include <vnet/plugin/plugin.h>
7 #include <vpp/app/version.h>
8 #include <snort/snort.h>
10 #include <sys/eventfd.h>
12 snort_main_t snort_main;
14 VLIB_REGISTER_LOG_CLASS (snort_log, static) = {
15 .class_name = "snort",
18 #define log_debug(fmt, ...) vlib_log_debug (snort_log.class, fmt, __VA_ARGS__)
19 #define log_err(fmt, ...) vlib_log_err (snort_log.class, fmt, __VA_ARGS__)
22 snort_client_disconnect (clib_file_t *uf)
24 vlib_main_t *vm = vlib_get_main ();
26 snort_main_t *sm = &snort_main;
27 snort_client_t *c = pool_elt_at_index (sm->clients, uf->private_data);
29 if (c->instance_index != ~0)
31 snort_per_thread_data_t *ptd =
32 vec_elt_at_index (sm->per_thread_data, vm->thread_index);
33 snort_instance_t *si =
34 pool_elt_at_index (sm->instances, c->instance_index);
35 vec_foreach (qp, si->qpairs)
36 __atomic_store_n (&qp->ready, 1, __ATOMIC_RELEASE);
38 si->client_index = ~0;
39 clib_interrupt_set (ptd->interrupts, uf->private_data);
40 vlib_node_set_interrupt_pending (vm, snort_deq_node.index);
43 clib_file_del (&file_main, uf);
44 clib_socket_close (&c->socket);
45 pool_put (sm->clients, c);
48 static snort_instance_t *
49 snort_get_instance_by_name (char *name)
51 snort_main_t *sm = &snort_main;
53 if ((p = hash_get_mem (sm->instance_by_name, name)) == 0)
56 return vec_elt_at_index (sm->instances, p[0]);
61 snort_conn_fd_read_ready (clib_file_t *uf)
63 vlib_main_t *vm = vlib_get_main ();
64 snort_main_t *sm = &snort_main;
65 snort_client_t *c = pool_elt_at_index (sm->clients, uf->private_data);
66 vlib_buffer_pool_t *bp;
69 snort_client_msg_queue_elt *e;
75 log_debug ("fd_read_ready: client %u", uf->private_data);
77 if ((err = clib_socket_recvmsg (&c->socket, &msg, sizeof (msg), 0, 0)))
79 log_err ("client recvmsg error: %U", format_clib_error, err);
80 snort_client_disconnect (uf);
81 clib_error_free (err);
85 if (msg.type != DAQ_VPP_MSG_TYPE_HELLO)
87 log_err ("unexpeced message recieved from client", 0);
88 snort_client_disconnect (uf);
92 msg.hello.inst_name[DAQ_VPP_INST_NAME_LEN - 1] = 0;
93 name = msg.hello.inst_name;
95 log_debug ("fd_read_ready: connect instance %s", name);
97 if ((si = snort_get_instance_by_name (name)) == 0)
99 log_err ("unknown instance '%s' requested by client", name);
100 snort_client_disconnect (uf);
104 vec_foreach (qp, si->qpairs)
106 u32 ready = __atomic_load_n (&qp->ready, __ATOMIC_ACQUIRE);
109 log_err ("instance '%s' is not ready to accept connections", name);
110 snort_client_disconnect (uf);
115 base = (u8 *) si->shm_base;
117 if (si->client_index != ~0)
119 log_err ("client already connected to instance '%s'", name);
120 snort_client_disconnect (uf);
123 si->client_index = uf->private_data;
124 c->instance_index = si->index;
126 log_debug ("fd_read_ready: connect instance index %u", si->index);
128 clib_fifo_add2 (c->msg_queue, e);
129 e->msg.type = DAQ_VPP_MSG_TYPE_CONFIG;
130 e->msg.config.num_bpools = vec_len (vm->buffer_main->buffer_pools);
131 e->msg.config.num_qpairs = vec_len (si->qpairs);
132 e->msg.config.shm_size = si->shm_size;
133 e->fds[0] = si->shm_fd;
136 vec_foreach (bp, vm->buffer_main->buffer_pools)
138 vlib_physmem_map_t *pm;
139 pm = vlib_physmem_get_map (vm, bp->physmem_map_index);
140 clib_fifo_add2 (c->msg_queue, e);
141 e->msg.type = DAQ_VPP_MSG_TYPE_BPOOL;
142 e->msg.bpool.size = pm->n_pages << pm->log2_page_size;
147 vec_foreach (qp, si->qpairs)
149 clib_fifo_add2 (c->msg_queue, e);
150 e->msg.type = DAQ_VPP_MSG_TYPE_QPAIR;
151 e->msg.qpair.log2_queue_size = qp->log2_queue_size;
152 e->msg.qpair.desc_table_offset = (u8 *) qp->descriptors - base;
153 e->msg.qpair.enq_ring_offset = (u8 *) qp->enq_ring - base;
154 e->msg.qpair.deq_ring_offset = (u8 *) qp->deq_ring - base;
155 e->msg.qpair.enq_head_offset = (u8 *) qp->enq_head - base;
156 e->msg.qpair.deq_head_offset = (u8 *) qp->deq_head - base;
157 e->fds[0] = qp->enq_fd;
158 e->fds[1] = qp->deq_fd;
162 clib_file_set_data_available_to_write (&file_main, c->file_index, 1);
166 static clib_error_t *
167 snort_conn_fd_write_ready (clib_file_t *uf)
169 snort_main_t *sm = &snort_main;
170 snort_client_t *c = pool_elt_at_index (sm->clients, uf->private_data);
171 snort_client_msg_queue_elt *e;
173 log_debug ("fd_write_ready: client %u", uf->private_data);
174 clib_fifo_sub2 (c->msg_queue, e);
176 if (clib_fifo_elts (c->msg_queue) == 0)
177 clib_file_set_data_available_to_write (&file_main, c->file_index, 0);
179 return clib_socket_sendmsg (&c->socket, &e->msg, sizeof (*e), e->fds,
184 snort_conn_fd_error (clib_file_t *uf)
186 log_debug ("fd_error: client %u", uf->private_data);
190 static clib_error_t *
191 snort_deq_ready (clib_file_t *uf)
193 vlib_main_t *vm = vlib_get_main ();
194 snort_main_t *sm = &snort_main;
195 snort_per_thread_data_t *ptd =
196 vec_elt_at_index (sm->per_thread_data, vm->thread_index);
200 bytes_read = read (uf->file_descriptor, &counter, sizeof (counter));
203 return clib_error_return (0, "client closed socket");
206 if (bytes_read < sizeof (counter))
208 return clib_error_return (0, "unexpected truncated read");
211 clib_interrupt_set (ptd->interrupts, uf->private_data);
212 vlib_node_set_interrupt_pending (vm, snort_deq_node.index);
216 static clib_error_t *
217 snort_conn_fd_accept_ready (clib_file_t *uf)
219 snort_main_t *sm = &snort_main;
222 clib_error_t *err = 0;
223 clib_file_t t = { 0 };
225 pool_get_zero (sm->clients, c);
226 c->instance_index = ~0;
229 if ((err = clib_socket_accept (sm->listener, s)))
231 log_err ("%U", format_clib_error, err);
232 pool_put (sm->clients, c);
236 t.read_function = snort_conn_fd_read_ready;
237 t.write_function = snort_conn_fd_write_ready;
238 t.error_function = snort_conn_fd_error;
239 t.file_descriptor = s->fd;
240 t.private_data = c - sm->clients;
241 t.description = format (0, "snort client");
242 c->file_index = clib_file_add (&file_main, &t);
244 log_debug ("snort_conn_fd_accept_ready: client %u", t.private_data);
248 static clib_error_t *
249 snort_listener_init (vlib_main_t *vm)
251 snort_main_t *sm = &snort_main;
253 clib_file_t t = { 0 };
259 s = clib_mem_alloc (sizeof (clib_socket_t));
260 clib_memset (s, 0, sizeof (clib_socket_t));
261 s->config = (char *) sm->socket_name;
262 s->flags = CLIB_SOCKET_F_IS_SERVER | CLIB_SOCKET_F_ALLOW_GROUP_WRITE |
263 CLIB_SOCKET_F_SEQPACKET | CLIB_SOCKET_F_PASSCRED;
265 if ((err = clib_socket_init (s)))
271 t.read_function = snort_conn_fd_accept_ready;
272 t.file_descriptor = s->fd;
273 t.description = format (0, "snort listener %s", s->config);
274 log_debug ("%v", t.description);
275 clib_file_add (&file_main, &t);
283 snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz,
284 u8 drop_on_disconnect)
286 vlib_thread_main_t *tm = vlib_get_thread_main ();
287 snort_main_t *sm = &snort_main;
288 snort_instance_t *si;
289 clib_error_t *err = 0;
291 u8 *base = CLIB_MEM_VM_MAP_FAILED;
294 u32 qpair_mem_sz = 0;
295 u32 qsz = 1 << log2_queue_sz;
296 u8 align = CLIB_CACHE_LINE_BYTES;
298 if (snort_get_instance_by_name (name))
299 return clib_error_return (0, "instance already exists");
301 /* descriptor table */
302 qpair_mem_sz += round_pow2 (qsz * sizeof (daq_vpp_desc_t), align);
304 /* enq and deq ring */
305 qpair_mem_sz += 2 * round_pow2 (qsz * sizeof (u32), align);
307 /* enq and deq head pointer */
308 qpair_mem_sz += 2 * round_pow2 (sizeof (u32), align);
310 size = round_pow2 ((uword) tm->n_vlib_mains * qpair_mem_sz,
311 clib_mem_get_page_size ());
312 fd = clib_mem_vm_create_fd (CLIB_MEM_PAGE_SZ_DEFAULT, "snort instance %s",
317 err = clib_error_return (0, "memory fd failure: %U", format_clib_error,
318 clib_mem_get_last_error ());
322 if ((ftruncate (fd, size)) == -1)
324 err = clib_error_return (0, "ftruncate failure");
328 base = clib_mem_vm_map_shared (0, size, fd, 0, "snort instance %s", name);
330 if (base == CLIB_MEM_VM_MAP_FAILED)
332 err = clib_error_return (0, "mmap failure");
336 pool_get_zero (sm->instances, si);
337 si->index = si - sm->instances;
338 si->client_index = ~0;
342 si->name = format (0, "%s%c", name, 0);
343 si->drop_on_disconnect = drop_on_disconnect;
344 index = si - sm->instances;
345 hash_set_mem (sm->instance_by_name, si->name, index);
347 log_debug ("instnce '%s' createed with fd %d at %p, len %u", name, fd, base,
350 vec_validate_aligned (sm->per_thread_data, tm->n_vlib_mains - 1,
351 CLIB_CACHE_LINE_BYTES);
352 vec_validate_aligned (si->qpairs, tm->n_vlib_mains - 1,
353 CLIB_CACHE_LINE_BYTES);
355 for (int i = 0; i < tm->n_vlib_mains; i++)
357 snort_qpair_t *qp = vec_elt_at_index (si->qpairs, i);
358 snort_per_thread_data_t *ptd = vec_elt_at_index (sm->per_thread_data, i);
359 clib_file_t t = { 0 };
361 qp->log2_queue_size = log2_queue_sz;
362 qp->descriptors = (void *) base;
363 base += round_pow2 (qsz * sizeof (daq_vpp_desc_t), align);
364 qp->enq_ring = (void *) base;
365 base += round_pow2 (qsz * sizeof (u32), align);
366 qp->deq_ring = (void *) base;
367 base += round_pow2 (qsz * sizeof (u32), align);
368 qp->enq_head = (void *) base;
369 base += round_pow2 (sizeof (u32), align);
370 qp->deq_head = (void *) base;
371 base += round_pow2 (sizeof (u32), align);
372 qp->enq_fd = eventfd (0, EFD_NONBLOCK);
373 qp->deq_fd = eventfd (0, EFD_NONBLOCK);
374 vec_validate_aligned (qp->buffer_indices, qsz - 1,
375 CLIB_CACHE_LINE_BYTES);
376 vec_validate_aligned (qp->next_indices, qsz - 1, CLIB_CACHE_LINE_BYTES);
377 clib_memset_u32 (qp->buffer_indices, ~0, qsz);
379 /* pre-populate freelist */
380 vec_validate_aligned (qp->freelist, qsz - 1, CLIB_CACHE_LINE_BYTES);
381 snort_freelist_init (qp->freelist);
383 /* listen on dequeue events */
384 t.read_function = snort_deq_ready;
385 t.file_descriptor = qp->deq_fd;
386 t.private_data = si->index;
388 format (0, "snort dequeue for instance '%s' qpair %u", si->name, i);
389 qp->deq_fd_file_index = clib_file_add (&file_main, &t);
391 clib_file_set_polling_thread (&file_main, qp->deq_fd_file_index, i);
392 clib_interrupt_resize (&ptd->interrupts, vec_len (sm->instances));
395 for (i = 0; i < vlib_get_n_threads (); i++)
396 vlib_node_set_state (vlib_get_main_by_index (i), snort_deq_node.index,
402 if (base != CLIB_MEM_VM_MAP_FAILED)
403 clib_mem_vm_unmap (base);
411 snort_interface_enable_disable (vlib_main_t *vm, char *instance_name,
412 u32 sw_if_index, int is_enable)
414 snort_main_t *sm = &snort_main;
415 vnet_main_t *vnm = vnet_get_main ();
416 snort_instance_t *si;
417 clib_error_t *err = 0;
422 if ((si = snort_get_instance_by_name (instance_name)) == 0)
424 err = clib_error_return (0, "unknown instance '%s'", instance_name);
428 vec_validate_init_empty (sm->instance_by_sw_if_index, sw_if_index, ~0);
430 index = sm->instance_by_sw_if_index[sw_if_index];
433 si = vec_elt_at_index (sm->instances, index);
434 err = clib_error_return (0,
435 "interface %U already assgined to "
437 format_vnet_sw_if_index_name, vnm,
438 sw_if_index, si->name);
442 index = sm->instance_by_sw_if_index[sw_if_index] = si->index;
443 vnet_feature_enable_disable ("ip4-unicast", "snort-enq", sw_if_index, 1,
444 &index, sizeof (index));
448 if (sw_if_index >= vec_len (sm->instance_by_sw_if_index) ||
449 sm->instance_by_sw_if_index[sw_if_index] == ~0)
452 clib_error_return (0,
453 "interface %U is not assigned to snort "
455 format_vnet_sw_if_index_name, vnm, sw_if_index);
458 index = sm->instance_by_sw_if_index[sw_if_index];
459 si = vec_elt_at_index (sm->instances, index);
461 sm->instance_by_sw_if_index[sw_if_index] = ~0;
462 vnet_feature_enable_disable ("ip4-unicast", "snort-enq", sw_if_index, 0,
463 &index, sizeof (index));
468 log_err ("%U", format_clib_error, err);
473 snort_set_node_mode (vlib_main_t *vm, u32 mode)
476 snort_main.input_mode = mode;
477 for (i = 0; i < vlib_get_n_threads (); i++)
478 vlib_node_set_state (vlib_get_main_by_index (i), snort_deq_node.index,
484 snort_set_default_socket (snort_main_t *sm, u8 *socket_name)
490 socket_name = (u8 *) DAQ_VPP_DEFAULT_SOCKET_FILE;
493 format (0, "%s/%s", vlib_unix_get_runtime_dir (), socket_name);
494 vec_terminate_c_string (sm->socket_name);
497 static clib_error_t *
498 snort_init (vlib_main_t *vm)
500 snort_main_t *sm = &snort_main;
501 sm->input_mode = VLIB_NODE_STATE_INTERRUPT;
502 sm->instance_by_name = hash_create_string (0, sizeof (uword));
503 vlib_buffer_pool_t *bp;
505 vec_foreach (bp, vm->buffer_main->buffer_pools)
507 vlib_physmem_map_t *pm =
508 vlib_physmem_get_map (vm, bp->physmem_map_index);
509 vec_add1 (sm->buffer_pool_base_addrs, pm->base);
512 if (!sm->socket_name)
513 snort_set_default_socket (sm, 0);
515 return snort_listener_init (vm);
518 VLIB_INIT_FUNCTION (snort_init);
520 VLIB_PLUGIN_REGISTER () = {
521 .version = VPP_BUILD_VER,
522 .description = "Snort",
525 VNET_FEATURE_INIT (snort_enq, static) = {
526 .arc_name = "ip4-unicast",
527 .node_name = "snort-enq",
528 .runs_before = VNET_FEATURES ("ip4-lookup"),