arping: add arping command
[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                                     &arp0->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                                   &args->address.ip.ip4);
504         }
505       else
506         {
507           if (args->silence == 0)
508             vlib_cli_output (vm, "Sending %u Neighbor Advertisement to %U",
509                              send_count, format_ip6_address,
510                              &args->address.ip.ip6);
511           ip6_neighbor_advertise (vm, vnm, args->sw_if_index,
512                                   &args->address.ip.ip6);
513         }
514       args->repeat--;
515       if ((args->interval > 0.0) && (args->repeat > 0))
516         vlib_process_suspend (vm, args->interval);
517     }
518
519   return 0;
520 }
521
522 static void
523 arping_vnet_feature_enable_disable (vlib_main_t *vm, const char *arc_name,
524                                     const char *node_name, u32 sw_if_index,
525                                     int enable_disable, void *feature_config,
526                                     u32 n_feature_config_bytes)
527 {
528   vlib_worker_thread_barrier_sync (vm);
529   vnet_feature_enable_disable (arc_name, node_name, sw_if_index,
530                                enable_disable, feature_config,
531                                n_feature_config_bytes);
532   vlib_worker_thread_barrier_release (vm);
533 }
534
535 static void
536 arping_vec_validate (vlib_main_t *vm, u32 sw_if_index)
537 {
538   arping_main_t *am = &arping_main;
539
540   if (sw_if_index >= vec_len (am->interfaces))
541     {
542       vlib_worker_thread_barrier_sync (vm);
543       vec_validate (am->interfaces, sw_if_index);
544       vlib_worker_thread_barrier_release (vm);
545     }
546 }
547
548 static clib_error_t *
549 arping_neighbor_probe_dst (vlib_main_t *vm, arping_args_t *args)
550 {
551   arping_main_t *am = &arping_main;
552   u32 send_count = 0;
553   clib_error_t *error;
554   arping_intf_t aif;
555
556   /* Disallow multiple sends on the same interface for now. Who needs it? */
557   if (am->interfaces && (am->interfaces[args->sw_if_index] != 0))
558     {
559       error = clib_error_return (
560         0, "arping command is in progress for the same interface. "
561            "Please try again later.");
562       args->rv = VNET_API_ERROR_INVALID_VALUE;
563       return error;
564     }
565
566   arping_vec_validate (vm, args->sw_if_index);
567   clib_memset (&aif, 0, sizeof (aif));
568   aif.interval = args->interval;
569   aif.repeat = args->repeat;
570   aif.reply_count = 0;
571   am->interfaces[args->sw_if_index] = &aif;
572
573   clib_memcpy (&aif.address, &args->address, sizeof (aif.address));
574   if (args->address.version == AF_IP4)
575     arping_vnet_feature_enable_disable (vm, "arp", "arping-input",
576                                         args->sw_if_index, 1, 0, 0);
577   else
578     arping_vnet_feature_enable_disable (vm, "ip6-local", "arping6-input",
579                                         args->sw_if_index, 1, 0, 0);
580
581   while (args->repeat > 0)
582     {
583       send_count++;
584       if (args->address.version == AF_IP4)
585         {
586           if (args->silence == 0)
587             vlib_cli_output (vm, "Sending %u ARP Request to %U", send_count,
588                              format_ip4_address, &args->address.ip.ip4);
589           ip4_neighbor_probe_dst (args->sw_if_index, &args->address.ip.ip4);
590         }
591       else
592         {
593           if (args->silence == 0)
594             vlib_cli_output (vm, "Sending %u Neighbor Solicitation  to %U",
595                              send_count, format_ip6_address,
596                              &args->address.ip.ip6);
597           ip6_neighbor_probe_dst (args->sw_if_index, &args->address.ip.ip6);
598         }
599       args->repeat--;
600       if ((args->interval > 0.0) && (args->repeat > 0))
601         vlib_process_suspend (vm, args->interval);
602     }
603
604   /* wait for a second on the reply */
605   u32 wait_count = 0;
606   while ((aif.reply_count < send_count) && (wait_count < 10))
607     {
608       vlib_process_suspend (vm, 0.1);
609       wait_count++;
610     }
611
612   if (args->address.version == AF_IP4)
613     {
614       clib_memcpy (&args->recv.from4, &aif.recv.from4,
615                    sizeof (args->recv.from4));
616       arping_vnet_feature_enable_disable (vm, "arp", "arping-input",
617                                           args->sw_if_index, 0, 0, 0);
618     }
619   else
620     {
621       clib_memcpy (&args->recv.from6, &aif.recv.from6,
622                    sizeof (args->recv.from6));
623       arping_vnet_feature_enable_disable (vm, "ip6-local", "arping6-input",
624                                           args->sw_if_index, 0, 0, 0);
625     }
626   args->reply_count = aif.reply_count;
627
628   am->interfaces[args->sw_if_index] = 0;
629
630   return 0;
631 }
632
633 void
634 arping_run_command (vlib_main_t *vm, arping_args_t *args)
635 {
636   if (args->is_garp)
637     args->error = arping_neighbor_advertisement (vm, args);
638   else
639     args->error = arping_neighbor_probe_dst (vm, args);
640 }
641
642 static clib_error_t *
643 arping_ip_address (vlib_main_t *vm, unformat_input_t *input,
644                    vlib_cli_command_t *cmd)
645 {
646   clib_error_t *error = 0;
647   vnet_main_t *vnm = vnet_get_main ();
648   arping_args_t args = { 0 };
649   f64 interval = ARPING_DEFAULT_INTERVAL;
650
651   args.repeat = ARPING_DEFAULT_REPEAT;
652   args.interval = ARPING_DEFAULT_INTERVAL;
653   args.sw_if_index = ~0;
654   args.silence = 0;
655
656   if (unformat (input, "gratuitous"))
657     args.is_garp = 1;
658
659   if (unformat (input, "%U", unformat_ip4_address, &args.address.ip.ip4))
660     args.address.version = AF_IP4;
661   else if (unformat (input, "%U", unformat_ip6_address, &args.address.ip.ip6))
662     args.address.version = AF_IP6;
663   else
664     {
665       error = clib_error_return (
666         0,
667         "expecting IP4/IP6 address `%U'. Usage: arping [gratuitous] <addr> "
668         "<intf> [repeat <count>] [interval <secs>]",
669         format_unformat_error, input);
670       goto done;
671     }
672
673   if (!unformat_user (input, unformat_vnet_sw_interface, vnm,
674                       &args.sw_if_index))
675     {
676       error = clib_error_return (0, "unknown interface `%U'",
677                                  format_unformat_error, input);
678       goto done;
679     }
680
681   /* parse the rest of the parameters  in a cycle */
682   while (!unformat_eof (input, NULL))
683     {
684       if (unformat (input, "interval"))
685         {
686           if (!unformat (input, "%f", &interval))
687             {
688               error = clib_error_return (
689                 0, "expecting interval (floating point number) got `%U'",
690                 format_unformat_error, input);
691               goto done;
692             }
693           args.interval = interval;
694         }
695       else if (unformat (input, "repeat"))
696         {
697           if (!unformat (input, "%u", &args.repeat))
698             {
699               error =
700                 clib_error_return (0, "expecting repeat count but got `%U'",
701                                    format_unformat_error, input);
702               goto done;
703             }
704         }
705       else
706         {
707           error = clib_error_return (0, "unknown input `%U'",
708                                      format_unformat_error, input);
709           goto done;
710         }
711     }
712
713   arping_run_command (vm, &args);
714
715   if (args.reply_count)
716     {
717       if (args.address.version == AF_IP4)
718         vlib_cli_output (vm, "Received %u ARP Replies from %U (%U)",
719                          args.reply_count, format_mac_address,
720                          &args.recv.from4.mac, format_ip4_address,
721                          &args.recv.from4.ip4);
722       else
723         vlib_cli_output (
724           vm, "Received %u ICMP6 neighbor advertisements from %U (%U)",
725           args.reply_count, format_mac_address, &args.recv.from6.mac,
726           format_ip6_address, &args.recv.from6.ip6);
727     }
728   else if (args.is_garp == 0)
729     vlib_cli_output (vm, "Received 0 Reply");
730
731   error = args.error;
732 done:
733   return error;
734 }
735 // clang-format off
736 /*?
737  * This command sends an ARP REQUEST or gratuitous ARP to network hosts. The
738  * address can be an IPv4 or IPv6 address.
739  *
740  * @cliexpar
741  * @parblock
742  * Example of how to send an IPv4 ARP REQUEST
743  * @cliexstart{arping 100.1.1.10 VirtualEthernet0/0/0 repeat 3 interval 1}
744  * Sending 1 ARP Request to 100.1.1.10
745  * Sending 2 ARP Request to 100.1.1.10
746  * Sending 3 ARP Request to 100.1.1.10
747  * Received 3 ARP Replies from 52:53:00:00:04:01 (100.1.1.10)
748  * @cliexend
749  *
750  * Example of how to send an IPv6 Neighbor Solicitation
751  * @cliexstart{arping 2001:192::2 VirtualEthernet0/0/0 repeat 3 interval 1}
752  * Sending 1 Neighbor Solicitation to 2001:192::2
753  * Sending 2 Neighbor Solicitation to 2001:192::2
754  * Sending 3 Neighbor Solicitation to 2001:192::2
755  * Received 3 ICMP6 neighbor advertisements from 52:53:00:00:04:01 (2001:192::2)
756  * @cliexend
757  *
758  * Example of how to send an IPv4 gratuitous ARP
759  * @cliexstart{arping gratuitous 100.1.1.100 VirtualEthernet0/0/0 repeat 2}
760  * Sending 1 GARP to 100.1.1.100
761  * Sending 2 GARP to 100.1.1.100
762  * @cliexend
763  * @endparblock
764  *
765 ?*/
766 // clang-format on
767 VLIB_CLI_COMMAND (arping_command, static) = {
768   .path = "arping",
769   .function = arping_ip_address,
770   .short_help = "arping [gratuitous] {addr} {interface}"
771                 " [interval {sec}] [repeat {cnt}]",
772   .is_mp_safe = 1,
773 };
774
775 static clib_error_t *
776 arping_cli_init (vlib_main_t *vm)
777 {
778   /* initialize binary API */
779   arping_plugin_api_hookup (vm);
780
781   return 0;
782 }
783
784 VLIB_INIT_FUNCTION (arping_cli_init);
785
786 VLIB_PLUGIN_REGISTER () = {
787   .version = VPP_BUILD_VER,
788   .description = "Arping (arping)",
789 };
790
791 /*
792  * fd.io coding-style-patch-verification: ON
793  *
794  * Local Variables:
795  * eval: (c-set-style "gnu")
796  * End:
797  */