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