c11 safe string handling support
[vpp.git] / src / plugins / nsh / nsh-md2-ioam / nsh_md2_ioam.c
1 /*
2  * nsh_md2_ioam.c - NSH iOAM functions for MD type 2
3  *
4  * Copyright (c) 2017 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <vnet/vnet.h>
19 #include <vnet/plugin/plugin.h>
20 #include <nsh/nsh.h>
21 #include <nsh/nsh_packet.h>
22 #include <vnet/ip/ip.h>
23 #include <nsh/nsh-md2-ioam/nsh_md2_ioam.h>
24
25 #include <vlibapi/api.h>
26 #include <vlibmemory/api.h>
27
28 #include <vnet/fib/ip6_fib.h>
29 #include <vnet/fib/ip4_fib.h>
30 #include <vnet/fib/fib_entry.h>
31
32 /* define message structures */
33 #define vl_typedefs
34 #include <nsh/nsh.api.h>
35 #undef vl_typedefs
36
37 /* define generated endian-swappers */
38 #define vl_endianfun
39 #include <nsh/nsh.api.h>
40 #undef vl_endianfun
41
42 nsh_md2_ioam_main_t nsh_md2_ioam_main;
43
44 static void
45 nsh_md2_ioam_set_clear_output_feature_on_intf (vlib_main_t * vm,
46                                                     u32 sw_if_index0,
47                                                     u8 is_add)
48 {
49
50
51
52   vnet_feature_enable_disable ("ip4-output",
53                                "nsh-md2-ioam-encap-transit",
54                                sw_if_index0, is_add,
55                                0 /* void *feature_config */ ,
56                                0 /* u32 n_feature_config_bytes */ );
57   return;
58 }
59
60 void
61 nsh_md2_ioam_clear_output_feature_on_all_intfs (vlib_main_t * vm)
62 {
63   vnet_sw_interface_t *si = 0;
64   vnet_main_t *vnm = vnet_get_main ();
65   vnet_interface_main_t *im = &vnm->interface_main;
66
67   pool_foreach (si, im->sw_interfaces, (
68                                          {
69                                          nsh_md2_ioam_set_clear_output_feature_on_intf
70                                          (vm, si->sw_if_index, 0);
71                                          }));
72   return;
73 }
74
75
76 extern fib_forward_chain_type_t
77 fib_entry_get_default_chain_type (const fib_entry_t * fib_entry);
78
79 int
80 nsh_md2_ioam_enable_disable_for_dest (vlib_main_t * vm,
81                                            ip46_address_t dst_addr,
82                                            u32 outer_fib_index,
83                                            u8 is_ipv4, u8 is_add)
84 {
85   nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
86   u32 fib_index0 = 0;
87
88   fib_node_index_t fei = ~0;
89   u32 *sw_if_index0 = NULL;
90 #if 0
91   fib_entry_t *fib_entry;
92   u32 adj_index0;
93   ip_adjacency_t *adj0;
94   load_balance_t *lb_m, *lb_b;
95   const dpo_id_t *dpo0, *dpo1;
96   u32 i, j, k;
97 #endif
98   u32 *intf_list = NULL;
99   fib_prefix_t fib_prefix;
100
101   if (is_ipv4)
102     {
103       clib_memset (&fib_prefix, 0, sizeof (fib_prefix_t));
104       fib_prefix.fp_len = 32;
105       fib_prefix.fp_proto = FIB_PROTOCOL_IP4;
106 #define  TRANSIT_UNIT_TEST_HACK 1
107 #ifdef TRANSIT_UNIT_TEST_HACK
108       clib_memset(&dst_addr, 0, sizeof(dst_addr));
109       dst_addr.ip4.as_u32 = clib_net_to_host_u32(0x14020102);
110 #endif
111       fib_prefix.fp_addr = dst_addr;
112     }
113   else
114     {
115       return 0;
116     }
117
118   fei = fib_table_lookup (fib_index0, &fib_prefix);
119 #if 0
120   fib_entry = fib_entry_get (fei);
121
122
123   if (!dpo_id_is_valid (&fib_entry->fe_lb))
124     {
125       return (-1);
126     }
127
128   lb_m = load_balance_get (fib_entry->fe_lb.dpoi_index);
129
130   for (i = 0; i < lb_m->lb_n_buckets; i++)
131     {
132       dpo0 = load_balance_get_bucket_i (lb_m, i);
133
134       if (dpo0->dpoi_type == DPO_LOAD_BALANCE ||
135           dpo0->dpoi_type == DPO_ADJACENCY)
136         {
137           if (dpo0->dpoi_type == DPO_ADJACENCY)
138             {
139               k = 1;
140             }
141           else
142             {
143               lb_b = load_balance_get (dpo0->dpoi_index);
144               k = lb_b->lb_n_buckets;
145             }
146
147           for (j = 0; j < k; j++)
148             {
149               if (dpo0->dpoi_type == DPO_ADJACENCY)
150                 {
151                   dpo1 = dpo0;
152                 }
153               else
154                 {
155                   dpo1 = load_balance_get_bucket_i (lb_b, j);
156                 }
157
158               if (dpo1->dpoi_type == DPO_ADJACENCY)
159                 {
160                   adj_index0 = dpo1->dpoi_index;
161
162                   if (ADJ_INDEX_INVALID == adj_index0)
163                     {
164                       continue;
165                     }
166
167                   adj0 =
168                     ip_get_adjacency (&(ip4_main.lookup_main), adj_index0);
169                   sw_if_index0 = adj0->rewrite_header.sw_if_index;
170
171                   if (~0 == sw_if_index0)
172                     {
173                       continue;
174                     }
175
176
177                   if (is_add)
178                     {
179                       vnet_feature_enable_disable ("ip4-output",
180                                                    "nsh-md2-ioam-encap-transit",
181                                                    sw_if_index0, is_add, 0,
182                                                    /* void *feature_config */
183                                                    0    /* u32 n_feature_config_bytes */
184                         );
185
186                       vec_validate_init_empty (hm->bool_ref_by_sw_if_index,
187                                                sw_if_index0, ~0);
188                       hm->bool_ref_by_sw_if_index[sw_if_index0] = 1;
189                     }
190                   else
191                     {
192                       hm->bool_ref_by_sw_if_index[sw_if_index0] = ~0;
193                     }
194                 }
195             }
196         }
197     }
198 #else
199
200 u32 fib_path_get_resolving_interface (fib_node_index_t path_index);
201     vec_add1(intf_list, fib_path_get_resolving_interface(fei));
202     vec_foreach(sw_if_index0, intf_list)
203     if (is_add)
204       {
205         vnet_feature_enable_disable ("ip4-output",
206                          "nsh-md2-ioam-encap-transit",
207                          *sw_if_index0, is_add, 0,
208                          /* void *feature_config */
209                          0    /* u32 n_feature_config_bytes */
210           );
211
212         vec_validate_init_empty (hm->bool_ref_by_sw_if_index,
213                          *sw_if_index0, ~0);
214         hm->bool_ref_by_sw_if_index[*sw_if_index0] = 1;
215       }
216     else
217       {
218         hm->bool_ref_by_sw_if_index[*sw_if_index0] = ~0;
219       }
220
221 #endif
222
223   if (is_ipv4)
224     {
225       uword *t = NULL;
226       nsh_md2_ioam_dest_tunnels_t *t1;
227       fib_prefix_t key4, *key4_copy;
228       hash_pair_t *hp;
229       clib_memset (&key4, 0, sizeof (key4));
230       key4.fp_proto = FIB_PROTOCOL_IP4;
231       key4.fp_addr.ip4.as_u32 = fib_prefix.fp_addr.ip4.as_u32;
232       t = hash_get_mem (hm->dst_by_ip4, &key4);
233       if (is_add)
234         {
235           if (t)
236             {
237               return 0;
238             }
239           pool_get_aligned (hm->dst_tunnels, t1, CLIB_CACHE_LINE_BYTES);
240           clib_memset (t1, 0, sizeof (*t1));
241           t1->fp_proto = FIB_PROTOCOL_IP4;
242           t1->dst_addr.ip4.as_u32 = fib_prefix.fp_addr.ip4.as_u32;
243           key4_copy = clib_mem_alloc (sizeof (*key4_copy));
244           clib_memset(key4_copy, 0, sizeof(*key4_copy));
245           clib_memcpy (key4_copy, &key4, sizeof (*key4_copy));
246           hash_set_mem (hm->dst_by_ip4, key4_copy, t1 - hm->dst_tunnels);
247           /*
248            * Attach to the FIB entry for the VxLAN-GPE destination
249            * and become its child. The dest route will invoke a callback
250            * when the fib entry changes, it can be used to
251            * re-program the output feature on the egress interface.
252            */
253
254           const fib_prefix_t tun_dst_pfx = {
255             .fp_len = 32,
256             .fp_proto = FIB_PROTOCOL_IP4,
257             .fp_addr = {.ip4 = t1->dst_addr.ip4,}
258           };
259
260           t1->fib_entry_index =
261             fib_table_entry_special_add (outer_fib_index,
262                                          &tun_dst_pfx,
263                                          FIB_SOURCE_RR,
264                                          FIB_ENTRY_FLAG_NONE);
265           t1->sibling_index =
266             fib_entry_child_add (t1->fib_entry_index,
267                                  hm->fib_entry_type, t1 - hm->dst_tunnels);
268           t1->outer_fib_index = outer_fib_index;
269
270         }
271       else
272         {
273           if (!t)
274             {
275               return 0;
276             }
277           t1 = pool_elt_at_index (hm->dst_tunnels, t[0]);
278           hp = hash_get_pair (hm->dst_by_ip4, &key4);
279           key4_copy = (void *) (hp->key);
280           hash_unset_mem (hm->dst_by_ip4, &key4);
281           clib_mem_free (key4_copy);
282           pool_put (hm->dst_tunnels, t1);
283         }
284     }
285   else
286     {
287       // TBD for IPv6
288     }
289
290   return 0;
291 }
292
293 void
294 nsh_md2_ioam_refresh_output_feature_on_all_dest (void)
295 {
296   nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
297   nsh_main_t *gm = &nsh_main;
298   nsh_md2_ioam_dest_tunnels_t *t;
299   u32 i;
300
301   if (pool_elts (hm->dst_tunnels) == 0)
302     return;
303
304   nsh_md2_ioam_clear_output_feature_on_all_intfs (gm->vlib_main);
305   i = vec_len (hm->bool_ref_by_sw_if_index);
306   vec_free (hm->bool_ref_by_sw_if_index);
307   vec_validate_init_empty (hm->bool_ref_by_sw_if_index, i, ~0);
308   pool_foreach (t, hm->dst_tunnels, (
309                                       {
310                                       nsh_md2_ioam_enable_disable_for_dest
311                                       (gm->vlib_main,
312                                        t->dst_addr,
313                                        t->outer_fib_index,
314                                        (t->fp_proto == FIB_PROTOCOL_IP4), 1
315                                        /* is_add */
316                                       );
317                                       }
318                 ));
319   return;
320 }
321
322 void
323 nsh_md2_ioam_clear_output_feature_on_select_intfs (void)
324 {
325   nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
326   nsh_main_t *gm = &nsh_main;
327
328   u32 sw_if_index0 = 0;
329   for (sw_if_index0 = 0;
330        sw_if_index0 < vec_len (hm->bool_ref_by_sw_if_index); sw_if_index0++)
331     {
332       if (hm->bool_ref_by_sw_if_index[sw_if_index0] == 0xFF)
333         {
334           nsh_md2_ioam_set_clear_output_feature_on_intf
335             (gm->vlib_main, sw_if_index0, 0);
336         }
337     }
338
339   return;
340 }
341
342
343
344
345 clib_error_t *
346 nsh_md2_ioam_enable_disable (int has_trace_option, int has_pot_option,
347                                   int has_ppc_option)
348 {
349   nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
350
351   hm->has_trace_option = has_trace_option;
352   hm->has_pot_option = has_pot_option;
353   hm->has_ppc_option = has_ppc_option;
354
355   if (hm->has_trace_option)
356     {
357       nsh_md2_ioam_trace_profile_setup ();
358     }
359   else if (!hm->has_trace_option)
360     {
361       nsh_md2_ioam_trace_profile_cleanup ();
362     }
363
364   return 0;
365 }
366
367
368 int nsh_md2_ioam_disable_for_dest
369   (vlib_main_t * vm, ip46_address_t dst_addr, u32 outer_fib_index,
370    u8 ipv4_set)
371 {
372   nsh_md2_ioam_dest_tunnels_t *t;
373   nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
374   nsh_main_t *gm = &nsh_main;
375
376   nsh_md2_ioam_enable_disable_for_dest (gm->vlib_main,
377                                              dst_addr, outer_fib_index,
378                                              ipv4_set, 0);
379   if (pool_elts (hm->dst_tunnels) == 0)
380     {
381       nsh_md2_ioam_clear_output_feature_on_select_intfs ();
382       return 0;
383     }
384
385   pool_foreach (t, hm->dst_tunnels, (
386                                       {
387                                       nsh_md2_ioam_enable_disable_for_dest
388                                       (gm->vlib_main,
389                                        t->dst_addr,
390                                        t->outer_fib_index,
391                                        (t->fp_proto ==
392                                         FIB_PROTOCOL_IP4), 1 /* is_add */ );
393                                       }
394                 ));
395   nsh_md2_ioam_clear_output_feature_on_select_intfs ();
396   return (0);
397
398 }
399
400 static clib_error_t *nsh_md2_ioam_set_transit_rewrite_command_fn
401   (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
402 {
403   nsh_main_t *gm = &nsh_main;
404   ip46_address_t dst_addr;
405   u8 dst_addr_set = 0;
406   u8 ipv4_set = 0;
407   u8 ipv6_set = 0;
408   u8 disable = 0;
409   clib_error_t *rv = 0;
410   u32 outer_fib_index = 0;
411   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
412     {
413       if (unformat (input, "dst-ip %U", unformat_ip4_address, &dst_addr.ip4))
414         {
415           dst_addr_set = 1;
416           ipv4_set = 1;
417         }
418       else
419         if (unformat
420             (input, "dst-ip %U", unformat_ip6_address, &dst_addr.ip6))
421         {
422           dst_addr_set = 1;
423           ipv6_set = 1;
424         }
425       else if (unformat (input, "outer-fib-index %d", &outer_fib_index))
426         {
427         }
428
429       else if (unformat (input, "disable"))
430         disable = 1;
431       else
432         break;
433     }
434
435   if (dst_addr_set == 0)
436     return clib_error_return (0,
437                               "LISP-GPE Tunnel destination address not specified");
438   if (ipv4_set && ipv6_set)
439     return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
440   if (!disable)
441     {
442       nsh_md2_ioam_enable_disable_for_dest (gm->vlib_main,
443                                                  dst_addr, outer_fib_index,
444                                                  ipv4_set, 1);
445     }
446   else
447     {
448       nsh_md2_ioam_disable_for_dest
449         (vm, dst_addr, outer_fib_index, ipv4_set);
450     }
451   return rv;
452 }
453
454 /* *INDENT-OFF* */
455 VLIB_CLI_COMMAND (nsh_md2_ioam_set_transit_rewrite_cmd, static) = {
456   .path = "set nsh-md2-ioam-transit",
457   .short_help = "set nsh-ioam-lisp-gpe-transit dst-ip <dst_ip> [outer-fib-index <outer_fib_index>] [disable]",
458   .function = nsh_md2_ioam_set_transit_rewrite_command_fn,
459 };
460
461 /**
462  * Function definition to backwalk a FIB node
463  */
464 static fib_node_back_walk_rc_t
465 nsh_md2_ioam_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
466 {
467   nsh_md2_ioam_refresh_output_feature_on_all_dest ();
468   return (FIB_NODE_BACK_WALK_CONTINUE);
469 }
470
471 /**
472  * Function definition to get a FIB node from its index
473  */
474 static fib_node_t *
475 nsh_md2_ioam_fib_node_get (fib_node_index_t index)
476 {
477   nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
478   return (&hm->node);
479 }
480
481 /**
482  * Function definition to inform the FIB node that its last lock has gone.
483  */
484 static void
485 nsh_md2_ioam_last_lock_gone (fib_node_t * node)
486 {
487   ASSERT (0);
488 }
489
490
491 /*
492  * Virtual function table registered by MPLS GRE tunnels
493  * for participation in the FIB object graph.
494  */
495 const static fib_node_vft_t nsh_md2_ioam_vft = {
496   .fnv_get = nsh_md2_ioam_fib_node_get,
497   .fnv_last_lock = nsh_md2_ioam_last_lock_gone,
498   .fnv_back_walk = nsh_md2_ioam_back_walk,
499 };
500
501 void
502 nsh_md2_ioam_interface_init (void)
503 {
504   nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
505   hm->fib_entry_type = fib_node_register_new_type (&nsh_md2_ioam_vft);
506   return;
507 }
508