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