2 * nsh_md2_ioam.c - NSH iOAM functions for MD type 2
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:
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <vnet/vnet.h>
19 #include <vnet/plugin/plugin.h>
21 #include <nsh/nsh_packet.h>
22 #include <vnet/ip/ip.h>
23 #include <nsh/nsh-md2-ioam/nsh_md2_ioam.h>
25 #include <vlibapi/api.h>
26 #include <vlibmemory/api.h>
28 #include <vnet/fib/ip6_fib.h>
29 #include <vnet/fib/ip4_fib.h>
30 #include <vnet/fib/fib_entry.h>
32 /* define message structures */
34 #include <nsh/nsh.api.h>
37 /* define generated endian-swappers */
39 #include <nsh/nsh.api.h>
42 nsh_md2_ioam_main_t nsh_md2_ioam_main;
45 nsh_md2_ioam_set_clear_output_feature_on_intf (vlib_main_t * vm,
52 vnet_feature_enable_disable ("ip4-output",
53 "nsh-md2-ioam-encap-transit",
55 0 /* void *feature_config */ ,
56 0 /* u32 n_feature_config_bytes */ );
61 nsh_md2_ioam_clear_output_feature_on_all_intfs (vlib_main_t * vm)
63 vnet_sw_interface_t *si = 0;
64 vnet_main_t *vnm = vnet_get_main ();
65 vnet_interface_main_t *im = &vnm->interface_main;
67 pool_foreach (si, im->sw_interfaces)
69 nsh_md2_ioam_set_clear_output_feature_on_intf
70 (vm, si->sw_if_index, 0);
76 extern fib_forward_chain_type_t
77 fib_entry_get_default_chain_type (const fib_entry_t * fib_entry);
80 nsh_md2_ioam_enable_disable_for_dest (vlib_main_t * vm,
81 ip46_address_t dst_addr,
83 u8 is_ipv4, u8 is_add)
85 nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
88 fib_node_index_t fei = ~0;
89 u32 *sw_if_index0 = NULL;
91 fib_entry_t *fib_entry;
94 load_balance_t *lb_m, *lb_b;
95 const dpo_id_t *dpo0, *dpo1;
98 u32 *intf_list = NULL;
99 fib_prefix_t fib_prefix;
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);
111 fib_prefix.fp_addr = dst_addr;
118 fei = fib_table_lookup (fib_index0, &fib_prefix);
120 fib_entry = fib_entry_get (fei);
123 if (!dpo_id_is_valid (&fib_entry->fe_lb))
128 lb_m = load_balance_get (fib_entry->fe_lb.dpoi_index);
130 for (i = 0; i < lb_m->lb_n_buckets; i++)
132 dpo0 = load_balance_get_bucket_i (lb_m, i);
134 if (dpo0->dpoi_type == DPO_LOAD_BALANCE ||
135 dpo0->dpoi_type == DPO_ADJACENCY)
137 if (dpo0->dpoi_type == DPO_ADJACENCY)
143 lb_b = load_balance_get (dpo0->dpoi_index);
144 k = lb_b->lb_n_buckets;
147 for (j = 0; j < k; j++)
149 if (dpo0->dpoi_type == DPO_ADJACENCY)
155 dpo1 = load_balance_get_bucket_i (lb_b, j);
158 if (dpo1->dpoi_type == DPO_ADJACENCY)
160 adj_index0 = dpo1->dpoi_index;
162 if (ADJ_INDEX_INVALID == adj_index0)
168 ip_get_adjacency (&(ip4_main.lookup_main), adj_index0);
169 sw_if_index0 = adj0->rewrite_header.sw_if_index;
171 if (~0 == sw_if_index0)
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 */
186 vec_validate_init_empty (hm->bool_ref_by_sw_if_index,
188 hm->bool_ref_by_sw_if_index[sw_if_index0] = 1;
192 hm->bool_ref_by_sw_if_index[sw_if_index0] = ~0;
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)
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 */
212 vec_validate_init_empty (hm->bool_ref_by_sw_if_index,
214 hm->bool_ref_by_sw_if_index[*sw_if_index0] = 1;
218 hm->bool_ref_by_sw_if_index[*sw_if_index0] = ~0;
226 nsh_md2_ioam_dest_tunnels_t *t1;
227 fib_prefix_t key4, *key4_copy;
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);
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_fast (key4_copy, &key4, sizeof (*key4_copy));
246 hash_set_mem (hm->dst_by_ip4, key4_copy, t1 - hm->dst_tunnels);
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.
254 const fib_prefix_t tun_dst_pfx = {
256 .fp_proto = FIB_PROTOCOL_IP4,
257 .fp_addr = {.ip4 = t1->dst_addr.ip4,}
260 t1->fib_entry_index =
261 fib_table_entry_special_add (outer_fib_index,
264 FIB_ENTRY_FLAG_NONE);
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;
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);
294 nsh_md2_ioam_refresh_output_feature_on_all_dest (void)
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;
301 if (pool_elts (hm->dst_tunnels) == 0)
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)
310 nsh_md2_ioam_enable_disable_for_dest
314 (t->fp_proto == FIB_PROTOCOL_IP4), 1
322 nsh_md2_ioam_clear_output_feature_on_select_intfs (void)
324 nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
325 nsh_main_t *gm = &nsh_main;
327 u32 sw_if_index0 = 0;
328 for (sw_if_index0 = 0;
329 sw_if_index0 < vec_len (hm->bool_ref_by_sw_if_index); sw_if_index0++)
331 if (hm->bool_ref_by_sw_if_index[sw_if_index0] == 0xFF)
333 nsh_md2_ioam_set_clear_output_feature_on_intf
334 (gm->vlib_main, sw_if_index0, 0);
345 nsh_md2_ioam_enable_disable (int has_trace_option, int has_pot_option,
348 nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
350 hm->has_trace_option = has_trace_option;
351 hm->has_pot_option = has_pot_option;
352 hm->has_ppc_option = has_ppc_option;
354 if (hm->has_trace_option)
356 nsh_md2_ioam_trace_profile_setup ();
358 else if (!hm->has_trace_option)
360 nsh_md2_ioam_trace_profile_cleanup ();
367 int nsh_md2_ioam_disable_for_dest
368 (vlib_main_t * vm, ip46_address_t dst_addr, u32 outer_fib_index,
371 nsh_md2_ioam_dest_tunnels_t *t;
372 nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
373 nsh_main_t *gm = &nsh_main;
375 nsh_md2_ioam_enable_disable_for_dest (gm->vlib_main,
376 dst_addr, outer_fib_index,
378 if (pool_elts (hm->dst_tunnels) == 0)
380 nsh_md2_ioam_clear_output_feature_on_select_intfs ();
384 pool_foreach (t, hm->dst_tunnels)
386 nsh_md2_ioam_enable_disable_for_dest
391 FIB_PROTOCOL_IP4), 1 /* is_add */ );
393 nsh_md2_ioam_clear_output_feature_on_select_intfs ();
398 static clib_error_t *nsh_md2_ioam_set_transit_rewrite_command_fn
399 (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
401 nsh_main_t *gm = &nsh_main;
402 ip46_address_t dst_addr;
407 clib_error_t *rv = 0;
408 u32 outer_fib_index = 0;
409 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
411 if (unformat (input, "dst-ip %U", unformat_ip4_address, &dst_addr.ip4))
418 (input, "dst-ip %U", unformat_ip6_address, &dst_addr.ip6))
423 else if (unformat (input, "outer-fib-index %d", &outer_fib_index))
427 else if (unformat (input, "disable"))
433 if (dst_addr_set == 0)
434 return clib_error_return (0,
435 "LISP-GPE Tunnel destination address not specified");
436 if (ipv4_set && ipv6_set)
437 return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
440 nsh_md2_ioam_enable_disable_for_dest (gm->vlib_main,
441 dst_addr, outer_fib_index,
446 nsh_md2_ioam_disable_for_dest
447 (vm, dst_addr, outer_fib_index, ipv4_set);
453 VLIB_CLI_COMMAND (nsh_md2_ioam_set_transit_rewrite_cmd, static) = {
454 .path = "set nsh-md2-ioam-transit",
455 .short_help = "set nsh-ioam-lisp-gpe-transit dst-ip <dst_ip> [outer-fib-index <outer_fib_index>] [disable]",
456 .function = nsh_md2_ioam_set_transit_rewrite_command_fn,
460 * Function definition to backwalk a FIB node
462 static fib_node_back_walk_rc_t
463 nsh_md2_ioam_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
465 nsh_md2_ioam_refresh_output_feature_on_all_dest ();
466 return (FIB_NODE_BACK_WALK_CONTINUE);
470 * Function definition to get a FIB node from its index
473 nsh_md2_ioam_fib_node_get (fib_node_index_t index)
475 nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
480 * Function definition to inform the FIB node that its last lock has gone.
483 nsh_md2_ioam_last_lock_gone (fib_node_t * node)
490 * Virtual function table registered by MPLS GRE tunnels
491 * for participation in the FIB object graph.
493 const static fib_node_vft_t nsh_md2_ioam_vft = {
494 .fnv_get = nsh_md2_ioam_fib_node_get,
495 .fnv_last_lock = nsh_md2_ioam_last_lock_gone,
496 .fnv_back_walk = nsh_md2_ioam_back_walk,
500 nsh_md2_ioam_interface_init (void)
502 nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
503 hm->fib_entry_type = fib_node_register_new_type (&nsh_md2_ioam_vft);