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