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