BIER neighbor stats
[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 *rpaths)
169 {
170     const bier_table_id_t *btid;
171     mpls_label_t olabel;
172
173     ASSERT(1 == vec_len(rpaths));
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 != rpaths->frp_label_stack)
192         {
193             olabel = rpaths->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     bfm->bfm_pl = fib_path_list_create((FIB_PATH_LIST_FLAG_SHARED |
224                                         FIB_PATH_LIST_FLAG_NO_URPF),
225                                        rpaths);
226     bfm->bfm_sibling = fib_path_list_child_add(bfm->bfm_pl,
227                                                FIB_NODE_TYPE_BIER_FMASK,
228                                                bier_fmask_get_index(bfm));
229
230     bier_fmask_stack(bfm);
231 }
232
233 static void
234 bier_fmask_destroy (bier_fmask_t *bfm)
235 {
236     clib_mem_free(bfm->bfm_bits.bfmb_refs);
237     clib_mem_free(bfm->bfm_bits.bfmb_input_reset_string.bbs_buckets);
238
239     bier_fmask_db_remove(bfm->bfm_id);
240     fib_path_list_child_remove(bfm->bfm_pl,
241                                bfm->bfm_sibling);
242     dpo_reset(&bfm->bfm_dpo);
243     clib_mem_free(bfm->bfm_id);
244     pool_put(bier_fmask_pool, bfm);
245 }
246
247 void
248 bier_fmask_unlock (index_t bfmi)
249 {
250     bier_fmask_t *bfm;
251
252     if (INDEX_INVALID == bfmi)
253     {
254         return;
255     }
256
257     bfm = bier_fmask_get(bfmi);
258
259     fib_node_unlock(&bfm->bfm_node);
260 }
261
262 void
263 bier_fmask_lock (index_t bfmi)
264 {
265     bier_fmask_t *bfm;
266
267     if (INDEX_INVALID == bfmi)
268     {
269         return;
270     }
271
272     bfm = bier_fmask_get(bfmi);
273
274     fib_node_lock(&bfm->bfm_node);
275 }
276
277 index_t
278 bier_fmask_create_and_lock (const bier_fmask_id_t *fmid,
279                             const fib_route_path_t *rpaths)
280 {
281     bier_fmask_t *bfm;
282     index_t bfmi;
283
284     pool_get_aligned(bier_fmask_pool, bfm, CLIB_CACHE_LINE_BYTES);
285     bfmi = bier_fmask_get_index(bfm);
286
287     vlib_validate_combined_counter (&(bier_fmask_counters), bfmi);
288     vlib_zero_combined_counter (&(bier_fmask_counters), bfmi);
289
290     bier_fmask_init(bfm, fmid, rpaths);
291
292     bier_fmask_lock(bfmi);
293
294     return (bfmi);
295 }
296
297 void
298 bier_fmask_link (index_t bfmi,
299                  bier_bp_t bp)
300 {
301     bier_fmask_t *bfm;
302
303     bfm = bier_fmask_get(bfmi);
304
305     if (0 == bfm->bfm_bits.bfmb_refs[BIER_BP_TO_INDEX(bp)])
306     {
307         /*
308          * 0 -> 1 transistion - set the bit in the string
309          */
310         bier_bit_string_set_bit(&bfm->bfm_bits.bfmb_input_reset_string, bp);
311     }
312
313     ++bfm->bfm_bits.bfmb_refs[BIER_BP_TO_INDEX(bp)];
314     ++bfm->bfm_bits.bfmb_count;
315 }
316
317 void
318 bier_fmask_unlink (index_t bfmi,
319                    bier_bp_t bp)
320 {
321     bier_fmask_t *bfm;
322
323     bfm = bier_fmask_get(bfmi);
324
325     --bfm->bfm_bits.bfmb_refs[BIER_BP_TO_INDEX(bp)];
326     --bfm->bfm_bits.bfmb_count;
327
328     if (0 == bfm->bfm_bits.bfmb_refs[BIER_BP_TO_INDEX(bp)])
329     {
330         /*
331          * 1 -> 0 transistion - clear the bit in the string
332          */
333         bier_bit_string_clear_bit(&bfm->bfm_bits.bfmb_input_reset_string, bp);
334     }
335 }
336
337 u8*
338 format_bier_fmask (u8 *s, va_list *ap)
339 {
340     index_t bfmi = va_arg(*ap, index_t);
341     u32 indent = va_arg(*ap, u32);
342     bier_fmask_attributes_t attr;
343     bier_fmask_t *bfm;
344     vlib_counter_t to;
345
346     if (pool_is_free_index(bier_fmask_pool, bfmi))
347     {
348         return (format(s, "No BIER f-mask %d", bfmi));
349     }
350
351     bfm = bier_fmask_get(bfmi);
352
353     s = format(s, "fmask: nh:%U bs:%U locks:%d ",
354                format_ip46_address, &bfm->bfm_id->bfmi_nh, IP46_TYPE_ANY,
355                format_bier_bit_string, &bfm->bfm_bits.bfmb_input_reset_string,
356                bfm->bfm_node.fn_locks);
357     s = format(s, "flags:");
358     FOR_EACH_BIER_FMASK_ATTR(attr) {
359         if ((1<<attr) & bfm->bfm_flags) {
360             s = format (s, "%s,", bier_fmask_attr_names[attr]);
361         }
362     }
363     vlib_get_combined_counter (&(bier_fmask_counters), bfmi, &to);
364     s = format (s, " to:[%Ld:%Ld]]", to.packets, to.bytes);
365     s = format(s, "\n");
366     s = fib_path_list_format(bfm->bfm_pl, s);
367
368     if (bfm->bfm_flags & BIER_FMASK_FLAG_MPLS)
369     {
370         s = format(s, "  output-label:%U",
371                    format_mpls_unicast_label,
372                    vnet_mpls_uc_get_label(clib_net_to_host_u32(bfm->bfm_label)));
373     }
374     else
375     {
376         s = format(s, "  output-bfit:[%U]",
377                    format_bier_bift_id,
378                    vnet_mpls_uc_get_label(clib_net_to_host_u32(bfm->bfm_label)));
379     }
380     s = format(s, "\n %U%U",
381                format_white_space, indent,
382                format_dpo_id, &bfm->bfm_dpo, indent+2);
383
384     return (s);
385 }
386
387 void
388 bier_fmask_get_stats (index_t bfmi, u64 * packets, u64 * bytes)
389 {
390   vlib_counter_t to;
391
392   vlib_get_combined_counter (&(bier_fmask_counters), bfmi, &to);
393
394   *packets = to.packets;
395   *bytes = to.bytes;
396 }
397
398 void
399 bier_fmask_encode (index_t bfmi,
400                    bier_table_id_t *btid,
401                    fib_route_path_encode_t *rpath)
402 {
403     bier_fmask_t *bfm;
404
405     bfm = bier_fmask_get(bfmi);
406     *btid = *bier_table_get_id(bfm->bfm_id->bfmi_bti);
407
408     memset(rpath, 0, sizeof(*rpath));
409
410     rpath->rpath.frp_sw_if_index = ~0;
411
412     switch (bfm->bfm_id->bfmi_nh_type)
413     {
414     case BIER_NH_UDP:
415         rpath->rpath.frp_flags = FIB_ROUTE_PATH_UDP_ENCAP;
416         rpath->rpath.frp_udp_encap_id = bfm->bfm_id->bfmi_id;
417         break;
418     case BIER_NH_IP:
419         memcpy(&rpath->rpath.frp_addr, &bfm->bfm_id->bfmi_nh,
420                sizeof(rpath->rpath.frp_addr));
421         break;
422     }
423 }
424
425 static fib_node_t *
426 bier_fmask_get_node (fib_node_index_t index)
427 {
428     bier_fmask_t *bfm = bier_fmask_get(index);
429     return (&(bfm->bfm_node));
430 }
431
432 static bier_fmask_t*
433 bier_fmask_get_from_node (fib_node_t *node)
434 {
435     return ((bier_fmask_t*)(((char*)node) -
436                             STRUCT_OFFSET_OF(bier_fmask_t,
437                                              bfm_node)));
438 }
439
440 /*
441  * bier_fmask_last_lock_gone
442  */
443 static void
444 bier_fmask_last_lock_gone (fib_node_t *node)
445 {
446     bier_fmask_destroy(bier_fmask_get_from_node(node));
447 }
448
449 /*
450  * bier_fmask_back_walk_notify
451  *
452  * A back walk has reached this BIER fmask
453  */
454 static fib_node_back_walk_rc_t
455 bier_fmask_back_walk_notify (fib_node_t *node,
456                              fib_node_back_walk_ctx_t *ctx)
457 {
458     /*
459      * re-stack the fmask on the n-eos of the via
460      */
461     bier_fmask_t *bfm = bier_fmask_get_from_node(node);
462
463     bier_fmask_stack(bfm);
464
465     /*
466      * propagate further up the graph.
467      * we can do this synchronously since the fan out is small.
468      */
469     fib_walk_sync(FIB_NODE_TYPE_BIER_FMASK, bier_fmask_get_index(bfm), ctx);
470
471     return (FIB_NODE_BACK_WALK_CONTINUE);
472 }
473
474 /*
475  * The BIER fmask's graph node virtual function table
476  */
477 static const fib_node_vft_t bier_fmask_vft = {
478     .fnv_get = bier_fmask_get_node,
479     .fnv_last_lock = bier_fmask_last_lock_gone,
480     .fnv_back_walk = bier_fmask_back_walk_notify,
481 };
482
483 static void
484 bier_fmask_dpo_lock (dpo_id_t *dpo)
485 {
486 }
487
488 static void
489 bier_fmask_dpo_unlock (dpo_id_t *dpo)
490 {
491 }
492
493 static void
494 bier_fmask_dpo_mem_show (void)
495 {
496     fib_show_memory_usage("BIER-fmask",
497                           pool_elts(bier_fmask_pool),
498                           pool_len(bier_fmask_pool),
499                           sizeof(bier_fmask_t));
500 }
501
502 const static dpo_vft_t bier_fmask_dpo_vft = {
503     .dv_lock = bier_fmask_dpo_lock,
504     .dv_unlock = bier_fmask_dpo_unlock,
505     .dv_mem_show = bier_fmask_dpo_mem_show,
506     .dv_format = format_bier_fmask,
507 };
508
509 const static char *const bier_fmask_mpls_nodes[] =
510 {
511     "bier-output",
512     NULL
513 };
514 const static char * const * const bier_fmask_nodes[DPO_PROTO_NUM] =
515 {
516     [DPO_PROTO_BIER] = bier_fmask_mpls_nodes,
517     [DPO_PROTO_MPLS] = bier_fmask_mpls_nodes,
518 };
519
520 clib_error_t *
521 bier_fmask_module_init (vlib_main_t * vm)
522 {
523     fib_node_register_type (FIB_NODE_TYPE_BIER_FMASK, &bier_fmask_vft);
524     dpo_register(DPO_BIER_FMASK, &bier_fmask_dpo_vft, bier_fmask_nodes);
525
526     return (NULL);
527 }
528
529 VLIB_INIT_FUNCTION (bier_fmask_module_init);
530
531 static clib_error_t *
532 bier_fmask_show (vlib_main_t * vm,
533                  unformat_input_t * input,
534                  vlib_cli_command_t * cmd)
535 {
536     bier_fmask_t *bfm;
537     index_t bfmi;
538
539     bfmi = INDEX_INVALID;
540
541     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
542         if (unformat (input, "%d", &bfmi))
543         {
544             ;
545         } else
546         {
547             break;
548         }
549     }
550
551     if (INDEX_INVALID == bfmi)
552     {
553         pool_foreach(bfm, bier_fmask_pool,
554         ({
555             vlib_cli_output (vm, "[@%d] %U",
556                              bier_fmask_get_index(bfm),
557                              format_bier_fmask, bier_fmask_get_index(bfm), 0);
558         }));
559     }
560     else
561     {
562         vlib_cli_output (vm, "%U", format_bier_fmask, bfmi, 0);
563     }
564
565     return (NULL);
566 }
567
568 VLIB_CLI_COMMAND (show_bier_fmask, static) = {
569     .path = "show bier fmask",
570     .short_help = "show bier fmask",
571     .function = bier_fmask_show,
572 };