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