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