BIER: fix support for longer bit-string lengths
[vpp.git] / src / vnet / bier / bier_api.c
1 /*
2  *------------------------------------------------------------------
3  * bier_api.c - vnet BIER api
4  *
5  * Copyright (c) 2016 Cisco and/or its affiliates.
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at:
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *------------------------------------------------------------------
18  */
19
20 #include <vnet/vnet.h>
21 #include <vlibmemory/api.h>
22
23 #include <vnet/api_errno.h>
24 #include <vnet/bier/bier_table.h>
25 #include <vnet/bier/bier_imp.h>
26 #include <vnet/bier/bier_disp_table.h>
27 #include <vnet/bier/bier_disp_entry.h>
28 #include <vnet/bier/bier_bit_string.h>
29 #include <vnet/bier/bier_hdr_inlines.h>
30 #include <vnet/fib/fib_path_list.h>
31 #include <vnet/fib/fib_api.h>
32 #include <vnet/fib/fib_table.h>
33 #include <vnet/mfib/mfib_table.h>
34
35 #include <vnet/vnet_msg_enum.h>
36
37 #define vl_typedefs             /* define message structures */
38 #include <vnet/vnet_all_api_h.h>
39 #undef vl_typedefs
40
41 #define vl_endianfun            /* define message structures */
42 #include <vnet/vnet_all_api_h.h>
43 #undef vl_endianfun
44
45 /* instantiate all the print functions we know about */
46 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
47 #define vl_printfun
48 #include <vnet/vnet_all_api_h.h>
49 #undef vl_printfun
50
51 #include <vlibapi/api_helper_macros.h>
52
53 #define foreach_bier_api_msg                            \
54     _(BIER_TABLE_ADD_DEL, bier_table_add_del)           \
55     _(BIER_TABLE_DUMP, bier_table_dump)                 \
56     _(BIER_ROUTE_ADD_DEL, bier_route_add_del)           \
57     _(BIER_ROUTE_DUMP, bier_route_dump)                 \
58     _(BIER_IMP_ADD, bier_imp_add)                       \
59     _(BIER_IMP_DEL, bier_imp_del)                       \
60     _(BIER_IMP_DUMP, bier_imp_dump)                     \
61     _(BIER_DISP_TABLE_ADD_DEL, bier_disp_table_add_del) \
62     _(BIER_DISP_TABLE_DUMP, bier_disp_table_dump)       \
63     _(BIER_DISP_ENTRY_ADD_DEL, bier_disp_entry_add_del) \
64     _(BIER_DISP_ENTRY_DUMP, bier_disp_entry_dump)
65
66 static void
67 vl_api_bier_table_add_del_t_handler (vl_api_bier_table_add_del_t * mp)
68 {
69     vl_api_bier_table_add_del_reply_t *rmp;
70     vnet_main_t *vnm;
71     int rv;
72
73     vnm = vnet_get_main ();
74     vnm->api_errno = 0;
75
76     if (mp->bt_tbl_id.bt_hdr_len_id >= BIER_HDR_LEN_2048)
77     {
78         rv = VNET_API_ERROR_BIER_BSL_UNSUP;
79     }
80     else
81     {
82         bier_table_id_t bti = {
83             .bti_set = mp->bt_tbl_id.bt_set,
84             .bti_sub_domain = mp->bt_tbl_id.bt_sub_domain,
85             .bti_hdr_len = mp->bt_tbl_id.bt_hdr_len_id,
86             .bti_type = BIER_TABLE_MPLS_SPF,
87             .bti_ecmp = BIER_ECMP_TABLE_ID_MAIN,
88         };
89
90         if (mp->bt_is_add)
91         {
92             mpls_label_t label = ntohl(mp->bt_label);
93
94             /*
95              * convert acceptable 'don't want a label' values from 
96              * the API to the correct internal INVLID value
97              */
98             if ((0 == label) || (~0 == label))
99             {
100                 label = MPLS_LABEL_INVALID;
101             }
102             bier_table_add_or_lock(&bti, label);
103         }
104         else
105         {
106             bier_table_unlock(&bti);
107         }
108
109         rv = vnm->api_errno;
110     }
111
112     REPLY_MACRO (VL_API_BIER_TABLE_ADD_DEL_REPLY);
113 }
114
115 static void
116 send_bier_table_details (vl_api_registration_t * reg,
117                          u32 context,
118                          const bier_table_t *bt)
119 {
120     vl_api_bier_table_details_t *mp;
121
122     mp = vl_msg_api_alloc(sizeof(*mp));
123     if (!mp)
124         return;
125     memset(mp, 0, sizeof(*mp));
126     mp->_vl_msg_id = ntohs(VL_API_BIER_TABLE_DETAILS);
127     mp->context = context;
128
129     mp->bt_label = bt->bt_ll;
130     mp->bt_tbl_id.bt_set = bt->bt_id.bti_set;
131     mp->bt_tbl_id.bt_sub_domain = bt->bt_id.bti_sub_domain;
132     mp->bt_tbl_id.bt_hdr_len_id = bt->bt_id.bti_hdr_len;
133
134     vl_api_send_msg (reg, (u8 *) mp);
135 }
136
137 static void
138 vl_api_bier_table_dump_t_handler (vl_api_bier_table_dump_t * mp)
139 {
140     vl_api_registration_t *reg;
141     bier_table_t *bt;
142
143     reg = vl_api_client_index_to_registration (mp->client_index);
144     if (!reg)
145       return;
146
147     pool_foreach(bt, bier_table_pool,
148     ({
149         /*
150          * skip the ecmp tables.
151          */
152         if (bier_table_is_main(bt))
153         {
154             send_bier_table_details(reg, mp->context, bt);
155         }
156     }));
157 }
158
159 static void
160 vl_api_bier_route_add_del_t_handler (vl_api_bier_route_add_del_t * mp)
161 {
162     vl_api_bier_route_add_del_reply_t *rmp;
163     fib_route_path_t *brpaths, *brpath;
164     vnet_main_t *vnm;
165     bier_bp_t bp;
166     int rv = 0;
167     u8 ii, jj;
168
169     vnm = vnet_get_main ();
170     vnm->api_errno = 0;
171     bp = ntohl(mp->br_bp);
172     brpaths = NULL;
173
174     if (mp->br_tbl_id.bt_hdr_len_id >= BIER_HDR_LEN_2048)
175     {
176         rv = VNET_API_ERROR_BIER_BSL_UNSUP;
177         goto done;
178     }
179     if (0 == bp || bp > BIER_BP_MAX)
180     {
181         rv = -1;
182         goto done;
183     }
184
185     bier_table_id_t bti = {
186         .bti_set = mp->br_tbl_id.bt_set,
187         .bti_sub_domain = mp->br_tbl_id.bt_sub_domain,
188         .bti_hdr_len = mp->br_tbl_id.bt_hdr_len_id,
189         .bti_type = BIER_TABLE_MPLS_SPF,
190         .bti_ecmp = BIER_ECMP_TABLE_ID_MAIN,
191     };
192
193     vec_validate(brpaths, mp->br_n_paths - 1);
194
195     vec_foreach_index(ii, brpaths)
196     {
197         brpath = &brpaths[ii];
198         memset(brpath, 0, sizeof(*brpath));
199         brpath->frp_sw_if_index = ~0;
200
201         vec_validate(brpath->frp_label_stack,
202                      mp->br_paths[ii].n_labels - 1);
203         for (jj = 0; jj < mp->br_paths[ii].n_labels; jj++)
204         {
205             brpath->frp_label_stack[jj] =
206                 ntohl(mp->br_paths[ii].label_stack[jj]);
207         }
208
209         if (mp->br_paths[ii].is_udp_encap)
210         {
211             brpath->frp_flags |= FIB_ROUTE_PATH_UDP_ENCAP;
212             brpath->frp_udp_encap_id = ntohl(mp->br_paths[ii].next_hop_id);
213         }
214         else
215         {
216             if (0 == mp->br_paths[ii].afi)
217             {
218                 clib_memcpy (&brpath->frp_addr.ip4,
219                              mp->br_paths[ii].next_hop,
220                              sizeof (brpath->frp_addr.ip4));
221             }
222             else
223             {
224                 clib_memcpy (&brpath->frp_addr.ip6,
225                              mp->br_paths[ii].next_hop,
226                              sizeof (brpath->frp_addr.ip6));
227             }
228             if (ip46_address_is_zero(&brpath->frp_addr))
229             {
230                 index_t bdti;
231
232                 bdti = bier_disp_table_find(ntohl(mp->br_paths[ii].table_id));
233
234                 if (INDEX_INVALID != bdti)
235                 {
236                     brpath->frp_fib_index = bdti;
237                     brpath->frp_proto = DPO_PROTO_BIER;
238                 }
239                 else
240                 {
241                     rv = VNET_API_ERROR_NO_SUCH_FIB;
242                     goto done;
243                 }
244             }
245         }
246     }
247
248     if (mp->br_is_add)
249     {
250         bier_table_route_add(&bti, bp, brpaths);
251     }
252     else
253     {
254         bier_table_route_remove(&bti, bp, brpaths);
255     }
256     vec_free(brpaths);
257
258 done:
259     rv = (rv == 0) ? vnm->api_errno : rv;
260
261     REPLY_MACRO (VL_API_BIER_ROUTE_ADD_DEL_REPLY);
262 }
263
264 typedef struct bier_route_details_walk_t_
265 {
266     vl_api_registration_t * reg;
267     u32 context;
268 } bier_route_details_walk_t;
269
270 static void
271 send_bier_route_details (const bier_table_t *bt,
272                          const bier_entry_t *be,
273                          void *args)
274 {
275     fib_route_path_encode_t *api_rpaths = NULL, *api_rpath;
276     bier_route_details_walk_t *ctx = args;
277     vl_api_bier_route_details_t *mp;
278     vl_api_fib_path3_t *fp;
279     u32 n_paths, m_size;
280
281     n_paths = fib_path_list_get_n_paths(be->be_path_list);
282     m_size = sizeof(*mp) + (n_paths * sizeof(vl_api_fib_path3_t));
283     mp = vl_msg_api_alloc(m_size);
284     if (!mp)
285         return;
286
287     memset(mp, 0, m_size);
288     mp->_vl_msg_id = ntohs(VL_API_BIER_ROUTE_DETAILS);
289     mp->context = ctx->context;
290
291     mp->br_tbl_id.bt_set = bt->bt_id.bti_set;
292     mp->br_tbl_id.bt_sub_domain = bt->bt_id.bti_sub_domain;
293     mp->br_tbl_id.bt_hdr_len_id = bt->bt_id.bti_hdr_len;
294     mp->br_bp = htons(be->be_bp);
295     mp->br_n_paths = htonl(n_paths);
296
297     fib_path_list_walk(be->be_path_list, fib_path_encode, &api_rpaths);
298
299     fp = mp->br_paths;
300     vec_foreach (api_rpath, api_rpaths)
301     {
302         fp->weight = api_rpath->rpath.frp_weight;
303         fp->preference = api_rpath->rpath.frp_preference;
304         fp->sw_if_index = htonl (api_rpath->rpath.frp_sw_if_index);
305         fp->n_labels = 0;
306         copy_fib_next_hop (api_rpath, fp);
307         fp++;
308     }
309
310     vl_api_send_msg (ctx->reg, (u8 *) mp);
311 }
312
313 static void
314 vl_api_bier_route_dump_t_handler (vl_api_bier_route_dump_t * mp)
315 {
316     vl_api_registration_t *reg;
317
318     reg = vl_api_client_index_to_registration (mp->client_index);
319     if (!reg)
320       return;
321
322     bier_table_id_t bti = {
323         .bti_set = mp->br_tbl_id.bt_set,
324         .bti_sub_domain = mp->br_tbl_id.bt_sub_domain,
325         .bti_hdr_len = mp->br_tbl_id.bt_hdr_len_id,
326         .bti_type = BIER_TABLE_MPLS_SPF,
327         .bti_ecmp = BIER_ECMP_TABLE_ID_MAIN,
328     };
329     bier_route_details_walk_t ctx = {
330         .reg = reg,
331         .context = mp->context,
332     };
333     bier_table_walk(&bti, send_bier_route_details, &ctx);
334 }
335
336 static void
337 vl_api_bier_imp_add_t_handler (vl_api_bier_imp_add_t * mp)
338 {
339     vl_api_bier_imp_add_reply_t *rmp;
340     vnet_main_t *vnm;
341     index_t bii;
342     int rv = 0;
343
344     vnm = vnet_get_main ();
345     vnm->api_errno = 0;
346
347     /*
348      * The BSL support by VPP is limited to the size of the
349      * available space in the vlib_buffer_t
350      */
351     if (mp->bi_tbl_id.bt_hdr_len_id >= BIER_HDR_LEN_2048)
352     {
353         rv = VNET_API_ERROR_BIER_BSL_UNSUP;
354     }
355     else
356     {
357         bier_table_id_t bti = {
358             .bti_set = mp->bi_tbl_id.bt_set,
359             .bti_sub_domain = mp->bi_tbl_id.bt_sub_domain,
360             .bti_hdr_len = mp->bi_tbl_id.bt_hdr_len_id,
361             .bti_type = BIER_TABLE_MPLS_SPF,
362             .bti_ecmp = BIER_ECMP_TABLE_ID_MAIN,
363         };
364         bier_bit_string_t bs = {
365             .bbs_len = mp->bi_n_bytes,
366             .bbs_buckets = mp->bi_bytes,
367         };
368
369         bii = bier_imp_add_or_lock(&bti, ntohs(mp->bi_src), &bs);
370     }
371
372     REPLY_MACRO2 (VL_API_BIER_IMP_ADD_REPLY,
373     ({
374         rmp->bi_index = ntohl (bii);
375     }));
376 }
377
378 static void
379 vl_api_bier_imp_del_t_handler (vl_api_bier_imp_del_t * mp)
380 {
381     vl_api_bier_imp_del_reply_t *rmp;
382     vnet_main_t *vnm;
383     int rv = 0;
384
385     vnm = vnet_get_main ();
386     vnm->api_errno = 0;
387
388     bier_imp_unlock(ntohl(mp->bi_index));
389
390     REPLY_MACRO(VL_API_BIER_IMP_DEL_REPLY);
391 }
392
393 static void
394 send_bier_imp_details (vl_api_registration_t * reg,
395                        u32 context,
396                        const bier_imp_t *bi)
397 {
398     vl_api_bier_imp_details_t *mp;
399     bier_hdr_t copy;
400     u8 n_bytes;
401
402     copy = bi->bi_hdr;
403     bier_hdr_ntoh(&copy);
404
405     n_bytes = bier_hdr_len_id_to_num_bytes(
406                   bier_hdr_get_len_id(&copy));
407     mp = vl_msg_api_alloc(sizeof(*mp) + n_bytes);
408     if (!mp)
409         return;
410     memset(mp, 0, sizeof(*mp)+n_bytes);
411     mp->_vl_msg_id = ntohs(VL_API_BIER_IMP_DETAILS);
412     mp->context = context;
413
414     mp->bi_tbl_id.bt_set = bi->bi_tbl.bti_set;
415     mp->bi_tbl_id.bt_sub_domain = bi->bi_tbl.bti_sub_domain;
416     mp->bi_tbl_id.bt_hdr_len_id = bi->bi_tbl.bti_hdr_len;
417
418     mp->bi_src = htons(bier_hdr_get_src_id(&copy));
419     mp->bi_n_bytes = n_bytes;
420     memcpy(mp->bi_bytes, bi->bi_bits, n_bytes);
421
422     vl_api_send_msg (reg, (u8 *) mp);
423 }
424
425 static void
426 vl_api_bier_imp_dump_t_handler (vl_api_bier_imp_dump_t * mp)
427 {
428     vl_api_registration_t *reg;
429     bier_imp_t *bi;
430
431     reg = vl_api_client_index_to_registration (mp->client_index);
432     if (!reg)
433       return;
434
435     pool_foreach(bi, bier_imp_pool,
436     ({
437         send_bier_imp_details(reg, mp->context, bi);
438     }));
439 }
440
441 static void
442 vl_api_bier_disp_table_add_del_t_handler (vl_api_bier_disp_table_add_del_t * mp)
443 {
444     vl_api_bier_disp_table_add_del_reply_t *rmp;
445     vnet_main_t *vnm;
446     u32 table_id;
447     int rv;
448
449     vnm = vnet_get_main ();
450     vnm->api_errno = 0;
451     table_id = ntohl(mp->bdt_tbl_id);
452
453     if (mp->bdt_is_add)
454     {
455         bier_disp_table_add_or_lock(table_id);
456     }
457     else
458     {
459         bier_disp_table_unlock_w_table_id(table_id);
460     }
461
462     rv = vnm->api_errno;
463
464     REPLY_MACRO (VL_API_BIER_DISP_TABLE_ADD_DEL_REPLY);
465 }
466
467 static void
468 send_bier_disp_table_details (vl_api_registration_t * reg,
469                               u32 context,
470                               const bier_disp_table_t *bdt)
471 {
472     vl_api_bier_disp_table_details_t *mp;
473
474     mp = vl_msg_api_alloc(sizeof(*mp));
475     if (!mp)
476         return;
477     memset(mp, 0, sizeof(*mp));
478     mp->_vl_msg_id = ntohs(VL_API_BIER_DISP_TABLE_DETAILS);
479     mp->context = context;
480
481     mp->bdt_tbl_id = htonl(bdt->bdt_table_id);
482
483     vl_api_send_msg (reg, (u8 *) mp);
484 }
485
486 static void
487 vl_api_bier_disp_table_dump_t_handler (vl_api_bier_disp_table_dump_t * mp)
488 {
489     vl_api_registration_t *reg;
490     bier_disp_table_t *bdt;
491
492     reg = vl_api_client_index_to_registration (mp->client_index);
493     if (!reg)
494       return;
495
496     pool_foreach(bdt, bier_disp_table_pool,
497     ({
498         send_bier_disp_table_details(reg, mp->context, bdt);
499     }));
500 }
501
502 static void
503 vl_api_bier_disp_entry_add_del_t_handler (vl_api_bier_disp_entry_add_del_t * mp)
504 {
505     vl_api_bier_disp_entry_add_del_reply_t *rmp;
506     fib_route_path_t *brps = NULL, *brp;
507     vnet_main_t *vnm;
508     bier_bp_t bp;
509     u32 table_id;
510     int rv = 0;
511     u32 ii;
512
513     vnm = vnet_get_main ();
514     vnm->api_errno = 0;
515     table_id = ntohl(mp->bde_tbl_id);
516     bp = ntohs(mp->bde_bp);
517
518     /*
519      * BP=0 is the default route
520      */
521     if (bp > 0xffff)
522     {
523         rv = -1;
524         goto done;
525     }
526
527     vec_validate(brps, mp->bde_n_paths - 1);
528     vec_foreach_index(ii, brps)
529     {
530         brp = &brps[ii];
531         brp->frp_fib_index = ntohl(mp->bde_paths[ii].table_id);
532         brp->frp_sw_if_index = ntohl(mp->bde_paths[ii].sw_if_index);
533
534         if (~0 != ntohl(mp->bde_paths[ii].rpf_id))
535         {
536             brp->frp_flags = FIB_ROUTE_PATH_RPF_ID;
537             brp->frp_rpf_id = ntohl(mp->bde_paths[ii].rpf_id);
538         }
539
540         if (0 == mp->bde_paths[ii].afi)
541         {
542             clib_memcpy (&brp->frp_addr.ip4,
543                          mp->bde_paths[ii].next_hop,
544                          sizeof (brp->frp_addr.ip4));
545         }
546         else
547         {
548             clib_memcpy (&brp->frp_addr.ip6,
549                          mp->bde_paths[ii].next_hop,
550                          sizeof (brp->frp_addr.ip6));
551         }
552         if (ip46_address_is_zero(&brp->frp_addr))
553         {
554             index_t fti;
555
556             switch (mp->bde_payload_proto)
557             {
558             case BIER_HDR_PROTO_INVALID:
559             case BIER_HDR_PROTO_MPLS_DOWN_STREAM:
560             case BIER_HDR_PROTO_MPLS_UP_STREAM:
561             case BIER_HDR_PROTO_ETHERNET:
562             case BIER_HDR_PROTO_VXLAN:
563             case BIER_HDR_PROTO_CTRL:
564             case BIER_HDR_PROTO_OAM:
565                 rv = VNET_API_ERROR_UNSUPPORTED;
566                 goto done;
567                 break;
568             case BIER_HDR_PROTO_IPV4:
569             case BIER_HDR_PROTO_IPV6:
570             {
571                 fib_protocol_t fproto;
572
573                 fproto = (mp->bde_payload_proto == BIER_HDR_PROTO_IPV4 ?
574                           FIB_PROTOCOL_IP4 :
575                           FIB_PROTOCOL_IP6);
576
577                 if (brp->frp_flags & FIB_ROUTE_PATH_RPF_ID)
578                 {
579                     fti = mfib_table_find (fproto,
580                                            ntohl (mp->bde_paths[ii].table_id));
581                 }
582                 else
583                 {
584                     fti = fib_table_find (fproto,
585                                           ntohl (mp->bde_paths[ii].table_id));
586                 }
587
588                 if (INDEX_INVALID != fti)
589                 {
590                     brp->frp_fib_index = fti;
591                 }
592                 else
593                 {
594                     rv = VNET_API_ERROR_NO_SUCH_FIB;
595                     goto done;
596                 }
597                 break;
598             }
599             }
600         }
601     }
602
603     if (mp->bde_is_add)
604     {
605         bier_disp_table_entry_path_add(table_id, bp,
606                                        mp->bde_payload_proto,
607                                        brps);
608     }
609     else
610     {
611         bier_disp_table_entry_path_remove(table_id, bp,
612                                           mp->bde_payload_proto,
613                                           brps);
614     }
615
616 done:
617     vec_free(brps);
618     rv = (rv == 0) ? vnm->api_errno : rv;
619
620     REPLY_MACRO (VL_API_BIER_DISP_ENTRY_ADD_DEL_REPLY);
621 }
622
623 typedef struct bier_disp_entry_details_walk_t_
624 {
625     vl_api_registration_t * reg;
626     u32 context;
627 } bier_disp_entry_details_walk_t;
628
629 static void
630 send_bier_disp_entry_details (const bier_disp_table_t *bdt,
631                               const bier_disp_entry_t *bde,
632                               u16 bp,
633                               void *args)
634 {
635     fib_route_path_encode_t *api_rpaths = NULL, *api_rpath;
636     bier_disp_entry_details_walk_t *ctx = args;
637     vl_api_bier_disp_entry_details_t *mp;
638     bier_hdr_proto_id_t pproto;
639     vl_api_fib_path3_t *fp;
640     u32 n_paths, m_size;
641
642     FOR_EACH_BIER_HDR_PROTO(pproto)
643     {
644         fib_node_index_t pl = bde->bde_pl[pproto];
645         if (INDEX_INVALID != pl)
646         {
647             n_paths = fib_path_list_get_n_paths(pl);
648             m_size = sizeof(*mp) + (n_paths * sizeof(vl_api_fib_path3_t));
649             mp = vl_msg_api_alloc(m_size);
650             if (!mp)
651                 return;
652
653             memset(mp, 0, m_size);
654             mp->_vl_msg_id = ntohs(VL_API_BIER_DISP_ENTRY_DETAILS);
655             mp->context = ctx->context;
656
657             mp->bde_tbl_id = htonl(bdt->bdt_table_id);
658             mp->bde_n_paths = htonl(n_paths);
659             mp->bde_payload_proto = pproto;
660             mp->bde_bp = htons(bp);
661
662             fib_path_list_walk(pl, fib_path_encode, &api_rpaths);
663
664             fp = mp->bde_paths;
665             vec_foreach (api_rpath, api_rpaths)
666             {
667                 fp->weight = api_rpath->rpath.frp_weight;
668                 fp->preference = api_rpath->rpath.frp_preference;
669                 fp->sw_if_index = htonl (api_rpath->rpath.frp_sw_if_index);
670                 fp->n_labels = 0;
671                 copy_fib_next_hop (api_rpath, fp);
672                 fp++;
673             }
674
675             vl_api_send_msg (ctx->reg, (u8 *) mp);
676         }
677     }
678 }
679
680 static void
681 vl_api_bier_disp_entry_dump_t_handler (vl_api_bier_disp_entry_dump_t * mp)
682 {
683     vl_api_registration_t *reg;
684
685     reg = vl_api_client_index_to_registration (mp->client_index);
686     if (!reg)
687       return;
688
689     bier_disp_entry_details_walk_t ctx = {
690         .reg = reg,
691         .context = mp->context,
692     };
693     bier_disp_table_walk(ntohl(mp->bde_tbl_id),
694                          send_bier_disp_entry_details,
695                          &ctx);
696 }
697
698 #define vl_msg_name_crc_list
699 #include <vnet/bier/bier.api.h>
700 #undef vl_msg_name_crc_list
701
702 static void
703 setup_message_id_table (api_main_t * am)
704 {
705 #define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
706     foreach_vl_msg_name_crc_bier;
707 #undef _
708 }
709
710 static clib_error_t *
711 bier_api_hookup (vlib_main_t * vm)
712 {
713     api_main_t *am = &api_main;
714
715 #define _(N,n)                                          \
716     vl_msg_api_set_handlers(VL_API_##N, #n,             \
717                             vl_api_##n##_t_handler,     \
718                             vl_noop_handler,            \
719                             vl_api_##n##_t_endian,      \
720                             vl_api_##n##_t_print,       \
721                             sizeof(vl_api_##n##_t), 1);
722     foreach_bier_api_msg;
723 #undef _
724
725     /*
726      * Set up the (msg_name, crc, message-id) table
727      */
728     setup_message_id_table (am);
729
730     return 0;
731 }
732
733 VLIB_API_INIT_FUNCTION (bier_api_hookup);