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