Propagate duplicate IF addr add/del error up to API.
[vpp.git] / src / vnet / ip / lookup.c
1 /*
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:
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  * ip/ip_lookup.c: ip4/6 adjacency and lookup table managment
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 #include <vnet/ip/ip.h>
41 #include <vnet/adj/adj.h>
42 #include <vnet/fib/fib_table.h>
43 #include <vnet/fib/ip4_fib.h>
44 #include <vnet/fib/ip6_fib.h>
45 #include <vnet/mpls/mpls.h>
46 #include <vnet/mfib/mfib_table.h>
47 #include <vnet/dpo/drop_dpo.h>
48 #include <vnet/dpo/classify_dpo.h>
49 #include <vnet/dpo/punt_dpo.h>
50 #include <vnet/dpo/receive_dpo.h>
51 #include <vnet/dpo/ip_null_dpo.h>
52 #include <vnet/ip/ip6_neighbor.h>
53
54 /**
55  * @file
56  * @brief IPv4 and IPv6 adjacency and lookup table managment.
57  *
58  */
59
60 clib_error_t *
61 ip_interface_address_add_del (ip_lookup_main_t * lm,
62                               u32 sw_if_index,
63                               void *addr_fib,
64                               u32 address_length,
65                               u32 is_del, u32 * result_if_address_index)
66 {
67   vnet_main_t *vnm = vnet_get_main ();
68   ip_interface_address_t *a, *prev, *next;
69   uword *p = mhash_get (&lm->address_to_if_address_index, addr_fib);
70
71   vec_validate_init_empty (lm->if_address_pool_index_by_sw_if_index,
72                            sw_if_index, ~0);
73   a = p ? pool_elt_at_index (lm->if_address_pool, p[0]) : 0;
74
75   /* Verify given length. */
76   if ((a && (address_length != a->address_length)) ||
77       (address_length == 0) ||
78       (lm->is_ip6 && address_length > 128) ||
79       (!lm->is_ip6 && address_length > 32))
80     {
81       vnm->api_errno = VNET_API_ERROR_ADDRESS_LENGTH_MISMATCH;
82       return clib_error_create
83         ("%U wrong length (expected %d) for interface %U",
84          lm->format_address_and_length, addr_fib,
85          address_length, a ? a->address_length : -1,
86          format_vnet_sw_if_index_name, vnm, sw_if_index);
87     }
88
89   if (is_del)
90     {
91       if (!a)
92         {
93           vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
94           vnm->api_errno = VNET_API_ERROR_ADDRESS_NOT_FOUND_FOR_INTERFACE;
95           return clib_error_create ("%U not found for interface %U",
96                                     lm->format_address_and_length,
97                                     addr_fib, address_length,
98                                     format_vnet_sw_interface_name, vnm, si);
99         }
100
101       if (a->prev_this_sw_interface != ~0)
102         {
103           prev =
104             pool_elt_at_index (lm->if_address_pool,
105                                a->prev_this_sw_interface);
106           prev->next_this_sw_interface = a->next_this_sw_interface;
107         }
108       if (a->next_this_sw_interface != ~0)
109         {
110           next =
111             pool_elt_at_index (lm->if_address_pool,
112                                a->next_this_sw_interface);
113           next->prev_this_sw_interface = a->prev_this_sw_interface;
114
115           if (a->prev_this_sw_interface == ~0)
116             lm->if_address_pool_index_by_sw_if_index[sw_if_index] =
117               a->next_this_sw_interface;
118         }
119
120       if ((a->next_this_sw_interface == ~0)
121           && (a->prev_this_sw_interface == ~0))
122         lm->if_address_pool_index_by_sw_if_index[sw_if_index] = ~0;
123
124       mhash_unset (&lm->address_to_if_address_index, addr_fib,
125                    /* old_value */ 0);
126       pool_put (lm->if_address_pool, a);
127
128       if (result_if_address_index)
129         *result_if_address_index = ~0;
130     }
131
132   else if (!a)
133     {
134       u32 pi;                   /* previous index */
135       u32 ai;
136       u32 hi;                   /* head index */
137
138       pool_get (lm->if_address_pool, a);
139       memset (a, ~0, sizeof (a[0]));
140       ai = a - lm->if_address_pool;
141
142       hi = pi = lm->if_address_pool_index_by_sw_if_index[sw_if_index];
143       prev = 0;
144       while (pi != (u32) ~ 0)
145         {
146           prev = pool_elt_at_index (lm->if_address_pool, pi);
147           pi = prev->next_this_sw_interface;
148         }
149       pi = prev ? prev - lm->if_address_pool : (u32) ~ 0;
150
151       a->address_key = mhash_set (&lm->address_to_if_address_index,
152                                   addr_fib, ai, /* old_value */ 0);
153       a->address_length = address_length;
154       a->sw_if_index = sw_if_index;
155       a->flags = 0;
156       a->prev_this_sw_interface = pi;
157       a->next_this_sw_interface = ~0;
158       if (prev)
159         prev->next_this_sw_interface = ai;
160
161       lm->if_address_pool_index_by_sw_if_index[sw_if_index] =
162         (hi != ~0) ? hi : ai;
163       if (result_if_address_index)
164         *result_if_address_index = ai;
165     }
166   else
167     {
168       if (sw_if_index != a->sw_if_index)
169         {
170           if (result_if_address_index)
171             *result_if_address_index = ~0;
172           vnm->api_errno = VNET_API_ERROR_DUPLICATE_IF_ADDRESS;
173           return clib_error_create
174             ("Prefix %U already found on interface %U",
175              lm->format_address_and_length, addr_fib, address_length,
176              format_vnet_sw_if_index_name, vnm, a->sw_if_index);
177         }
178
179       if (result_if_address_index)
180         *result_if_address_index = a - lm->if_address_pool;
181     }
182
183   return /* no error */ 0;
184 }
185
186 static clib_error_t *
187 ip_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_add)
188 {
189   vec_validate_init_empty (ip4_main.
190                            lookup_main.if_address_pool_index_by_sw_if_index,
191                            sw_if_index, ~0);
192   vec_validate_init_empty (ip6_main.
193                            lookup_main.if_address_pool_index_by_sw_if_index,
194                            sw_if_index, ~0);
195
196   return (NULL);
197 }
198
199 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ip_sw_interface_add_del);
200
201 void
202 ip_lookup_init (ip_lookup_main_t * lm, u32 is_ip6)
203 {
204   if (!lm->fib_result_n_bytes)
205     lm->fib_result_n_bytes = sizeof (uword);
206
207   lm->is_ip6 = is_ip6;
208   if (is_ip6)
209     {
210       lm->format_address_and_length = format_ip6_address_and_length;
211       mhash_init (&lm->address_to_if_address_index, sizeof (uword),
212                   sizeof (ip6_address_fib_t));
213     }
214   else
215     {
216       lm->format_address_and_length = format_ip4_address_and_length;
217       mhash_init (&lm->address_to_if_address_index, sizeof (uword),
218                   sizeof (ip4_address_fib_t));
219     }
220
221   {
222     int i;
223
224     /* Setup all IP protocols to be punted and builtin-unknown. */
225     for (i = 0; i < 256; i++)
226       {
227         lm->local_next_by_ip_protocol[i] = IP_LOCAL_NEXT_PUNT;
228         lm->builtin_protocol_by_ip_protocol[i] = IP_BUILTIN_PROTOCOL_UNKNOWN;
229       }
230
231     lm->local_next_by_ip_protocol[IP_PROTOCOL_UDP] = IP_LOCAL_NEXT_UDP_LOOKUP;
232     lm->local_next_by_ip_protocol[is_ip6 ? IP_PROTOCOL_ICMP6 :
233                                   IP_PROTOCOL_ICMP] = IP_LOCAL_NEXT_ICMP;
234     lm->builtin_protocol_by_ip_protocol[IP_PROTOCOL_UDP] =
235       IP_BUILTIN_PROTOCOL_UDP;
236     lm->builtin_protocol_by_ip_protocol[is_ip6 ? IP_PROTOCOL_ICMP6 :
237                                         IP_PROTOCOL_ICMP] =
238       IP_BUILTIN_PROTOCOL_ICMP;
239   }
240 }
241
242 u8 *
243 format_ip_flow_hash_config (u8 * s, va_list * args)
244 {
245   flow_hash_config_t flow_hash_config = va_arg (*args, u32);
246
247 #define _(n,v) if (flow_hash_config & v) s = format (s, "%s ", #n);
248   foreach_flow_hash_bit;
249 #undef _
250
251   return s;
252 }
253
254 u8 *
255 format_ip_lookup_next (u8 * s, va_list * args)
256 {
257   /* int promotion of ip_lookup_next_t */
258   ip_lookup_next_t n = va_arg (*args, int);
259   char *t = 0;
260
261   switch (n)
262     {
263     default:
264       s = format (s, "unknown %d", n);
265       return s;
266
267     case IP_LOOKUP_NEXT_DROP:
268       t = "drop";
269       break;
270     case IP_LOOKUP_NEXT_PUNT:
271       t = "punt";
272       break;
273     case IP_LOOKUP_NEXT_ARP:
274       t = "arp";
275       break;
276     case IP_LOOKUP_NEXT_MIDCHAIN:
277       t = "midchain";
278       break;
279     case IP_LOOKUP_NEXT_GLEAN:
280       t = "glean";
281       break;
282     case IP_LOOKUP_NEXT_MCAST:
283       t = "mcast";
284       break;
285     case IP_LOOKUP_NEXT_REWRITE:
286       break;
287     }
288
289   if (t)
290     vec_add (s, t, strlen (t));
291
292   return s;
293 }
294
295 u8 *
296 format_ip_adjacency_packet_data (u8 * s, va_list * args)
297 {
298   u32 adj_index = va_arg (*args, u32);
299   u8 *packet_data = va_arg (*args, u8 *);
300   u32 n_packet_data_bytes = va_arg (*args, u32);
301   ip_adjacency_t *adj = adj_get (adj_index);
302
303   switch (adj->lookup_next_index)
304     {
305     case IP_LOOKUP_NEXT_REWRITE:
306     case IP_LOOKUP_NEXT_MCAST:
307       s =
308         format (s, "%U", format_hex_bytes, packet_data, n_packet_data_bytes);
309       break;
310
311     default:
312       break;
313     }
314
315   return s;
316 }
317
318 static uword
319 unformat_dpo (unformat_input_t * input, va_list * args)
320 {
321   dpo_id_t *dpo = va_arg (*args, dpo_id_t *);
322   fib_protocol_t fp = va_arg (*args, int);
323   dpo_proto_t proto;
324
325   proto = fib_proto_to_dpo (fp);
326
327   if (unformat (input, "drop"))
328     dpo_copy (dpo, drop_dpo_get (proto));
329   else if (unformat (input, "punt"))
330     dpo_copy (dpo, punt_dpo_get (proto));
331   else if (unformat (input, "local"))
332     receive_dpo_add_or_lock (proto, ~0, NULL, dpo);
333   else if (unformat (input, "null-send-unreach"))
334     ip_null_dpo_add_and_lock (proto, IP_NULL_ACTION_SEND_ICMP_UNREACH, dpo);
335   else if (unformat (input, "null-send-prohibit"))
336     ip_null_dpo_add_and_lock (proto, IP_NULL_ACTION_SEND_ICMP_PROHIBIT, dpo);
337   else if (unformat (input, "null"))
338     ip_null_dpo_add_and_lock (proto, IP_NULL_ACTION_NONE, dpo);
339   else if (unformat (input, "classify"))
340     {
341       u32 classify_table_index;
342
343       if (!unformat (input, "%d", &classify_table_index))
344         {
345           clib_warning ("classify adj must specify table index");
346           return 0;
347         }
348
349       dpo_set (dpo, DPO_CLASSIFY, proto,
350                classify_dpo_create (proto, classify_table_index));
351     }
352   else
353     return 0;
354
355   return 1;
356 }
357
358 const ip46_address_t zero_addr = {
359   .as_u64 = {
360              0, 0},
361 };
362
363 clib_error_t *
364 vnet_ip_route_cmd (vlib_main_t * vm,
365                    unformat_input_t * main_input, vlib_cli_command_t * cmd)
366 {
367   unformat_input_t _line_input, *line_input = &_line_input;
368   fib_route_path_t *rpaths = NULL, rpath;
369   dpo_id_t dpo = DPO_INVALID, *dpos = NULL;
370   fib_prefix_t *prefixs = NULL, pfx;
371   mpls_label_t out_label, via_label;
372   clib_error_t *error = NULL;
373   u32 weight, preference;
374   u32 table_id, is_del;
375   vnet_main_t *vnm;
376   u32 fib_index;
377   f64 count;
378   int i;
379
380   vnm = vnet_get_main ();
381   is_del = 0;
382   table_id = 0;
383   count = 1;
384   memset (&pfx, 0, sizeof (pfx));
385   out_label = via_label = MPLS_LABEL_INVALID;
386
387   /* Get a line of input. */
388   if (!unformat_user (main_input, unformat_line_input, line_input))
389     return 0;
390
391   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
392     {
393       memset (&rpath, 0, sizeof (rpath));
394
395       if (unformat (line_input, "table %d", &table_id))
396         ;
397       else if (unformat (line_input, "resolve-via-host"))
398         {
399           if (vec_len (rpaths) == 0)
400             {
401               error = clib_error_return (0, "Paths then flags");
402               goto done;
403             }
404           rpaths[vec_len (rpaths) - 1].frp_flags |=
405             FIB_ROUTE_PATH_RESOLVE_VIA_HOST;
406         }
407       else if (unformat (line_input, "resolve-via-attached"))
408         {
409           if (vec_len (rpaths) == 0)
410             {
411               error = clib_error_return (0, "Paths then flags");
412               goto done;
413             }
414           rpaths[vec_len (rpaths) - 1].frp_flags |=
415             FIB_ROUTE_PATH_RESOLVE_VIA_ATTACHED;
416         }
417       else if (unformat (line_input, "out-labels"))
418         {
419           if (vec_len (rpaths) == 0)
420             {
421               error = clib_error_return (0, "Paths then labels");
422               goto done;
423             }
424           else
425             {
426               while (unformat (line_input, "%U",
427                                unformat_mpls_unicast_label, &out_label))
428                 {
429                   vec_add1 (rpaths[vec_len (rpaths) - 1].frp_label_stack,
430                             out_label);
431                 }
432             }
433         }
434       else if (unformat (line_input, "via-label %U",
435                          unformat_mpls_unicast_label, &rpath.frp_local_label))
436         {
437           rpath.frp_weight = 1;
438           rpath.frp_eos = MPLS_NON_EOS;
439           rpath.frp_proto = DPO_PROTO_MPLS;
440           rpath.frp_sw_if_index = ~0;
441           vec_add1 (rpaths, rpath);
442         }
443       else if (unformat (line_input, "count %f", &count))
444         ;
445
446       else if (unformat (line_input, "%U/%d",
447                          unformat_ip4_address, &pfx.fp_addr.ip4, &pfx.fp_len))
448         {
449           pfx.fp_proto = FIB_PROTOCOL_IP4;
450           vec_add1 (prefixs, pfx);
451         }
452       else if (unformat (line_input, "%U/%d",
453                          unformat_ip6_address, &pfx.fp_addr.ip6, &pfx.fp_len))
454         {
455           pfx.fp_proto = FIB_PROTOCOL_IP6;
456           vec_add1 (prefixs, pfx);
457         }
458       else if (unformat (line_input, "via %U %U",
459                          unformat_ip4_address,
460                          &rpath.frp_addr.ip4,
461                          unformat_vnet_sw_interface, vnm,
462                          &rpath.frp_sw_if_index))
463         {
464           rpath.frp_weight = 1;
465           rpath.frp_proto = DPO_PROTO_IP4;
466           vec_add1 (rpaths, rpath);
467         }
468
469       else if (unformat (line_input, "via %U %U",
470                          unformat_ip6_address,
471                          &rpath.frp_addr.ip6,
472                          unformat_vnet_sw_interface, vnm,
473                          &rpath.frp_sw_if_index))
474         {
475           rpath.frp_weight = 1;
476           rpath.frp_proto = DPO_PROTO_IP6;
477           vec_add1 (rpaths, rpath);
478         }
479       else if (unformat (line_input, "weight %u", &weight))
480         {
481           ASSERT (vec_len (rpaths));
482           rpaths[vec_len (rpaths) - 1].frp_weight = weight;
483         }
484       else if (unformat (line_input, "preference %u", &preference))
485         {
486           ASSERT (vec_len (rpaths));
487           rpaths[vec_len (rpaths) - 1].frp_preference = preference;
488         }
489       else if (unformat (line_input, "via %U next-hop-table %d",
490                          unformat_ip4_address,
491                          &rpath.frp_addr.ip4, &rpath.frp_fib_index))
492         {
493           rpath.frp_weight = 1;
494           rpath.frp_sw_if_index = ~0;
495           rpath.frp_proto = DPO_PROTO_IP4;
496           vec_add1 (rpaths, rpath);
497         }
498       else if (unformat (line_input, "via %U next-hop-table %d",
499                          unformat_ip6_address,
500                          &rpath.frp_addr.ip6, &rpath.frp_fib_index))
501         {
502           rpath.frp_weight = 1;
503           rpath.frp_sw_if_index = ~0;
504           rpath.frp_proto = DPO_PROTO_IP6;
505           vec_add1 (rpaths, rpath);
506         }
507       else if (unformat (line_input, "via %U",
508                          unformat_ip4_address, &rpath.frp_addr.ip4))
509         {
510           /*
511            * the recursive next-hops are by default in the same table
512            * as the prefix
513            */
514           rpath.frp_fib_index = table_id;
515           rpath.frp_weight = 1;
516           rpath.frp_sw_if_index = ~0;
517           rpath.frp_proto = DPO_PROTO_IP4;
518           vec_add1 (rpaths, rpath);
519         }
520       else if (unformat (line_input, "via %U",
521                          unformat_ip6_address, &rpath.frp_addr.ip6))
522         {
523           rpath.frp_fib_index = table_id;
524           rpath.frp_weight = 1;
525           rpath.frp_sw_if_index = ~0;
526           rpath.frp_proto = DPO_PROTO_IP6;
527           vec_add1 (rpaths, rpath);
528         }
529       else if (unformat (line_input,
530                          "lookup in table %d", &rpath.frp_fib_index))
531         {
532           rpath.frp_proto = fib_proto_to_dpo (pfx.fp_proto);
533           rpath.frp_sw_if_index = ~0;
534           vec_add1 (rpaths, rpath);
535         }
536       else if (vec_len (prefixs) > 0 &&
537                unformat (line_input, "via %U",
538                          unformat_vnet_sw_interface, vnm,
539                          &rpath.frp_sw_if_index))
540         {
541           rpath.frp_weight = 1;
542           rpath.frp_proto = fib_proto_to_dpo (prefixs[0].fp_proto);
543           vec_add1 (rpaths, rpath);
544         }
545       else if (vec_len (prefixs) > 0 &&
546                unformat (line_input, "via %U",
547                          unformat_dpo, &dpo, prefixs[0].fp_proto))
548         {
549           vec_add1 (dpos, dpo);
550         }
551       else if (unformat (line_input, "del"))
552         is_del = 1;
553       else if (unformat (line_input, "add"))
554         is_del = 0;
555       else
556         {
557           error = unformat_parse_error (line_input);
558           goto done;
559         }
560     }
561
562   if (vec_len (prefixs) == 0)
563     {
564       error =
565         clib_error_return (0, "expected ip4/ip6 destination address/length.");
566       goto done;
567     }
568
569   if (!is_del && vec_len (rpaths) + vec_len (dpos) == 0)
570     {
571       error = clib_error_return (0, "expected paths.");
572       goto done;
573     }
574
575   if (~0 == table_id)
576     {
577       /*
578        * if no table_id is passed we will manipulate the default
579        */
580       fib_index = 0;
581     }
582   else
583     {
584       fib_index = fib_table_find (prefixs[0].fp_proto, table_id);
585
586       if (~0 == fib_index)
587         {
588           error = clib_error_return (0, "Nonexistent table id %d", table_id);
589           goto done;
590         }
591     }
592
593   for (i = 0; i < vec_len (prefixs); i++)
594     {
595       if (is_del && 0 == vec_len (rpaths))
596         {
597           fib_table_entry_delete (fib_index, &prefixs[i], FIB_SOURCE_CLI);
598         }
599       else if (!is_del && 1 == vec_len (dpos))
600         {
601           fib_table_entry_special_dpo_add (fib_index,
602                                            &prefixs[i],
603                                            FIB_SOURCE_CLI,
604                                            FIB_ENTRY_FLAG_EXCLUSIVE,
605                                            &dpos[0]);
606           dpo_reset (&dpos[0]);
607         }
608       else if (vec_len (dpos) > 0)
609         {
610           error =
611             clib_error_return (0,
612                                "Load-balancing over multiple special adjacencies is unsupported");
613           goto done;
614         }
615       else if (0 < vec_len (rpaths))
616         {
617           u32 k, j, n, incr;
618           ip46_address_t dst = prefixs[i].fp_addr;
619           f64 t[2];
620           n = count;
621           t[0] = vlib_time_now (vm);
622           incr = 1 << ((FIB_PROTOCOL_IP4 == prefixs[0].fp_proto ? 32 : 128) -
623                        prefixs[i].fp_len);
624
625           for (k = 0; k < n; k++)
626             {
627               for (j = 0; j < vec_len (rpaths); j++)
628                 {
629                   u32 fi;
630                   /*
631                    * the CLI parsing stored table Ids, swap to FIB indicies
632                    */
633                   fi = fib_table_find (prefixs[i].fp_proto,
634                                        rpaths[i].frp_fib_index);
635
636                   if (~0 == fi)
637                     {
638                       error =
639                         clib_error_return (0, "Via table %d does not exist",
640                                            rpaths[i].frp_fib_index);
641                       goto done;
642                     }
643                   rpaths[i].frp_fib_index = fi;
644
645                   fib_prefix_t rpfx = {
646                     .fp_len = prefixs[i].fp_len,
647                     .fp_proto = prefixs[i].fp_proto,
648                     .fp_addr = dst,
649                   };
650
651                   if (is_del)
652                     fib_table_entry_path_remove2 (fib_index,
653                                                   &rpfx,
654                                                   FIB_SOURCE_CLI, &rpaths[j]);
655                   else
656                     fib_table_entry_path_add2 (fib_index,
657                                                &rpfx,
658                                                FIB_SOURCE_CLI,
659                                                FIB_ENTRY_FLAG_NONE,
660                                                &rpaths[j]);
661                 }
662
663               if (FIB_PROTOCOL_IP4 == prefixs[0].fp_proto)
664                 {
665                   dst.ip4.as_u32 =
666                     clib_host_to_net_u32 (incr +
667                                           clib_net_to_host_u32 (dst.
668                                                                 ip4.as_u32));
669                 }
670               else
671                 {
672                   int bucket = (incr < 64 ? 0 : 1);
673                   dst.ip6.as_u64[bucket] =
674                     clib_host_to_net_u64 (incr +
675                                           clib_net_to_host_u64 (dst.ip6.as_u64
676                                                                 [bucket]));
677
678                 }
679             }
680           t[1] = vlib_time_now (vm);
681           if (count > 1)
682             vlib_cli_output (vm, "%.6e routes/sec", count / (t[1] - t[0]));
683         }
684       else
685         {
686           error = clib_error_return (0, "Don't understand what you want...");
687           goto done;
688         }
689     }
690
691
692 done:
693   vec_free (dpos);
694   vec_free (prefixs);
695   vec_free (rpaths);
696   unformat_free (line_input);
697   return error;
698 }
699
700 clib_error_t *
701 vnet_ip_table_cmd (vlib_main_t * vm,
702                    unformat_input_t * main_input,
703                    vlib_cli_command_t * cmd, fib_protocol_t fproto)
704 {
705   unformat_input_t _line_input, *line_input = &_line_input;
706   clib_error_t *error = NULL;
707   u32 table_id, is_add;
708   u8 *name = NULL;
709
710   is_add = 1;
711   table_id = ~0;
712
713   /* Get a line of input. */
714   if (!unformat_user (main_input, unformat_line_input, line_input))
715     return 0;
716
717   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
718     {
719       if (unformat (line_input, "%d", &table_id))
720         ;
721       else if (unformat (line_input, "del"))
722         is_add = 0;
723       else if (unformat (line_input, "add"))
724         is_add = 1;
725       else if (unformat (line_input, "name %s", &name))
726         ;
727       else
728         {
729           error = unformat_parse_error (line_input);
730           goto done;
731         }
732     }
733
734   if (~0 == table_id)
735     {
736       error = clib_error_return (0, "No table id");
737       goto done;
738     }
739   else if (0 == table_id)
740     {
741       error = clib_error_return (0, "Can't change the default table");
742       goto done;
743     }
744   else
745     {
746       if (is_add)
747         {
748           ip_table_create (fproto, table_id, 0, name);
749         }
750       else
751         {
752           ip_table_delete (fproto, table_id, 0);
753         }
754     }
755
756 done:
757   unformat_free (line_input);
758   return error;
759 }
760
761 clib_error_t *
762 vnet_ip4_table_cmd (vlib_main_t * vm,
763                     unformat_input_t * main_input, vlib_cli_command_t * cmd)
764 {
765   return (vnet_ip_table_cmd (vm, main_input, cmd, FIB_PROTOCOL_IP4));
766 }
767
768 clib_error_t *
769 vnet_ip6_table_cmd (vlib_main_t * vm,
770                     unformat_input_t * main_input, vlib_cli_command_t * cmd)
771 {
772   return (vnet_ip_table_cmd (vm, main_input, cmd, FIB_PROTOCOL_IP6));
773 }
774
775 /* *INDENT-OFF* */
776 VLIB_CLI_COMMAND (vlib_cli_ip_command, static) = {
777   .path = "ip",
778   .short_help = "Internet protocol (IP) commands",
779 };
780 /* *INDENT-ON* */
781
782 /* *INDENT-OFF* */
783 VLIB_CLI_COMMAND (vlib_cli_ip6_command, static) = {
784   .path = "ip6",
785   .short_help = "Internet protocol version 6 (IPv6) commands",
786 };
787 /* *INDENT-ON* */
788
789 /* *INDENT-OFF* */
790 VLIB_CLI_COMMAND (vlib_cli_show_ip_command, static) = {
791   .path = "show ip",
792   .short_help = "Internet protocol (IP) show commands",
793 };
794 /* *INDENT-ON* */
795
796 /* *INDENT-OFF* */
797 VLIB_CLI_COMMAND (vlib_cli_show_ip6_command, static) = {
798   .path = "show ip6",
799   .short_help = "Internet protocol version 6 (IPv6) show commands",
800 };
801 /* *INDENT-ON* */
802
803 /*?
804  * This command is used to add or delete IPv4 or IPv6 routes. All
805  * IP Addresses ('<em><dst-ip-addr>/<width></em>',
806  * '<em><next-hop-ip-addr></em>' and '<em><adj-hop-ip-addr></em>')
807  * can be IPv4 or IPv6, but all must be of the same form in a single
808  * command. To display the current set of routes, use the commands
809  * '<em>show ip fib</em>' and '<em>show ip6 fib</em>'.
810  *
811  * @cliexpar
812  * Example of how to add a straight forward static route:
813  * @cliexcmd{ip route add 6.0.1.2/32 via 6.0.0.1 GigabitEthernet2/0/0}
814  * Example of how to delete a straight forward static route:
815  * @cliexcmd{ip route del 6.0.1.2/32 via 6.0.0.1 GigabitEthernet2/0/0}
816  * Mainly for route add/del performance testing, one can add or delete
817  * multiple routes by adding 'count N' to the previous item:
818  * @cliexcmd{ip route add count 10 7.0.0.0/24 via 6.0.0.1 GigabitEthernet2/0/0}
819  * Add multiple routes for the same destination to create equal-cost multipath:
820  * @cliexcmd{ip route add 7.0.0.1/32 via 6.0.0.1 GigabitEthernet2/0/0}
821  * @cliexcmd{ip route add 7.0.0.1/32 via 6.0.0.2 GigabitEthernet2/0/0}
822  * For unequal-cost multipath, specify the desired weights. This
823  * combination of weights results in 3/4 of the traffic following the
824  * second path, 1/4 following the first path:
825  * @cliexcmd{ip route add 7.0.0.1/32 via 6.0.0.1 GigabitEthernet2/0/0 weight 1}
826  * @cliexcmd{ip route add 7.0.0.1/32 via 6.0.0.2 GigabitEthernet2/0/0 weight 3}
827  * To add a route to a particular FIB table (VRF), use:
828  * @cliexcmd{ip route add 172.16.24.0/24 table 7 via GigabitEthernet2/0/0}
829  ?*/
830 /* *INDENT-OFF* */
831 VLIB_CLI_COMMAND (ip_route_command, static) = {
832   .path = "ip route",
833   .short_help = "ip route [add|del] [count <n>] <dst-ip-addr>/<width> [table <table-id>] [via <next-hop-ip-addr> [<interface>] [weight <weight>]] | [via arp <interface> <adj-hop-ip-addr>] | [via drop|punt|local<id>|arp|classify <classify-idx>] [lookup in table <out-table-id>]",
834   .function = vnet_ip_route_cmd,
835   .is_mp_safe = 1,
836 };
837
838 /* *INDENT-ON* */
839 /*?
840  * This command is used to add or delete IPv4  Tables. All
841  * Tables must be explicitly added before that can be used. Creating a
842  * table will add both unicast and multicast FIBs
843  *
844  ?*/
845 /* *INDENT-OFF* */
846 VLIB_CLI_COMMAND (ip4_table_command, static) = {
847   .path = "ip table",
848   .short_help = "ip table [add|del] <table-id>",
849   .function = vnet_ip4_table_cmd,
850   .is_mp_safe = 1,
851 };
852 /* *INDENT-ON* */
853
854 /* *INDENT-ON* */
855 /*?
856  * This command is used to add or delete IPv4  Tables. All
857  * Tables must be explicitly added before that can be used. Creating a
858  * table will add both unicast and multicast FIBs
859  *
860  ?*/
861 /* *INDENT-OFF* */
862 VLIB_CLI_COMMAND (ip6_table_command, static) = {
863   .path = "ip6 table",
864   .short_help = "ip6 table [add|del] <table-id>",
865   .function = vnet_ip6_table_cmd,
866   .is_mp_safe = 1,
867 };
868
869 static clib_error_t *
870 ip_table_bind_cmd (vlib_main_t * vm,
871                    unformat_input_t * input,
872                    vlib_cli_command_t * cmd,
873                    fib_protocol_t fproto)
874 {
875   vnet_main_t *vnm = vnet_get_main ();
876   clib_error_t *error = 0;
877   u32 sw_if_index, table_id;
878   int rv;
879
880   sw_if_index = ~0;
881
882   if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
883     {
884       error = clib_error_return (0, "unknown interface `%U'",
885                                  format_unformat_error, input);
886       goto done;
887     }
888
889   if (unformat (input, "%d", &table_id))
890     ;
891   else
892     {
893       error = clib_error_return (0, "expected table id `%U'",
894                                  format_unformat_error, input);
895       goto done;
896     }
897
898   rv = ip_table_bind (fproto, sw_if_index, table_id, 0);
899
900   if (VNET_API_ERROR_ADDRESS_FOUND_FOR_INTERFACE == rv)
901     {
902       error = clib_error_return (0, "IP addresses are still present on %U",
903                                  format_vnet_sw_if_index_name,
904                                  vnet_get_main(),
905                                  sw_if_index);
906     }
907   else if (VNET_API_ERROR_NO_SUCH_FIB == rv)
908     {
909       error = clib_error_return (0, "no such table %d", table_id);
910     }
911   else if (0 != rv)
912     {
913       error = clib_error_return (0, "unknown error");
914     }
915
916  done:
917   return error;
918 }
919
920 static clib_error_t *
921 ip4_table_bind_cmd (vlib_main_t * vm,
922                     unformat_input_t * input,
923                     vlib_cli_command_t * cmd)
924 {
925   return (ip_table_bind_cmd (vm , input, cmd, FIB_PROTOCOL_IP4));
926 }
927
928 static clib_error_t *
929 ip6_table_bind_cmd (vlib_main_t * vm,
930                     unformat_input_t * input,
931                     vlib_cli_command_t * cmd)
932 {
933   return (ip_table_bind_cmd (vm , input, cmd, FIB_PROTOCOL_IP6));
934 }
935
936 /*?
937  * Place the indicated interface into the supplied IPv4 FIB table (also known
938  * as a VRF). If the FIB table does not exist, this command creates it. To
939  * display the current IPv4 FIB table, use the command '<em>show ip fib</em>'.
940  * FIB table will only be displayed if a route has been added to the table, or
941  * an IP Address is assigned to an interface in the table (which adds a route
942  * automatically).
943  *
944  * @note IP addresses added after setting the interface IP table are added to
945  * the indicated FIB table. If an IP address is added prior to changing the
946  * table then this is an error. The control plane must remove these addresses
947  * first and then change the table. VPP will not automatically move the
948  * addresses from the old to the new table as it does not know the validity
949  * of such a change.
950  *
951  * @cliexpar
952  * Example of how to add an interface to an IPv4 FIB table (where 2 is the table-id):
953  * @cliexcmd{set interface ip table GigabitEthernet2/0/0 2}
954  ?*/
955 /* *INDENT-OFF* */
956 VLIB_CLI_COMMAND (set_interface_ip_table_command, static) =
957 {
958   .path = "set interface ip table",
959   .function = ip4_table_bind_cmd,
960   .short_help = "set interface ip table <interface> <table-id>",
961 };
962 /* *INDENT-ON* */
963
964 /*?
965  * Place the indicated interface into the supplied IPv6 FIB table (also known
966  * as a VRF). If the FIB table does not exist, this command creates it. To
967  * display the current IPv6 FIB table, use the command '<em>show ip6 fib</em>'.
968  * FIB table will only be displayed if a route has been added to the table, or
969  * an IP Address is assigned to an interface in the table (which adds a route
970  * automatically).
971  *
972  * @note IP addresses added after setting the interface IP table are added to
973  * the indicated FIB table. If an IP address is added prior to changing the
974  * table then this is an error. The control plane must remove these addresses
975  * first and then change the table. VPP will not automatically move the
976  * addresses from the old to the new table as it does not know the validity
977  * of such a change.
978  *
979  * @cliexpar
980  * Example of how to add an interface to an IPv6 FIB table (where 2 is the table-id):
981  * @cliexcmd{set interface ip6 table GigabitEthernet2/0/0 2}
982  ?*/
983 /* *INDENT-OFF* */
984 VLIB_CLI_COMMAND (set_interface_ip6_table_command, static) =
985 {
986   .path = "set interface ip6 table",
987   .function = ip6_table_bind_cmd,
988   .short_help = "set interface ip6 table <interface> <table-id>"
989 };
990 /* *INDENT-ON* */
991
992 clib_error_t *
993 vnet_ip_mroute_cmd (vlib_main_t * vm,
994                     unformat_input_t * main_input, vlib_cli_command_t * cmd)
995 {
996   unformat_input_t _line_input, *line_input = &_line_input;
997   clib_error_t *error = NULL;
998   fib_route_path_t rpath;
999   u32 table_id, is_del;
1000   vnet_main_t *vnm;
1001   mfib_prefix_t pfx;
1002   u32 fib_index;
1003   mfib_itf_flags_t iflags = 0;
1004   mfib_entry_flags_t eflags = 0;
1005   u32 gcount, scount, ss, gg, incr;
1006   f64 timet[2];
1007
1008   gcount = scount = 1;
1009   vnm = vnet_get_main ();
1010   is_del = 0;
1011   table_id = 0;
1012   memset (&pfx, 0, sizeof (pfx));
1013   memset (&rpath, 0, sizeof (rpath));
1014   rpath.frp_sw_if_index = ~0;
1015
1016   /* Get a line of input. */
1017   if (!unformat_user (main_input, unformat_line_input, line_input))
1018     return 0;
1019
1020   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1021     {
1022       if (unformat (line_input, "table %d", &table_id))
1023         ;
1024       else if (unformat (line_input, "del"))
1025         is_del = 1;
1026       else if (unformat (line_input, "add"))
1027         is_del = 0;
1028       else if (unformat (line_input, "scount %d", &scount))
1029         ;
1030       else if (unformat (line_input, "gcount %d", &gcount))
1031         ;
1032       else if (unformat (line_input, "%U %U",
1033                          unformat_ip4_address,
1034                          &pfx.fp_src_addr.ip4,
1035                          unformat_ip4_address, &pfx.fp_grp_addr.ip4))
1036         {
1037           pfx.fp_proto = FIB_PROTOCOL_IP4;
1038           pfx.fp_len = 64;
1039         }
1040       else if (unformat (line_input, "%U %U",
1041                          unformat_ip6_address,
1042                          &pfx.fp_src_addr.ip6,
1043                          unformat_ip6_address, &pfx.fp_grp_addr.ip6))
1044         {
1045           pfx.fp_proto = FIB_PROTOCOL_IP6;
1046           pfx.fp_len = 256;
1047         }
1048       else if (unformat (line_input, "%U/%d",
1049                          unformat_ip4_address,
1050                          &pfx.fp_grp_addr.ip4, &pfx.fp_len))
1051         {
1052           memset (&pfx.fp_src_addr.ip4, 0, sizeof (pfx.fp_src_addr.ip4));
1053           pfx.fp_proto = FIB_PROTOCOL_IP4;
1054         }
1055       else if (unformat (line_input, "%U/%d",
1056                          unformat_ip6_address,
1057                          &pfx.fp_grp_addr.ip6, &pfx.fp_len))
1058         {
1059           memset (&pfx.fp_src_addr.ip6, 0, sizeof (pfx.fp_src_addr.ip6));
1060           pfx.fp_proto = FIB_PROTOCOL_IP6;
1061         }
1062       else if (unformat (line_input, "%U",
1063                          unformat_ip4_address, &pfx.fp_grp_addr.ip4))
1064         {
1065           memset (&pfx.fp_src_addr.ip4, 0, sizeof (pfx.fp_src_addr.ip4));
1066           pfx.fp_proto = FIB_PROTOCOL_IP4;
1067           pfx.fp_len = 32;
1068         }
1069       else if (unformat (line_input, "%U",
1070                          unformat_ip6_address, &pfx.fp_grp_addr.ip6))
1071         {
1072           memset (&pfx.fp_src_addr.ip6, 0, sizeof (pfx.fp_src_addr.ip6));
1073           pfx.fp_proto = FIB_PROTOCOL_IP6;
1074           pfx.fp_len = 128;
1075         }
1076       else if (unformat (line_input, "via %U",
1077                          unformat_vnet_sw_interface, vnm,
1078                          &rpath.frp_sw_if_index))
1079         {
1080           rpath.frp_weight = 1;
1081         }
1082       else if (unformat (line_input, "via local"))
1083         {
1084           rpath.frp_sw_if_index = ~0;
1085           rpath.frp_weight = 1;
1086           rpath.frp_flags |= FIB_ROUTE_PATH_LOCAL;
1087         }
1088       else if (unformat (line_input, "%U", unformat_mfib_itf_flags, &iflags))
1089         ;
1090       else if (unformat (line_input, "%U",
1091                          unformat_mfib_entry_flags, &eflags))
1092         ;
1093       else
1094         {
1095           error = unformat_parse_error (line_input);
1096           goto done;
1097         }
1098     }
1099
1100   if (~0 == table_id)
1101     {
1102       /*
1103        * if no table_id is passed we will manipulate the default
1104        */
1105       fib_index = 0;
1106     }
1107   else
1108     {
1109       fib_index = mfib_table_find (pfx.fp_proto, table_id);
1110
1111       if (~0 == fib_index)
1112         {
1113           error = clib_error_return (0, "Nonexistent table id %d", table_id);
1114           goto done;
1115         }
1116     }
1117
1118   timet[0] = vlib_time_now (vm);
1119
1120   if (FIB_PROTOCOL_IP4 == pfx.fp_proto)
1121     {
1122       incr = 1 << (32 - (pfx.fp_len % 32));
1123     }
1124   else
1125     {
1126       incr = 1 << (128 - (pfx.fp_len % 128));
1127     }
1128
1129   for (ss = 0; ss < scount; ss++)
1130     {
1131       for (gg = 0; gg < gcount; gg++)
1132         {
1133           if (is_del && 0 == rpath.frp_weight)
1134             {
1135               /* no path provided => route delete */
1136               mfib_table_entry_delete (fib_index, &pfx, MFIB_SOURCE_CLI);
1137             }
1138           else if (eflags)
1139             {
1140               mfib_table_entry_update (fib_index, &pfx, MFIB_SOURCE_CLI,
1141                                        MFIB_RPF_ID_NONE, eflags);
1142             }
1143           else
1144             {
1145               if (is_del)
1146                 mfib_table_entry_path_remove (fib_index,
1147                                               &pfx, MFIB_SOURCE_CLI, &rpath);
1148               else
1149                 mfib_table_entry_path_update (fib_index,
1150                                               &pfx, MFIB_SOURCE_CLI, &rpath,
1151                                               iflags);
1152             }
1153
1154           if (FIB_PROTOCOL_IP4 == pfx.fp_proto)
1155             {
1156               pfx.fp_grp_addr.ip4.as_u32 =
1157                 clib_host_to_net_u32 (incr +
1158                                       clib_net_to_host_u32 (pfx.
1159                                                             fp_grp_addr.ip4.
1160                                                             as_u32));
1161             }
1162           else
1163             {
1164               int bucket = (incr < 64 ? 0 : 1);
1165               pfx.fp_grp_addr.ip6.as_u64[bucket] =
1166                 clib_host_to_net_u64 (incr +
1167                                       clib_net_to_host_u64 (pfx.
1168                                                             fp_grp_addr.ip6.as_u64
1169                                                             [bucket]));
1170
1171             }
1172         }
1173       if (FIB_PROTOCOL_IP4 == pfx.fp_proto)
1174         {
1175           pfx.fp_src_addr.ip4.as_u32 =
1176             clib_host_to_net_u32 (1 +
1177                                   clib_net_to_host_u32 (pfx.fp_src_addr.
1178                                                         ip4.as_u32));
1179         }
1180       else
1181         {
1182           pfx.fp_src_addr.ip6.as_u64[1] =
1183             clib_host_to_net_u64 (1 +
1184                                   clib_net_to_host_u64 (pfx.fp_src_addr.
1185                                                         ip6.as_u64[1]));
1186         }
1187     }
1188
1189   timet[1] = vlib_time_now (vm);
1190
1191   if (scount > 1 || gcount > 1)
1192     vlib_cli_output (vm, "%.6e routes/sec",
1193                      (scount * gcount) / (timet[1] - timet[0]));
1194
1195 done:
1196   unformat_free (line_input);
1197
1198   return error;
1199 }
1200
1201 /*?
1202  * This command is used to add or delete IPv4 or IPv6  multicastroutes. All
1203  * IP Addresses ('<em><dst-ip-addr>/<width></em>',
1204  * '<em><next-hop-ip-addr></em>' and '<em><adj-hop-ip-addr></em>')
1205  * can be IPv4 or IPv6, but all must be of the same form in a single
1206  * command. To display the current set of routes, use the commands
1207  * '<em>show ip mfib</em>' and '<em>show ip6 mfib</em>'.
1208  * The full set of support flags for interfaces and route is shown via;
1209  * '<em>show mfib route flags</em>' and '<em>show mfib itf flags</em>'
1210  * respectively.
1211  * @cliexpar
1212  * Example of how to add a forwarding interface to a route (and create the
1213  * route if it does not exist)
1214  * @cliexcmd{ip mroute add 232.1.1.1 via GigabitEthernet2/0/0 Forward}
1215  * Example of how to add an accepting interface to a route (and create the
1216  * route if it does not exist)
1217  * @cliexcmd{ip mroute add 232.1.1.1 via GigabitEthernet2/0/1 Accept}
1218  * Example of changing the route's flags to send signals via the API
1219  * @cliexcmd{ip mroute add 232.1.1.1 Signal}
1220
1221  ?*/
1222 /* *INDENT-OFF* */
1223 VLIB_CLI_COMMAND (ip_mroute_command, static) =
1224 {
1225   .path = "ip mroute",
1226   .short_help = "ip mroute [add|del] <dst-ip-addr>/<width> [table <table-id>] [via <next-hop-ip-addr> [<interface>],",
1227   .function = vnet_ip_mroute_cmd,
1228   .is_mp_safe = 1,
1229 };
1230 /* *INDENT-ON* */
1231
1232 /*
1233  * The next two routines address a longstanding script hemorrhoid.
1234  * Probing a v4 or v6 neighbor needs to appear to be synchronous,
1235  * or dependent route-adds will simply fail.
1236  */
1237 static clib_error_t *
1238 ip6_probe_neighbor_wait (vlib_main_t * vm, ip6_address_t * a, u32 sw_if_index,
1239                          int retry_count)
1240 {
1241   vnet_main_t *vnm = vnet_get_main ();
1242   clib_error_t *e;
1243   int i;
1244   int resolved = 0;
1245   uword event_type;
1246   uword *event_data = 0;
1247
1248   ASSERT (vlib_in_process_context (vm));
1249
1250   if (retry_count > 0)
1251     vnet_register_ip6_neighbor_resolution_event
1252       (vnm, a, vlib_get_current_process (vm)->node_runtime.node_index,
1253        1 /* event */ , 0 /* data */ );
1254
1255   for (i = 0; i < retry_count; i++)
1256     {
1257       /* The interface may be down, etc. */
1258       e = ip6_probe_neighbor (vm, a, sw_if_index);
1259
1260       if (e)
1261         return e;
1262
1263       vlib_process_wait_for_event_or_clock (vm, 1.0);
1264       event_type = vlib_process_get_events (vm, &event_data);
1265       switch (event_type)
1266         {
1267         case 1:         /* resolved... */
1268           vlib_cli_output (vm, "Resolved %U", format_ip6_address, a);
1269           resolved = 1;
1270           goto done;
1271
1272         case ~0:                /* timeout */
1273           break;
1274
1275         default:
1276           clib_warning ("unknown event_type %d", event_type);
1277         }
1278       vec_reset_length (event_data);
1279     }
1280
1281 done:
1282
1283   if (!resolved)
1284     return clib_error_return (0, "Resolution failed for %U",
1285                               format_ip6_address, a);
1286   return 0;
1287 }
1288
1289 static clib_error_t *
1290 ip4_probe_neighbor_wait (vlib_main_t * vm, ip4_address_t * a, u32 sw_if_index,
1291                          int retry_count)
1292 {
1293   vnet_main_t *vnm = vnet_get_main ();
1294   clib_error_t *e;
1295   int i;
1296   int resolved = 0;
1297   uword event_type;
1298   uword *event_data = 0;
1299
1300   ASSERT (vlib_in_process_context (vm));
1301
1302   if (retry_count > 0)
1303     vnet_register_ip4_arp_resolution_event
1304       (vnm, a, vlib_get_current_process (vm)->node_runtime.node_index,
1305        1 /* event */ , 0 /* data */ );
1306
1307   for (i = 0; i < retry_count; i++)
1308     {
1309       /* The interface may be down, etc. */
1310       e = ip4_probe_neighbor (vm, a, sw_if_index);
1311
1312       if (e)
1313         return e;
1314
1315       vlib_process_wait_for_event_or_clock (vm, 1.0);
1316       event_type = vlib_process_get_events (vm, &event_data);
1317       switch (event_type)
1318         {
1319         case 1:         /* resolved... */
1320           vlib_cli_output (vm, "Resolved %U", format_ip4_address, a);
1321           resolved = 1;
1322           goto done;
1323
1324         case ~0:                /* timeout */
1325           break;
1326
1327         default:
1328           clib_warning ("unknown event_type %d", event_type);
1329         }
1330       vec_reset_length (event_data);
1331     }
1332
1333 done:
1334
1335   vec_reset_length (event_data);
1336
1337   if (!resolved)
1338     return clib_error_return (0, "Resolution failed for %U",
1339                               format_ip4_address, a);
1340   return 0;
1341 }
1342
1343 static clib_error_t *
1344 probe_neighbor_address (vlib_main_t * vm,
1345                         unformat_input_t * input, vlib_cli_command_t * cmd)
1346 {
1347   vnet_main_t *vnm = vnet_get_main ();
1348   unformat_input_t _line_input, *line_input = &_line_input;
1349   ip4_address_t a4;
1350   ip6_address_t a6;
1351   clib_error_t *error = 0;
1352   u32 sw_if_index = ~0;
1353   int retry_count = 3;
1354   int is_ip4 = 1;
1355   int address_set = 0;
1356
1357   /* Get a line of input. */
1358   if (!unformat_user (input, unformat_line_input, line_input))
1359     return 0;
1360
1361   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1362     {
1363       if (unformat_user (line_input, unformat_vnet_sw_interface, vnm,
1364                          &sw_if_index))
1365         ;
1366       else if (unformat (line_input, "retry %d", &retry_count))
1367         ;
1368
1369       else if (unformat (line_input, "%U", unformat_ip4_address, &a4))
1370         address_set++;
1371       else if (unformat (line_input, "%U", unformat_ip6_address, &a6))
1372         {
1373           address_set++;
1374           is_ip4 = 0;
1375         }
1376       else
1377         {
1378           error = clib_error_return (0, "unknown input '%U'",
1379                                      format_unformat_error, line_input);
1380           goto done;
1381         }
1382     }
1383
1384   if (sw_if_index == ~0)
1385     {
1386       error = clib_error_return (0, "Interface required, not set.");
1387       goto done;
1388     }
1389   if (address_set == 0)
1390     {
1391       error = clib_error_return (0, "ip address required, not set.");
1392       goto done;
1393     }
1394   if (address_set > 1)
1395     {
1396       error = clib_error_return (0, "Multiple ip addresses not supported.");
1397       goto done;
1398     }
1399
1400   if (is_ip4)
1401     error = ip4_probe_neighbor_wait (vm, &a4, sw_if_index, retry_count);
1402   else
1403     error = ip6_probe_neighbor_wait (vm, &a6, sw_if_index, retry_count);
1404
1405 done:
1406   unformat_free (line_input);
1407
1408   return error;
1409 }
1410
1411 /*?
1412  * The '<em>ip probe-neighbor</em>' command ARPs for IPv4 addresses or
1413  * attempts IPv6 neighbor discovery depending on the supplied IP address
1414  * format.
1415  *
1416  * @note This command will not immediately affect the indicated FIB; it
1417  * is not suitable for use in establishing a FIB entry prior to adding
1418  * recursive FIB entries. As in: don't use it in a script to probe a
1419  * gateway prior to adding a default route. It won't work. Instead,
1420  * configure a static ARP cache entry [see '<em>set ip arp</em>'], or
1421  * a static IPv6 neighbor [see '<em>set ip6 neighbor</em>'].
1422  *
1423  * @cliexpar
1424  * Example of probe for an IPv4 address:
1425  * @cliexcmd{ip probe-neighbor GigabitEthernet2/0/0 172.16.1.2}
1426 ?*/
1427 /* *INDENT-OFF* */
1428 VLIB_CLI_COMMAND (ip_probe_neighbor_command, static) = {
1429   .path = "ip probe-neighbor",
1430   .function = probe_neighbor_address,
1431   .short_help = "ip probe-neighbor <interface> <ip4-addr> | <ip6-addr> [retry nn]",
1432   .is_mp_safe = 1,
1433 };
1434 /* *INDENT-ON* */
1435
1436 /*
1437  * fd.io coding-style-patch-verification: ON
1438  *
1439  * Local Variables:
1440  * eval: (c-set-style "gnu")
1441  * End:
1442  */