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