IOAM Coverity fix
[vpp.git] / src / plugins / ioam / analyse / ioam_analyse.h
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 #ifndef PLUGINS_IOAM_PLUGIN_IOAM_ANALYSE_IOAM_ANALYSE_H_
17 #define PLUGINS_IOAM_PLUGIN_IOAM_ANALYSE_IOAM_ANALYSE_H_
18
19 #include <vlib/vlib.h>
20 #include <vnet/vnet.h>
21 #include <vppinfra/types.h>
22 #include <ioam/lib-e2e/e2e_util.h>
23 #include <ioam/lib-trace/trace_util.h>
24
25 #define IOAM_FLOW_TEMPLATE_ID    260
26 #define IOAM_TRACE_MAX_NODES      10
27 #define IOAM_MAX_PATHS_PER_FLOW   10
28
29 typedef struct
30 {
31   u16 ingress_if;
32   u16 egress_if;
33   u32 node_id;
34   u32 state_up;
35 } ioam_path_map_t;
36
37 /** @brief Analysed iOAM trace data.
38     @note cache aligned.
39 */
40 typedef struct
41 {
42   /** No of nodes in path. */
43   u8 num_nodes;
44
45   /** Data contained in trace - NodeId, TTL, Ingress & Egress Link, Timestamp. */
46   u8 trace_type;
47
48   /** Flag to indicate whether node is allocated. */
49   u8 is_free;
50
51   u8 pad[5];
52
53   /** Actual PATH flow has taken. */
54   ioam_path_map_t path[IOAM_TRACE_MAX_NODES];
55
56   /** Num of pkts in the flow going over path. */
57   u32 pkt_counter;
58
59   /** Num of bytes in the flow going over path. */
60   u32 bytes_counter;
61
62   /** Minumum Dealay for the flow. */
63   u32 min_delay;
64
65   /** Maximum Dealay for the flow. */
66   u32 max_delay;
67
68   /** Average Dealay for the flow. */
69   u32 mean_delay;
70
71   u32 reserve;
72 } ioam_analyse_trace_record;
73
74 typedef struct
75 {
76   ioam_analyse_trace_record path_data[IOAM_MAX_PATHS_PER_FLOW];
77 } ioam_analyse_trace_data;
78
79 /** @brief Analysed iOAM pot data.
80     @note cache aligned.
81 */
82 typedef struct
83 {
84   /** Number of packets validated (passes through the service chain)
85       within the timestamps. */
86   u32 sfc_validated_count;
87
88   /** Number of packets invalidated (failed through the service chain)
89       within the timestamps. */
90   u32 sfc_invalidated_count;
91 } ioam_analyse_pot_data;
92
93 /** @brief Analysed iOAM data.
94     @note cache aligned.
95 */
96 typedef struct ioam_analyser_data_t_
97 {
98   u8 is_free;
99   u8 pad[3];
100
101   /** Num of pkts sent for this flow. */
102   u32 pkt_sent;
103
104   /** Num of pkts matching this flow. */
105   u32 pkt_counter;
106
107   /** Num of bytes matching this flow. */
108   u32 bytes_counter;
109
110   /** Analysed iOAM trace data. */
111   ioam_analyse_trace_data trace_data;
112
113   /** Analysed iOAM pot data. */
114   ioam_analyse_pot_data pot_data;
115
116   /** Analysed iOAM seqno data. */
117   seqno_rx_info seqno_data;
118
119   /** Cache of previously analysed data, useful for export. */
120   struct ioam_analyser_data_t_ *chached_data_list;
121
122   /** Lock to since we use this to export the data in other thread. */
123   volatile u32 *writer_lock;
124 } ioam_analyser_data_t;
125
126 always_inline f64
127 ip6_ioam_analyse_calc_delay (ioam_trace_hdr_t * trace, u16 trace_len,
128                              u8 oneway)
129 {
130   u16 size_of_all_traceopts;
131   u8 size_of_traceopt_per_node;
132   u8 num_nodes;
133   u32 *start_elt, *end_elt, *uturn_elt;;
134   u32 start_time, end_time;
135   u8 done = 0;
136
137   size_of_traceopt_per_node = fetch_trace_data_size (trace->ioam_trace_type);
138   // Unknown trace type
139   if (size_of_traceopt_per_node == 0)
140     return 0;
141   size_of_all_traceopts = trace_len;    /*ioam_trace_type,data_list_elts_left */
142
143   num_nodes = (u8) (size_of_all_traceopts / size_of_traceopt_per_node);
144   if ((num_nodes == 0) || (num_nodes <= trace->data_list_elts_left))
145     return 0;
146
147   num_nodes -= trace->data_list_elts_left;
148
149   start_elt = trace->elts;
150   end_elt =
151     trace->elts +
152     (u32) ((size_of_traceopt_per_node / sizeof (u32)) * (num_nodes - 1));
153
154   if (oneway && (trace->ioam_trace_type & BIT_TTL_NODEID))
155     {
156       done = 0;
157       do
158         {
159           uturn_elt = start_elt - size_of_traceopt_per_node / sizeof (u32);
160
161           if ((clib_net_to_host_u32 (*start_elt) >> 24) <=
162               (clib_net_to_host_u32 (*uturn_elt) >> 24))
163             done = 1;
164         }
165       while (!done && (start_elt = uturn_elt) != end_elt);
166     }
167   if (trace->ioam_trace_type & BIT_TTL_NODEID)
168     {
169       start_elt++;
170       end_elt++;
171     }
172   if (trace->ioam_trace_type & BIT_ING_INTERFACE)
173     {
174       start_elt++;
175       end_elt++;
176     }
177   start_time = clib_net_to_host_u32 (*start_elt);
178   end_time = clib_net_to_host_u32 (*end_elt);
179
180   return (f64) (end_time - start_time);
181 }
182
183 always_inline void
184 ip6_ioam_analyse_set_paths_down (ioam_analyser_data_t * data)
185 {
186   ioam_analyse_trace_data *trace_data;
187   ioam_analyse_trace_record *trace_record;
188   ioam_path_map_t *path;
189   u8 k, i;
190
191   while (__sync_lock_test_and_set (data->writer_lock, 1))
192     ;
193
194   trace_data = &data->trace_data;
195
196   for (i = 0; i < IOAM_MAX_PATHS_PER_FLOW; i++)
197     {
198       trace_record = trace_data->path_data + i;
199
200       if (trace_record->is_free)
201         continue;
202
203       path = trace_record->path;
204
205       for (k = 0; k < trace_record->num_nodes; k++)
206         path[k].state_up = 0;
207     }
208   *(data->writer_lock) = 0;
209 }
210
211 always_inline void
212 ip6_ioam_analyse_hbh_trace_loopback (ioam_analyser_data_t * data,
213                                      ioam_trace_hdr_t * trace, u16 trace_len)
214 {
215   ioam_analyse_trace_data *trace_data;
216   ioam_analyse_trace_record *trace_record;
217   ioam_path_map_t *path;
218   u8 i, j, k, num_nodes, max_nodes;
219   u8 *ptr;
220   u32 nodeid;
221   u16 ingress_if, egress_if;
222   u16 size_of_traceopt_per_node;
223   u16 size_of_all_traceopts;
224
225   while (__sync_lock_test_and_set (data->writer_lock, 1))
226     ;
227
228   trace_data = &data->trace_data;
229
230   size_of_traceopt_per_node = fetch_trace_data_size (trace->ioam_trace_type);
231   if (0 == size_of_traceopt_per_node)
232     goto end;
233
234   size_of_all_traceopts = trace_len;
235
236   ptr = (u8 *) trace->elts;
237   max_nodes = (u8) (size_of_all_traceopts / size_of_traceopt_per_node);
238   num_nodes = max_nodes - trace->data_list_elts_left;
239
240   for (i = 0; i < IOAM_MAX_PATHS_PER_FLOW; i++)
241     {
242       trace_record = trace_data->path_data + i;
243       path = trace_record->path;
244
245       if (trace_record->is_free)
246         continue;
247
248       for (j = max_nodes, k = 0; k < num_nodes; j--, k++)
249         {
250           ptr =
251             (u8 *) ((u8 *) trace->elts +
252                     (size_of_traceopt_per_node * (j - 1)));
253
254           nodeid = clib_net_to_host_u32 (*((u32 *) ptr)) & 0x00ffffff;
255           ptr += 4;
256
257           if (nodeid != path[k].node_id)
258             goto end;
259
260           if ((trace->ioam_trace_type == TRACE_TYPE_IF_TS_APP) ||
261               (trace->ioam_trace_type == TRACE_TYPE_IF))
262             {
263               ingress_if = clib_net_to_host_u16 (*((u16 *) ptr));
264               ptr += 2;
265               egress_if = clib_net_to_host_u16 (*((u16 *) ptr));
266               if ((ingress_if != path[k].ingress_if) ||
267                   (egress_if != path[k].egress_if))
268                 {
269                   goto end;
270                 }
271             }
272           /* Found Match - set path hop state to up */
273           path[k].state_up = 1;
274         }
275     }
276 end:
277   *(data->writer_lock) = 0;
278 }
279
280 always_inline int
281 ip6_ioam_analyse_hbh_trace (ioam_analyser_data_t * data,
282                             ioam_trace_hdr_t * trace, u16 pak_len,
283                             u16 trace_len)
284 {
285   ioam_analyse_trace_data *trace_data;
286   u16 size_of_traceopt_per_node;
287   u16 size_of_all_traceopts;
288   u8 i, j, k, num_nodes, max_nodes;
289   u8 *ptr;
290   u32 nodeid;
291   u16 ingress_if, egress_if;
292   ioam_path_map_t *path = NULL;
293   ioam_analyse_trace_record *trace_record;
294
295   while (__sync_lock_test_and_set (data->writer_lock, 1))
296     ;
297
298   trace_data = &data->trace_data;
299
300   size_of_traceopt_per_node = fetch_trace_data_size (trace->ioam_trace_type);
301   // Unknown trace type
302   if (size_of_traceopt_per_node == 0)
303     goto DONE;
304   size_of_all_traceopts = trace_len;
305
306   ptr = (u8 *) trace->elts;
307   max_nodes = (u8) (size_of_all_traceopts / size_of_traceopt_per_node);
308   num_nodes = max_nodes - trace->data_list_elts_left;
309
310   for (i = 0; i < IOAM_MAX_PATHS_PER_FLOW; i++)
311     {
312       trace_record = trace_data->path_data + i;
313
314       if (trace_record->is_free ||
315           (num_nodes != trace_record->num_nodes) ||
316           (trace->ioam_trace_type != trace_record->trace_type))
317         continue;
318
319       path = trace_record->path;
320
321       for (j = max_nodes, k = 0; k < num_nodes; j--, k++)
322         {
323           ptr =
324             (u8 *) ((u8 *) trace->elts +
325                     (size_of_traceopt_per_node * (j - 1)));
326
327           nodeid = clib_net_to_host_u32 (*((u32 *) ptr)) & 0x00ffffff;
328           ptr += 4;
329
330           if (nodeid != path[k].node_id)
331             break;
332
333           if ((trace->ioam_trace_type == TRACE_TYPE_IF_TS_APP) ||
334               (trace->ioam_trace_type == TRACE_TYPE_IF))
335             {
336               ingress_if = clib_net_to_host_u16 (*((u16 *) ptr));
337               ptr += 2;
338               egress_if = clib_net_to_host_u16 (*((u16 *) ptr));
339               if ((ingress_if != path[k].ingress_if) ||
340                   (egress_if != path[k].egress_if))
341                 {
342                   break;
343                 }
344             }
345         }
346
347       if (k == num_nodes)
348         {
349           goto found_match;
350         }
351     }
352
353   for (i = 0; i < IOAM_MAX_PATHS_PER_FLOW; i++)
354     {
355       trace_record = trace_data->path_data + i;
356       if (trace_record->is_free)
357         {
358           trace_record->is_free = 0;
359           trace_record->num_nodes = num_nodes;
360           trace_record->trace_type = trace->ioam_trace_type;
361           path = trace_data->path_data[i].path;
362           trace_record->pkt_counter = 0;
363           trace_record->bytes_counter = 0;
364           trace_record->min_delay = 0xFFFFFFFF;
365           trace_record->max_delay = 0;
366           trace_record->mean_delay = 0;
367           break;
368         }
369     }
370
371   for (j = max_nodes, k = 0; k < num_nodes; j--, k++)
372     {
373       ptr =
374         (u8 *) ((u8 *) trace->elts + (size_of_traceopt_per_node * (j - 1)));
375
376       path[k].node_id = clib_net_to_host_u32 (*((u32 *) ptr)) & 0x00ffffff;
377       ptr += 4;
378
379       if ((trace->ioam_trace_type == TRACE_TYPE_IF_TS_APP) ||
380           (trace->ioam_trace_type == TRACE_TYPE_IF))
381         {
382           path[k].ingress_if = clib_net_to_host_u16 (*((u16 *) ptr));
383           ptr += 2;
384           path[k].egress_if = clib_net_to_host_u16 (*((u16 *) ptr));
385         }
386     }
387
388 found_match:
389   /* Set path state to UP */
390   for (k = 0; k < num_nodes; k++)
391     path[k].state_up = 1;
392
393   trace_record->pkt_counter++;
394   trace_record->bytes_counter += pak_len;
395   if (trace->ioam_trace_type & BIT_TIMESTAMP)
396     {
397       /* Calculate time delay */
398       u32 delay = (u32) ip6_ioam_analyse_calc_delay (trace, trace_len, 0);
399       if (delay < trace_record->min_delay)
400         trace_record->min_delay = delay;
401       else if (delay > trace_record->max_delay)
402         trace_record->max_delay = delay;
403
404       u64 sum = (trace_record->mean_delay * data->seqno_data.rx_packets);
405       trace_record->mean_delay =
406         (u32) ((sum + delay) / (data->seqno_data.rx_packets + 1));
407     }
408 DONE:
409   *(data->writer_lock) = 0;
410   return 0;
411 }
412
413 always_inline int
414 ip6_ioam_analyse_hbh_e2e (ioam_analyser_data_t * data,
415                           ioam_e2e_packet_t * e2e, u16 len)
416 {
417   while (__sync_lock_test_and_set (data->writer_lock, 1))
418     ;
419
420   ioam_analyze_seqno (&data->seqno_data,
421                       (u64) clib_net_to_host_u32 (e2e->e2e_data));
422
423   *(data->writer_lock) = 0;
424   return 0;
425 }
426
427 always_inline u8 *
428 format_path_map (u8 * s, va_list * args)
429 {
430   ioam_path_map_t *pm = va_arg (*args, ioam_path_map_t *);
431   u32 num_of_elts = va_arg (*args, u32);
432   u32 i;
433
434   for (i = 0; i < num_of_elts; i++)
435     {
436       s =
437         format (s,
438                 "node_id: 0x%x, ingress_if: 0x%x, egress_if:0x%x, state:%s\n",
439                 pm->node_id, pm->ingress_if, pm->egress_if,
440                 pm->state_up ? "UP" : "DOWN");
441       pm++;
442     }
443
444   return (s);
445 }
446
447 always_inline u8 *
448 print_analyse_flow (u8 * s, ioam_analyser_data_t * record)
449 {
450   int j;
451   ioam_analyse_trace_record *trace_record;
452
453   s = format (s, "pkt_sent : %u\n", record->pkt_sent);
454   s = format (s, "pkt_counter : %u\n", record->pkt_counter);
455   s = format (s, "bytes_counter : %u\n", record->bytes_counter);
456
457   s = format (s, "Trace data: \n");
458
459   for (j = 0; j < IOAM_MAX_PATHS_PER_FLOW; j++)
460     {
461       trace_record = record->trace_data.path_data + j;
462       if (trace_record->is_free)
463         continue;
464
465       s = format (s, "path_map:\n%U", format_path_map,
466                   trace_record->path, trace_record->num_nodes);
467       s = format (s, "pkt_counter: %u\n", trace_record->pkt_counter);
468       s = format (s, "bytes_counter: %u\n", trace_record->bytes_counter);
469
470       s = format (s, "min_delay: %u\n", trace_record->min_delay);
471       s = format (s, "max_delay: %u\n", trace_record->max_delay);
472       s = format (s, "mean_delay: %u\n", trace_record->mean_delay);
473     }
474
475   s = format (s, "\nPOT data: \n");
476   s = format (s, "sfc_validated_count : %u\n",
477               record->pot_data.sfc_validated_count);
478   s = format (s, "sfc_invalidated_count : %u\n",
479               record->pot_data.sfc_invalidated_count);
480
481   s = format (s, "\nSeqno Data:\n");
482   s = format (s,
483               "RX Packets        : %lu\n"
484               "Lost Packets      : %lu\n"
485               "Duplicate Packets : %lu\n"
486               "Reordered Packets : %lu\n",
487               record->seqno_data.rx_packets,
488               record->seqno_data.lost_packets,
489               record->seqno_data.dup_packets,
490               record->seqno_data.reordered_packets);
491
492   s = format (s, "\n");
493   return s;
494 }
495
496 always_inline void
497 ioam_analyse_init_data (ioam_analyser_data_t * data)
498 {
499   u16 j;
500   ioam_analyse_trace_data *trace_data;
501
502   data->is_free = 1;
503
504   /* We maintain data corresponding to last IP-Fix export, this may
505    * get extended in future to maintain history of data */
506   vec_validate_aligned (data->chached_data_list, 0, CLIB_CACHE_LINE_BYTES);
507
508   data->writer_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
509                                               CLIB_CACHE_LINE_BYTES);
510   *(data->writer_lock) = 0;
511
512   trace_data = &(data->trace_data);
513   for (j = 0; j < IOAM_MAX_PATHS_PER_FLOW; j++)
514     trace_data->path_data[j].is_free = 1;
515 }
516
517 #endif /* PLUGINS_IOAM_PLUGIN_IOAM_ANALYSE_IOAM_ANALYSE_H_ */
518
519 /*
520  * fd.io coding-style-patch-verification: ON
521  *
522  * Local Variables:
523  * eval: (c-set-style "gnu")
524  * End:
525  */