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