vlib: prevent some signals from being executed on workers
[vpp.git] / src / plugins / ioam / analyse / ip6 / node.c
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vlib/vlib.h>
17 #include <vnet/vnet.h>
18 #include <vppinfra/error.h>
19 #include <vnet/ethernet/ethernet.h>
20 #include <vnet/ip/ip.h>
21 #include <ioam/export-common/ioam_export.h>
22 #include <ioam/encap/ip6_ioam_trace.h>
23 #include <ioam/encap/ip6_ioam_pot.h>
24 #include <ioam/lib-pot/pot_util.h>
25 #include <ioam/encap/ip6_ioam_e2e.h>
26 #include <ioam/analyse/ioam_analyse.h>
27 #include <ioam/analyse/ip6/ip6_ioam_analyse.h>
28 #include <vnet/plugin/plugin.h>
29
30 typedef struct
31 {
32   u32 next_index;
33   u32 flow_id;
34 } analyse_trace_t;
35
36 vlib_node_registration_t analyse_node_local;
37 vlib_node_registration_t analyse_node_remote;
38
39 #define foreach_analyse_error \
40 _(ANALYSED, "Packets analysed for summarization") \
41 _(FAILED, "Packets analysis failed") \
42
43 typedef enum
44 {
45 #define _(sym,str) ANALYSE_ERROR_##sym,
46   foreach_analyse_error
47 #undef _
48     ANALYSE_N_ERROR,
49 } analyse_error_t;
50
51 static char *analyse_error_strings[] = {
52 #define _(sym,string) string,
53   foreach_analyse_error
54 #undef _
55 };
56
57 typedef enum
58 {
59   ANALYSE_NEXT_IP4_LOOKUP,
60   ANALYSE_NEXT_IP4_DROP,
61   ANALYSE_N_NEXT,
62 } analyse_next_t;
63
64 ip6_ioam_analyser_main_t ioam_analyser_main;
65
66 /* packet trace format function */
67 static u8 *
68 format_analyse_trace (u8 * s, va_list * args)
69 {
70   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
71   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
72   analyse_trace_t *t = va_arg (*args, analyse_trace_t *);
73
74   s = format (s, "IP6-ioam-analyse: flow_id %d, next index %d",
75               t->flow_id, t->next_index);
76   return s;
77 }
78
79 always_inline u8
80 ioam_analyse_hbh (u32 flow_id,
81                   ip6_hop_by_hop_header_t * hbh0,
82                   ip6_hop_by_hop_option_t * opt0,
83                   ip6_hop_by_hop_option_t * limit0, u16 len)
84 {
85   ip6_ioam_analyser_main_t *am = &ioam_analyser_main;
86   u8 type0;
87   u8 error0 = 0;
88
89   while (opt0 < limit0)
90     {
91       type0 = opt0->type;
92       switch (type0)
93         {
94         case 0:         /* Pad1 */
95           opt0 = (ip6_hop_by_hop_option_t *) ((u8 *) opt0) + 1;
96           continue;
97         case 1:         /* PadN */
98           break;
99         default:
100           if (am->analyse_hbh_handler[type0])
101             {
102               if (PREDICT_TRUE
103                   ((*am->analyse_hbh_handler[type0]) (flow_id, opt0,
104                                                       len) < 0))
105                 {
106                   error0 = ANALYSE_ERROR_FAILED;
107                   return (error0);
108                 }
109             }
110         }
111       opt0 =
112         (ip6_hop_by_hop_option_t *) (((u8 *) opt0) + opt0->length +
113                                      sizeof (ip6_hop_by_hop_option_t));
114     }
115   return (error0);
116 }
117
118 /**
119  * @brief IPv6 InBandOAM Analyse node.
120  * @node ip6-hbh-analyse-local, ip6-hbh-analyse-remote
121  *
122  * This function receives IP-FIX packets containing IPv6-iOAM records, analyses
123  * them and collects/aggregates the statistics.
124  *
125  * @param vm    vlib_main_t corresponding to the current thread.
126  * @param node  vlib_node_runtime_t data for this node.
127  * @param frame vlib_frame_t whose contents should be dispatched.
128  *
129  * @par Graph mechanics: buffer, next index usage
130  *
131  * <em>Uses:</em>
132  * - <code>vlib_buffer_get_current(p0)</code>
133  *     - Walks on each ioam record present in IP-Fix record, analyse them and
134  *       store the statistics.
135  *
136  * <em>Next Index:</em>
137  * - Dispatches the packet to ip4-lookup if executed under ip6-hbh-analyse-local
138  *   node context and to ip4-drop if executed under ip6-hbh-analyse-remote node
139  *   context.
140  */
141 static uword
142 ip6_ioam_analyse_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
143                           vlib_frame_t * frame)
144 {
145   u32 n_left_from, *from, *to_next;
146   analyse_next_t next_index;
147   u32 pkts_analysed = 0;
148   u32 pkts_failed = 0;
149   u8 remote = 0;
150   u32 next0 = ANALYSE_NEXT_IP4_LOOKUP;
151
152   from = vlib_frame_vector_args (frame);
153   n_left_from = frame->n_vectors;
154   next_index = node->cached_next_index;
155
156   if (PREDICT_FALSE (analyse_node_remote.index == node->node_index))
157     {
158       remote = 1;
159       next0 = ANALYSE_NEXT_IP4_DROP;
160     }
161
162   while (n_left_from > 0)
163     {
164       u32 n_left_to_next;
165
166       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
167
168       while (n_left_from > 0 && n_left_to_next > 0)
169         {
170           u32 bi0;
171           vlib_buffer_t *p0;
172           ip4_header_t *ip40;
173           u8 *data, *limit;
174           u16 num_ioam_records;
175
176           /* speculatively enqueue p0 to the current next frame */
177           bi0 = from[0];
178           to_next[0] = bi0;
179           from += 1;
180           to_next += 1;
181           n_left_from -= 1;
182           n_left_to_next -= 1;
183
184           p0 = vlib_get_buffer (vm, bi0);
185           if (PREDICT_FALSE (remote))
186             {
187               vlib_buffer_advance (p0, -(word) (sizeof (udp_header_t) +
188                                                 sizeof (ip4_header_t) +
189                                                 sizeof
190                                                 (ipfix_message_header_t) +
191                                                 sizeof (ipfix_set_header_t)));
192             }
193           data = (u8 *) vlib_buffer_get_current (p0);
194           ip40 = (ip4_header_t *) vlib_buffer_get_current (p0);
195           limit = data + clib_net_to_host_u16 (ip40->length);
196           data += sizeof (ip4_header_t) + sizeof (udp_header_t)
197             + sizeof (ipfix_message_header_t) + sizeof (ipfix_set_header_t);
198
199           num_ioam_records = (limit - data) / DEFAULT_EXPORT_SIZE;
200
201           while (num_ioam_records >= 4)
202             {
203               /* Prefetch next 2 ioam records */
204               {
205                 CLIB_PREFETCH (data + (2 * DEFAULT_EXPORT_SIZE),
206                                (DEFAULT_EXPORT_SIZE), LOAD);
207                 CLIB_PREFETCH (data + (3 * DEFAULT_EXPORT_SIZE),
208                                (DEFAULT_EXPORT_SIZE), LOAD);
209               }
210
211               num_ioam_records -= 2;
212
213               ip6_header_t *ip60, *ip61;
214               ip6_hop_by_hop_header_t *hbh0, *hbh1;
215               ip6_hop_by_hop_option_t *opt0, *limit0, *opt1, *limit1;
216               u32 flow_id0, flow_id1;
217               u8 error0, error1;
218               ioam_analyser_data_t *data0, *data1;
219               u16 p_len0, p_len1;
220
221               ip60 = (ip6_header_t *) data;
222               ip61 = (ip6_header_t *) (data + DEFAULT_EXPORT_SIZE);
223
224               data += (2 * DEFAULT_EXPORT_SIZE);
225
226               hbh0 = (ip6_hop_by_hop_header_t *) (ip60 + 1);
227               hbh1 = (ip6_hop_by_hop_header_t *) (ip61 + 1);
228
229               opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
230               opt1 = (ip6_hop_by_hop_option_t *) (hbh1 + 1);
231
232               limit0 =
233                 (ip6_hop_by_hop_option_t *) ((u8 *) hbh0 +
234                                              ((hbh0->length + 1) << 3));
235               limit1 =
236                 (ip6_hop_by_hop_option_t *) ((u8 *) hbh1 +
237                                              ((hbh1->length + 1) << 3));
238
239               flow_id0 =
240                 clib_net_to_host_u32
241                 (ip60->ip_version_traffic_class_and_flow_label) & 0xFFFFF;
242               flow_id1 =
243                 clib_net_to_host_u32
244                 (ip61->ip_version_traffic_class_and_flow_label) & 0xFFFFF;
245
246               p_len0 = clib_net_to_host_u16 (ip60->payload_length);
247               p_len1 = clib_net_to_host_u16 (ip61->payload_length);
248
249               error0 =
250                 ioam_analyse_hbh (flow_id0, hbh0, opt0, limit0, p_len0);
251               error1 =
252                 ioam_analyse_hbh (flow_id1, hbh1, opt1, limit1, p_len0);
253
254               if (PREDICT_TRUE ((error0 == 0) && (error1 == 0)))
255                 {
256                   pkts_analysed += 2;
257                   data0 = ioam_analyse_get_data_from_flow_id (flow_id0);
258                   data1 = ioam_analyse_get_data_from_flow_id (flow_id1);
259
260                   clib_spinlock_lock (&data0->writer_lock);
261                   data0->pkt_counter++;
262                   data0->bytes_counter += p_len0;
263                   clib_spinlock_unlock (&data0->writer_lock);
264
265                   clib_spinlock_lock (&data1->writer_lock);
266                   data1->pkt_counter++;
267                   data1->bytes_counter += p_len1;
268                   clib_spinlock_unlock (&data1->writer_lock);
269                 }
270               else if (error0 == 0)
271                 {
272                   pkts_analysed++;
273                   pkts_failed++;
274
275                   data0 = ioam_analyse_get_data_from_flow_id (flow_id0);
276                   clib_spinlock_lock (&data0->writer_lock);
277                   data0->pkt_counter++;
278                   data0->bytes_counter += p_len0;
279                   clib_spinlock_unlock (&data0->writer_lock);
280                 }
281               else if (error1 == 0)
282                 {
283                   pkts_analysed++;
284                   pkts_failed++;
285
286                   data1 = ioam_analyse_get_data_from_flow_id (flow_id1);
287                   clib_spinlock_lock (&data1->writer_lock);
288                   data1->pkt_counter++;
289                   data1->bytes_counter += p_len1;
290                   clib_spinlock_unlock (&data1->writer_lock);
291                 }
292               else
293                 pkts_failed += 2;
294             }
295
296           while (num_ioam_records > 0)
297             {
298               num_ioam_records--;
299
300               ip6_header_t *ip60;
301               ip6_hop_by_hop_header_t *hbh0;
302               ip6_hop_by_hop_option_t *opt0, *limit0;
303               u32 flow_id0;
304               u8 error0;
305               ioam_analyser_data_t *data0;
306               u16 p_len0;
307
308               ip60 = (ip6_header_t *) data;
309               data += (1 * DEFAULT_EXPORT_SIZE);
310               hbh0 = (ip6_hop_by_hop_header_t *) (ip60 + 1);
311               opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
312               limit0 =
313                 (ip6_hop_by_hop_option_t *) ((u8 *) hbh0 +
314                                              ((hbh0->length + 1) << 3));
315
316               flow_id0 =
317                 clib_net_to_host_u32
318                 (ip60->ip_version_traffic_class_and_flow_label) & 0xFFFFF;
319               p_len0 = clib_net_to_host_u16 (ip60->payload_length);
320               error0 =
321                 ioam_analyse_hbh (flow_id0, hbh0, opt0, limit0, p_len0);
322
323               if (PREDICT_TRUE (error0 == 0))
324                 {
325                   pkts_analysed++;
326                   data0 = ioam_analyse_get_data_from_flow_id (flow_id0);
327                   clib_spinlock_lock (&data0->writer_lock);
328                   data0->pkt_counter++;
329                   data0->bytes_counter +=
330                     clib_net_to_host_u16 (ip60->payload_length);
331                   clib_spinlock_unlock (&data0->writer_lock);
332                 }
333               else
334                 pkts_failed++;
335             }
336
337           /* verify speculative enqueue, maybe switch current next frame */
338           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
339                                            n_left_to_next, bi0, next0);
340         }
341
342       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
343     }
344
345   vlib_node_increment_counter (vm, node->node_index, ANALYSE_ERROR_ANALYSED,
346                                pkts_analysed);
347
348   if (PREDICT_FALSE (pkts_failed))
349     vlib_node_increment_counter (vm, node->node_index, ANALYSE_ERROR_FAILED,
350                                  pkts_failed);
351
352   return frame->n_vectors;
353 }
354
355 int
356 ip6_ioam_analyse_hbh_trace_internal (u32 flow_id,
357                                      ip6_hop_by_hop_option_t * opt, u16 len)
358 {
359   ioam_analyser_data_t *data;
360   ioam_trace_option_t *trace = (ioam_trace_option_t *) opt;
361
362   data = ioam_analyse_get_data_from_flow_id (flow_id);
363   ASSERT (data != NULL);
364
365   (void) ip6_ioam_analyse_hbh_trace (data, &trace->trace_hdr, len,
366                                      (trace->hdr.length - 2)
367                                      /*ioam_trace_type,data_list_elts_left */
368     );
369   return 0;
370 }
371
372 int
373 ip6_ioam_analyse_hbh_pot (u32 flow_id, ip6_hop_by_hop_option_t * opt0,
374                           u16 len)
375 {
376
377   ioam_pot_option_t *pot0;
378   u64 random = 0;
379   u64 cumulative = 0;
380   pot_profile *pot_profile = 0;
381   int ret;
382   ioam_analyser_data_t *data;
383
384   data = ioam_analyse_get_data_from_flow_id (flow_id);
385
386   pot0 = (ioam_pot_option_t *) opt0;
387   random = clib_net_to_host_u64 (pot0->random);
388   cumulative = clib_net_to_host_u64 (pot0->cumulative);
389   pot_profile = pot_profile_get_active ();
390   ret = pot_validate (pot_profile, cumulative, random);
391
392   clib_spinlock_lock (&data->writer_lock);
393
394   (0 == ret) ? (data->pot_data.sfc_validated_count++) :
395     (data->pot_data.sfc_invalidated_count++);
396
397   clib_spinlock_unlock (&data->writer_lock);
398   return 0;
399 }
400
401 int
402 ip6_ioam_analyse_hbh_e2e_internal (u32 flow_id, ip6_hop_by_hop_option_t * opt,
403                                    u16 len)
404 {
405   ioam_analyser_data_t *data;
406   ioam_e2e_option_t *e2e;
407
408   data = ioam_analyse_get_data_from_flow_id (flow_id);
409   e2e = (ioam_e2e_option_t *) opt;
410   ip6_ioam_analyse_hbh_e2e (data, &e2e->e2e_hdr, len);
411   return 0;
412 }
413
414 int
415 ip6_ioam_analyse_register_hbh_handler (u8 option,
416                                        int options (u32 flow_id,
417                                                     ip6_hop_by_hop_option_t *
418                                                     opt, u16 len))
419 {
420   ip6_ioam_analyser_main_t *am = &ioam_analyser_main;
421
422   ASSERT ((u32) option < ARRAY_LEN (am->analyse_hbh_handler));
423
424   /* Already registered */
425   if (am->analyse_hbh_handler[option])
426     return (-1);
427
428   am->analyse_hbh_handler[option] = options;
429
430   return (0);
431 }
432
433 int
434 ip6_ioam_analyse_unregister_hbh_handler (u8 option)
435 {
436   ip6_ioam_analyser_main_t *am = &ioam_analyser_main;
437
438   ASSERT ((u32) option < ARRAY_LEN (am->analyse_hbh_handler));
439
440   /* Not registered */
441   if (!am->analyse_hbh_handler[option])
442     return (-1);
443
444   am->analyse_hbh_handler[option] = NULL;
445   return (0);
446 }
447
448 void
449 ip6_ioam_analyse_register_handlers ()
450 {
451   ip6_ioam_analyse_register_hbh_handler (HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST,
452                                          ip6_ioam_analyse_hbh_trace_internal);
453   ip6_ioam_analyse_register_hbh_handler
454     (HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT, ip6_ioam_analyse_hbh_pot);
455   ip6_ioam_analyse_register_hbh_handler (HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE,
456                                          ip6_ioam_analyse_hbh_e2e_internal);
457 }
458
459 void
460 ip6_ioam_analyse_unregister_handlers ()
461 {
462   ip6_ioam_analyse_unregister_hbh_handler
463     (HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST);
464   ip6_ioam_analyse_unregister_hbh_handler
465     (HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT);
466   ip6_ioam_analyse_unregister_hbh_handler (HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE);
467 }
468
469
470 /*
471  * Node for IP6 analyse - packets
472  */
473 VLIB_REGISTER_NODE (analyse_node_local) = {
474   .function = ip6_ioam_analyse_node_fn,
475   .name = "ip6-hbh-analyse-local",
476   .vector_size = sizeof (u32),
477   .format_trace = format_analyse_trace,
478   .type = VLIB_NODE_TYPE_INTERNAL,
479   .n_errors = ARRAY_LEN (analyse_error_strings),
480   .error_strings = analyse_error_strings,
481   .n_next_nodes = ANALYSE_N_NEXT,
482   /* edit / add dispositions here */
483   .next_nodes = {
484     [ANALYSE_NEXT_IP4_LOOKUP] = "ip4-lookup",
485     [ANALYSE_NEXT_IP4_DROP] = "ip4-drop",
486   },
487 };
488
489 /*
490  * Node for IP6 analyse - packets
491  */
492 VLIB_REGISTER_NODE (analyse_node_remote) =
493 {
494   .function = ip6_ioam_analyse_node_fn,
495   .name = "ip6-hbh-analyse-remote",
496   .vector_size = sizeof (u32),
497   .format_trace = format_analyse_trace,
498   .type = VLIB_NODE_TYPE_INTERNAL,
499   .n_errors = ARRAY_LEN (analyse_error_strings),
500   .error_strings = analyse_error_strings,
501   .n_next_nodes = ANALYSE_N_NEXT,
502   /* edit / add dispositions here */
503   .next_nodes = {
504     [ANALYSE_NEXT_IP4_LOOKUP] = "ip4-lookup",
505     [ANALYSE_NEXT_IP4_DROP] = "ip4-drop",
506   },
507 };
508
509
510 /*
511  * fd.io coding-style-patch-verification: ON
512  *
513  * Local Variables:
514  * eval: (c-set-style "gnu")
515  * End:
516  */