Line data Source code
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 0 : 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 0 : u8 done = 0;
140 :
141 0 : size_of_traceopt_per_node = fetch_trace_data_size (trace->ioam_trace_type);
142 : // Unknown trace type
143 0 : if (size_of_traceopt_per_node == 0)
144 0 : return 0;
145 0 : size_of_all_traceopts = trace_len; /*ioam_trace_type,data_list_elts_left */
146 :
147 0 : num_nodes = (u8) (size_of_all_traceopts / size_of_traceopt_per_node);
148 0 : if ((num_nodes == 0) || (num_nodes <= trace->data_list_elts_left))
149 0 : return 0;
150 :
151 0 : num_nodes -= trace->data_list_elts_left;
152 :
153 0 : start_elt = trace->elts;
154 0 : end_elt =
155 0 : trace->elts +
156 0 : (u32) ((size_of_traceopt_per_node / sizeof (u32)) * (num_nodes - 1));
157 :
158 0 : if (oneway && (trace->ioam_trace_type & BIT_TTL_NODEID))
159 : {
160 0 : done = 0;
161 : do
162 : {
163 0 : uturn_elt = start_elt - size_of_traceopt_per_node / sizeof (u32);
164 :
165 0 : if ((clib_net_to_host_u32 (*start_elt) >> 24) <=
166 0 : (clib_net_to_host_u32 (*uturn_elt) >> 24))
167 0 : done = 1;
168 : }
169 0 : while (!done && (start_elt = uturn_elt) != end_elt);
170 : }
171 0 : if (trace->ioam_trace_type & BIT_TTL_NODEID)
172 : {
173 0 : start_elt++;
174 0 : end_elt++;
175 : }
176 0 : if (trace->ioam_trace_type & BIT_ING_INTERFACE)
177 : {
178 0 : start_elt++;
179 0 : end_elt++;
180 : }
181 0 : start_time = clib_net_to_host_u32 (*start_elt);
182 0 : end_time = clib_net_to_host_u32 (*end_elt);
183 :
184 0 : return (f64) (end_time - start_time);
185 : }
186 :
187 : always_inline void
188 0 : 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 0 : clib_spinlock_lock (&data->writer_lock);
196 :
197 0 : trace_data = &data->trace_data;
198 :
199 0 : for (i = 0; i < IOAM_MAX_PATHS_PER_FLOW; i++)
200 : {
201 0 : trace_record = trace_data->path_data + i;
202 :
203 0 : if (trace_record->is_free)
204 0 : continue;
205 :
206 0 : path = trace_record->path;
207 :
208 0 : for (k = 0; k < trace_record->num_nodes; k++)
209 0 : path[k].state_up = 0;
210 : }
211 0 : clib_spinlock_unlock (&data->writer_lock);
212 0 : }
213 :
214 : always_inline void
215 0 : 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 0 : clib_spinlock_lock (&data->writer_lock);
229 :
230 0 : trace_data = &data->trace_data;
231 :
232 0 : size_of_traceopt_per_node = fetch_trace_data_size (trace->ioam_trace_type);
233 0 : if (0 == size_of_traceopt_per_node)
234 0 : goto end;
235 :
236 0 : size_of_all_traceopts = trace_len;
237 :
238 0 : ptr = (u8 *) trace->elts;
239 0 : max_nodes = (u8) (size_of_all_traceopts / size_of_traceopt_per_node);
240 0 : num_nodes = max_nodes - trace->data_list_elts_left;
241 :
242 0 : for (i = 0; i < IOAM_MAX_PATHS_PER_FLOW; i++)
243 : {
244 0 : trace_record = trace_data->path_data + i;
245 0 : path = trace_record->path;
246 :
247 0 : if (trace_record->is_free)
248 0 : continue;
249 :
250 0 : for (j = max_nodes, k = 0; k < num_nodes; j--, k++)
251 : {
252 0 : ptr =
253 0 : (u8 *) ((u8 *) trace->elts +
254 0 : (size_of_traceopt_per_node * (j - 1)));
255 :
256 0 : nodeid = clib_net_to_host_u32 (*((u32 *) ptr)) & 0x00ffffff;
257 0 : ptr += 4;
258 :
259 0 : if (nodeid != path[k].node_id)
260 0 : goto end;
261 :
262 0 : if ((trace->ioam_trace_type == TRACE_TYPE_IF_TS_APP) ||
263 0 : (trace->ioam_trace_type == TRACE_TYPE_IF))
264 : {
265 0 : ingress_if = clib_net_to_host_u16 (*((u16 *) ptr));
266 0 : ptr += 2;
267 0 : egress_if = clib_net_to_host_u16 (*((u16 *) ptr));
268 0 : if ((ingress_if != path[k].ingress_if) ||
269 0 : (egress_if != path[k].egress_if))
270 : {
271 0 : goto end;
272 : }
273 : }
274 : /* Found Match - set path hop state to up */
275 0 : path[k].state_up = 1;
276 : }
277 : }
278 0 : end:
279 0 : clib_spinlock_unlock (&data->writer_lock);
280 0 : }
281 :
282 : always_inline int
283 0 : 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 0 : ioam_path_map_t *path = NULL;
295 : ioam_analyse_trace_record *trace_record;
296 :
297 0 : clib_spinlock_lock (&data->writer_lock);
298 :
299 0 : trace_data = &data->trace_data;
300 :
301 0 : size_of_traceopt_per_node = fetch_trace_data_size (trace->ioam_trace_type);
302 : // Unknown trace type
303 0 : if (size_of_traceopt_per_node == 0)
304 0 : goto DONE;
305 0 : size_of_all_traceopts = trace_len;
306 :
307 0 : ptr = (u8 *) trace->elts;
308 0 : max_nodes = (u8) (size_of_all_traceopts / size_of_traceopt_per_node);
309 0 : num_nodes = max_nodes - trace->data_list_elts_left;
310 :
311 0 : for (i = 0; i < IOAM_MAX_PATHS_PER_FLOW; i++)
312 : {
313 0 : trace_record = trace_data->path_data + i;
314 :
315 0 : if (trace_record->is_free ||
316 0 : (num_nodes != trace_record->num_nodes) ||
317 0 : (trace->ioam_trace_type != trace_record->trace_type))
318 0 : continue;
319 :
320 0 : path = trace_record->path;
321 :
322 0 : for (j = max_nodes, k = 0; k < num_nodes; j--, k++)
323 : {
324 0 : ptr =
325 0 : (u8 *) ((u8 *) trace->elts +
326 0 : (size_of_traceopt_per_node * (j - 1)));
327 :
328 0 : nodeid = clib_net_to_host_u32 (*((u32 *) ptr)) & 0x00ffffff;
329 0 : ptr += 4;
330 :
331 0 : if (nodeid != path[k].node_id)
332 0 : break;
333 :
334 0 : if ((trace->ioam_trace_type == TRACE_TYPE_IF_TS_APP) ||
335 0 : (trace->ioam_trace_type == TRACE_TYPE_IF))
336 : {
337 0 : ingress_if = clib_net_to_host_u16 (*((u16 *) ptr));
338 0 : ptr += 2;
339 0 : egress_if = clib_net_to_host_u16 (*((u16 *) ptr));
340 0 : if ((ingress_if != path[k].ingress_if) ||
341 0 : (egress_if != path[k].egress_if))
342 : {
343 : break;
344 : }
345 : }
346 : }
347 :
348 0 : if (k == num_nodes)
349 : {
350 0 : goto found_match;
351 : }
352 : }
353 :
354 0 : for (i = 0; i < IOAM_MAX_PATHS_PER_FLOW; i++)
355 : {
356 0 : trace_record = trace_data->path_data + i;
357 0 : if (trace_record->is_free)
358 : {
359 0 : trace_record->is_free = 0;
360 0 : trace_record->num_nodes = num_nodes;
361 0 : trace_record->trace_type = trace->ioam_trace_type;
362 0 : path = trace_data->path_data[i].path;
363 0 : trace_record->pkt_counter = 0;
364 0 : trace_record->bytes_counter = 0;
365 0 : trace_record->min_delay = 0xFFFFFFFF;
366 0 : trace_record->max_delay = 0;
367 0 : trace_record->mean_delay = 0;
368 0 : break;
369 : }
370 : }
371 :
372 0 : for (j = max_nodes, k = 0; k < num_nodes; j--, k++)
373 : {
374 0 : ptr =
375 0 : (u8 *) ((u8 *) trace->elts + (size_of_traceopt_per_node * (j - 1)));
376 :
377 0 : path[k].node_id = clib_net_to_host_u32 (*((u32 *) ptr)) & 0x00ffffff;
378 0 : ptr += 4;
379 :
380 0 : if ((trace->ioam_trace_type == TRACE_TYPE_IF_TS_APP) ||
381 0 : (trace->ioam_trace_type == TRACE_TYPE_IF))
382 : {
383 0 : path[k].ingress_if = clib_net_to_host_u16 (*((u16 *) ptr));
384 0 : ptr += 2;
385 0 : path[k].egress_if = clib_net_to_host_u16 (*((u16 *) ptr));
386 : }
387 : }
388 :
389 0 : found_match:
390 : /* Set path state to UP */
391 0 : for (k = 0; k < num_nodes; k++)
392 0 : path[k].state_up = 1;
393 :
394 0 : trace_record->pkt_counter++;
395 0 : trace_record->bytes_counter += pak_len;
396 0 : if (trace->ioam_trace_type & BIT_TIMESTAMP)
397 : {
398 : /* Calculate time delay */
399 0 : u32 delay = (u32) ip6_ioam_analyse_calc_delay (trace, trace_len, 0);
400 0 : if (delay < trace_record->min_delay)
401 0 : trace_record->min_delay = delay;
402 0 : else if (delay > trace_record->max_delay)
403 0 : trace_record->max_delay = delay;
404 :
405 0 : u64 sum = (trace_record->mean_delay * data->seqno_data.rx_packets);
406 0 : trace_record->mean_delay =
407 0 : (u32) ((sum + delay) / (data->seqno_data.rx_packets + 1));
408 : }
409 0 : DONE:
410 0 : clib_spinlock_unlock (&data->writer_lock);
411 0 : return 0;
412 : }
413 :
414 : always_inline int
415 0 : ip6_ioam_analyse_hbh_e2e (ioam_analyser_data_t * data,
416 : ioam_e2e_packet_t * e2e, u16 len)
417 : {
418 0 : clib_spinlock_lock (&data->writer_lock);
419 :
420 0 : ioam_analyze_seqno (&data->seqno_data,
421 0 : (u64) clib_net_to_host_u32 (e2e->e2e_data));
422 :
423 0 : clib_spinlock_unlock (&data->writer_lock);
424 :
425 0 : return 0;
426 : }
427 :
428 : always_inline u8 *
429 0 : format_path_map (u8 * s, va_list * args)
430 : {
431 0 : ioam_path_map_t *pm = va_arg (*args, ioam_path_map_t *);
432 0 : u32 num_of_elts = va_arg (*args, u32);
433 : u32 i;
434 :
435 0 : for (i = 0; i < num_of_elts; i++)
436 : {
437 : s =
438 0 : format (s,
439 : "node_id: 0x%x, ingress_if: 0x%x, egress_if:0x%x, state:%s\n",
440 0 : pm->node_id, pm->ingress_if, pm->egress_if,
441 0 : pm->state_up ? "UP" : "DOWN");
442 0 : pm++;
443 : }
444 :
445 0 : return (s);
446 : }
447 :
448 : always_inline u8 *
449 0 : print_analyse_flow (u8 * s, ioam_analyser_data_t * record)
450 : {
451 : int j;
452 : ioam_analyse_trace_record *trace_record;
453 :
454 0 : s = format (s, "pkt_sent : %u\n", record->pkt_sent);
455 0 : s = format (s, "pkt_counter : %u\n", record->pkt_counter);
456 0 : s = format (s, "bytes_counter : %u\n", record->bytes_counter);
457 :
458 0 : s = format (s, "Trace data: \n");
459 :
460 0 : for (j = 0; j < IOAM_MAX_PATHS_PER_FLOW; j++)
461 : {
462 0 : trace_record = record->trace_data.path_data + j;
463 0 : if (trace_record->is_free)
464 0 : continue;
465 :
466 0 : s = format (s, "path_map:\n%U", format_path_map,
467 0 : trace_record->path, trace_record->num_nodes);
468 0 : s = format (s, "pkt_counter: %u\n", trace_record->pkt_counter);
469 0 : s = format (s, "bytes_counter: %u\n", trace_record->bytes_counter);
470 :
471 0 : s = format (s, "min_delay: %u\n", trace_record->min_delay);
472 0 : s = format (s, "max_delay: %u\n", trace_record->max_delay);
473 0 : s = format (s, "mean_delay: %u\n", trace_record->mean_delay);
474 : }
475 :
476 0 : s = format (s, "\nPOT data: \n");
477 0 : s = format (s, "sfc_validated_count : %u\n",
478 : record->pot_data.sfc_validated_count);
479 0 : s = format (s, "sfc_invalidated_count : %u\n",
480 : record->pot_data.sfc_invalidated_count);
481 :
482 0 : s = format (s, "\nSeqno Data:\n");
483 0 : 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 0 : s = format (s, "\n");
494 0 : return s;
495 : }
496 :
497 : always_inline void
498 29325 : ioam_analyse_init_data (ioam_analyser_data_t * data)
499 : {
500 : u16 j;
501 : ioam_analyse_trace_data *trace_data;
502 :
503 29325 : 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 29325 : vec_validate_aligned (data->chached_data_list, 0, CLIB_CACHE_LINE_BYTES);
508 :
509 29325 : clib_spinlock_init (&data->writer_lock);
510 :
511 29325 : trace_data = &(data->trace_data);
512 322575 : for (j = 0; j < IOAM_MAX_PATHS_PER_FLOW; j++)
513 293250 : trace_data->path_data[j].is_free = 1;
514 29325 : }
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 : */
|