nat: Include platform specific headers on FreeBSD
[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 };
17
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__)
20
21 static void
22 snort_client_disconnect (clib_file_t *uf)
23 {
24   vlib_main_t *vm = vlib_get_main ();
25   snort_qpair_t *qp;
26   snort_main_t *sm = &snort_main;
27   snort_client_t *c = pool_elt_at_index (sm->clients, uf->private_data);
28
29   if (c->instance_index != ~0)
30     {
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);
37
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);
41     }
42
43   clib_file_del (&file_main, uf);
44   clib_socket_close (&c->socket);
45   pool_put (sm->clients, c);
46 }
47
48 static snort_instance_t *
49 snort_get_instance_by_name (char *name)
50 {
51   snort_main_t *sm = &snort_main;
52   uword *p;
53   if ((p = hash_get_mem (sm->instance_by_name, name)) == 0)
54     return 0;
55
56   return vec_elt_at_index (sm->instances, p[0]);
57   ;
58 }
59
60 static clib_error_t *
61 snort_conn_fd_read_ready (clib_file_t *uf)
62 {
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;
67   snort_instance_t *si;
68   snort_qpair_t *qp;
69   snort_client_msg_queue_elt *e;
70   clib_error_t *err;
71   daq_vpp_msg_t msg;
72   char *name;
73   u8 *base;
74
75   log_debug ("fd_read_ready: client %u", uf->private_data);
76
77   if ((err = clib_socket_recvmsg (&c->socket, &msg, sizeof (msg), 0, 0)))
78     {
79       log_err ("client recvmsg error: %U", format_clib_error, err);
80       snort_client_disconnect (uf);
81       clib_error_free (err);
82       return 0;
83     }
84
85   if (msg.type != DAQ_VPP_MSG_TYPE_HELLO)
86     {
87       log_err ("unexpeced message recieved from client", 0);
88       snort_client_disconnect (uf);
89       return 0;
90     }
91
92   msg.hello.inst_name[DAQ_VPP_INST_NAME_LEN - 1] = 0;
93   name = msg.hello.inst_name;
94
95   log_debug ("fd_read_ready: connect instance %s", name);
96
97   if ((si = snort_get_instance_by_name (name)) == 0)
98     {
99       log_err ("unknown instance '%s' requested by client", name);
100       snort_client_disconnect (uf);
101       return 0;
102     }
103
104   vec_foreach (qp, si->qpairs)
105     {
106       u32 ready = __atomic_load_n (&qp->ready, __ATOMIC_ACQUIRE);
107       if (!ready)
108         {
109           log_err ("instance '%s' is not ready to accept connections", name);
110           snort_client_disconnect (uf);
111           return 0;
112         }
113     }
114
115   base = (u8 *) si->shm_base;
116
117   if (si->client_index != ~0)
118     {
119       log_err ("client already connected to instance '%s'", name);
120       snort_client_disconnect (uf);
121       return 0;
122     }
123   si->client_index = uf->private_data;
124   c->instance_index = si->index;
125
126   log_debug ("fd_read_ready: connect instance index %u", si->index);
127
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;
134   e->n_fds = 1;
135
136   vec_foreach (bp, vm->buffer_main->buffer_pools)
137     {
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;
143       e->fds[0] = pm->fd;
144       e->n_fds = 1;
145     }
146
147   vec_foreach (qp, si->qpairs)
148     {
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;
159       e->n_fds = 2;
160     }
161
162   clib_file_set_data_available_to_write (&file_main, c->file_index, 1);
163   return 0;
164 }
165
166 static clib_error_t *
167 snort_conn_fd_write_ready (clib_file_t *uf)
168 {
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;
172
173   log_debug ("fd_write_ready: client %u", uf->private_data);
174   clib_fifo_sub2 (c->msg_queue, e);
175
176   if (clib_fifo_elts (c->msg_queue) == 0)
177     clib_file_set_data_available_to_write (&file_main, c->file_index, 0);
178
179   return clib_socket_sendmsg (&c->socket, &e->msg, sizeof (*e), e->fds,
180                               e->n_fds);
181 }
182
183 clib_error_t *
184 snort_conn_fd_error (clib_file_t *uf)
185 {
186   log_debug ("fd_error: client %u", uf->private_data);
187   return 0;
188 }
189
190 static clib_error_t *
191 snort_deq_ready (clib_file_t *uf)
192 {
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);
197   u64 counter;
198   ssize_t bytes_read;
199
200   bytes_read = read (uf->file_descriptor, &counter, sizeof (counter));
201   if (bytes_read < 0)
202     {
203       return clib_error_return (0, "client closed socket");
204     }
205
206   if (bytes_read < sizeof (counter))
207     {
208       return clib_error_return (0, "unexpected truncated read");
209     }
210
211   clib_interrupt_set (ptd->interrupts, uf->private_data);
212   vlib_node_set_interrupt_pending (vm, snort_deq_node.index);
213   return 0;
214 }
215
216 static clib_error_t *
217 snort_conn_fd_accept_ready (clib_file_t *uf)
218 {
219   snort_main_t *sm = &snort_main;
220   snort_client_t *c;
221   clib_socket_t *s;
222   clib_error_t *err = 0;
223   clib_file_t t = { 0 };
224
225   pool_get_zero (sm->clients, c);
226   c->instance_index = ~0;
227   s = &c->socket;
228
229   if ((err = clib_socket_accept (sm->listener, s)))
230     {
231       log_err ("%U", format_clib_error, err);
232       pool_put (sm->clients, c);
233       return err;
234     }
235
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);
243
244   log_debug ("snort_conn_fd_accept_ready: client %u", t.private_data);
245   return 0;
246 }
247
248 static clib_error_t *
249 snort_listener_init (vlib_main_t *vm)
250 {
251   snort_main_t *sm = &snort_main;
252   clib_error_t *err;
253   clib_file_t t = { 0 };
254   clib_socket_t *s;
255
256   if (sm->listener)
257     return 0;
258
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->is_server = 1;
263   s->allow_group_write = 1;
264   s->is_seqpacket = 1;
265   s->passcred = 1;
266
267   if ((err = clib_socket_init (s)))
268     {
269       clib_mem_free (s);
270       return err;
271     }
272
273   t.read_function = snort_conn_fd_accept_ready;
274   t.file_descriptor = s->fd;
275   t.description = format (0, "snort listener %s", s->config);
276   log_debug ("%v", t.description);
277   clib_file_add (&file_main, &t);
278
279   sm->listener = s;
280
281   return 0;
282 }
283
284 clib_error_t *
285 snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz,
286                        u8 drop_on_disconnect)
287 {
288   vlib_thread_main_t *tm = vlib_get_thread_main ();
289   snort_main_t *sm = &snort_main;
290   snort_instance_t *si;
291   clib_error_t *err = 0;
292   u32 index, i;
293   u8 *base = CLIB_MEM_VM_MAP_FAILED;
294   u32 size;
295   int fd = -1;
296   u32 qpair_mem_sz = 0;
297   u32 qsz = 1 << log2_queue_sz;
298   u8 align = CLIB_CACHE_LINE_BYTES;
299
300   if (snort_get_instance_by_name (name))
301     return clib_error_return (0, "instance already exists");
302
303   /* descriptor table */
304   qpair_mem_sz += round_pow2 (qsz * sizeof (daq_vpp_desc_t), align);
305
306   /* enq and deq ring */
307   qpair_mem_sz += 2 * round_pow2 (qsz * sizeof (u32), align);
308
309   /* enq and deq head pointer */
310   qpair_mem_sz += 2 * round_pow2 (sizeof (u32), align);
311
312   size = round_pow2 ((uword) tm->n_vlib_mains * qpair_mem_sz,
313                      clib_mem_get_page_size ());
314   fd = clib_mem_vm_create_fd (CLIB_MEM_PAGE_SZ_DEFAULT, "snort instance %s",
315                               name);
316
317   if (fd == -1)
318     {
319       err = clib_error_return (0, "memory fd failure: %U", format_clib_error,
320                                clib_mem_get_last_error ());
321       goto done;
322     }
323
324   if ((ftruncate (fd, size)) == -1)
325     {
326       err = clib_error_return (0, "ftruncate failure");
327       goto done;
328     }
329
330   base = clib_mem_vm_map_shared (0, size, fd, 0, "snort instance %s", name);
331
332   if (base == CLIB_MEM_VM_MAP_FAILED)
333     {
334       err = clib_error_return (0, "mmap failure");
335       goto done;
336     }
337
338   pool_get_zero (sm->instances, si);
339   si->index = si - sm->instances;
340   si->client_index = ~0;
341   si->shm_base = base;
342   si->shm_fd = fd;
343   si->shm_size = size;
344   si->name = format (0, "%s%c", name, 0);
345   si->drop_on_disconnect = drop_on_disconnect;
346   index = si - sm->instances;
347   hash_set_mem (sm->instance_by_name, si->name, index);
348
349   log_debug ("instnce '%s' createed with fd %d at %p, len %u", name, fd, base,
350              size);
351
352   vec_validate_aligned (sm->per_thread_data, tm->n_vlib_mains - 1,
353                         CLIB_CACHE_LINE_BYTES);
354   vec_validate_aligned (si->qpairs, tm->n_vlib_mains - 1,
355                         CLIB_CACHE_LINE_BYTES);
356
357   for (int i = 0; i < tm->n_vlib_mains; i++)
358     {
359       snort_qpair_t *qp = vec_elt_at_index (si->qpairs, i);
360       snort_per_thread_data_t *ptd = vec_elt_at_index (sm->per_thread_data, i);
361       clib_file_t t = { 0 };
362
363       qp->log2_queue_size = log2_queue_sz;
364       qp->descriptors = (void *) base;
365       base += round_pow2 (qsz * sizeof (daq_vpp_desc_t), align);
366       qp->enq_ring = (void *) base;
367       base += round_pow2 (qsz * sizeof (u32), align);
368       qp->deq_ring = (void *) base;
369       base += round_pow2 (qsz * sizeof (u32), align);
370       qp->enq_head = (void *) base;
371       base += round_pow2 (sizeof (u32), align);
372       qp->deq_head = (void *) base;
373       base += round_pow2 (sizeof (u32), align);
374       qp->enq_fd = eventfd (0, EFD_NONBLOCK);
375       qp->deq_fd = eventfd (0, EFD_NONBLOCK);
376       vec_validate_aligned (qp->buffer_indices, qsz - 1,
377                             CLIB_CACHE_LINE_BYTES);
378       vec_validate_aligned (qp->next_indices, qsz - 1, CLIB_CACHE_LINE_BYTES);
379       clib_memset_u32 (qp->buffer_indices, ~0, qsz);
380
381       /* pre-populate freelist */
382       vec_validate_aligned (qp->freelist, qsz - 1, CLIB_CACHE_LINE_BYTES);
383       snort_freelist_init (qp->freelist);
384
385       /* listen on dequeue events */
386       t.read_function = snort_deq_ready;
387       t.file_descriptor = qp->deq_fd;
388       t.private_data = si->index;
389       t.description =
390         format (0, "snort dequeue for instance '%s' qpair %u", si->name, i);
391       qp->deq_fd_file_index = clib_file_add (&file_main, &t);
392       qp->ready = 1;
393       clib_file_set_polling_thread (&file_main, qp->deq_fd_file_index, i);
394       clib_interrupt_resize (&ptd->interrupts, vec_len (sm->instances));
395     }
396
397   for (i = 0; i < vlib_get_n_threads (); i++)
398     vlib_node_set_state (vlib_get_main_by_index (i), snort_deq_node.index,
399                          sm->input_mode);
400
401 done:
402   if (err)
403     {
404       if (base != CLIB_MEM_VM_MAP_FAILED)
405         clib_mem_vm_unmap (base);
406       if (fd != -1)
407         close (fd);
408     }
409   return err;
410 }
411
412 clib_error_t *
413 snort_interface_enable_disable (vlib_main_t *vm, char *instance_name,
414                                 u32 sw_if_index, int is_enable,
415                                 snort_attach_dir_t snort_dir)
416 {
417   snort_main_t *sm = &snort_main;
418   vnet_main_t *vnm = vnet_get_main ();
419   snort_instance_t *si;
420   clib_error_t *err = 0;
421   u64 fa_data;
422   u32 index;
423
424   if (is_enable)
425     {
426       if ((si = snort_get_instance_by_name (instance_name)) == 0)
427         {
428           err = clib_error_return (0, "unknown instance '%s'", instance_name);
429           goto done;
430         }
431
432       vec_validate_init_empty (sm->instance_by_sw_if_index, sw_if_index, ~0);
433
434       index = sm->instance_by_sw_if_index[sw_if_index];
435       if (index != ~0)
436         {
437           si = vec_elt_at_index (sm->instances, index);
438           err = clib_error_return (0,
439                                    "interface %U already assgined to "
440                                    "instance '%s'",
441                                    format_vnet_sw_if_index_name, vnm,
442                                    sw_if_index, si->name);
443           goto done;
444         }
445
446       index = sm->instance_by_sw_if_index[sw_if_index] = si->index;
447       if (snort_dir & SNORT_INPUT)
448         {
449           fa_data = (u64) index;
450           vnet_feature_enable_disable ("ip4-unicast", "snort-enq", sw_if_index,
451                                        1, &fa_data, sizeof (fa_data));
452         }
453       if (snort_dir & SNORT_OUTPUT)
454         {
455           fa_data = (1LL << 32 | index);
456           vnet_feature_enable_disable ("ip4-output", "snort-enq", sw_if_index,
457                                        1, &fa_data, sizeof (fa_data));
458         }
459     }
460   else
461     {
462       if (sw_if_index >= vec_len (sm->instance_by_sw_if_index) ||
463           sm->instance_by_sw_if_index[sw_if_index] == ~0)
464         {
465           err =
466             clib_error_return (0,
467                                "interface %U is not assigned to snort "
468                                "instance!",
469                                format_vnet_sw_if_index_name, vnm, sw_if_index);
470           goto done;
471         }
472       index = sm->instance_by_sw_if_index[sw_if_index];
473       si = vec_elt_at_index (sm->instances, index);
474
475       sm->instance_by_sw_if_index[sw_if_index] = ~0;
476       if (snort_dir & SNORT_INPUT)
477         {
478           fa_data = (u64) index;
479           vnet_feature_enable_disable ("ip4-unicast", "snort-enq", sw_if_index,
480                                        0, &fa_data, sizeof (fa_data));
481         }
482       if (snort_dir & SNORT_OUTPUT)
483         {
484           fa_data = (1LL << 32 | index);
485           vnet_feature_enable_disable ("ip4-output", "snort-enq", sw_if_index,
486                                        0, &fa_data, sizeof (fa_data));
487         }
488     }
489
490 done:
491   if (err)
492     log_err ("%U", format_clib_error, err);
493   return 0;
494 }
495
496 clib_error_t *
497 snort_set_node_mode (vlib_main_t *vm, u32 mode)
498 {
499   int i;
500   snort_main.input_mode = mode;
501   for (i = 0; i < vlib_get_n_threads (); i++)
502     vlib_node_set_state (vlib_get_main_by_index (i), snort_deq_node.index,
503                          mode);
504   return 0;
505 }
506
507 static void
508 snort_set_default_socket (snort_main_t *sm, u8 *socket_name)
509 {
510   if (sm->socket_name)
511     return;
512
513   if (!socket_name)
514     socket_name = (u8 *) DAQ_VPP_DEFAULT_SOCKET_FILE;
515
516   sm->socket_name =
517     format (0, "%s/%s", vlib_unix_get_runtime_dir (), socket_name);
518   vec_terminate_c_string (sm->socket_name);
519 }
520
521 static clib_error_t *
522 snort_init (vlib_main_t *vm)
523 {
524   snort_main_t *sm = &snort_main;
525   sm->input_mode = VLIB_NODE_STATE_INTERRUPT;
526   sm->instance_by_name = hash_create_string (0, sizeof (uword));
527   vlib_buffer_pool_t *bp;
528
529   vec_foreach (bp, vm->buffer_main->buffer_pools)
530     {
531       vlib_physmem_map_t *pm =
532         vlib_physmem_get_map (vm, bp->physmem_map_index);
533       vec_add1 (sm->buffer_pool_base_addrs, pm->base);
534     }
535
536   if (!sm->socket_name)
537     snort_set_default_socket (sm, 0);
538
539   return snort_listener_init (vm);
540 }
541
542 VLIB_INIT_FUNCTION (snort_init);
543
544 VLIB_PLUGIN_REGISTER () = {
545   .version = VPP_BUILD_VER,
546   .description = "Snort",
547 };
548
549 VNET_FEATURE_INIT (snort_enq, static) = {
550   .arc_name = "ip4-unicast",
551   .node_name = "snort-enq",
552   .runs_before = VNET_FEATURES ("ip4-lookup"),
553 };
554
555 VNET_FEATURE_INIT (snort_enq_out, static) = {
556   .arc_name = "ip4-output",
557   .node_name = "snort-enq",
558   .runs_before = VNET_FEATURES ("interface-output"),
559 };