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