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