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