/* *------------------------------------------------------------------ * bier_api.c - vnet BIER api * * Copyright (c) 2016 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *------------------------------------------------------------------ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define REPLY_MSG_ID_BASE bier_main.msg_id_base #include typedef struct { u16 msg_id_base; } bier_main_t; bier_main_t bier_main; static void vl_api_bier_table_add_del_t_handler (vl_api_bier_table_add_del_t * mp) { vl_api_bier_table_add_del_reply_t *rmp; vnet_main_t *vnm; int rv; vnm = vnet_get_main (); vnm->api_errno = 0; if (mp->bt_tbl_id.bt_hdr_len_id >= BIER_HDR_LEN_2048) { rv = VNET_API_ERROR_BIER_BSL_UNSUP; } else { bier_table_id_t bti = { .bti_set = mp->bt_tbl_id.bt_set, .bti_sub_domain = mp->bt_tbl_id.bt_sub_domain, .bti_hdr_len = mp->bt_tbl_id.bt_hdr_len_id, .bti_type = BIER_TABLE_MPLS_SPF, .bti_ecmp = BIER_ECMP_TABLE_ID_MAIN, }; if (mp->bt_is_add) { mpls_label_t label = ntohl(mp->bt_label); /* * convert acceptable 'don't want a label' values from * the API to the correct internal INVLID value */ if ((0 == label) || (~0 == label)) { label = MPLS_LABEL_INVALID; } bier_table_add_or_lock(&bti, label); } else { bier_table_unlock(&bti); } rv = vnm->api_errno; } REPLY_MACRO (VL_API_BIER_TABLE_ADD_DEL_REPLY); } static void send_bier_table_details (vl_api_registration_t * reg, u32 context, const bier_table_t *bt) { vl_api_bier_table_details_t *mp; mp = vl_msg_api_alloc(sizeof(*mp)); if (!mp) return; clib_memset(mp, 0, sizeof(*mp)); mp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_BIER_TABLE_DETAILS); mp->context = context; mp->bt_label = bt->bt_ll; mp->bt_tbl_id.bt_set = bt->bt_id.bti_set; mp->bt_tbl_id.bt_sub_domain = bt->bt_id.bti_sub_domain; mp->bt_tbl_id.bt_hdr_len_id = bt->bt_id.bti_hdr_len; vl_api_send_msg (reg, (u8 *) mp); } static void vl_api_bier_table_dump_t_handler (vl_api_bier_table_dump_t * mp) { vl_api_registration_t *reg; bier_table_t *bt; reg = vl_api_client_index_to_registration (mp->client_index); if (!reg) return; pool_foreach (bt, bier_table_pool) { /* * skip the ecmp tables. */ if (bier_table_is_main(bt)) { send_bier_table_details(reg, mp->context, bt); } } } static void vl_api_bier_route_add_del_t_handler (vl_api_bier_route_add_del_t * mp) { vl_api_bier_route_add_del_reply_t *rmp; fib_route_path_t *brpaths, *brpath; vnet_main_t *vnm; bier_bp_t bp; int rv = 0; u8 ii; vnm = vnet_get_main (); vnm->api_errno = 0; bp = ntohl(mp->br_route.br_bp); brpaths = NULL; if (mp->br_route.br_tbl_id.bt_hdr_len_id >= BIER_HDR_LEN_2048) { rv = VNET_API_ERROR_BIER_BSL_UNSUP; goto done; } if (0 == bp || bp > BIER_BP_MAX) { rv = -1; goto done; } bier_table_id_t bti = { .bti_set = mp->br_route.br_tbl_id.bt_set, .bti_sub_domain = mp->br_route.br_tbl_id.bt_sub_domain, .bti_hdr_len = mp->br_route.br_tbl_id.bt_hdr_len_id, .bti_type = BIER_TABLE_MPLS_SPF, .bti_ecmp = BIER_ECMP_TABLE_ID_MAIN, }; vec_validate(brpaths, mp->br_route.br_n_paths - 1); vec_foreach_index(ii, brpaths) { brpath = &brpaths[ii]; rv = fib_api_path_decode(&mp->br_route.br_paths[ii], brpath); if (0 != rv) { goto done; } } if (mp->br_is_replace) { if (0 == vec_len(brpaths)) { bier_table_route_delete(&bti, bp); } else { bier_table_route_path_update(&bti, bp, brpaths); } } else if (mp->br_is_add) { bier_table_route_path_add(&bti, bp, brpaths); } else { bier_table_route_path_remove(&bti, bp, brpaths); } vec_free(brpaths); done: rv = (rv == 0) ? vnm->api_errno : rv; REPLY_MACRO (VL_API_BIER_ROUTE_ADD_DEL_REPLY); } typedef struct bier_route_details_walk_t_ { vl_api_registration_t * reg; u32 context; } bier_route_details_walk_t; static void send_bier_route_details (const bier_table_t *bt, const bier_entry_t *be, void *args) { bier_route_details_walk_t *ctx = args; vl_api_bier_route_details_t *mp; fib_path_encode_ctx_t path_ctx = { .rpaths = NULL, }; fib_route_path_t *rpath; vl_api_fib_path_t *fp; u32 n_paths, m_size; n_paths = fib_path_list_get_n_paths(be->be_path_list); m_size = sizeof(*mp) + (n_paths * sizeof(vl_api_fib_path_t)); mp = vl_msg_api_alloc(m_size); if (!mp) return; clib_memset(mp, 0, m_size); mp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_BIER_ROUTE_DETAILS); mp->context = ctx->context; mp->br_route.br_tbl_id.bt_set = bt->bt_id.bti_set; mp->br_route.br_tbl_id.bt_sub_domain = bt->bt_id.bti_sub_domain; mp->br_route.br_tbl_id.bt_hdr_len_id = bt->bt_id.bti_hdr_len; mp->br_route.br_bp = htonl(be->be_bp); mp->br_route.br_n_paths = htonl(n_paths); fib_path_list_walk_w_ext(be->be_path_list, NULL, fib_path_encode, &path_ctx); fp = mp->br_route.br_paths; vec_foreach (rpath, path_ctx.rpaths) { fib_api_path_encode(rpath, fp); fp++; } vec_free(path_ctx.rpaths); vl_api_send_msg (ctx->reg, (u8 *) mp); } static void vl_api_bier_route_dump_t_handler (vl_api_bier_route_dump_t * mp) { vl_api_registration_t *reg; reg = vl_api_client_index_to_registration (mp->client_index); if (!reg) return; bier_table_id_t bti = { .bti_set = mp->br_tbl_id.bt_set, .bti_sub_domain = mp->br_tbl_id.bt_sub_domain, .bti_hdr_len = mp->br_tbl_id.bt_hdr_len_id, .bti_type = BIER_TABLE_MPLS_SPF, .bti_ecmp = BIER_ECMP_TABLE_ID_MAIN, }; bier_route_details_walk_t ctx = { .reg = reg, .context = mp->context, }; bier_table_walk(&bti, send_bier_route_details, &ctx); } static void vl_api_bier_imp_add_t_handler (vl_api_bier_imp_add_t * mp) { vl_api_bier_imp_add_reply_t *rmp; vnet_main_t *vnm; index_t bii = ~0; int rv = 0; vnm = vnet_get_main (); vnm->api_errno = 0; /* * The BSL support by VPP is limited to the size of the * available space in the vlib_buffer_t */ if (mp->bi_tbl_id.bt_hdr_len_id >= BIER_HDR_LEN_2048) { rv = VNET_API_ERROR_BIER_BSL_UNSUP; } else { bier_table_id_t bti = { .bti_set = mp->bi_tbl_id.bt_set, .bti_sub_domain = mp->bi_tbl_id.bt_sub_domain, .bti_hdr_len = mp->bi_tbl_id.bt_hdr_len_id, .bti_type = BIER_TABLE_MPLS_SPF, .bti_ecmp = BIER_ECMP_TABLE_ID_MAIN, }; bier_bit_string_t bs = { .bbs_len = mp->bi_n_bytes, .bbs_buckets = mp->bi_bytes, }; bii = bier_imp_add_or_lock(&bti, ntohs(mp->bi_src), &bs); } REPLY_MACRO2 (VL_API_BIER_IMP_ADD_REPLY, ({ rmp->bi_index = ntohl (bii); })); } static void vl_api_bier_imp_del_t_handler (vl_api_bier_imp_del_t * mp) { vl_api_bier_imp_del_reply_t *rmp; vnet_main_t *vnm; int rv = 0; vnm = vnet_get_main (); vnm->api_errno = 0; bier_imp_unlock(ntohl(mp->bi_index)); REPLY_MACRO(VL_API_BIER_IMP_DEL_REPLY); } static void send_bier_imp_details (vl_api_registration_t * reg, u32 context, const bier_imp_t *bi) { vl_api_bier_imp_details_t *mp; bier_hdr_t copy; u8 n_bytes; copy = bi->bi_hdr; bier_hdr_ntoh(©); n_bytes = bier_hdr_len_id_to_num_bytes( bier_hdr_get_len_id(©)); mp = vl_msg_api_alloc(sizeof(*mp) + n_bytes); if (!mp) return; clib_memset(mp, 0, sizeof(*mp)+n_bytes); mp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_BIER_IMP_DETAILS); mp->context = context; mp->bi_tbl_id.bt_set = bi->bi_tbl.bti_set; mp->bi_tbl_id.bt_sub_domain = bi->bi_tbl.bti_sub_domain; mp->bi_tbl_id.bt_hdr_len_id = bi->bi_tbl.bti_hdr_len; mp->bi_src = htons(bier_hdr_get_src_id(©)); mp->bi_n_bytes = n_bytes; memcpy(mp->bi_bytes, bi->bi_bits, n_bytes); vl_api_send_msg (reg, (u8 *) mp); } static void vl_api_bier_imp_dump_t_handler (vl_api_bier_imp_dump_t * mp) { vl_api_registration_t *reg; bier_imp_t *bi; reg = vl_api_client_index_to_registration (mp->client_index); if (!reg) return; pool_foreach (bi, bier_imp_pool) { send_bier_imp_details(reg, mp->context, bi); } } static void vl_api_bier_disp_table_add_del_t_handler (vl_api_bier_disp_table_add_del_t * mp) { vl_api_bier_disp_table_add_del_reply_t *rmp; vnet_main_t *vnm; u32 table_id; int rv; vnm = vnet_get_main (); vnm->api_errno = 0; table_id = ntohl(mp->bdt_tbl_id); if (mp->bdt_is_add) { bier_disp_table_add_or_lock(table_id); } else { bier_disp_table_unlock_w_table_id(table_id); } rv = vnm->api_errno; REPLY_MACRO (VL_API_BIER_DISP_TABLE_ADD_DEL_REPLY); } static void send_bier_disp_table_details (vl_api_registration_t * reg, u32 context, const bier_disp_table_t *bdt) { vl_api_bier_disp_table_details_t *mp; mp = vl_msg_api_alloc(sizeof(*mp)); if (!mp) return; clib_memset(mp, 0, sizeof(*mp)); mp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_BIER_DISP_TABLE_DETAILS); mp->context = context; mp->bdt_tbl_id = htonl(bdt->bdt_table_id); vl_api_send_msg (reg, (u8 *) mp); } static void vl_api_bier_disp_table_dump_t_handler (vl_api_bier_disp_table_dump_t * mp) { vl_api_registration_t *reg; bier_disp_table_t *bdt; reg = vl_api_client_index_to_registration (mp->client_index); if (!reg) return; pool_foreach (bdt, bier_disp_table_pool) { send_bier_disp_table_details(reg, mp->context, bdt); } } static void vl_api_bier_disp_entry_add_del_t_handler (vl_api_bier_disp_entry_add_del_t * mp) { vl_api_bier_disp_entry_add_del_reply_t *rmp; fib_route_path_t *brps = NULL, *brp; vnet_main_t *vnm; bier_bp_t bp; u32 table_id; int rv = 0; u32 ii; vnm = vnet_get_main (); vnm->api_errno = 0; table_id = ntohl(mp->bde_tbl_id); bp = ntohs(mp->bde_bp); /* * BP=0 is the default route */ if (bp > 0xffff) { rv = -1; goto done; } vec_validate(brps, mp->bde_n_paths - 1); vec_foreach_index(ii, brps) { brp = &brps[ii]; brp->frp_fib_index = ntohl(mp->bde_paths[ii].table_id); brp->frp_sw_if_index = ntohl(mp->bde_paths[ii].sw_if_index); if (~0 != ntohl(mp->bde_paths[ii].rpf_id)) { brp->frp_flags = FIB_ROUTE_PATH_RPF_ID; brp->frp_rpf_id = ntohl(mp->bde_paths[ii].rpf_id); } if (FIB_API_PATH_NH_PROTO_IP4 == mp->bde_paths[ii].proto) { clib_memcpy (&brp->frp_addr.ip4, &mp->bde_paths[ii].nh.address.ip4, sizeof (brp->frp_addr.ip4)); } else if (FIB_API_PATH_NH_PROTO_IP6 == mp->bde_paths[ii].proto) { clib_memcpy (&brp->frp_addr.ip6, &mp->bde_paths[ii].nh.address.ip6, sizeof (brp->frp_addr.ip6)); } if (ip46_address_is_zero(&brp->frp_addr)) { index_t fti; switch (mp->bde_payload_proto) { case BIER_HDR_PROTO_INVALID: case BIER_HDR_PROTO_MPLS_DOWN_STREAM: case BIER_HDR_PROTO_MPLS_UP_STREAM: case BIER_HDR_PROTO_ETHERNET: case BIER_HDR_PROTO_VXLAN: case BIER_HDR_PROTO_CTRL: case BIER_HDR_PROTO_OAM: rv = VNET_API_ERROR_UNSUPPORTED; goto done; break; case BIER_HDR_PROTO_IPV4: case BIER_HDR_PROTO_IPV6: { fib_protocol_t fproto; fproto = (mp->bde_payload_proto == BIER_HDR_PROTO_IPV4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6); if (brp->frp_flags & FIB_ROUTE_PATH_RPF_ID) { fti = mfib_table_find (fproto, ntohl (mp->bde_paths[ii].table_id)); } else { fti = fib_table_find (fproto, ntohl (mp->bde_paths[ii].table_id)); } if (INDEX_INVALID != fti) { brp->frp_fib_index = fti; } else { rv = VNET_API_ERROR_NO_SUCH_FIB; goto done; } break; } } } } if (mp->bde_is_add) { bier_disp_table_entry_path_add(table_id, bp, mp->bde_payload_proto, brps); } else { bier_disp_table_entry_path_remove(table_id, bp, mp->bde_payload_proto, brps); } done: vec_free(brps); rv = (rv == 0) ? vnm->api_errno : rv; REPLY_MACRO (VL_API_BIER_DISP_ENTRY_ADD_DEL_REPLY); } typedef struct bier_disp_entry_details_walk_t_ { vl_api_registration_t * reg; u32 context; } bier_disp_entry_details_walk_t; static void send_bier_disp_entry_details (const bier_disp_table_t *bdt, const bier_disp_entry_t *bde, u16 bp, void *args) { bier_disp_entry_details_walk_t *ctx = args; vl_api_bier_disp_entry_details_t *mp; bier_hdr_proto_id_t pproto; vl_api_fib_path_t *fp; u32 n_paths, m_size; FOR_EACH_BIER_HDR_PROTO(pproto) { fib_node_index_t pl = bde->bde_pl[pproto]; if (INDEX_INVALID != pl) { fib_path_encode_ctx_t path_ctx = { .rpaths = NULL, }; fib_route_path_t *rpath; n_paths = fib_path_list_get_n_paths(pl); m_size = sizeof(*mp) + (n_paths * sizeof(vl_api_fib_path_t)); mp = vl_msg_api_alloc(m_size); if (!mp) return; clib_memset(mp, 0, m_size); mp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_BIER_DISP_ENTRY_DETAILS); mp->context = ctx->context; mp->bde_tbl_id = htonl (bdt->bdt_table_id); mp->bde_n_paths = htonl (n_paths); mp->bde_payload_proto = pproto; mp->bde_bp = htons (bp); fib_path_list_walk_w_ext (pl, NULL, fib_path_encode, &path_ctx); fp = mp->bde_paths; vec_foreach (rpath, path_ctx.rpaths) { fib_api_path_encode (rpath, fp); fp++; } vl_api_send_msg (ctx->reg, (u8 *) mp); vec_free (path_ctx.rpaths); } } } static void vl_api_bier_disp_entry_dump_t_handler (vl_api_bier_disp_entry_dump_t * mp) { vl_api_registration_t *reg; reg = vl_api_client_index_to_registration (mp->client_index); if (!reg) return; bier_disp_entry_details_walk_t ctx = { .reg = reg, .context = mp->context, }; bier_disp_table_walk(ntohl(mp->bde_tbl_id), send_bier_disp_entry_details, &ctx); } #include static clib_error_t * bier_api_hookup (vlib_main_t * vm) { bier_main.msg_id_base = setup_message_id_table (); return 0; } VLIB_API_INIT_FUNCTION (bier_api_hookup);