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