misc: move to new pool_foreach macros
[vpp.git] / src / plugins / ioam / lib-vxlan-gpe / vxlan_gpe_ioam.c
1 /*
2  * Copyright (c) 2016 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 #include <vnet/vxlan-gpe/vxlan_gpe.h>
16 #include <vnet/vxlan-gpe/vxlan_gpe_packet.h>
17 #include <vnet/ip/format.h>
18 #include <ioam/lib-vxlan-gpe/vxlan_gpe_ioam.h>
19 #include <vnet/dpo/load_balance.h>
20 #include <vnet/fib/ip4_fib.h>
21 #include <vnet/fib/fib_entry.h>
22
23 vxlan_gpe_ioam_main_t vxlan_gpe_ioam_main;
24
25 int
26 vxlan_gpe_ioam_set_rewrite (vxlan_gpe_tunnel_t * t, int has_trace_option,
27                             int has_pot_option, int has_ppc_option,
28                             u8 ipv6_set)
29 {
30   vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
31   u32 size;
32   vxlan_gpe_ioam_hdr_t *vxlan_gpe_ioam_hdr;
33   u8 *current;
34   u8 trace_data_size = 0;
35   u8 pot_data_size = 0;
36
37   if (has_trace_option == 0 && has_pot_option == 0)
38     return -1;
39
40   /* Work out how much space we need */
41   size = sizeof (vxlan_gpe_ioam_hdr_t);
42
43   if (has_trace_option
44       && hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_TRACE] != 0)
45     {
46       size += sizeof (vxlan_gpe_ioam_option_t);
47       size += hm->options_size[VXLAN_GPE_OPTION_TYPE_IOAM_TRACE];
48     }
49   if (has_pot_option
50       && hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] != 0)
51     {
52       size += sizeof (vxlan_gpe_ioam_option_t);
53       size += hm->options_size[VXLAN_GPE_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT];
54     }
55
56   t->rewrite_size = size;
57
58   if (!ipv6_set)
59     {
60       vxlan4_gpe_rewrite (t, size, VXLAN_GPE_PROTOCOL_IOAM,
61                           hm->encap_v4_next_node);
62       vxlan_gpe_ioam_hdr =
63         (vxlan_gpe_ioam_hdr_t *) (t->rewrite +
64                                   sizeof (ip4_vxlan_gpe_header_t));
65     }
66   else
67     {
68       vxlan6_gpe_rewrite (t, size, VXLAN_GPE_PROTOCOL_IOAM,
69                           VXLAN_GPE_ENCAP_NEXT_IP6_LOOKUP);
70       vxlan_gpe_ioam_hdr =
71         (vxlan_gpe_ioam_hdr_t *) (t->rewrite +
72                                   sizeof (ip6_vxlan_gpe_header_t));
73     }
74
75
76   vxlan_gpe_ioam_hdr->type = VXLAN_GPE_PROTOCOL_IOAM;
77   /* Length of the header in octets */
78   vxlan_gpe_ioam_hdr->length = size;
79   vxlan_gpe_ioam_hdr->protocol = t->protocol;
80   current = (u8 *) vxlan_gpe_ioam_hdr + sizeof (vxlan_gpe_ioam_hdr_t);
81
82   if (has_trace_option
83       && hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_TRACE] != 0)
84     {
85       if (0 != hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_TRACE] (current,
86                                                                   &trace_data_size))
87         return -1;
88       current += trace_data_size;
89     }
90   if (has_pot_option
91       && hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] != 0)
92     {
93       pot_data_size =
94         hm->options_size[VXLAN_GPE_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT];
95       if (0 ==
96           hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT]
97           (current, &pot_data_size))
98         current += pot_data_size;
99     }
100
101   return 0;
102 }
103
104 int
105 vxlan_gpe_ioam_clear_rewrite (vxlan_gpe_tunnel_t * t, int has_trace_option,
106                               int has_pot_option, int has_ppc_option,
107                               u8 ipv6_set)
108 {
109
110   t->rewrite_size = 0;
111
112   if (!ipv6_set)
113     {
114       vxlan4_gpe_rewrite (t, 0, 0, VXLAN_GPE_ENCAP_NEXT_IP4_LOOKUP);
115     }
116   else
117     {
118       vxlan6_gpe_rewrite (t, 0, 0, VXLAN_GPE_ENCAP_NEXT_IP6_LOOKUP);
119     }
120
121
122   return 0;
123 }
124
125 clib_error_t *
126 vxlan_gpe_ioam_clear (vxlan_gpe_tunnel_t * t,
127                       int has_trace_option, int has_pot_option,
128                       int has_ppc_option, u8 ipv6_set)
129 {
130   int rv;
131   rv = vxlan_gpe_ioam_clear_rewrite (t, 0, 0, 0, 0);
132
133   if (rv == 0)
134     {
135       return (0);
136     }
137   else
138     {
139       return clib_error_return_code (0, rv, 0,
140                                      "vxlan_gpe_ioam_clear_rewrite returned %d",
141                                      rv);
142     }
143
144 }
145
146
147 clib_error_t *
148 vxlan_gpe_ioam_set (vxlan_gpe_tunnel_t * t,
149                     int has_trace_option, int has_pot_option,
150                     int has_ppc_option, u8 ipv6_set)
151 {
152   int rv;
153   rv = vxlan_gpe_ioam_set_rewrite (t, has_trace_option,
154                                    has_pot_option, has_ppc_option, ipv6_set);
155
156   if (rv == 0)
157     {
158       return (0);
159     }
160   else
161     {
162       return clib_error_return_code (0, rv, 0,
163                                      "vxlan_gpe_ioam_set_rewrite returned %d",
164                                      rv);
165     }
166
167 }
168
169 static void
170 vxlan_gpe_set_clear_output_feature_on_intf (vlib_main_t * vm,
171                                             u32 sw_if_index0, u8 is_add)
172 {
173
174
175
176   vnet_feature_enable_disable ("ip4-output", "vxlan-gpe-transit-ioam",
177                                sw_if_index0, is_add,
178                                0 /* void *feature_config */ ,
179                                0 /* u32 n_feature_config_bytes */ );
180   return;
181 }
182
183 void
184 vxlan_gpe_clear_output_feature_on_all_intfs (vlib_main_t * vm)
185 {
186   vnet_sw_interface_t *si = 0;
187   vnet_main_t *vnm = vnet_get_main ();
188   vnet_interface_main_t *im = &vnm->interface_main;
189
190   pool_foreach (si, im->sw_interfaces)
191   {
192     vxlan_gpe_set_clear_output_feature_on_intf (vm, si->sw_if_index, 0);
193   }
194   return;
195 }
196
197
198 extern fib_forward_chain_type_t
199 fib_entry_get_default_chain_type (const fib_entry_t * fib_entry);
200
201 int
202 vxlan_gpe_enable_disable_ioam_for_dest (vlib_main_t * vm,
203                                         ip46_address_t dst_addr,
204                                         u32 outer_fib_index,
205                                         u8 is_ipv4, u8 is_add)
206 {
207   vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
208   u32 fib_index0 = 0;
209   u32 sw_if_index0 = ~0;
210
211   fib_node_index_t fei = ~0;
212   fib_entry_t *fib_entry;
213   u32 adj_index0;
214   ip_adjacency_t *adj0;
215   fib_prefix_t fib_prefix;
216   //fib_forward_chain_type_t fct;
217   load_balance_t *lb_m, *lb_b;
218   const dpo_id_t *dpo0, *dpo1;
219   u32 i, j;
220   //vnet_hw_interface_t *hw;
221
222   if (is_ipv4)
223     {
224       clib_memset (&fib_prefix, 0, sizeof (fib_prefix_t));
225       fib_prefix.fp_len = 32;
226       fib_prefix.fp_proto = FIB_PROTOCOL_IP4;
227       fib_prefix.fp_addr = dst_addr;
228     }
229   else
230     {
231       return 0;
232     }
233
234   fei = fib_table_lookup (fib_index0, &fib_prefix);
235   fib_entry = fib_entry_get (fei);
236
237   //fct = fib_entry_get_default_chain_type (fib_entry);
238
239   if (!dpo_id_is_valid (&fib_entry->fe_lb /*[fct] */ ))
240     {
241       return (-1);
242     }
243
244   lb_m = load_balance_get (fib_entry->fe_lb /*[fct] */ .dpoi_index);
245
246   for (i = 0; i < lb_m->lb_n_buckets; i++)
247     {
248       dpo0 = load_balance_get_bucket_i (lb_m, i);
249
250       if (dpo0->dpoi_type == DPO_LOAD_BALANCE)
251         {
252           lb_b = load_balance_get (dpo0->dpoi_index);
253
254           for (j = 0; j < lb_b->lb_n_buckets; j++)
255             {
256               dpo1 = load_balance_get_bucket_i (lb_b, j);
257
258               if (dpo1->dpoi_type == DPO_ADJACENCY)
259                 {
260                   adj_index0 = dpo1->dpoi_index;
261
262                   if (ADJ_INDEX_INVALID == adj_index0)
263                     {
264                       continue;
265                     }
266
267                   adj0 = adj_get (adj_index0);
268                   sw_if_index0 = adj0->rewrite_header.sw_if_index;
269
270                   if (~0 == sw_if_index0)
271                     {
272                       continue;
273                     }
274
275
276                   if (is_add)
277                     {
278                       vnet_feature_enable_disable ("ip4-output",
279                                                    "vxlan-gpe-transit-ioam",
280                                                    sw_if_index0, is_add, 0
281                                                    /* void *feature_config */
282                                                    , 0  /* u32 n_feature_config_bytes */
283                         );
284
285                       vec_validate_init_empty (hm->bool_ref_by_sw_if_index,
286                                                sw_if_index0, ~0);
287                       hm->bool_ref_by_sw_if_index[sw_if_index0] = 1;
288                     }
289                   else
290                     {
291                       hm->bool_ref_by_sw_if_index[sw_if_index0] = ~0;
292                     }
293                 }
294             }
295         }
296     }
297
298   if (is_ipv4)
299     {
300
301       uword *t = NULL;
302       vxlan_gpe_ioam_dest_tunnels_t *t1;
303       fib_prefix_t key4, *key4_copy;
304       hash_pair_t *hp;
305       clib_memset (&key4, 0, sizeof (key4));
306       key4.fp_proto = FIB_PROTOCOL_IP4;
307       key4.fp_addr.ip4.as_u32 = fib_prefix.fp_addr.ip4.as_u32;
308       t = hash_get_mem (hm->dst_by_ip4, &key4);
309       if (is_add)
310         {
311           if (t)
312             {
313               return 0;
314             }
315           pool_get_aligned (hm->dst_tunnels, t1, CLIB_CACHE_LINE_BYTES);
316           clib_memset (t1, 0, sizeof (*t1));
317           t1->fp_proto = FIB_PROTOCOL_IP4;
318           t1->dst_addr.ip4.as_u32 = fib_prefix.fp_addr.ip4.as_u32;
319           key4_copy = clib_mem_alloc (sizeof (*key4_copy));
320           clib_memcpy (key4_copy, &key4, sizeof (*key4_copy));
321           hash_set_mem (hm->dst_by_ip4, key4_copy, t1 - hm->dst_tunnels);
322           /*
323            * Attach to the FIB entry for the VxLAN-GPE destination
324            * and become its child. The dest route will invoke a callback
325            * when the fib entry changes, it can be used to
326            * re-program the output feature on the egress interface.
327            */
328
329           const fib_prefix_t tun_dst_pfx = {
330             .fp_len = 32,
331             .fp_proto = FIB_PROTOCOL_IP4,
332             .fp_addr = {.ip4 = t1->dst_addr.ip4,}
333           };
334
335           t1->fib_entry_index =
336             fib_table_entry_special_add (outer_fib_index,
337                                          &tun_dst_pfx,
338                                          FIB_SOURCE_RR, FIB_ENTRY_FLAG_NONE);
339           t1->sibling_index =
340             fib_entry_child_add (t1->fib_entry_index,
341                                  hm->fib_entry_type, t1 - hm->dst_tunnels);
342           t1->outer_fib_index = outer_fib_index;
343
344         }
345       else
346         {
347           if (!t)
348             {
349               return 0;
350             }
351           t1 = pool_elt_at_index (hm->dst_tunnels, t[0]);
352           hp = hash_get_pair (hm->dst_by_ip4, &key4);
353           key4_copy = (void *) (hp->key);
354           hash_unset_mem (hm->dst_by_ip4, &key4);
355           clib_mem_free (key4_copy);
356           pool_put (hm->dst_tunnels, t1);
357         }
358     }
359   else
360     {
361       // TBD for IPv6
362     }
363
364   return 0;
365 }
366
367 void
368 vxlan_gpe_refresh_output_feature_on_all_dest (void)
369 {
370   vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
371   vxlan_gpe_ioam_dest_tunnels_t *t;
372   u32 i;
373   if (pool_elts (hm->dst_tunnels) == 0)
374     return;
375   vxlan_gpe_clear_output_feature_on_all_intfs (hm->vlib_main);
376   i = vec_len (hm->bool_ref_by_sw_if_index);
377   vec_free (hm->bool_ref_by_sw_if_index);
378   vec_validate_init_empty (hm->bool_ref_by_sw_if_index, i, ~0);
379   pool_foreach (t, hm->dst_tunnels)
380   {
381     vxlan_gpe_enable_disable_ioam_for_dest
382       (hm->vlib_main, t->dst_addr, t->outer_fib_index,
383        (t->fp_proto == FIB_PROTOCOL_IP4), 1 /* is_add */ );
384   }
385   return;
386 }
387
388 void
389 vxlan_gpe_clear_output_feature_on_select_intfs (void)
390 {
391   vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
392   u32 sw_if_index0 = 0;
393   for (sw_if_index0 = 0;
394        sw_if_index0 < vec_len (hm->bool_ref_by_sw_if_index); sw_if_index0++)
395     {
396       if (hm->bool_ref_by_sw_if_index[sw_if_index0] == 0xFF)
397         {
398           vxlan_gpe_set_clear_output_feature_on_intf
399             (hm->vlib_main, sw_if_index0, 0);
400         }
401     }
402
403   return;
404 }
405
406 static clib_error_t *
407 vxlan_gpe_set_ioam_rewrite_command_fn (vlib_main_t *
408                                        vm,
409                                        unformat_input_t
410                                        * input, vlib_cli_command_t * cmd)
411 {
412   vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
413   ip46_address_t local, remote;
414   u8 local_set = 0;
415   u8 remote_set = 0;
416   u8 ipv4_set = 0;
417   u8 ipv6_set = 0;
418   u32 vni;
419   u8 vni_set = 0;
420   u8 disable = 0;
421   clib_error_t *rv = 0;
422   vxlan4_gpe_tunnel_key_t key4;
423   vxlan6_gpe_tunnel_key_t key6;
424   uword *p;
425   vxlan_gpe_main_t *gm = &vxlan_gpe_main;
426   vxlan_gpe_tunnel_t *t = 0;
427   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
428     {
429       if (unformat (input, "local %U", unformat_ip4_address, &local.ip4))
430         {
431           local_set = 1;
432           ipv4_set = 1;
433         }
434       else
435         if (unformat (input, "remote %U", unformat_ip4_address, &remote.ip4))
436         {
437           remote_set = 1;
438           ipv4_set = 1;
439         }
440       else if (unformat (input, "local %U", unformat_ip6_address, &local.ip6))
441         {
442           local_set = 1;
443           ipv6_set = 1;
444         }
445       else
446         if (unformat (input, "remote %U", unformat_ip6_address, &remote.ip6))
447         {
448           remote_set = 1;
449           ipv6_set = 1;
450         }
451       else if (unformat (input, "vni %d", &vni))
452         vni_set = 1;
453       else if (unformat (input, "disable"))
454         disable = 1;
455       else
456         break;
457     }
458
459   if (local_set == 0)
460     return clib_error_return (0, "tunnel local address not specified");
461   if (remote_set == 0)
462     return clib_error_return (0, "tunnel remote address not specified");
463   if (ipv4_set && ipv6_set)
464     return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
465   if ((ipv4_set
466        && memcmp (&local.ip4, &remote.ip4,
467                   sizeof (local.ip4)) == 0) || (ipv6_set
468                                                 &&
469                                                 memcmp
470                                                 (&local.ip6,
471                                                  &remote.ip6,
472                                                  sizeof (local.ip6)) == 0))
473     return clib_error_return (0, "src and dst addresses are identical");
474   if (vni_set == 0)
475     return clib_error_return (0, "vni not specified");
476   if (!ipv6_set)
477     {
478       key4.local = local.ip4.as_u32;
479       key4.remote = remote.ip4.as_u32;
480       key4.vni = clib_host_to_net_u32 (vni << 8);
481       key4.pad = 0;
482       p = hash_get_mem (gm->vxlan4_gpe_tunnel_by_key, &key4);
483     }
484   else
485     {
486       key6.local.as_u64[0] = local.ip6.as_u64[0];
487       key6.local.as_u64[1] = local.ip6.as_u64[1];
488       key6.remote.as_u64[0] = remote.ip6.as_u64[0];
489       key6.remote.as_u64[1] = remote.ip6.as_u64[1];
490       key6.vni = clib_host_to_net_u32 (vni << 8);
491       p = hash_get_mem (gm->vxlan6_gpe_tunnel_by_key, &key6);
492     }
493
494   if (!p)
495     return clib_error_return (0, "VxLAN Tunnel not found");
496   t = pool_elt_at_index (gm->tunnels, p[0]);
497   if (!disable)
498     {
499       rv =
500         vxlan_gpe_ioam_set (t, hm->has_trace_option,
501                             hm->has_pot_option, hm->has_ppc_option, ipv6_set);
502     }
503   else
504     {
505       rv = vxlan_gpe_ioam_clear (t, 0, 0, 0, 0);
506     }
507   return rv;
508 }
509
510
511 /* *INDENT-OFF* */
512 VLIB_CLI_COMMAND (vxlan_gpe_set_ioam_rewrite_cmd, static) = {
513   .path = "set vxlan-gpe-ioam",
514   .short_help = "set vxlan-gpe-ioam vxlan <src-ip> <dst_ip> <vnid> [disable]",
515   .function = vxlan_gpe_set_ioam_rewrite_command_fn,
516 };
517 /* *INDENT-ON* */
518
519
520
521 clib_error_t *
522 vxlan_gpe_ioam_enable (int has_trace_option,
523                        int has_pot_option, int has_ppc_option)
524 {
525   vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
526   hm->has_trace_option = has_trace_option;
527   hm->has_pot_option = has_pot_option;
528   hm->has_ppc_option = has_ppc_option;
529   if (hm->has_trace_option)
530     {
531       vxlan_gpe_trace_profile_setup ();
532     }
533
534   return 0;
535 }
536
537 clib_error_t *
538 vxlan_gpe_ioam_disable (int
539                         has_trace_option,
540                         int has_pot_option, int has_ppc_option)
541 {
542   vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
543   hm->has_trace_option = has_trace_option;
544   hm->has_pot_option = has_pot_option;
545   hm->has_ppc_option = has_ppc_option;
546   if (!hm->has_trace_option)
547     {
548       vxlan_gpe_trace_profile_cleanup ();
549     }
550
551   return 0;
552 }
553
554 void
555 vxlan_gpe_set_next_override (uword next)
556 {
557   vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
558   hm->decap_v4_next_override = next;
559   return;
560 }
561
562 static clib_error_t *
563 vxlan_gpe_set_ioam_flags_command_fn (vlib_main_t * vm,
564                                      unformat_input_t
565                                      * input, vlib_cli_command_t * cmd)
566 {
567   int has_trace_option = 0;
568   int has_pot_option = 0;
569   int has_ppc_option = 0;
570   clib_error_t *rv = 0;
571   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
572     {
573       if (unformat (input, "trace"))
574         has_trace_option = 1;
575       else if (unformat (input, "pot"))
576         has_pot_option = 1;
577       else if (unformat (input, "ppc encap"))
578         has_ppc_option = PPC_ENCAP;
579       else if (unformat (input, "ppc decap"))
580         has_ppc_option = PPC_DECAP;
581       else if (unformat (input, "ppc none"))
582         has_ppc_option = PPC_NONE;
583       else
584         break;
585     }
586
587
588   rv =
589     vxlan_gpe_ioam_enable (has_trace_option, has_pot_option, has_ppc_option);
590   return rv;
591 }
592
593 /* *INDENT-OFF* */
594 VLIB_CLI_COMMAND (vxlan_gpe_set_ioam_flags_cmd, static) =
595 {
596 .path = "set vxlan-gpe-ioam rewrite",
597 .short_help = "set vxlan-gpe-ioam [trace] [pot] [ppc <encap|decap>]",
598 .function = vxlan_gpe_set_ioam_flags_command_fn,};
599 /* *INDENT-ON* */
600
601
602 int vxlan_gpe_ioam_disable_for_dest
603   (vlib_main_t * vm, ip46_address_t dst_addr, u32 outer_fib_index,
604    u8 ipv4_set)
605 {
606   vxlan_gpe_ioam_dest_tunnels_t *t;
607   vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
608
609   vxlan_gpe_enable_disable_ioam_for_dest (hm->vlib_main,
610                                           dst_addr, outer_fib_index, ipv4_set,
611                                           0);
612   if (pool_elts (hm->dst_tunnels) == 0)
613     {
614       vxlan_gpe_clear_output_feature_on_select_intfs ();
615       return 0;
616     }
617
618   pool_foreach (t, hm->dst_tunnels)
619   {
620     vxlan_gpe_enable_disable_ioam_for_dest
621       (hm->vlib_main,
622        t->dst_addr,
623        t->outer_fib_index,
624        (t->fp_proto == FIB_PROTOCOL_IP4), 1 /* is_add */ );
625   }
626   vxlan_gpe_clear_output_feature_on_select_intfs ();
627   return (0);
628
629 }
630
631 static clib_error_t *vxlan_gpe_set_ioam_transit_rewrite_command_fn
632   (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
633 {
634   vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
635   ip46_address_t dst_addr;
636   u8 dst_addr_set = 0;
637   u8 ipv4_set = 0;
638   u8 ipv6_set = 0;
639   u8 disable = 0;
640   clib_error_t *rv = 0;
641   u32 outer_fib_index = 0;
642   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
643     {
644       if (unformat (input, "dst-ip %U", unformat_ip4_address, &dst_addr.ip4))
645         {
646           dst_addr_set = 1;
647           ipv4_set = 1;
648         }
649       else
650         if (unformat
651             (input, "dst-ip %U", unformat_ip6_address, &dst_addr.ip6))
652         {
653           dst_addr_set = 1;
654           ipv6_set = 1;
655         }
656       else if (unformat (input, "outer-fib-index %d", &outer_fib_index))
657         {
658         }
659
660       else if (unformat (input, "disable"))
661         disable = 1;
662       else
663         break;
664     }
665
666   if (dst_addr_set == 0)
667     return clib_error_return (0, "tunnel destination address not specified");
668   if (ipv4_set && ipv6_set)
669     return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
670   if (!disable)
671     {
672       vxlan_gpe_enable_disable_ioam_for_dest (hm->vlib_main,
673                                               dst_addr, outer_fib_index,
674                                               ipv4_set, 1);
675     }
676   else
677     {
678       vxlan_gpe_ioam_disable_for_dest
679         (vm, dst_addr, outer_fib_index, ipv4_set);
680     }
681   return rv;
682 }
683
684        /* *INDENT-OFF* */
685 VLIB_CLI_COMMAND (vxlan_gpe_set_ioam_transit_rewrite_cmd, static) = {
686   .path = "set vxlan-gpe-ioam-transit",
687   .short_help = "set vxlan-gpe-ioam-transit dst-ip <dst_ip> [outer-fib-index <outer_fib_index>] [disable]",
688   .function = vxlan_gpe_set_ioam_transit_rewrite_command_fn,
689 };
690 /* *INDENT-ON* */
691
692 clib_error_t *clear_vxlan_gpe_ioam_rewrite_command_fn
693   (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
694 {
695   return (vxlan_gpe_ioam_disable (0, 0, 0));
696 }
697
698 /* *INDENT-OFF* */
699 VLIB_CLI_COMMAND (vxlan_gpe_clear_ioam_flags_cmd, static) =
700 {
701 .path = "clear vxlan-gpe-ioam rewrite",
702 .short_help = "clear vxlan-gpe-ioam rewrite",
703 .function = clear_vxlan_gpe_ioam_rewrite_command_fn,
704 };
705 /* *INDENT-ON* */
706
707
708 /**
709  * Function definition to backwalk a FIB node
710  */
711 static fib_node_back_walk_rc_t
712 vxlan_gpe_ioam_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
713 {
714   vxlan_gpe_refresh_output_feature_on_all_dest ();
715   return (FIB_NODE_BACK_WALK_CONTINUE);
716 }
717
718 /**
719  * Function definition to get a FIB node from its index
720  */
721 static fib_node_t *
722 vxlan_gpe_ioam_fib_node_get (fib_node_index_t index)
723 {
724   vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
725   return (&hm->node);
726 }
727
728 /**
729  * Function definition to inform the FIB node that its last lock has gone.
730  */
731 static void
732 vxlan_gpe_ioam_last_lock_gone (fib_node_t * node)
733 {
734   ASSERT (0);
735 }
736
737
738 /*
739  * Virtual function table registered by MPLS GRE tunnels
740  * for participation in the FIB object graph.
741  */
742 const static fib_node_vft_t vxlan_gpe_ioam_vft = {
743   .fnv_get = vxlan_gpe_ioam_fib_node_get,
744   .fnv_last_lock = vxlan_gpe_ioam_last_lock_gone,
745   .fnv_back_walk = vxlan_gpe_ioam_back_walk,
746 };
747
748 void
749 vxlan_gpe_ioam_interface_init (void)
750 {
751   vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
752   hm->fib_entry_type = fib_node_register_new_type (&vxlan_gpe_ioam_vft);
753   return;
754 }
755
756 /*
757  * fd.io coding-style-patch-verification: ON
758  *
759  * Local Variables:
760  * eval: (c-set-style "gnu")
761  * End:
762  */