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