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