2 * Copyright (c) 2021 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 <vlib/vlib.h>
19 #include <vlib/unix/unix.h>
20 #include <vnet/plugin/plugin.h>
21 #include <vpp/app/version.h>
22 #include <vnet/ip-neighbor/ip4_neighbor.h>
23 #include <vnet/ip-neighbor/ip6_neighbor.h>
24 #include <arping/arping.h>
26 arping_main_t arping_main;
28 #define foreach_arping_error _ (NONE, "no error")
32 #define _(f, s) ARPING_ERROR_##f,
38 static char *arping_error_strings[] = {
44 #define foreach_arping \
45 _ (DROP, "error-drop") \
46 _ (IO, "interface-output")
50 #define _(sym, str) ARPING_NEXT_##sym,
56 typedef struct arping_trace_t_
60 ethernet_arp_ip4_over_ethernet_address_t reply;
65 #define _(sym, str) ARPING6_NEXT_##sym,
71 typedef CLIB_PACKED (struct {
74 }) ethernet_arp_ip6_over_ethernet_address_t;
76 typedef struct arping6_trace_t_
80 ethernet_arp_ip6_over_ethernet_address_t reply;
83 /* packet trace format function */
85 format_arping_trace (u8 *s, va_list *args)
87 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
88 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
89 arping_trace_t *t = va_arg (*args, arping_trace_t *);
91 s = format (s, "sw-if-index: %u, opcode: %U, from %U (%U)", t->sw_if_index,
92 format_ethernet_arp_opcode, t->arp_opcode, format_mac_address,
93 &t->reply.mac, format_ip4_address, &t->reply.ip4);
99 format_arping6_trace (u8 *s, va_list *args)
101 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
102 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
103 arping6_trace_t *t = va_arg (*args, arping6_trace_t *);
105 s = format (s, "sw-if-index: %u, type: %u, from %U (%U)", t->sw_if_index,
106 t->type, format_mac_address, &t->reply.mac, format_ip6_address,
112 VLIB_NODE_FN (arping_input_node)
113 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
115 u32 n_left_from, *from, *to_next, n_left_to_next;
116 arping_next_t next_index;
117 arping_main_t *am = &arping_main;
119 next_index = node->cached_next_index;
120 n_left_from = frame->n_vectors;
121 from = vlib_frame_vector_args (frame);
123 while (n_left_from > 0)
125 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
127 while (n_left_from >= 2 && n_left_to_next >= 2)
129 u32 next0, next1, bi0, bi1;
130 vlib_buffer_t *b0, *b1;
131 ethernet_arp_header_t *arp0, *arp1;
132 u32 sw_if_index0, sw_if_index1;
133 arping_intf_t *aif0, *aif1;
135 bi0 = to_next[0] = from[0];
136 bi1 = to_next[1] = from[1];
143 next0 = next1 = ARPING_NEXT_DROP;
145 b0 = vlib_get_buffer (vm, bi0);
146 b1 = vlib_get_buffer (vm, bi1);
148 arp0 = vlib_buffer_get_current (b0);
149 arp1 = vlib_buffer_get_current (b1);
151 vnet_feature_next (&next0, b0);
152 vnet_feature_next (&next1, b1);
154 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
155 sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
157 if (PREDICT_TRUE (arp0->opcode ==
158 clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply)))
160 aif0 = am->interfaces[sw_if_index0];
161 if (PREDICT_TRUE (aif0->address.ip.ip4.as_u32 ==
162 arp0->ip4_over_ethernet[0].ip4.as_u32))
164 aif0->recv.from4.ip4.as_u32 =
165 arp0->ip4_over_ethernet[0].ip4.as_u32;
166 clib_memcpy_fast (&aif0->recv.from4.mac,
167 &arp0->ip4_over_ethernet[0].mac, 6);
171 if (PREDICT_TRUE (arp1->opcode ==
172 clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply)))
174 aif1 = am->interfaces[sw_if_index1];
175 if (PREDICT_TRUE (aif1->address.ip.ip4.as_u32 ==
176 arp1->ip4_over_ethernet[0].ip4.as_u32))
178 aif1->recv.from4.ip4.as_u32 =
179 arp1->ip4_over_ethernet[0].ip4.as_u32;
180 clib_memcpy_fast (&aif1->recv.from4.mac,
181 &arp1->ip4_over_ethernet[0].mac, 6);
186 if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
188 arping_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
189 t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
190 t->arp_opcode = clib_host_to_net_u16 (arp0->opcode);
191 t->reply.ip4.as_u32 = arp0->ip4_over_ethernet[0].ip4.as_u32;
192 clib_memcpy_fast (&t->reply.mac, &arp0->ip4_over_ethernet[0].mac,
195 if (PREDICT_FALSE ((b1->flags & VLIB_BUFFER_IS_TRACED)))
197 arping_trace_t *t = vlib_add_trace (vm, node, b1, sizeof (*t));
198 t->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];
199 t->arp_opcode = clib_host_to_net_u16 (arp1->opcode);
200 t->reply.ip4.as_u32 = arp1->ip4_over_ethernet[0].ip4.as_u32;
201 clib_memcpy_fast (&t->reply.mac, &arp1->ip4_over_ethernet[0].mac,
205 vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
206 n_left_to_next, bi0, bi1, next0,
210 while (n_left_from > 0 && n_left_to_next > 0)
214 ethernet_arp_header_t *arp0;
218 bi0 = to_next[0] = from[0];
224 next0 = ARPING_NEXT_DROP;
226 b0 = vlib_get_buffer (vm, bi0);
227 arp0 = vlib_buffer_get_current (b0);
229 vnet_feature_next (&next0, b0);
231 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
233 if (PREDICT_TRUE (arp0->opcode ==
234 clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply)))
236 aif0 = am->interfaces[sw_if_index0];
237 if (PREDICT_TRUE (aif0->address.ip.ip4.as_u32 ==
238 arp0->ip4_over_ethernet[0].ip4.as_u32))
240 aif0->recv.from4.ip4.as_u32 =
241 arp0->ip4_over_ethernet[0].ip4.as_u32;
242 clib_memcpy_fast (&aif0->recv.from4.mac,
243 &arp0->ip4_over_ethernet[0].mac, 6);
248 if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
250 arping_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
251 t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
252 t->arp_opcode = clib_host_to_net_u16 (arp0->opcode);
253 t->reply.ip4.as_u32 = arp0->ip4_over_ethernet[0].ip4.as_u32;
254 clib_memcpy_fast (&t->reply.mac, &arp0->ip4_over_ethernet[0].mac,
258 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
259 n_left_to_next, bi0, next0);
262 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
265 return frame->n_vectors;
268 VLIB_REGISTER_NODE (arping_input_node) =
270 .name = "arping-input",.vector_size = sizeof (u32),.format_trace =
271 format_arping_trace,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors =
272 ARPING_N_ERROR,.error_strings = arping_error_strings,.n_next_nodes =
273 ARPING_N_NEXT,.next_nodes =
275 [ARPING_NEXT_DROP] = "error-drop",[ARPING_NEXT_IO] = "interface-output",}
278 VNET_FEATURE_INIT (arping_feat_node, static) = {
280 .node_name = "arping-input",
281 .runs_before = VNET_FEATURES ("arp-reply"),
284 VLIB_NODE_FN (arping6_input_node)
285 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
287 u32 n_left_from, *from, *to_next, n_left_to_next;
288 arping_next_t next_index;
289 arping_main_t *am = &arping_main;
291 next_index = node->cached_next_index;
292 n_left_from = frame->n_vectors;
293 from = vlib_frame_vector_args (frame);
295 while (n_left_from > 0)
297 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
299 while (n_left_from >= 2 && n_left_to_next >= 2)
301 u32 next0, next1, bi0, bi1;
302 vlib_buffer_t *b0, *b1;
303 ip6_header_t *ip60, *ip61;
304 u32 sw_if_index0, sw_if_index1;
305 arping_intf_t *aif0, *aif1;
306 icmp6_neighbor_solicitation_or_advertisement_header_t *sol_adv0,
308 icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
312 bi0 = to_next[0] = from[0];
313 bi1 = to_next[1] = from[1];
320 next0 = next1 = ARPING6_NEXT_DROP;
322 b0 = vlib_get_buffer (vm, bi0);
323 b1 = vlib_get_buffer (vm, bi1);
325 ip60 = vlib_buffer_get_current (b0);
326 ip61 = vlib_buffer_get_current (b1);
328 vnet_feature_next (&next0, b0);
329 vnet_feature_next (&next1, b1);
331 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
332 sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
334 sol_adv0 = ip6_next_header (ip60);
336 (icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
339 if (PREDICT_TRUE (sol_adv0->icmp.type ==
340 ICMP6_neighbor_advertisement))
342 aif0 = am->interfaces[sw_if_index0];
343 if (PREDICT_TRUE (clib_memcmp (&aif0->address.ip.ip6,
344 &sol_adv0->target_address,
345 sizeof (aif0->address.ip.ip6)) ==
348 clib_memcpy_fast (&aif0->recv.from6.ip6,
349 &sol_adv0->target_address,
350 sizeof (aif0->recv.from6.ip6));
351 clib_memcpy_fast (&aif0->recv.from6.mac,
352 lladdr0->ethernet_address, 6);
357 sol_adv1 = ip6_next_header (ip61);
359 (icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
362 if (PREDICT_TRUE (sol_adv1->icmp.type ==
363 ICMP6_neighbor_advertisement))
365 aif1 = am->interfaces[sw_if_index1];
366 if (PREDICT_TRUE (clib_memcmp (&aif1->address.ip.ip6,
367 &sol_adv1->target_address,
368 sizeof (aif1->address.ip.ip6)) ==
371 clib_memcpy_fast (&aif1->recv.from6.ip6,
372 &sol_adv1->target_address,
373 sizeof (aif1->recv.from6.ip6));
374 clib_memcpy_fast (&aif1->recv.from6.mac,
375 lladdr1->ethernet_address, 6);
380 if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
382 arping6_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
383 t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
384 t->type = sol_adv0->icmp.type;
385 clib_memcpy_fast (&t->reply.ip6, &sol_adv0->target_address,
386 sizeof (t->reply.ip6));
387 clib_memcpy_fast (&t->reply.mac, lladdr0->ethernet_address, 6);
389 if (PREDICT_FALSE ((b1->flags & VLIB_BUFFER_IS_TRACED)))
391 arping6_trace_t *t = vlib_add_trace (vm, node, b1, sizeof (*t));
392 t->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];
393 t->type = sol_adv1->icmp.type;
394 clib_memcpy_fast (&t->reply.ip6, &sol_adv1->target_address,
395 sizeof (t->reply.ip6));
396 clib_memcpy_fast (&t->reply.mac, lladdr1->ethernet_address, 6);
399 vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
400 n_left_to_next, bi0, bi1, next0,
404 while (n_left_from > 0 && n_left_to_next > 0)
411 icmp6_neighbor_solicitation_or_advertisement_header_t *sol_adv0;
412 icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
415 bi0 = to_next[0] = from[0];
421 next0 = ARPING_NEXT_DROP;
423 b0 = vlib_get_buffer (vm, bi0);
424 ip60 = vlib_buffer_get_current (b0);
426 vnet_feature_next (&next0, b0);
428 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
430 sol_adv0 = ip6_next_header (ip60);
432 (icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
434 if (PREDICT_TRUE (sol_adv0->icmp.type ==
435 ICMP6_neighbor_advertisement))
437 aif0 = am->interfaces[sw_if_index0];
438 if (PREDICT_TRUE (clib_memcmp (&aif0->address.ip.ip6,
439 &sol_adv0->target_address,
440 sizeof (aif0->address.ip.ip6)) ==
443 clib_memcpy_fast (&aif0->recv.from6.ip6,
444 &sol_adv0->target_address,
445 sizeof (aif0->recv.from6.ip6));
446 clib_memcpy_fast (&aif0->recv.from6.mac,
447 lladdr0->ethernet_address, 6);
452 if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
454 arping6_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
455 t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
456 t->type = sol_adv0->icmp.type;
457 clib_memcpy_fast (&t->reply.ip6, &sol_adv0->target_address,
458 sizeof (t->reply.ip6));
459 clib_memcpy_fast (&t->reply.mac, lladdr0->ethernet_address, 6);
462 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
463 n_left_to_next, bi0, next0);
466 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
469 return frame->n_vectors;
472 VLIB_REGISTER_NODE (arping6_input_node) =
474 .name = "arping6-input",.vector_size = sizeof (u32),.format_trace =
475 format_arping6_trace,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors =
476 ARPING_N_ERROR,.error_strings = arping_error_strings,.n_next_nodes =
477 ARPING_N_NEXT,.next_nodes =
479 [ARPING6_NEXT_DROP] = "error-drop",[ARPING6_NEXT_IO] = "interface-output",}
482 VNET_FEATURE_INIT (arping6_feat_node, static) = {
483 .arc_name = "ip6-local",
484 .node_name = "arping6-input",
485 .runs_before = VNET_FEATURES ("ip6-local-end-of-arc"),
488 static clib_error_t *
489 arping_neighbor_advertisement (vlib_main_t *vm, arping_args_t *args)
491 vnet_main_t *vnm = vnet_get_main ();
494 while (args->repeat > 0)
497 if (args->address.version == AF_IP4)
499 if (args->silence == 0)
500 vlib_cli_output (vm, "Sending %u GARP to %U", send_count,
501 format_ip4_address, &args->address.ip.ip4);
502 ip4_neighbor_advertise (vm, vnm, args->sw_if_index,
503 vlib_get_thread_index (),
504 &args->address.ip.ip4);
508 if (args->silence == 0)
509 vlib_cli_output (vm, "Sending %u Neighbor Advertisement to %U",
510 send_count, format_ip6_address,
511 &args->address.ip.ip6);
512 ip6_neighbor_advertise (vm, vnm, args->sw_if_index,
513 vlib_get_thread_index (),
514 &args->address.ip.ip6);
517 if ((args->interval > 0.0) && (args->repeat > 0))
518 vlib_process_suspend (vm, args->interval);
525 arping_vnet_feature_enable_disable (vlib_main_t *vm, const char *arc_name,
526 const char *node_name, u32 sw_if_index,
527 int enable_disable, void *feature_config,
528 u32 n_feature_config_bytes)
530 vlib_worker_thread_barrier_sync (vm);
531 vnet_feature_enable_disable (arc_name, node_name, sw_if_index,
532 enable_disable, feature_config,
533 n_feature_config_bytes);
534 vlib_worker_thread_barrier_release (vm);
538 arping_vec_validate (vlib_main_t *vm, u32 sw_if_index)
540 arping_main_t *am = &arping_main;
542 if (sw_if_index >= vec_len (am->interfaces))
544 vlib_worker_thread_barrier_sync (vm);
545 vec_validate (am->interfaces, sw_if_index);
546 vlib_worker_thread_barrier_release (vm);
550 static clib_error_t *
551 arping_neighbor_probe_dst (vlib_main_t *vm, arping_args_t *args)
553 arping_main_t *am = &arping_main;
558 /* Disallow multiple sends on the same interface for now. Who needs it? */
559 if ((vec_len (am->interfaces) > args->sw_if_index) &&
560 (am->interfaces[args->sw_if_index] != 0))
562 error = clib_error_return (
563 0, "arping command is in progress for the same interface. "
564 "Please try again later.");
565 args->rv = VNET_API_ERROR_INVALID_VALUE;
569 arping_vec_validate (vm, args->sw_if_index);
570 clib_memset (&aif, 0, sizeof (aif));
571 aif.interval = args->interval;
572 aif.repeat = args->repeat;
574 am->interfaces[args->sw_if_index] = &aif;
576 clib_memcpy (&aif.address, &args->address, sizeof (aif.address));
577 if (args->address.version == AF_IP4)
578 arping_vnet_feature_enable_disable (vm, "arp", "arping-input",
579 args->sw_if_index, 1, 0, 0);
581 arping_vnet_feature_enable_disable (vm, "ip6-local", "arping6-input",
582 args->sw_if_index, 1, 0, 0);
584 while (args->repeat > 0)
587 if (args->address.version == AF_IP4)
589 if (args->silence == 0)
590 vlib_cli_output (vm, "Sending %u ARP Request to %U", send_count,
591 format_ip4_address, &args->address.ip.ip4);
592 ip4_neighbor_probe_dst (args->sw_if_index, vlib_get_thread_index (),
593 &args->address.ip.ip4);
597 if (args->silence == 0)
598 vlib_cli_output (vm, "Sending %u Neighbor Solicitation to %U",
599 send_count, format_ip6_address,
600 &args->address.ip.ip6);
601 ip6_neighbor_probe_dst (args->sw_if_index, vlib_get_thread_index (),
602 &args->address.ip.ip6);
605 if ((args->interval > 0.0) && (args->repeat > 0))
606 vlib_process_suspend (vm, args->interval);
609 /* wait for a second on the reply */
611 while ((aif.reply_count < send_count) && (wait_count < 10))
613 vlib_process_suspend (vm, 0.1);
617 if (args->address.version == AF_IP4)
619 clib_memcpy (&args->recv.from4, &aif.recv.from4,
620 sizeof (args->recv.from4));
621 arping_vnet_feature_enable_disable (vm, "arp", "arping-input",
622 args->sw_if_index, 0, 0, 0);
626 clib_memcpy (&args->recv.from6, &aif.recv.from6,
627 sizeof (args->recv.from6));
628 arping_vnet_feature_enable_disable (vm, "ip6-local", "arping6-input",
629 args->sw_if_index, 0, 0, 0);
631 args->reply_count = aif.reply_count;
633 am->interfaces[args->sw_if_index] = 0;
639 arping_run_command (vlib_main_t *vm, arping_args_t *args)
642 args->error = arping_neighbor_advertisement (vm, args);
644 args->error = arping_neighbor_probe_dst (vm, args);
647 static clib_error_t *
648 arping_ip_address (vlib_main_t *vm, unformat_input_t *input,
649 vlib_cli_command_t *cmd)
651 clib_error_t *error = 0;
652 vnet_main_t *vnm = vnet_get_main ();
653 arping_args_t args = { 0 };
654 f64 interval = ARPING_DEFAULT_INTERVAL;
656 args.repeat = ARPING_DEFAULT_REPEAT;
657 args.interval = ARPING_DEFAULT_INTERVAL;
658 args.sw_if_index = ~0;
661 if (unformat (input, "gratuitous"))
664 if (unformat (input, "%U", unformat_ip4_address, &args.address.ip.ip4))
665 args.address.version = AF_IP4;
666 else if (unformat (input, "%U", unformat_ip6_address, &args.address.ip.ip6))
667 args.address.version = AF_IP6;
670 error = clib_error_return (
672 "expecting IP4/IP6 address `%U'. Usage: arping [gratuitous] <addr> "
673 "<intf> [repeat <count>] [interval <secs>]",
674 format_unformat_error, input);
678 if (!unformat_user (input, unformat_vnet_sw_interface, vnm,
681 error = clib_error_return (0, "unknown interface `%U'",
682 format_unformat_error, input);
686 /* parse the rest of the parameters in a cycle */
687 while (!unformat_eof (input, NULL))
689 if (unformat (input, "interval"))
691 if (!unformat (input, "%f", &interval))
693 error = clib_error_return (
694 0, "expecting interval (floating point number) got `%U'",
695 format_unformat_error, input);
698 args.interval = interval;
700 else if (unformat (input, "repeat"))
702 if (!unformat (input, "%u", &args.repeat))
705 clib_error_return (0, "expecting repeat count but got `%U'",
706 format_unformat_error, input);
712 error = clib_error_return (0, "unknown input `%U'",
713 format_unformat_error, input);
718 arping_run_command (vm, &args);
720 if (args.reply_count)
722 if (args.address.version == AF_IP4)
723 vlib_cli_output (vm, "Received %u ARP Replies from %U (%U)",
724 args.reply_count, format_mac_address,
725 &args.recv.from4.mac, format_ip4_address,
726 &args.recv.from4.ip4);
729 vm, "Received %u ICMP6 neighbor advertisements from %U (%U)",
730 args.reply_count, format_mac_address, &args.recv.from6.mac,
731 format_ip6_address, &args.recv.from6.ip6);
733 else if (args.is_garp == 0)
734 vlib_cli_output (vm, "Received 0 Reply");
742 * This command sends an ARP REQUEST or gratuitous ARP to network hosts. The
743 * address can be an IPv4 or IPv6 address.
747 * Example of how to send an IPv4 ARP REQUEST
748 * @cliexstart{arping 100.1.1.10 VirtualEthernet0/0/0 repeat 3 interval 1}
749 * Sending 1 ARP Request to 100.1.1.10
750 * Sending 2 ARP Request to 100.1.1.10
751 * Sending 3 ARP Request to 100.1.1.10
752 * Received 3 ARP Replies from 52:53:00:00:04:01 (100.1.1.10)
755 * Example of how to send an IPv6 Neighbor Solicitation
756 * @cliexstart{arping 2001:192::2 VirtualEthernet0/0/0 repeat 3 interval 1}
757 * Sending 1 Neighbor Solicitation to 2001:192::2
758 * Sending 2 Neighbor Solicitation to 2001:192::2
759 * Sending 3 Neighbor Solicitation to 2001:192::2
760 * Received 3 ICMP6 neighbor advertisements from 52:53:00:00:04:01 (2001:192::2)
763 * Example of how to send an IPv4 gratuitous ARP
764 * @cliexstart{arping gratuitous 100.1.1.100 VirtualEthernet0/0/0 repeat 2}
765 * Sending 1 GARP to 100.1.1.100
766 * Sending 2 GARP to 100.1.1.100
772 VLIB_CLI_COMMAND (arping_command, static) = {
774 .function = arping_ip_address,
775 .short_help = "arping [gratuitous] {addr} {interface}"
776 " [interval {sec}] [repeat {cnt}]",
780 static clib_error_t *
781 arping_cli_init (vlib_main_t *vm)
783 /* initialize binary API */
784 arping_plugin_api_hookup (vm);
789 VLIB_INIT_FUNCTION (arping_cli_init);
791 VLIB_PLUGIN_REGISTER () = {
792 .version = VPP_BUILD_VER,
793 .description = "Arping (arping)",
797 * fd.io coding-style-patch-verification: ON
800 * eval: (c-set-style "gnu")