blob: e30425cda4e85a3d11c6cde0909e90044efbfb3b [file] [log] [blame]
Neale Rannsd792d9c2017-10-21 10:53:20 -07001/*
2 * Copyright (c) 2016 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <vnet/fib/fib_entry.h>
17#include <vnet/fib/fib_table.h>
18#include <vnet/fib/fib_walk.h>
19
20#include <vnet/bier/bier_table.h>
21#include <vnet/bier/bier_fmask.h>
22#include <vnet/bier/bier_bit_string.h>
23#include <vnet/bier/bier_disp_table.h>
24
25#include <vnet/mpls/mpls.h>
26#include <vnet/dpo/drop_dpo.h>
27#include <vnet/dpo/load_balance.h>
28
29/*
30 * attributes names for formatting
31 */
32static const char *const bier_fmask_attr_names[] = BIER_FMASK_ATTR_NAMES;
33
34/*
35 * pool of BIER fmask objects
36 */
37bier_fmask_t *bier_fmask_pool;
38
39static inline index_t
40bier_fmask_get_index (const bier_fmask_t *bfm)
41{
42 return (bfm - bier_fmask_pool);
43}
44
45static void
46bier_fmask_bits_init (bier_fmask_bits_t *bits,
47 bier_hdr_len_id_t hlid)
48{
49 bits->bfmb_refs = clib_mem_alloc(sizeof(bits->bfmb_refs[0]) *
50 bier_hdr_len_id_to_num_bits(hlid));
51 memset(bits->bfmb_refs,
52 0,
53 (sizeof(bits->bfmb_refs[0]) *
54 bier_hdr_len_id_to_num_bits(hlid)));
55
56 bits->bfmb_input_reset_string.bbs_len =
57 bier_hdr_len_id_to_num_buckets(hlid);
58
59 /*
60 * The buckets are accessed in the switch path
61 */
62 bits->bfmb_input_reset_string.bbs_buckets =
63 clib_mem_alloc_aligned(
64 sizeof(bits->bfmb_input_reset_string.bbs_buckets[0]) *
65 bier_hdr_len_id_to_num_buckets(hlid),
66 CLIB_CACHE_LINE_BYTES);
67 memset(bits->bfmb_input_reset_string.bbs_buckets,
68 0,
69 sizeof(bits->bfmb_input_reset_string.bbs_buckets[0]) *
70 bier_hdr_len_id_to_num_buckets(hlid));
71}
72
73static void
74bier_fmask_stack (bier_fmask_t *bfm)
75{
76 dpo_id_t via_dpo = DPO_INVALID;
77
78 if (bfm->bfm_flags & BIER_FMASK_FLAG_DISP)
79 {
80 bier_disp_table_contribute_forwarding(bfm->bfm_disp,
81 &via_dpo);
82 }
83 else
84 {
85 fib_entry_contribute_forwarding(bfm->bfm_fei,
86 FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
87 &via_dpo);
88 }
89
90 /*
91 * If the via fib entry provides no forwarding (i.e. a drop)
92 * then niether does this fmask. That way children consider this fmask
93 * unresolved and other ECMP options are used instead.
94 */
95 if (dpo_is_drop(&via_dpo) ||
96 load_balance_is_drop(&via_dpo))
97 {
98 bfm->bfm_flags &= ~BIER_FMASK_FLAG_FORWARDING;
99 }
100 else
101 {
102 bfm->bfm_flags |= BIER_FMASK_FLAG_FORWARDING;
103 }
104
105 dpo_stack(DPO_BIER_FMASK,
106 DPO_PROTO_BIER,
107 &bfm->bfm_dpo,
108 &via_dpo);
109 dpo_reset(&via_dpo);
110}
111
112void
113bier_fmask_contribute_forwarding (index_t bfmi,
114 dpo_id_t *dpo)
115{
116 bier_fmask_t *bfm;
117
118 bfm = bier_fmask_get(bfmi);
119
120 if (bfm->bfm_flags & BIER_FMASK_FLAG_FORWARDING)
121 {
122 dpo_set(dpo,
123 DPO_BIER_FMASK,
124 DPO_PROTO_BIER,
125 bfmi);
126 }
127 else
128 {
129 dpo_copy(dpo, drop_dpo_get(DPO_PROTO_BIER));
130 }
131}
132
133static void
134bier_fmask_resolve (bier_fmask_t *bfm)
135{
136 if (bfm->bfm_flags & BIER_FMASK_FLAG_DISP)
137 {
138 bier_disp_table_lock(bfm->bfm_disp);
139 }
140 else
141 {
142 /*
143 * source a recursive route through which we resolve.
144 */
145 fib_prefix_t pfx = {
146 .fp_addr = bfm->bfm_id.bfmi_nh,
147 .fp_proto = (ip46_address_is_ip4(&(bfm->bfm_id.bfmi_nh)) ?
148 FIB_PROTOCOL_IP4 :
149 FIB_PROTOCOL_IP6),
150 .fp_len = (ip46_address_is_ip4(&(bfm->bfm_id.bfmi_nh)) ? 32 : 128),
151 };
152
153 bfm->bfm_fei = fib_table_entry_special_add(0, // default table
154 &pfx,
155 FIB_SOURCE_RR,
156 FIB_ENTRY_FLAG_NONE);
157
158 bfm->bfm_sibling = fib_entry_child_add(bfm->bfm_fei,
159 FIB_NODE_TYPE_BIER_FMASK,
160 bier_fmask_get_index(bfm));
161 }
162
163 bier_fmask_stack(bfm);
164}
165
166static void
167bier_fmask_unresolve (bier_fmask_t *bfm)
168{
169 if (bfm->bfm_flags & BIER_FMASK_FLAG_DISP)
170 {
171 bier_disp_table_unlock(bfm->bfm_disp);
172 }
173 else
174 {
175 /*
176 * un-source the recursive route through which we resolve.
177 */
178 fib_prefix_t pfx = {
179 .fp_addr = bfm->bfm_id.bfmi_nh,
180 .fp_proto = (ip46_address_is_ip4(&(bfm->bfm_id.bfmi_nh)) ?
181 FIB_PROTOCOL_IP4 :
182 FIB_PROTOCOL_IP6),
183 .fp_len = (ip46_address_is_ip4(&(bfm->bfm_id.bfmi_nh)) ? 32 : 128),
184 };
185
186 fib_entry_child_remove(bfm->bfm_fei, bfm->bfm_sibling);
187 fib_table_entry_special_remove(0, &pfx, FIB_SOURCE_RR);
188 }
189 dpo_reset(&bfm->bfm_dpo);
190}
191
192u32
193bier_fmask_child_add (fib_node_index_t bfmi,
194 fib_node_type_t child_type,
195 fib_node_index_t child_index)
196{
197 return (fib_node_child_add(FIB_NODE_TYPE_BIER_FMASK,
198 bfmi,
199 child_type,
200 child_index));
201};
202
203void
204bier_fmask_child_remove (fib_node_index_t bfmi,
205 u32 sibling_index)
206{
207 fib_node_child_remove(FIB_NODE_TYPE_BIER_FMASK,
208 bfmi,
209 sibling_index);
210}
211
212static void
213bier_fmask_init (bier_fmask_t *bfm,
214 const bier_fmask_id_t *fmid,
215 index_t bti,
216 const fib_route_path_t *rpath)
217{
218 const bier_table_id_t *btid;
219 mpls_label_t olabel;
220
221 bfm->bfm_id = *fmid;
222 bfm->bfm_fib_index = bti;
223 dpo_reset(&bfm->bfm_dpo);
224
225 if (ip46_address_is_zero(&(bfm->bfm_id.bfmi_nh)))
226 {
227 bfm->bfm_flags |= BIER_FMASK_FLAG_DISP;
228 }
229
230 if (!(bfm->bfm_flags & BIER_FMASK_FLAG_DISP))
231 {
232 olabel = rpath->frp_label_stack[0];
233 vnet_mpls_uc_set_label(&bfm->bfm_label, olabel);
234 vnet_mpls_uc_set_exp(&bfm->bfm_label, 0);
235 vnet_mpls_uc_set_s(&bfm->bfm_label, 1);
236 vnet_mpls_uc_set_ttl(&bfm->bfm_label, 0xff);
237 bfm->bfm_label = clib_host_to_net_u32(bfm->bfm_label);
238 }
239 else
240 {
241 bfm->bfm_disp = rpath->frp_bier_fib_index;
242 }
243
244 btid = bier_table_get_id(bfm->bfm_fib_index);
245 bier_fmask_bits_init(&bfm->bfm_bits, btid->bti_hdr_len);
246 bier_fmask_resolve(bfm);
247}
248
249static void
250bier_fmask_destroy (bier_fmask_t *bfm)
251{
252 clib_mem_free(bfm->bfm_bits.bfmb_refs);
253 clib_mem_free(bfm->bfm_bits.bfmb_input_reset_string.bbs_buckets);
254
255 bier_fmask_db_remove(bfm->bfm_fib_index, &(bfm->bfm_id));
256 bier_fmask_unresolve(bfm);
257 pool_put(bier_fmask_pool, bfm);
258}
259
260void
261bier_fmask_unlock (index_t bfmi)
262{
263 bier_fmask_t *bfm;
264
265 if (INDEX_INVALID == bfmi)
266 {
267 return;
268 }
269
270 bfm = bier_fmask_get(bfmi);
271
272 fib_node_unlock(&bfm->bfm_node);
273}
274
275void
276bier_fmask_lock (index_t bfmi)
277{
278 bier_fmask_t *bfm;
279
280 if (INDEX_INVALID == bfmi)
281 {
282 return;
283 }
284
285 bfm = bier_fmask_get(bfmi);
286
287 fib_node_lock(&bfm->bfm_node);
288}
289
290index_t
291bier_fmask_create_and_lock (const bier_fmask_id_t *fmid,
292 index_t bti,
293 const fib_route_path_t *rpath)
294{
295 bier_fmask_t *bfm;
296
297 pool_get_aligned(bier_fmask_pool, bfm, CLIB_CACHE_LINE_BYTES);
298
299 memset(bfm, 0, sizeof(*bfm));
300
301 fib_node_init(&bfm->bfm_node, FIB_NODE_TYPE_BIER_FMASK);
302 bier_fmask_init(bfm, fmid, bti, rpath);
303
304 bier_fmask_lock(bier_fmask_get_index(bfm));
305
306 return (bier_fmask_get_index(bfm));
307}
308
309void
310bier_fmask_link (index_t bfmi,
311 bier_bp_t bp)
312{
313 bier_fmask_t *bfm;
314
315 bfm = bier_fmask_get(bfmi);
316
317 if (0 == bfm->bfm_bits.bfmb_refs[BIER_BP_TO_INDEX(bp)])
318 {
319 /*
320 * 0 -> 1 transistion - set the bit in the string
321 */
322 bier_bit_string_set_bit(&bfm->bfm_bits.bfmb_input_reset_string, bp);
323 }
324
325 ++bfm->bfm_bits.bfmb_refs[BIER_BP_TO_INDEX(bp)];
326 ++bfm->bfm_bits.bfmb_count;
327}
328
329void
330bier_fmask_unlink (index_t bfmi,
331 bier_bp_t bp)
332{
333 bier_fmask_t *bfm;
334
335 bfm = bier_fmask_get(bfmi);
336
337 --bfm->bfm_bits.bfmb_refs[BIER_BP_TO_INDEX(bp)];
338 --bfm->bfm_bits.bfmb_count;
339
340 if (0 == bfm->bfm_bits.bfmb_refs[BIER_BP_TO_INDEX(bp)])
341 {
342 /*
343 * 1 -> 0 transistion - clear the bit in the string
344 */
345 bier_bit_string_clear_bit(&bfm->bfm_bits.bfmb_input_reset_string, bp);
346 }
347}
348
349u8*
350format_bier_fmask (u8 *s, va_list *ap)
351{
352 index_t bfmi = va_arg(*ap, index_t);
353 u32 indent = va_arg(*ap, u32);
354 bier_fmask_attributes_t attr;
355 bier_fmask_t *bfm;
356
357 if (pool_is_free_index(bier_fmask_pool, bfmi))
358 {
359 return (format(s, "No BIER f-mask %d", bfmi));
360 }
361
362 bfm = bier_fmask_get(bfmi);
363
364 s = format(s, "fmask: nh:%U bs:%U locks:%d ",
365 format_ip46_address, &bfm->bfm_id.bfmi_nh, IP46_TYPE_ANY,
366 format_bier_bit_string, &bfm->bfm_bits.bfmb_input_reset_string,
367 bfm->bfm_node.fn_locks);
368 s = format(s, "flags:");
369 FOR_EACH_BIER_FMASK_ATTR(attr) {
370 if ((1<<attr) & bfm->bfm_flags) {
371 s = format (s, "%s,", bier_fmask_attr_names[attr]);
372 }
373 }
374 s = format(s, "\n%U%U",
375 format_white_space, indent,
376 format_dpo_id, &bfm->bfm_dpo, indent+2);
377
378 return (s);
379}
380
381
382static fib_node_t *
383bier_fmask_get_node (fib_node_index_t index)
384{
385 bier_fmask_t *bfm = bier_fmask_get(index);
386 return (&(bfm->bfm_node));
387}
388
389static bier_fmask_t*
390bier_fmask_get_from_node (fib_node_t *node)
391{
392 return ((bier_fmask_t*)(((char*)node) -
393 STRUCT_OFFSET_OF(bier_fmask_t,
394 bfm_node)));
395}
396
397/*
398 * bier_fmask_last_lock_gone
399 */
400static void
401bier_fmask_last_lock_gone (fib_node_t *node)
402{
403 bier_fmask_destroy(bier_fmask_get_from_node(node));
404}
405
406/*
407 * bier_fmask_back_walk_notify
408 *
409 * A back walk has reached this BIER fmask
410 */
411static fib_node_back_walk_rc_t
412bier_fmask_back_walk_notify (fib_node_t *node,
413 fib_node_back_walk_ctx_t *ctx)
414{
415 /*
416 * re-stack the fmask on the n-eos of the via
417 */
418 bier_fmask_t *bfm = bier_fmask_get_from_node(node);
419
420 bier_fmask_stack(bfm);
421
422 /*
423 * propagate further up the graph.
424 * we can do this synchronously since the fan out is small.
425 */
426 fib_walk_sync(FIB_NODE_TYPE_BIER_FMASK, bier_fmask_get_index(bfm), ctx);
427
428 return (FIB_NODE_BACK_WALK_CONTINUE);
429}
430
431/*
432 * The BIER fmask's graph node virtual function table
433 */
434static const fib_node_vft_t bier_fmask_vft = {
435 .fnv_get = bier_fmask_get_node,
436 .fnv_last_lock = bier_fmask_last_lock_gone,
437 .fnv_back_walk = bier_fmask_back_walk_notify,
438};
439
440static void
441bier_fmask_dpo_lock (dpo_id_t *dpo)
442{
443}
444
445static void
446bier_fmask_dpo_unlock (dpo_id_t *dpo)
447{
448}
449
450static void
451bier_fmask_dpo_mem_show (void)
452{
453 fib_show_memory_usage("BIER-fmask",
454 pool_elts(bier_fmask_pool),
455 pool_len(bier_fmask_pool),
456 sizeof(bier_fmask_t));
457}
458
459const static dpo_vft_t bier_fmask_dpo_vft = {
460 .dv_lock = bier_fmask_dpo_lock,
461 .dv_unlock = bier_fmask_dpo_unlock,
462 .dv_mem_show = bier_fmask_dpo_mem_show,
463 .dv_format = format_bier_fmask,
464};
465
466const static char *const bier_fmask_mpls_nodes[] =
467{
468 "bier-output"
469};
470const static char * const * const bier_fmask_nodes[DPO_PROTO_NUM] =
471{
472 [DPO_PROTO_BIER] = bier_fmask_mpls_nodes,
473 [DPO_PROTO_MPLS] = bier_fmask_mpls_nodes,
474};
475
476clib_error_t *
477bier_fmask_module_init (vlib_main_t * vm)
478{
479 fib_node_register_type (FIB_NODE_TYPE_BIER_FMASK, &bier_fmask_vft);
480 dpo_register(DPO_BIER_FMASK, &bier_fmask_dpo_vft, bier_fmask_nodes);
481
482 return (NULL);
483}
484
485VLIB_INIT_FUNCTION (bier_fmask_module_init);
486
487static clib_error_t *
488bier_fmask_show (vlib_main_t * vm,
489 unformat_input_t * input,
490 vlib_cli_command_t * cmd)
491{
492 bier_fmask_t *bfm;
493 index_t bfmi;
494
495 bfmi = INDEX_INVALID;
496
497 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
498 if (unformat (input, "%d", &bfmi))
499 {
500 ;
501 } else
502 {
503 break;
504 }
505 }
506
507 if (INDEX_INVALID == bfmi)
508 {
509 pool_foreach(bfm, bier_fmask_pool,
510 ({
511 vlib_cli_output (vm, "%U",
512 format_bier_fmask, bier_fmask_get_index(bfm), 0);
513 }));
514 }
515 else
516 {
517 vlib_cli_output (vm, "%U", format_bier_fmask, bfmi, 0);
518 }
519
520 return (NULL);
521}
522
523VLIB_CLI_COMMAND (show_bier_fmask, static) = {
524 .path = "show bier fmask",
525 .short_help = "show bier fmask",
526 .function = bier_fmask_show,
527};