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