snort: snort3 plugin and DAQ
[vpp.git] / src / plugins / snort / dequeue.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/feature/feature.h>
7 #include <snort/snort.h>
8
9 typedef struct
10 {
11   u32 next_index;
12   u32 sw_if_index;
13 } snort_deq_trace_t;
14
15 static u8 *
16 format_snort_deq_trace (u8 *s, va_list *args)
17 {
18   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
19   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
20   snort_deq_trace_t *t = va_arg (*args, snort_deq_trace_t *);
21
22   s = format (s, "snort-deq: sw_if_index %d, next index %d\n", t->sw_if_index,
23               t->next_index);
24
25   return s;
26 }
27
28 #define foreach_snort_deq_error                                               \
29   _ (BAD_DESC, "bad descriptor")                                              \
30   _ (BAD_DESC_INDEX, "bad descriptor index")
31
32 typedef enum
33 {
34 #define _(sym, str) SNORT_DEQ_ERROR_##sym,
35   foreach_snort_deq_error
36 #undef _
37     SNORT_DEQ_N_ERROR,
38 } snort_deq_error_t;
39
40 static char *snort_deq_error_strings[] = {
41 #define _(sym, string) string,
42   foreach_snort_deq_error
43 #undef _
44 };
45
46 static_always_inline uword
47 snort_deq_instance (vlib_main_t *vm, u32 instance_index, snort_qpair_t *qp,
48                     u32 *buffer_indices, u16 *nexts, u32 max_recv)
49 {
50   snort_main_t *sm = &snort_main;
51   snort_per_thread_data_t *ptd =
52     vec_elt_at_index (sm->per_thread_data, vm->thread_index);
53   u32 mask = pow2_mask (qp->log2_queue_size);
54   u32 head, next, n_recv = 0, n_left;
55
56   head = __atomic_load_n (qp->deq_head, __ATOMIC_ACQUIRE);
57   next = qp->next_desc;
58
59   n_left = head - next;
60
61   if (n_left == 0)
62     return 0;
63
64   if (n_left > max_recv)
65     {
66       n_left = max_recv;
67       clib_interrupt_set (ptd->interrupts, instance_index);
68       vlib_node_set_interrupt_pending (vm, snort_deq_node.index);
69     }
70
71   while (n_left)
72     {
73       u32 desc_index, bi;
74       daq_vpp_desc_t *d;
75
76       /* check if descriptor index taken from dequqe ring is valid */
77       if ((desc_index = qp->deq_ring[next & mask]) & ~mask)
78         {
79           vlib_node_increment_counter (vm, snort_deq_node.index,
80                                        SNORT_DEQ_ERROR_BAD_DESC_INDEX, 1);
81           goto next;
82         }
83
84       /* check if descriptor index taken from dequeue ring points to enqueued
85        * buffer */
86       if ((bi = qp->buffer_indices[desc_index]) == ~0)
87         {
88           vlib_node_increment_counter (vm, snort_deq_node.index,
89                                        SNORT_DEQ_ERROR_BAD_DESC, 1);
90           goto next;
91         }
92
93       /* put descriptor back to freelist */
94       vec_add1 (qp->freelist, desc_index);
95       d = qp->descriptors + desc_index;
96       buffer_indices++[0] = bi;
97       if (d->action == DAQ_VPP_ACTION_FORWARD)
98         nexts[0] = qp->next_indices[desc_index];
99       else
100         nexts[0] = SNORT_ENQ_NEXT_DROP;
101       qp->buffer_indices[desc_index] = ~0;
102       nexts++;
103       n_recv++;
104
105       /* next */
106     next:
107       next = next + 1;
108       n_left--;
109     }
110
111   qp->next_desc = next;
112
113   return n_recv;
114 }
115
116 static_always_inline u32
117 snort_process_all_buffer_indices (snort_qpair_t *qp, u32 *b, u16 *nexts,
118                                   u32 max_recv, u8 drop_on_disconnect)
119 {
120   u32 *bi, n_processed = 0;
121   u32 desc_index = 0;
122
123   vec_foreach (bi, qp->buffer_indices)
124     {
125       if (n_processed >= max_recv)
126         break;
127
128       if (bi[0] == ~0)
129         continue;
130
131       desc_index = bi - qp->buffer_indices;
132
133       b[0] = bi[0];
134       if (drop_on_disconnect)
135         nexts[0] = SNORT_ENQ_NEXT_DROP;
136       else
137         nexts[0] = qp->next_indices[desc_index];
138       qp->buffer_indices[desc_index] = ~0;
139
140       nexts += 1;
141       b += 1;
142       n_processed += 1;
143     }
144   return n_processed;
145 }
146
147 static_always_inline uword
148 snort_deq_instance_all_interrupt (vlib_main_t *vm, u32 instance_index,
149                                   snort_qpair_t *qp, u32 *buffer_indices,
150                                   u16 *nexts, u32 max_recv,
151                                   u8 drop_on_disconnect)
152 {
153   snort_main_t *sm = &snort_main;
154   snort_per_thread_data_t *ptd =
155     vec_elt_at_index (sm->per_thread_data, vm->thread_index);
156   u32 n_processed;
157
158   n_processed = snort_process_all_buffer_indices (
159     qp, buffer_indices, nexts, max_recv, drop_on_disconnect);
160
161   if (n_processed == max_recv)
162     {
163       clib_interrupt_set (ptd->interrupts, instance_index);
164       vlib_node_set_interrupt_pending (vm, snort_deq_node.index);
165     }
166   else
167     {
168       *qp->enq_head = *qp->deq_head = qp->next_desc = 0;
169       snort_freelist_init (qp->freelist);
170       __atomic_store_n (&qp->ready, 1, __ATOMIC_RELEASE);
171     }
172
173   return n_processed;
174 }
175
176 static u32
177 snort_deq_node_interrupt (vlib_main_t *vm, vlib_node_runtime_t *node,
178                           vlib_frame_t *frame)
179 {
180   snort_main_t *sm = &snort_main;
181   snort_per_thread_data_t *ptd =
182     vec_elt_at_index (sm->per_thread_data, vm->thread_index);
183   u32 buffer_indices[VLIB_FRAME_SIZE], *bi = buffer_indices;
184   u16 next_indices[VLIB_FRAME_SIZE], *nexts = next_indices;
185   u32 n_left = VLIB_FRAME_SIZE, n;
186   snort_qpair_t *qp;
187   snort_instance_t *si;
188   int inst = -1;
189
190   while ((inst = clib_interrupt_get_next (ptd->interrupts, inst)) != -1)
191     {
192       clib_interrupt_clear (ptd->interrupts, inst);
193       si = vec_elt_at_index (sm->instances, inst);
194       qp = vec_elt_at_index (si->qpairs, vm->thread_index);
195       u32 ready = __atomic_load_n (&qp->ready, __ATOMIC_ACQUIRE);
196       if (!ready)
197         n = snort_deq_instance_all_interrupt (vm, inst, qp, bi, nexts, n_left,
198                                               si->drop_on_disconnect);
199       else
200         n = snort_deq_instance (vm, inst, qp, bi, nexts, n_left);
201
202       n_left -= n;
203       bi += n;
204       nexts += n;
205
206       if (n_left == 0)
207         goto enq;
208     }
209
210   if (n_left == VLIB_FRAME_SIZE)
211     return 0;
212
213 enq:
214   n = VLIB_FRAME_SIZE - n_left;
215   vlib_buffer_enqueue_to_next (vm, node, buffer_indices, next_indices, n);
216   return n;
217 }
218
219 static_always_inline uword
220 snort_deq_instance_poll (vlib_main_t *vm, snort_qpair_t *qp,
221                          u32 *buffer_indices, u16 *nexts, u32 max_recv)
222 {
223   u32 mask = pow2_mask (qp->log2_queue_size);
224   u32 head, next, n_recv = 0, n_left;
225
226   head = __atomic_load_n (qp->deq_head, __ATOMIC_ACQUIRE);
227   next = qp->next_desc;
228
229   n_left = head - next;
230
231   if (n_left == 0)
232     return 0;
233
234   if (n_left > max_recv)
235     n_left = max_recv;
236
237   while (n_left)
238     {
239       u32 desc_index, bi;
240       daq_vpp_desc_t *d;
241
242       /* check if descriptor index taken from dequqe ring is valid */
243       if ((desc_index = qp->deq_ring[next & mask]) & ~mask)
244         {
245           vlib_node_increment_counter (vm, snort_deq_node.index,
246                                        SNORT_DEQ_ERROR_BAD_DESC_INDEX, 1);
247           goto next;
248         }
249
250       /* check if descriptor index taken from dequeue ring points to enqueued
251        * buffer */
252       if ((bi = qp->buffer_indices[desc_index]) == ~0)
253         {
254           vlib_node_increment_counter (vm, snort_deq_node.index,
255                                        SNORT_DEQ_ERROR_BAD_DESC, 1);
256           goto next;
257         }
258
259       /* put descriptor back to freelist */
260       vec_add1 (qp->freelist, desc_index);
261       d = qp->descriptors + desc_index;
262       buffer_indices++[0] = bi;
263       if (d->action == DAQ_VPP_ACTION_FORWARD)
264         nexts[0] = qp->next_indices[desc_index];
265       else
266         nexts[0] = SNORT_ENQ_NEXT_DROP;
267       qp->buffer_indices[desc_index] = ~0;
268       nexts++;
269       n_recv++;
270
271       /* next */
272     next:
273       next = next + 1;
274       n_left--;
275     }
276
277   qp->next_desc = next;
278
279   return n_recv;
280 }
281
282 static_always_inline uword
283 snort_deq_instance_all_poll (vlib_main_t *vm, snort_qpair_t *qp,
284                              u32 *buffer_indices, u16 *nexts, u32 max_recv,
285                              u8 drop_on_disconnect)
286 {
287   u32 n_processed = snort_process_all_buffer_indices (
288     qp, buffer_indices, nexts, max_recv, drop_on_disconnect);
289   if (n_processed < max_recv)
290     {
291       *qp->enq_head = *qp->deq_head = qp->next_desc = 0;
292       snort_freelist_init (qp->freelist);
293       __atomic_store_n (&qp->ready, 1, __ATOMIC_RELEASE);
294     }
295
296   return n_processed;
297 }
298
299 static u32
300 snort_deq_node_polling (vlib_main_t *vm, vlib_node_runtime_t *node,
301                         vlib_frame_t *frame)
302 {
303   snort_main_t *sm = &snort_main;
304   u32 buffer_indices[VLIB_FRAME_SIZE], *bi = buffer_indices;
305   u16 next_indices[VLIB_FRAME_SIZE], *nexts = next_indices;
306   u32 n_left = VLIB_FRAME_SIZE, n, n_total = 0;
307   snort_qpair_t *qp;
308   snort_instance_t *si;
309
310   vec_foreach (si, sm->instances)
311     {
312       qp = vec_elt_at_index (si->qpairs, vm->thread_index);
313       u32 ready = __atomic_load_n (&qp->ready, __ATOMIC_ACQUIRE);
314       if (!ready)
315         n = snort_deq_instance_all_poll (vm, qp, bi, nexts, n_left,
316                                          si->drop_on_disconnect);
317       else
318         n = snort_deq_instance_poll (vm, qp, bi, nexts, n_left);
319
320       n_left -= n;
321       bi += n;
322       nexts += n;
323
324       if (n_left == 0)
325         {
326           n = VLIB_FRAME_SIZE - n_left;
327           vlib_buffer_enqueue_to_next (vm, node, buffer_indices, next_indices,
328                                        n);
329           n_left = VLIB_FRAME_SIZE;
330           bi = buffer_indices;
331           nexts = next_indices;
332           n_total += n;
333         }
334     }
335
336   if (n_left < VLIB_FRAME_SIZE)
337     {
338       n = VLIB_FRAME_SIZE - n_left;
339       vlib_buffer_enqueue_to_next (vm, node, buffer_indices, next_indices, n);
340       n_total += n;
341     }
342   return n_total;
343 }
344
345 VLIB_NODE_FN (snort_deq_node)
346 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
347 {
348   snort_main_t *sm = &snort_main;
349   if (sm->input_mode == VLIB_NODE_STATE_POLLING)
350     return snort_deq_node_polling (vm, node, frame);
351   return snort_deq_node_interrupt (vm, node, frame);
352 }
353
354 VLIB_REGISTER_NODE (snort_deq_node) = {
355   .name = "snort-deq",
356   .vector_size = sizeof (u32),
357   .format_trace = format_snort_deq_trace,
358   .type = VLIB_NODE_TYPE_INPUT,
359   .state = VLIB_NODE_STATE_DISABLED,
360   .sibling_of = "snort-enq",
361
362   .n_errors = ARRAY_LEN (snort_deq_error_strings),
363   .error_strings = snort_deq_error_strings,
364
365   .n_next_nodes = 0,
366 };