Line data Source code
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 0 : 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 0 : 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 0 : return;
58 : }
59 :
60 : void
61 0 : nsh_md2_ioam_clear_output_feature_on_all_intfs (vlib_main_t * vm)
62 : {
63 0 : vnet_sw_interface_t *si = 0;
64 0 : vnet_main_t *vnm = vnet_get_main ();
65 0 : vnet_interface_main_t *im = &vnm->interface_main;
66 :
67 0 : pool_foreach (si, im->sw_interfaces)
68 : {
69 0 : nsh_md2_ioam_set_clear_output_feature_on_intf
70 : (vm, si->sw_if_index, 0);
71 : }
72 0 : 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 0 : 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 0 : nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
86 0 : u32 fib_index0 = 0;
87 :
88 0 : fib_node_index_t fei = ~0;
89 0 : 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 0 : u32 *intf_list = NULL;
99 : fib_prefix_t fib_prefix;
100 :
101 0 : if (is_ipv4)
102 : {
103 0 : clib_memset (&fib_prefix, 0, sizeof (fib_prefix_t));
104 0 : fib_prefix.fp_len = 32;
105 0 : fib_prefix.fp_proto = FIB_PROTOCOL_IP4;
106 : #define TRANSIT_UNIT_TEST_HACK 1
107 : #ifdef TRANSIT_UNIT_TEST_HACK
108 0 : clib_memset(&dst_addr, 0, sizeof(dst_addr));
109 0 : dst_addr.ip4.as_u32 = clib_net_to_host_u32(0x14020102);
110 : #endif
111 0 : fib_prefix.fp_addr = dst_addr;
112 : }
113 : else
114 : {
115 0 : return 0;
116 : }
117 :
118 0 : 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 0 : vec_add1(intf_list, fib_path_get_resolving_interface(fei));
202 0 : vec_foreach(sw_if_index0, intf_list)
203 0 : if (is_add)
204 : {
205 0 : 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 0 : vec_validate_init_empty (hm->bool_ref_by_sw_if_index,
213 : *sw_if_index0, ~0);
214 0 : hm->bool_ref_by_sw_if_index[*sw_if_index0] = 1;
215 : }
216 : else
217 : {
218 0 : hm->bool_ref_by_sw_if_index[*sw_if_index0] = ~0;
219 : }
220 :
221 : #endif
222 :
223 0 : if (is_ipv4)
224 : {
225 0 : uword *t = NULL;
226 : nsh_md2_ioam_dest_tunnels_t *t1;
227 : fib_prefix_t key4, *key4_copy;
228 : hash_pair_t *hp;
229 0 : clib_memset (&key4, 0, sizeof (key4));
230 0 : key4.fp_proto = FIB_PROTOCOL_IP4;
231 0 : key4.fp_addr.ip4.as_u32 = fib_prefix.fp_addr.ip4.as_u32;
232 0 : t = hash_get_mem (hm->dst_by_ip4, &key4);
233 0 : if (is_add)
234 : {
235 0 : if (t)
236 : {
237 0 : return 0;
238 : }
239 0 : pool_get_aligned (hm->dst_tunnels, t1, CLIB_CACHE_LINE_BYTES);
240 0 : clib_memset (t1, 0, sizeof (*t1));
241 0 : t1->fp_proto = FIB_PROTOCOL_IP4;
242 0 : t1->dst_addr.ip4.as_u32 = fib_prefix.fp_addr.ip4.as_u32;
243 0 : key4_copy = clib_mem_alloc (sizeof (*key4_copy));
244 0 : clib_memset(key4_copy, 0, sizeof(*key4_copy));
245 0 : clib_memcpy_fast (key4_copy, &key4, sizeof (*key4_copy));
246 0 : 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 0 : const fib_prefix_t tun_dst_pfx = {
255 : .fp_len = 32,
256 : .fp_proto = FIB_PROTOCOL_IP4,
257 0 : .fp_addr = {.ip4 = t1->dst_addr.ip4,}
258 : };
259 :
260 0 : t1->fib_entry_index =
261 0 : fib_table_entry_special_add (outer_fib_index,
262 : &tun_dst_pfx,
263 : FIB_SOURCE_RR,
264 : FIB_ENTRY_FLAG_NONE);
265 0 : t1->sibling_index =
266 0 : fib_entry_child_add (t1->fib_entry_index,
267 0 : hm->fib_entry_type, t1 - hm->dst_tunnels);
268 0 : t1->outer_fib_index = outer_fib_index;
269 :
270 : }
271 : else
272 : {
273 0 : if (!t)
274 : {
275 0 : return 0;
276 : }
277 0 : t1 = pool_elt_at_index (hm->dst_tunnels, t[0]);
278 0 : hp = hash_get_pair (hm->dst_by_ip4, &key4);
279 0 : key4_copy = (void *) (hp->key);
280 0 : hash_unset_mem (hm->dst_by_ip4, &key4);
281 0 : clib_mem_free (key4_copy);
282 0 : pool_put (hm->dst_tunnels, t1);
283 : }
284 : }
285 : else
286 : {
287 : // TBD for IPv6
288 : }
289 :
290 0 : return 0;
291 : }
292 :
293 : void
294 0 : nsh_md2_ioam_refresh_output_feature_on_all_dest (void)
295 : {
296 0 : nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
297 0 : nsh_main_t *gm = &nsh_main;
298 : nsh_md2_ioam_dest_tunnels_t *t;
299 : u32 i;
300 :
301 0 : if (pool_elts (hm->dst_tunnels) == 0)
302 0 : return;
303 :
304 0 : nsh_md2_ioam_clear_output_feature_on_all_intfs (gm->vlib_main);
305 0 : i = vec_len (hm->bool_ref_by_sw_if_index);
306 0 : vec_free (hm->bool_ref_by_sw_if_index);
307 0 : vec_validate_init_empty (hm->bool_ref_by_sw_if_index, i, ~0);
308 0 : pool_foreach (t, hm->dst_tunnels)
309 : {
310 0 : nsh_md2_ioam_enable_disable_for_dest
311 : (gm->vlib_main,
312 : t->dst_addr,
313 : t->outer_fib_index,
314 0 : (t->fp_proto == FIB_PROTOCOL_IP4), 1
315 : /* is_add */
316 : );
317 : }
318 0 : return;
319 : }
320 :
321 : void
322 0 : nsh_md2_ioam_clear_output_feature_on_select_intfs (void)
323 : {
324 0 : nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
325 0 : nsh_main_t *gm = &nsh_main;
326 :
327 0 : u32 sw_if_index0 = 0;
328 0 : for (sw_if_index0 = 0;
329 0 : sw_if_index0 < vec_len (hm->bool_ref_by_sw_if_index); sw_if_index0++)
330 : {
331 0 : if (hm->bool_ref_by_sw_if_index[sw_if_index0] == 0xFF)
332 : {
333 0 : nsh_md2_ioam_set_clear_output_feature_on_intf
334 : (gm->vlib_main, sw_if_index0, 0);
335 : }
336 : }
337 :
338 0 : return;
339 : }
340 :
341 :
342 :
343 :
344 : clib_error_t *
345 0 : nsh_md2_ioam_enable_disable (int has_trace_option, int has_pot_option,
346 : int has_ppc_option)
347 : {
348 0 : nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
349 :
350 0 : hm->has_trace_option = has_trace_option;
351 0 : hm->has_pot_option = has_pot_option;
352 0 : hm->has_ppc_option = has_ppc_option;
353 :
354 0 : if (hm->has_trace_option)
355 : {
356 0 : nsh_md2_ioam_trace_profile_setup ();
357 : }
358 0 : else if (!hm->has_trace_option)
359 : {
360 0 : nsh_md2_ioam_trace_profile_cleanup ();
361 : }
362 :
363 0 : return 0;
364 : }
365 :
366 :
367 0 : int nsh_md2_ioam_disable_for_dest
368 : (vlib_main_t * vm, ip46_address_t dst_addr, u32 outer_fib_index,
369 : u8 ipv4_set)
370 : {
371 : nsh_md2_ioam_dest_tunnels_t *t;
372 0 : nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
373 0 : nsh_main_t *gm = &nsh_main;
374 :
375 0 : nsh_md2_ioam_enable_disable_for_dest (gm->vlib_main,
376 : dst_addr, outer_fib_index,
377 : ipv4_set, 0);
378 0 : if (pool_elts (hm->dst_tunnels) == 0)
379 : {
380 0 : nsh_md2_ioam_clear_output_feature_on_select_intfs ();
381 0 : return 0;
382 : }
383 :
384 0 : pool_foreach (t, hm->dst_tunnels)
385 : {
386 0 : nsh_md2_ioam_enable_disable_for_dest
387 : (gm->vlib_main,
388 : t->dst_addr,
389 : t->outer_fib_index,
390 0 : (t->fp_proto ==
391 : FIB_PROTOCOL_IP4), 1 /* is_add */ );
392 : }
393 0 : nsh_md2_ioam_clear_output_feature_on_select_intfs ();
394 0 : return (0);
395 :
396 : }
397 :
398 0 : 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)
400 : {
401 0 : nsh_main_t *gm = &nsh_main;
402 : ip46_address_t dst_addr;
403 0 : u8 dst_addr_set = 0;
404 0 : u8 ipv4_set = 0;
405 0 : u8 ipv6_set = 0;
406 0 : u8 disable = 0;
407 0 : clib_error_t *rv = 0;
408 0 : u32 outer_fib_index = 0;
409 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
410 : {
411 0 : if (unformat (input, "dst-ip %U", unformat_ip4_address, &dst_addr.ip4))
412 : {
413 0 : dst_addr_set = 1;
414 0 : ipv4_set = 1;
415 : }
416 : else
417 0 : if (unformat
418 : (input, "dst-ip %U", unformat_ip6_address, &dst_addr.ip6))
419 : {
420 0 : dst_addr_set = 1;
421 0 : ipv6_set = 1;
422 : }
423 0 : else if (unformat (input, "outer-fib-index %d", &outer_fib_index))
424 : {
425 : }
426 :
427 0 : else if (unformat (input, "disable"))
428 0 : disable = 1;
429 : else
430 0 : break;
431 : }
432 :
433 0 : if (dst_addr_set == 0)
434 0 : return clib_error_return (0,
435 : "LISP-GPE Tunnel destination address not specified");
436 0 : if (ipv4_set && ipv6_set)
437 0 : return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
438 0 : if (!disable)
439 : {
440 0 : nsh_md2_ioam_enable_disable_for_dest (gm->vlib_main,
441 : dst_addr, outer_fib_index,
442 : ipv4_set, 1);
443 : }
444 : else
445 : {
446 0 : nsh_md2_ioam_disable_for_dest
447 : (vm, dst_addr, outer_fib_index, ipv4_set);
448 : }
449 0 : return rv;
450 : }
451 :
452 : /* *INDENT-OFF* */
453 58901 : 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,
457 : };
458 :
459 : /**
460 : * Function definition to backwalk a FIB node
461 : */
462 : static fib_node_back_walk_rc_t
463 0 : nsh_md2_ioam_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
464 : {
465 0 : nsh_md2_ioam_refresh_output_feature_on_all_dest ();
466 0 : return (FIB_NODE_BACK_WALK_CONTINUE);
467 : }
468 :
469 : /**
470 : * Function definition to get a FIB node from its index
471 : */
472 : static fib_node_t *
473 0 : nsh_md2_ioam_fib_node_get (fib_node_index_t index)
474 : {
475 0 : nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
476 0 : return (&hm->node);
477 : }
478 :
479 : /**
480 : * Function definition to inform the FIB node that its last lock has gone.
481 : */
482 : static void
483 0 : nsh_md2_ioam_last_lock_gone (fib_node_t * node)
484 : {
485 0 : ASSERT (0);
486 0 : }
487 :
488 :
489 : /*
490 : * Virtual function table registered by MPLS GRE tunnels
491 : * for participation in the FIB object graph.
492 : */
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,
497 : };
498 :
499 : void
500 575 : nsh_md2_ioam_interface_init (void)
501 : {
502 575 : nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
503 575 : hm->fib_entry_type = fib_node_register_new_type ("nsh", &nsh_md2_ioam_vft);
504 575 : return;
505 : }
506 :
|