2 * Copyright (c) 2015 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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <vnet/devices/dpdk/dpdk.h>
23 static vlib_node_registration_t oam_node;
26 init_oam_packet_template (oam_main_t * om, oam_target_t * t)
33 vec_validate_aligned (t->template, 0, CLIB_CACHE_LINE_BYTES);
36 memset (h, 0, sizeof (*h));
38 h->ip4.src_address.as_u32 = t->src_address.as_u32;
39 h->ip4.dst_address.as_u32 = t->dst_address.as_u32;
40 h->ip4.ip_version_and_header_length = 0x45;
41 h->ip4.length = clib_host_to_net_u16 (sizeof (*h));
42 h->ip4.ttl = 64; /* as in linux */
43 h->ip4.protocol = IP_PROTOCOL_ICMP;
44 h->ip4.checksum = ip4_header_checksum (&h->ip4);
47 * Template has seq = 0. Each time we send one of these puppies,
48 * change the sequence number and fix the execrated checksum
50 h->icmp.type = ICMP4_echo_request;
51 h->id = clib_host_to_net_u16 (t->id);
53 for (i = 0; i < ARRAY_LEN (h->data); i++)
56 sum = ip_incremental_checksum (0, &h->icmp,
57 sizeof (h->icmp) + sizeof (h->id) +
58 sizeof (h->seq) + sizeof (h->data));
59 csum = ~ip_csum_fold (sum);
60 h->icmp.checksum = csum;
64 vpe_oam_add_del_target (ip4_address_t * src_address,
65 ip4_address_t * dst_address, u32 fib_id, int is_add)
69 oam_main_t *om = &oam_main;
71 ip4_main_t *im = &ip4_main;
74 /* Make sure the FIB actually exists */
75 p = hash_get (im->fib_index_by_table_id, fib_id);
77 return VNET_API_ERROR_NO_SUCH_FIB;
81 key = ((u64) fib_index << 32) | (dst_address->as_u32);
82 p = hash_get (om->target_by_address_and_fib_id, key);
87 return VNET_API_ERROR_INVALID_REGISTRATION; /* already there... */
89 pool_get (om->targets, t);
90 memset (t, 0, sizeof (*t));
91 t->src_address.as_u32 = src_address->as_u32;
92 t->dst_address.as_u32 = dst_address->as_u32;
94 t->fib_index = fib_index;
95 t->state = OAM_STATE_DEAD;
96 t->last_heard_time = vlib_time_now (om->vlib_main);
97 t->last_heard_seq = (u16) ~ om->misses_allowed;
98 t->id = (u16) random_u32 (&om->random_seed);
100 init_oam_packet_template (om, t);
101 hash_set (om->target_by_address_and_fib_id, key, t - om->targets);
106 return VNET_API_ERROR_NO_SUCH_ENTRY; /* no such oam target */
107 t = pool_elt_at_index (om->targets, p[0]);
108 vec_free (t->template);
109 hash_unset (om->target_by_address_and_fib_id, key);
110 pool_put (om->targets, t);
115 static clib_error_t *
116 oam_add_del_target_command_fn (vlib_main_t * vm,
117 unformat_input_t * input,
118 vlib_cli_command_t * cmd)
121 ip4_address_t src_address;
123 ip4_address_t dst_address;
127 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
129 if (unformat (input, "add"))
131 else if (unformat (input, "del"))
133 else if (unformat (input, "src %U", unformat_ip4_address, &src_address))
135 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_address))
137 else if (unformat (input, "fib %d", &fib_id))
140 return clib_error_return (0, "unknown input `%U'",
141 format_unformat_error, input);
145 return clib_error_return (0, "missing add / del qualifier");
147 return clib_error_return (0, "src address not set");
149 return clib_error_return (0, "dst address not set");
151 (void) vpe_oam_add_del_target (&src_address, &dst_address, fib_id, is_add);
157 VLIB_CLI_COMMAND (oam_add_del_target_command, static) = {
159 .short_help = "oam [add|del] target <ip4-address> fib <fib-id>",
160 .function = oam_add_del_target_command_fn,
165 oam_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f_arg)
167 oam_main_t *om = &oam_main;
168 uword *event_data = 0;
176 u32 ip4_lookup_node_index;
177 vlib_node_t *ip4_lookup_node;
180 oam_template_copy_t *copy_src, *copy_dst;
181 void send_oam_event (oam_target_t * t);
184 /* Enqueue pkts to ip4-lookup */
185 ip4_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip4-lookup");
186 ip4_lookup_node_index = ip4_lookup_node->index;
190 /* Only timeout events at the moment */
191 vlib_process_wait_for_event_or_clock (vm, om->interval);
192 vec_reset_length (event_data);
194 if (pool_elts (om->targets) == 0)
197 if (vec_len (buffers) < pool_elts (om->targets))
198 vec_validate (buffers, pool_elts (om->targets) - 1);
200 nalloc = vlib_buffer_alloc (vm, buffers, pool_elts (om->targets));
201 if (nalloc < pool_elts (om->targets))
203 vlib_buffer_free (vm, buffers, nalloc);
207 f = vlib_get_frame_to_node (vm, ip4_lookup_node_index);
209 to_next = vlib_frame_vector_args (f);
213 pool_foreach (t, om->targets,
215 /* State transition announcement... */
216 if ((t->seq - t->last_heard_seq) >= om->misses_allowed)
218 if (t->state == OAM_STATE_ALIVE)
221 clib_warning ("oam target %U now DEAD",
222 format_ip4_address, &t->dst_address);
223 t->state = OAM_STATE_DEAD;
229 if (t->state == OAM_STATE_DEAD)
232 clib_warning ("oam target %U now ALIVE",
233 format_ip4_address, &t->dst_address);
234 t->state = OAM_STATE_ALIVE;
239 /* Send a new icmp */
241 new_seq = clib_host_to_net_u16 (t->seq);
246 b0 = vlib_get_buffer (vm, bi0);
247 vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
248 vnet_buffer (b0)->sw_if_index [VLIB_TX] = t->fib_index;
250 /* Marginally faster than memcpy, probably */
251 copy_dst = (oam_template_copy_t *) b0->data;
252 copy_src = (oam_template_copy_t *) t->template;
254 copy_dst->v8[0] = copy_src->v8[0];
255 copy_dst->v8[1] = copy_src->v8[1];
256 copy_dst->v8[2] = copy_src->v8[2];
257 copy_dst->v8[3] = copy_src->v8[3];
258 copy_dst->v4 = copy_src->v4;
260 b0->current_data = 0;
261 b0->current_length = sizeof (*t->template);
262 h0 = vlib_buffer_get_current (b0);
264 sum0 = h0->icmp.checksum;
265 sum0 = ip_csum_update(sum0, 0 /* old seq */,
266 new_seq, oam_template_t, seq);
268 h0->icmp.checksum = ip_csum_fold (sum0);
273 if (f->n_vectors == VLIB_FRAME_SIZE)
275 clib_warning ("Too many OAM clients...");
282 vlib_put_frame_to_node (vm, ip4_lookup_node_index, f);
284 return 0; /* not so much */
288 VLIB_REGISTER_NODE (oam_process_node,static) = {
289 .function = oam_process,
290 .type = VLIB_NODE_TYPE_PROCESS,
291 .name = "vpe-oam-process",
295 static clib_error_t *
296 oam_config (vlib_main_t * vm, unformat_input_t * input)
298 oam_main_t *om = &oam_main;
302 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
304 if (unformat (input, "interval %f", &interval))
305 om->interval = interval;
306 else if (unformat (input, "misses-allowed %d", &misses_allowed))
307 om->interval = misses_allowed;
309 return clib_error_return (0, "unknown input `%U'",
310 format_unformat_error, input);
315 VLIB_CONFIG_FUNCTION (oam_config, "oam");
317 static clib_error_t *
318 oam_init (vlib_main_t * vm)
320 oam_main_t *om = &oam_main;
323 om->vnet_main = vnet_get_main ();
325 om->misses_allowed = 3;
326 om->random_seed = (u32) (vlib_time_now (vm) * 1e6);
327 om->target_by_address_and_fib_id = hash_create (0, sizeof (uword));
328 om->icmp_id = random_u32 (&om->random_seed);
330 ip4_icmp_register_type (vm, ICMP4_echo_reply, oam_node.index);
335 VLIB_INIT_FUNCTION (oam_init);
338 format_oam_target (u8 * s, va_list * args)
340 oam_target_t *t = va_arg (*args, oam_target_t *);
341 int verbose = va_arg (*args, int);
344 return format (s, "%=6s%=14s%=14s%=12s%=10s",
345 "Fib", "Src", "Dst", "Last Heard", "State");
347 s = format (s, "%=6d%=14U%=14U%=12.2f%=10s",
349 format_ip4_address, &t->src_address,
350 format_ip4_address, &t->dst_address,
352 (t->state == OAM_STATE_ALIVE) ? "alive" : "dead");
354 s = format (s, " seq %d last_heard_seq %d", t->seq, t->last_heard_seq);
359 static clib_error_t *
360 show_oam_command_fn (vlib_main_t * vm,
361 unformat_input_t * input, vlib_cli_command_t * cmd)
363 oam_main_t *om = &oam_main;
367 if (unformat (input, "verbose") || unformat (input, "v"))
371 vlib_cli_output (vm, "%U", format_oam_target, 0, verbose);
374 pool_foreach (t, om->targets,
376 vlib_cli_output (vm, "%U", format_oam_target, t, verbose);
384 VLIB_CLI_COMMAND (show_oam_command, static) = {
386 .short_help = "show oam",
387 .function = show_oam_command_fn,
393 u32 target_pool_index;
394 ip4_address_t address;
397 /* packet trace format function */
399 format_swap_trace (u8 * s, va_list * args)
401 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
402 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
403 oam_trace_t *t = va_arg (*args, oam_trace_t *);
405 s = format (s, "OAM: rx from address %U, target index %d",
406 format_ip4_address, &t->address, t->target_pool_index);
411 #define foreach_oam_error \
412 _(PROCESSED, "vpe icmp4 oam replies processed") \
413 _(DROPPED, "icmp4 replies dropped (no registration)")
417 #define _(sym,str) OAM_ERROR_##sym,
423 static char *oam_error_strings[] = {
424 #define _(sym,string) string,
430 * To drop a pkt and increment one of the previous counters:
432 * set b0->error = error_node->errors[OAM_ERROR_EXAMPLE];
433 * set next0 to a disposition index bound to "error-drop".
435 * To manually increment the specific counter OAM_ERROR_EXAMPLE:
437 * vlib_node_t *n = vlib_get_node (vm, oam.index);
438 * u32 node_counter_base_index = n->error_heap_index;
439 * vlib_error_main_t * em = &vm->error_main;
440 * em->counters[node_counter_base_index + OAM_ERROR_EXAMPLE] += 1;
452 oam_node_fn (vlib_main_t * vm,
453 vlib_node_runtime_t * node, vlib_frame_t * frame)
455 u32 n_left_from, *from, *to_next;
456 oam_next_t next_index;
457 oam_main_t *om = &oam_main;
458 u32 next0 = OAM_NEXT_DROP; /* all pkts go to the hopper... */
459 u32 next1 = OAM_NEXT_DROP;
461 oam_template_t *oam0, *oam1;
462 u32 fib_index0, fib_index1;
464 oam_target_t *t0, *t1;
465 ip4_main_t *im = &ip4_main;
467 from = vlib_frame_vector_args (frame);
468 n_left_from = frame->n_vectors;
469 next_index = node->cached_next_index;
471 while (n_left_from > 0)
475 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
477 while (n_left_from >= 4 && n_left_to_next >= 2)
480 vlib_buffer_t *b0, *b1;
481 u32 sw_if_index0, sw_if_index1;
483 /* Prefetch next iteration. */
485 vlib_buffer_t *p2, *p3;
487 p2 = vlib_get_buffer (vm, from[2]);
488 p3 = vlib_get_buffer (vm, from[3]);
490 vlib_prefetch_buffer_header (p2, LOAD);
491 vlib_prefetch_buffer_header (p3, LOAD);
493 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
494 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
497 /* speculatively enqueue b0 and b1 to the current next frame */
498 to_next[0] = bi0 = from[0];
499 to_next[1] = bi1 = from[1];
505 b0 = vlib_get_buffer (vm, bi0);
506 b1 = vlib_get_buffer (vm, bi1);
508 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
509 sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
511 oam0 = vlib_buffer_get_current (b0);
512 oam1 = vlib_buffer_get_current (b1);
513 fib_index0 = vec_elt (im->fib_index_by_sw_if_index, sw_if_index0);
514 fib_index1 = vec_elt (im->fib_index_by_sw_if_index, sw_if_index1);
516 key0 = ((u64) fib_index0 << 32) | oam0->ip4.src_address.as_u32;
517 u0 = hash_get (om->target_by_address_and_fib_id, key0);
520 t0 = pool_elt_at_index (om->targets, u0[0]);
521 t0->last_heard_time = vlib_time_now (vm);
522 t0->last_heard_seq = clib_net_to_host_u16 (oam0->seq);
523 b0->error = node->errors[OAM_ERROR_PROCESSED];
526 b0->error = node->errors[OAM_ERROR_DROPPED];
528 key1 = ((u64) fib_index1 << 32) | oam1->ip4.src_address.as_u32;
529 u1 = hash_get (om->target_by_address_and_fib_id, key1);
532 t1 = pool_elt_at_index (om->targets, u1[0]);
533 t1->last_heard_time = vlib_time_now (vm);
534 t1->last_heard_seq = clib_net_to_host_u16 (oam1->seq);
535 b1->error = node->errors[OAM_ERROR_PROCESSED];
538 b1->error = node->errors[OAM_ERROR_DROPPED];
540 if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
542 if (b0->flags & VLIB_BUFFER_IS_TRACED)
544 oam_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
545 t->target_pool_index = u0 ? u0[0] : (u32) ~ 0;
546 t->address.as_u32 = oam0->ip4.src_address.as_u32;
548 if (b1->flags & VLIB_BUFFER_IS_TRACED)
550 oam_trace_t *t = vlib_add_trace (vm, node, b1, sizeof (*t));
551 t->target_pool_index = u1 ? u1[0] : (u32) ~ 0;
552 t->address.as_u32 = oam1->ip4.src_address.as_u32;
557 if (vm->os_punt_frame)
558 next0 = next1 = OAM_NEXT_PUNT;
560 /* verify speculative enqueues, maybe switch current next frame */
561 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
562 to_next, n_left_to_next,
563 bi0, bi1, next0, next1);
566 while (n_left_from > 0 && n_left_to_next > 0)
568 u32 bi0, sw_if_index0;
571 /* speculatively enqueue b0 to the current next frame */
579 b0 = vlib_get_buffer (vm, bi0);
581 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
583 oam0 = vlib_buffer_get_current (b0);
584 fib_index0 = vec_elt (im->fib_index_by_sw_if_index, sw_if_index0);
586 key0 = ((u64) fib_index0 << 32) | oam0->ip4.src_address.as_u32;
587 u0 = hash_get (om->target_by_address_and_fib_id, key0);
590 t0 = pool_elt_at_index (om->targets, u0[0]);
591 t0->last_heard_time = vlib_time_now (vm);
592 t0->last_heard_seq = clib_net_to_host_u16 (oam0->seq);
593 b0->error = node->errors[OAM_ERROR_PROCESSED];
596 b0->error = node->errors[OAM_ERROR_DROPPED];
598 if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
599 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
601 oam_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
602 t->target_pool_index = u0 ? u0[0] : (u32) ~ 0;
603 t->address.as_u32 = oam0->ip4.src_address.as_u32;
606 if (vm->os_punt_frame)
607 next0 = OAM_NEXT_PUNT;
609 /* verify speculative enqueue, maybe switch current next frame */
610 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
611 to_next, n_left_to_next,
615 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
618 return frame->n_vectors;
622 VLIB_REGISTER_NODE (oam_node,static) = {
623 .function = oam_node_fn,
624 .name = "vpe-icmp4-oam",
625 .vector_size = sizeof (u32),
626 .format_trace = format_swap_trace,
627 .type = VLIB_NODE_TYPE_INTERNAL,
629 .n_errors = ARRAY_LEN(oam_error_strings),
630 .error_strings = oam_error_strings,
632 .n_next_nodes = OAM_N_NEXT,
634 /* edit / add dispositions here */
636 [OAM_NEXT_DROP] = "error-drop",
637 [OAM_NEXT_PUNT] = "error-punt",
643 * fd.io coding-style-patch-verification: ON
646 * eval: (c-set-style "gnu")