ip-neighbor: ARP and ND stats per-interface.
[vpp.git] / src / plugins / arping / arping.c
1 /*
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:
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 <stddef.h>
17
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>
25
26 arping_main_t arping_main;
27
28 #define foreach_arping_error _ (NONE, "no error")
29
30 typedef enum
31 {
32 #define _(f, s) ARPING_ERROR_##f,
33   foreach_arping_error
34 #undef _
35     ARPING_N_ERROR,
36 } arping__error_t;
37
38 static char *arping_error_strings[] = {
39 #define _(n, s) s,
40   foreach_arping_error
41 #undef _
42 };
43
44 #define foreach_arping                                                        \
45   _ (DROP, "error-drop")                                                      \
46   _ (IO, "interface-output")
47
48 typedef enum
49 {
50 #define _(sym, str) ARPING_NEXT_##sym,
51   foreach_arping
52 #undef _
53     ARPING_N_NEXT,
54 } arping_next_t;
55
56 typedef struct arping_trace_t_
57 {
58   u32 sw_if_index;
59   u16 arp_opcode;
60   ethernet_arp_ip4_over_ethernet_address_t reply;
61 } arping_trace_t;
62
63 typedef enum
64 {
65 #define _(sym, str) ARPING6_NEXT_##sym,
66   foreach_arping
67 #undef _
68     ARPING6_N_NEXT,
69 } arping6_next_t;
70
71 typedef CLIB_PACKED (struct {
72   mac_address_t mac;
73   ip6_address_t ip6;
74 }) ethernet_arp_ip6_over_ethernet_address_t;
75
76 typedef struct arping6_trace_t_
77 {
78   u32 sw_if_index;
79   u8 type;
80   ethernet_arp_ip6_over_ethernet_address_t reply;
81 } arping6_trace_t;
82
83 /* packet trace format function */
84 static u8 *
85 format_arping_trace (u8 *s, va_list *args)
86 {
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 *);
90
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);
94
95   return s;
96 }
97
98 static u8 *
99 format_arping6_trace (u8 *s, va_list *args)
100 {
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 *);
104
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,
107               &t->reply.ip6);
108
109   return s;
110 }
111
112 VLIB_NODE_FN (arping_input_node)
113 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
114 {
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;
118
119   next_index = node->cached_next_index;
120   n_left_from = frame->n_vectors;
121   from = vlib_frame_vector_args (frame);
122
123   while (n_left_from > 0)
124     {
125       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
126
127       while (n_left_from >= 2 && n_left_to_next >= 2)
128         {
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;
134
135           bi0 = to_next[0] = from[0];
136           bi1 = to_next[1] = from[1];
137
138           from += 2;
139           n_left_from -= 2;
140           to_next += 2;
141           n_left_to_next -= 2;
142
143           next0 = next1 = ARPING_NEXT_DROP;
144
145           b0 = vlib_get_buffer (vm, bi0);
146           b1 = vlib_get_buffer (vm, bi1);
147
148           arp0 = vlib_buffer_get_current (b0);
149           arp1 = vlib_buffer_get_current (b1);
150
151           vnet_feature_next (&next0, b0);
152           vnet_feature_next (&next1, b1);
153
154           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
155           sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
156
157           if (PREDICT_TRUE (arp0->opcode ==
158                             clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply)))
159             {
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))
163                 {
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);
168                   aif0->reply_count++;
169                 }
170             }
171           if (PREDICT_TRUE (arp1->opcode ==
172                             clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply)))
173             {
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))
177                 {
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);
182                   aif1->reply_count++;
183                 }
184             }
185
186           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
187             {
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,
193                                 6);
194             }
195           if (PREDICT_FALSE ((b1->flags & VLIB_BUFFER_IS_TRACED)))
196             {
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,
202                                 6);
203             }
204
205           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
206                                            n_left_to_next, bi0, bi1, next0,
207                                            next1);
208         }
209
210       while (n_left_from > 0 && n_left_to_next > 0)
211         {
212           u32 next0, bi0;
213           vlib_buffer_t *b0;
214           ethernet_arp_header_t *arp0;
215           arping_intf_t *aif0;
216           u32 sw_if_index0;
217
218           bi0 = to_next[0] = from[0];
219
220           from += 1;
221           n_left_from -= 1;
222           to_next += 1;
223           n_left_to_next -= 1;
224           next0 = ARPING_NEXT_DROP;
225
226           b0 = vlib_get_buffer (vm, bi0);
227           arp0 = vlib_buffer_get_current (b0);
228
229           vnet_feature_next (&next0, b0);
230
231           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
232
233           if (PREDICT_TRUE (arp0->opcode ==
234                             clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply)))
235             {
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))
239                 {
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);
244                   aif0->reply_count++;
245                 }
246             }
247
248           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
249             {
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,
255                                 6);
256             }
257
258           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
259                                            n_left_to_next, bi0, next0);
260         }
261
262       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
263     }
264
265   return frame->n_vectors;
266 }
267
268 VLIB_REGISTER_NODE (arping_input_node) =
269 {
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 =
274   {
275   [ARPING_NEXT_DROP] = "error-drop",[ARPING_NEXT_IO] = "interface-output",}
276 ,};
277
278 VNET_FEATURE_INIT (arping_feat_node, static) = {
279   .arc_name = "arp",
280   .node_name = "arping-input",
281   .runs_before = VNET_FEATURES ("arp-reply"),
282 };
283
284 VLIB_NODE_FN (arping6_input_node)
285 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
286 {
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;
290
291   next_index = node->cached_next_index;
292   n_left_from = frame->n_vectors;
293   from = vlib_frame_vector_args (frame);
294
295   while (n_left_from > 0)
296     {
297       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
298
299       while (n_left_from >= 2 && n_left_to_next >= 2)
300         {
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,
307             *sol_adv1;
308           icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
309             *lladdr0,
310             *lladdr1;
311
312           bi0 = to_next[0] = from[0];
313           bi1 = to_next[1] = from[1];
314
315           from += 2;
316           n_left_from -= 2;
317           to_next += 2;
318           n_left_to_next -= 2;
319
320           next0 = next1 = ARPING6_NEXT_DROP;
321
322           b0 = vlib_get_buffer (vm, bi0);
323           b1 = vlib_get_buffer (vm, bi1);
324
325           ip60 = vlib_buffer_get_current (b0);
326           ip61 = vlib_buffer_get_current (b1);
327
328           vnet_feature_next (&next0, b0);
329           vnet_feature_next (&next1, b1);
330
331           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
332           sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
333
334           sol_adv0 = ip6_next_header (ip60);
335           lladdr0 =
336             (icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
337                *) (sol_adv0 + 1);
338
339           if (PREDICT_TRUE (sol_adv0->icmp.type ==
340                             ICMP6_neighbor_advertisement))
341             {
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)) ==
346                                 0))
347                 {
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);
353                   aif0->reply_count++;
354                 }
355             }
356
357           sol_adv1 = ip6_next_header (ip61);
358           lladdr1 =
359             (icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
360                *) (sol_adv1 + 1);
361
362           if (PREDICT_TRUE (sol_adv1->icmp.type ==
363                             ICMP6_neighbor_advertisement))
364             {
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)) ==
369                                 0))
370                 {
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);
376                   aif1->reply_count++;
377                 }
378             }
379
380           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
381             {
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);
388             }
389           if (PREDICT_FALSE ((b1->flags & VLIB_BUFFER_IS_TRACED)))
390             {
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);
397             }
398
399           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
400                                            n_left_to_next, bi0, bi1, next0,
401                                            next1);
402         }
403
404       while (n_left_from > 0 && n_left_to_next > 0)
405         {
406           u32 next0, bi0;
407           vlib_buffer_t *b0;
408           arping_intf_t *aif0;
409           u32 sw_if_index0;
410           ip6_header_t *ip60;
411           icmp6_neighbor_solicitation_or_advertisement_header_t *sol_adv0;
412           icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
413             *lladdr0;
414
415           bi0 = to_next[0] = from[0];
416
417           from += 1;
418           n_left_from -= 1;
419           to_next += 1;
420           n_left_to_next -= 1;
421           next0 = ARPING_NEXT_DROP;
422
423           b0 = vlib_get_buffer (vm, bi0);
424           ip60 = vlib_buffer_get_current (b0);
425
426           vnet_feature_next (&next0, b0);
427
428           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
429
430           sol_adv0 = ip6_next_header (ip60);
431           lladdr0 =
432             (icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
433                *) (sol_adv0 + 1);
434           if (PREDICT_TRUE (sol_adv0->icmp.type ==
435                             ICMP6_neighbor_advertisement))
436             {
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)) ==
441                                 0))
442                 {
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);
448                   aif0->reply_count++;
449                 }
450             }
451
452           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
453             {
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);
460             }
461
462           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
463                                            n_left_to_next, bi0, next0);
464         }
465
466       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
467     }
468
469   return frame->n_vectors;
470 }
471
472 VLIB_REGISTER_NODE (arping6_input_node) =
473 {
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 =
478   {
479   [ARPING6_NEXT_DROP] = "error-drop",[ARPING6_NEXT_IO] = "interface-output",}
480 ,};
481
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"),
486 };
487
488 static clib_error_t *
489 arping_neighbor_advertisement (vlib_main_t *vm, arping_args_t *args)
490 {
491   vnet_main_t *vnm = vnet_get_main ();
492   u32 send_count = 0;
493
494   while (args->repeat > 0)
495     {
496       send_count++;
497       if (args->address.version == AF_IP4)
498         {
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);
505         }
506       else
507         {
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);
515         }
516       args->repeat--;
517       if ((args->interval > 0.0) && (args->repeat > 0))
518         vlib_process_suspend (vm, args->interval);
519     }
520
521   return 0;
522 }
523
524 static void
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)
529 {
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);
535 }
536
537 static void
538 arping_vec_validate (vlib_main_t *vm, u32 sw_if_index)
539 {
540   arping_main_t *am = &arping_main;
541
542   if (sw_if_index >= vec_len (am->interfaces))
543     {
544       vlib_worker_thread_barrier_sync (vm);
545       vec_validate (am->interfaces, sw_if_index);
546       vlib_worker_thread_barrier_release (vm);
547     }
548 }
549
550 static clib_error_t *
551 arping_neighbor_probe_dst (vlib_main_t *vm, arping_args_t *args)
552 {
553   arping_main_t *am = &arping_main;
554   u32 send_count = 0;
555   clib_error_t *error;
556   arping_intf_t aif;
557
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))
561     {
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;
566       return error;
567     }
568
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;
573   aif.reply_count = 0;
574   am->interfaces[args->sw_if_index] = &aif;
575
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);
580   else
581     arping_vnet_feature_enable_disable (vm, "ip6-local", "arping6-input",
582                                         args->sw_if_index, 1, 0, 0);
583
584   while (args->repeat > 0)
585     {
586       send_count++;
587       if (args->address.version == AF_IP4)
588         {
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);
594         }
595       else
596         {
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);
603         }
604       args->repeat--;
605       if ((args->interval > 0.0) && (args->repeat > 0))
606         vlib_process_suspend (vm, args->interval);
607     }
608
609   /* wait for a second on the reply */
610   u32 wait_count = 0;
611   while ((aif.reply_count < send_count) && (wait_count < 10))
612     {
613       vlib_process_suspend (vm, 0.1);
614       wait_count++;
615     }
616
617   if (args->address.version == AF_IP4)
618     {
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);
623     }
624   else
625     {
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);
630     }
631   args->reply_count = aif.reply_count;
632
633   am->interfaces[args->sw_if_index] = 0;
634
635   return 0;
636 }
637
638 void
639 arping_run_command (vlib_main_t *vm, arping_args_t *args)
640 {
641   if (args->is_garp)
642     args->error = arping_neighbor_advertisement (vm, args);
643   else
644     args->error = arping_neighbor_probe_dst (vm, args);
645 }
646
647 static clib_error_t *
648 arping_ip_address (vlib_main_t *vm, unformat_input_t *input,
649                    vlib_cli_command_t *cmd)
650 {
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;
655
656   args.repeat = ARPING_DEFAULT_REPEAT;
657   args.interval = ARPING_DEFAULT_INTERVAL;
658   args.sw_if_index = ~0;
659   args.silence = 0;
660
661   if (unformat (input, "gratuitous"))
662     args.is_garp = 1;
663
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;
668   else
669     {
670       error = clib_error_return (
671         0,
672         "expecting IP4/IP6 address `%U'. Usage: arping [gratuitous] <addr> "
673         "<intf> [repeat <count>] [interval <secs>]",
674         format_unformat_error, input);
675       goto done;
676     }
677
678   if (!unformat_user (input, unformat_vnet_sw_interface, vnm,
679                       &args.sw_if_index))
680     {
681       error = clib_error_return (0, "unknown interface `%U'",
682                                  format_unformat_error, input);
683       goto done;
684     }
685
686   /* parse the rest of the parameters  in a cycle */
687   while (!unformat_eof (input, NULL))
688     {
689       if (unformat (input, "interval"))
690         {
691           if (!unformat (input, "%f", &interval))
692             {
693               error = clib_error_return (
694                 0, "expecting interval (floating point number) got `%U'",
695                 format_unformat_error, input);
696               goto done;
697             }
698           args.interval = interval;
699         }
700       else if (unformat (input, "repeat"))
701         {
702           if (!unformat (input, "%u", &args.repeat))
703             {
704               error =
705                 clib_error_return (0, "expecting repeat count but got `%U'",
706                                    format_unformat_error, input);
707               goto done;
708             }
709         }
710       else
711         {
712           error = clib_error_return (0, "unknown input `%U'",
713                                      format_unformat_error, input);
714           goto done;
715         }
716     }
717
718   arping_run_command (vm, &args);
719
720   if (args.reply_count)
721     {
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);
727       else
728         vlib_cli_output (
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);
732     }
733   else if (args.is_garp == 0)
734     vlib_cli_output (vm, "Received 0 Reply");
735
736   error = args.error;
737 done:
738   return error;
739 }
740 // clang-format off
741 /*?
742  * This command sends an ARP REQUEST or gratuitous ARP to network hosts. The
743  * address can be an IPv4 or IPv6 address.
744  *
745  * @cliexpar
746  * @parblock
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)
753  * @cliexend
754  *
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)
761  * @cliexend
762  *
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
767  * @cliexend
768  * @endparblock
769  *
770 ?*/
771 // clang-format on
772 VLIB_CLI_COMMAND (arping_command, static) = {
773   .path = "arping",
774   .function = arping_ip_address,
775   .short_help = "arping [gratuitous] {addr} {interface}"
776                 " [interval {sec}] [repeat {cnt}]",
777   .is_mp_safe = 1,
778 };
779
780 static clib_error_t *
781 arping_cli_init (vlib_main_t *vm)
782 {
783   /* initialize binary API */
784   arping_plugin_api_hookup (vm);
785
786   return 0;
787 }
788
789 VLIB_INIT_FUNCTION (arping_cli_init);
790
791 VLIB_PLUGIN_REGISTER () = {
792   .version = VPP_BUILD_VER,
793   .description = "Arping (arping)",
794 };
795
796 /*
797  * fd.io coding-style-patch-verification: ON
798  *
799  * Local Variables:
800  * eval: (c-set-style "gnu")
801  * End:
802  */