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