MPLS Unifom mode
[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].fml_value =
206                 ntohl(mp->br_paths[ii].label_stack[jj].label);
207             brpath->frp_label_stack[jj].fml_ttl =
208                 mp->br_paths[ii].label_stack[jj].ttl;
209             brpath->frp_label_stack[jj].fml_exp =
210                 mp->br_paths[ii].label_stack[jj].exp;
211             brpath->frp_label_stack[jj].fml_mode =
212                 (mp->br_paths[ii].label_stack[jj].is_uniform ?
213                  FIB_MPLS_LSP_MODE_UNIFORM :
214                  FIB_MPLS_LSP_MODE_PIPE);
215         }
216
217         if (mp->br_paths[ii].is_udp_encap)
218         {
219             brpath->frp_flags |= FIB_ROUTE_PATH_UDP_ENCAP;
220             brpath->frp_udp_encap_id = ntohl(mp->br_paths[ii].next_hop_id);
221         }
222         else
223         {
224             if (0 == mp->br_paths[ii].afi)
225             {
226                 clib_memcpy (&brpath->frp_addr.ip4,
227                              mp->br_paths[ii].next_hop,
228                              sizeof (brpath->frp_addr.ip4));
229             }
230             else
231             {
232                 clib_memcpy (&brpath->frp_addr.ip6,
233                              mp->br_paths[ii].next_hop,
234                              sizeof (brpath->frp_addr.ip6));
235             }
236             if (ip46_address_is_zero(&brpath->frp_addr))
237             {
238                 index_t bdti;
239
240                 bdti = bier_disp_table_find(ntohl(mp->br_paths[ii].table_id));
241
242                 if (INDEX_INVALID != bdti)
243                 {
244                     brpath->frp_fib_index = bdti;
245                     brpath->frp_proto = DPO_PROTO_BIER;
246                 }
247                 else
248                 {
249                     rv = VNET_API_ERROR_NO_SUCH_FIB;
250                     goto done;
251                 }
252             }
253         }
254     }
255
256     if (mp->br_is_add)
257     {
258         bier_table_route_add(&bti, bp, brpaths);
259     }
260     else
261     {
262         bier_table_route_remove(&bti, bp, brpaths);
263     }
264     vec_free(brpaths);
265
266 done:
267     rv = (rv == 0) ? vnm->api_errno : rv;
268
269     REPLY_MACRO (VL_API_BIER_ROUTE_ADD_DEL_REPLY);
270 }
271
272 typedef struct bier_route_details_walk_t_
273 {
274     vl_api_registration_t * reg;
275     u32 context;
276 } bier_route_details_walk_t;
277
278 static void
279 send_bier_route_details (const bier_table_t *bt,
280                          const bier_entry_t *be,
281                          void *args)
282 {
283     fib_route_path_encode_t *api_rpaths = NULL, *api_rpath;
284     bier_route_details_walk_t *ctx = args;
285     vl_api_bier_route_details_t *mp;
286     vl_api_fib_path_t *fp;
287     u32 n_paths, m_size;
288
289     n_paths = fib_path_list_get_n_paths(be->be_path_list);
290     m_size = sizeof(*mp) + (n_paths * sizeof(vl_api_fib_path_t));
291     mp = vl_msg_api_alloc(m_size);
292     if (!mp)
293         return;
294
295     memset(mp, 0, m_size);
296     mp->_vl_msg_id = ntohs(VL_API_BIER_ROUTE_DETAILS);
297     mp->context = ctx->context;
298
299     mp->br_tbl_id.bt_set = bt->bt_id.bti_set;
300     mp->br_tbl_id.bt_sub_domain = bt->bt_id.bti_sub_domain;
301     mp->br_tbl_id.bt_hdr_len_id = bt->bt_id.bti_hdr_len;
302     mp->br_bp = htons(be->be_bp);
303     mp->br_n_paths = htonl(n_paths);
304
305     fib_path_list_walk(be->be_path_list, fib_path_encode, &api_rpaths);
306
307     fp = mp->br_paths;
308     vec_foreach (api_rpath, api_rpaths)
309     {
310         fp->weight = api_rpath->rpath.frp_weight;
311         fp->preference = api_rpath->rpath.frp_preference;
312         fp->sw_if_index = htonl (api_rpath->rpath.frp_sw_if_index);
313         fp->n_labels = 0;
314         copy_fib_next_hop (api_rpath, fp);
315         fp++;
316     }
317
318     vl_api_send_msg (ctx->reg, (u8 *) mp);
319 }
320
321 static void
322 vl_api_bier_route_dump_t_handler (vl_api_bier_route_dump_t * mp)
323 {
324     vl_api_registration_t *reg;
325
326     reg = vl_api_client_index_to_registration (mp->client_index);
327     if (!reg)
328       return;
329
330     bier_table_id_t bti = {
331         .bti_set = mp->br_tbl_id.bt_set,
332         .bti_sub_domain = mp->br_tbl_id.bt_sub_domain,
333         .bti_hdr_len = mp->br_tbl_id.bt_hdr_len_id,
334         .bti_type = BIER_TABLE_MPLS_SPF,
335         .bti_ecmp = BIER_ECMP_TABLE_ID_MAIN,
336     };
337     bier_route_details_walk_t ctx = {
338         .reg = reg,
339         .context = mp->context,
340     };
341     bier_table_walk(&bti, send_bier_route_details, &ctx);
342 }
343
344 static void
345 vl_api_bier_imp_add_t_handler (vl_api_bier_imp_add_t * mp)
346 {
347     vl_api_bier_imp_add_reply_t *rmp;
348     vnet_main_t *vnm;
349     index_t bii;
350     int rv = 0;
351
352     vnm = vnet_get_main ();
353     vnm->api_errno = 0;
354
355     /*
356      * The BSL support by VPP is limited to the size of the
357      * available space in the vlib_buffer_t
358      */
359     if (mp->bi_tbl_id.bt_hdr_len_id >= BIER_HDR_LEN_2048)
360     {
361         rv = VNET_API_ERROR_BIER_BSL_UNSUP;
362     }
363     else
364     {
365         bier_table_id_t bti = {
366             .bti_set = mp->bi_tbl_id.bt_set,
367             .bti_sub_domain = mp->bi_tbl_id.bt_sub_domain,
368             .bti_hdr_len = mp->bi_tbl_id.bt_hdr_len_id,
369             .bti_type = BIER_TABLE_MPLS_SPF,
370             .bti_ecmp = BIER_ECMP_TABLE_ID_MAIN,
371         };
372         bier_bit_string_t bs = {
373             .bbs_len = mp->bi_n_bytes,
374             .bbs_buckets = mp->bi_bytes,
375         };
376
377         bii = bier_imp_add_or_lock(&bti, ntohs(mp->bi_src), &bs);
378     }
379
380     REPLY_MACRO2 (VL_API_BIER_IMP_ADD_REPLY,
381     ({
382         rmp->bi_index = ntohl (bii);
383     }));
384 }
385
386 static void
387 vl_api_bier_imp_del_t_handler (vl_api_bier_imp_del_t * mp)
388 {
389     vl_api_bier_imp_del_reply_t *rmp;
390     vnet_main_t *vnm;
391     int rv = 0;
392
393     vnm = vnet_get_main ();
394     vnm->api_errno = 0;
395
396     bier_imp_unlock(ntohl(mp->bi_index));
397
398     REPLY_MACRO(VL_API_BIER_IMP_DEL_REPLY);
399 }
400
401 static void
402 send_bier_imp_details (vl_api_registration_t * reg,
403                        u32 context,
404                        const bier_imp_t *bi)
405 {
406     vl_api_bier_imp_details_t *mp;
407     bier_hdr_t copy;
408     u8 n_bytes;
409
410     copy = bi->bi_hdr;
411     bier_hdr_ntoh(&copy);
412
413     n_bytes = bier_hdr_len_id_to_num_bytes(
414                   bier_hdr_get_len_id(&copy));
415     mp = vl_msg_api_alloc(sizeof(*mp) + n_bytes);
416     if (!mp)
417         return;
418     memset(mp, 0, sizeof(*mp)+n_bytes);
419     mp->_vl_msg_id = ntohs(VL_API_BIER_IMP_DETAILS);
420     mp->context = context;
421
422     mp->bi_tbl_id.bt_set = bi->bi_tbl.bti_set;
423     mp->bi_tbl_id.bt_sub_domain = bi->bi_tbl.bti_sub_domain;
424     mp->bi_tbl_id.bt_hdr_len_id = bi->bi_tbl.bti_hdr_len;
425
426     mp->bi_src = htons(bier_hdr_get_src_id(&copy));
427     mp->bi_n_bytes = n_bytes;
428     memcpy(mp->bi_bytes, bi->bi_bits, n_bytes);
429
430     vl_api_send_msg (reg, (u8 *) mp);
431 }
432
433 static void
434 vl_api_bier_imp_dump_t_handler (vl_api_bier_imp_dump_t * mp)
435 {
436     vl_api_registration_t *reg;
437     bier_imp_t *bi;
438
439     reg = vl_api_client_index_to_registration (mp->client_index);
440     if (!reg)
441       return;
442
443     pool_foreach(bi, bier_imp_pool,
444     ({
445         send_bier_imp_details(reg, mp->context, bi);
446     }));
447 }
448
449 static void
450 vl_api_bier_disp_table_add_del_t_handler (vl_api_bier_disp_table_add_del_t * mp)
451 {
452     vl_api_bier_disp_table_add_del_reply_t *rmp;
453     vnet_main_t *vnm;
454     u32 table_id;
455     int rv;
456
457     vnm = vnet_get_main ();
458     vnm->api_errno = 0;
459     table_id = ntohl(mp->bdt_tbl_id);
460
461     if (mp->bdt_is_add)
462     {
463         bier_disp_table_add_or_lock(table_id);
464     }
465     else
466     {
467         bier_disp_table_unlock_w_table_id(table_id);
468     }
469
470     rv = vnm->api_errno;
471
472     REPLY_MACRO (VL_API_BIER_DISP_TABLE_ADD_DEL_REPLY);
473 }
474
475 static void
476 send_bier_disp_table_details (vl_api_registration_t * reg,
477                               u32 context,
478                               const bier_disp_table_t *bdt)
479 {
480     vl_api_bier_disp_table_details_t *mp;
481
482     mp = vl_msg_api_alloc(sizeof(*mp));
483     if (!mp)
484         return;
485     memset(mp, 0, sizeof(*mp));
486     mp->_vl_msg_id = ntohs(VL_API_BIER_DISP_TABLE_DETAILS);
487     mp->context = context;
488
489     mp->bdt_tbl_id = htonl(bdt->bdt_table_id);
490
491     vl_api_send_msg (reg, (u8 *) mp);
492 }
493
494 static void
495 vl_api_bier_disp_table_dump_t_handler (vl_api_bier_disp_table_dump_t * mp)
496 {
497     vl_api_registration_t *reg;
498     bier_disp_table_t *bdt;
499
500     reg = vl_api_client_index_to_registration (mp->client_index);
501     if (!reg)
502       return;
503
504     pool_foreach(bdt, bier_disp_table_pool,
505     ({
506         send_bier_disp_table_details(reg, mp->context, bdt);
507     }));
508 }
509
510 static void
511 vl_api_bier_disp_entry_add_del_t_handler (vl_api_bier_disp_entry_add_del_t * mp)
512 {
513     vl_api_bier_disp_entry_add_del_reply_t *rmp;
514     fib_route_path_t *brps = NULL, *brp;
515     vnet_main_t *vnm;
516     bier_bp_t bp;
517     u32 table_id;
518     int rv = 0;
519     u32 ii;
520
521     vnm = vnet_get_main ();
522     vnm->api_errno = 0;
523     table_id = ntohl(mp->bde_tbl_id);
524     bp = ntohs(mp->bde_bp);
525
526     /*
527      * BP=0 is the default route
528      */
529     if (bp > 0xffff)
530     {
531         rv = -1;
532         goto done;
533     }
534
535     vec_validate(brps, mp->bde_n_paths - 1);
536     vec_foreach_index(ii, brps)
537     {
538         brp = &brps[ii];
539         brp->frp_fib_index = ntohl(mp->bde_paths[ii].table_id);
540         brp->frp_sw_if_index = ntohl(mp->bde_paths[ii].sw_if_index);
541
542         if (~0 != ntohl(mp->bde_paths[ii].rpf_id))
543         {
544             brp->frp_flags = FIB_ROUTE_PATH_RPF_ID;
545             brp->frp_rpf_id = ntohl(mp->bde_paths[ii].rpf_id);
546         }
547
548         if (0 == mp->bde_paths[ii].afi)
549         {
550             clib_memcpy (&brp->frp_addr.ip4,
551                          mp->bde_paths[ii].next_hop,
552                          sizeof (brp->frp_addr.ip4));
553         }
554         else
555         {
556             clib_memcpy (&brp->frp_addr.ip6,
557                          mp->bde_paths[ii].next_hop,
558                          sizeof (brp->frp_addr.ip6));
559         }
560         if (ip46_address_is_zero(&brp->frp_addr))
561         {
562             index_t fti;
563
564             switch (mp->bde_payload_proto)
565             {
566             case BIER_HDR_PROTO_INVALID:
567             case BIER_HDR_PROTO_MPLS_DOWN_STREAM:
568             case BIER_HDR_PROTO_MPLS_UP_STREAM:
569             case BIER_HDR_PROTO_ETHERNET:
570             case BIER_HDR_PROTO_VXLAN:
571             case BIER_HDR_PROTO_CTRL:
572             case BIER_HDR_PROTO_OAM:
573                 rv = VNET_API_ERROR_UNSUPPORTED;
574                 goto done;
575                 break;
576             case BIER_HDR_PROTO_IPV4:
577             case BIER_HDR_PROTO_IPV6:
578             {
579                 fib_protocol_t fproto;
580
581                 fproto = (mp->bde_payload_proto == BIER_HDR_PROTO_IPV4 ?
582                           FIB_PROTOCOL_IP4 :
583                           FIB_PROTOCOL_IP6);
584
585                 if (brp->frp_flags & FIB_ROUTE_PATH_RPF_ID)
586                 {
587                     fti = mfib_table_find (fproto,
588                                            ntohl (mp->bde_paths[ii].table_id));
589                 }
590                 else
591                 {
592                     fti = fib_table_find (fproto,
593                                           ntohl (mp->bde_paths[ii].table_id));
594                 }
595
596                 if (INDEX_INVALID != fti)
597                 {
598                     brp->frp_fib_index = fti;
599                 }
600                 else
601                 {
602                     rv = VNET_API_ERROR_NO_SUCH_FIB;
603                     goto done;
604                 }
605                 break;
606             }
607             }
608         }
609     }
610
611     if (mp->bde_is_add)
612     {
613         bier_disp_table_entry_path_add(table_id, bp,
614                                        mp->bde_payload_proto,
615                                        brps);
616     }
617     else
618     {
619         bier_disp_table_entry_path_remove(table_id, bp,
620                                           mp->bde_payload_proto,
621                                           brps);
622     }
623
624 done:
625     vec_free(brps);
626     rv = (rv == 0) ? vnm->api_errno : rv;
627
628     REPLY_MACRO (VL_API_BIER_DISP_ENTRY_ADD_DEL_REPLY);
629 }
630
631 typedef struct bier_disp_entry_details_walk_t_
632 {
633     vl_api_registration_t * reg;
634     u32 context;
635 } bier_disp_entry_details_walk_t;
636
637 static void
638 send_bier_disp_entry_details (const bier_disp_table_t *bdt,
639                               const bier_disp_entry_t *bde,
640                               u16 bp,
641                               void *args)
642 {
643     fib_route_path_encode_t *api_rpaths = NULL, *api_rpath;
644     bier_disp_entry_details_walk_t *ctx = args;
645     vl_api_bier_disp_entry_details_t *mp;
646     bier_hdr_proto_id_t pproto;
647     vl_api_fib_path_t *fp;
648     u32 n_paths, m_size;
649
650     FOR_EACH_BIER_HDR_PROTO(pproto)
651     {
652         fib_node_index_t pl = bde->bde_pl[pproto];
653         if (INDEX_INVALID != pl)
654         {
655             n_paths = fib_path_list_get_n_paths(pl);
656             m_size = sizeof(*mp) + (n_paths * sizeof(vl_api_fib_path_t));
657             mp = vl_msg_api_alloc(m_size);
658             if (!mp)
659                 return;
660
661             memset(mp, 0, m_size);
662             mp->_vl_msg_id = ntohs(VL_API_BIER_DISP_ENTRY_DETAILS);
663             mp->context = ctx->context;
664
665             mp->bde_tbl_id = htonl(bdt->bdt_table_id);
666             mp->bde_n_paths = htonl(n_paths);
667             mp->bde_payload_proto = pproto;
668             mp->bde_bp = htons(bp);
669
670             fib_path_list_walk(pl, fib_path_encode, &api_rpaths);
671
672             fp = mp->bde_paths;
673             vec_foreach (api_rpath, api_rpaths)
674             {
675                 fp->weight = api_rpath->rpath.frp_weight;
676                 fp->preference = api_rpath->rpath.frp_preference;
677                 fp->sw_if_index = htonl (api_rpath->rpath.frp_sw_if_index);
678                 fp->n_labels = 0;
679                 copy_fib_next_hop (api_rpath, fp);
680                 fp++;
681             }
682
683             vl_api_send_msg (ctx->reg, (u8 *) mp);
684         }
685     }
686 }
687
688 static void
689 vl_api_bier_disp_entry_dump_t_handler (vl_api_bier_disp_entry_dump_t * mp)
690 {
691     vl_api_registration_t *reg;
692
693     reg = vl_api_client_index_to_registration (mp->client_index);
694     if (!reg)
695       return;
696
697     bier_disp_entry_details_walk_t ctx = {
698         .reg = reg,
699         .context = mp->context,
700     };
701     bier_disp_table_walk(ntohl(mp->bde_tbl_id),
702                          send_bier_disp_entry_details,
703                          &ctx);
704 }
705
706 #define vl_msg_name_crc_list
707 #include <vnet/bier/bier.api.h>
708 #undef vl_msg_name_crc_list
709
710 static void
711 setup_message_id_table (api_main_t * am)
712 {
713 #define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
714     foreach_vl_msg_name_crc_bier;
715 #undef _
716 }
717
718 static clib_error_t *
719 bier_api_hookup (vlib_main_t * vm)
720 {
721     api_main_t *am = &api_main;
722
723 #define _(N,n)                                          \
724     vl_msg_api_set_handlers(VL_API_##N, #n,             \
725                             vl_api_##n##_t_handler,     \
726                             vl_noop_handler,            \
727                             vl_api_##n##_t_endian,      \
728                             vl_api_##n##_t_print,       \
729                             sizeof(vl_api_##n##_t), 1);
730     foreach_bier_api_msg;
731 #undef _
732
733     /*
734      * Set up the (msg_name, crc, message-id) table
735      */
736     setup_message_id_table (am);
737
738     return 0;
739 }
740
741 VLIB_API_INIT_FUNCTION (bier_api_hookup);