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