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