Line data Source code
1 : /*
2 : * Copyright (c) 2016 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 : #include <vlib/vlib.h>
16 : #include <vnet/vnet.h>
17 : #include <vppinfra/error.h>
18 : #include <vpp/app/version.h>
19 :
20 : #include <vnet/ip/ip6.h>
21 : #include <vnet/ip/ip6_hop_by_hop.h>
22 : #include <vnet/ip/ip6_hop_by_hop_packet.h>
23 :
24 : #include <vppinfra/hash.h>
25 : #include <vppinfra/error.h>
26 : #include <vppinfra/elog.h>
27 : #include <vnet/plugin/plugin.h>
28 :
29 : #include <ioam/lib-trace/trace_util.h>
30 : #include <ioam/lib-trace/trace_config.h>
31 : #include <ioam/encap/ip6_ioam_trace.h>
32 : #include <ioam/udp-ping/udp_ping.h>
33 : #include <ioam/udp-ping/udp_ping_packet.h>
34 : #include <ioam/udp-ping/udp_ping_util.h>
35 :
36 : /* Timestamp precision multipliers for seconds, milliseconds, microseconds
37 : * and nanoseconds respectively.
38 : */
39 : static f64 trace_tsp_mul[4] = { 1, 1e3, 1e6, 1e9 };
40 :
41 : typedef union
42 : {
43 : u64 as_u64;
44 : u32 as_u32[2];
45 : } time_u64_t;
46 :
47 : extern ip6_hop_by_hop_ioam_main_t ip6_hop_by_hop_ioam_main;
48 : extern ip6_main_t ip6_main;
49 :
50 : #define foreach_ip6_hop_by_hop_ioam_trace_stats \
51 : _(PROCESSED, "Pkts with ip6 hop-by-hop trace options") \
52 : _(PROFILE_MISS, "Pkts with ip6 hop-by-hop trace options but no profile set") \
53 : _(UPDATED, "Pkts with trace updated") \
54 : _(FULL, "Pkts with trace options but no space") \
55 : _(LOOPBACK, "Pkts with trace options Loopback") \
56 : _(LOOPBACK_REPLY, "Pkts with trace options Loopback Reply")
57 :
58 : static char *ip6_hop_by_hop_ioam_trace_stats_strings[] = {
59 : #define _(sym,string) string,
60 : foreach_ip6_hop_by_hop_ioam_trace_stats
61 : #undef _
62 : };
63 :
64 : typedef enum
65 : {
66 : #define _(sym,str) IP6_IOAM_TRACE_##sym,
67 : foreach_ip6_hop_by_hop_ioam_trace_stats
68 : #undef _
69 : IP6_IOAM_TRACE_N_STATS,
70 : } ip6_ioam_trace_stats_t;
71 :
72 :
73 : typedef struct
74 : {
75 : /* stats */
76 : u64 counters[ARRAY_LEN (ip6_hop_by_hop_ioam_trace_stats_strings)];
77 :
78 : /* convenience */
79 : vlib_main_t *vlib_main;
80 : vnet_main_t *vnet_main;
81 : } ip6_hop_by_hop_ioam_trace_main_t;
82 :
83 : ip6_hop_by_hop_ioam_trace_main_t ip6_hop_by_hop_ioam_trace_main;
84 :
85 : always_inline void
86 0 : ip6_ioam_trace_stats_increment_counter (u32 counter_index, u64 increment)
87 : {
88 0 : ip6_hop_by_hop_ioam_trace_main_t *hm = &ip6_hop_by_hop_ioam_trace_main;
89 :
90 0 : hm->counters[counter_index] += increment;
91 0 : }
92 :
93 :
94 : static u8 *
95 0 : format_ioam_data_list_element (u8 * s, va_list * args)
96 : {
97 0 : u32 *elt = va_arg (*args, u32 *);
98 0 : u8 *trace_type_p = va_arg (*args, u8 *);
99 0 : u8 trace_type = *trace_type_p;
100 :
101 :
102 0 : if (trace_type & BIT_TTL_NODEID)
103 : {
104 0 : u32 ttl_node_id_host_byte_order = clib_net_to_host_u32 (*elt);
105 0 : s = format (s, "ttl 0x%x node id 0x%x ",
106 : ttl_node_id_host_byte_order >> 24,
107 : ttl_node_id_host_byte_order & 0x00FFFFFF);
108 :
109 0 : elt++;
110 : }
111 :
112 0 : if (trace_type & BIT_ING_INTERFACE && trace_type & BIT_ING_INTERFACE)
113 : {
114 0 : u32 ingress_host_byte_order = clib_net_to_host_u32 (*elt);
115 0 : s = format (s, "ingress 0x%x egress 0x%x ",
116 : ingress_host_byte_order >> 16,
117 : ingress_host_byte_order & 0xFFFF);
118 0 : elt++;
119 : }
120 :
121 0 : if (trace_type & BIT_TIMESTAMP)
122 : {
123 0 : u32 ts_in_host_byte_order = clib_net_to_host_u32 (*elt);
124 0 : s = format (s, "ts 0x%x \n", ts_in_host_byte_order);
125 0 : elt++;
126 : }
127 :
128 0 : if (trace_type & BIT_APPDATA)
129 : {
130 0 : u32 appdata_in_host_byte_order = clib_net_to_host_u32 (*elt);
131 0 : s = format (s, "app 0x%x ", appdata_in_host_byte_order);
132 0 : elt++;
133 : }
134 :
135 0 : return s;
136 : }
137 :
138 :
139 : int
140 0 : ip6_ioam_trace_get_sizeof_handler (u32 * result)
141 : {
142 0 : u16 size = 0;
143 0 : u8 trace_data_size = 0;
144 0 : trace_profile *profile = NULL;
145 :
146 0 : *result = 0;
147 :
148 0 : profile = trace_profile_find ();
149 :
150 0 : if (PREDICT_FALSE (!profile))
151 : {
152 0 : ip6_ioam_trace_stats_increment_counter (IP6_IOAM_TRACE_PROFILE_MISS, 1);
153 0 : return (-1);
154 : }
155 :
156 0 : trace_data_size = fetch_trace_data_size (profile->trace_type);
157 0 : if (PREDICT_FALSE (trace_data_size == 0))
158 0 : return VNET_API_ERROR_INVALID_VALUE;
159 :
160 0 : if (PREDICT_FALSE (profile->num_elts * trace_data_size > 254))
161 0 : return VNET_API_ERROR_INVALID_VALUE;
162 :
163 0 : size +=
164 0 : sizeof (ioam_trace_option_t) + (profile->num_elts * trace_data_size);
165 0 : *result = size;
166 :
167 0 : return 0;
168 : }
169 :
170 :
171 :
172 : int
173 0 : ip6_hop_by_hop_ioam_trace_rewrite_handler (u8 * rewrite_string,
174 : u8 * rewrite_size)
175 : {
176 0 : ioam_trace_option_t *trace_option = NULL;
177 0 : u8 trace_data_size = 0;
178 0 : u8 trace_option_elts = 0;
179 0 : trace_profile *profile = NULL;
180 :
181 :
182 0 : profile = trace_profile_find ();
183 :
184 0 : if (PREDICT_FALSE (!profile))
185 : {
186 0 : ip6_ioam_trace_stats_increment_counter (IP6_IOAM_TRACE_PROFILE_MISS, 1);
187 0 : return (-1);
188 : }
189 :
190 0 : if (PREDICT_FALSE (!rewrite_string))
191 0 : return -1;
192 :
193 0 : trace_option_elts = profile->num_elts;
194 0 : trace_data_size = fetch_trace_data_size (profile->trace_type);
195 0 : trace_option = (ioam_trace_option_t *) rewrite_string;
196 0 : trace_option->hdr.type = HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST |
197 : HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE;
198 0 : trace_option->hdr.length = 2 /*ioam_trace_type,data_list_elts_left */ +
199 0 : trace_option_elts * trace_data_size;
200 0 : trace_option->trace_hdr.ioam_trace_type =
201 0 : profile->trace_type & TRACE_TYPE_MASK;
202 0 : trace_option->trace_hdr.data_list_elts_left = trace_option_elts;
203 0 : *rewrite_size =
204 0 : sizeof (ioam_trace_option_t) + (trace_option_elts * trace_data_size);
205 :
206 0 : return 0;
207 : }
208 :
209 : always_inline void
210 0 : ip6_hbh_ioam_loopback_handler (vlib_buffer_t * b, ip6_header_t * ip,
211 : ioam_trace_option_t * trace)
212 : {
213 : u32 buf_index;
214 0 : ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
215 : vlib_buffer_t *b0;
216 0 : vlib_frame_t *nf = 0;
217 : u32 *to_next;
218 : vlib_node_t *next_node;
219 : ip6_header_t *ip6;
220 : ip6_hop_by_hop_header_t *hbh;
221 : ioam_trace_option_t *opt;
222 : udp_ping_t *udp;
223 :
224 0 : b0 = vlib_buffer_copy (hm->vlib_main, b);
225 0 : if (b0 == NULL)
226 0 : return;
227 :
228 0 : buf_index = vlib_get_buffer_index (hm->vlib_main, b0);
229 0 : next_node = vlib_get_node_by_name (hm->vlib_main, (u8 *) "ip6-lookup");
230 0 : nf = vlib_get_frame_to_node (hm->vlib_main, next_node->index);
231 0 : nf->n_vectors = 0;
232 0 : to_next = vlib_frame_vector_args (nf);
233 :
234 0 : vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
235 0 : vnet_buffer (b0)->sw_if_index[VLIB_TX] = ~0;
236 :
237 0 : ip6 = vlib_buffer_get_current (b0);
238 0 : hbh = (ip6_hop_by_hop_header_t *) (ip6 + 1);
239 : opt = (ioam_trace_option_t *)
240 0 : ip6_hbh_get_option (hbh, HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST);
241 :
242 0 : udp = (udp_ping_t *) ((u8 *) hbh + ((hbh->length + 1) << 3));
243 0 : udp_ping_create_reply_from_probe_ip6 (ip6, hbh, udp);
244 0 : ip6_hbh_ioam_trace_set_bit (opt, BIT_LOOPBACK_REPLY);
245 :
246 0 : *to_next = buf_index;
247 0 : nf->n_vectors++;
248 0 : to_next++;
249 :
250 0 : vlib_put_frame_to_node (hm->vlib_main, next_node->index, nf);
251 0 : ip6_ioam_trace_stats_increment_counter (IP6_IOAM_TRACE_LOOPBACK, 1);
252 : }
253 :
254 : int
255 0 : ip6_hbh_ioam_trace_data_list_handler (vlib_buffer_t * b, ip6_header_t * ip,
256 : ip6_hop_by_hop_option_t * opt)
257 : {
258 0 : ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
259 0 : u8 elt_index = 0;
260 0 : ioam_trace_option_t *trace = (ioam_trace_option_t *) opt;
261 0 : u32 adj_index = vnet_buffer (b)->ip.adj_index[VLIB_TX];
262 0 : ip_adjacency_t *adj = adj_get (adj_index);
263 : time_u64_t time_u64;
264 : u32 *elt;
265 0 : int rv = 0;
266 0 : trace_profile *profile = NULL;
267 :
268 :
269 0 : profile = trace_profile_find ();
270 :
271 0 : if (PREDICT_FALSE (!profile))
272 : {
273 0 : ip6_ioam_trace_stats_increment_counter (IP6_IOAM_TRACE_PROFILE_MISS, 1);
274 0 : return (-1);
275 : }
276 :
277 : /* Don't trace loopback reply packets */
278 0 : if (trace->trace_hdr.ioam_trace_type & BIT_LOOPBACK_REPLY)
279 : {
280 0 : ip6_ioam_trace_stats_increment_counter (IP6_IOAM_TRACE_LOOPBACK_REPLY,
281 : 1);
282 0 : return rv;
283 : }
284 :
285 0 : time_u64.as_u64 = 0;
286 :
287 0 : if (PREDICT_TRUE (trace->trace_hdr.data_list_elts_left))
288 : {
289 0 : trace->trace_hdr.data_list_elts_left--;
290 : /* fetch_trace_data_size returns in bytes. Convert it to 4-bytes
291 : * to skip to this node's location.
292 : */
293 0 : elt_index =
294 0 : trace->trace_hdr.data_list_elts_left *
295 0 : fetch_trace_data_size (trace->trace_hdr.ioam_trace_type) / 4;
296 0 : elt = &trace->trace_hdr.elts[elt_index];
297 0 : if (trace->trace_hdr.ioam_trace_type & BIT_TTL_NODEID)
298 : {
299 0 : *elt =
300 0 : clib_host_to_net_u32 ((ip->hop_limit << 24) | profile->node_id);
301 0 : elt++;
302 : }
303 :
304 0 : if (trace->trace_hdr.ioam_trace_type & BIT_ING_INTERFACE)
305 : {
306 0 : *elt =
307 0 : (vnet_buffer (b)->sw_if_index[VLIB_RX] & 0xFFFF) << 16 |
308 0 : (adj->rewrite_header.sw_if_index & 0xFFFF);
309 0 : *elt = clib_host_to_net_u32 (*elt);
310 0 : elt++;
311 : }
312 :
313 0 : if (trace->trace_hdr.ioam_trace_type & BIT_TIMESTAMP)
314 : {
315 : /* Send least significant 32 bits */
316 0 : f64 time_f64 =
317 0 : (f64) (((f64) hm->unix_time_0) +
318 0 : (vlib_time_now (hm->vlib_main) - hm->vlib_time_0));
319 :
320 0 : time_u64.as_u64 = time_f64 * trace_tsp_mul[profile->trace_tsp];
321 0 : *elt = clib_host_to_net_u32 (time_u64.as_u32[0]);
322 0 : elt++;
323 : }
324 :
325 0 : if (trace->trace_hdr.ioam_trace_type & BIT_APPDATA)
326 : {
327 : /* $$$ set elt0->app_data */
328 0 : *elt = clib_host_to_net_u32 (profile->app_data);
329 0 : elt++;
330 : }
331 :
332 :
333 0 : if (PREDICT_FALSE (trace->trace_hdr.ioam_trace_type & BIT_LOOPBACK))
334 : {
335 : /* if loopback flag set then copy the packet
336 : * and send it back to source */
337 0 : ip6_hbh_ioam_loopback_handler (b, ip, trace);
338 : }
339 :
340 0 : ip6_ioam_trace_stats_increment_counter (IP6_IOAM_TRACE_UPDATED, 1);
341 : }
342 : else
343 : {
344 0 : ip6_ioam_trace_stats_increment_counter (IP6_IOAM_TRACE_FULL, 1);
345 : }
346 0 : return (rv);
347 : }
348 :
349 : u8 *
350 0 : ip6_hbh_ioam_trace_data_list_trace_handler (u8 * s,
351 : ip6_hop_by_hop_option_t * opt)
352 : {
353 : ioam_trace_option_t *trace;
354 0 : u8 trace_data_size_in_words = 0;
355 : u32 *elt;
356 0 : int elt_index = 0;
357 :
358 0 : trace = (ioam_trace_option_t *) opt;
359 : s =
360 0 : format (s, " Trace Type 0x%x , %d elts left\n",
361 0 : trace->trace_hdr.ioam_trace_type,
362 0 : trace->trace_hdr.data_list_elts_left);
363 0 : trace_data_size_in_words =
364 0 : fetch_trace_data_size (trace->trace_hdr.ioam_trace_type) / 4;
365 0 : elt = &trace->trace_hdr.elts[0];
366 0 : while ((u8 *) elt <
367 0 : ((u8 *) (&trace->trace_hdr.elts[0]) + trace->hdr.length - 2
368 : /* -2 accounts for ioam_trace_type,elts_left */ ))
369 : {
370 0 : s = format (s, " [%d] %U\n", elt_index,
371 : format_ioam_data_list_element,
372 : elt, &trace->trace_hdr.ioam_trace_type);
373 0 : elt_index++;
374 0 : elt += trace_data_size_in_words;
375 : }
376 0 : return (s);
377 : }
378 :
379 :
380 : static clib_error_t *
381 0 : ip6_show_ioam_trace_cmd_fn (vlib_main_t * vm,
382 : unformat_input_t * input,
383 : vlib_cli_command_t * cmd)
384 : {
385 0 : ip6_hop_by_hop_ioam_trace_main_t *hm = &ip6_hop_by_hop_ioam_trace_main;
386 0 : u8 *s = 0;
387 0 : int i = 0;
388 :
389 0 : for (i = 0; i < IP6_IOAM_TRACE_N_STATS; i++)
390 : {
391 0 : s =
392 0 : format (s, " %s - %lu\n", ip6_hop_by_hop_ioam_trace_stats_strings[i],
393 : hm->counters[i]);
394 : }
395 :
396 0 : vlib_cli_output (vm, "%v", s);
397 0 : vec_free (s);
398 0 : return 0;
399 : }
400 :
401 :
402 : /* *INDENT-OFF* */
403 176567 : VLIB_CLI_COMMAND (ip6_show_ioam_trace_cmd, static) = {
404 : .path = "show ioam trace",
405 : .short_help = "iOAM trace statistics",
406 : .function = ip6_show_ioam_trace_cmd_fn,
407 : };
408 : /* *INDENT-ON* */
409 :
410 : /* *INDENT-OFF* */
411 : VLIB_PLUGIN_REGISTER () = {
412 : .version = VPP_BUILD_VER,
413 : .description = "Inbound Operations, Administration, and Maintenance (OAM)",
414 : };
415 : /* *INDENT-ON* */
416 :
417 : static clib_error_t *
418 559 : ip6_hop_by_hop_ioam_trace_init (vlib_main_t * vm)
419 : {
420 559 : ip6_hop_by_hop_ioam_trace_main_t *hm = &ip6_hop_by_hop_ioam_trace_main;
421 :
422 559 : hm->vlib_main = vm;
423 559 : hm->vnet_main = vnet_get_main ();
424 559 : clib_memset (hm->counters, 0, sizeof (hm->counters));
425 :
426 :
427 559 : if (ip6_hbh_register_option
428 : (HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST,
429 : ip6_hbh_ioam_trace_data_list_handler,
430 : ip6_hbh_ioam_trace_data_list_trace_handler) < 0)
431 0 : return (clib_error_create
432 : ("registration of HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST failed"));
433 :
434 :
435 559 : if (ip6_hbh_add_register_option (HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST,
436 : sizeof (ioam_trace_option_t),
437 : ip6_hop_by_hop_ioam_trace_rewrite_handler)
438 : < 0)
439 0 : return (clib_error_create
440 : ("registration of HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST for rewrite failed"));
441 :
442 :
443 559 : return (0);
444 : }
445 :
446 : /* *INDENT-OFF* */
447 2239 : VLIB_INIT_FUNCTION (ip6_hop_by_hop_ioam_trace_init) =
448 : {
449 : .runs_after = VLIB_INITS ("ip_main_init", "ip6_lookup_init",
450 : "ip6_hop_by_hop_ioam_init"),
451 : };
452 : /* *INDENT-ON* */
453 :
454 : int
455 559 : ip6_trace_profile_cleanup (void)
456 : {
457 559 : ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
458 :
459 559 : hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] = 0;
460 :
461 559 : return 0;
462 :
463 : }
464 :
465 :
466 : int
467 0 : ip6_trace_profile_setup (void)
468 : {
469 0 : u32 trace_size = 0;
470 0 : ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
471 :
472 0 : trace_profile *profile = NULL;
473 :
474 :
475 0 : profile = trace_profile_find ();
476 :
477 0 : if (PREDICT_FALSE (!profile))
478 : {
479 0 : ip6_ioam_trace_stats_increment_counter (IP6_IOAM_TRACE_PROFILE_MISS, 1);
480 0 : return (-1);
481 : }
482 :
483 :
484 0 : if (ip6_ioam_trace_get_sizeof_handler (&trace_size) < 0)
485 0 : return (-1);
486 :
487 0 : hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] = trace_size;
488 :
489 0 : return (0);
490 : }
491 :
492 : /*
493 : * fd.io coding-style-patch-verification: ON
494 : *
495 : * Local Variables:
496 : * eval: (c-set-style "gnu")
497 : * End:
498 : */
|