BIER
[vpp.git] / src / vnet / bier / bier_disp_entry.c
1 /*
2  * Copyright (c) 2017 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  * bier_dispositon : The BIER dispositon object
17  *
18  * A BIER dispositon object is present in the IP mcast output list
19  * and represents the dispositon of a BIER bitmask. After BIER header
20  * dispositon the packet is forward within the appropriate/specifid
21  * BIER table
22  */
23
24 #include <vnet/bier/bier_disp_entry.h>
25 #include <vnet/bier/bier_hdr_inlines.h>
26 #include <vnet/fib/fib_path_list.h>
27 #include <vnet/dpo/drop_dpo.h>
28
29 /**
30  * The memory pool of all imp objects
31  */
32 bier_disp_entry_t *bier_disp_entry_pool;
33
34 /**
35  * When constructing the BIER imp ID from an index and BSL, shift
36  * the BSL this far
37  */
38 #define BIER_DISP_ENTRY_ID_HLEN_SHIFT 24
39
40 static void
41 bier_disp_entry_lock_i (bier_disp_entry_t *bde)
42 {
43     bde->bde_locks++;
44 }
45
46 void
47 bier_disp_entry_lock (index_t bdei)
48 {
49     bier_disp_entry_lock_i(bier_disp_entry_get(bdei));
50 }
51
52 static index_t
53 bier_disp_entry_get_index(bier_disp_entry_t *bde)
54 {
55     return (bde - bier_disp_entry_pool);
56 }
57
58 index_t
59 bier_disp_entry_add_or_lock (void)
60 {
61     dpo_id_t invalid = DPO_INVALID;
62     bier_hdr_proto_id_t pproto;
63     bier_disp_entry_t *bde;
64
65     pool_get_aligned(bier_disp_entry_pool, bde, CLIB_CACHE_LINE_BYTES);
66
67     bde->bde_locks = 0;
68
69     FOR_EACH_BIER_HDR_PROTO(pproto)
70     {
71         bde->bde_fwd[pproto].bde_dpo = invalid;
72         bde->bde_fwd[pproto].bde_rpf_id = ~0;
73         bde->bde_pl[pproto] = FIB_NODE_INDEX_INVALID;
74     }
75
76     bier_disp_entry_lock_i(bde);
77     return (bier_disp_entry_get_index(bde));
78 }
79
80 void
81 bier_disp_entry_unlock (index_t bdei)
82 {
83     bier_disp_entry_t *bde;
84
85     if (INDEX_INVALID == bdei)
86     {
87         return;
88     }
89
90     bde = bier_disp_entry_get(bdei);
91
92     bde->bde_locks--;
93
94     if (0 == bde->bde_locks)
95     {
96         bier_hdr_proto_id_t pproto;
97
98         FOR_EACH_BIER_HDR_PROTO(pproto)
99         {
100             dpo_unlock(&bde->bde_fwd[pproto].bde_dpo);
101             bde->bde_fwd[pproto].bde_rpf_id = ~0;
102             fib_path_list_unlock(bde->bde_pl[pproto]);
103         }
104         pool_put(bier_disp_entry_pool, bde);
105     }
106 }
107
108 typedef struct bier_disp_entry_path_list_walk_ctx_t_
109 {
110     u32 bdew_rpf_id;
111 } bier_disp_entry_path_list_walk_ctx_t;
112
113 static fib_path_list_walk_rc_t
114 bier_disp_entry_path_list_walk (fib_node_index_t pl_index,
115                                 fib_node_index_t path_index,
116                                 void *arg)
117 {
118     bier_disp_entry_path_list_walk_ctx_t *ctx = arg;
119
120     ctx->bdew_rpf_id = fib_path_get_rpf_id(path_index);
121
122     if (~0 != ctx->bdew_rpf_id)
123     {
124         return (FIB_PATH_LIST_WALK_STOP);
125     }
126     return (FIB_PATH_LIST_WALK_CONTINUE);
127 }
128
129 static void
130 bier_disp_entry_restack (bier_disp_entry_t *bde,
131                          bier_hdr_proto_id_t pproto)
132 {
133     dpo_id_t via_dpo = DPO_INVALID;
134     fib_node_index_t pli;
135
136     pli = bde->bde_pl[pproto];
137
138     if (FIB_NODE_INDEX_INVALID == pli)
139     {
140         dpo_copy(&via_dpo,
141                  drop_dpo_get(bier_hdr_proto_to_dpo(pproto)));
142     }
143     else
144     {
145         fib_path_list_contribute_forwarding(pli,
146                                             fib_forw_chain_type_from_dpo_proto(
147                                                 bier_hdr_proto_to_dpo(pproto)),
148                                             &via_dpo);
149
150         bier_disp_entry_path_list_walk_ctx_t ctx = {
151             .bdew_rpf_id = ~0,
152         };
153
154         fib_path_list_walk(pli, bier_disp_entry_path_list_walk, &ctx);
155         bde->bde_fwd[pproto].bde_rpf_id = ctx.bdew_rpf_id;
156     }
157
158     dpo_stack(DPO_BIER_DISP_ENTRY,
159               DPO_PROTO_BIER,
160               &bde->bde_fwd[pproto].bde_dpo,
161               &via_dpo);
162 }
163
164 void
165 bier_disp_entry_path_add (index_t bdei,
166                           bier_hdr_proto_id_t pproto,
167                           const fib_route_path_t *rpaths)
168 {
169     fib_node_index_t *pli, old_pli;
170     bier_disp_entry_t *bde;
171
172     bde = bier_disp_entry_get(bdei);
173     pli = &bde->bde_pl[pproto];
174     old_pli = *pli;
175
176     /*
177      * create a new or update the exisitng path-list for this
178      * payload protocol
179      */
180     if (FIB_NODE_INDEX_INVALID == *pli)
181     {
182         *pli = fib_path_list_create((FIB_PATH_LIST_FLAG_SHARED |
183                                      FIB_PATH_LIST_FLAG_NO_URPF),
184                                     rpaths);
185     }
186     else
187     {
188         *pli = fib_path_list_copy_and_path_add(old_pli,
189                                                (FIB_PATH_LIST_FLAG_SHARED |
190                                                 FIB_PATH_LIST_FLAG_NO_URPF),
191                                                rpaths);
192     }
193
194     fib_path_list_lock(*pli);
195     fib_path_list_unlock(old_pli);
196
197     bier_disp_entry_restack(bde, pproto);
198 }
199
200 int
201 bier_disp_entry_path_remove (index_t bdei,
202                              bier_hdr_proto_id_t pproto,
203                              const fib_route_path_t *rpaths)
204 {
205     fib_node_index_t *pli, old_pli;
206     bier_disp_entry_t *bde;
207
208     bde = bier_disp_entry_get(bdei);
209     pli = &bde->bde_pl[pproto];
210     old_pli = *pli;
211
212     /*
213      * update the exisitng path-list for this payload protocol
214      */
215     if (FIB_NODE_INDEX_INVALID != *pli)
216     {
217         *pli = fib_path_list_copy_and_path_remove(old_pli,
218                                                   (FIB_PATH_LIST_FLAG_SHARED |
219                                                    FIB_PATH_LIST_FLAG_NO_URPF),
220                                                   rpaths);
221
222         fib_path_list_lock(*pli);
223         fib_path_list_unlock(old_pli);
224
225         bier_disp_entry_restack(bde, pproto);
226     }
227
228     /*
229      * if there are no path-list defined for any payload protocol
230      * then this entry is OK for removal
231      */
232     int remove = 1;
233
234     FOR_EACH_BIER_HDR_PROTO(pproto)
235     {
236         if (FIB_NODE_INDEX_INVALID != bde->bde_pl[pproto])
237         {
238             remove = 0;
239             break;
240         }
241     }
242
243     return (remove);
244 }
245
246 u8*
247 format_bier_disp_entry (u8* s, va_list *args)
248 {
249     index_t bdei = va_arg (*args, index_t);
250     u32 indent = va_arg(*args, u32);
251     bier_show_flags_t flags = va_arg(*args, bier_show_flags_t);
252     bier_hdr_proto_id_t pproto;
253     bier_disp_entry_t *bde;
254
255     bde = bier_disp_entry_get(bdei);
256
257     s = format(s, "bier-disp:[%d]", bdei);
258
259     FOR_EACH_BIER_HDR_PROTO(pproto)
260     {
261         if (INDEX_INVALID != bde->bde_pl[pproto])
262         {
263             s = format(s, "\n");
264             s = fib_path_list_format(bde->bde_pl[pproto], s);
265
266             if (flags & BIER_SHOW_DETAIL)
267             {
268                 s = format(s, "\n%UForwarding:",
269                            format_white_space, indent);
270                 s = format(s, "\n%Urpf-id:%d",
271                            format_white_space, indent+1,
272                            bde->bde_fwd[pproto].bde_rpf_id);
273                 s = format(s, "\n%U%U",
274                            format_white_space, indent+1,
275                            format_dpo_id, &bde->bde_fwd[pproto].bde_dpo, indent+2);
276             }
277         }
278     }
279     return (s);
280 }
281
282 void
283 bier_disp_entry_contribute_forwarding (index_t bdei,
284                                        dpo_id_t *dpo)
285 {
286     dpo_set(dpo, DPO_BIER_DISP_ENTRY, DPO_PROTO_BIER, bdei);
287 }
288
289 const static char* const bier_disp_entry_bier_nodes[] =
290 {
291     "bier-disp-dispatch",
292     NULL,
293 };
294
295 const static char* const * const bier_disp_entry_nodes[DPO_PROTO_NUM] =
296 {
297     [DPO_PROTO_BIER]  = bier_disp_entry_bier_nodes,
298 };
299
300 static void
301 bier_disp_entry_dpo_lock (dpo_id_t *dpo)
302 {
303     bier_disp_entry_lock(dpo->dpoi_index);
304 }
305
306 static void
307 bier_disp_entry_dpo_unlock (dpo_id_t *dpo)
308 {
309     bier_disp_entry_unlock(dpo->dpoi_index);
310 }
311
312 static void
313 bier_disp_entry_dpo_mem_show (void)
314 {
315     fib_show_memory_usage("BIER dispositon",
316                           pool_elts(bier_disp_entry_pool),
317                           pool_len(bier_disp_entry_pool),
318                           sizeof(bier_disp_entry_t));
319 }
320
321 static u8*
322 format_bier_disp_entry_dpo (u8* s, va_list *ap)
323 {
324     index_t index = va_arg(*ap, index_t);
325     u32 indent = va_arg(*ap, u32);
326
327     s = format(s, "%U", format_bier_disp_entry, index, indent, BIER_SHOW_DETAIL);
328
329     return (s);
330 }
331
332 const static dpo_vft_t bier_disp_entry_vft = {
333     .dv_lock = bier_disp_entry_dpo_lock,
334     .dv_unlock = bier_disp_entry_dpo_unlock,
335     .dv_format = format_bier_disp_entry_dpo,
336     .dv_mem_show = bier_disp_entry_dpo_mem_show,
337 };
338
339 clib_error_t *
340 bier_disp_entry_db_module_init (vlib_main_t *vm)
341 {
342     dpo_register(DPO_BIER_DISP_ENTRY,
343                  &bier_disp_entry_vft,
344                  bier_disp_entry_nodes);
345
346     return (NULL);
347 }
348
349 VLIB_INIT_FUNCTION (bier_disp_entry_db_module_init);
350
351 static clib_error_t *
352 show_bier_disp_entry (vlib_main_t * vm,
353                unformat_input_t * input,
354                vlib_cli_command_t * cmd)
355 {
356     index_t bdei;
357
358     bdei = INDEX_INVALID;
359
360     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
361         if (unformat (input, "%d", &bdei))
362             ;
363         else
364         {
365             break;
366         }
367     }
368
369     if (INDEX_INVALID == bdei)
370     {
371         return (NULL);
372     }
373     else
374     {
375         vlib_cli_output(vm, "%U", format_bier_disp_entry, bdei, 1,
376                         BIER_SHOW_DETAIL);
377     }
378     return (NULL);
379 }
380
381 VLIB_CLI_COMMAND (show_bier_disp_entry_node, static) = {
382     .path = "show bier disp entry",
383     .short_help = "show bier disp entry index",
384     .function = show_bier_disp_entry,
385 };