nsh_md2_ioam.c revision 8aae5e9e
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	  t1->sibling_index =
270	    fib_entry_child_add (t1->fib_entry_index,
271				 hm->fib_entry_type, t1 - hm->dst_tunnels);
272	  t1->outer_fib_index = outer_fib_index;
273
274	}
275      else
276	{
277	  if (!t)
278	    {
279	      return 0;
280	    }
281	  t1 = pool_elt_at_index (hm->dst_tunnels, t[0]);
282	  hp = hash_get_pair (hm->dst_by_ip4, &key4);
283	  key4_copy = (void *) (hp->key);
284	  hash_unset_mem (hm->dst_by_ip4, &key4);
285	  clib_mem_free (key4_copy);
286	  pool_put (hm->dst_tunnels, t1);
287	}
288    }
289  else
290    {
291      // TBD for IPv6
292    }
293
294  return 0;
295}
296
297void
298nsh_md2_ioam_refresh_output_feature_on_all_dest (void)
299{
300  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
301  nsh_main_t *gm = &nsh_main;
302  nsh_md2_ioam_dest_tunnels_t *t;
303  u32 i;
304
305  if (pool_elts (hm->dst_tunnels) == 0)
306    return;
307
308  nsh_md2_ioam_clear_output_feature_on_all_intfs (gm->vlib_main);
309  i = vec_len (hm->bool_ref_by_sw_if_index);
310  vec_free (hm->bool_ref_by_sw_if_index);
311  vec_validate_init_empty (hm->bool_ref_by_sw_if_index, i, ~0);
312  pool_foreach (t, hm->dst_tunnels, (
313				      {
314				      nsh_md2_ioam_enable_disable_for_dest
315				      (gm->vlib_main,
316				       t->dst_addr,
317				       t->outer_fib_index,
318				       (t->fp_proto == FIB_PROTOCOL_IP4), 1
319				       /* is_add */
320				      );
321				      }
322		));
323  return;
324}
325
326void
327nsh_md2_ioam_clear_output_feature_on_select_intfs (void)
328{
329  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
330  nsh_main_t *gm = &nsh_main;
331
332  u32 sw_if_index0 = 0;
333  for (sw_if_index0 = 0;
334       sw_if_index0 < vec_len (hm->bool_ref_by_sw_if_index); sw_if_index0++)
335    {
336      if (hm->bool_ref_by_sw_if_index[sw_if_index0] == 0xFF)
337	{
338	  nsh_md2_ioam_set_clear_output_feature_on_intf
339	    (gm->vlib_main, sw_if_index0, 0);
340	}
341    }
342
343  return;
344}
345
346
347
348
349clib_error_t *
350nsh_md2_ioam_enable_disable (int has_trace_option, int has_pot_option,
351				  int has_ppc_option)
352{
353  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
354
355  hm->has_trace_option = has_trace_option;
356  hm->has_pot_option = has_pot_option;
357  hm->has_ppc_option = has_ppc_option;
358
359  if (hm->has_trace_option)
360    {
361      nsh_md2_ioam_trace_profile_setup ();
362    }
363  else if (!hm->has_trace_option)
364    {
365      nsh_md2_ioam_trace_profile_cleanup ();
366    }
367
368  return 0;
369}
370
371
372int nsh_md2_ioam_disable_for_dest
373  (vlib_main_t * vm, ip46_address_t dst_addr, u32 outer_fib_index,
374   u8 ipv4_set)
375{
376  nsh_md2_ioam_dest_tunnels_t *t;
377  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
378  nsh_main_t *gm = &nsh_main;
379
380  nsh_md2_ioam_enable_disable_for_dest (gm->vlib_main,
381					     dst_addr, outer_fib_index,
382					     ipv4_set, 0);
383  if (pool_elts (hm->dst_tunnels) == 0)
384    {
385      nsh_md2_ioam_clear_output_feature_on_select_intfs ();
386      return 0;
387    }
388
389  pool_foreach (t, hm->dst_tunnels, (
390				      {
391				      nsh_md2_ioam_enable_disable_for_dest
392				      (gm->vlib_main,
393				       t->dst_addr,
394				       t->outer_fib_index,
395				       (t->fp_proto ==
396					FIB_PROTOCOL_IP4), 1 /* is_add */ );
397				      }
398		));
399  nsh_md2_ioam_clear_output_feature_on_select_intfs ();
400  return (0);
401
402}
403
404static clib_error_t *nsh_md2_ioam_set_transit_rewrite_command_fn
405  (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
406{
407  nsh_main_t *gm = &nsh_main;
408  ip46_address_t dst_addr;
409  u8 dst_addr_set = 0;
410  u8 ipv4_set = 0;
411  u8 ipv6_set = 0;
412  u8 disable = 0;
413  clib_error_t *rv = 0;
414  u32 outer_fib_index = 0;
415  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
416    {
417      if (unformat (input, "dst-ip %U", unformat_ip4_address, &dst_addr.ip4))
418	{
419	  dst_addr_set = 1;
420	  ipv4_set = 1;
421	}
422      else
423	if (unformat
424	    (input, "dst-ip %U", unformat_ip6_address, &dst_addr.ip6))
425	{
426	  dst_addr_set = 1;
427	  ipv6_set = 1;
428	}
429      else if (unformat (input, "outer-fib-index %d", &outer_fib_index))
430	{
431	}
432
433      else if (unformat (input, "disable"))
434	disable = 1;
435      else
436	break;
437    }
438
439  if (dst_addr_set == 0)
440    return clib_error_return (0,
441			      "LISP-GPE Tunnel destination address not specified");
442  if (ipv4_set && ipv6_set)
443    return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
444  if (!disable)
445    {
446      nsh_md2_ioam_enable_disable_for_dest (gm->vlib_main,
447						 dst_addr, outer_fib_index,
448						 ipv4_set, 1);
449    }
450  else
451    {
452      nsh_md2_ioam_disable_for_dest
453	(vm, dst_addr, outer_fib_index, ipv4_set);
454    }
455  return rv;
456}
457
458/* *INDENT-OFF* */
459VLIB_CLI_COMMAND (nsh_md2_ioam_set_transit_rewrite_cmd, static) = {
460  .path = "set nsh-md2-ioam-transit",
461  .short_help = "set nsh-ioam-lisp-gpe-transit dst-ip <dst_ip> [outer-fib-index <outer_fib_index>] [disable]",
462  .function = nsh_md2_ioam_set_transit_rewrite_command_fn,
463};
464
465/**
466 * Function definition to backwalk a FIB node
467 */
468static fib_node_back_walk_rc_t
469nsh_md2_ioam_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
470{
471  nsh_md2_ioam_refresh_output_feature_on_all_dest ();
472  return (FIB_NODE_BACK_WALK_CONTINUE);
473}
474
475/**
476 * Function definition to get a FIB node from its index
477 */
478static fib_node_t *
479nsh_md2_ioam_fib_node_get (fib_node_index_t index)
480{
481  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
482  return (&hm->node);
483}
484
485/**
486 * Function definition to inform the FIB node that its last lock has gone.
487 */
488static void
489nsh_md2_ioam_last_lock_gone (fib_node_t * node)
490{
491  ASSERT (0);
492}
493
494
495/*
496 * Virtual function table registered by MPLS GRE tunnels
497 * for participation in the FIB object graph.
498 */
499const static fib_node_vft_t nsh_md2_ioam_vft = {
500  .fnv_get = nsh_md2_ioam_fib_node_get,
501  .fnv_last_lock = nsh_md2_ioam_last_lock_gone,
502  .fnv_back_walk = nsh_md2_ioam_back_walk,
503};
504
505void
506nsh_md2_ioam_interface_init (void)
507{
508  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
509  hm->fib_entry_type = fib_node_register_new_type (&nsh_md2_ioam_vft);
510  return;
511}
512
513