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;
25 static void init_oam_packet_template (oam_main_t *om, oam_target_t * t)
32 vec_validate_aligned (t->template, 0, CLIB_CACHE_LINE_BYTES);
35 memset (h, 0, sizeof (*h));
37 h->ip4.src_address.as_u32 = t->src_address.as_u32;
38 h->ip4.dst_address.as_u32 = t->dst_address.as_u32;
39 h->ip4.ip_version_and_header_length = 0x45;
40 h->ip4.length = clib_host_to_net_u16 (sizeof(*h));
41 h->ip4.ttl = 64; /* as in linux */
42 h->ip4.protocol = IP_PROTOCOL_ICMP;
43 h->ip4.checksum = ip4_header_checksum (&h->ip4);
46 * Template has seq = 0. Each time we send one of these puppies,
47 * change the sequence number and fix the execrated checksum
49 h->icmp.type = ICMP4_echo_request;
50 h->id = clib_host_to_net_u16 (t->id);
52 for (i = 0; i < ARRAY_LEN(h->data); i++)
55 sum = ip_incremental_checksum (0, &h->icmp,
56 sizeof(h->icmp) + sizeof (h->id) +
57 sizeof (h->seq) + sizeof (h->data));
58 csum = ~ip_csum_fold (sum);
59 h->icmp.checksum = csum;
62 int vpe_oam_add_del_target (ip4_address_t *src_address,
63 ip4_address_t *dst_address, u32 fib_id, int is_add)
67 oam_main_t * om = &oam_main;
69 ip4_main_t * im = &ip4_main;
72 /* Make sure the FIB actually exists */
73 p = hash_get (im->fib_index_by_table_id, fib_id);
75 return VNET_API_ERROR_NO_SUCH_FIB;
79 key = ((u64)fib_index<<32) | (dst_address->as_u32);
80 p = hash_get (om->target_by_address_and_fib_id, key);
84 return VNET_API_ERROR_INVALID_REGISTRATION; /* already there... */
86 pool_get (om->targets, t);
87 memset (t, 0, sizeof (*t));
88 t->src_address.as_u32 = src_address->as_u32;
89 t->dst_address.as_u32 = dst_address->as_u32;
91 t->fib_index = fib_index;
92 t->state = OAM_STATE_DEAD;
93 t->last_heard_time = vlib_time_now (om->vlib_main);
94 t->last_heard_seq = (u16) ~om->misses_allowed;
95 t->id = (u16) random_u32 (&om->random_seed);
97 init_oam_packet_template (om, t);
98 hash_set (om->target_by_address_and_fib_id, key, t - om->targets);
101 return VNET_API_ERROR_NO_SUCH_ENTRY; /* no such oam target */
102 t = pool_elt_at_index (om->targets, p[0]);
103 vec_free (t->template);
104 hash_unset (om->target_by_address_and_fib_id, key);
105 pool_put (om->targets, t);
110 static clib_error_t *
111 oam_add_del_target_command_fn (vlib_main_t * vm,
112 unformat_input_t * input,
113 vlib_cli_command_t * cmd)
116 ip4_address_t src_address;
118 ip4_address_t dst_address;
122 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
123 if (unformat(input, "add"))
125 else if (unformat(input, "del"))
127 else if (unformat(input, "src %U",
128 unformat_ip4_address, &src_address))
130 else if (unformat(input, "dst %U",
131 unformat_ip4_address, &dst_address))
133 else if (unformat (input, "fib %d", &fib_id))
136 return clib_error_return (0, "unknown input `%U'",
137 format_unformat_error, input);
141 return clib_error_return (0, "missing add / del qualifier");
143 return clib_error_return (0, "src address not set");
145 return clib_error_return (0, "dst address not set");
147 (void) vpe_oam_add_del_target (&src_address, &dst_address, fib_id, is_add);
152 VLIB_CLI_COMMAND (oam_add_del_target_command, static) = {
154 .short_help = "oam [add|del] target <ip4-address> fib <fib-id>",
155 .function = oam_add_del_target_command_fn,
159 oam_process (vlib_main_t * vm,
160 vlib_node_runtime_t * rt,
161 vlib_frame_t * f_arg)
163 oam_main_t * om = &oam_main;
164 uword *event_data = 0;
171 u32 * to_next, * from;
172 u32 ip4_lookup_node_index;
173 vlib_node_t * ip4_lookup_node;
175 static u32 * buffers;
176 oam_template_copy_t * copy_src, * copy_dst;
177 void send_oam_event (oam_target_t * t);
180 /* Enqueue pkts to ip4-lookup */
181 ip4_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip4-lookup");
182 ip4_lookup_node_index = ip4_lookup_node->index;
185 /* Only timeout events at the moment */
186 vlib_process_wait_for_event_or_clock (vm, om->interval);
187 vec_reset_length (event_data);
189 if (pool_elts (om->targets) == 0)
192 if (vec_len(buffers) < pool_elts(om->targets))
193 vec_validate (buffers, pool_elts(om->targets)-1);
195 nalloc = vlib_buffer_alloc (vm, buffers, pool_elts(om->targets));
196 if (nalloc < pool_elts(om->targets)) {
197 vlib_buffer_free (vm, buffers, nalloc);
201 f = vlib_get_frame_to_node (vm, ip4_lookup_node_index);
203 to_next = vlib_frame_vector_args (f);
206 pool_foreach (t, om->targets,
208 /* State transition announcement... */
209 if ((t->seq - t->last_heard_seq) >= om->misses_allowed) {
210 if (t->state == OAM_STATE_ALIVE) {
212 clib_warning ("oam target %U now DEAD",
213 format_ip4_address, &t->dst_address);
214 t->state = OAM_STATE_DEAD;
218 if (t->state == OAM_STATE_DEAD) {
220 clib_warning ("oam target %U now ALIVE",
221 format_ip4_address, &t->dst_address);
222 t->state = OAM_STATE_ALIVE;
227 /* Send a new icmp */
229 new_seq = clib_host_to_net_u16 (t->seq);
234 b0 = vlib_get_buffer (vm, bi0);
235 vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
236 vnet_buffer (b0)->sw_if_index [VLIB_TX] = t->fib_index;
238 /* Marginally faster than memcpy, probably */
239 copy_dst = (oam_template_copy_t *) b0->data;
240 copy_src = (oam_template_copy_t *) t->template;
242 copy_dst->v8[0] = copy_src->v8[0];
243 copy_dst->v8[1] = copy_src->v8[1];
244 copy_dst->v8[2] = copy_src->v8[2];
245 copy_dst->v8[3] = copy_src->v8[3];
246 copy_dst->v4 = copy_src->v4;
248 b0->current_data = 0;
249 b0->current_length = sizeof (*t->template);
250 h0 = vlib_buffer_get_current (b0);
252 sum0 = h0->icmp.checksum;
253 sum0 = ip_csum_update(sum0, 0 /* old seq */,
254 new_seq, oam_template_t, seq);
256 h0->icmp.checksum = ip_csum_fold (sum0);
261 if (f->n_vectors == VLIB_FRAME_SIZE) {
262 clib_warning ("Too many OAM clients...");
268 vlib_put_frame_to_node (vm, ip4_lookup_node_index, f);
270 return 0; /* not so much */
273 VLIB_REGISTER_NODE (oam_process_node,static) = {
274 .function = oam_process,
275 .type = VLIB_NODE_TYPE_PROCESS,
276 .name = "vpe-oam-process",
279 static clib_error_t *
280 oam_config (vlib_main_t * vm, unformat_input_t * input)
282 oam_main_t * om = &oam_main;
286 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
287 if (unformat (input, "interval %f", &interval))
288 om->interval = interval;
289 else if (unformat (input, "misses-allowed %d", &misses_allowed))
290 om->interval = misses_allowed;
291 else return clib_error_return (0, "unknown input `%U'",
292 format_unformat_error, input);
297 VLIB_CONFIG_FUNCTION (oam_config, "oam");
299 static clib_error_t *
300 oam_init (vlib_main_t * vm)
302 oam_main_t * om = &oam_main;
305 om->vnet_main = vnet_get_main();
307 om->misses_allowed = 3;
308 om->random_seed = (u32) (vlib_time_now(vm)*1e6);
309 om->target_by_address_and_fib_id = hash_create (0, sizeof(uword));
310 om->icmp_id = random_u32 (&om->random_seed);
312 ip4_icmp_register_type (vm, ICMP4_echo_reply, oam_node.index);
317 VLIB_INIT_FUNCTION (oam_init);
319 static u8 * format_oam_target (u8 * s, va_list * args)
321 oam_target_t *t = va_arg (*args, oam_target_t *);
322 int verbose = va_arg (*args, int);
325 return format(s, "%=6s%=14s%=14s%=12s%=10s",
326 "Fib", "Src", "Dst", "Last Heard", "State");
328 s = format (s, "%=6d%=14U%=14U%=12.2f%=10s",
330 format_ip4_address, &t->src_address,
331 format_ip4_address, &t->dst_address,
333 (t->state == OAM_STATE_ALIVE) ? "alive" : "dead");
335 s = format (s, " seq %d last_heard_seq %d",
336 t->seq, t->last_heard_seq);
341 static clib_error_t *
342 show_oam_command_fn (vlib_main_t * vm,
343 unformat_input_t * input,
344 vlib_cli_command_t * cmd)
346 oam_main_t * om = &oam_main;
350 if (unformat (input, "verbose") ||
351 unformat (input, "v"))
355 vlib_cli_output (vm, "%U", format_oam_target, 0, verbose);
357 pool_foreach (t, om->targets,
359 vlib_cli_output (vm, "%U", format_oam_target, t, verbose);
365 VLIB_CLI_COMMAND (show_oam_command, static) = {
367 .short_help = "show oam",
368 .function = show_oam_command_fn,
372 u32 target_pool_index;
373 ip4_address_t address;
376 /* packet trace format function */
377 static u8 * format_swap_trace (u8 * s, va_list * args)
379 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
380 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
381 oam_trace_t * t = va_arg (*args, oam_trace_t *);
383 s = format (s, "OAM: rx from address %U, target index %d",
384 format_ip4_address, &t->address, t->target_pool_index);
389 #define foreach_oam_error \
390 _(PROCESSED, "vpe icmp4 oam replies processed") \
391 _(DROPPED, "icmp4 replies dropped (no registration)")
394 #define _(sym,str) OAM_ERROR_##sym,
400 static char * oam_error_strings[] = {
401 #define _(sym,string) string,
407 * To drop a pkt and increment one of the previous counters:
409 * set b0->error = error_node->errors[OAM_ERROR_EXAMPLE];
410 * set next0 to a disposition index bound to "error-drop".
412 * To manually increment the specific counter OAM_ERROR_EXAMPLE:
414 * vlib_node_t *n = vlib_get_node (vm, oam.index);
415 * u32 node_counter_base_index = n->error_heap_index;
416 * vlib_error_main_t * em = &vm->error_main;
417 * em->counters[node_counter_base_index + OAM_ERROR_EXAMPLE] += 1;
428 oam_node_fn (vlib_main_t * vm,
429 vlib_node_runtime_t * node,
430 vlib_frame_t * frame)
432 u32 n_left_from, * from, * to_next;
433 oam_next_t next_index;
434 oam_main_t * om = &oam_main;
435 u32 next01 = OAM_NEXT_DROP; /* all pkts go to the hopper... */
437 oam_template_t * oam0, * oam1;
438 u32 fib_index0, fib_index1;
440 oam_target_t * t0, *t1;
441 ip4_main_t * im = &ip4_main;
443 from = vlib_frame_vector_args (frame);
444 n_left_from = frame->n_vectors;
445 next_index = node->cached_next_index;
447 while (n_left_from > 0)
451 vlib_get_next_frame (vm, node, next_index,
452 to_next, n_left_to_next);
454 while (n_left_from >= 4 && n_left_to_next >= 2)
457 vlib_buffer_t * b0, * b1;
458 u32 sw_if_index0, sw_if_index1;
460 /* Prefetch next iteration. */
462 vlib_buffer_t * p2, * p3;
464 p2 = vlib_get_buffer (vm, from[2]);
465 p3 = vlib_get_buffer (vm, from[3]);
467 vlib_prefetch_buffer_header (p2, LOAD);
468 vlib_prefetch_buffer_header (p3, LOAD);
470 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
471 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
474 /* speculatively enqueue b0 and b1 to the current next frame */
475 to_next[0] = bi0 = from[0];
476 to_next[1] = bi1 = from[1];
482 b0 = vlib_get_buffer (vm, bi0);
483 b1 = vlib_get_buffer (vm, bi1);
485 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
486 sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
488 oam0 = vlib_buffer_get_current (b0);
489 oam1 = vlib_buffer_get_current (b1);
490 fib_index0 = vec_elt (im->fib_index_by_sw_if_index, sw_if_index0);
491 fib_index1 = vec_elt (im->fib_index_by_sw_if_index, sw_if_index1);
493 key0 = ((u64)fib_index0<<32) | oam0->ip4.src_address.as_u32;
494 u0 = hash_get (om->target_by_address_and_fib_id, key0);
496 t0 = pool_elt_at_index (om->targets, u0[0]);
497 t0->last_heard_time = vlib_time_now (vm);
498 t0->last_heard_seq = clib_net_to_host_u16(oam0->seq);
499 b0->error = node->errors[OAM_ERROR_PROCESSED];
501 b0->error = node->errors[OAM_ERROR_DROPPED];
503 key1 = ((u64)fib_index1<<32) | oam1->ip4.src_address.as_u32;
504 u1 = hash_get (om->target_by_address_and_fib_id, key1);
506 t1 = pool_elt_at_index (om->targets, u1[0]);
507 t1->last_heard_time = vlib_time_now (vm);
508 t1->last_heard_seq = clib_net_to_host_u16(oam1->seq);
509 b1->error = node->errors[OAM_ERROR_PROCESSED];
511 b1->error = node->errors[OAM_ERROR_DROPPED];
513 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
515 if (b0->flags & VLIB_BUFFER_IS_TRACED) {
517 vlib_add_trace (vm, node, b0, sizeof (*t));
518 t->target_pool_index = u0 ? u0[0] : (u32)~0;
519 t->address.as_u32 = oam0->ip4.src_address.as_u32;
521 if (b1->flags & VLIB_BUFFER_IS_TRACED)
524 vlib_add_trace (vm, node, b1, sizeof (*t));
525 t->target_pool_index = u1 ? u1[0] : (u32)~0;
526 t->address.as_u32 = oam1->ip4.src_address.as_u32;
531 if (vm->os_punt_frame)
532 next01 = OAM_NEXT_PUNT;
534 /* verify speculative enqueues, maybe switch current next frame */
535 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
536 to_next, n_left_to_next,
537 bi0, bi1, next01, next01);
540 while (n_left_from > 0 && n_left_to_next > 0)
542 u32 bi0, sw_if_index0;
545 /* speculatively enqueue b0 to the current next frame */
553 b0 = vlib_get_buffer (vm, bi0);
555 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
557 oam0 = vlib_buffer_get_current (b0);
558 fib_index0 = vec_elt (im->fib_index_by_sw_if_index, sw_if_index0);
560 key0 = ((u64)fib_index0<<32) | oam0->ip4.src_address.as_u32;
561 u0 = hash_get (om->target_by_address_and_fib_id, key0);
563 t0 = pool_elt_at_index (om->targets, u0[0]);
564 t0->last_heard_time = vlib_time_now (vm);
565 t0->last_heard_seq = clib_net_to_host_u16(oam0->seq);
566 b0->error = node->errors[OAM_ERROR_PROCESSED];
568 b0->error = node->errors[OAM_ERROR_DROPPED];
570 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
571 && (b0->flags & VLIB_BUFFER_IS_TRACED))) {
573 vlib_add_trace (vm, node, b0, sizeof (*t));
574 t->target_pool_index = u0 ? u0[0] : (u32)~0;
575 t->address.as_u32 = oam0->ip4.src_address.as_u32;
578 if (vm->os_punt_frame)
579 next01 = OAM_NEXT_PUNT;
581 /* verify speculative enqueue, maybe switch current next frame */
582 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
583 to_next, n_left_to_next,
587 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
590 return frame->n_vectors;
593 VLIB_REGISTER_NODE (oam_node,static) = {
594 .function = oam_node_fn,
595 .name = "vpe-icmp4-oam",
596 .vector_size = sizeof (u32),
597 .format_trace = format_swap_trace,
598 .type = VLIB_NODE_TYPE_INTERNAL,
600 .n_errors = ARRAY_LEN(oam_error_strings),
601 .error_strings = oam_error_strings,
603 .n_next_nodes = OAM_N_NEXT,
605 /* edit / add dispositions here */
607 [OAM_NEXT_DROP] = "error-drop",
608 [OAM_NEXT_PUNT] = "error-punt",