snort: snort3 plugin and DAQ
[vpp.git] / src / plugins / snort / main.c
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright(c) 2021 Cisco Systems, Inc.
3  */
4
5 #include <vlib/vlib.h>
6 #include <vnet/plugin/plugin.h>
7 #include <vpp/app/version.h>
8 #include <snort/snort.h>
9
10 #include <sys/eventfd.h>
11
12 snort_main_t snort_main;
13
14 VLIB_REGISTER_LOG_CLASS (snort_log, static) = {
15   .class_name = "snort",
16   .default_syslog_level = VLIB_LOG_LEVEL_DEBUG,
17 };
18
19 #define log_debug(fmt, ...) vlib_log_debug (snort_log.class, fmt, __VA_ARGS__)
20 #define log_err(fmt, ...)   vlib_log_err (snort_log.class, fmt, __VA_ARGS__)
21
22 static void
23 snort_client_disconnect (clib_file_t *uf)
24 {
25   vlib_main_t *vm = vlib_get_main ();
26   snort_qpair_t *qp;
27   snort_main_t *sm = &snort_main;
28   snort_client_t *c = pool_elt_at_index (sm->clients, uf->private_data);
29
30   if (c->instance_index != ~0)
31     {
32       snort_per_thread_data_t *ptd =
33         vec_elt_at_index (sm->per_thread_data, vm->thread_index);
34       snort_instance_t *si =
35         pool_elt_at_index (sm->instances, c->instance_index);
36       vec_foreach (qp, si->qpairs)
37         __atomic_store_n (&qp->ready, 1, __ATOMIC_RELEASE);
38
39       si->client_index = ~0;
40       clib_interrupt_set (ptd->interrupts, uf->private_data);
41       vlib_node_set_interrupt_pending (vm, snort_deq_node.index);
42     }
43
44   clib_file_del (&file_main, uf);
45   clib_socket_close (&c->socket);
46   pool_put (sm->clients, c);
47 }
48
49 static snort_instance_t *
50 snort_get_instance_by_name (char *name)
51 {
52   snort_main_t *sm = &snort_main;
53   uword *p;
54   if ((p = hash_get_mem (sm->instance_by_name, name)) == 0)
55     return 0;
56
57   return vec_elt_at_index (sm->instances, p[0]);
58   ;
59 }
60
61 static clib_error_t *
62 snort_conn_fd_read_ready (clib_file_t *uf)
63 {
64   vlib_main_t *vm = vlib_get_main ();
65   snort_main_t *sm = &snort_main;
66   snort_client_t *c = pool_elt_at_index (sm->clients, uf->private_data);
67   vlib_buffer_pool_t *bp;
68   snort_instance_t *si;
69   snort_qpair_t *qp;
70   snort_client_msg_queue_elt *e;
71   clib_error_t *err;
72   daq_vpp_msg_t msg;
73   char *name;
74   u8 *base;
75
76   log_debug ("fd_read_ready: client %u", uf->private_data);
77
78   if ((err = clib_socket_recvmsg (&c->socket, &msg, sizeof (msg), 0, 0)))
79     {
80       log_err ("client recvmsg error: %U", format_clib_error, err);
81       snort_client_disconnect (uf);
82       clib_error_free (err);
83       return 0;
84     }
85
86   if (msg.type != DAQ_VPP_MSG_TYPE_HELLO)
87     {
88       log_err ("unexpeced message recieved from client", 0);
89       snort_client_disconnect (uf);
90       return 0;
91     }
92
93   msg.hello.inst_name[DAQ_VPP_INST_NAME_LEN - 1] = 0;
94   name = msg.hello.inst_name;
95
96   log_debug ("fd_read_ready: connect instance %s", name);
97
98   if ((si = snort_get_instance_by_name (name)) == 0)
99     {
100       log_err ("unknown instance '%s' requested by client", name);
101       snort_client_disconnect (uf);
102       return 0;
103     }
104
105   vec_foreach (qp, si->qpairs)
106     {
107       u32 ready = __atomic_load_n (&qp->ready, __ATOMIC_ACQUIRE);
108       if (!ready)
109         {
110           log_err ("instance '%s' is not ready to accept connections", name);
111           snort_client_disconnect (uf);
112           return 0;
113         }
114     }
115
116   base = (u8 *) si->shm_base;
117
118   if (si->client_index != ~0)
119     {
120       log_err ("client already connected to instance '%s'", name);
121       snort_client_disconnect (uf);
122       return 0;
123     }
124   si->client_index = uf->private_data;
125   c->instance_index = si->index;
126
127   log_debug ("fd_read_ready: connect instance index %u", si->index);
128
129   clib_fifo_add2 (c->msg_queue, e);
130   e->msg.type = DAQ_VPP_MSG_TYPE_CONFIG;
131   e->msg.config.num_bpools = vec_len (vm->buffer_main->buffer_pools);
132   e->msg.config.num_qpairs = vec_len (si->qpairs);
133   e->msg.config.shm_size = si->shm_size;
134   e->fds[0] = si->shm_fd;
135   e->n_fds = 1;
136
137   vec_foreach (bp, vm->buffer_main->buffer_pools)
138     {
139       vlib_physmem_map_t *pm;
140       pm = vlib_physmem_get_map (vm, bp->physmem_map_index);
141       clib_fifo_add2 (c->msg_queue, e);
142       e->msg.type = DAQ_VPP_MSG_TYPE_BPOOL;
143       e->msg.bpool.size = pm->n_pages << pm->log2_page_size;
144       e->fds[0] = pm->fd;
145       e->n_fds = 1;
146     }
147
148   vec_foreach (qp, si->qpairs)
149     {
150       clib_fifo_add2 (c->msg_queue, e);
151       e->msg.type = DAQ_VPP_MSG_TYPE_QPAIR;
152       e->msg.qpair.log2_queue_size = qp->log2_queue_size;
153       e->msg.qpair.desc_table_offset = (u8 *) qp->descriptors - base;
154       e->msg.qpair.enq_ring_offset = (u8 *) qp->enq_ring - base;
155       e->msg.qpair.deq_ring_offset = (u8 *) qp->deq_ring - base;
156       e->msg.qpair.enq_head_offset = (u8 *) qp->enq_head - base;
157       e->msg.qpair.deq_head_offset = (u8 *) qp->deq_head - base;
158       e->fds[0] = qp->enq_fd;
159       e->fds[1] = qp->deq_fd;
160       e->n_fds = 2;
161     }
162
163   clib_file_set_data_available_to_write (&file_main, c->file_index, 1);
164   return 0;
165 }
166
167 static clib_error_t *
168 snort_conn_fd_write_ready (clib_file_t *uf)
169 {
170   snort_main_t *sm = &snort_main;
171   snort_client_t *c = pool_elt_at_index (sm->clients, uf->private_data);
172   snort_client_msg_queue_elt *e;
173
174   log_debug ("fd_write_ready: client %u", uf->private_data);
175   clib_fifo_sub2 (c->msg_queue, e);
176
177   if (clib_fifo_elts (c->msg_queue) == 0)
178     clib_file_set_data_available_to_write (&file_main, c->file_index, 0);
179
180   return clib_socket_sendmsg (&c->socket, &e->msg, sizeof (*e), e->fds,
181                               e->n_fds);
182 }
183
184 clib_error_t *
185 snort_conn_fd_error (clib_file_t *uf)
186 {
187   log_debug ("fd_error: client %u", uf->private_data);
188   return 0;
189 }
190
191 static clib_error_t *
192 snort_deq_ready (clib_file_t *uf)
193 {
194   vlib_main_t *vm = vlib_get_main ();
195   snort_main_t *sm = &snort_main;
196   snort_per_thread_data_t *ptd =
197     vec_elt_at_index (sm->per_thread_data, vm->thread_index);
198   u64 counter;
199
200   if (read (uf->file_descriptor, &counter, sizeof (counter)) < 0)
201     return clib_error_return (0, "client closed socket");
202
203   clib_interrupt_set (ptd->interrupts, uf->private_data);
204   vlib_node_set_interrupt_pending (vm, snort_deq_node.index);
205   return 0;
206 }
207
208 static clib_error_t *
209 snort_conn_fd_accept_ready (clib_file_t *uf)
210 {
211   snort_main_t *sm = &snort_main;
212   snort_client_t *c;
213   clib_socket_t *s;
214   clib_error_t *err = 0;
215   clib_file_t t = { 0 };
216
217   pool_get_zero (sm->clients, c);
218   c->instance_index = ~0;
219   s = &c->socket;
220
221   if ((err = clib_socket_accept (sm->listener, s)))
222     {
223       log_err ("%U", format_clib_error, err);
224       pool_put (sm->clients, c);
225       return err;
226     }
227
228   t.read_function = snort_conn_fd_read_ready;
229   t.write_function = snort_conn_fd_write_ready;
230   t.error_function = snort_conn_fd_error;
231   t.file_descriptor = s->fd;
232   t.private_data = c - sm->clients;
233   t.description = format (0, "snort client");
234   c->file_index = clib_file_add (&file_main, &t);
235
236   log_debug ("snort_conn_fd_accept_ready: client %u", t.private_data);
237   return 0;
238 }
239
240 static clib_error_t *
241 snort_listener_init (vlib_main_t *vm)
242 {
243   snort_main_t *sm = &snort_main;
244   clib_error_t *err;
245   clib_file_t t = { 0 };
246   clib_socket_t *s;
247
248   if (sm->listener)
249     return 0;
250
251   s = clib_mem_alloc (sizeof (clib_socket_t));
252   clib_memset (s, 0, sizeof (clib_socket_t));
253   s->config = (char *) sm->socket_name;
254   s->flags = CLIB_SOCKET_F_IS_SERVER | CLIB_SOCKET_F_ALLOW_GROUP_WRITE |
255              CLIB_SOCKET_F_SEQPACKET | CLIB_SOCKET_F_PASSCRED;
256
257   if ((err = clib_socket_init (s)))
258     {
259       clib_mem_free (s);
260       return err;
261     }
262
263   t.read_function = snort_conn_fd_accept_ready;
264   t.file_descriptor = s->fd;
265   t.description = format (0, "snort listener %s", s->config);
266   log_debug ("%v", t.description);
267   clib_file_add (&file_main, &t);
268
269   sm->listener = s;
270
271   return 0;
272 }
273
274 clib_error_t *
275 snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz,
276                        u8 drop_on_disconnect)
277 {
278   vlib_thread_main_t *tm = vlib_get_thread_main ();
279   snort_main_t *sm = &snort_main;
280   snort_instance_t *si;
281   clib_error_t *err = 0;
282   u32 index, i;
283   u8 *base = CLIB_MEM_VM_MAP_FAILED;
284   u32 size;
285   int fd = -1;
286   u32 qpair_mem_sz = 0;
287   u32 qsz = 1 << log2_queue_sz;
288   u8 align = CLIB_CACHE_LINE_BYTES;
289
290   if (snort_get_instance_by_name (name))
291     return clib_error_return (0, "instance already exists");
292
293   /* descriptor table */
294   qpair_mem_sz += round_pow2 (qsz * sizeof (daq_vpp_desc_t), align);
295
296   /* enq and deq ring */
297   qpair_mem_sz += 2 * round_pow2 (qsz * sizeof (u32), align);
298
299   /* enq and deq head pointer */
300   qpair_mem_sz += 2 * round_pow2 (sizeof (u32), align);
301
302   size =
303     round_pow2 (tm->n_vlib_mains * qpair_mem_sz, clib_mem_get_page_size ());
304   fd = clib_mem_vm_create_fd (CLIB_MEM_PAGE_SZ_DEFAULT, "snort instance %s",
305                               name);
306
307   if (fd == -1)
308     {
309       err = clib_error_return (0, "memory fd failure: %U", format_clib_error,
310                                clib_mem_get_last_error ());
311       goto done;
312     }
313
314   if ((ftruncate (fd, size)) == -1)
315     {
316       err = clib_error_return (0, "ftruncate failure");
317       goto done;
318     }
319
320   base = clib_mem_vm_map_shared (0, size, fd, 0, "snort instance %s", name);
321
322   if (base == CLIB_MEM_VM_MAP_FAILED)
323     {
324       err = clib_error_return (0, "mmap failure");
325       goto done;
326     }
327
328   pool_get_zero (sm->instances, si);
329   si->index = si - sm->instances;
330   si->client_index = ~0;
331   si->shm_base = base;
332   si->shm_fd = fd;
333   si->shm_size = size;
334   si->name = format (0, "%s%c", name, 0);
335   si->drop_on_disconnect = drop_on_disconnect;
336   index = si - sm->instances;
337   hash_set_mem (sm->instance_by_name, si->name, index);
338
339   log_debug ("instnce '%s' createed with fd %d at %p, len %u", name, fd, base,
340              size);
341
342   vec_validate_aligned (sm->per_thread_data, tm->n_vlib_mains - 1,
343                         CLIB_CACHE_LINE_BYTES);
344   vec_validate_aligned (si->qpairs, tm->n_vlib_mains - 1,
345                         CLIB_CACHE_LINE_BYTES);
346
347   for (int i = 0; i < tm->n_vlib_mains; i++)
348     {
349       snort_qpair_t *qp = vec_elt_at_index (si->qpairs, i);
350       snort_per_thread_data_t *ptd = vec_elt_at_index (sm->per_thread_data, i);
351       clib_file_t t = { 0 };
352
353       qp->log2_queue_size = log2_queue_sz;
354       qp->descriptors = (void *) base;
355       base += round_pow2 (qsz * sizeof (daq_vpp_desc_t), align);
356       qp->enq_ring = (void *) base;
357       base += round_pow2 (qsz * sizeof (u32), align);
358       qp->deq_ring = (void *) base;
359       base += round_pow2 (qsz * sizeof (u32), align);
360       qp->enq_head = (void *) base;
361       base += round_pow2 (sizeof (u32), align);
362       qp->deq_head = (void *) base;
363       base += round_pow2 (sizeof (u32), align);
364       qp->enq_fd = eventfd (0, EFD_NONBLOCK);
365       qp->deq_fd = eventfd (0, EFD_NONBLOCK);
366       vec_validate_aligned (qp->buffer_indices, qsz - 1,
367                             CLIB_CACHE_LINE_BYTES);
368       vec_validate_aligned (qp->next_indices, qsz - 1, CLIB_CACHE_LINE_BYTES);
369       clib_memset_u32 (qp->buffer_indices, ~0, qsz);
370
371       /* pre-populate freelist */
372       vec_validate_aligned (qp->freelist, qsz - 1, CLIB_CACHE_LINE_BYTES);
373       snort_freelist_init (qp->freelist);
374
375       /* listen on dequeue events */
376       t.read_function = snort_deq_ready;
377       t.file_descriptor = qp->deq_fd;
378       t.private_data = si->index;
379       t.description =
380         format (0, "snort dequeue for instance '%s' qpair %u", si->name, i);
381       qp->deq_fd_file_index = clib_file_add (&file_main, &t);
382       qp->ready = 1;
383       clib_file_set_polling_thread (&file_main, qp->deq_fd_file_index, i);
384       clib_interrupt_resize (&ptd->interrupts, vec_len (sm->instances));
385     }
386
387   for (i = 0; i < vlib_get_n_threads (); i++)
388     vlib_node_set_state (vlib_get_main_by_index (i), snort_deq_node.index,
389                          VLIB_NODE_STATE_INTERRUPT);
390
391 done:
392   if (err)
393     {
394       if (base != CLIB_MEM_VM_MAP_FAILED)
395         clib_mem_vm_unmap (base);
396       if (fd != -1)
397         close (fd);
398     }
399   return err;
400 }
401
402 clib_error_t *
403 snort_interface_enable_disable (vlib_main_t *vm, char *instance_name,
404                                 u32 sw_if_index, int is_enable)
405 {
406   snort_main_t *sm = &snort_main;
407   vnet_main_t *vnm = vnet_get_main ();
408   snort_instance_t *si;
409   clib_error_t *err = 0;
410   u32 index;
411
412   if (is_enable)
413     {
414       if ((si = snort_get_instance_by_name (instance_name)) == 0)
415         {
416           err = clib_error_return (0, "unknown instance '%s'", instance_name);
417           goto done;
418         }
419
420       vec_validate_init_empty (sm->instance_by_sw_if_index, sw_if_index, ~0);
421
422       index = sm->instance_by_sw_if_index[sw_if_index];
423       if (index != ~0)
424         {
425           si = vec_elt_at_index (sm->instances, index);
426           err = clib_error_return (0,
427                                    "interface %U already assgined to "
428                                    "instance '%s'",
429                                    format_vnet_sw_if_index_name, vnm,
430                                    sw_if_index, si->name);
431           goto done;
432         }
433
434       index = sm->instance_by_sw_if_index[sw_if_index] = si->index;
435       vnet_feature_enable_disable ("ip4-unicast", "snort-enq", sw_if_index, 1,
436                                    &index, sizeof (index));
437     }
438   else
439     {
440       if (sw_if_index >= vec_len (sm->instance_by_sw_if_index) ||
441           sm->instance_by_sw_if_index[sw_if_index] == ~0)
442         {
443           err =
444             clib_error_return (0,
445                                "interface %U is not assigned to snort "
446                                "instance!",
447                                format_vnet_sw_if_index_name, vnm, sw_if_index);
448           goto done;
449         }
450       index = sm->instance_by_sw_if_index[sw_if_index];
451       si = vec_elt_at_index (sm->instances, index);
452
453       sm->instance_by_sw_if_index[sw_if_index] = ~0;
454       vnet_feature_enable_disable ("ip4-unicast", "snort-enq", sw_if_index, 0,
455                                    &index, sizeof (index));
456     }
457
458 done:
459   if (err)
460     log_err ("%U", format_clib_error, err);
461   return 0;
462 }
463
464 clib_error_t *
465 snort_set_node_mode (vlib_main_t *vm, u32 mode)
466 {
467   int i;
468   snort_main.input_mode = mode;
469   for (i = 0; i < vlib_get_n_threads (); i++)
470     vlib_node_set_state (vlib_get_main_by_index (i), snort_deq_node.index,
471                          mode);
472   return 0;
473 }
474
475 static void
476 snort_set_default_socket (snort_main_t *sm, u8 *socket_name)
477 {
478   if (sm->socket_name)
479     return;
480
481   if (!socket_name)
482     socket_name = (u8 *) DAQ_VPP_DEFAULT_SOCKET_FILE;
483
484   sm->socket_name =
485     format (0, "%s/%s", vlib_unix_get_runtime_dir (), socket_name);
486   vec_terminate_c_string (sm->socket_name);
487 }
488
489 static clib_error_t *
490 snort_init (vlib_main_t *vm)
491 {
492   snort_main_t *sm = &snort_main;
493   sm->instance_by_name = hash_create_string (0, sizeof (uword));
494   vlib_buffer_pool_t *bp;
495
496   vec_foreach (bp, vm->buffer_main->buffer_pools)
497     {
498       vlib_physmem_map_t *pm =
499         vlib_physmem_get_map (vm, bp->physmem_map_index);
500       vec_add1 (sm->buffer_pool_base_addrs, pm->base);
501     }
502
503   if (!sm->socket_name)
504     snort_set_default_socket (sm, 0);
505
506   return snort_listener_init (vm);
507 }
508
509 VLIB_INIT_FUNCTION (snort_init);
510
511 VLIB_PLUGIN_REGISTER () = {
512   .version = VPP_BUILD_VER,
513   .description = "Snort",
514 };
515
516 VNET_FEATURE_INIT (snort_enq, static) = {
517   .arc_name = "ip4-unicast",
518   .node_name = "snort-enq",
519   .runs_before = VNET_FEATURES ("ip4-lookup"),
520 };