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