diff options
author | Neale Ranns <nranns@cisco.com> | 2017-10-21 10:53:20 -0700 |
---|---|---|
committer | Damjan Marion <dmarion.lists@gmail.com> | 2017-11-09 15:16:52 +0000 |
commit | d792d9c01e60656cbfe1b0f1fd6a9b125f5dab0c (patch) | |
tree | db88d99dd8102389fb92e8ed44bc7d6a55dc3080 | |
parent | a2ff7b8cfc829ffbb6d5de7534efb51f7cba9cf3 (diff) |
BIER
- see draft-ietf-bier-mpls-encapsulation-10
- midpoint, head and tail functions
- supported payload protocols; IPv4 and IPv6 only.
Change-Id: I59d7363bb6fdfdce8e4016a68a9c8f5a5e5791cb
Signed-off-by: Neale Ranns <nranns@cisco.com>
73 files changed, 10133 insertions, 147 deletions
diff --git a/src/vat/api_format.c b/src/vat/api_format.c index eee5c743959..c75857bc111 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -5180,6 +5180,8 @@ _(ip_mroute_add_del_reply) \ _(mpls_route_add_del_reply) \ _(mpls_table_add_del_reply) \ _(mpls_ip_bind_unbind_reply) \ +_(bier_route_add_del_reply) \ +_(bier_table_add_del_reply) \ _(proxy_arp_add_del_reply) \ _(proxy_arp_intfc_enable_disable_reply) \ _(sw_interface_set_unnumbered_reply) \ @@ -5383,6 +5385,8 @@ _(IP_MROUTE_ADD_DEL_REPLY, ip_mroute_add_del_reply) \ _(MPLS_TABLE_ADD_DEL_REPLY, mpls_table_add_del_reply) \ _(MPLS_ROUTE_ADD_DEL_REPLY, mpls_route_add_del_reply) \ _(MPLS_IP_BIND_UNBIND_REPLY, mpls_ip_bind_unbind_reply) \ +_(BIER_ROUTE_ADD_DEL_REPLY, bier_route_add_del_reply) \ +_(BIER_TABLE_ADD_DEL_REPLY, bier_table_add_del_reply) \ _(PROXY_ARP_ADD_DEL_REPLY, proxy_arp_add_del_reply) \ _(PROXY_ARP_INTFC_ENABLE_DISABLE_REPLY, \ proxy_arp_intfc_enable_disable_reply) \ @@ -8548,6 +8552,154 @@ api_mpls_ip_bind_unbind (vat_main_t * vam) } static int +api_bier_table_add_del (vat_main_t * vam) +{ + unformat_input_t *i = vam->input; + vl_api_bier_table_add_del_t *mp; + u8 is_add = 1; + u32 set = 0, sub_domain = 0, hdr_len = 3; + mpls_label_t local_label = MPLS_LABEL_INVALID; + int ret; + + /* Parse args required to build the message */ + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "sub-domain %d", &sub_domain)) + ; + else if (unformat (i, "set %d", &set)) + ; + else if (unformat (i, "label %d", &local_label)) + ; + else if (unformat (i, "hdr-len %d", &hdr_len)) + ; + else if (unformat (i, "add")) + is_add = 1; + else if (unformat (i, "del")) + is_add = 0; + else + { + clib_warning ("parse error '%U'", format_unformat_error, i); + return -99; + } + } + + if (MPLS_LABEL_INVALID == local_label) + { + errmsg ("missing label\n"); + return -99; + } + + /* Construct the API message */ + M (BIER_TABLE_ADD_DEL, mp); + + mp->bt_is_add = is_add; + mp->bt_label = ntohl (local_label); + mp->bt_tbl_id.bt_set = set; + mp->bt_tbl_id.bt_sub_domain = sub_domain; + mp->bt_tbl_id.bt_hdr_len_id = hdr_len; + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + + return (ret); +} + +static int +api_bier_route_add_del (vat_main_t * vam) +{ + unformat_input_t *i = vam->input; + vl_api_bier_route_add_del_t *mp; + u8 is_add = 1; + u32 set = 0, sub_domain = 0, hdr_len = 3, bp = 0; + ip4_address_t v4_next_hop_address; + ip6_address_t v6_next_hop_address; + u8 next_hop_set = 0; + u8 next_hop_proto_is_ip4 = 1; + mpls_label_t next_hop_out_label = MPLS_LABEL_INVALID; + int ret; + + /* Parse args required to build the message */ + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "%U", unformat_ip4_address, &v4_next_hop_address)) + { + next_hop_proto_is_ip4 = 1; + next_hop_set = 1; + } + else if (unformat (i, "%U", unformat_ip6_address, &v6_next_hop_address)) + { + next_hop_proto_is_ip4 = 0; + next_hop_set = 1; + } + if (unformat (i, "sub-domain %d", &sub_domain)) + ; + else if (unformat (i, "set %d", &set)) + ; + else if (unformat (i, "hdr-len %d", &hdr_len)) + ; + else if (unformat (i, "bp %d", &bp)) + ; + else if (unformat (i, "add")) + is_add = 1; + else if (unformat (i, "del")) + is_add = 0; + else if (unformat (i, "out-label %d", &next_hop_out_label)) + ; + else + { + clib_warning ("parse error '%U'", format_unformat_error, i); + return -99; + } + } + + if (!next_hop_set || (MPLS_LABEL_INVALID == next_hop_out_label)) + { + errmsg ("next hop / label set\n"); + return -99; + } + if (0 == bp) + { + errmsg ("bit=position not set\n"); + return -99; + } + + /* Construct the API message */ + M2 (BIER_ROUTE_ADD_DEL, mp, sizeof (vl_api_fib_path3_t)); + + mp->br_is_add = is_add; + mp->br_tbl_id.bt_set = set; + mp->br_tbl_id.bt_sub_domain = sub_domain; + mp->br_tbl_id.bt_hdr_len_id = hdr_len; + mp->br_bp = ntohs (bp); + mp->br_n_paths = 1; + mp->br_paths[0].n_labels = 1; + mp->br_paths[0].label_stack[0] = ntohl (next_hop_out_label); + mp->br_paths[0].afi = (next_hop_proto_is_ip4 ? 0 : 1); + + if (next_hop_proto_is_ip4) + { + clib_memcpy (mp->br_paths[0].next_hop, + &v4_next_hop_address, sizeof (v4_next_hop_address)); + } + else + { + clib_memcpy (mp->br_paths[0].next_hop, + &v6_next_hop_address, sizeof (v6_next_hop_address)); + } + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + + return (ret); +} + +static int api_proxy_arp_add_del (vat_main_t * vam) { unformat_input_t *i = vam->input; @@ -22238,6 +22390,12 @@ _(mpls_ip_bind_unbind, \ _(mpls_tunnel_add_del, \ " via <addr> [table-id <n>]\n" \ "sw_if_index <id>] [l2] [del]") \ +_(bier_table_add_del, \ + "<label> <sub-domain> <set> <bsl> [del]") \ +_(bier_route_add_del, \ + "<bit-position> <sub-domain> <set> <bsl> via <addr> [table-id <n>]\n" \ + "[<intfc> | sw_if_index <id>]" \ + "[weight <n>] [del] [multipath]") \ _(proxy_arp_add_del, \ "<lo-ip4-addr> - <hi-ip4-addr> [vrf <n>] [del]") \ _(proxy_arp_intfc_enable_disable, \ diff --git a/src/vnet.am b/src/vnet.am index f5548307647..3204a5b5598 100644 --- a/src/vnet.am +++ b/src/vnet.am @@ -1112,6 +1112,40 @@ libvnet_la_SOURCES += \ vnet/util/trajectory.c ######################################## +# BIER +######################################## + +libvnet_la_SOURCES += \ + vnet/bier/bier_bit_string.c \ + vnet/bier/bier_entry.c \ + vnet/bier/bier_fmask.c \ + vnet/bier/bier_fmask_db.c \ + vnet/bier/bier_input.c \ + vnet/bier/bier_lookup.c \ + vnet/bier/bier_output.c \ + vnet/bier/bier_table.c \ + vnet/bier/bier_types.c \ + vnet/bier/bier_test.c \ + vnet/bier/bier_api.c \ + vnet/bier/bier_drop.c \ + vnet/bier/bier_update.c \ + vnet/bier/bier_imp_node.c \ + vnet/bier/bier_imp.c \ + vnet/bier/bier_disp_entry.c \ + vnet/bier/bier_disp_lookup_node.c \ + vnet/bier/bier_disp_dispatch_node.c \ + vnet/bier/bier_disp_table.c + +nobase_include_HEADERS += \ + vnet/bier/bier_types.h \ + vnet/bier/bier_entry.h \ + vnet/bier/bier_update.h \ + vnet/bier/bier.api.h \ + vnet/bier/bier_table.h + +API_FILES += vnet/bier/bier.api + +######################################## # Plugin client library ######################################## diff --git a/src/vnet/bier/bier.api b/src/vnet/bier/bier.api new file mode 100644 index 00000000000..466524cc6a3 --- /dev/null +++ b/src/vnet/bier/bier.api @@ -0,0 +1,268 @@ +/* + * 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. + */ + +/** \file + + This file defines vpp BIER control-plane API messages which are generally + called through a shared memory interface. +*/ +vl_api_version 1.0.0 + +/** \brief BIER Table Indentifier + @param bt_set + @param bt_sub_domain + @param bt_bit_header_length +*/ +typeonly define bier_table_id +{ + u8 bt_set; + u8 bt_sub_domain; + u8 bt_hdr_len_id; +}; + +/** \brief BIER Table Add / del route + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param bt_tbl_id - The BIER table-id the route is added in + @param bt_mpls_label - The MPLS label for the table + @param bt_is_add - Is this a route add or delete +*/ +autoreply define bier_table_add_del +{ + u32 client_index; + u32 context; + vl_api_bier_table_id_t bt_tbl_id; + u32 bt_label; + u8 bt_is_add; +}; + +define bier_table_dump +{ + u32 client_index; + u32 context; +}; + +define bier_table_details +{ + u32 context; + u32 bt_label; + vl_api_bier_table_id_t bt_tbl_id; +}; + +/** \brief FIB path + @param sw_if_index - index of the interface + @param weight - The weight, for UCMP + @param preference - The preference of the path. lowest preference is prefered + @param is_local - local if non-zero, else remote + @param is_drop - Drop the packet + @param is_unreach - Drop the packet and rate limit send ICMP unreachable + @param is_prohibit - Drop the packet and rate limit send ICMP prohibited + @param afi - the afi of the next hop, IP46_TYPE_IP4=1, IP46_TYPE_IP6=2 + @param next_hop[16] - the next hop address + + WARNING: this type is replicated, pending cleanup completion +*/ +typeonly define fib_path3 +{ + u32 sw_if_index; + u32 table_id; + u8 weight; + u8 preference; + u8 is_local; + u8 is_drop; + u8 is_unreach; + u8 is_prohibit; + u8 afi; + u8 next_hop[16]; + u32 rpf_id; + u8 n_labels; + u32 label_stack[16]; +}; + +/** \brief BIER Route Add / del route + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param br_bp - The Bit-position value + @param br_tbl_id - The BIER table-id the route is added in + @param br_is_add - Is this a route add or delete + @param br_is_replace - Are the paths specfied replacing those already + present or are they to be combined. + @param br_n_paths - The number of paths + @param br_paths - The array of paths +*/ +autoreply define bier_route_add_del +{ + u32 client_index; + u32 context; + u16 br_bp; + u8 br_is_add; + u8 br_is_replace; + vl_api_bier_table_id_t br_tbl_id; + u8 br_n_paths; + vl_api_fib_path3_t br_paths[br_n_paths]; +}; + +define bier_route_dump +{ + u32 client_index; + u32 context; + vl_api_bier_table_id_t br_tbl_id; +}; + +define bier_route_details +{ + u32 client_index; + u32 context; + u16 br_bp; + vl_api_bier_table_id_t br_tbl_id; + u32 br_n_paths; + vl_api_fib_path3_t br_paths[br_n_paths]; +}; + +/** \brief BIER Imposition Add + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param bi_tbl_id - The BIER table-id used to forward post encap + @param bi_src - The source Bit-position in the encap. + @param bi_is_add - Is this a route add or delete + @param bi_n_bytes - The number of bytes in the following bit-string + @param bi_bytes - The bit-string represented as a byte array +*/ +define bier_imp_add +{ + u32 client_index; + u32 context; + vl_api_bier_table_id_t bi_tbl_id; + u16 bi_src; + u8 bi_is_add; + u8 bi_n_bytes; + u8 bi_bytes[bi_n_bytes]; +}; + +/** \brief Reply for BIER route add / del request + @param context - returned sender context, to match reply w/ request + @param retval - return code + @param bi_index - The index of the created imposition object. +*/ +define bier_imp_add_reply +{ + u32 context; + i32 retval; + u32 bi_index; +}; + +/** \brief BIER Imposition Del + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param bi_index - The index of the imposition object (as returned + from the ADD) +*/ +autoreply define bier_imp_del +{ + u32 client_index; + u32 context; + u32 bi_index; +}; + +define bier_imp_dump +{ + u32 client_index; + u32 context; +}; + +define bier_imp_details +{ + u32 client_index; + u32 context; + vl_api_bier_table_id_t bi_tbl_id; + u16 bi_src; + u8 bi_n_bytes; + u8 bi_bytes[bi_n_bytes]; +}; + +/** \brief BIER Disposition Table Add / del route + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param bt_tbl_id - The BIER Disposition table-id. +*/ +autoreply define bier_disp_table_add_del +{ + u32 client_index; + u32 context; + u32 bdt_tbl_id; + u8 bdt_is_add; +}; + +define bier_disp_table_dump +{ + u32 client_index; + u32 context; +}; + +define bier_disp_table_details +{ + u32 context; + u32 bdt_tbl_id; +}; + +/** \brief BIER Disposition Entry Add / del + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param bde_bp - The Bit-position value for the entry + @param bde_tbl_id - The BIER dispositiontable-id the route is added in + @param bde_next_hop_sw_if_index - the nextop interface + @param bde_is_add - Is this a route add or delete + @param bde_payload_proto - The payload protocol for which the next-hop + is added + @param bde_next_hop_table_id - The table ID for the next-hop + @param bde_next_hop_proto_is_ip4 - The next-hop is IPV4 + @param bde_next_hop[16] - the nextop address. + Set this to all 0s for dispostion. +*/ +autoreply define bier_disp_entry_add_del +{ + u32 client_index; + u32 context; + u16 bde_bp; + u32 bde_tbl_id; + u8 bde_is_add; + u8 bde_payload_proto; + u8 bde_n_paths; + vl_api_fib_path3_t bde_paths[bde_n_paths]; +}; + +define bier_disp_entry_dump +{ + u32 client_index; + u32 context; + u32 bde_tbl_id; +}; + +define bier_disp_entry_details +{ + u32 context; + u16 bde_bp; + u32 bde_tbl_id; + u8 bde_is_add; + u8 bde_payload_proto; + u8 bde_n_paths; + vl_api_fib_path3_t bde_paths[bde_n_paths]; +}; + +/* + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/bier/bier_api.c b/src/vnet/bier/bier_api.c new file mode 100644 index 00000000000..aacee0910bf --- /dev/null +++ b/src/vnet/bier/bier_api.c @@ -0,0 +1,690 @@ +/* + *------------------------------------------------------------------ + * 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 <vnet/vnet.h> +#include <vlibmemory/api.h> + +#include <vnet/api_errno.h> +#include <vnet/bier/bier_table.h> +#include <vnet/bier/bier_imp.h> +#include <vnet/bier/bier_disp_table.h> +#include <vnet/bier/bier_disp_entry.h> +#include <vnet/bier/bier_bit_string.h> +#include <vnet/bier/bier_hdr_inlines.h> +#include <vnet/fib/fib_path_list.h> +#include <vnet/fib/fib_api.h> +#include <vnet/fib/fib_table.h> +#include <vnet/mfib/mfib_table.h> + +#include <vnet/vnet_msg_enum.h> + +#define vl_typedefs /* define message structures */ +#include <vnet/vnet_all_api_h.h> +#undef vl_typedefs + +#define vl_endianfun /* define message structures */ +#include <vnet/vnet_all_api_h.h> +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) +#define vl_printfun +#include <vnet/vnet_all_api_h.h> +#undef vl_printfun + +#include <vlibapi/api_helper_macros.h> + +#define foreach_bier_api_msg \ + _(BIER_TABLE_ADD_DEL, bier_table_add_del) \ + _(BIER_TABLE_DUMP, bier_table_dump) \ + _(BIER_ROUTE_ADD_DEL, bier_route_add_del) \ + _(BIER_ROUTE_DUMP, bier_route_dump) \ + _(BIER_IMP_ADD, bier_imp_add) \ + _(BIER_IMP_DEL, bier_imp_del) \ + _(BIER_IMP_DUMP, bier_imp_dump) \ + _(BIER_DISP_TABLE_ADD_DEL, bier_disp_table_add_del) \ + _(BIER_DISP_TABLE_DUMP, bier_disp_table_dump) \ + _(BIER_DISP_ENTRY_ADD_DEL, bier_disp_entry_add_del) \ + _(BIER_DISP_ENTRY_DUMP, bier_disp_entry_dump) + +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; + + 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) + { + bier_table_add_or_lock(&bti, ntohl(mp->bt_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 (unix_shared_memory_queue_t * q, + u32 context, + const bier_table_t *bt) +{ + vl_api_bier_table_details_t *mp; + + mp = vl_msg_api_alloc(sizeof(*mp)); + if (!mp) + return; + memset(mp, 0, sizeof(*mp)); + mp->_vl_msg_id = ntohs(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_msg_api_send_shmem (q, (u8 *) & mp); +} + +static void +vl_api_bier_table_dump_t_handler (vl_api_bier_table_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + bier_table_t *bt; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + pool_foreach(bt, bier_table_pool, + ({ + /* + * skip the ecmp tables. + */ + if (bier_table_is_main(bt)) + { + send_bier_table_details(q, 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, jj; + + vnm = vnet_get_main (); + vnm->api_errno = 0; + + bp = ntohs(mp->br_bp); + brpaths = NULL; + + if (0 == bp || bp > 0xffff) + { + rv = -1; + goto done; + } + + 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, + }; + + vec_validate(brpaths, mp->br_n_paths - 1); + + vec_foreach_index(ii, brpaths) + { + brpath = &brpaths[ii]; + memset(brpath, 0, sizeof(*brpath)); + brpath->frp_flags = FIB_ROUTE_PATH_BIER_FMASK; + + vec_validate(brpath->frp_label_stack, + mp->br_paths[ii].n_labels); + for (jj = 0; jj < mp->br_paths[ii].n_labels; jj++) + { + brpath->frp_label_stack[jj] = + ntohl(mp->br_paths[ii].label_stack[jj]); + } + + if (0 == mp->br_paths[ii].afi) + { + clib_memcpy (&brpath->frp_addr.ip4, + mp->br_paths[ii].next_hop, + sizeof (brpath->frp_addr.ip4)); + } + else + { + clib_memcpy (&brpath->frp_addr.ip6, + mp->br_paths[ii].next_hop, + sizeof (brpath->frp_addr.ip6)); + } + if (ip46_address_is_zero(&brpath->frp_addr)) + { + index_t bdti; + + bdti = bier_disp_table_find(ntohl(mp->br_paths[ii].table_id)); + + if (INDEX_INVALID != bdti) + brpath->frp_fib_index = bdti; + else + { + rv = VNET_API_ERROR_NO_SUCH_FIB; + goto done; + } + } + } + + if (mp->br_is_add) + { + bier_table_route_add(&bti, ntohs(mp->br_bp), brpaths); + } + else + { + bier_table_route_remove(&bti, ntohs(mp->br_bp), brpaths); + } + +done: + vec_free(brpaths); + rv = (rv == 0) ? vnm->api_errno : rv; + + REPLY_MACRO (VL_API_BIER_ROUTE_ADD_DEL_REPLY); +} + +typedef struct bier_route_details_walk_t_ +{ + unix_shared_memory_queue_t * q; + 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) +{ + fib_route_path_encode_t *api_rpaths = NULL, *api_rpath; + bier_route_details_walk_t *ctx = args; + vl_api_bier_route_details_t *mp; + vl_api_fib_path3_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_path3_t)); + mp = vl_msg_api_alloc(m_size); + if (!mp) + return; + + memset(mp, 0, m_size); + mp->_vl_msg_id = ntohs(VL_API_BIER_ROUTE_DETAILS); + mp->context = ctx->context; + + mp->br_tbl_id.bt_set = bt->bt_id.bti_set; + mp->br_tbl_id.bt_sub_domain = bt->bt_id.bti_sub_domain; + mp->br_tbl_id.bt_hdr_len_id = bt->bt_id.bti_hdr_len; + mp->br_bp = htons(be->be_bp); + mp->br_n_paths = htonl(n_paths); + + fib_path_list_walk(be->be_path_list, fib_path_encode, &api_rpaths); + + fp = mp->br_paths; + vec_foreach (api_rpath, api_rpaths) + { + fp->weight = api_rpath->rpath.frp_weight; + fp->preference = api_rpath->rpath.frp_preference; + fp->sw_if_index = htonl (api_rpath->rpath.frp_sw_if_index); + fp->n_labels = 0; + copy_fib_next_hop (api_rpath, fp); + fp++; + } + + vl_msg_api_send_shmem (ctx->q, (u8 *) & mp); +} + +static void +vl_api_bier_route_dump_t_handler (vl_api_bier_route_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + 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 = { + .q = q, + .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; + int rv = 0; + + vnm = vnet_get_main (); + vnm->api_errno = 0; + + 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); + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_BIER_IMP_ADD_REPLY, + ({ + rmp->bi_index = bii; + })); + /* *INDENT-OM* */ +} + +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 (unix_shared_memory_queue_t * q, + 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; + memset(mp, 0, sizeof(*mp)+n_bytes); + mp->_vl_msg_id = ntohs(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.bits, n_bytes); + + vl_msg_api_send_shmem (q, (u8 *) & mp); +} + +static void +vl_api_bier_imp_dump_t_handler (vl_api_bier_imp_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + bier_imp_t *bi; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + pool_foreach(bi, bier_imp_pool, + ({ + send_bier_imp_details(q, 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 (unix_shared_memory_queue_t * q, + 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; + memset(mp, 0, sizeof(*mp)); + mp->_vl_msg_id = ntohs(VL_API_BIER_DISP_TABLE_DETAILS); + mp->context = context; + + mp->bdt_tbl_id = htonl(bdt->bdt_table_id); + + vl_msg_api_send_shmem (q, (u8 *) & mp); +} + +static void +vl_api_bier_disp_table_dump_t_handler (vl_api_bier_disp_table_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + bier_disp_table_t *bdt; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + pool_foreach(bdt, bier_disp_table_pool, + ({ + send_bier_disp_table_details(q, 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); + + if (0 == bp || 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 (0 == mp->bde_paths[ii].afi) + { + clib_memcpy (&brp->frp_addr.ip4, + mp->bde_paths[ii].next_hop, + sizeof (brp->frp_addr.ip4)); + } + else + { + clib_memcpy (&brp->frp_addr.ip6, + mp->bde_paths[ii].next_hop, + 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_ +{ + unix_shared_memory_queue_t * q; + 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) +{ + fib_route_path_encode_t *api_rpaths = NULL, *api_rpath; + 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_path3_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) + { + n_paths = fib_path_list_get_n_paths(pl); + m_size = sizeof(*mp) + (n_paths * sizeof(vl_api_fib_path3_t)); + mp = vl_msg_api_alloc(m_size); + if (!mp) + return; + + memset(mp, 0, m_size); + mp->_vl_msg_id = ntohs(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(pl, fib_path_encode, &api_rpaths); + + fp = mp->bde_paths; + vec_foreach (api_rpath, api_rpaths) + { + fp->weight = api_rpath->rpath.frp_weight; + fp->preference = api_rpath->rpath.frp_preference; + fp->sw_if_index = htonl (api_rpath->rpath.frp_sw_if_index); + fp->n_labels = 0; + copy_fib_next_hop (api_rpath, fp); + fp++; + } + + vl_msg_api_send_shmem (ctx->q, (u8 *) & mp); + } + } +} + +static void +vl_api_bier_disp_entry_dump_t_handler (vl_api_bier_disp_entry_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + bier_disp_entry_details_walk_t ctx = { + .q = q, + .context = mp->context, + }; + bier_disp_table_walk(ntohl(mp->bde_tbl_id), + send_bier_disp_entry_details, + &ctx); +} + +#define vl_msg_name_crc_list +#include <vnet/bier/bier.api.h> +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (api_main_t * am) +{ +#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id); + foreach_vl_msg_name_crc_bier; +#undef _ +} + +static clib_error_t * +bier_api_hookup (vlib_main_t * vm) +{ + api_main_t *am = &api_main; + +#define _(N,n) \ + vl_msg_api_set_handlers(VL_API_##N, #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_bier_api_msg; +#undef _ + + /* + * Set up the (msg_name, crc, message-id) table + */ + setup_message_id_table (am); + + return 0; +} + +VLIB_API_INIT_FUNCTION (bier_api_hookup); diff --git a/src/vnet/bier/bier_bit_string.c b/src/vnet/bier/bier_bit_string.c new file mode 100644 index 00000000000..aa42e083be8 --- /dev/null +++ b/src/vnet/bier/bier_bit_string.c @@ -0,0 +1,109 @@ +/* + * 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 <vnet/vnet.h> + +#include <vnet/bier/bier_types.h> +#include <vnet/bier/bier_bit_string.h> + +/* + * the first bit in the first byte is bit position 1. + * bit position 0 is not valid + */ +#define BIER_GET_STRING_POS(_bp, _byte, _bit, _str) \ +{ \ + _bp--; \ + _byte = ((BIER_BBS_LEN_TO_BUCKETS((_str)->bbs_len) - 1 ) - \ + (_bp / BIER_BIT_MASK_BITS_PER_BUCKET)); \ + _bit = _bp % BIER_BIT_MASK_BITS_PER_BUCKET; \ +} + +static inline int +bier_bit_pos_is_valid (bier_bp_t bp, const bier_bit_string_t *bbs) +{ + if (!((bp <= BIER_BBS_LEN_TO_BITS((bbs)->bbs_len)) && + (bp >= 1))) { + return (0); + } + return (1); +} + +/* + * Validate a bit poistion + */ +#define BIER_BIT_POS_IS_VALID(_bp, _str) \ +{ \ + if (!bier_bit_pos_is_valid(_bp, _str)) return; \ +} + +void +bier_bit_string_set_bit (bier_bit_string_t *bit_string, + bier_bp_t bp) +{ + bier_bit_mask_bucket_t bmask; + uint16_t byte_pos, bit_pos; + + BIER_BIT_POS_IS_VALID(bp, bit_string); + BIER_GET_STRING_POS(bp, byte_pos, bit_pos, bit_string); + + bmask = ((bier_bit_mask_bucket_t)1 << bit_pos); + bit_string->bbs_buckets[byte_pos] |= bmask; +} + +void +bier_bit_string_clear_bit (bier_bit_string_t *bit_string, + bier_bp_t bp) +{ + uint16_t byte_pos, bit_pos; + + BIER_BIT_POS_IS_VALID(bp, bit_string); + BIER_GET_STRING_POS(bp, byte_pos, bit_pos, bit_string); + + bit_string->bbs_buckets[byte_pos] &= ~(1 << bit_pos); +} + +u8 * +format_bier_bit_string (u8 * string, + va_list * args) +{ + bier_bit_string_t *bs = va_arg(*args, bier_bit_string_t *); + int leading_marker = 0; + int suppress_zero = 0; + u16 index; + u32 *ptr; + + ptr = (u32 *)bs->bbs_buckets; + + string = format(string, "%d#", (8 * bs->bbs_len)); + + for (index = 0; index < (bs->bbs_len/4); index++) { + if (!ptr[index]) { + if (!leading_marker) { + leading_marker = 1; + suppress_zero = 1; + string = format(string, ":"); + continue; + } + if (suppress_zero) continue; + } else { + suppress_zero = 0; + } + + string = format(string, "%s%X", index ? ":" : "", + clib_net_to_host_u32(ptr[index])); + } + + return (string); +} diff --git a/src/vnet/bier/bier_bit_string.h b/src/vnet/bier/bier_bit_string.h new file mode 100644 index 00000000000..9baebfa6e69 --- /dev/null +++ b/src/vnet/bier/bier_bit_string.h @@ -0,0 +1,107 @@ +/* + * 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. + */ + +#ifndef __BIER_BIT_STRING_H__ +#define __BIER_BIT_STRING_H__ + +#include <vppinfra/byte_order.h> +#include <vppinfra/format.h> + +#include <vnet/bier/bier_types.h> + +#define BIER_BBS_LEN_TO_BUCKETS(_len) (_len) +#define BIER_BBS_LEN_TO_BITS(_len) (_len * 8) +#define BIER_BBS_LEN_TO_INTS(_len) ((_len) / sizeof(int)) +#define BIER_BIT_MASK_BITS_PER_INT (sizeof(int) * 8) + +/* + * bier_find_first_bit_set + * + * find the position of the first bit set in a long + */ +static inline int +bier_find_first_bit_string_set (int mask) +{ + return (__builtin_ffs(clib_net_to_host_u32(mask))); +} + +extern void bier_bit_string_set_bit(bier_bit_string_t *mask, + bier_bp_t bp); + + +extern void bier_bit_string_clear_bit(bier_bit_string_t *mask, + bier_bp_t bp); + + +extern u8 *format_bier_bit_string(u8 * s, va_list * args); + +#define BIER_BBS_NUM_INT_BUKCETS(_bbs) \ + (BIER_BBS_LEN_TO_BUCKETS(_bbs->bbs_len) / sizeof(int)) + +always_inline int +bier_bit_string_is_zero (const bier_bit_string_t *src) +{ + uint16_t index; + + for (index = 0; + index < BIER_BBS_NUM_INT_BUKCETS(src); + index++) { + if (((int*)src->bbs_buckets)[index] != 0) { + return (0); + } + } + return (1); +} + +always_inline void +bier_bit_string_clear_string (const bier_bit_string_t *src, + bier_bit_string_t *dest) +{ + uint16_t index; + + ASSERT(src->bbs_len == dest->bbs_len); + + for (index = 0; + index < BIER_BBS_NUM_INT_BUKCETS(src); + index++) { + ((int*)dest->bbs_buckets)[index] &= ~(((int*)src->bbs_buckets)[index]); + } +} + +always_inline void +bier_bit_string_logical_and_string (const bier_bit_string_t *src, + bier_bit_string_t *dest) +{ + uint16_t index; + + ASSERT(src->bbs_len == dest->bbs_len); + + for (index = 0; + index < BIER_BBS_NUM_INT_BUKCETS(src); + index++) { + ((int*)dest->bbs_buckets)[index] &= ((int*)src->bbs_buckets)[index]; + } +} + +always_inline void +bier_bit_string_init (bier_bit_string_t *bbs, + bier_hdr_len_id_t len, + bier_bit_mask_bucket_t *buckets) +{ + bbs->bbs_len = bier_hdr_len_id_to_num_bytes(len); + bbs->bbs_buckets = buckets; +} + +#endif diff --git a/src/vnet/bier/bier_disp_dispatch_node.c b/src/vnet/bier/bier_disp_dispatch_node.c new file mode 100644 index 00000000000..a00c2eea5f9 --- /dev/null +++ b/src/vnet/bier/bier_disp_dispatch_node.c @@ -0,0 +1,150 @@ +/* + * 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 <vnet/bier/bier_disp_entry.h> +#include <vnet/bier/bier_hdr_inlines.h> + +/** + * @brief A struct to hold tracing information for the MPLS label imposition + * node. + */ +typedef struct bier_disp_dispatch_trace_t_ +{ + /** + * BIER payload protocol used to dispatch + */ + bier_hdr_proto_id_t pproto; + + /** + * RPF-ID packet is tagged with + */ + u32 rpf_id; +} bier_disp_dispatch_trace_t; + +always_inline uword +bier_disp_dispatch_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + u32 n_left_from, next_index, * from, * to_next; + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + bier_hdr_proto_id_t pproto0; + bier_disp_entry_t *bde0; + u32 next0, bi0, bdei0; + const dpo_id_t *dpo0; + vlib_buffer_t * b0; + bier_hdr_t *hdr0; + u32 entropy0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + bdei0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX]; + hdr0 = vlib_buffer_get_current(b0); + bde0 = bier_disp_entry_get(bdei0); + + /* + * header is in network order - flip it, we are about to + * consume it anyway + */ + bier_hdr_ntoh(hdr0); + pproto0 = bier_hdr_get_proto_id(hdr0); + entropy0 = bier_hdr_get_entropy(hdr0); + + /* + * strip the header and copy the entropy value into + * the packets flow-hash field + * DSCP mumble mumble... + */ + vlib_buffer_advance(b0, (vnet_buffer(b0)->bier.n_bytes + + sizeof(*hdr0))); + vnet_buffer(b0)->ip.flow_hash = entropy0; + + /* + * use the payload proto to dispatch to the + * correct stacked DPO. + */ + dpo0 = &bde0->bde_fwd[pproto0].bde_dpo; + next0 = dpo0->dpoi_next_node; + vnet_buffer(b0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index; + vnet_buffer(b0)->ip.rpf_id = bde0->bde_fwd[pproto0].bde_rpf_id; + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + bier_disp_dispatch_trace_t *tr = + vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->pproto = pproto0; + tr->rpf_id = vnet_buffer(b0)->ip.rpf_id; + } + + vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + return from_frame->n_vectors; +} + +static u8 * +format_bier_disp_dispatch_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + bier_disp_dispatch_trace_t * t; + + t = va_arg (*args, bier_disp_dispatch_trace_t *); + s = format (s, "%U", format_bier_hdr_proto, t->pproto); + + return (s); +} + +static uword +bier_disp_dispatch (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (bier_disp_dispatch_inline(vm, node, frame)); +} + +VLIB_REGISTER_NODE (bier_disp_dispatch_node) = { + .function = bier_disp_dispatch, + .name = "bier-disp-dispatch", + .vector_size = sizeof (u32), + + .format_trace = format_bier_disp_dispatch_trace, + .n_next_nodes = 1, + .next_nodes = { + [0] = "bier-drop", + } +}; +VLIB_NODE_FUNCTION_MULTIARCH (bier_disp_dispatch_node, bier_disp_dispatch) diff --git a/src/vnet/bier/bier_disp_entry.c b/src/vnet/bier/bier_disp_entry.c new file mode 100644 index 00000000000..3326aba2f86 --- /dev/null +++ b/src/vnet/bier/bier_disp_entry.c @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2017 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. + */ +/** + * bier_dispositon : The BIER dispositon object + * + * A BIER dispositon object is present in the IP mcast output list + * and represents the dispositon of a BIER bitmask. After BIER header + * dispositon the packet is forward within the appropriate/specifid + * BIER table + */ + +#include <vnet/bier/bier_disp_entry.h> +#include <vnet/bier/bier_hdr_inlines.h> +#include <vnet/fib/fib_path_list.h> +#include <vnet/dpo/drop_dpo.h> + +/** + * The memory pool of all imp objects + */ +bier_disp_entry_t *bier_disp_entry_pool; + +/** + * When constructing the BIER imp ID from an index and BSL, shift + * the BSL this far + */ +#define BIER_DISP_ENTRY_ID_HLEN_SHIFT 24 + +static void +bier_disp_entry_lock_i (bier_disp_entry_t *bde) +{ + bde->bde_locks++; +} + +void +bier_disp_entry_lock (index_t bdei) +{ + bier_disp_entry_lock_i(bier_disp_entry_get(bdei)); +} + +static index_t +bier_disp_entry_get_index(bier_disp_entry_t *bde) +{ + return (bde - bier_disp_entry_pool); +} + +index_t +bier_disp_entry_add_or_lock (void) +{ + dpo_id_t invalid = DPO_INVALID; + bier_hdr_proto_id_t pproto; + bier_disp_entry_t *bde; + + pool_get_aligned(bier_disp_entry_pool, bde, CLIB_CACHE_LINE_BYTES); + + bde->bde_locks = 0; + + FOR_EACH_BIER_HDR_PROTO(pproto) + { + bde->bde_fwd[pproto].bde_dpo = invalid; + bde->bde_fwd[pproto].bde_rpf_id = ~0; + bde->bde_pl[pproto] = FIB_NODE_INDEX_INVALID; + } + + bier_disp_entry_lock_i(bde); + return (bier_disp_entry_get_index(bde)); +} + +void +bier_disp_entry_unlock (index_t bdei) +{ + bier_disp_entry_t *bde; + + if (INDEX_INVALID == bdei) + { + return; + } + + bde = bier_disp_entry_get(bdei); + + bde->bde_locks--; + + if (0 == bde->bde_locks) + { + bier_hdr_proto_id_t pproto; + + FOR_EACH_BIER_HDR_PROTO(pproto) + { + dpo_unlock(&bde->bde_fwd[pproto].bde_dpo); + bde->bde_fwd[pproto].bde_rpf_id = ~0; + fib_path_list_unlock(bde->bde_pl[pproto]); + } + pool_put(bier_disp_entry_pool, bde); + } +} + +typedef struct bier_disp_entry_path_list_walk_ctx_t_ +{ + u32 bdew_rpf_id; +} bier_disp_entry_path_list_walk_ctx_t; + +static fib_path_list_walk_rc_t +bier_disp_entry_path_list_walk (fib_node_index_t pl_index, + fib_node_index_t path_index, + void *arg) +{ + bier_disp_entry_path_list_walk_ctx_t *ctx = arg; + + ctx->bdew_rpf_id = fib_path_get_rpf_id(path_index); + + if (~0 != ctx->bdew_rpf_id) + { + return (FIB_PATH_LIST_WALK_STOP); + } + return (FIB_PATH_LIST_WALK_CONTINUE); +} + +static void +bier_disp_entry_restack (bier_disp_entry_t *bde, + bier_hdr_proto_id_t pproto) +{ + dpo_id_t via_dpo = DPO_INVALID; + fib_node_index_t pli; + + pli = bde->bde_pl[pproto]; + + if (FIB_NODE_INDEX_INVALID == pli) + { + dpo_copy(&via_dpo, + drop_dpo_get(bier_hdr_proto_to_dpo(pproto))); + } + else + { + fib_path_list_contribute_forwarding(pli, + fib_forw_chain_type_from_dpo_proto( + bier_hdr_proto_to_dpo(pproto)), + &via_dpo); + + bier_disp_entry_path_list_walk_ctx_t ctx = { + .bdew_rpf_id = ~0, + }; + + fib_path_list_walk(pli, bier_disp_entry_path_list_walk, &ctx); + bde->bde_fwd[pproto].bde_rpf_id = ctx.bdew_rpf_id; + } + + dpo_stack(DPO_BIER_DISP_ENTRY, + DPO_PROTO_BIER, + &bde->bde_fwd[pproto].bde_dpo, + &via_dpo); +} + +void +bier_disp_entry_path_add (index_t bdei, + bier_hdr_proto_id_t pproto, + const fib_route_path_t *rpaths) +{ + fib_node_index_t *pli, old_pli; + bier_disp_entry_t *bde; + + bde = bier_disp_entry_get(bdei); + pli = &bde->bde_pl[pproto]; + old_pli = *pli; + + /* + * create a new or update the exisitng path-list for this + * payload protocol + */ + if (FIB_NODE_INDEX_INVALID == *pli) + { + *pli = fib_path_list_create((FIB_PATH_LIST_FLAG_SHARED | + FIB_PATH_LIST_FLAG_NO_URPF), + rpaths); + } + else + { + *pli = fib_path_list_copy_and_path_add(old_pli, + (FIB_PATH_LIST_FLAG_SHARED | + FIB_PATH_LIST_FLAG_NO_URPF), + rpaths); + } + + fib_path_list_lock(*pli); + fib_path_list_unlock(old_pli); + + bier_disp_entry_restack(bde, pproto); +} + +int +bier_disp_entry_path_remove (index_t bdei, + bier_hdr_proto_id_t pproto, + const fib_route_path_t *rpaths) +{ + fib_node_index_t *pli, old_pli; + bier_disp_entry_t *bde; + + bde = bier_disp_entry_get(bdei); + pli = &bde->bde_pl[pproto]; + old_pli = *pli; + + /* + * update the exisitng path-list for this payload protocol + */ + if (FIB_NODE_INDEX_INVALID != *pli) + { + *pli = fib_path_list_copy_and_path_remove(old_pli, + (FIB_PATH_LIST_FLAG_SHARED | + FIB_PATH_LIST_FLAG_NO_URPF), + rpaths); + + fib_path_list_lock(*pli); + fib_path_list_unlock(old_pli); + + bier_disp_entry_restack(bde, pproto); + } + + /* + * if there are no path-list defined for any payload protocol + * then this entry is OK for removal + */ + int remove = 1; + + FOR_EACH_BIER_HDR_PROTO(pproto) + { + if (FIB_NODE_INDEX_INVALID != bde->bde_pl[pproto]) + { + remove = 0; + break; + } + } + + return (remove); +} + +u8* +format_bier_disp_entry (u8* s, va_list *args) +{ + index_t bdei = va_arg (*args, index_t); + u32 indent = va_arg(*args, u32); + bier_show_flags_t flags = va_arg(*args, bier_show_flags_t); + bier_hdr_proto_id_t pproto; + bier_disp_entry_t *bde; + + bde = bier_disp_entry_get(bdei); + + s = format(s, "bier-disp:[%d]", bdei); + + FOR_EACH_BIER_HDR_PROTO(pproto) + { + if (INDEX_INVALID != bde->bde_pl[pproto]) + { + s = format(s, "\n"); + s = fib_path_list_format(bde->bde_pl[pproto], s); + + if (flags & BIER_SHOW_DETAIL) + { + s = format(s, "\n%UForwarding:", + format_white_space, indent); + s = format(s, "\n%Urpf-id:%d", + format_white_space, indent+1, + bde->bde_fwd[pproto].bde_rpf_id); + s = format(s, "\n%U%U", + format_white_space, indent+1, + format_dpo_id, &bde->bde_fwd[pproto].bde_dpo, indent+2); + } + } + } + return (s); +} + +void +bier_disp_entry_contribute_forwarding (index_t bdei, + dpo_id_t *dpo) +{ + dpo_set(dpo, DPO_BIER_DISP_ENTRY, DPO_PROTO_BIER, bdei); +} + +const static char* const bier_disp_entry_bier_nodes[] = +{ + "bier-disp-dispatch", + NULL, +}; + +const static char* const * const bier_disp_entry_nodes[DPO_PROTO_NUM] = +{ + [DPO_PROTO_BIER] = bier_disp_entry_bier_nodes, +}; + +static void +bier_disp_entry_dpo_lock (dpo_id_t *dpo) +{ + bier_disp_entry_lock(dpo->dpoi_index); +} + +static void +bier_disp_entry_dpo_unlock (dpo_id_t *dpo) +{ + bier_disp_entry_unlock(dpo->dpoi_index); +} + +static void +bier_disp_entry_dpo_mem_show (void) +{ + fib_show_memory_usage("BIER dispositon", + pool_elts(bier_disp_entry_pool), + pool_len(bier_disp_entry_pool), + sizeof(bier_disp_entry_t)); +} + +static u8* +format_bier_disp_entry_dpo (u8* s, va_list *ap) +{ + index_t index = va_arg(*ap, index_t); + u32 indent = va_arg(*ap, u32); + + s = format(s, "%U", format_bier_disp_entry, index, indent, BIER_SHOW_DETAIL); + + return (s); +} + +const static dpo_vft_t bier_disp_entry_vft = { + .dv_lock = bier_disp_entry_dpo_lock, + .dv_unlock = bier_disp_entry_dpo_unlock, + .dv_format = format_bier_disp_entry_dpo, + .dv_mem_show = bier_disp_entry_dpo_mem_show, +}; + +clib_error_t * +bier_disp_entry_db_module_init (vlib_main_t *vm) +{ + dpo_register(DPO_BIER_DISP_ENTRY, + &bier_disp_entry_vft, + bier_disp_entry_nodes); + + return (NULL); +} + +VLIB_INIT_FUNCTION (bier_disp_entry_db_module_init); + +static clib_error_t * +show_bier_disp_entry (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + index_t bdei; + + bdei = INDEX_INVALID; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { + if (unformat (input, "%d", &bdei)) + ; + else + { + break; + } + } + + if (INDEX_INVALID == bdei) + { + return (NULL); + } + else + { + vlib_cli_output(vm, "%U", format_bier_disp_entry, bdei, 1, + BIER_SHOW_DETAIL); + } + return (NULL); +} + +VLIB_CLI_COMMAND (show_bier_disp_entry_node, static) = { + .path = "show bier disp entry", + .short_help = "show bier disp entry index", + .function = show_bier_disp_entry, +}; diff --git a/src/vnet/bier/bier_disp_entry.h b/src/vnet/bier/bier_disp_entry.h new file mode 100644 index 00000000000..34ca5d4a09a --- /dev/null +++ b/src/vnet/bier/bier_disp_entry.h @@ -0,0 +1,81 @@ +/* + * 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. + */ +/** + * bier_dispositon : The BIER dispositon object + * + * A BIER dispositon object is used to pop the BIER header for for-us + * packets and steer the packet down the payload protocol specific graph + */ + +#ifndef __BIER_DISP_ENTRY_H__ +#define __BIER_DISP_ENTRY_H__ + +#include <vnet/bier/bier_types.h> +#include <vnet/fib/fib_types.h> +#include <vnet/dpo/dpo.h> + +/** + * The BIER dispositon object + */ +typedef struct bier_disp_entry_t_ { + /** + * The DPO contirubted from the per-payload protocol parents + * on cachline 1. + */ + struct + { + dpo_id_t bde_dpo; + u32 bde_rpf_id; + } bde_fwd[BIER_HDR_N_PROTO]; + + /** + * number of locks + */ + u32 bde_locks; + + /** + * The path-lists used by per-payload protocol parents. + * We don't add the disp entry to the graph as a sibling + * since there is nothing we can do with the updates to + * forwarding. + */ + fib_node_index_t bde_pl[BIER_HDR_N_PROTO]; +} bier_disp_entry_t; + +extern index_t bier_disp_entry_add_or_lock(void); +extern void bier_disp_entry_path_add(index_t bdei, + bier_hdr_proto_id_t pproto, + const fib_route_path_t *rpaths); +extern int bier_disp_entry_path_remove(index_t bdei, + bier_hdr_proto_id_t pproto, + const fib_route_path_t *rpaths); + +extern void bier_disp_entry_unlock(index_t bdi); +extern void bier_disp_entry_lock(index_t bdi); + +extern u8* format_bier_disp_entry(u8* s, va_list *ap); + +extern void bier_disp_entry_contribute_forwarding(index_t bdi, + dpo_id_t *dpo); + +extern bier_disp_entry_t *bier_disp_entry_pool; + +always_inline bier_disp_entry_t* +bier_disp_entry_get (index_t bdi) +{ + return (pool_elt_at_index(bier_disp_entry_pool, bdi)); +} + +#endif diff --git a/src/vnet/bier/bier_disp_lookup_node.c b/src/vnet/bier/bier_disp_lookup_node.c new file mode 100644 index 00000000000..15515f467ca --- /dev/null +++ b/src/vnet/bier/bier_disp_lookup_node.c @@ -0,0 +1,147 @@ +/* + * 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 <vnet/bier/bier_disp_table.h> +#include <vnet/bier/bier_hdr_inlines.h> + +/** + * @brief A struct to hold tracing information for the MPLS label imposition + * node. + */ +typedef struct bier_disp_lookup_trace_t_ +{ + /** + * BIER source BP used in the lookup - host order + */ + bier_bp_t bp; + /** + * BIER disp table + */ + index_t bdti; +} bier_disp_lookup_trace_t; + +/** + * Next nodes from BIER disposition lookup + */ +typedef enum bier_disp_lookup_next_t_ +{ + BIER_DISP_LOOKUP_NEXT_DROP, + BIER_DISP_LOOKUP_NEXT_DISPATCH, +} bier_disp_lookup_next_t; +#define BIER_DISP_LOOKUP_N_NEXT (BIER_DISP_LOOKUP_NEXT_DISPATCH+1) + +always_inline uword +bier_disp_lookup_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + u32 n_left_from, next_index, * from, * to_next; + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + const bier_hdr_t *hdr0; + vlib_buffer_t * b0; + u32 bdei0, bdti0; + u32 next0, bi0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + bdti0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX]; + hdr0 = vlib_buffer_get_current(b0); + + /* + * lookup - source is in network order. + */ + bdei0 = bier_disp_table_lookup(bdti0, bier_hdr_get_src_id(hdr0)); + + if (PREDICT_FALSE(INDEX_INVALID == bdei0)) + { + next0 = BIER_DISP_LOOKUP_NEXT_DROP; + } + else + { + next0 = BIER_DISP_LOOKUP_NEXT_DISPATCH; + } + + vnet_buffer(b0)->ip.adj_index[VLIB_TX] = bdei0; + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + bier_disp_lookup_trace_t *tr = + vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->bp = clib_net_to_host_u16(bier_hdr_get_src_id(hdr0)); + tr->bdti = bdti0; + } + + vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + return from_frame->n_vectors; +} + +static u8 * +format_bier_disp_lookup_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + bier_disp_lookup_trace_t * t; + + t = va_arg (*args, bier_disp_lookup_trace_t *); + s = format (s, "tbl:%d src:%d", t->bdti, t->bp); + + return (s); +} + +static uword +bier_disp_lookup (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (bier_disp_lookup_inline(vm, node, frame)); +} + +VLIB_REGISTER_NODE (bier_disp_lookup_node) = { + .function = bier_disp_lookup, + .name = "bier-disp-lookup", + .vector_size = sizeof (u32), + + .format_trace = format_bier_disp_lookup_trace, + .n_next_nodes = BIER_DISP_LOOKUP_N_NEXT, + .next_nodes = { + [BIER_DISP_LOOKUP_NEXT_DROP] = "bier-drop", + [BIER_DISP_LOOKUP_NEXT_DISPATCH] = "bier-disp-dispatch", + } +}; +VLIB_NODE_FUNCTION_MULTIARCH (bier_disp_lookup_node, bier_disp_lookup) diff --git a/src/vnet/bier/bier_disp_table.c b/src/vnet/bier/bier_disp_table.c new file mode 100644 index 00000000000..5caf86d3705 --- /dev/null +++ b/src/vnet/bier/bier_disp_table.c @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2017 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 <vnet/bier/bier_disp_table.h> +#include <vnet/bier/bier_disp_entry.h> + +/** + * memory pool for disposition tables + */ +bier_disp_table_t *bier_disp_table_pool; + +/** + * Hash table to map client table IDs to VPP index + */ +static uword *bier_disp_table_id_to_index; + +static index_t +bier_disp_table_get_index (const bier_disp_table_t *bdt) +{ + return (bdt - bier_disp_table_pool); +} + +static void +bier_disp_table_lock_i (bier_disp_table_t *bdt) +{ + bdt->bdt_locks++; +} + +index_t +bier_disp_table_find(u32 table_id) +{ + uword *p; + + p = hash_get(bier_disp_table_id_to_index, table_id); + + if (NULL != p) + { + return (p[0]); + } + + return (INDEX_INVALID); +} + +index_t +bier_disp_table_add_or_lock (u32 table_id) +{ + bier_disp_table_t *bdt; + index_t bdti; + + bdti = bier_disp_table_find(table_id); + + if (INDEX_INVALID == bdti) + { + pool_get_aligned(bier_disp_table_pool, bdt, + CLIB_CACHE_LINE_BYTES); + + bdt->bdt_table_id = table_id; + bdt->bdt_locks = 0; + + hash_set(bier_disp_table_id_to_index, table_id, + bier_disp_table_get_index(bdt)); + + /** + * Set the result for each entry in the DB to be invalid + */ + memset(bdt->bdt_db, 0xff, sizeof(bdt->bdt_db)); + } + else + { + bdt = pool_elt_at_index(bier_disp_table_pool, bdti); + } + + bier_disp_table_lock_i(bdt); + + return (bier_disp_table_get_index(bdt)); +} + +void +bier_disp_table_unlock_w_table_id (u32 table_id) +{ + index_t bdti; + + bdti = bier_disp_table_find(table_id); + + if (INDEX_INVALID != bdti) + { + bier_disp_table_unlock(bdti); + } +} + +void +bier_disp_table_unlock (index_t bdti) +{ + bier_disp_table_t *bdt; + + bdt = bier_disp_table_get(bdti); + + bdt->bdt_locks--; + + if (0 == bdt->bdt_locks) + { + u32 ii; + + for (ii = 0; ii < BIER_BP_MAX; ii++) + { + bier_disp_entry_unlock(bdt->bdt_db[ii]); + } + hash_unset(bier_disp_table_id_to_index, bdt->bdt_table_id); + pool_put(bier_disp_table_pool, bdt); + } +} + +void +bier_disp_table_lock (index_t bdti) +{ + bier_disp_table_lock_i(bier_disp_table_get(bdti)); +} + +void +bier_disp_table_contribute_forwarding (index_t bdti, + dpo_id_t *dpo) +{ + dpo_set(dpo, + DPO_BIER_DISP_TABLE, + DPO_PROTO_BIER, + bdti); +} + + +u8* +format_bier_disp_table (u8* s, va_list *ap) +{ + index_t bdti = va_arg(*ap, index_t); + u32 indent = va_arg(*ap, u32); + bier_show_flags_t flags = va_arg(*ap, bier_show_flags_t); + bier_disp_table_t *bdt; + + bdt = bier_disp_table_get(bdti); + + s = format(s, "bier-disp-table:[%d]; table-id:%d locks:%d", + bdti, bdt->bdt_table_id, bdt->bdt_locks); + + if (flags & BIER_SHOW_DETAIL) + { + u32 ii; + + for (ii = 0; ii < BIER_BP_MAX; ii++) + { + if (INDEX_INVALID != bdt->bdt_db[ii]) + { + u16 src = ii; + s = format(s, "\n%Usrc:%d", format_white_space, indent, + clib_host_to_net_u16(src)); + s = format(s, "\n%U%U", format_white_space, indent+2, + format_bier_disp_entry, bdt->bdt_db[ii], + indent+4, BIER_SHOW_BRIEF); + } + } + } + return (s); +} + +static u8* +format_bier_disp_table_dpo (u8* s, va_list *ap) +{ + index_t bdti = va_arg(*ap, index_t); + u32 indent = va_arg(*ap, u32); + + return (format(s, "%U", + format_bier_disp_table, bdti, indent, + BIER_SHOW_BRIEF)); +} + +static void +bier_disp_table_entry_insert (index_t bdti, + bier_bp_t src, + index_t bdei) +{ + bier_disp_table_t *bdt; + + bdt = bier_disp_table_get(bdti); + bdt->bdt_db[clib_host_to_net_u16(src)] = bdei; +} + +static void +bier_disp_table_entry_remove (index_t bdti, + bier_bp_t src) +{ + bier_disp_table_t *bdt; + + bdt = bier_disp_table_get(bdti); + bdt->bdt_db[clib_host_to_net_u16(src)] = INDEX_INVALID; +} + +static index_t +bier_disp_table_lookup_hton(index_t bdti, + bier_bp_t src) +{ + return (bier_disp_table_lookup(bdti, clib_host_to_net_u16(src))); +} + +void +bier_disp_table_entry_path_add (u32 table_id, + bier_bp_t src, + bier_hdr_proto_id_t payload_proto, + const fib_route_path_t *rpaths) +{ + index_t bdti, bdei; + + bdti = bier_disp_table_find(table_id); + + if (INDEX_INVALID == bdti) + { + return; + } + + bdei = bier_disp_table_lookup_hton(bdti, src); + + if (INDEX_INVALID == bdei) + { + bdei = bier_disp_entry_add_or_lock(); + bier_disp_table_entry_insert(bdti, src, bdei); + } + + bier_disp_entry_path_add(bdei, payload_proto, rpaths); +} + +void +bier_disp_table_entry_path_remove (u32 table_id, + bier_bp_t src, + bier_hdr_proto_id_t payload_proto, + const fib_route_path_t *rpath) +{ + index_t bdti, bdei; + + bdti = bier_disp_table_find(table_id); + + if (INDEX_INVALID == bdti) + { + return; + } + + bdei = bier_disp_table_lookup_hton(bdti, src); + + if (INDEX_INVALID != bdei) + { + int remove; + + remove = bier_disp_entry_path_remove(bdei, payload_proto, rpath); + + if (remove) + { + bier_disp_table_entry_remove(bdti, src); + bier_disp_entry_unlock(bdei); + } + } +} + +void +bier_disp_table_walk (u32 table_id, + bier_disp_table_walk_fn_t fn, + void *ctx) +{ + const bier_disp_table_t *bdt; + const bier_disp_entry_t *bde; + index_t bdti; + u32 ii; + + bdti = bier_disp_table_find(table_id); + + if (INDEX_INVALID != bdti) + { + bdt = bier_disp_table_get(bdti); + + for (ii = 0; ii < BIER_BP_MAX; ii++) + { + if (INDEX_INVALID != bdt->bdt_db[ii]) + { + u16 src = ii; + + bde = bier_disp_entry_get(bdt->bdt_db[ii]); + + fn(bdt, bde, clib_host_to_net_u16(src), ctx); + } + } + } +} + +static void +bier_disp_table_dpo_lock (dpo_id_t *dpo) +{ + bier_disp_table_lock(dpo->dpoi_index); +} + +static void +bier_disp_table_dpo_unlock (dpo_id_t *dpo) +{ + bier_disp_table_unlock(dpo->dpoi_index); +} + +static void +bier_disp_table_dpo_mem_show (void) +{ + fib_show_memory_usage("BIER disposition table", + pool_elts(bier_disp_table_pool), + pool_len(bier_disp_table_pool), + sizeof(bier_disp_table_t)); +} + +const static dpo_vft_t bier_disp_table_dpo_vft = { + .dv_lock = bier_disp_table_dpo_lock, + .dv_unlock = bier_disp_table_dpo_unlock, + .dv_mem_show = bier_disp_table_dpo_mem_show, + .dv_format = format_bier_disp_table_dpo, +}; + +const static char *const bier_disp_table_bier_nodes[] = +{ + "bier-disp-lookup" +}; + +const static char * const * const bier_disp_table_nodes[DPO_PROTO_NUM] = +{ + [DPO_PROTO_BIER] = bier_disp_table_bier_nodes, +}; + +clib_error_t * +bier_disp_table_module_init (vlib_main_t *vm) +{ + dpo_register(DPO_BIER_DISP_TABLE, + &bier_disp_table_dpo_vft, + bier_disp_table_nodes); + + bier_disp_table_id_to_index = hash_create(0, sizeof(index_t)); + + return (NULL); +} + +VLIB_INIT_FUNCTION (bier_disp_table_module_init); + +static clib_error_t * +show_bier_disp_table (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + bier_disp_table_t *bdt; + index_t bdti; + + bdti = INDEX_INVALID; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { + if (unformat (input, "%d", &bdti)) + ; + else if (unformat (input, "%d", &bdti)) + ; + else + { + break; + } + } + + if (INDEX_INVALID == bdti) + { + pool_foreach(bdt, bier_disp_table_pool, + ({ + vlib_cli_output(vm, "%U", format_bier_disp_table, + bier_disp_table_get_index(bdt), + 1, + BIER_SHOW_BRIEF); + })); + } + else + { + vlib_cli_output(vm, "%U", format_bier_disp_table, bdti, 1, + BIER_SHOW_DETAIL); + } + return (NULL); +} + +VLIB_CLI_COMMAND (show_bier_disp_table_node, static) = { + .path = "show bier disp table", + .short_help = "show bier disp table [index]", + .function = show_bier_disp_table, +}; diff --git a/src/vnet/bier/bier_disp_table.h b/src/vnet/bier/bier_disp_table.h new file mode 100644 index 00000000000..18726c8ee6a --- /dev/null +++ b/src/vnet/bier/bier_disp_table.h @@ -0,0 +1,110 @@ +/* + * 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. + */ + +#ifndef __BIER_DISP_TABLE_H__ +#define __BIER_DISP_TABLE_H__ + +#include <vnet/ip/ip.h> +#include <vnet/adj/adj.h> +#include <vnet/dpo/replicate_dpo.h> + +#include <vnet/bier/bier_types.h> +#include <vnet/bier/bier_disp_entry.h> + +/** + * @brief + * A protocol Independent IP multicast FIB table + */ +typedef struct bier_disp_table_t_ +{ + /** + * number of locks on the table + */ + u16 bdt_locks; + + /** + * Table ID (hash key) for this FIB. + */ + u32 bdt_table_id; + + /** + * The lookup DB based on sender BP. Value is the index of the + * BIER disp object. + */ + index_t bdt_db[BIER_BP_MAX]; +} bier_disp_table_t; + +/** + * @brief + * Format the description/name of the table + */ +extern u8* format_bier_disp_table(u8* s, va_list *ap); + +extern void bier_disp_table_entry_path_add(u32 table_id, + bier_bp_t src, + bier_hdr_proto_id_t payload_proto, + const fib_route_path_t *rpath); + +extern void bier_disp_table_entry_path_remove(u32 table_id, + bier_bp_t src, + bier_hdr_proto_id_t payload_proto, + const fib_route_path_t *paths); + +extern index_t bier_disp_table_find(u32 table_id); + + +extern index_t bier_disp_table_add_or_lock(u32 table_id); +extern void bier_disp_table_unlock_w_table_id(u32 table_id); + +extern void bier_disp_table_unlock(index_t bdti); +extern void bier_disp_table_lock(index_t bdti); +extern void bier_disp_table_contribute_forwarding(index_t bdti, + dpo_id_t *dpo); + +/** + * Types and functions to walk all the entries in one BIER Table + */ +typedef void (*bier_disp_table_walk_fn_t)(const bier_disp_table_t *bdt, + const bier_disp_entry_t *bde, + u16 bp, + void *ctx); +extern void bier_disp_table_walk(u32 table_id, + bier_disp_table_walk_fn_t fn, + void *ctx); + +/** + * @brief + * Get a pointer to a FIB table + */ +extern bier_disp_table_t *bier_disp_table_pool; + +static inline bier_disp_table_t * +bier_disp_table_get (index_t bdti) +{ + return (pool_elt_at_index(bier_disp_table_pool, bdti)); +} + +static inline index_t +bier_disp_table_lookup (index_t bdti, + bier_bp_t src) +{ + bier_disp_table_t *bdt; + + bdt = bier_disp_table_get(bdti); + + return (bdt->bdt_db[src]); +} + +#endif diff --git a/src/vnet/bier/bier_drop.c b/src/vnet/bier/bier_drop.c new file mode 100644 index 00000000000..eb873acf7ba --- /dev/null +++ b/src/vnet/bier/bier_drop.c @@ -0,0 +1,101 @@ +/* + * 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 <vnet/buffer.h> +#include <vlib/vlib.h> +#include <vnet/dpo/dpo.h> + +#include <vnet/bier/bier_hdr_inlines.h> + +typedef struct bier_drop_trace_t_ +{ + index_t dpi; +} bier_drop_trace_t; + +static void +bier_drop_trace (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 *from, n_left; + + n_left = frame->n_vectors; + from = vlib_frame_vector_args (frame); + while (n_left >= 1) + { + u32 bi0; + vlib_buffer_t *b0; + bier_drop_trace_t *t0; + + bi0 = from[0]; + + b0 = vlib_get_buffer (vm, bi0); + + if (b0->flags & VLIB_BUFFER_IS_TRACED) + { + t0 = vlib_add_trace (vm, node, b0, sizeof(*t0)); + + t0->dpi = vnet_buffer (b0)->ip.adj_index[VLIB_TX]; + } + from += 1; + n_left -= 1; + } +} + +static uword +bier_drop (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 *buffers = vlib_frame_vector_args (frame); + uword n_packets = frame->n_vectors; + + vlib_error_drop_buffers (vm, node, buffers, + /* stride */ 1, + n_packets, + /* next */ 0, + 0, // bier_input_node.index, + 0); + + if (node->flags & VLIB_NODE_FLAG_TRACE) + bier_drop_trace (vm, node, frame); + + return n_packets; +} + +static u8 * +format_bier_drop_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + bier_drop_trace_t *t = va_arg (*args, bier_drop_trace_t *); + + s = format (s, "dpo-idx %d", t->dpi); + + return s; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (bier_drop_node, static) = +{ + .function = bier_drop,. + name = "bier-drop", + .vector_size = sizeof (u32), + .format_trace = format_bier_drop_trace, + .n_next_nodes = 1, + .next_nodes = { + [0] = "error-drop", + }, +}; diff --git a/src/vnet/bier/bier_entry.c b/src/vnet/bier/bier_entry.c new file mode 100644 index 00000000000..88be8125f51 --- /dev/null +++ b/src/vnet/bier/bier_entry.c @@ -0,0 +1,388 @@ +/* + * 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 <vnet/bier/bier_entry.h> +#include <vnet/bier/bier_update.h> + +#include <vnet/fib/fib_path_list.h> + +#include <vnet/bier/bier_fmask_db.h> +#include <vnet/bier/bier_fmask.h> +#include <vnet/bier/bier_table.h> + +bier_entry_t *bier_entry_pool; + +static index_t +bier_entry_get_index (const bier_entry_t *be) +{ + return (be - bier_entry_pool); +} + +static fib_path_list_walk_rc_t +bier_entry_link_walk (fib_node_index_t pl_index, + fib_node_index_t path_index, + void *arg) +{ + bier_entry_t *be = arg; + index_t bfmi; + + bfmi = fib_path_get_resolving_index(path_index); + bier_fmask_link(bfmi, be->be_bp); + + return (FIB_PATH_LIST_WALK_CONTINUE); +} + +static fib_path_list_walk_rc_t +bier_entry_unlink_walk (fib_node_index_t pl_index, + fib_node_index_t path_index, + void *arg) +{ + bier_entry_t *be = arg; + index_t bfmi; + + bfmi = fib_path_get_resolving_index(path_index); + bier_fmask_unlink(bfmi, be->be_bp); + + return (FIB_PATH_LIST_WALK_CONTINUE); +} + +index_t +bier_entry_create (index_t bti, + bier_bp_t bp) +{ + bier_entry_t *be; + + pool_get(bier_entry_pool, be); + + be->be_bp = bp; + be->be_bti = bti; + be->be_path_list = FIB_NODE_INDEX_INVALID; + + return (bier_entry_get_index(be)); +} + +void +bier_entry_delete (index_t bei) +{ + bier_entry_t *be; + + be = bier_entry_get(bei); + + /* + * if we still ahve a path-list, unlink from it + */ + if (FIB_NODE_INDEX_INVALID != be->be_path_list) + { + fib_path_list_walk(be->be_path_list, + bier_entry_unlink_walk, + be); + fib_path_list_child_remove(be->be_path_list, + be->be_sibling_index); + } + + pool_put(bier_entry_pool, be); +} + +static void +bier_entry_table_ecmp_walk_add_fmask (index_t btei, + void *arg) +{ + bier_entry_t *be = arg;; + + /* + * choose a fmask from the entry's resolved set to add + * to ECMP table's lookup table + */ + if (FIB_NODE_INDEX_INVALID != be->be_path_list) + { + const bier_table_id_t *btid; + dpo_id_t dpo = DPO_INVALID; + const dpo_id_t *choice; + load_balance_t *lb; + + btid = bier_table_get_id(btei); + + fib_path_list_contribute_forwarding(be->be_path_list, + FIB_FORW_CHAIN_TYPE_BIER, + &dpo); + + /* + * select the appropriate bucket from the LB + */ + ASSERT(dpo.dpoi_type == DPO_LOAD_BALANCE); + + lb = load_balance_get(dpo.dpoi_index); + + choice = load_balance_get_bucket_i(lb, + btid->bti_ecmp & + (lb->lb_n_buckets_minus_1)); + + if (choice->dpoi_type == DPO_BIER_FMASK) + { + bier_table_ecmp_set_fmask(btei, be->be_bp, + choice->dpoi_index); + } + else + { + /* + * any other type results in a drop, which we represent + * with an empty bucket + */ + bier_table_ecmp_set_fmask(btei, be->be_bp, + INDEX_INVALID); + } + + dpo_reset(&dpo); + } + else + { + /* + * no fmasks left. insert a drop + */ + bier_table_ecmp_set_fmask(btei, be->be_bp, INDEX_INVALID); + } +} + +void +bier_entry_path_add (index_t bei, + const fib_route_path_t *rpaths) +{ + fib_node_index_t old_pl_index; + bier_entry_t *be; + + be = bier_entry_get(bei); + old_pl_index = be->be_path_list; + + /* + * lock the path-list so it does not go away before we unlink + * from its resolved fmasks + */ + fib_path_list_lock(old_pl_index); + + if (FIB_NODE_INDEX_INVALID == be->be_path_list) + { + old_pl_index = FIB_NODE_INDEX_INVALID; + be->be_path_list = fib_path_list_create((FIB_PATH_LIST_FLAG_SHARED | + FIB_PATH_LIST_FLAG_NO_URPF), + rpaths); + be->be_sibling_index = fib_path_list_child_add(be->be_path_list, + FIB_NODE_TYPE_BIER_ENTRY, + bier_entry_get_index(be)); + } + else + { + + old_pl_index = be->be_path_list; + + be->be_path_list = + fib_path_list_copy_and_path_add(old_pl_index, + (FIB_PATH_LIST_FLAG_SHARED | + FIB_PATH_LIST_FLAG_NO_URPF), + rpaths); + + fib_path_list_child_remove(old_pl_index, + be->be_sibling_index); + be->be_sibling_index = fib_path_list_child_add(be->be_path_list, + FIB_NODE_TYPE_BIER_ENTRY, + bier_entry_get_index(be)); + } + /* + * link the entry's bit-position to each fmask in the new path-list + * then unlink from the old. + */ + fib_path_list_walk(be->be_path_list, + bier_entry_link_walk, + be); + if (FIB_NODE_INDEX_INVALID != old_pl_index) + { + fib_path_list_walk(old_pl_index, + bier_entry_unlink_walk, + be); + } + + /* + * update the ECNP tables with the new choice + */ + bier_table_ecmp_walk(be->be_bti, + bier_entry_table_ecmp_walk_add_fmask, + be); + + /* + * symmetric unlock. The old path-list may not exist hereinafter + */ + fib_path_list_unlock(old_pl_index); +} + +int +bier_entry_path_remove (index_t bei, + const fib_route_path_t *rpaths) +{ + fib_node_index_t old_pl_index; + bier_entry_t *be; + + be = bier_entry_get(bei); + old_pl_index = be->be_path_list; + + fib_path_list_lock(old_pl_index); + + ASSERT (FIB_NODE_INDEX_INVALID != be->be_path_list); + + be->be_path_list = + fib_path_list_copy_and_path_remove(old_pl_index, + (FIB_PATH_LIST_FLAG_SHARED | + FIB_PATH_LIST_FLAG_NO_URPF), + rpaths); + + if (be->be_path_list != old_pl_index) + { + /* + * a path was removed + */ + fib_path_list_child_remove(old_pl_index, + be->be_sibling_index); + + if (FIB_NODE_INDEX_INVALID != be->be_path_list) + { + /* + * link the entry's bit-position to each fmask in the new path-list + * then unlink from the old. + */ + fib_path_list_walk(be->be_path_list, + bier_entry_link_walk, + be); + be->be_sibling_index = + fib_path_list_child_add(be->be_path_list, + FIB_NODE_TYPE_BIER_ENTRY, + bier_entry_get_index(be)); + } + + fib_path_list_walk(old_pl_index, + bier_entry_unlink_walk, + be); + } + fib_path_list_unlock(old_pl_index); + + + /* + * update the ECNP tables with the new choice + */ + bier_table_ecmp_walk(be->be_bti, + bier_entry_table_ecmp_walk_add_fmask, + be); + + return (fib_path_list_get_n_paths(be->be_path_list)); +} + +void +bier_entry_contribute_forwarding(index_t bei, + dpo_id_t *dpo) +{ + bier_entry_t *be = bier_entry_get(bei); + + fib_path_list_contribute_forwarding(be->be_path_list, + FIB_FORW_CHAIN_TYPE_BIER, + dpo); +} + +u8* +format_bier_entry (u8* s, va_list *ap) +{ + index_t bei = va_arg(*ap, index_t); + bier_show_flags_t flags = va_arg(*ap, bier_show_flags_t); + + bier_entry_t *be = bier_entry_get(bei); + + s = format(s, " bp:%d\n", be->be_bp); + s = fib_path_list_format(be->be_path_list, s); + + if (flags & BIER_SHOW_DETAIL) + { + dpo_id_t dpo = DPO_INVALID; + + bier_entry_contribute_forwarding(bei, &dpo); + + s = format(s, " forwarding:\n"); + s = format(s, " %U", + format_dpo_id, &dpo, 2); + s = format(s, "\n"); + } + + return (s); +} + +static fib_node_t * +bier_entry_get_node (fib_node_index_t index) +{ + bier_entry_t *be = bier_entry_get(index); + return (&(be->be_node)); +} + +static bier_entry_t* +bier_entry_get_from_node (fib_node_t *node) +{ + return ((bier_entry_t*)(((char*)node) - + STRUCT_OFFSET_OF(bier_entry_t, + be_node))); +} + +static void +bier_entry_last_lock_gone (fib_node_t *node) +{ + /* + * the lifetime of the entry is managed by the table. + */ + ASSERT(0); +} + +/* + * A back walk has reached this BIER entry + */ +static fib_node_back_walk_rc_t +bier_entry_back_walk_notify (fib_node_t *node, + fib_node_back_walk_ctx_t *ctx) +{ + /* + * re-populate the ECMP tables with new choices + */ + bier_entry_t *be = bier_entry_get_from_node(node); + + bier_table_ecmp_walk(be->be_bti, + bier_entry_table_ecmp_walk_add_fmask, + be); + + /* + * no need to propagate further up the graph. + */ + return (FIB_NODE_BACK_WALK_CONTINUE); +} + +/* + * The BIER fmask's graph node virtual function table + */ +static const fib_node_vft_t bier_entry_vft = { + .fnv_get = bier_entry_get_node, + .fnv_last_lock = bier_entry_last_lock_gone, + .fnv_back_walk = bier_entry_back_walk_notify, +}; + +clib_error_t * +bier_entry_module_init (vlib_main_t * vm) +{ + fib_node_register_type (FIB_NODE_TYPE_BIER_ENTRY, &bier_entry_vft); + + return (NULL); +} + +VLIB_INIT_FUNCTION (bier_entry_module_init); diff --git a/src/vnet/bier/bier_entry.h b/src/vnet/bier/bier_entry.h new file mode 100644 index 00000000000..e514c64bb24 --- /dev/null +++ b/src/vnet/bier/bier_entry.h @@ -0,0 +1,94 @@ +/* + * 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. + */ +/** + * bier_entry : The BIER entry + * + * The interface to the BIER entry is through a bier_entry_t* rather + * than an index. This is becuase the BIER table allocates the entries + * in a contiguous array once and only once when the table is created. + * this is done for forwarding performance. The entry is thus not subject + * to realloc, and does not need to be malloc'd when a route to that + * bit-position is first learned. + * + */ + +#ifndef __BIER_ENTRY_H__ +#define __BIER_ENTRY_H__ + +#include <vlib/vlib.h> +#include <vnet/fib/fib_node.h> +#include <vnet/bier/bier_types.h> + +/** + * Forward declarations + */ +struct bier_route_update_t_; +struct bier_fmask_db_t_; + +/** + * The BIER entry + * + * the BIER entry is the representation of a BIER forwarding egress router (BFER) + * (or the egress PE) that is assigned a bit position. + */ +typedef struct bier_entry_t_ { + /** + * linkage into the FIB graph + */ + fib_node_t be_node; + + /** + * The index of the BIER table in which this entry resides + */ + index_t be_bti; + + /** + * the bit position this entry represents. + * this is the key table insertion + */ + bier_bp_t be_bp; + + /** + * the FIB path-list this entry resolves through. + * the path-list is itself resoved on the entry's fmasks + */ + fib_node_index_t be_path_list; + /** + * sibling index on the path list + */ + fib_node_index_t be_sibling_index; +} bier_entry_t; + +extern index_t bier_entry_create(index_t bti, + bier_bp_t bp); +extern void bier_entry_delete(index_t bei); + +extern void bier_entry_path_add(index_t bei, + const fib_route_path_t *brp); + +extern int bier_entry_path_remove(index_t bei, + const fib_route_path_t *brp); + +extern u8* format_bier_entry(u8* s, va_list *ap); + +extern void bier_entry_contribute_forwarding(index_t bei, + dpo_id_t *dpo); + +extern bier_entry_t *bier_entry_pool; +always_inline bier_entry_t* bier_entry_get(index_t bei) +{ + return (&bier_entry_pool[bei]); +} +#endif diff --git a/src/vnet/bier/bier_fmask.c b/src/vnet/bier/bier_fmask.c new file mode 100644 index 00000000000..e30425cda4e --- /dev/null +++ b/src/vnet/bier/bier_fmask.c @@ -0,0 +1,527 @@ +/* + * 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 <vnet/fib/fib_entry.h> +#include <vnet/fib/fib_table.h> +#include <vnet/fib/fib_walk.h> + +#include <vnet/bier/bier_table.h> +#include <vnet/bier/bier_fmask.h> +#include <vnet/bier/bier_bit_string.h> +#include <vnet/bier/bier_disp_table.h> + +#include <vnet/mpls/mpls.h> +#include <vnet/dpo/drop_dpo.h> +#include <vnet/dpo/load_balance.h> + +/* + * attributes names for formatting + */ +static const char *const bier_fmask_attr_names[] = BIER_FMASK_ATTR_NAMES; + +/* + * pool of BIER fmask objects + */ +bier_fmask_t *bier_fmask_pool; + +static inline index_t +bier_fmask_get_index (const bier_fmask_t *bfm) +{ + return (bfm - bier_fmask_pool); +} + +static void +bier_fmask_bits_init (bier_fmask_bits_t *bits, + bier_hdr_len_id_t hlid) +{ + bits->bfmb_refs = clib_mem_alloc(sizeof(bits->bfmb_refs[0]) * + bier_hdr_len_id_to_num_bits(hlid)); + memset(bits->bfmb_refs, + 0, + (sizeof(bits->bfmb_refs[0]) * + bier_hdr_len_id_to_num_bits(hlid))); + + bits->bfmb_input_reset_string.bbs_len = + bier_hdr_len_id_to_num_buckets(hlid); + + /* + * The buckets are accessed in the switch path + */ + bits->bfmb_input_reset_string.bbs_buckets = + clib_mem_alloc_aligned( + sizeof(bits->bfmb_input_reset_string.bbs_buckets[0]) * + bier_hdr_len_id_to_num_buckets(hlid), + CLIB_CACHE_LINE_BYTES); + memset(bits->bfmb_input_reset_string.bbs_buckets, + 0, + sizeof(bits->bfmb_input_reset_string.bbs_buckets[0]) * + bier_hdr_len_id_to_num_buckets(hlid)); +} + +static void +bier_fmask_stack (bier_fmask_t *bfm) +{ + dpo_id_t via_dpo = DPO_INVALID; + + if (bfm->bfm_flags & BIER_FMASK_FLAG_DISP) + { + bier_disp_table_contribute_forwarding(bfm->bfm_disp, + &via_dpo); + } + else + { + fib_entry_contribute_forwarding(bfm->bfm_fei, + FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS, + &via_dpo); + } + + /* + * If the via fib entry provides no forwarding (i.e. a drop) + * then niether does this fmask. That way children consider this fmask + * unresolved and other ECMP options are used instead. + */ + if (dpo_is_drop(&via_dpo) || + load_balance_is_drop(&via_dpo)) + { + bfm->bfm_flags &= ~BIER_FMASK_FLAG_FORWARDING; + } + else + { + bfm->bfm_flags |= BIER_FMASK_FLAG_FORWARDING; + } + + dpo_stack(DPO_BIER_FMASK, + DPO_PROTO_BIER, + &bfm->bfm_dpo, + &via_dpo); + dpo_reset(&via_dpo); +} + +void +bier_fmask_contribute_forwarding (index_t bfmi, + dpo_id_t *dpo) +{ + bier_fmask_t *bfm; + + bfm = bier_fmask_get(bfmi); + + if (bfm->bfm_flags & BIER_FMASK_FLAG_FORWARDING) + { + dpo_set(dpo, + DPO_BIER_FMASK, + DPO_PROTO_BIER, + bfmi); + } + else + { + dpo_copy(dpo, drop_dpo_get(DPO_PROTO_BIER)); + } +} + +static void +bier_fmask_resolve (bier_fmask_t *bfm) +{ + if (bfm->bfm_flags & BIER_FMASK_FLAG_DISP) + { + bier_disp_table_lock(bfm->bfm_disp); + } + else + { + /* + * source a recursive route through which we resolve. + */ + fib_prefix_t pfx = { + .fp_addr = bfm->bfm_id.bfmi_nh, + .fp_proto = (ip46_address_is_ip4(&(bfm->bfm_id.bfmi_nh)) ? + FIB_PROTOCOL_IP4 : + FIB_PROTOCOL_IP6), + .fp_len = (ip46_address_is_ip4(&(bfm->bfm_id.bfmi_nh)) ? 32 : 128), + }; + + bfm->bfm_fei = fib_table_entry_special_add(0, // default table + &pfx, + FIB_SOURCE_RR, + FIB_ENTRY_FLAG_NONE); + + bfm->bfm_sibling = fib_entry_child_add(bfm->bfm_fei, + FIB_NODE_TYPE_BIER_FMASK, + bier_fmask_get_index(bfm)); + } + + bier_fmask_stack(bfm); +} + +static void +bier_fmask_unresolve (bier_fmask_t *bfm) +{ + if (bfm->bfm_flags & BIER_FMASK_FLAG_DISP) + { + bier_disp_table_unlock(bfm->bfm_disp); + } + else + { + /* + * un-source the recursive route through which we resolve. + */ + fib_prefix_t pfx = { + .fp_addr = bfm->bfm_id.bfmi_nh, + .fp_proto = (ip46_address_is_ip4(&(bfm->bfm_id.bfmi_nh)) ? + FIB_PROTOCOL_IP4 : + FIB_PROTOCOL_IP6), + .fp_len = (ip46_address_is_ip4(&(bfm->bfm_id.bfmi_nh)) ? 32 : 128), + }; + + fib_entry_child_remove(bfm->bfm_fei, bfm->bfm_sibling); + fib_table_entry_special_remove(0, &pfx, FIB_SOURCE_RR); + } + dpo_reset(&bfm->bfm_dpo); +} + +u32 +bier_fmask_child_add (fib_node_index_t bfmi, + fib_node_type_t child_type, + fib_node_index_t child_index) +{ + return (fib_node_child_add(FIB_NODE_TYPE_BIER_FMASK, + bfmi, + child_type, + child_index)); +}; + +void +bier_fmask_child_remove (fib_node_index_t bfmi, + u32 sibling_index) +{ + fib_node_child_remove(FIB_NODE_TYPE_BIER_FMASK, + bfmi, + sibling_index); +} + +static void +bier_fmask_init (bier_fmask_t *bfm, + const bier_fmask_id_t *fmid, + index_t bti, + const fib_route_path_t *rpath) +{ + const bier_table_id_t *btid; + mpls_label_t olabel; + + bfm->bfm_id = *fmid; + bfm->bfm_fib_index = bti; + dpo_reset(&bfm->bfm_dpo); + + if (ip46_address_is_zero(&(bfm->bfm_id.bfmi_nh))) + { + bfm->bfm_flags |= BIER_FMASK_FLAG_DISP; + } + + if (!(bfm->bfm_flags & BIER_FMASK_FLAG_DISP)) + { + olabel = rpath->frp_label_stack[0]; + vnet_mpls_uc_set_label(&bfm->bfm_label, olabel); + vnet_mpls_uc_set_exp(&bfm->bfm_label, 0); + vnet_mpls_uc_set_s(&bfm->bfm_label, 1); + vnet_mpls_uc_set_ttl(&bfm->bfm_label, 0xff); + bfm->bfm_label = clib_host_to_net_u32(bfm->bfm_label); + } + else + { + bfm->bfm_disp = rpath->frp_bier_fib_index; + } + + btid = bier_table_get_id(bfm->bfm_fib_index); + bier_fmask_bits_init(&bfm->bfm_bits, btid->bti_hdr_len); + bier_fmask_resolve(bfm); +} + +static void +bier_fmask_destroy (bier_fmask_t *bfm) +{ + clib_mem_free(bfm->bfm_bits.bfmb_refs); + clib_mem_free(bfm->bfm_bits.bfmb_input_reset_string.bbs_buckets); + + bier_fmask_db_remove(bfm->bfm_fib_index, &(bfm->bfm_id)); + bier_fmask_unresolve(bfm); + pool_put(bier_fmask_pool, bfm); +} + +void +bier_fmask_unlock (index_t bfmi) +{ + bier_fmask_t *bfm; + + if (INDEX_INVALID == bfmi) + { + return; + } + + bfm = bier_fmask_get(bfmi); + + fib_node_unlock(&bfm->bfm_node); +} + +void +bier_fmask_lock (index_t bfmi) +{ + bier_fmask_t *bfm; + + if (INDEX_INVALID == bfmi) + { + return; + } + + bfm = bier_fmask_get(bfmi); + + fib_node_lock(&bfm->bfm_node); +} + +index_t +bier_fmask_create_and_lock (const bier_fmask_id_t *fmid, + index_t bti, + const fib_route_path_t *rpath) +{ + bier_fmask_t *bfm; + + pool_get_aligned(bier_fmask_pool, bfm, CLIB_CACHE_LINE_BYTES); + + memset(bfm, 0, sizeof(*bfm)); + + fib_node_init(&bfm->bfm_node, FIB_NODE_TYPE_BIER_FMASK); + bier_fmask_init(bfm, fmid, bti, rpath); + + bier_fmask_lock(bier_fmask_get_index(bfm)); + + return (bier_fmask_get_index(bfm)); +} + +void +bier_fmask_link (index_t bfmi, + bier_bp_t bp) +{ + bier_fmask_t *bfm; + + bfm = bier_fmask_get(bfmi); + + if (0 == bfm->bfm_bits.bfmb_refs[BIER_BP_TO_INDEX(bp)]) + { + /* + * 0 -> 1 transistion - set the bit in the string + */ + bier_bit_string_set_bit(&bfm->bfm_bits.bfmb_input_reset_string, bp); + } + + ++bfm->bfm_bits.bfmb_refs[BIER_BP_TO_INDEX(bp)]; + ++bfm->bfm_bits.bfmb_count; +} + +void +bier_fmask_unlink (index_t bfmi, + bier_bp_t bp) +{ + bier_fmask_t *bfm; + + bfm = bier_fmask_get(bfmi); + + --bfm->bfm_bits.bfmb_refs[BIER_BP_TO_INDEX(bp)]; + --bfm->bfm_bits.bfmb_count; + + if (0 == bfm->bfm_bits.bfmb_refs[BIER_BP_TO_INDEX(bp)]) + { + /* + * 1 -> 0 transistion - clear the bit in the string + */ + bier_bit_string_clear_bit(&bfm->bfm_bits.bfmb_input_reset_string, bp); + } +} + +u8* +format_bier_fmask (u8 *s, va_list *ap) +{ + index_t bfmi = va_arg(*ap, index_t); + u32 indent = va_arg(*ap, u32); + bier_fmask_attributes_t attr; + bier_fmask_t *bfm; + + if (pool_is_free_index(bier_fmask_pool, bfmi)) + { + return (format(s, "No BIER f-mask %d", bfmi)); + } + + bfm = bier_fmask_get(bfmi); + + s = format(s, "fmask: nh:%U bs:%U locks:%d ", + format_ip46_address, &bfm->bfm_id.bfmi_nh, IP46_TYPE_ANY, + format_bier_bit_string, &bfm->bfm_bits.bfmb_input_reset_string, + bfm->bfm_node.fn_locks); + s = format(s, "flags:"); + FOR_EACH_BIER_FMASK_ATTR(attr) { + if ((1<<attr) & bfm->bfm_flags) { + s = format (s, "%s,", bier_fmask_attr_names[attr]); + } + } + s = format(s, "\n%U%U", + format_white_space, indent, + format_dpo_id, &bfm->bfm_dpo, indent+2); + + return (s); +} + + +static fib_node_t * +bier_fmask_get_node (fib_node_index_t index) +{ + bier_fmask_t *bfm = bier_fmask_get(index); + return (&(bfm->bfm_node)); +} + +static bier_fmask_t* +bier_fmask_get_from_node (fib_node_t *node) +{ + return ((bier_fmask_t*)(((char*)node) - + STRUCT_OFFSET_OF(bier_fmask_t, + bfm_node))); +} + +/* + * bier_fmask_last_lock_gone + */ +static void +bier_fmask_last_lock_gone (fib_node_t *node) +{ + bier_fmask_destroy(bier_fmask_get_from_node(node)); +} + +/* + * bier_fmask_back_walk_notify + * + * A back walk has reached this BIER fmask + */ +static fib_node_back_walk_rc_t +bier_fmask_back_walk_notify (fib_node_t *node, + fib_node_back_walk_ctx_t *ctx) +{ + /* + * re-stack the fmask on the n-eos of the via + */ + bier_fmask_t *bfm = bier_fmask_get_from_node(node); + + bier_fmask_stack(bfm); + + /* + * propagate further up the graph. + * we can do this synchronously since the fan out is small. + */ + fib_walk_sync(FIB_NODE_TYPE_BIER_FMASK, bier_fmask_get_index(bfm), ctx); + + return (FIB_NODE_BACK_WALK_CONTINUE); +} + +/* + * The BIER fmask's graph node virtual function table + */ +static const fib_node_vft_t bier_fmask_vft = { + .fnv_get = bier_fmask_get_node, + .fnv_last_lock = bier_fmask_last_lock_gone, + .fnv_back_walk = bier_fmask_back_walk_notify, +}; + +static void +bier_fmask_dpo_lock (dpo_id_t *dpo) +{ +} + +static void +bier_fmask_dpo_unlock (dpo_id_t *dpo) +{ +} + +static void +bier_fmask_dpo_mem_show (void) +{ + fib_show_memory_usage("BIER-fmask", + pool_elts(bier_fmask_pool), + pool_len(bier_fmask_pool), + sizeof(bier_fmask_t)); +} + +const static dpo_vft_t bier_fmask_dpo_vft = { + .dv_lock = bier_fmask_dpo_lock, + .dv_unlock = bier_fmask_dpo_unlock, + .dv_mem_show = bier_fmask_dpo_mem_show, + .dv_format = format_bier_fmask, +}; + +const static char *const bier_fmask_mpls_nodes[] = +{ + "bier-output" +}; +const static char * const * const bier_fmask_nodes[DPO_PROTO_NUM] = +{ + [DPO_PROTO_BIER] = bier_fmask_mpls_nodes, + [DPO_PROTO_MPLS] = bier_fmask_mpls_nodes, +}; + +clib_error_t * +bier_fmask_module_init (vlib_main_t * vm) +{ + fib_node_register_type (FIB_NODE_TYPE_BIER_FMASK, &bier_fmask_vft); + dpo_register(DPO_BIER_FMASK, &bier_fmask_dpo_vft, bier_fmask_nodes); + + return (NULL); +} + +VLIB_INIT_FUNCTION (bier_fmask_module_init); + +static clib_error_t * +bier_fmask_show (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + bier_fmask_t *bfm; + index_t bfmi; + + bfmi = INDEX_INVALID; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { + if (unformat (input, "%d", &bfmi)) + { + ; + } else + { + break; + } + } + + if (INDEX_INVALID == bfmi) + { + pool_foreach(bfm, bier_fmask_pool, + ({ + vlib_cli_output (vm, "%U", + format_bier_fmask, bier_fmask_get_index(bfm), 0); + })); + } + else + { + vlib_cli_output (vm, "%U", format_bier_fmask, bfmi, 0); + } + + return (NULL); +} + +VLIB_CLI_COMMAND (show_bier_fmask, static) = { + .path = "show bier fmask", + .short_help = "show bier fmask", + .function = bier_fmask_show, +}; diff --git a/src/vnet/bier/bier_fmask.h b/src/vnet/bier/bier_fmask.h new file mode 100644 index 00000000000..d5ff4590ffe --- /dev/null +++ b/src/vnet/bier/bier_fmask.h @@ -0,0 +1,198 @@ +/* + * 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. + */ +/** + * @brief bier_fmask : The BIER fmask + * + * The BIER fmask contains the bitString that is applied to packets that + * egress towards the next-hop. As such the fmask is part of the rewrite + * (adj) for that next-hop. It it thus an extension of the next-hop and in + * no way associated with the bit-position(s) that are reachable through it. + * Fmasks are thus shared by bit-positions that egress throught the same + * nh (BFR-NBR). + * Deag fmasks are also shread in the event that a router has local + * bit-positions. This is necessary to prevent the router recieving two copies + * of each packet. Consequently it also means that they share the same + * disposition data for the global data. + */ + +#ifndef __BIER_FMASK_H__ +#define __BIER_FMASK_H__ + +#include <vlib/vlib.h> + +#include <vnet/fib/fib_node.h> +#include <vnet/mpls/packet.h> +#include <vnet/dpo/dpo.h> + +#include <vnet/bier/bier_types.h> +#include <vnet/bier/bier_fmask_db.h> + +/** + * A struct that represents the reference counting of the bits + */ +typedef struct bier_fmask_bits_t_ { + /** + * each bit in the mask needs to be reference counted + * and set/cleared on the 0->1 and 1->0 transitions. + */ + bier_bit_string_t bfmb_input_reset_string; + u32 *bfmb_refs; + + /** + * The total number of references to bits set on this mask + * in effect a count of the number of children. + */ + uint32_t bfmb_count; +} bier_fmask_bits_t; + +/** + * Flags on fmask + */ +typedef enum bier_fmask_attributes_t_ +{ + BIER_FMASK_ATTR_FIRST, + BIER_FMASK_ATTR_FORWARDING = BIER_FMASK_ATTR_FIRST, + BIER_FMASK_ATTR_DISP, + BIER_FMASK_ATTR_LAST = BIER_FMASK_ATTR_DISP, +} bier_fmask_attributes_t; + +#define BIER_FMASK_ATTR_NAMES { \ + [BIER_FMASK_ATTR_FORWARDING] = "forwarding", \ + [BIER_FMASK_ATTR_DISP] = "disposition", \ +} + +#define FOR_EACH_BIER_FMASK_ATTR(_item) \ + for (_item = BIER_FMASK_ATTR_FIRST; \ + _item <= BIER_FMASK_ATTR_LAST; \ + _item++) + +typedef enum bier_fmask_flags_t_ +{ + BIER_FMASK_FLAG_FORWARDING = (1 << BIER_FMASK_ATTR_FORWARDING), + BIER_FMASK_FLAG_DISP = (1 << BIER_FMASK_ATTR_DISP), +} bier_fmask_flags_t; + +/** + * An outgoing BIER mask. aka forwarding bit mask (in the RFCs) + * + * This mask's function is two-fold + * 1 - it is logical-AND with the input packet header to produce the + * output packet header + * 2 - it is logical NAND with the input packet header to modify the bit-mask + * for the next lookup + */ +typedef struct bier_fmask_t_ { + /** + * The BIER fmask is a child of a FIB entry in the FIB graph. + */ + fib_node_t bfm_node; + + /** + * operational/state flags on the fmask + */ + bier_fmask_flags_t bfm_flags; + + /** + * The bits, and their ref counts, that are set on this mask + * This mask changes as BIER entries link to and from this fmask + */ + bier_fmask_bits_t bfm_bits; + + struct + { + /** + * The key to this fmask - used for store/lookup in the DB + */ + bier_fmask_id_t bfm_id; + + /** + * The BIER Table this Fmask is used in + */ + index_t bfm_fib_index; + }; + + union + { + /** + * For forwarding via a next-hop + */ + struct + { + /** + * The parent fib entry + */ + fib_node_index_t bfm_fei; + /** + * The MPLS label to paint on the header during forwarding + */ + mpls_label_t bfm_label; + }; + + /** + * For disposition + */ + struct + { + /** + * The parent disposition table object + */ + index_t bfm_disp; + }; + }; + + /** + * the index of this fmask in the parent's child list. + */ + u32 bfm_sibling; + + /** + * The index into the adj table for the adj that + * this fmask resolves via + */ + dpo_id_t bfm_dpo; +} bier_fmask_t; + +extern void bier_fmask_link(index_t bfmi, bier_bp_t bp); +extern void bier_fmask_unlink(index_t bfmi, bier_bp_t bp); +extern void bier_fmask_unlock(index_t bfmi); +extern void bier_fmask_lock(index_t bfmi); + +extern index_t bier_fmask_create_and_lock(const bier_fmask_id_t *fmid, + index_t bti, + const fib_route_path_t *rpath); + +extern u8* format_bier_fmask(u8 *s, va_list *ap); + +extern void bier_fmask_contribute_forwarding(index_t bfmi, + dpo_id_t *dpo); + +extern u32 bier_fmask_child_add (fib_node_index_t fib_entry_index, + fib_node_type_t child_type, + fib_node_index_t child_index); +extern void bier_fmask_child_remove (fib_node_index_t fib_entry_index, + u32 sibling_index); + +/* + * provided for fast data-path access + */ +bier_fmask_t *bier_fmask_pool; + +static inline bier_fmask_t * +bier_fmask_get (u32 index) +{ + return (pool_elt_at_index(bier_fmask_pool, index)); +} + +#endif diff --git a/src/vnet/bier/bier_fmask_db.c b/src/vnet/bier/bier_fmask_db.c new file mode 100644 index 00000000000..37cbb365897 --- /dev/null +++ b/src/vnet/bier/bier_fmask_db.c @@ -0,0 +1,161 @@ +/* + * 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 <vnet/bier/bier_fmask_db.h> +#include <vnet/bier/bier_fmask.h> + +/** + * Global Table of fmask objects + * The key into this table includes the table's key and the fmask's key, + * so there could be a DB per-table. But it is more efficient + * at forwarding time to extract the fmask from a single global table + * which is hot in dcache. + * + * The table's key is part of this DB key, since the fmasks therein build up + * their forwarding mask based on the routes that resolve through + * it, so cross polination would be bad. + */ +typedef struct bier_fmask_db_t_ { + /** + * hash table for underlying storage + */ + mhash_t bfdb_hash; + + /** + * Pool for memory + */ + struct bier_fmask_t_ *bfdb_pool; +} bier_fmask_db_t; + +/** + * The key used in the fmask DB to compare fmask objects. + * There is one global DB, so we need to use the table's ID and the fmasks ID + */ +typedef struct bier_fmask_db_key_t_ { + bier_fmask_id_t bfmdbk_fm_id; + index_t bfmdbk_tbl_id; +} bier_fmask_db_key_t; +// TODO packed? + +/** + * Single fmask DB + */ +static bier_fmask_db_t bier_fmask_db; + + +u32 +bier_fmask_get_index (const bier_fmask_t *bfm) +{ + return (bfm - bier_fmask_db.bfdb_pool); +} + +u32 +bier_fmask_db_find_or_create_and_lock (index_t bti, + const bier_fmask_id_t *fmid, + const fib_route_path_t *rpath) +{ + bier_fmask_db_key_t key; + u32 index; + uword *p; + + /* + * there be padding in that thar key, and it's + * used as a memcmp in the mhash. + */ + memset(&key, 0, sizeof(key)); + key.bfmdbk_tbl_id = bti; + key.bfmdbk_fm_id = *fmid; + + index = INDEX_INVALID; + p = mhash_get (&bier_fmask_db.bfdb_hash, &key); + + if (NULL == p) + { + /* + * adding a new fmask object + */ + index = bier_fmask_create_and_lock(fmid, bti, rpath); + + mhash_set (&bier_fmask_db.bfdb_hash, &key, index, 0 /*old_value*/); + } + else + { + index = p[0]; + bier_fmask_lock(index); + } + + return (index); +} + +u32 +bier_fmask_db_find (index_t bti, + const bier_fmask_id_t *fmid) +{ + bier_fmask_db_key_t key; + u32 index; + uword *p; + + /* + * there be padding in that thar key, and it's + * used as a memcmp in the mhash. + */ + memset(&key, 0, sizeof(key)); + key.bfmdbk_tbl_id = bti; + key.bfmdbk_fm_id = *fmid; + + index = INDEX_INVALID; + p = mhash_get(&bier_fmask_db.bfdb_hash, &key); + + if (NULL != p) + { + index = p[0]; + } + + return (index); +} + +void +bier_fmask_db_remove (index_t bti, + const bier_fmask_id_t *fmid) +{ + bier_fmask_db_key_t key = { + .bfmdbk_tbl_id = bti, + .bfmdbk_fm_id = *fmid, + }; + uword *p; + + p = mhash_get (&bier_fmask_db.bfdb_hash, &key); + + if (NULL == p) { + /* + * remove a non-exitant entry - oops + */ + ASSERT (!"remove non-existant fmask"); + } else { + mhash_unset (&(bier_fmask_db.bfdb_hash), &key, 0); + } +} + +clib_error_t * +bier_fmask_db_module_init (vlib_main_t *vm) +{ + mhash_init (&bier_fmask_db.bfdb_hash, + sizeof(uword), + sizeof(bier_fmask_db_key_t)); + + return (NULL); +} + +VLIB_INIT_FUNCTION (bier_fmask_db_module_init); diff --git a/src/vnet/bier/bier_fmask_db.h b/src/vnet/bier/bier_fmask_db.h new file mode 100644 index 00000000000..6ba40f3a839 --- /dev/null +++ b/src/vnet/bier/bier_fmask_db.h @@ -0,0 +1,61 @@ +/* + * 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. + */ +/** + * @brief bier_fmask_db : The BIER fmask Database + */ + +#ifndef __BIER_FMASK_DB_H__ +#define __BIER_FMASK_DB_H__ + +#include <vnet/ip/ip.h> + +#include <vnet/bier/bier_types.h> + +/** + * Foward declarations + */ +struct bier_fmask_t_; + +typedef enum bier_hdr_type_t_ { + BIER_HDR_IN_IP6, + BIER_HDR_O_MPLS, +} bier_hdr_type_t; + +typedef struct bier_fmask_id_t_ { + /** + * Type of BIER header this fmask supports + */ + bier_hdr_type_t bfmi_hdr_type; + + /** + * next-hop of the peer + */ + ip46_address_t bfmi_nh; +} bier_fmask_id_t; + +extern u32 +bier_fmask_db_find_or_create_and_lock(index_t bti, + const bier_fmask_id_t *fmid, + const fib_route_path_t *rpath); + +extern u32 +bier_fmask_db_find(index_t bti, + const bier_fmask_id_t *fmid); + +extern void +bier_fmask_db_remove(index_t bti, + const bier_fmask_id_t *fmid); + +#endif diff --git a/src/vnet/bier/bier_hdr_inlines.h b/src/vnet/bier/bier_hdr_inlines.h new file mode 100644 index 00000000000..4437dc82e24 --- /dev/null +++ b/src/vnet/bier/bier_hdr_inlines.h @@ -0,0 +1,217 @@ +/* + * 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. + */ +/** + * @brief The BIER inline functions acting on the bier header + */ + +#ifndef __BIER_HDR_INLINES_H__ +#define __BIER_HDR_INLINES_H__ + +#include <vppinfra/byte_order.h> +#include <vppinfra/string.h> + +#include <vnet/bier/bier_types.h> +#include <vnet/bier/bier_bit_string.h> +#include <vnet/ip/ip6_packet.h> + +/** + * Mask and shift values for the fields incorporated + * into the header's first word + */ +#define BIER_HDR_1ST_NIBBLE_MASK 0xf0000000 +#define BIER_HDR_VERSION_FIELD_MASK 0x0f000000 +#define BIER_HDR_LEN_FIELD_MASK 0x00f00000 +#define BIER_HDR_ENTROPY_FIELD_MASK 0x000fffff + +#define BIER_HDR_1ST_NIBBLE_SHIFT 28 +#define BIER_HDR_VERSION_FIELD_SHIFT 24 +#define BIER_HDR_LEN_FIELD_SHIFT 20 +#define BIER_HDR_ENTROPY_FIELD_SHIFT 0 + +#define BIER_HDR_1ST_NIBBLE_VALUE 0x5 + +/** + * Mask and shift values for fields in the headers trainling word + */ +#define BIER_HDR_PROTO_FIELD_MASK 0x003f +#define BIER_HDR_OAM_FIELD_MASK 0xc000 +#define BIER_HDR_DSCP_FIELD_MASK 0x0fc0 +#define BIER_HDR_DSCP_FIELD_SHIFT 6 +#define BIER_HDR_PROTO_FIELD_SHIFT 0 +#define BIER_HDR_OAM_FIELD_SHIFT 14 + +static inline bier_hdr_version_t +bier_hdr_get_version (const bier_hdr_t *bier_hdr) +{ + return ((bier_hdr->bh_first_word & + BIER_HDR_VERSION_FIELD_MASK) >> + BIER_HDR_VERSION_FIELD_SHIFT); +} + +static inline bier_hdr_len_id_t +bier_hdr_get_len_id (const bier_hdr_t *bier_hdr) +{ + return ((bier_hdr->bh_first_word & + BIER_HDR_LEN_FIELD_MASK) >> + BIER_HDR_LEN_FIELD_SHIFT); +} + +static inline bier_hdr_entropy_t +bier_hdr_get_entropy (const bier_hdr_t *bier_hdr) +{ + return ((bier_hdr->bh_first_word & + BIER_HDR_ENTROPY_FIELD_MASK) >> + BIER_HDR_ENTROPY_FIELD_SHIFT); +} + +static inline void +bier_hdr_1st_nibble (bier_hdr_t *hdr) +{ + hdr->bh_first_word &= ~(BIER_HDR_1ST_NIBBLE_MASK); + hdr->bh_first_word |= (BIER_HDR_1ST_NIBBLE_VALUE << + BIER_HDR_1ST_NIBBLE_SHIFT); +} + +static inline u8 +bier_hdr_get_1st_nibble (bier_hdr_t *hdr) +{ + return ((hdr->bh_first_word & BIER_HDR_1ST_NIBBLE_MASK) >> + BIER_HDR_1ST_NIBBLE_SHIFT); +} + +static inline void +bier_hdr_set_version (bier_hdr_t *hdr, + bier_hdr_version_t version) +{ + hdr->bh_first_word &= ~(BIER_HDR_VERSION_FIELD_MASK); + hdr->bh_first_word |= (version << BIER_HDR_VERSION_FIELD_SHIFT); +} + +static inline void +bier_hdr_set_len_id (bier_hdr_t *hdr, + bier_hdr_len_id_t len) +{ + hdr->bh_first_word &= ~(BIER_HDR_LEN_FIELD_MASK); + hdr->bh_first_word |= (len << BIER_HDR_LEN_FIELD_SHIFT); +} + +static inline void +bier_hdr_set_entropy (bier_hdr_t *hdr, + bier_hdr_entropy_t entropy) +{ + entropy = entropy & BIER_HDR_ENTROPY_FIELD_MASK; + hdr->bh_first_word &= ~(BIER_HDR_ENTROPY_FIELD_MASK); + hdr->bh_first_word |= (entropy << BIER_HDR_ENTROPY_FIELD_SHIFT); +} + +#define BIER_HDR_FIRST_WORD(version, len, entropy) \ + ((BIER_HDR_1ST_NIBBLE_VALUE << \ + BIER_HDR_1ST_NIBBLE_SHIFT) | \ + (version << BIER_HDR_VERSION_FIELD_SHIFT) | \ + (len << BIER_HDR_LEN_FIELD_SHIFT) | \ + ((entropy & BIER_HDR_ENTROPY_FIELD_MASK) \ + << BIER_HDR_ENTROPY_FIELD_SHIFT)) + +static inline void +bier_hdr_ntoh (bier_hdr_t *bier_hdr) +{ + bier_hdr->bh_first_word = clib_net_to_host_u32(bier_hdr->bh_first_word); + bier_hdr->bh_oam_dscp_proto = clib_net_to_host_u16(bier_hdr->bh_oam_dscp_proto); + bier_hdr->bh_bfr_id = clib_net_to_host_u16(bier_hdr->bh_bfr_id); +} + +static inline void +bier_hdr_hton (bier_hdr_t *bier_hdr) +{ + bier_hdr->bh_first_word = clib_host_to_net_u32(bier_hdr->bh_first_word); + bier_hdr->bh_oam_dscp_proto = clib_host_to_net_u16(bier_hdr->bh_oam_dscp_proto); + bier_hdr->bh_bfr_id = clib_host_to_net_u16(bier_hdr->bh_bfr_id); +} + +static inline bier_hdr_src_id_t +bier_hdr_get_src_id (const bier_hdr_t *bier_hdr) +{ + return (bier_hdr->bh_bfr_id); +} + +static inline void +bier_hdr_set_src_id (bier_hdr_t *bier_hdr, + bier_hdr_src_id_t src_id) +{ + bier_hdr->bh_bfr_id = src_id; +} +static inline void +bier_hdr_set_proto_id (bier_hdr_t *bier_hdr, + bier_hdr_proto_id_t proto) +{ + bier_hdr->bh_oam_dscp_proto &= ~(BIER_HDR_PROTO_FIELD_MASK); + bier_hdr->bh_oam_dscp_proto |= (proto << BIER_HDR_PROTO_FIELD_SHIFT); +} + +static inline bier_hdr_proto_id_t +bier_hdr_get_proto_id (const bier_hdr_t *bier_hdr) +{ + return ((bier_hdr->bh_oam_dscp_proto & BIER_HDR_PROTO_FIELD_MASK) >> + BIER_HDR_PROTO_FIELD_SHIFT); +} + +static inline void +bier_hdr_clear (bier_hdr_t *bier_hdr) +{ + memset(&bier_hdr->bh_bit_string, 0, + bier_hdr_len_id_to_num_buckets( + bier_hdr_get_len_id(bier_hdr))); +} + +static inline void +bier_hdr_init (bier_hdr_t *bier_hdr, + bier_hdr_version_t version, + bier_hdr_proto_id_t proto, + bier_hdr_len_id_t len, + bier_hdr_entropy_t entropy, + bier_bp_t src) +{ + bier_hdr_1st_nibble(bier_hdr); + bier_hdr_set_version(bier_hdr, version); + bier_hdr_set_len_id(bier_hdr, len); + bier_hdr_set_entropy(bier_hdr, entropy); + bier_hdr_set_proto_id(bier_hdr, proto); + bier_hdr_set_src_id(bier_hdr, src); + bier_hdr_clear(bier_hdr); +} + +static inline size_t +bier_hdr_str_num_bytes (const bier_hdr_t *bier_hdr) +{ + return (bier_hdr_len_id_to_num_bytes( + bier_hdr_get_len_id(bier_hdr))); +} + +static inline size_t +bier_hdr_num_bytes (const bier_hdr_t *bier_hdr) +{ + return (sizeof(bier_hdr_t) + + bier_hdr_str_num_bytes(bier_hdr)); +} + +static inline void +bier_bit_string_init_from_hdr (bier_hdr_t *bier_hdr, + bier_bit_string_t *bit_string) +{ + bit_string->bbs_len = bier_hdr_str_num_bytes(bier_hdr); + bit_string->bbs_buckets = bier_hdr->bh_bit_string; +} + +#endif diff --git a/src/vnet/bier/bier_imp.c b/src/vnet/bier/bier_imp.c new file mode 100644 index 00000000000..c51dede9c0a --- /dev/null +++ b/src/vnet/bier/bier_imp.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2017 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. + */ +/** + * bier_imposition : The BIER imposition object + * + * A BIER imposition object is present in the IP mcast output list + * and represents the imposition of a BIER bitmask. After BIER header + * imposition the packet is forward within the appropriate/specifid + * BIER table + */ + +#include <vnet/bier/bier_imp.h> +#include <vnet/bier/bier_table.h> +#include <vnet/bier/bier_hdr_inlines.h> +#include <vnet/fib/fib_node.h> +#include <vnet/mpls/mpls_types.h> + +/** + * The memory pool of all imp objects + */ +bier_imp_t *bier_imp_pool; + +/** + * When constructing the BIER imp ID from an index and BSL, shift + * the BSL this far + */ +#define BIER_IMP_ID_HLEN_SHIFT 24 + +static void +bier_imp_lock_i (bier_imp_t *bi) +{ + bi->bi_locks++; +} + +void +bier_imp_lock (index_t bii) +{ + bier_imp_lock_i(bier_imp_get(bii)); +} + +static index_t +bier_imp_get_index(bier_imp_t *bi) +{ + return (bi - bier_imp_pool); +} + +index_t +bier_imp_add_or_lock (const bier_table_id_t *bti, + bier_bp_t sender, + const bier_bit_string_t *bs) +{ + bier_imp_t *bi = NULL; + fib_protocol_t fproto; + index_t btii; + + pool_get_aligned(bier_imp_pool, bi, CLIB_CACHE_LINE_BYTES); + + bi->bi_tbl = *bti; + btii = bier_table_add_or_lock(bti, MPLS_LABEL_INVALID); + + /* + * init the BIER header we will paint on in the data plane + */ + bier_hdr_init(&bi->bi_hdr, + BIER_HDR_VERSION_1, + BIER_HDR_PROTO_INVALID, // filled in later + bti->bti_hdr_len, + 0, // entropy + sender); + bier_hdr_hton(&bi->bi_hdr); + clib_memcpy(&bi->bi_bits, bs->bbs_buckets, bs->bbs_len); + + bier_imp_lock_i(bi); + + /* + * get and stack on the forwarding info from the table + */ + FOR_EACH_FIB_IP_PROTOCOL(fproto) + { + /* + * initialise to invalid first, lest we pick up garbage + * from the pool alloc + */ + dpo_id_t dpo = DPO_INVALID; + bi->bi_dpo[fproto] = dpo; + + bier_table_contribute_forwarding(btii, &dpo); + dpo_stack(DPO_BIER_IMP, fib_proto_to_dpo(fproto), + &bi->bi_dpo[fproto], + &dpo); + dpo_reset(&dpo); + } + + return (bier_imp_get_index(bi)); +} + +void +bier_imp_unlock (index_t bii) +{ + fib_protocol_t fproto; + bier_imp_t *bi; + + if (INDEX_INVALID == bii) + { + return; + } + + bi = bier_imp_get(bii); + + bi->bi_locks--; + + if (0 == bi->bi_locks) + { + bier_table_unlock(&bi->bi_tbl); + + FOR_EACH_FIB_IP_PROTOCOL(fproto) + { + dpo_reset(&bi->bi_dpo[fproto]); + } + pool_put(bier_imp_pool, bi); + } +} + +u8* +format_bier_imp (u8* s, va_list *args) +{ + index_t bii = va_arg (*args, index_t); + u32 indent = va_arg(*args, u32); + bier_show_flags_t flags = va_arg(*args, bier_show_flags_t); + bier_imp_t *bi; + + bi = bier_imp_get(bii); + + s = format(s, "bier-imp:[%d]: tbl:[%U] hdr:[%U]", + bier_imp_get_index(bi), + format_bier_table_id, &bi->bi_tbl, + format_bier_hdr, &bi->bi_hdr); + + if (BIER_SHOW_DETAIL & flags) + { + bier_bit_string_t bbs; + bier_hdr_t copy; + + copy = bi->bi_hdr; + bier_hdr_ntoh(©); + bier_bit_string_init(&bbs, + bier_hdr_get_len_id(©), + bi->bi_bits.bits); + + s = format(s, "\n%U%U", + format_white_space, indent, + format_bier_bit_string, &bbs); + s = format(s, "\n%U%U", + format_white_space, indent, + format_dpo_id, &bi->bi_dpo, indent+2); + } + + return (s); +} + +void +bier_imp_contribute_forwarding (index_t bii, + dpo_proto_t proto, + dpo_id_t *dpo) +{ + dpo_set(dpo, DPO_BIER_IMP, proto, bii); +} + +const static char* const bier_imp_ip4_nodes[] = +{ + "bier-imp-ip4", + NULL, +}; +const static char* const bier_imp_ip6_nodes[] = +{ + "bier-imp-ip6", + NULL, +}; + +const static char* const * const bier_imp_nodes[DPO_PROTO_NUM] = +{ + [DPO_PROTO_IP4] = bier_imp_ip4_nodes, + [DPO_PROTO_IP6] = bier_imp_ip6_nodes, +}; + +static void +bier_imp_dpo_lock (dpo_id_t *dpo) +{ + bier_imp_lock(dpo->dpoi_index); +} + +static void +bier_imp_dpo_unlock (dpo_id_t *dpo) +{ + bier_imp_unlock(dpo->dpoi_index); +} + +static void +bier_imp_dpo_mem_show (void) +{ + fib_show_memory_usage("BIER imposition", + pool_elts(bier_imp_pool), + pool_len(bier_imp_pool), + sizeof(bier_imp_t)); +} + +static u8* +format_bier_imp_dpo (u8* s, va_list *ap) +{ + index_t index = va_arg(*ap, index_t); + u32 indent = va_arg(*ap, u32); + + s = format(s, "%U", format_bier_imp, index, indent, BIER_SHOW_DETAIL); + + return (s); +} + +const static dpo_vft_t bier_imp_vft = { + .dv_lock = bier_imp_dpo_lock, + .dv_unlock = bier_imp_dpo_unlock, + .dv_format = format_bier_imp_dpo, + .dv_mem_show = bier_imp_dpo_mem_show, +}; + +clib_error_t * +bier_imp_db_module_init (vlib_main_t *vm) +{ + dpo_register(DPO_BIER_IMP, &bier_imp_vft, bier_imp_nodes); + + return (NULL); +} + +VLIB_INIT_FUNCTION (bier_imp_db_module_init); + +static clib_error_t * +show_bier_imp (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + bier_imp_t *bi; + index_t bii; + + bii = INDEX_INVALID; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { + if (unformat (input, "%d", &bii)) + ; + else + { + break; + } + } + + if (INDEX_INVALID == bii) + { + pool_foreach(bi, bier_imp_pool, + ({ + vlib_cli_output(vm, "%U", format_bier_imp, + bier_imp_get_index(bi), + 1, + BIER_SHOW_BRIEF); + })); + } + else + { + vlib_cli_output(vm, "%U", format_bier_imp, bii, 1, + BIER_SHOW_DETAIL); + } + return (NULL); +} + +VLIB_CLI_COMMAND (show_bier_imp_node, static) = { + .path = "show bier imp", + .short_help = "show bier imp [index]", + .function = show_bier_imp, +}; diff --git a/src/vnet/bier/bier_imp.h b/src/vnet/bier/bier_imp.h new file mode 100644 index 00000000000..fa53989fc93 --- /dev/null +++ b/src/vnet/bier/bier_imp.h @@ -0,0 +1,87 @@ +/* + * 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. + */ +/** + * bier_imposition : The BIER imposition object + * + * A BIER imposition object is present in the IP mcast output list + * and represents the imposition of a BIER bitmask. After BIER header + * imposition the packet is forward within the appropriate/specifid + * BIER table + */ + +#ifndef __BIER_IMPOSITION_H__ +#define __BIER_IMPOSITION_H__ + +#include <vnet/bier/bier_types.h> +#include <vnet/fib/fib_types.h> +#include <vnet/dpo/dpo.h> + +/** + * The BIER imposition object + */ +typedef struct bier_imp_t_ { + /** + * The BIER table into which to forward the post imposed packet + */ + bier_table_id_t bi_tbl; + + /** + * number of locks + */ + u32 bi_locks; + + /** + * The DPO contirubted from the resolving BIER table. + * One per-IP protocol. This allows us to share a BIER imposition + * object for a IPv4 and IPv6 mfib path. + */ + dpo_id_t bi_dpo[FIB_PROTOCOL_IP_MAX]; + + /** + * The Header to impose. + */ + bier_hdr_t bi_hdr; + + /** + * The bit string. + * This is a memory v. speed tradeoff. We inline here the + * largest header type so as the bitstring is on the same + * cacheline as the header. + */ + bier_bit_mask_4096_t bi_bits; +} bier_imp_t; + +extern index_t bier_imp_add_or_lock(const bier_table_id_t *bt, + bier_bp_t sender, + const bier_bit_string_t *bs); + +extern void bier_imp_unlock(index_t bii); +extern void bier_imp_lock(index_t bii); + +extern u8* format_bier_imp(u8* s, va_list *ap); + +extern void bier_imp_contribute_forwarding(index_t bii, + dpo_proto_t proto, + dpo_id_t *dpo); + +extern bier_imp_t *bier_imp_pool; + +always_inline bier_imp_t* +bier_imp_get (index_t bii) +{ + return (pool_elt_at_index(bier_imp_pool, bii)); +} + +#endif diff --git a/src/vnet/bier/bier_imp_node.c b/src/vnet/bier/bier_imp_node.c new file mode 100644 index 00000000000..e9aae93b460 --- /dev/null +++ b/src/vnet/bier/bier_imp_node.c @@ -0,0 +1,217 @@ +/* + * 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 <vnet/bier/bier_imp.h> +#include <vnet/bier/bier_hdr_inlines.h> +#include <vnet/ip/ip.h> + +/** + * @brief A struct to hold tracing information for the BIER imposition + * node. + */ +typedef struct bier_imp_trace_t_ +{ + /** + * BIER imposition object hit + */ + index_t imp; + + /** + * BIER hdr applied + */ + bier_hdr_t hdr; +} bier_imp_trace_t; + +always_inline uword +bier_imp_dpo_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame, + fib_protocol_t fproto, + bier_hdr_proto_id_t bproto) +{ + u32 n_left_from, next_index, * from, * to_next; + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + vlib_buffer_t * b0; + bier_imp_t *bimp0; + bier_hdr_t *hdr0; + u32 bi0, bii0; + u32 next0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + bii0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX]; + bimp0 = bier_imp_get(bii0); + + if (FIB_PROTOCOL_IP4 == fproto) + { + /* + * decrement the TTL on ingress to the BIER domain + */ + ip4_header_t * ip0 = vlib_buffer_get_current(b0); + u32 checksum0; + + checksum0 = ip0->checksum + clib_host_to_net_u16 (0x0100); + checksum0 += checksum0 >= 0xffff; + + ip0->checksum = checksum0; + ip0->ttl -= 1; + + /* + * calculate an entropy + */ + if (0 == vnet_buffer(b0)->ip.flow_hash) + { + vnet_buffer(b0)->ip.flow_hash = + ip4_compute_flow_hash (ip0, IP_FLOW_HASH_DEFAULT); + } + } + if (FIB_PROTOCOL_IP6 == fproto) + { + /* + * decrement the TTL on ingress to the BIER domain + */ + ip6_header_t * ip0 = vlib_buffer_get_current(b0); + + ip0->hop_limit -= 1; + + /* + * calculate an entropy + */ + if (0 == vnet_buffer(b0)->ip.flow_hash) + { + vnet_buffer(b0)->ip.flow_hash = + ip6_compute_flow_hash (ip0, IP_FLOW_HASH_DEFAULT); + } + } + + /* Paint the BIER header */ + vlib_buffer_advance(b0, -(sizeof(bier_hdr_t) + + bier_hdr_len_id_to_num_bytes(bimp0->bi_tbl.bti_hdr_len))); + hdr0 = vlib_buffer_get_current(b0); + clib_memcpy(hdr0, &bimp0->bi_hdr, + (sizeof(bier_hdr_t) + + bier_hdr_len_id_to_num_bytes(bimp0->bi_tbl.bti_hdr_len))); + /* + * Fixup the entropy and protocol, both of which have a + * zero value post the paint job + */ + hdr0->bh_oam_dscp_proto |= + clib_host_to_net_u16(bproto << BIER_HDR_PROTO_FIELD_SHIFT); + hdr0->bh_first_word |= + clib_host_to_net_u32((vnet_buffer(b0)->ip.flow_hash & + BIER_HDR_ENTROPY_FIELD_MASK) << + BIER_HDR_ENTROPY_FIELD_SHIFT); + + /* next node */ + next0 = bimp0->bi_dpo[fproto].dpoi_next_node; + vnet_buffer(b0)->ip.adj_index[VLIB_TX] = + bimp0->bi_dpo[fproto].dpoi_index; + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + bier_imp_trace_t *tr = + vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->imp = bii0; + tr->hdr = *hdr0; + } + + vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + return from_frame->n_vectors; +} + +static u8 * +format_bier_imp_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + bier_imp_trace_t * t; + u32 indent; + + t = va_arg (*args, bier_imp_trace_t *); + indent = format_get_indent (s); + + s = format (s, "%U", format_bier_imp, t->imp, indent, BIER_SHOW_BRIEF); + return (s); +} + +static uword +bier_imp_ip4 (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (bier_imp_dpo_inline(vm, node, frame, + FIB_PROTOCOL_IP4, + BIER_HDR_PROTO_IPV4)); +} + +VLIB_REGISTER_NODE (bier_imp_ip4_node) = { + .function = bier_imp_ip4, + .name = "bier-imp-ip4", + .vector_size = sizeof (u32), + + .format_trace = format_bier_imp_trace, + .n_next_nodes = 1, + .next_nodes = { + [0] = "error-drop", + } +}; +VLIB_NODE_FUNCTION_MULTIARCH (bier_imp_ip4_node, bier_imp_ip4) + +static uword +bier_imp_ip6 (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (bier_imp_dpo_inline(vm, node, frame, + FIB_PROTOCOL_IP6, + BIER_HDR_PROTO_IPV6)); +} + +VLIB_REGISTER_NODE (bier_imp_ip6_node) = { + .function = bier_imp_ip6, + .name = "bier-imp-ip6", + .vector_size = sizeof (u32), + + .format_trace = format_bier_imp_trace, + .n_next_nodes = 1, + .next_nodes = { + [0] = "error-drop", + } +}; +VLIB_NODE_FUNCTION_MULTIARCH (bier_imp_ip6_node, bier_imp_ip6) diff --git a/src/vnet/bier/bier_input.c b/src/vnet/bier/bier_input.c new file mode 100644 index 00000000000..88b37fc80c0 --- /dev/null +++ b/src/vnet/bier/bier_input.c @@ -0,0 +1,175 @@ +/* + * 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 <vnet/buffer.h> + +#include <vnet/bier/bier_table.h> +#include <vnet/bier/bier_hdr_inlines.h> + +typedef enum { +#define bier_error(n,s) BIER_INPUT_ERROR_##n, +#include <vnet/bier/bier_input_error.def> +#undef bier_error + BIER_INPUT_N_ERROR, +} bier_input_error_t; + +static char * bier_error_strings[] = { +#define bier_error(n,s) s, +#include <vnet/bier/bier_input_error.def> +#undef bier_error +}; + +typedef enum bier_input_next_t_ { + BIER_INPUT_NEXT_BIER_LOOKUP, + BIER_INPUT_NEXT_DROP, + BIER_INPUT_N_NEXT, +} bier_input_next_t; + +vlib_node_registration_t bier_input_node; + +/** + * @brief Packet trace recoed for a BIER output + */ +typedef struct bier_input_trace_t_ +{ + u32 next_index; + u32 bt_index; +} bier_input_trace_t; + +static int +bier_hdr_validate (bier_hdr_t *bier_hdr, + bier_hdr_len_id_t expected_length) +{ + /* + * checks: + * - the version field must be 1 + * - the header length matches the length expected + */ + if (PREDICT_FALSE((BIER_HDR_VERSION_1 != bier_hdr_get_version(bier_hdr)) || + (expected_length != bier_hdr_get_len_id(bier_hdr)))) { + return (0); + } + + return (1); +} + +static uword +bier_input (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + u32 n_left_from, next_index, * from, * to_next; + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + + /* + * objection your honour! speculation! + */ + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, + to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + const bier_table_t *bt0; + vlib_buffer_t * b0; + bier_hdr_t * bh0; + u32 bi0, next0; + u32 bt_index0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + bh0 = vlib_buffer_get_current (b0); + bier_hdr_ntoh(bh0); + + /* + * In the MPLS decap node we squirelled away the + * index for the BIER table as the tx adjacency + */ + bt_index0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX]; + bt0 = bier_table_get(bt_index0); + + if (PREDICT_TRUE(bier_hdr_validate(bh0, bt0->bt_id.bti_hdr_len))) + { + next0 = BIER_INPUT_NEXT_BIER_LOOKUP; + } else { + next0 = BIER_INPUT_NEXT_DROP; + b0->error = node->errors[BIER_INPUT_ERROR_INVALID_HEADER]; + } + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + bier_input_trace_t *tr; + + tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->next_index = next0; + tr->bt_index = bt_index0; + } + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, bier_input_node.index, + BIER_INPUT_ERROR_PKTS_VALID, + from_frame->n_vectors); + return (from_frame->n_vectors); +} + +static u8 * +format_bier_input_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + bier_input_trace_t * t = va_arg (*args, bier_input_trace_t *); + + s = format (s, " next [%d], BIER Table index %d", + t->next_index, t->bt_index); + return s; +} + +VLIB_REGISTER_NODE (bier_input_node) = { + .function = bier_input, + .name = "bier-input", + /* Takes a vector of packets. */ + .vector_size = sizeof (u32), + + .n_errors = BIER_INPUT_N_ERROR, + .error_strings = bier_error_strings, + + .n_next_nodes = BIER_INPUT_N_NEXT, + .next_nodes = { + [BIER_INPUT_NEXT_BIER_LOOKUP] = "bier-lookup", + [BIER_INPUT_NEXT_DROP] = "bier-drop", + }, + + .format_trace = format_bier_input_trace, +}; diff --git a/src/vnet/bier/bier_input_error.def b/src/vnet/bier/bier_input_error.def new file mode 100644 index 00000000000..6f0034316f0 --- /dev/null +++ b/src/vnet/bier/bier_input_error.def @@ -0,0 +1,19 @@ +/* + * 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. + */ + +bier_error (NONE, "no error") +bier_error (TABLE_NOT_FOUND, "table not found") +bier_error (INVALID_HEADER, "invalid header") +bier_error (PKTS_VALID, "BIER input packets") diff --git a/src/vnet/bier/bier_lookup.c b/src/vnet/bier/bier_lookup.c new file mode 100644 index 00000000000..4cf29f886e4 --- /dev/null +++ b/src/vnet/bier/bier_lookup.c @@ -0,0 +1,367 @@ +/* + * 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 <vnet/buffer.h> +#include <vnet/vnet.h> + +#include <vnet/bier/bier_fmask.h> +#include <vnet/bier/bier_hdr_inlines.h> +#include <vnet/bier/bier_table.h> +#include <vnet/bier/bier_fmask.h> + +/** + * Struct maintining the per-worker thread data for BIER lookups + */ +typedef struct bier_lookup_main_t_ +{ + /* per-cpu vector of cloned packets */ + u32 **blm_clones; + /* per-cpu vector of BIER fmasks */ + u32 **blm_fmasks; +} bier_lookup_main_t; + +/** + * Single instance of the lookup main + */ +static bier_lookup_main_t bier_lookup_main; + +static char * bier_lookup_error_strings[] = { +#define bier_error(n,s) s, +#include <vnet/bier/bier_lookup_error.def> +#undef bier_error +}; + +/* + * Keep these values sematically the same as BIER lookup + */ +#define foreach_bier_lookup_next \ + _(DROP, "bier-drop") \ + _(OUTPUT, "bier-output") + +typedef enum { +#define _(s,n) BIER_LOOKUP_NEXT_##s, + foreach_bier_lookup_next +#undef _ + BIER_LOOKUP_N_NEXT, +} bier_lookup_next_t; + +typedef enum { +#define bier_error(n,s) BIER_LOOKUP_ERROR_##n, +#include <vnet/bier/bier_lookup_error.def> +#undef bier_error + BIER_LOOKUP_N_ERROR, +} bier_lookup_error_t; + +vlib_node_registration_t bier_lookup_node; + +/** + * @brief Packet trace recoed for a BIER lookup + */ +typedef struct bier_lookup_trace_t_ +{ + u32 next_index; + index_t bt_index; + index_t bfm_index; +} bier_lookup_trace_t; + +static uword +bier_lookup (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + u32 n_left_from, next_index, * from, * to_next; + bier_lookup_main_t *blm = &bier_lookup_main; + u32 thread_index = vlib_get_thread_index(); + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + next_index = BIER_LOOKUP_NEXT_DROP; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, + to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + bier_bit_mask_bucket_t buckets_copy[BIER_HDR_BUCKETS_256]; + u32 next0, bi0, n_bytes, bti0, bfmi0; + const bier_fmask_t *bfm0; + const bier_table_t *bt0; + u16 index, num_buckets; + const bier_hdr_t *bh0; + bier_bit_string_t bbs; + vlib_buffer_t *b0; + bier_bp_t fbs; + int bucket; + + bi0 = from[0]; + from += 1; + n_left_from -= 1; + + b0 = vlib_get_buffer (vm, bi0); + bh0 = vlib_buffer_get_current (b0); + bti0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX]; + + /* + * default to drop so that if no bits are matched then + * that is where we go - DROP. + */ + next0 = BIER_LOOKUP_NEXT_DROP; + + /* + * At the imposition or input node, + * we stored the BIER Table index in the TX adjacency + */ + bt0 = bier_table_get(vnet_buffer(b0)->ip.adj_index[VLIB_TX]); + + /* + * we should only forward via one for the ECMP tables + */ + ASSERT(!bier_table_is_main(bt0)); + + /* + * number of integer sized buckets + */ + n_bytes = bier_hdr_len_id_to_num_buckets(bt0->bt_id.bti_hdr_len); + vnet_buffer(b0)->bier.n_bytes = n_bytes; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = ~0; + num_buckets = n_bytes / sizeof(int); + bier_bit_string_init(&bbs, + bt0->bt_id.bti_hdr_len, + buckets_copy); + memcpy(bbs.bbs_buckets, bh0->bh_bit_string, bbs.bbs_len); + + /* + * reset the fmask and clone storage vectors + */ + vec_reset_length (blm->blm_fmasks[thread_index]); + vec_reset_length (blm->blm_clones[thread_index]); + + /* + * Loop through the buckets in the header + */ + for (index = 0; index < num_buckets; index++) { + /* + * loop through each bit in the bucket + */ + bucket = ((int*)bbs.bbs_buckets)[index]; + + while (bucket) { + fbs = bier_find_first_bit_string_set(bucket); + fbs += (((num_buckets - 1) - index) * + BIER_BIT_MASK_BITS_PER_INT); + + bfmi0 = bier_table_fwd_lookup(bt0, fbs); + + /* + * whatever happens, the bit we just looked for + * MUST be cleared from the packet + * otherwise we could be in this loop a while ... + */ + bier_bit_string_clear_bit(&bbs, fbs); + + if (PREDICT_TRUE(INDEX_INVALID != bfmi0)) + { + bfm0 = bier_fmask_get(bfmi0); + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = bfmi0; + + /* + * use the bit-string on the fmask to reset + * the bits in the header we are walking + */ + bier_bit_string_clear_string( + &bfm0->bfm_bits.bfmb_input_reset_string, + &bbs); + bucket = ((int*)bbs.bbs_buckets)[index]; + + /* + * the fmask is resolved so replicate a + * packet its way + */ + next0 = BIER_LOOKUP_NEXT_OUTPUT; + + vec_add1 (blm->blm_fmasks[thread_index], bfmi0); + } else { + /* + * go to the next bit-position set + */ + bucket = ((int*)bbs.bbs_buckets)[index]; + continue; + } + } + } + + /* + * Full mask now processed. + * Create the number of clones we need based on the number + * of fmasks we are sending to. + */ + u8 num_cloned, clone; + u32 n_clones; + + n_clones = vec_len(blm->blm_fmasks[thread_index]); + + if (PREDICT_TRUE(0 != n_clones)) + { + ASSERT(n_clones < 256); + num_cloned = vlib_buffer_clone(vm, bi0, + blm->blm_clones[thread_index], + n_clones, 128); + + if (num_cloned != n_clones) + { + vlib_node_increment_counter + (vm, node->node_index, + BIER_LOOKUP_ERROR_BUFFER_ALLOCATION_FAILURE, 1); + } + + for (clone = 0; clone < num_cloned; clone++) + { + vlib_buffer_t *c0; + u32 ci0; + + ci0 = blm->blm_clones[thread_index][clone]; + c0 = vlib_get_buffer(vm, ci0); + + to_next[0] = ci0; + to_next += 1; + n_left_to_next -= 1; + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + bier_lookup_trace_t *tr; + + vlib_trace_buffer (vm, node, next0, c0, 0); + tr = vlib_add_trace (vm, node, c0, sizeof (*tr)); + tr->bt_index = bti0; + tr->bfm_index = blm->blm_fmasks[thread_index][clone]; + + c0->flags |= VLIB_BUFFER_IS_TRACED; + } + + vlib_validate_buffer_enqueue_x1(vm, node, next_index, + to_next, n_left_to_next, + ci0, next0); + + /* + * After the enqueue it is possible that we over-flow the + * frame of the to-next node. When this happens we need to + * 'put' that full frame to the node and get a fresh empty + * one. Note that these are macros with side effects that + * change to_next & n_left_to_next + */ + if (PREDICT_FALSE(0 == n_left_to_next)) + { + vlib_put_next_frame (vm, node, next_index, + n_left_to_next); + vlib_get_next_frame (vm, node, next_index, + to_next, n_left_to_next); + } + } + } + else + { + /* + * no clones/replications required. drop this packet + */ + next0 = BIER_LOOKUP_NEXT_DROP; + to_next[0] = bi0; + to_next += 1; + n_left_to_next -= 1; + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + bier_lookup_trace_t *tr; + + tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); + + tr->bt_index = bti0; + tr->bfm_index = ~0; + } + + vlib_validate_buffer_enqueue_x1(vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, bier_lookup_node.index, + BIER_LOOKUP_ERROR_NONE, + from_frame->n_vectors); + return (from_frame->n_vectors); +} + +static u8 * +format_bier_lookup_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + bier_lookup_trace_t * t = va_arg (*args, bier_lookup_trace_t *); + + s = format (s, "BIER: next [%d], tbl:%d BFM:%d", + t->next_index, + t->bt_index, + t->bfm_index); + return s; +} + +VLIB_REGISTER_NODE (bier_lookup_node) = { + .function = bier_lookup, + .name = "bier-lookup", + /* Takes a vector of packets. */ + .vector_size = sizeof (u32), + + .n_errors = BIER_LOOKUP_N_ERROR, + .error_strings = bier_lookup_error_strings, + + .format_trace = format_bier_lookup_trace, + .n_next_nodes = BIER_LOOKUP_N_NEXT, + .next_nodes = { + [BIER_LOOKUP_NEXT_DROP] = "bier-drop", + [BIER_LOOKUP_NEXT_OUTPUT] = "bier-output", + }, +}; + +clib_error_t * +bier_lookup_module_init (vlib_main_t * vm) +{ + bier_lookup_main_t *blm = &bier_lookup_main; + u32 thread_index; + + vec_validate (blm->blm_clones, vlib_num_workers()); + vec_validate (blm->blm_fmasks, vlib_num_workers()); + + for (thread_index = 0; + thread_index <= vlib_num_workers(); + thread_index++) + { + /* + * 4096 is the most we will ever need to support + * a Bit-Mask length of 4096 + */ + vec_validate(blm->blm_fmasks[thread_index], 4095); + vec_validate(blm->blm_clones[thread_index], 4095); + } + + return 0; +} + +VLIB_INIT_FUNCTION (bier_lookup_module_init); diff --git a/src/vnet/bier/bier_lookup_error.def b/src/vnet/bier/bier_lookup_error.def new file mode 100644 index 00000000000..a0510b40ca3 --- /dev/null +++ b/src/vnet/bier/bier_lookup_error.def @@ -0,0 +1,18 @@ +/* + * 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. + */ + +bier_error (NONE, "no error") +bier_error (FMASK_UNRES, "fmask unresolved") +bier_error (BUFFER_ALLOCATION_FAILURE, "Buffer Allocation Failure") diff --git a/src/vnet/bier/bier_output.c b/src/vnet/bier/bier_output.c new file mode 100644 index 00000000000..fce6c50b309 --- /dev/null +++ b/src/vnet/bier/bier_output.c @@ -0,0 +1,195 @@ +/* + * 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 <vnet/buffer.h> + +#include <vnet/bier/bier_fmask.h> +#include <vnet/bier/bier_hdr_inlines.h> +#include <vlib/vlib.h> + +static char * bier_output_error_strings[] = { +#define bier_error(n,s) s, +#include <vnet/bier/bier_output_error.def> +#undef bier_error +}; + +/* + * Keep these values sematically the same as BIER output + */ +#define foreach_bier_output_next \ + _(DROP, "bier-drop") + +typedef enum { +#define _(s,n) BIER_OUTPUT_NEXT_##s, + foreach_bier_output_next +#undef _ + BIER_OUTPUT_N_NEXT, +} bier_output_next_t; + +typedef enum { +#define bier_error(n,s) BIER_OUTPUT_ERROR_##n, +#include <vnet/bier/bier_output_error.def> +#undef bier_error + BIER_OUTPUT_N_ERROR, +} bier_output_error_t; + +/** + * Forward declaration + */ +vlib_node_registration_t bier_output_node; + +/** + * @brief Packet trace recoed for a BIER output + */ +typedef struct bier_output_trace_t_ +{ + u32 next_index; + index_t bfm_index; +} bier_output_trace_t; + +static uword +bier_output (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + u32 n_left_from, next_index, * from, * to_next; + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + + // vnet_buffer(b0)->sw_if_index[VLIB_TX] = d0->tx_fib_index; + + /* + * objection your honour! speculation! + */ + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, + to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + bier_output_next_t next0; + bier_bit_string_t bbs; + vlib_buffer_t * b0; + bier_fmask_t *bfm0; + bier_hdr_t *bh0; + u32 bi0, *h0; + u32 bfmi0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + bh0 = vlib_buffer_get_current (b0); + bier_bit_string_init_from_hdr(bh0, &bbs); + + /* + * In the BIER Lookup node we squirelled away the + * BIER fmask index as the adj index + */ + bfmi0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX]; + bfm0 = bier_fmask_get(bfmi0); + + /* + * perform the logical AND of the packet's mask with + * that of the fmask objects, to reset the bits that + * are only on the shortest path the the fmask NH. + */ + bier_bit_string_logical_and_string( + &bfm0->bfm_bits.bfmb_input_reset_string, + &bbs); + + /* + * this is the last time we touch the BIER header + * so flip to network order + */ + bier_hdr_hton(bh0); + + /* + * paint the BIER peer's label + */ + if (!(bfm0->bfm_flags & BIER_FMASK_FLAG_DISP)) + { + vlib_buffer_advance(b0, -(word)sizeof(mpls_label_t)); + h0 = vlib_buffer_get_current(b0); + h0[0] = bfm0->bfm_label; + } + + /* + * setup next graph node + */ + next0 = bfm0->bfm_dpo.dpoi_next_node; + vnet_buffer(b0)->ip.adj_index[VLIB_TX] = bfm0->bfm_dpo.dpoi_index; + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + bier_output_trace_t *tr; + + tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->next_index = next0; + tr->bfm_index = bfmi0; + } + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, bier_output_node.index, + BIER_OUTPUT_ERROR_NONE, + from_frame->n_vectors); + return (from_frame->n_vectors); +} + +static u8 * +format_bier_output_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + bier_output_trace_t * t = va_arg (*args, bier_output_trace_t *); + + s = format (s, " next [%d], BFM index %d", + t->next_index, t->bfm_index); + return s; +} + +VLIB_REGISTER_NODE (bier_output_node) = { + .function = bier_output, + .name = "bier-output", + /* Takes a vector of packets. */ + .vector_size = sizeof (u32), + + .n_errors = BIER_OUTPUT_N_ERROR, + .error_strings = bier_output_error_strings, + + .n_next_nodes = BIER_OUTPUT_N_NEXT, + .next_nodes = { + [BIER_OUTPUT_NEXT_DROP] = "bier-drop", + }, + + .format_trace = format_bier_output_trace, +}; diff --git a/src/vnet/bier/bier_output_error.def b/src/vnet/bier/bier_output_error.def new file mode 100644 index 00000000000..983c13c0c2d --- /dev/null +++ b/src/vnet/bier/bier_output_error.def @@ -0,0 +1,17 @@ +/* + * 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. + */ + +bier_error (NONE, "no error") +bier_error (FMASK_UNRES, "fmask unresolved") diff --git a/src/vnet/bier/bier_table.c b/src/vnet/bier/bier_table.c new file mode 100644 index 00000000000..74a09910601 --- /dev/null +++ b/src/vnet/bier/bier_table.c @@ -0,0 +1,744 @@ +/* + * 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 <vppinfra/vec.h> + +#include <vnet/bier/bier_table.h> +#include <vnet/bier/bier_entry.h> +#include <vnet/bier/bier_update.h> +#include <vnet/bier/bier_fmask_db.h> +#include <vnet/bier/bier_fmask.h> + +#include <vnet/fib/mpls_fib.h> +#include <vnet/mpls/mpls.h> +#include <vnet/fib/fib_path_list.h> + +/** + * Memory pool of all the allocated tables + */ +bier_table_t *bier_table_pool; + +/** + * DB store of all BIER tables index by SD/set/hdr-len + */ +static uword *bier_tables_by_key; + +/** + * The magic number of BIER ECMP tables to create. + * The load-balance distribution algorithm will use a power of 2 + * for the number of buckets, which constrains the choice. + */ +#define BIER_N_ECMP_TABLES 16 + +static inline index_t +bier_table_get_index (const bier_table_t *bt) +{ + return (bt - bier_table_pool); +} + +int +bier_table_is_main (const bier_table_t *bt) +{ + return (BIER_ECMP_TABLE_ID_MAIN == bt->bt_id.bti_ecmp); +} + +/* + * Construct the key to use to find a BIER table + * in the global hash map + */ +static u32 +bier_table_mk_key (const bier_table_id_t *id) +{ + /* + * the set and sub-domain Ids are 8 bit values. + * we have space for ECMP table ID and talbe type (SPF/TE) + * for later + */ + u32 key = ((id->bti_sub_domain << 24) | + (id->bti_set << 16) | + (id->bti_ecmp << 8) | + (id->bti_hdr_len << 4) | + (id->bti_type)); + + return (key); +} + +static void +bier_table_init (bier_table_t *bt, + const bier_table_id_t *id, + mpls_label_t ll) +{ + u32 num_entries; + + bt->bt_lfei = FIB_NODE_INDEX_INVALID; + bt->bt_id = *id; + bt->bt_ll = ll; + num_entries = bier_hdr_len_id_to_num_bits(bt->bt_id.bti_hdr_len); + + /* + * create the lookup table of entries. + */ + if (bier_table_is_main(bt)) + { + vec_validate_init_empty_aligned(bt->bt_entries, + num_entries, + INDEX_INVALID, + CLIB_CACHE_LINE_BYTES); + fib_table_find_or_create_and_lock(FIB_PROTOCOL_MPLS, + MPLS_FIB_DEFAULT_TABLE_ID, + FIB_SOURCE_BIER); + } + else + { + vec_validate_init_empty_aligned(bt->bt_fmasks, + num_entries, + INDEX_INVALID, + CLIB_CACHE_LINE_BYTES); + } +} + +static void +bier_table_rm_lfib (bier_table_t *bt) +{ + if (FIB_NODE_INDEX_INVALID != bt->bt_lfei) + { + fib_table_entry_delete_index(bt->bt_lfei, + FIB_SOURCE_BIER); + } + bt->bt_lfei = FIB_NODE_INDEX_INVALID; +} + +static void +bier_table_destroy (bier_table_t *bt) +{ + if (bier_table_is_main(bt)) + { + index_t *bei; + + fib_path_list_unlock(bt->bt_pl); + bt->bt_pl = FIB_NODE_INDEX_INVALID; + /* + * unresolve/remove all entries from the table + */ + vec_foreach (bei, bt->bt_entries) + { + if (INDEX_INVALID != *bei) + { + bier_entry_delete(*bei); + } + } + vec_free (bt->bt_entries); + fib_table_unlock(fib_table_find(FIB_PROTOCOL_MPLS, + MPLS_FIB_DEFAULT_TABLE_ID), + FIB_PROTOCOL_MPLS, + FIB_SOURCE_BIER); + } + else + { + index_t *bfmi; + + /* + * unlock any fmasks + */ + vec_foreach (bfmi, bt->bt_fmasks) + { + bier_fmask_unlock(*bfmi); + } + vec_free(bt->bt_fmasks); + } + + hash_unset(bier_tables_by_key, + bier_table_mk_key(&bt->bt_id)); + pool_put(bier_table_pool, bt); +} + +static void +bier_table_lock_i (bier_table_t *bt) +{ + bt->bt_locks++; +} + +static void +bier_table_unlock_i (bier_table_t *bt) +{ + bt->bt_locks--; + + if (0 == bt->bt_locks) + { + bier_table_rm_lfib(bt); + bier_table_destroy(bt); + } +} + +void +bier_table_unlock (const bier_table_id_t *bti) +{ + uword *p; + u32 key; + + key = bier_table_mk_key(bti); + + p = hash_get (bier_tables_by_key, key); + + if (NULL != p) { + bier_table_unlock_i(bier_table_get(p[0])); + } +} + +static void +bier_table_mk_lfib (bier_table_t *bt) +{ + /* + * Add a new MPLS lfib entry + */ + if (MPLS_LABEL_INVALID != bt->bt_ll) { + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_MPLS, + .fp_len = 21, + .fp_label = bt->bt_ll, + .fp_eos = MPLS_EOS, + .fp_payload_proto = DPO_PROTO_BIER, + }; + u32 mpls_fib_index; + dpo_id_t dpo = DPO_INVALID; + + /* + * stack the entry on the forwarding chain prodcued by the + * path-list via the ECMP tables. + */ + fib_path_list_contribute_forwarding(bt->bt_pl, + FIB_FORW_CHAIN_TYPE_BIER, + &dpo); + + mpls_fib_index = fib_table_find(FIB_PROTOCOL_MPLS, + MPLS_FIB_DEFAULT_TABLE_ID); + bt->bt_lfei = fib_table_entry_special_dpo_add(mpls_fib_index, + &pfx, + FIB_SOURCE_BIER, + FIB_ENTRY_FLAG_EXCLUSIVE, + &dpo); + dpo_reset(&dpo); + } +} + +static bier_table_t * +bier_table_find (const bier_table_id_t *bti) +{ + uword *p; + u32 key; + + key = bier_table_mk_key(bti); + + p = hash_get(bier_tables_by_key, key); + + if (NULL != p) + { + return (bier_table_get(p[0])); + } + + return (NULL); +} + +static bier_table_t * +bier_table_mk_ecmp (index_t bti) +{ + fib_route_path_t *rpaths; + fib_node_index_t pli; + bier_table_t *bt; + int ii; + + rpaths = NULL; + bt = bier_table_get(bti); + + vec_validate(rpaths, BIER_N_ECMP_TABLES-1); + + vec_foreach_index(ii, rpaths) + { + rpaths[ii].frp_bier_tbl = bt->bt_id; + rpaths[ii].frp_bier_tbl.bti_ecmp = ii; + rpaths[ii].frp_flags = FIB_ROUTE_PATH_BIER_TABLE; + } + + /* + * no oppotunity to share, this the resolving ECMP tables are unique + * to this table. + * no need to be a child of the path list, we can do nothing with any + * notifications it would generate [not that it will]. + */ + pli = fib_path_list_create(FIB_PATH_LIST_FLAG_NO_URPF, rpaths); + fib_path_list_lock(pli); + + /* + * constructing the path-list will have created many more BIER tables, + * so this main table will no doubt have re-alloc. + */ + bt = bier_table_get(bti); + bt->bt_pl = pli; + + vec_free(rpaths); + + return (bt); +} + +index_t +bier_table_add_or_lock (const bier_table_id_t *btid, + mpls_label_t local_label) +{ + bier_table_t *bt; + index_t bti; + + bt = bier_table_find(btid); + + if (NULL != bt) { + /* + * modify an existing table. + * change the lfib entry to the new local label + */ + if (bier_table_is_main(bt) && + (local_label != MPLS_LABEL_INVALID)) + { + bier_table_rm_lfib(bt); + + bt->bt_ll = local_label; + bier_table_mk_lfib(bt); + } + bti = bier_table_get_index(bt); + } + else + { + /* + * add a new table + */ + u32 key; + + key = bier_table_mk_key(btid); + + pool_get_aligned(bier_table_pool, bt, CLIB_CACHE_LINE_BYTES); + bier_table_init(bt, btid, local_label); + + hash_set(bier_tables_by_key, key, bier_table_get_index(bt)); + bti = bier_table_get_index(bt); + + if (bier_table_is_main(bt)) + { + bt = bier_table_mk_ecmp(bti); + bier_table_mk_lfib(bt); + } + } + + bier_table_lock_i(bt); + + return (bti); +} + +index_t +bier_table_ecmp_create_and_lock (const bier_table_id_t *btid) +{ + return (bier_table_add_or_lock(btid, MPLS_LABEL_INVALID)); +} + +void +bier_table_ecmp_unlock (index_t bti) +{ + bier_table_unlock_i(bier_table_get(bti)); +} + +static void +bier_table_dpo_lock (dpo_id_t *dpo) +{ +} + +static void +bier_table_dpo_unlock (dpo_id_t *dpo) +{ +} + +static void +bier_table_dpo_mem_show (void) +{ + fib_show_memory_usage("BIER-table", + pool_elts(bier_table_pool), + pool_len(bier_table_pool), + sizeof(bier_table_t)); +} +static u8 * +format_bier_table_dpo (u8 *s, va_list *ap) +{ + index_t bti = va_arg(*ap, index_t); + bier_table_t *bt; + + bt = bier_table_get(bti); + + return (format(s, "[%U]", format_bier_table_id, &bt->bt_id)); +} + +const static dpo_vft_t bier_table_dpo_vft = { + .dv_lock = bier_table_dpo_lock, + .dv_unlock = bier_table_dpo_unlock, + .dv_format = format_bier_table_dpo, + .dv_mem_show = bier_table_dpo_mem_show, +}; + +const static char *const bier_table_mpls_nodes[] = +{ + "bier-input" +}; +const static char * const * const bier_table_nodes[DPO_PROTO_NUM] = +{ + [DPO_PROTO_BIER] = bier_table_mpls_nodes, +}; + +static clib_error_t * +bier_table_module_init (vlib_main_t *vm) +{ + dpo_register(DPO_BIER_TABLE, &bier_table_dpo_vft, bier_table_nodes); + + return (NULL); +} + +VLIB_INIT_FUNCTION (bier_table_module_init); + +const bier_table_id_t * +bier_table_get_id (index_t bti) +{ + bier_table_t *bt; + + bt = bier_table_get(bti); + + return (&bt->bt_id); +} + +static void +bier_table_insert (bier_table_t *bt, + bier_bp_t bp, + index_t bei) +{ + bt->bt_entries[BIER_BP_TO_INDEX(bp)] = bei; +} + +static void +bier_table_remove (bier_table_t *bt, + bier_bp_t bp) +{ + bt->bt_entries[BIER_BP_TO_INDEX(bp)] = INDEX_INVALID; +} + +void +bier_table_route_add (const bier_table_id_t *btid, + bier_bp_t bp, + fib_route_path_t *brps) +{ + index_t bfmi, bti, bei, *bfmip, *bfmis = NULL; + fib_route_path_t *brp; + bier_table_t *bt; + + bt = bier_table_find(btid); + + if (NULL == bt) { + return; + } + + bti = bier_table_get_index(bt); + bei = bier_table_lookup(bt, bp); + + /* + * set the FIB index in the path to the BIER table index + */ + vec_foreach(brp, brps) + { + bier_fmask_id_t fmid = { + .bfmi_nh = brp->frp_addr, + .bfmi_hdr_type = BIER_HDR_O_MPLS, + }; + bfmi = bier_fmask_db_find_or_create_and_lock(bier_table_get_index(bt), + &fmid, + brp); + + brp->frp_bier_fib_index = bti; + vec_add1(bfmis, bfmi); + } + + if (INDEX_INVALID == bei) + { + bei = bier_entry_create(bti, bp); + bier_table_insert(bt, bp, bei); + } + bier_entry_path_add(bei, brps); + + vec_foreach(bfmip, bfmis) + { + bier_fmask_unlock(*bfmip); + } + vec_free(bfmis); +} + +void +bier_table_route_remove (const bier_table_id_t *bti, + bier_bp_t bp, + fib_route_path_t *brps) +{ + fib_route_path_t *brp = NULL; + bier_table_t *bt; + index_t bei; + + bt = bier_table_find(bti); + + if (NULL == bt) { + return; + } + + bei = bier_table_lookup(bt, bp); + + if (INDEX_INVALID == bei) + { + /* no such entry */ + return; + } + + vec_foreach(brp, brps) + { + brp->frp_bier_fib_index = bier_table_get_index(bt); + } + + if (0 == bier_entry_path_remove(bei, brps)) + { + /* 0 remaining paths */ + bier_table_remove(bt, bp); + bier_entry_delete(bei); + } +} + +void +bier_table_contribute_forwarding (index_t bti, + dpo_id_t *dpo) +{ + bier_table_t *bt; + + bt = bier_table_get(bti); + + if (BIER_ECMP_TABLE_ID_MAIN == bt->bt_id.bti_ecmp) + { + /* + * return the load-balance for the ECMP tables + */ + fib_path_list_contribute_forwarding(bt->bt_pl, + FIB_FORW_CHAIN_TYPE_BIER, + dpo); + } + else + { + dpo_set(dpo, DPO_BIER_TABLE, DPO_PROTO_BIER, bti); + } +} + +typedef struct bier_table_ecmp_walk_ctx_t_ +{ + bier_table_ecmp_walk_fn_t fn; + void *ctx; +} bier_table_ecmp_walk_ctx_t; + +static fib_path_list_walk_rc_t +bier_table_ecmp_walk_path_list (fib_node_index_t pl_index, + fib_node_index_t path_index, + void *arg) +{ + bier_table_ecmp_walk_ctx_t *ctx = arg; + + ctx->fn(fib_path_get_resolving_index(path_index), ctx->ctx); + /* continue */ + return (FIB_PATH_LIST_WALK_CONTINUE); +} + +void +bier_table_ecmp_walk (index_t bti, + bier_table_ecmp_walk_fn_t fn, + void *ctx) +{ + bier_table_ecmp_walk_ctx_t ewc = { + .fn = fn, + .ctx = ctx, + }; + bier_table_t *bt; + + bt = bier_table_get(bti); + + fib_path_list_walk(bt->bt_pl, + bier_table_ecmp_walk_path_list, + &ewc); +} + +void +bier_table_ecmp_set_fmask (index_t bti, + bier_bp_t bp, + index_t bfmi) +{ + bier_table_t *bt; + + bt = bier_table_get(bti); + + /* + * we hold a lock for fmasks in the table + */ + bier_fmask_lock(bfmi); + bier_fmask_unlock(bt->bt_fmasks[BIER_BP_TO_INDEX(bp)]); + + bt->bt_fmasks[BIER_BP_TO_INDEX(bp)] = bfmi; +} + +u8 * +format_bier_table_entry (u8 *s, va_list *ap) +{ + index_t bti = va_arg(*ap, index_t); + bier_bp_t bp = va_arg(*ap, bier_bp_t); + bier_table_t *bt; + bt = bier_table_get(bti); + + if (bier_table_is_main(bt)) + { + index_t bei; + + bei = bier_table_lookup(bier_table_get(bti), bp); + + if (INDEX_INVALID != bei) + { + s = format(s, "%U", format_bier_entry, bei, + BIER_SHOW_DETAIL); + } + } + else + { + index_t bfmi; + + bfmi = bier_table_fwd_lookup(bier_table_get(bti), bp); + + if (INDEX_INVALID != bfmi) + { + s = format(s, "%U", format_bier_fmask, bfmi, + BIER_SHOW_DETAIL); + } + } + return (s); +} + +u8 * +format_bier_table (u8 *s, va_list *ap) +{ + index_t bti = va_arg(*ap, index_t); + bier_show_flags_t flags = va_arg(*ap, bier_show_flags_t); + bier_table_t *bt; + + if (pool_is_free_index(bier_table_pool, bti)) + { + return (format(s, "No BIER f-mask %d", bti)); + } + + bt = bier_table_get(bti); + + s = format(s, "[@%d] bier-table:[%U local-label:%U]", + bti, + format_bier_table_id, &bt->bt_id, + format_mpls_unicast_label, bt->bt_ll); + + if (flags & BIER_SHOW_DETAIL) + { + s = format(s, " locks:%d", bt->bt_locks); + } + s = format(s, "]"); + + if (flags & BIER_SHOW_DETAIL) + { + if (bier_table_is_main(bt)) + { + index_t *bei; + + vec_foreach (bei, bt->bt_entries) + { + if (INDEX_INVALID != *bei) + { + s = format(s, "\n%U", format_bier_entry, *bei, 2); + } + } + } + else + { + u32 ii; + + vec_foreach_index (ii, bt->bt_fmasks) + { + if (INDEX_INVALID != bt->bt_fmasks[ii]) + { + s = format(s, "\n bp:%d\n %U", ii, + format_bier_fmask, bt->bt_fmasks[ii], 2); + } + } + } + } + + return (s); +} + +void +bier_table_show_all (vlib_main_t * vm, + bier_show_flags_t flags) +{ + if (!pool_elts(bier_table_pool)) + { + vlib_cli_output (vm, "No BIER tables"); + } + else + { + int ii; + + pool_foreach_index(ii, bier_table_pool, + ({ + vlib_cli_output (vm, "%U", format_bier_table, ii, flags); + })); + } +} + +void +bier_tables_walk (bier_tables_walk_fn_t fn, + void *ctx) +{ + ASSERT(0); +} + + +void +bier_table_walk (const bier_table_id_t *bti, + bier_table_walk_fn_t fn, + void *ctx) +{ + bier_table_t *bt; + bier_entry_t *be; + index_t *bei; + + bt = bier_table_find(bti); + + if (NULL == bt) + { + return; + } + + vec_foreach (bei, bt->bt_entries) + { + if (INDEX_INVALID != *bei) + { + be = bier_entry_get(*bei); + + fn(bt, be, ctx); + } + } +} diff --git a/src/vnet/bier/bier_table.h b/src/vnet/bier/bier_table.h new file mode 100644 index 00000000000..a22e2e335b2 --- /dev/null +++ b/src/vnet/bier/bier_table.h @@ -0,0 +1,172 @@ +/* + * 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. + */ + +#ifndef __BIER_TABLE_H__ +#define __BIER_TABLE_H__ + +#include <vlib/vlib.h> +#include <vnet/fib/fib_types.h> +#include <vnet/bier/bier_types.h> +#include <vnet/bier/bier_entry.h> + +#include <vnet/dpo/dpo.h> + +/** + * Forward declarations + */ +struct bier_route_update_t_; + +/** + * A BIER Table is the bit-indexed forwarding table. + * Each entry (bit-position) represents one destination, and its reachability + * + * The number of entries in a table is thus the maximum supported + * bit-position. Since this is smal <4096, the table is a flat arry + */ +typedef struct bier_table_t_ { + /** + * Save the MPLS local label associated with the table + */ + mpls_label_t bt_ll; + + /** + * The path-list used for the ECMP-tables + */ + fib_node_index_t bt_pl; + + /** + * The index of the lfib entry created for this table. + * Only the EOS is required. + */ + fib_node_index_t bt_lfei; + + /** + * Number of locks on the table + */ + u16 bt_locks; + + /** + * Entries in the table + * This is a vector sized to the appropriate number of entries + * given the table's supported Bit-string length + */ + index_t *bt_entries; + + /** + * Everything before this declaration is unused in the switch path + */ + CLIB_CACHE_LINE_ALIGN_MARK(cacheline0); + + /** + * The identity/key or the table. we need the hdr_len in the data-path + */ + bier_table_id_t bt_id; + + /** + * f-masks in the ECMP table + * This is a vector sized to the appropriate number of entries + * given the table's supported Bit-string length. + * In the ECMP table the LB choice has been pre-resolved, so each entry + * links to just one f-mask, i.e. there is a 1:1 mapping of bit-position to + * fmask. For efficient forwarding we collapse the fmasks up to the table. + */ + index_t *bt_fmasks; +} bier_table_t; + +STATIC_ASSERT((sizeof(bier_table_t) <= 2*CLIB_CACHE_LINE_BYTES), + "BIER table fits on 2 cache lines"); + +extern index_t bier_table_add_or_lock(const bier_table_id_t *id, + mpls_label_t ll); +extern void bier_table_unlock(const bier_table_id_t *id); + +extern void bier_table_route_add(const bier_table_id_t *bti, + bier_bp_t bp, + fib_route_path_t *brp); +extern void bier_table_route_remove(const bier_table_id_t *bti, + bier_bp_t bp, + fib_route_path_t *brp); + +extern void bier_table_show_all(vlib_main_t * vm, + bier_show_flags_t flags); + +extern const bier_table_id_t *bier_table_get_id(index_t bti); + +extern u8 *format_bier_table (u8 *s, va_list *args); +extern u8 *format_bier_table_entry (u8 *s, va_list *args); + +extern index_t bier_table_ecmp_create_and_lock(const bier_table_id_t *id); +extern void bier_table_ecmp_unlock(index_t bti); +extern void bier_table_ecmp_set_fmask(index_t bti, + bier_bp_t bp, + index_t bfmi); + +extern void bier_table_contribute_forwarding(index_t bti, + dpo_id_t *dpo); + +/** + * Types and functions to walk the ECMP tables of a main table + */ +typedef void (*bier_table_ecmp_walk_fn_t)(index_t btei, + void *ctx); +extern void bier_table_ecmp_walk(index_t bti, + bier_table_ecmp_walk_fn_t fn, + void *ctx); +extern int bier_table_is_main (const bier_table_t *bt); + +/** + * Types and functions to walk all the BIER Tables + */ +typedef void (*bier_tables_walk_fn_t)(const bier_table_t *bt, + void *ctx); +extern void bier_tables_walk(bier_tables_walk_fn_t fn, + void *ctx); + +/** + * Types and functions to walk all the entries in one BIER Table + */ +typedef void (*bier_table_walk_fn_t)(const bier_table_t *bt, + const bier_entry_t *be, + void *ctx); +extern void bier_table_walk(const bier_table_id_t *id, + bier_table_walk_fn_t fn, + void *ctx); + +/* + * provided for fast data plane access. + */ +extern bier_table_t *bier_table_pool; + +static inline bier_table_t * +bier_table_get (index_t bti) +{ + return (pool_elt_at_index(bier_table_pool, bti)); +} + +static inline const index_t +bier_table_lookup (const bier_table_t *bt, + bier_bp_t bp) +{ + return (bt->bt_entries[BIER_BP_TO_INDEX(bp)]); +} + +static inline const index_t +bier_table_fwd_lookup (const bier_table_t *bt, + bier_bp_t bp) +{ + return (bt->bt_fmasks[BIER_BP_TO_INDEX(bp)]); +} + +#endif diff --git a/src/vnet/bier/bier_test.c b/src/vnet/bier/bier_test.c new file mode 100644 index 00000000000..6119410edfa --- /dev/null +++ b/src/vnet/bier/bier_test.c @@ -0,0 +1,951 @@ +/* + * 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 <vnet/mpls/mpls.h> +#include <vnet/bier/bier_table.h> +#include <vnet/bier/bier_entry.h> +#include <vnet/bier/bier_fmask.h> +#include <vnet/bier/bier_bit_string.h> +#include <vnet/bier/bier_imp.h> +#include <vnet/bier/bier_disp_table.h> +#include <vnet/bier/bier_disp_entry.h> +#include <vnet/fib/fib_entry.h> +#include <vnet/fib/fib_table.h> +#include <vnet/fib/mpls_fib.h> +#include <vnet/dpo/load_balance.h> +#include <vnet/dpo/drop_dpo.h> +#include <vnet/dpo/lookup_dpo.h> +#include <vnet/mfib/mfib_table.h> + +#include <vnet/fib/fib_test.h> + +/* + * Add debugs for passing tests + */ +static int bier_test_do_debug; + +#define BIER_TEST_I(_cond, _comment, _args...) \ +({ \ + int _evald = (_cond); \ + if (!(_evald)) { \ + fformat(stderr, "FAIL:%d: " _comment "\n", \ + __LINE__, ##_args); \ + } else { \ + if (bier_test_do_debug) \ + fformat(stderr, "PASS:%d: " _comment "\n", \ + __LINE__, ##_args); \ + } \ + _evald; \ +}) +#define BIER_TEST(_cond, _comment, _args...) \ +{ \ + if (!BIER_TEST_I(_cond, _comment, ##_args)) { \ + return 1; \ + ASSERT(!("FAIL: " _comment)); \ + } \ +} + +/** + * A 'i'm not fussed is this is not efficient' store of test data + */ +typedef struct test_main_t_ { + /** + * HW if indicies + */ + u32 hw_if_indicies[4]; + /** + * HW interfaces + */ + vnet_hw_interface_t * hw[4]; + +} test_main_t; +static test_main_t test_main; + +/* fake ethernet device class, distinct from "fake-ethX" */ +static u8 * format_test_interface_name (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + return format (s, "test-eth%d", dev_instance); +} + +static uword dummy_interface_tx (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + clib_warning ("you shouldn't be here, leaking buffers..."); + return frame->n_vectors; +} + +VNET_DEVICE_CLASS (test_interface_device_class,static) = { + .name = "Test interface", + .format_device_name = format_test_interface_name, + .tx_function = dummy_interface_tx, +}; + +static u8 *hw_address; + +static int +bier_test_mk_intf (u32 ninterfaces) +{ + clib_error_t * error = NULL; + test_main_t *tm = &test_main; + u8 byte; + u32 i; + + ASSERT(ninterfaces <= ARRAY_LEN(tm->hw_if_indicies)); + + for (i=0; i<6; i++) + { + byte = 0xd0+i; + vec_add1(hw_address, byte); + } + + for (i = 0; i < ninterfaces; i++) + { + hw_address[5] = i; + + error = ethernet_register_interface(vnet_get_main(), + test_interface_device_class.index, + i /* instance */, + hw_address, + &tm->hw_if_indicies[i], + /* flag change */ 0); + + BIER_TEST((NULL == error), "ADD interface %d", i); + + tm->hw[i] = vnet_get_hw_interface(vnet_get_main(), + tm->hw_if_indicies[i]); + vec_validate (ip4_main.fib_index_by_sw_if_index, tm->hw[i]->sw_if_index); + vec_validate (ip6_main.fib_index_by_sw_if_index, tm->hw[i]->sw_if_index); + ip4_main.fib_index_by_sw_if_index[tm->hw[i]->sw_if_index] = 0; + ip6_main.fib_index_by_sw_if_index[tm->hw[i]->sw_if_index] = 0; + error = vnet_sw_interface_set_flags(vnet_get_main(), + tm->hw[i]->sw_if_index, + VNET_SW_INTERFACE_FLAG_ADMIN_UP); + BIER_TEST((NULL == error), "UP interface %d", i); + } + /* + * re-eval after the inevitable realloc + */ + for (i = 0; i < ninterfaces; i++) + { + tm->hw[i] = vnet_get_hw_interface(vnet_get_main(), + tm->hw_if_indicies[i]); + } + + return (0); +} + +#define BIER_TEST_LB(_cond, _comment, _args...) \ +{ \ + if (!BIER_TEST_I(_cond, _comment, ##_args)) { \ + return (0); \ + } \ +} + +static int +bier_test_validate_entry (index_t bei, + u16 n_buckets, + ...) +{ + dpo_id_t dpo = DPO_INVALID; + const load_balance_t *lb; + va_list ap; + int res; + + va_start(ap, n_buckets); + + bier_entry_contribute_forwarding(bei, &dpo); + + BIER_TEST_LB((DPO_LOAD_BALANCE == dpo.dpoi_type), + "Entry links to %U", + format_dpo_type, dpo.dpoi_type); + + lb = load_balance_get(dpo.dpoi_index); + res = fib_test_validate_lb_v(lb, n_buckets, &ap); + + dpo_reset(&dpo); + + va_end(ap); + + return (res); +} + +static int +bier_test_mpls_spf (void) +{ + fib_node_index_t lfei, fei, bti; + u32 mpls_fib_index; + test_main_t *tm; + int lb_count; + + lb_count = pool_elts(load_balance_pool); + tm = &test_main; +#define N_BIER_ECMP_TABLES 16 + int ii; + + /* + * Add the BIER Main table + */ + const bier_table_id_t bt_0_0_0_256 = { + .bti_set = 0, + .bti_sub_domain = 0, + .bti_hdr_len = BIER_HDR_LEN_256, + .bti_type = BIER_TABLE_MPLS_SPF, + .bti_ecmp = BIER_ECMP_TABLE_ID_MAIN, + }; + + bti = bier_table_add_or_lock(&bt_0_0_0_256, 1600); + + fib_test_lb_bucket_t l_o_bt[N_BIER_ECMP_TABLES]; + bier_table_id_t bt_ecmp_0_0_0_256 = bt_0_0_0_256; + + for (ii = 0; ii < N_BIER_ECMP_TABLES; ii++) + { + bt_ecmp_0_0_0_256.bti_ecmp = ii; + + l_o_bt[ii].type = FT_LB_BIER_TABLE; + l_o_bt[ii].bier.table = + bier_table_ecmp_create_and_lock(&bt_ecmp_0_0_0_256); + }; + const fib_prefix_t pfx_1600_neos = { + .fp_len = 21, + .fp_proto = FIB_PROTOCOL_MPLS, + .fp_label = 1600, + .fp_eos = MPLS_NON_EOS, + .fp_payload_proto = DPO_PROTO_BIER, + }; + const fib_prefix_t pfx_1600_eos = { + .fp_len = 21, + .fp_proto = FIB_PROTOCOL_MPLS, + .fp_label = 1600, + .fp_eos = MPLS_EOS, + .fp_payload_proto = DPO_PROTO_BIER, + }; + + mpls_fib_index = fib_table_find(FIB_PROTOCOL_MPLS, + MPLS_FIB_DEFAULT_TABLE_ID); + + lfei = fib_table_lookup(mpls_fib_index, &pfx_1600_neos); + BIER_TEST(FIB_NODE_INDEX_INVALID == lfei, "1600/0 is not present"); + + lfei = fib_table_lookup(mpls_fib_index, &pfx_1600_eos); + BIER_TEST(fib_test_validate_entry(lfei, FIB_FORW_CHAIN_TYPE_MPLS_EOS, + 16, + &l_o_bt[0], + &l_o_bt[1], + &l_o_bt[2], + &l_o_bt[3], + &l_o_bt[4], + &l_o_bt[5], + &l_o_bt[6], + &l_o_bt[7], + &l_o_bt[8], + &l_o_bt[9], + &l_o_bt[10], + &l_o_bt[11], + &l_o_bt[12], + &l_o_bt[13], + &l_o_bt[14], + &l_o_bt[15]), + "1600/1 LB stacks on BIER table %d", bti); + + /* + * modify the table's local label - keep the lock count accurate + */ + const fib_prefix_t pfx_1601_eos = { + .fp_len = 21, + .fp_proto = FIB_PROTOCOL_MPLS, + .fp_label = 1601, + .fp_eos = MPLS_EOS, + .fp_payload_proto = DPO_PROTO_BIER, + }; + bti = bier_table_add_or_lock(&bt_0_0_0_256, 1601); + bier_table_unlock(&bt_0_0_0_256); + + lfei = fib_table_lookup(mpls_fib_index, &pfx_1600_eos); + BIER_TEST(FIB_NODE_INDEX_INVALID == lfei, "1600/1 is deleted"); + + lfei = fib_table_lookup(mpls_fib_index, &pfx_1601_eos); + BIER_TEST(fib_test_validate_entry(lfei, FIB_FORW_CHAIN_TYPE_MPLS_EOS, + 16, + &l_o_bt[0], + &l_o_bt[1], + &l_o_bt[2], + &l_o_bt[3], + &l_o_bt[4], + &l_o_bt[5], + &l_o_bt[6], + &l_o_bt[7], + &l_o_bt[8], + &l_o_bt[9], + &l_o_bt[10], + &l_o_bt[11], + &l_o_bt[12], + &l_o_bt[13], + &l_o_bt[14], + &l_o_bt[15]), + "1601/1 LB stacks on BIER table %d", bti); + + /* + * add a route to the table. the via IP route does not exist. + */ + const ip46_address_t nh_1_1_1_1 = { + .ip4 = { + .as_u32 = clib_host_to_net_u32(0x01010101), + }, + }; + fib_route_path_t *paths_1_1_1_1 = NULL; + fib_route_path_t path_1_1_1_1 = { + .frp_addr = nh_1_1_1_1, + .frp_bier_fib_index = bti, + .frp_flags = FIB_ROUTE_PATH_BIER_FMASK, + }; + vec_add1(path_1_1_1_1.frp_label_stack, 500); + vec_add1(paths_1_1_1_1, path_1_1_1_1); + const fib_prefix_t pfx_1_1_1_1_s_32 = { + .fp_addr = nh_1_1_1_1, + .fp_len = 32, + .fp_proto = FIB_PROTOCOL_IP4, + }; + const bier_fmask_id_t bfm_id_1_1_1_1 = { + .bfmi_hdr_type = BIER_HDR_O_MPLS, + .bfmi_nh = nh_1_1_1_1, + }; + index_t bei_1; + + bier_table_route_add(&bt_0_0_0_256, 1, paths_1_1_1_1); + bei_1 = bier_table_lookup(bier_table_get(bti), 1); + + BIER_TEST((INDEX_INVALID != bei_1), "BP:1 present"); + + /* + * the newly created fmask should stack on the non-eos chain + * of the via-fib-entry + */ + dpo_id_t neos_dpo_1_1_1_1 = DPO_INVALID; + bier_fmask_t *bfm_1_1_1_1; + index_t bfmi_1_1_1_1; + + fei = fib_table_lookup_exact_match(0, &pfx_1_1_1_1_s_32); + fib_entry_contribute_forwarding(fei, + FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS, + &neos_dpo_1_1_1_1); + + bfmi_1_1_1_1 = bier_fmask_db_find(bti, &bfm_id_1_1_1_1); + bfm_1_1_1_1 = bier_fmask_get(bfmi_1_1_1_1); + + BIER_TEST(!dpo_cmp(&neos_dpo_1_1_1_1, &bfm_1_1_1_1->bfm_dpo), + "Fmask via 1.1.1.1 stacks on neos from 1.1.1.1/32"); + + /* + * and that n-eos LB at this stage is a drop.. + */ + const fib_test_lb_bucket_t bucket_drop = { + .type = FT_LB_DROP, + }; + BIER_TEST(fib_test_validate_lb(&neos_dpo_1_1_1_1, 1, &bucket_drop), + "1.1.1.1/32 n-eos LB 1 buckets via: DROP"); + + /* + * The BIER entry should stack on the forwarding chain of the fmask + */ + const fib_test_lb_bucket_t dpo_o_bfm_1_1_1_1 = { + .type = FT_LB_BIER_FMASK, + .bier = { + .fmask = bfmi_1_1_1_1, + }, + }; + BIER_TEST(bier_test_validate_entry(bei_1, 1, &bucket_drop), + "BP:1 stacks on bier drop"); + + /* + * give 1.1.1.1/32 a path and hence a interesting n-eos chain + */ + ip46_address_t nh_10_10_10_1 = { + .ip4 = { + .as_u32 = clib_host_to_net_u32(0x0a0a0a01), + }, + }; + adj_index_t ai_mpls_10_10_10_1; + ai_mpls_10_10_10_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4, + VNET_LINK_MPLS, + &nh_10_10_10_1, + tm->hw[0]->sw_if_index); + + fib_test_lb_bucket_t bucket_neos_99_via_10_10_10_1 = { + .type = FT_LB_LABEL_O_ADJ, + .label_o_adj = { + .label = 99, + .eos = MPLS_NON_EOS, + .adj = ai_mpls_10_10_10_1, + .ttl = 255, + }, + }; + mpls_label_t *out_lbl_99 = NULL; + vec_add1(out_lbl_99, 99); + + fei = fib_table_entry_update_one_path(0, + &pfx_1_1_1_1_s_32, + FIB_SOURCE_API, + FIB_ENTRY_FLAG_NONE, + DPO_PROTO_IP4, + &nh_10_10_10_1, + tm->hw[0]->sw_if_index, + ~0, // invalid fib index + 1, + out_lbl_99, + FIB_ROUTE_PATH_FLAG_NONE); + fib_entry_contribute_forwarding(fei, + FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS, + &neos_dpo_1_1_1_1); + BIER_TEST(fib_test_validate_lb(&neos_dpo_1_1_1_1, 1, + &bucket_neos_99_via_10_10_10_1), + "1.1.1.1/32 n-eos LB 1 buckets via: 99 + 10.10.10.1"); + BIER_TEST(!dpo_cmp(&neos_dpo_1_1_1_1, + &bfm_1_1_1_1->bfm_dpo), + "Fmask via 1.1.1.1 stacks on updated non-eos of 1.1.1.1/32"); + BIER_TEST(bier_test_validate_entry(bei_1, 1, &dpo_o_bfm_1_1_1_1), + "BP:1 stacks on fmask 1.1.1.1"); + + /* + * add another path to the via entry. + * this makes the via-entry instantiate a new load-balance with + * 2 buckets. and the back-walk to the BIER entry will need to + * re-stack on it. + */ + ip46_address_t nh_10_10_10_2 = { + .ip4 = { + .as_u32 = clib_host_to_net_u32(0x0a0a0a02), + }, + }; + adj_index_t ai_mpls_10_10_10_2; + + ai_mpls_10_10_10_2 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4, + VNET_LINK_MPLS, + &nh_10_10_10_2, + tm->hw[0]->sw_if_index); + + fib_test_lb_bucket_t bucket_neos_100_via_10_10_10_2 = { + .type = FT_LB_LABEL_O_ADJ, + .label_o_adj = { + .label = 100, + .eos = MPLS_NON_EOS, + .adj = ai_mpls_10_10_10_2, + .ttl = 255, + }, + }; + mpls_label_t *out_lbl_100 = NULL; + vec_add1(out_lbl_100, 100); + + fei = fib_table_entry_path_add(0, + &pfx_1_1_1_1_s_32, + FIB_SOURCE_API, + FIB_ENTRY_FLAG_NONE, + DPO_PROTO_IP4, + &nh_10_10_10_2, + tm->hw[0]->sw_if_index, + ~0, // invalid fib index + 1, + out_lbl_100, + FIB_ROUTE_PATH_FLAG_NONE); + + fib_entry_contribute_forwarding(fei, + FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS, + &neos_dpo_1_1_1_1); + BIER_TEST(fib_test_validate_lb(&neos_dpo_1_1_1_1, 2, + &bucket_neos_99_via_10_10_10_1, + &bucket_neos_100_via_10_10_10_2), + "1.1.1.1/32 n-eos LB 2 buckets " + "via: 99 + 10.10.10.1, " + "via: 100 + 10.10.10.2"); + BIER_TEST(!dpo_cmp(&neos_dpo_1_1_1_1, + &bfm_1_1_1_1->bfm_dpo), + "Fmask via 1.1.1.1 stacks on updated non-eos of 1.1.1.1/32"); + + /* + * add another bier bit-position via the same next-hop + * since its the same next hop, the two bit-positions should link + * to the same fmask + */ + index_t bei_2; + + bier_table_route_add(&bt_0_0_0_256, 2, paths_1_1_1_1); + bei_2 = bier_table_lookup(bier_table_get(bti), 2); + + BIER_TEST(bier_test_validate_entry(bei_2, 1, &dpo_o_bfm_1_1_1_1), + "BP:2 stacks on fmask 1.1.1.1"); + + /* + * now add a bit-position via a different next hop and expect to + * link via a different fmask + */ + const ip46_address_t nh_1_1_1_2 = { + .ip4 = { + .as_u32 = clib_host_to_net_u32(0x01010102), + }, + }; + const fib_prefix_t pfx_1_1_1_2_s_32 = { + .fp_addr = nh_1_1_1_2, + .fp_len = 32, + .fp_proto = FIB_PROTOCOL_IP4, + }; + fib_route_path_t *paths_1_1_1_2 = NULL, path_1_1_1_2 = { + .frp_addr = nh_1_1_1_2, + .frp_bier_fib_index = bti, + .frp_flags = FIB_ROUTE_PATH_BIER_FMASK, + }; + vec_add1(path_1_1_1_2.frp_label_stack, 501); + vec_add1(paths_1_1_1_2, path_1_1_1_2); + const bier_fmask_id_t bfm_id_1_1_1_2 = { + .bfmi_hdr_type = BIER_HDR_O_MPLS, + .bfmi_nh = nh_1_1_1_2, + }; + index_t bei_3; + + mpls_label_t *out_lbl_101 = NULL; + vec_add1(out_lbl_101, 101); + fei = fib_table_entry_path_add(0, + &pfx_1_1_1_2_s_32, + FIB_SOURCE_API, + FIB_ENTRY_FLAG_NONE, + DPO_PROTO_IP4, + &nh_10_10_10_2, + tm->hw[0]->sw_if_index, + ~0, // invalid fib index + 1, + out_lbl_101, + FIB_ROUTE_PATH_FLAG_NONE); + bier_table_route_add(&bt_0_0_0_256, 3, paths_1_1_1_2); + bei_3 = bier_table_lookup(bier_table_get(bti), 3); + + BIER_TEST((INDEX_INVALID != bei_3), "BP:3 present"); + + /* + * the newly created fmask should stack on the non-eos chain + * of the via-fib-entry + */ + dpo_id_t neos_dpo_1_1_1_2 = DPO_INVALID; + bier_fmask_t *bfm_1_1_1_2; + index_t bfmi_1_1_1_2; + + fei = fib_table_lookup_exact_match(0, &pfx_1_1_1_2_s_32); + fib_entry_contribute_forwarding(fei, + FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS, + &neos_dpo_1_1_1_2); + + bfmi_1_1_1_2 = bier_fmask_db_find(bti, &bfm_id_1_1_1_2); + bfm_1_1_1_2 = bier_fmask_get(bfmi_1_1_1_2); + + BIER_TEST(!dpo_cmp(&neos_dpo_1_1_1_2, + &bfm_1_1_1_2->bfm_dpo), + "Fmask via 1.1.1.2 stacks on non-eos of 1.1.1.2/32"); + + /* + * The BIER entry should stack on the forwarding chain of the fmask + */ + const fib_test_lb_bucket_t dpo_o_bfm_1_1_1_2 = { + .type = FT_LB_BIER_FMASK, + .bier = { + .fmask = bfmi_1_1_1_2, + }, + }; + BIER_TEST(bier_test_validate_entry(bei_3, 1, &dpo_o_bfm_1_1_1_2), + "BP:3 stacks on fmask 1.1.1.2"); + + /* + * Load-balance BP:3 over both next-hops + */ + bier_table_route_add(&bt_0_0_0_256, 3, paths_1_1_1_1); + + BIER_TEST(bier_test_validate_entry(bei_3, 2, + &dpo_o_bfm_1_1_1_1, + &dpo_o_bfm_1_1_1_2), + "BP:3 stacks on fmask 1.1.1.2 & 1.1.1.1"); + + /* + * test that the ECMP choices for BP:3 have been spread over the + * ECMP tables + */ + BIER_TEST((bier_table_fwd_lookup(bier_table_get(l_o_bt[0].bier.table), 3) == + bfmi_1_1_1_1), + "fwd lookup for BP:3 ECMP:0 is 1.1.1.1"); + BIER_TEST((bier_table_fwd_lookup(bier_table_get(l_o_bt[1].bier.table), 3) == + bfmi_1_1_1_2), + "fwd lookup for BP:3 ECMP:1 is 1.1.1.2"); + + /* + * Withdraw one of the via FIB and thus bring down the fmask + * expect the bier0entry forwarding to remove this from the set + */ + fib_table_entry_delete(0, &pfx_1_1_1_2_s_32, FIB_SOURCE_API); + + BIER_TEST(bier_test_validate_entry(bei_3, 1, + &dpo_o_bfm_1_1_1_1), + "BP:3 post 1.1.1.2 removal stacks on fmask 1.1.1.1"); + + BIER_TEST((bier_table_fwd_lookup(bier_table_get(l_o_bt[0].bier.table), 3) == + bfmi_1_1_1_1), + "fwd lookup for BP:3 ECMP:0 is 1.1.1.1"); + BIER_TEST((bier_table_fwd_lookup(bier_table_get(l_o_bt[1].bier.table), 3) == + bfmi_1_1_1_1), + "fwd lookup for BP:3 ECMP:1 is 1.1.1.1"); + + /* + * add the via back + */ + out_lbl_101 = NULL; + vec_add1(out_lbl_101, 101); + fei = fib_table_entry_path_add(0, + &pfx_1_1_1_2_s_32, + FIB_SOURCE_API, + FIB_ENTRY_FLAG_NONE, + DPO_PROTO_IP4, + &nh_10_10_10_2, + tm->hw[0]->sw_if_index, + ~0, // invalid fib index + 1, + out_lbl_101, + FIB_ROUTE_PATH_FLAG_NONE); + /* suspend so the update walk kicks int */ + vlib_process_suspend(vlib_get_main(), 1e-5); + + BIER_TEST(bier_test_validate_entry(bei_3, 2, + &dpo_o_bfm_1_1_1_1, + &dpo_o_bfm_1_1_1_2), + "BP:3 stacks on fmask 1.1.1.2 & 1.1.1.1"); + BIER_TEST((bier_table_fwd_lookup(bier_table_get(l_o_bt[0].bier.table), 3) == + bfmi_1_1_1_1), + "fwd lookup for BP:3 ECMP:0 is 1.1.1.1"); + BIER_TEST((bier_table_fwd_lookup(bier_table_get(l_o_bt[1].bier.table), 3) == + bfmi_1_1_1_2), + "fwd lookup for BP:3 ECMP:1 is 1.1.1.2"); + + /* + * remove the original 1.1.1.2 fmask from BP:3 + */ + bier_table_route_remove(&bt_0_0_0_256, 3, paths_1_1_1_2); + BIER_TEST(bier_test_validate_entry(bei_3, 1, + &dpo_o_bfm_1_1_1_1), + "BP:3 stacks on fmask 1.1.1.1"); + /* + * test that the ECMP choices for BP:3 have been updated + */ + BIER_TEST((bier_table_fwd_lookup(bier_table_get(l_o_bt[0].bier.table), 3) == + bfmi_1_1_1_1), + "fwd lookup for BP:3 ECMP:0 is 1.1.1.1"); + BIER_TEST((bier_table_fwd_lookup(bier_table_get(l_o_bt[1].bier.table), 3) == + bfmi_1_1_1_1), + "fwd lookup for BP:3 ECMP:1 is 1.1.1.1"); + + /* + * remove the routes added + */ + bier_table_route_remove(&bt_0_0_0_256, 2, paths_1_1_1_1); + bier_table_route_remove(&bt_0_0_0_256, 3, paths_1_1_1_2); + bier_table_route_remove(&bt_0_0_0_256, 3, paths_1_1_1_1); + bier_table_route_remove(&bt_0_0_0_256, 1, paths_1_1_1_1); + + + /* + * delete the table + */ + bier_table_unlock(&bt_0_0_0_256); + + /* + * test resources are freed + */ + for (ii = 0; ii < N_BIER_ECMP_TABLES; ii++) + { + bier_table_ecmp_unlock(l_o_bt[ii].bier.table); + }; + BIER_TEST(0 == pool_elts(bier_table_pool), "BIER table pool empty"); + BIER_TEST(0 == pool_elts(bier_fmask_pool), "BIER fmask pool empty"); + BIER_TEST(0 == pool_elts(bier_entry_pool), "BIER entry pool empty"); + + adj_unlock(ai_mpls_10_10_10_1); + adj_unlock(ai_mpls_10_10_10_2); + dpo_reset(&neos_dpo_1_1_1_1); + dpo_reset(&neos_dpo_1_1_1_2); + fib_table_entry_delete(0, &pfx_1_1_1_1_s_32, FIB_SOURCE_API); + fib_table_entry_delete(0, &pfx_1_1_1_2_s_32, FIB_SOURCE_API); + + /* +1 to account for the one time alloc'd drop LB in the MPLS fibs */ + BIER_TEST(lb_count+1 == pool_elts(load_balance_pool), + "Load-balance resources freed "); + BIER_TEST((0 == adj_nbr_db_size()), "ADJ DB size is %d", + adj_nbr_db_size()); + + vec_free(paths_1_1_1_1); + vec_free(paths_1_1_1_2); + + return (0); +} + +static int +bier_test_mpls_imp (void) +{ + fib_node_index_t bii; + /* test_main_t *tm; */ + + /* tm = &test_main; */ + + /* + * Add the BIER Main table + */ + const bier_table_id_t bt_0_0_0_256 = { + .bti_set = 0, + .bti_sub_domain = 0, + .bti_hdr_len = BIER_HDR_LEN_256, + .bti_type = BIER_TABLE_MPLS_SPF, + .bti_ecmp = BIER_ECMP_TABLE_ID_MAIN, + }; + + bier_table_add_or_lock(&bt_0_0_0_256, 1600); + + /* + * A bit-string for imp 1. + */ + bier_bit_string_t bbs_256; + u8 buckets[BIER_HDR_BUCKETS_256]; + memset(buckets, 0x5, BIER_HDR_BUCKETS_256); + + bier_bit_string_init(&bbs_256, BIER_HDR_LEN_256, buckets); + + bii = bier_imp_add_or_lock(&bt_0_0_0_256, 1, &bbs_256); + + /* + * An mfib entry that resolves via the BIER imposition + */ + const mfib_prefix_t pfx_1_1_1_1_c_239_1_1_1 = { + .fp_len = 64, + .fp_proto = FIB_PROTOCOL_IP4, + .fp_grp_addr = { + .ip4.as_u32 = clib_host_to_net_u32(0xef010101), + }, + .fp_src_addr = { + .ip4.as_u32 = clib_host_to_net_u32(0x01010101), + }, + }; + fib_route_path_t path_via_bier_imp_1 = { + .frp_proto = DPO_PROTO_BIER, + .frp_bier_imp = bii, + .frp_weight = 0, + .frp_flags = FIB_ROUTE_PATH_BIER_IMP, + }; + mfib_table_entry_path_update(0, // default table + &pfx_1_1_1_1_c_239_1_1_1 , + MFIB_SOURCE_API, + &path_via_bier_imp_1, + MFIB_ITF_FLAG_FORWARD); + mfib_table_entry_delete(0, + &pfx_1_1_1_1_c_239_1_1_1 , + MFIB_SOURCE_API); + + bier_imp_unlock(bii); + bier_table_unlock(&bt_0_0_0_256); + + BIER_TEST(0 == pool_elts(bier_imp_pool), + "BIER imposition resources freed "); + BIER_TEST(0 == pool_elts(bier_table_pool), + "BIER table resources freed "); + + return (0); +} + +static int +bier_test_mpls_disp (void) +{ + /* test_main_t *tm; */ + + /* tm = &test_main; */ + + /* + * Add the BIER Main table + */ + const bier_table_id_t bt_0_0_0_256 = { + .bti_set = 0, + .bti_sub_domain = 0, + .bti_hdr_len = BIER_HDR_LEN_256, + .bti_type = BIER_TABLE_MPLS_SPF, + .bti_ecmp = BIER_ECMP_TABLE_ID_MAIN, + }; + index_t bti; + + bti = bier_table_add_or_lock(&bt_0_0_0_256, 1600); + + /* + * Add a BIER dispoition table + */ + const u32 bier_disp_tbl_id = 1; + index_t bdti1; + + bdti1 = bier_disp_table_add_or_lock(bier_disp_tbl_id); + + /* + * add a bit-poistion in the table that resolves via + * DISP table, i.e. a for-us bit-position + */ + fib_route_path_t *paths_via_disp = NULL, path_via_disp = { + // .frp_addr = all-zeros + .frp_bier_fib_index = bdti1, + .frp_flags = FIB_ROUTE_PATH_BIER_FMASK, + }; + vec_add1(paths_via_disp, path_via_disp); + + bier_table_route_add(&bt_0_0_0_256, 3, paths_via_disp); + + /* + * the fmask should stack on the BIER disp table + */ + const bier_fmask_id_t bfm_id_0_0_0_0 = { + .bfmi_hdr_type = BIER_HDR_O_MPLS, + }; + bier_fmask_t *bfm_0_0_0_0; + index_t bfmi_0_0_0_0; + dpo_id_t dpo_disp_tbl_1 = DPO_INVALID; + + bier_disp_table_contribute_forwarding(bdti1, &dpo_disp_tbl_1); + + bfmi_0_0_0_0 = bier_fmask_db_find(bti, &bfm_id_0_0_0_0); + bfm_0_0_0_0 = bier_fmask_get(bfmi_0_0_0_0); + + BIER_TEST(!dpo_cmp(&dpo_disp_tbl_1, &bfm_0_0_0_0->bfm_dpo), + "Fmask via 0.0.0.0 stacks on BIER disp table 1"); + + /* + * and a deag entry into the disposition table + */ + fib_route_path_t *rpaths = NULL, path_via_mfib = { + .frp_proto = DPO_PROTO_IP4, + .frp_addr = zero_addr, + .frp_fib_index = 0, // default MFIB table + .frp_rpf_id = 9, // some non-zero value + .frp_flags = FIB_ROUTE_PATH_RPF_ID, + }; + u16 src = 99; + vec_add1(rpaths, path_via_mfib); + bier_disp_table_entry_path_add(bier_disp_tbl_id, src, + BIER_HDR_PROTO_IPV4, rpaths); + + /* which should stack on a lookup in the mfib table */ + const dpo_id_t *dpo_disp_entry_lb; + const dpo_id_t *dpo_disp_entry_v4; + bier_disp_entry_t *bde_99; + index_t bdei; + + bdei = bier_disp_table_lookup(bdti1, clib_host_to_net_u16(src)); + bde_99 = bier_disp_entry_get(bdei); + dpo_disp_entry_lb = &bde_99->bde_fwd[BIER_HDR_PROTO_IPV4].bde_dpo; + + BIER_TEST(dpo_disp_entry_lb->dpoi_type == DPO_LOAD_BALANCE, + "BIER Disp entry stacks on LB"); + + load_balance_t *lb; + lb = load_balance_get(dpo_disp_entry_lb->dpoi_index); + dpo_disp_entry_v4 = load_balance_get_bucket_i(lb, 0); + + lookup_dpo_t *lkd = lookup_dpo_get(dpo_disp_entry_v4->dpoi_index); + + BIER_TEST((bdti1 == lkd->lkd_fib_index), + "disp is deag in %d %U", + lkd->lkd_fib_index, + format_dpo_id, dpo_disp_entry_v4, 0); + BIER_TEST((LOOKUP_INPUT_DST_ADDR == lkd->lkd_input), + "disp is destination deag in %d %U", + lkd->lkd_input, + format_dpo_id, dpo_disp_entry_v4, 0); + BIER_TEST((LOOKUP_MULTICAST == lkd->lkd_cast), + "disp is multicast deag in %d %U", + lkd->lkd_input, + format_dpo_id, dpo_disp_entry_v4, 0); + + /* + * cleanup + */ + dpo_reset(&dpo_disp_tbl_1); + + bier_disp_table_entry_path_remove(bier_disp_tbl_id, src, + BIER_HDR_PROTO_IPV4, rpaths); + bier_table_route_remove(&bt_0_0_0_256, 3, paths_via_disp); + + bier_disp_table_unlock_w_table_id(bier_disp_tbl_id); + + bier_table_unlock(&bt_0_0_0_256); + + BIER_TEST(0 == pool_elts(bier_fmask_pool), + "BIER fmask resources freed "); + BIER_TEST(0 == pool_elts(bier_table_pool), + "BIER table resources freed "); + BIER_TEST(0 == pool_elts(bier_disp_table_pool), + "BIER Disposition table resources freed "); + BIER_TEST(0 == pool_elts(bier_disp_entry_pool), + "BIER Disposition entry resources freed "); + + vec_free(paths_via_disp); + return (0); +} + +static clib_error_t * +bier_test (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd_arg) +{ + int res = 0; + + res += bier_test_mk_intf(4); + + if (unformat (input, "debug")) + { + bier_test_do_debug = 1; + } + + if (unformat (input, "mid")) + res += bier_test_mpls_spf(); + else if (unformat (input, "head")) + res += bier_test_mpls_imp(); + else if (unformat (input, "tail")) + res += bier_test_mpls_disp(); + else + { + res += bier_test_mpls_spf(); + res += bier_test_mpls_imp(); + res += bier_test_mpls_disp(); + } + + if (res) + { + return clib_error_return(0, "BIER Unit Test Failed"); + } + else + { + return (NULL); + } +} + +VLIB_CLI_COMMAND (test_route_command, static) = { + .path = "test bier", + .short_help = "bier unit tests", + .function = bier_test, +}; + +clib_error_t * +bier_test_init (vlib_main_t *vm) +{ + return 0; +} + +VLIB_INIT_FUNCTION (bier_test_init); diff --git a/src/vnet/bier/bier_types.c b/src/vnet/bier/bier_types.c new file mode 100644 index 00000000000..0d27524aeb1 --- /dev/null +++ b/src/vnet/bier/bier_types.c @@ -0,0 +1,190 @@ +/* + * 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 <vppinfra/types.h> +#include <vnet/bier/bier_types.h> +#include <vnet/bier/bier_hdr_inlines.h> + +/* + * enum to string conversions + */ +const static char* const bier_hdr_len_id_names[] = BIER_HDR_LEN_IDS; +const static char* const bier_hdr_proto_names[] = BIER_HDR_PROTO_ID_NAMES; + +const static u16 bier_hdr_len_num_buckets[] = { + [BIER_HDR_LEN_INVALID] = 0, + [BIER_HDR_LEN_64] = 8, + [BIER_HDR_LEN_128] = 16, + [BIER_HDR_LEN_256] = 32, + [BIER_HDR_LEN_512] = 64, + [BIER_HDR_LEN_1024] = 128, + [BIER_HDR_LEN_2048] = 256, + [BIER_HDR_LEN_4096] = 512, +}; + +const static u16 bier_hdr_len_num_bits[] = { + [BIER_HDR_LEN_INVALID] = 0, + [BIER_HDR_LEN_64] = 64, + [BIER_HDR_LEN_128] = 128, + [BIER_HDR_LEN_256] = 256, + [BIER_HDR_LEN_512] = 512, + [BIER_HDR_LEN_1024] = 1024, + [BIER_HDR_LEN_2048] = 2048, + [BIER_HDR_LEN_4096] = 4096, +}; + +const static u16 bier_hdr_len_prefix_len[] = { + [BIER_HDR_LEN_INVALID] = 0, + [BIER_HDR_LEN_64] = 7, + [BIER_HDR_LEN_128] = 8, + [BIER_HDR_LEN_256] = 9, + [BIER_HDR_LEN_512] = 10, + [BIER_HDR_LEN_1024] = 11, + [BIER_HDR_LEN_2048] = 12, + [BIER_HDR_LEN_4096] = 13, +}; + +uint32_t +bier_hdr_len_id_to_num_buckets (bier_hdr_len_id_t id) +{ + return (bier_hdr_len_num_buckets[id]); +} + +uint32_t +bier_hdr_len_id_to_num_bytes (bier_hdr_len_id_t id) +{ + return (bier_hdr_len_id_to_num_buckets(id)); +} + +uint32_t +bier_hdr_len_id_to_max_bucket (bier_hdr_len_id_t id) +{ + return (bier_hdr_len_id_to_num_buckets(id) - 1); +} + +uint32_t +bier_hdr_len_id_to_num_bits (bier_hdr_len_id_t id) +{ + return (bier_hdr_len_num_bits[id]); +} + +uint32_t +bier_hdr_len_id_to_max_bit (bier_hdr_len_id_t id) +{ + return (bier_hdr_len_id_to_num_bits(id)); +} + +uint32_t +bier_hdr_len_id_to_prefix_len (bier_hdr_len_id_t id) +{ + return (bier_hdr_len_prefix_len[id]); +} + +u8 * +format_bier_hdr_len_id (u8 *s, va_list *ap) +{ + bier_hdr_len_id_t hli = va_arg(*ap, int); // int promotion of bier_hdr_len_id_t + + return (format(s, "%s", bier_hdr_len_id_names[hli])); +} + +u8 * +format_bier_hdr_proto (u8 *s, va_list *ap) +{ + bier_hdr_proto_id_t pi = va_arg(*ap, int); + + return (format(s, "%s", bier_hdr_proto_names[pi])); +} + +int +bier_table_id_cmp (const bier_table_id_t *btid1, + const bier_table_id_t *btid2) +{ + int res; + + res = (btid1->bti_set - btid2->bti_set); + + if (0 == res) + { + res = (btid1->bti_sub_domain - btid2->bti_sub_domain); + } + if (0 == res) + { + res = (btid1->bti_ecmp - btid2->bti_ecmp); + } + if (0 == res) + { + res = (btid1->bti_hdr_len - btid2->bti_hdr_len); + } + if (0 == res) + { + res = (btid1->bti_type - btid2->bti_type); + } + return (res); +} + +dpo_proto_t +bier_hdr_proto_to_dpo (bier_hdr_proto_id_t bproto) +{ + switch (bproto) + { + case BIER_HDR_PROTO_INVALID: + case BIER_HDR_PROTO_CTRL: + case BIER_HDR_PROTO_OAM: + ASSERT(0); + break; + case BIER_HDR_PROTO_MPLS_DOWN_STREAM: + case BIER_HDR_PROTO_MPLS_UP_STREAM: + return (DPO_PROTO_MPLS); + case BIER_HDR_PROTO_ETHERNET: + case BIER_HDR_PROTO_VXLAN: + return (DPO_PROTO_ETHERNET); + case BIER_HDR_PROTO_IPV4: + return (DPO_PROTO_IP4); + case BIER_HDR_PROTO_IPV6: + return (DPO_PROTO_IP4); + } + + return (DPO_PROTO_NUM); +} + +u8 * +format_bier_table_id (u8 *s, va_list *ap) +{ + bier_table_id_t *btid = va_arg(*ap, bier_table_id_t *); + + return (format(s, "sub-domain:%d set:%d ecmp:%d bsl:%U", + btid->bti_sub_domain, + btid->bti_set, + btid->bti_ecmp, + format_bier_hdr_len_id, btid->bti_hdr_len)); +} + +u8 * +format_bier_hdr (u8 *s, va_list *ap) +{ + bier_hdr_t *bh = va_arg(*ap, bier_hdr_t *); + bier_hdr_t copy = *bh; + + bier_hdr_ntoh(©); + + return (format(s, "nibble:%d version:%d hdr-len:%U entropy:%d proto:%U src:%d", + bier_hdr_get_1st_nibble(©), + bier_hdr_get_version(©), + format_bier_hdr_len_id, bier_hdr_get_len_id(©), + bier_hdr_get_entropy(©), + format_bier_hdr_proto, bier_hdr_get_proto_id(©), + bier_hdr_get_src_id(©))); +} diff --git a/src/vnet/bier/bier_types.h b/src/vnet/bier/bier_types.h new file mode 100644 index 00000000000..fa1cd423278 --- /dev/null +++ b/src/vnet/bier/bier_types.h @@ -0,0 +1,507 @@ +/* + * 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. + */ + +#ifndef __BIER_TYPES_H__ +#define __BIER_TYPES_H__ + +#include <vlib/vlib.h> +#include <vnet/dpo/dpo.h> + +/** + * @brief Flags to control show output + */ +typedef enum bier_show_flags_t_ { + BIER_SHOW_BRIEF, + BIER_SHOW_DETAIL = (1 << 0), +} bier_show_flags_t; + +/** + * Types of BIER tables + */ +typedef enum bier_table_type_t_ { + /** + * BIER over MPLS with SPF + */ + BIER_TABLE_MPLS_SPF, + + /** + * BIER over MPLS for TE + */ + BIER_TABLE_MPLS_TE, +} __attribute__((packed)) bier_table_type_t; + +#define BIER_TABLE_TYPES { \ + [BIER_TABLE_MPLS_SPF] = "mpls-spf", \ + [BIER_TABLE_MPLS_TE] = "mpls-te", \ +} + +/** + * bier_hdr_len_id_t enumerator + **/ +typedef enum bier_hdr_len_id_t_ { + BIER_HDR_LEN_64 = 0, + BIER_HDR_LEN_128, + BIER_HDR_LEN_256, + BIER_HDR_LEN_512, + BIER_HDR_LEN_1024, + BIER_HDR_LEN_2048, + BIER_HDR_LEN_4096, + BIER_HDR_LEN_INVALID, +} __attribute__((packed)) bier_hdr_len_id_t; + +#define BIER_HDR_LEN_IDS { \ + [BIER_HDR_LEN_INVALID] = "invalid",\ + [BIER_HDR_LEN_64] = "64", \ + [BIER_HDR_LEN_128] = "128", \ + [BIER_HDR_LEN_256] = "256", \ + [BIER_HDR_LEN_512] = "512", \ + [BIER_HDR_LEN_1024] = "1024", \ + [BIER_HDR_LEN_2048] = "2048", \ + [BIER_HDR_LEN_4096] = "4096", \ +} + +#define FOR_EACH_BIER_HDR_LEN(_len) \ + for (_item = BIER_HDR_LEN_64; \ + _item <= BIER_HDR_LEN_4096; \ + _item++) + +/** + * Format the header length field + */ +extern u8 *format_bier_hdr_len_id(u8 *s, va_list *ap); + +/* + * convert from prefix len to hdr ID + */ +static inline bier_hdr_len_id_t +bier_prefix_len_to_hdr_id (u16 prfx_len) { + + switch (prfx_len) { + case 7: + return (BIER_HDR_LEN_64); + case 8: + return (BIER_HDR_LEN_128); + case 9: + return (BIER_HDR_LEN_256); + case 10: + return (BIER_HDR_LEN_512); + case 11: + return (BIER_HDR_LEN_1024); + case 12: + return (BIER_HDR_LEN_2048); + case 13: + return (BIER_HDR_LEN_4096); + default: + break; + } + + return (BIER_HDR_LEN_INVALID); +} + +static inline bier_hdr_len_id_t +bier_hdr_byte_len_to_id (u32 bytes) +{ + switch (bytes) { + case 8: + return (BIER_HDR_LEN_64); + case 16: + return (BIER_HDR_LEN_128); + case 32: + return (BIER_HDR_LEN_256); + case 64: + return (BIER_HDR_LEN_512); + case 128: + return (BIER_HDR_LEN_1024); + case 256: + return (BIER_HDR_LEN_2048); + case 512: + return (BIER_HDR_LEN_4096); + } + + return (BIER_HDR_LEN_INVALID); +} + +static inline bier_hdr_len_id_t +bier_hdr_bit_len_to_id (u32 bytes) +{ + switch (bytes) { + case 64: + return (BIER_HDR_LEN_64); + case 128: + return (BIER_HDR_LEN_128); + case 256: + return (BIER_HDR_LEN_256); + case 512: + return (BIER_HDR_LEN_512); + case 1024: + return (BIER_HDR_LEN_1024); + case 2048: + return (BIER_HDR_LEN_2048); + case 4096: + return (BIER_HDR_LEN_4096); + } + + return (BIER_HDR_LEN_INVALID); +} + +/** + * bier_hdr_len_num_buckets_t enumerator + **/ +typedef enum bier_hdr_len_num_buckets_t_ { + BIER_HDR_BUCKETS_64 = 8, + BIER_HDR_BUCKETS_128 = 16, + BIER_HDR_BUCKETS_256 = 32, + BIER_HDR_BUCKETS_512 = 64, + BIER_HDR_BUCKETS_1024 = 128, + BIER_HDR_BUCKETS_2048 = 256, + BIER_HDR_BUCKETS_4096 = 512, +} bier_hdr_len_num_buckets_t; + +/** + * BIER header protocol payload types + **/ +typedef enum bier_hdr_proto_id_t_ { + BIER_HDR_PROTO_INVALID = 0, + BIER_HDR_PROTO_MPLS_DOWN_STREAM, + BIER_HDR_PROTO_MPLS_UP_STREAM, + BIER_HDR_PROTO_ETHERNET, + BIER_HDR_PROTO_IPV4, + BIER_HDR_PROTO_IPV6, + BIER_HDR_PROTO_VXLAN, + BIER_HDR_PROTO_CTRL, + BIER_HDR_PROTO_OAM, +} __attribute__((packed)) bier_hdr_proto_id_t; + +#define BIER_HDR_N_PROTO (BIER_HDR_PROTO_OAM + 1) + +#define BIER_HDR_PROTO_ID_NAMES { \ + [BIER_HDR_PROTO_INVALID] = "invalid", \ + [BIER_HDR_PROTO_MPLS_DOWN_STREAM] = "mpls-down-stream", \ + [BIER_HDR_PROTO_MPLS_UP_STREAM] = "mpls-up-stream", \ + [BIER_HDR_PROTO_ETHERNET] = "ethernet", \ + [BIER_HDR_PROTO_IPV4] = "ipv4", \ + [BIER_HDR_PROTO_IPV6] = "ipv6", \ + [BIER_HDR_PROTO_VXLAN] = "vxlan", \ + [BIER_HDR_PROTO_CTRL] = "control-plane", \ + [BIER_HDR_PROTO_OAM] = "oam", \ +} + +#define FOR_EACH_BIER_HDR_PROTO(_proto) \ + for (_proto = BIER_HDR_PROTO_MPLS_DOWN_STREAM; \ + _proto <= BIER_HDR_PROTO_OAM; \ + _proto++) + +/** + * Format the header length field + */ +extern u8 *format_bier_hdr_proto(u8 *s, va_list *ap); + +/** + * Convert from BIER next-hop proto to DPO proto + */ +extern dpo_proto_t bier_hdr_proto_to_dpo(bier_hdr_proto_id_t bproto); + +/** + * BIER header versions + **/ +typedef enum bier_hdr_version_t_ { + BIER_HDR_VERSION_1 = 0, +} __attribute__((packed)) bier_hdr_version_t; + +/** + * bier_hdr_code_t enumerator + **/ +typedef enum bier_hdr_code_t_ { + BIER_HDR_CODE_OAM_IPV4 = 0, + BIER_HDR_CODE_OAM_IPV6 = 1, + BIER_HDR_CODE_CTRL_IPV4 = 2, + BIER_HDR_CODE_CTRL_IPV6 = 3, +} __attribute__((packed)) bier_hdr_code_t; + +/** + * bier_hdr_oam_sub_code_t enumerator + */ +typedef enum bier_hdr_oam_sub_code_t_ { + BIER_HDR_SUB_CODE_OAM_PING_REQ = 0, + BIER_HDR_SUB_CODE_OAM_PING_RESP = 1, +} __attribute__((packed)) bier_hdr_oam_sub_code_t; + +/** + * bier_hdr_ctrl_sub_code_t enumerator + */ +typedef enum bier_hdr_ctrl_sub_code_t_ { + BIER_HDR_SUB_CODE_CTRL_MEMBER_REQ = 0, + BIER_HDR_SUB_CODE_CTRL_ATTACHED_NET = 1, +} __attribute__((packed)) bier_hdr_ctrl_sub_code_t; + +/** + * A bucket is a byte. The byte string is thus always in network byte order. + */ +typedef u8 bier_bit_mask_bucket_t; + +/** + * A BIER Bit-String value of length 64 bits. + */ +typedef struct bier_bit_mask_64_t_ { + bier_bit_mask_bucket_t bits[BIER_HDR_BUCKETS_64]; +} bier_bit_mask_64_t; + +/** + * A BIER Bit-String value of length 128 bits. + */ +typedef struct bier_bit_mask_128_t_ { + bier_bit_mask_bucket_t bits[BIER_HDR_BUCKETS_128]; +} bier_bit_mask_128_t; + +/** + * A BIER Bit-String value of length 256 bits. + */ +typedef struct bier_bit_mask_256_t_ { + bier_bit_mask_bucket_t bits[BIER_HDR_BUCKETS_256]; +} bier_bit_mask_256_t; + +/** + * A BIER Bit-String value of length 512 bits. + */ +typedef struct bier_bit_mask_512_t_ { + bier_bit_mask_bucket_t bits[BIER_HDR_BUCKETS_512]; +} bier_bit_mask_512_t; + +/** + * A BIER Bit-String value of length 1024 bits. + */ +typedef struct bier_bit_mask_1024_t_ { + bier_bit_mask_bucket_t bits[BIER_HDR_BUCKETS_1024]; +} bier_bit_mask_1024_t; + +/** + * A BIER Bit-String value of length 2048 bits. + */ +typedef struct bier_bit_mask_2048_t_ { + bier_bit_mask_bucket_t bits[BIER_HDR_BUCKETS_2048]; +} bier_bit_mask_2048_t; + +/** + * A BIER Bit-String value of length 4096 bits. + */ +typedef struct bier_bit_mask_4096_t_ { + bier_bit_mask_bucket_t bits[BIER_HDR_BUCKETS_4096]; +} bier_bit_mask_4096_t; + + +/** + * 256 bits = 32 bytes + */ +#define BIER_BIT_MASK_NUM_BUCKETS 32 +#define BIER_BIT_MASK_MAX_BUCKET (BIER_BIT_MASK_NUM_BUCKETS - 1) + +/** + * number of bits in a bucket + */ +#define BIER_BIT_MASK_BITS_PER_BUCKET 8 + +/** + * Supported bit-posiotn range + */ +#define BIER_BIT_MASK_MIN_POS (1) + +/** + * A Variable length BitString + */ +typedef struct bier_bit_string_t_ { + /** + * The length of the string in BYTES + */ + u16 bbs_len; + + /** + * The buckets in the string + */ + bier_bit_mask_bucket_t *bbs_buckets; +} bier_bit_string_t; + +/** + * A BIER Bit-mask value + * + * The size of this mask represents this platforms BIER capabilities + */ +typedef bier_bit_mask_256_t bier_bit_mask_t; + +/** + * A bit positon + * as assigned to egress PEs + */ +typedef u32 bier_bp_t; + +#define BIER_BP_TO_INDEX(bp) (bp - 1) + +/** + * The maximum BP that can be assigned + */ +#define BIER_BP_MAX 0x10000 + +/** + * An identifier of the sender of BIER packets + * this is the source of the 'tree' - the BFIR + */ +typedef u16 bier_hdr_src_id_t; + +/** + * An entropy value in a BIER header + */ +typedef u32 bier_hdr_entropy_t; + +#define BIER_BP_INVALID 0 + +/** + * A BIER header of variable length + * The encoding follows: + * https://tools.ietf.org/html/draft-ietf-bier-mpls-encapsulation-10 + */ +typedef struct bier_hdr_t_ { + /** + * The first nibble is always set to 0101 + * to ensure that when carried over MPLS, the BIER packet + * is not mistaken for IPv[46]: + * type: bier_hdr_version_t + * + * The second nibble is the version - this is 0: + * type: bier_hdr_version_t + * + * The third nibble is header length ID + * type: bier_hdr_len_id_t + * + * The next 20 bits are entropy + * An entropy value, calculated by the head end, used + * at the head and mid-points for load-balance hash + * type: bier_hdr_entropy_t + */ + u32 bh_first_word; + + /** + * The second word comprises: + * 2 bits of OAM for passive perf measurement + * 2 reserved bits; + * 6 bits of DSCP + * 6 bits for the next-proto field of type; + * bier_hdr_proto_id_t + */ + u16 bh_oam_dscp_proto; + + /** + * The BFR-ID of the sender + */ + u16 bh_bfr_id; + + /** + * The variable length bit-string + */ + bier_bit_mask_bucket_t bh_bit_string[0]; +} bier_hdr_t; + +/** + * Format a BIER header + */ +extern u8 *format_bier_hdr(u8 *s, va_list *ap); + +/** + * The BIER Set ID assigned to a BIER table + */ +typedef u32 bier_table_set_id_t; + +#define BIER_TABLE_SET_INVALID_ID 0xffffffff + +/** + * The BIER Sub-domain ID assigned to a BIER table + */ +typedef u32 bier_table_sub_domain_id_t; + +#define BIER_TABLE_SUB_DOMAIN_INVALID_ID 0xffffffff + +/** + * An ID or instance number of a BIER sub-table + */ +typedef u32 bier_table_ecmp_id_t; + +/** + * Definition of the ID of the BIER main table + */ +#define BIER_ECMP_TABLE_ID_MAIN 0xFFFF + +/** + * The ID of a table + */ +typedef struct bier_table_id_t_ { + /** + * The SET-ID + * The control plane divdies the bit-position space + * into sets in the case the max bit-position is greater + * than the table's bit-string size + */ + bier_table_set_id_t bti_set; + + /** + * The Sub-Domain-ID + * The control plane has the configuration option to specify multiple + * domains or topologies. + */ + bier_table_sub_domain_id_t bti_sub_domain; + + /** + * The SUB/ECMP-ID + * Constructed by FIB to achieve ECMP between BFR-NBRs + */ + bier_table_ecmp_id_t bti_ecmp; + + /** + * The size of the bit string processed by this table. + */ + bier_hdr_len_id_t bti_hdr_len; + + /** + * The type of the table; SPF or TE, MPLS or IPv6 + */ + bier_table_type_t bti_type; +} bier_table_id_t; + +/** + * Format a BIER table ID + */ +extern u8 *format_bier_table_id(u8 *s, va_list *ap); + +/** + * Compare to BIER table IDs for equality + */ +extern int bier_table_id_cmp(const bier_table_id_t *btid1, + const bier_table_id_t *btid2); + +/** + * Conversion functions for the enumerated bit-string length + * values, to bit and bytes + */ +extern u32 bier_hdr_len_id_to_num_buckets(bier_hdr_len_id_t id); +extern u32 bier_hdr_len_id_to_num_bytes(bier_hdr_len_id_t id); +extern u32 bier_hdr_len_id_to_max_bucket(bier_hdr_len_id_t id); +extern u32 bier_hdr_len_id_to_num_bits(bier_hdr_len_id_t id); +extern u32 bier_hdr_len_id_to_max_bit(bier_hdr_len_id_t id); +extern u32 bier_hdr_len_id_to_prefix_len(bier_hdr_len_id_t id); + +#define BIER_OK 0 +#define BIER_ERR_NO_TABLE 1 +#define BIER_ERR_DUPLICATE_TABLE 2 +#define BIER_ERR_PANIC 3 +typedef int bier_rc; + +#endif /* __BIER_TYPES_H__ */ diff --git a/src/vnet/bier/bier_update.c b/src/vnet/bier/bier_update.c new file mode 100644 index 00000000000..ddbdd72f8ee --- /dev/null +++ b/src/vnet/bier/bier_update.c @@ -0,0 +1,182 @@ +/* + * 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 <vnet/vnet.h> +#include <vnet/mpls/mpls.h> + +#include <vnet/bier/bier_table.h> +#include <vnet/bier/bier_types.h> +#include <vnet/bier/bier_update.h> + +clib_error_t * +vnet_bier_table_cmd (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + u32 hdr_len, local_label; + clib_error_t * error = 0; + bier_table_id_t bti = { + .bti_ecmp = BIER_ECMP_TABLE_ID_MAIN, + }; + u32 is_add = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { + if (unformat (input, "del")) { + is_add = 0; + } else if (unformat (input, "add")) { + is_add = 1; + } else if (unformat (input, "sd %d", &bti.bti_sub_domain)) { + } else if (unformat (input, "set %d", &bti.bti_set)) { + } else if (unformat (input, "bsl %d", &hdr_len)) { + } else if (unformat (input, "mpls %d", &local_label)) { + } else { + error = unformat_parse_error (input); + goto done; + } + } + + bti.bti_hdr_len = bier_hdr_bit_len_to_id(hdr_len); + // FIXME + bti.bti_type = BIER_TABLE_MPLS_SPF; + + if (is_add) + { + bier_table_add_or_lock(&bti, local_label); + } + else + { + bier_table_unlock(&bti); + } + +done: + return (error); +} + +VLIB_CLI_COMMAND (bier_table_command) = { + .path = "bier table", + .short_help = "Add/delete BIER Tables", + .function = vnet_bier_table_cmd, +}; + +clib_error_t * +vnet_bier_route_cmd (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + clib_error_t * error = NULL; + fib_route_path_t brp = { + .frp_flags = FIB_ROUTE_PATH_BIER_FMASK, + }; + bier_table_id_t bti = { + }; + mpls_label_t out_label; + bier_bp_t bp; + u32 hdr_len; + u32 add = 1; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { + if (unformat (input, "del")) { + add = 0; + } else if (unformat (input, "sd %d", &bti.bti_sub_domain)) { + } else if (unformat (input, "set %d", &bti.bti_set)) { + } else if (unformat (input, "bsl %d", &hdr_len)) { + } else if (unformat (input, "bp %d", &bp)) { + } else if (unformat (input, "v4-nh %U", + unformat_ip46_address, + &brp.frp_addr, 0)) { + } else if (unformat (input, "mpls %d", &out_label)) { + vec_add1(brp.frp_label_stack, out_label); + } else { + error = unformat_parse_error (input); + goto done; + } + } + + bti.bti_hdr_len = bier_hdr_bit_len_to_id(hdr_len); + // FIXME + bti.bti_type = BIER_TABLE_MPLS_SPF; + + if (add) + { + bier_table_route_add(&bti, bp, &brp); + } + else + { + bier_table_route_remove(&bti, bp, &brp); + } + +done: + return (error); +} + +VLIB_CLI_COMMAND (bier_route_command) = { + .path = "bier route", + .short_help = "Add/delete BIER Routes", + .function = vnet_bier_route_cmd, +}; + +static clib_error_t * +show_bier_fib_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + bier_show_flags_t flags; + index_t bti, bei; + bier_bp_t bp; + + bp = BIER_BP_INVALID; + bti = bei = INDEX_INVALID; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { + if (unformat (input, "%d %d", &bti, &bp)) + { + flags = BIER_SHOW_DETAIL; + } + else if (unformat (input, "%d", &bti)) + { + flags = BIER_SHOW_DETAIL; + } + else + { + break; + } + } + + if (INDEX_INVALID == bti) + { + bier_table_show_all(vm, flags); + } + else + { + if (!pool_is_free_index(bier_table_pool, bti)) + { + if (BIER_BP_INVALID == bp) + { + vlib_cli_output (vm, "%U", format_bier_table, bti, flags); + } + else + { + vlib_cli_output (vm, "%U", format_bier_table_entry, bti, bp); + } + } + } + return (NULL); +} + +VLIB_CLI_COMMAND (show_bier_fib_command, static) = { + .path = "show bier fib", + .short_help = "show bier fib [table-index] [bit-position]", + .function = show_bier_fib_command_fn, +}; diff --git a/src/vnet/bier/bier_update.h b/src/vnet/bier/bier_update.h new file mode 100644 index 00000000000..b4bee9fef72 --- /dev/null +++ b/src/vnet/bier/bier_update.h @@ -0,0 +1,24 @@ +/* + * 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. + */ + +#ifndef __BIER_UPDATE_H__ +#define __BIER_UPDATE_H__ + +#include <vppinfra/types.h> +#include <vnet/bier/bier_types.h> +#include <vnet/ip/ip.h> + + +#endif diff --git a/src/vnet/buffer.h b/src/vnet/buffer.h index 045328816cd..6518fb67016 100644 --- a/src/vnet/buffer.h +++ b/src/vnet/buffer.h @@ -88,6 +88,8 @@ _(ipsec) \ _(map) \ _(map_t) \ _(ip_frag) \ +_(mpls) \ +_(bier) \ _(tcp) /* @@ -165,6 +167,16 @@ typedef struct u8 first; } mpls; + /* + * BIER - the nubmer of bytes in the header. + * the len field inthe header is not authoritative. It's the + * value in the table that counts. + */ + struct + { + u8 n_bytes; + } bier; + /* ip4-in-ip6 softwire termination, only valid there */ struct { diff --git a/src/vnet/dpo/dpo.c b/src/vnet/dpo/dpo.c index 7658132d47a..1ee0727135d 100644 --- a/src/vnet/dpo/dpo.c +++ b/src/vnet/dpo/dpo.c @@ -122,6 +122,7 @@ dpo_proto_to_link (dpo_proto_t dp) case DPO_PROTO_IP4: return (VNET_LINK_IP4); case DPO_PROTO_MPLS: + case DPO_PROTO_BIER: return (VNET_LINK_MPLS); case DPO_PROTO_ETHERNET: return (VNET_LINK_ETHERNET); diff --git a/src/vnet/dpo/dpo.h b/src/vnet/dpo/dpo.h index 304b4331495..27c129ed444 100644 --- a/src/vnet/dpo/dpo.h +++ b/src/vnet/dpo/dpo.h @@ -63,6 +63,7 @@ typedef enum dpo_proto_t_ DPO_PROTO_IP6, DPO_PROTO_MPLS, DPO_PROTO_ETHERNET, + DPO_PROTO_BIER, DPO_PROTO_NSH, } __attribute__((packed)) dpo_proto_t; @@ -75,6 +76,7 @@ typedef enum dpo_proto_t_ [DPO_PROTO_ETHERNET] = "ethernet", \ [DPO_PROTO_MPLS] = "mpls", \ [DPO_PROTO_NSH] = "nsh", \ + [DPO_PROTO_BIER] = "bier", \ } #define FOR_EACH_DPO_PROTO(_proto) \ @@ -116,6 +118,11 @@ typedef enum dpo_type_t_ { DPO_INTERFACE_TX, DPO_L2_BRIDGE, DPO_L3_PROXY, + DPO_BIER_TABLE, + DPO_BIER_FMASK, + DPO_BIER_IMP, + DPO_BIER_DISP_TABLE, + DPO_BIER_DISP_ENTRY, DPO_LAST, } __attribute__((packed)) dpo_type_t; @@ -145,6 +152,11 @@ typedef enum dpo_type_t_ { [DPO_INTERFACE_TX] = "dpo-interface-tx", \ [DPO_L2_BRIDGE] = "dpo-l2-bridge", \ [DPO_L3_PROXY] = "dpo-l3-proxy", \ + [DPO_BIER_TABLE] = "bier-table", \ + [DPO_BIER_FMASK] = "bier-fmask", \ + [DPO_BIER_IMP] = "bier-imposition", \ + [DPO_BIER_DISP_ENTRY] = "bier-disp-entry", \ + [DPO_BIER_DISP_TABLE] = "bier-disp-table", \ } /** diff --git a/src/vnet/dpo/drop_dpo.c b/src/vnet/dpo/drop_dpo.c index a1821dddfcc..6bb3ff7d5e0 100644 --- a/src/vnet/dpo/drop_dpo.c +++ b/src/vnet/dpo/drop_dpo.c @@ -96,6 +96,11 @@ const static char* const drop_nsh_nodes[] = "error-drop", NULL, }; +const static char* const drop_bier_nodes[] = +{ + "bier-drop", + NULL, +}; const static char* const * const drop_nodes[DPO_PROTO_NUM] = { [DPO_PROTO_IP4] = drop_ip4_nodes, @@ -103,6 +108,7 @@ const static char* const * const drop_nodes[DPO_PROTO_NUM] = [DPO_PROTO_MPLS] = drop_mpls_nodes, [DPO_PROTO_ETHERNET] = drop_ethernet_nodes, [DPO_PROTO_NSH] = drop_nsh_nodes, + [DPO_PROTO_BIER] = drop_bier_nodes, }; void diff --git a/src/vnet/dpo/load_balance.c b/src/vnet/dpo/load_balance.c index af054f1c3f4..b48702ae3f2 100644 --- a/src/vnet/dpo/load_balance.c +++ b/src/vnet/dpo/load_balance.c @@ -21,6 +21,7 @@ #include <vnet/adj/adj.h> #include <vnet/adj/adj_internal.h> #include <vnet/fib/fib_urpf_list.h> +#include <vnet/bier/bier_hdr_inlines.h> /* * distribution error tolerance for load-balancing @@ -814,6 +815,10 @@ const static char* const load_balance_l2_nodes[] = const static char* const load_balance_nsh_nodes[] = { "nsh-load-balance", +}; +const static char* const load_balance_bier_nodes[] = +{ + "bier-load-balance", NULL, }; const static char* const * const load_balance_nodes[DPO_PROTO_NUM] = @@ -823,6 +828,7 @@ const static char* const * const load_balance_nodes[DPO_PROTO_NUM] = [DPO_PROTO_MPLS] = load_balance_mpls_nodes, [DPO_PROTO_ETHERNET] = load_balance_l2_nodes, [DPO_PROTO_NSH] = load_balance_nsh_nodes, + [DPO_PROTO_BIER] = load_balance_bier_nodes, }; void @@ -935,10 +941,11 @@ typedef struct load_balance_trace_t_ index_t lb_index; } load_balance_trace_t; -static uword -l2_load_balance (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) +always_inline uword +load_balance_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + int is_l2) { u32 n_left_from, next_index, *from, *to_next; @@ -973,7 +980,16 @@ l2_load_balance (vlib_main_t * vm, lbi0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX]; lb0 = load_balance_get(lbi0); - vnet_buffer(b0)->ip.flow_hash = l2_flow_hash(b0); + if (is_l2) + { + vnet_buffer(b0)->ip.flow_hash = l2_flow_hash(b0); + } + else + { + /* it's BIER */ + const bier_hdr_t *bh0 = vlib_buffer_get_current(b0); + vnet_buffer(b0)->ip.flow_hash = bier_hdr_get_entropy(bh0); + } dpo0 = load_balance_get_bucket_i(lb0, vnet_buffer(b0)->ip.flow_hash & @@ -998,6 +1014,14 @@ l2_load_balance (vlib_main_t * vm, return frame->n_vectors; } +static uword +l2_load_balance (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (load_balance_inline(vm, node, frame, 1)); +} + static u8 * format_l2_load_balance_trace (u8 * s, va_list * args) { @@ -1113,3 +1137,34 @@ VLIB_REGISTER_NODE (nsh_load_balance_node) = { [0] = "error-drop", }, }; + +static u8 * +format_bier_load_balance_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + load_balance_trace_t *t = va_arg (*args, load_balance_trace_t *); + + s = format (s, "BIER-load-balance: index %d", t->lb_index); + return s; +} + +static uword +bier_load_balance (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (load_balance_inline(vm, node, frame, 0)); +} + +/** + * @brief + */ +VLIB_REGISTER_NODE (bier_load_balance_node) = { + .function = bier_load_balance, + .name = "bier-load-balance", + .vector_size = sizeof (u32), + + .format_trace = format_bier_load_balance_trace, + .sibling_of = "mpls-load-balance", +}; diff --git a/src/vnet/dpo/replicate_dpo.c b/src/vnet/dpo/replicate_dpo.c index 9fdb9a05071..ae045f6a431 100644 --- a/src/vnet/dpo/replicate_dpo.c +++ b/src/vnet/dpo/replicate_dpo.c @@ -261,7 +261,7 @@ replicate_fill_buckets (replicate_t *rep, u32 n_buckets) { load_balance_path_t * nh; - u16 ii, bucket; + u16 bucket; bucket = 0; @@ -271,11 +271,8 @@ replicate_fill_buckets (replicate_t *rep, */ vec_foreach (nh, nhs) { - for (ii = 0; ii < nh->path_weight; ii++) - { - ASSERT(bucket < n_buckets); - replicate_set_bucket_i(rep, bucket++, buckets, &nh->path_dpo); - } + ASSERT(bucket < n_buckets); + replicate_set_bucket_i(rep, bucket++, buckets, &nh->path_dpo); } } @@ -694,7 +691,10 @@ replicate_inline (vlib_main_t * vm, if (PREDICT_FALSE(c0->flags & VLIB_BUFFER_IS_TRACED)) { - replicate_trace_t *t = vlib_add_trace (vm, node, c0, sizeof (*t)); + replicate_trace_t *t; + + vlib_trace_buffer (vm, node, next0, c0, 0); + t = vlib_add_trace (vm, node, c0, sizeof (*t)); t->rep_index = repi0; t->dpo = *dpo0; } diff --git a/src/vnet/fib/fib_entry.c b/src/vnet/fib/fib_entry.c index 4c9b1abd881..74c6a4a587b 100644 --- a/src/vnet/fib/fib_entry.c +++ b/src/vnet/fib/fib_entry.c @@ -394,6 +394,7 @@ fib_entry_chain_type_mcast_to_ucast (fib_forward_chain_type_t fct) case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS: case FIB_FORW_CHAIN_TYPE_ETHERNET: case FIB_FORW_CHAIN_TYPE_NSH: + case FIB_FORW_CHAIN_TYPE_BIER: break; } diff --git a/src/vnet/fib/fib_entry.h b/src/vnet/fib/fib_entry.h index 7e4b52acffd..cd2a685b765 100644 --- a/src/vnet/fib/fib_entry.h +++ b/src/vnet/fib/fib_entry.h @@ -61,6 +61,10 @@ typedef enum fib_source_t_ { */ FIB_SOURCE_PLUGIN_HI, /** + * From the BIER subsystem + */ + FIB_SOURCE_BIER, + /** * From the control plane API */ FIB_SOURCE_API, @@ -141,6 +145,7 @@ STATIC_ASSERT (sizeof(fib_source_t) == 1, [FIB_SOURCE_SPECIAL] = "special", \ [FIB_SOURCE_INTERFACE] = "interface", \ [FIB_SOURCE_PROXY] = "proxy", \ + [FIB_SOURCE_BIER] = "BIER", \ [FIB_SOURCE_API] = "API", \ [FIB_SOURCE_CLI] = "CLI", \ [FIB_SOURCE_ADJ] = "adjacency", \ diff --git a/src/vnet/fib/fib_entry_delegate.c b/src/vnet/fib/fib_entry_delegate.c index 41af14f2245..4bf37df0a7e 100644 --- a/src/vnet/fib/fib_entry_delegate.c +++ b/src/vnet/fib/fib_entry_delegate.c @@ -122,6 +122,7 @@ fib_entry_chain_type_to_delegate_type (fib_forward_chain_type_t fct) return (FIB_ENTRY_DELEGATE_CHAIN_ETHERNET); case FIB_FORW_CHAIN_TYPE_MCAST_IP4: case FIB_FORW_CHAIN_TYPE_MCAST_IP6: + case FIB_FORW_CHAIN_TYPE_BIER: break; case FIB_FORW_CHAIN_TYPE_NSH: return (FIB_ENTRY_DELEGATE_CHAIN_NSH); diff --git a/src/vnet/fib/fib_entry_src.c b/src/vnet/fib/fib_entry_src.c index 173df74f0a4..667aa485f7c 100644 --- a/src/vnet/fib/fib_entry_src.c +++ b/src/vnet/fib/fib_entry_src.c @@ -275,6 +275,7 @@ fib_entry_src_get_path_forwarding (fib_node_index_t path_index, case FIB_FORW_CHAIN_TYPE_UNICAST_IP6: case FIB_FORW_CHAIN_TYPE_MCAST_IP4: case FIB_FORW_CHAIN_TYPE_MCAST_IP6: + case FIB_FORW_CHAIN_TYPE_BIER: /* * EOS traffic with no label to stack, we need the IP Adj */ @@ -480,8 +481,8 @@ fib_entry_src_mk_lb (fib_entry_t *fib_entry, } else { + fib_protocol_t flow_hash_proto; flow_hash_config_t fhc; - fib_protocol_t fp; /* * if the protocol for the LB we are building does not match that @@ -489,16 +490,17 @@ fib_entry_src_mk_lb (fib_entry_t *fib_entry, * then the fib_index is not an index that relates to the table * type we need. So get the default flow-hash config instead. */ - fp = dpo_proto_to_fib(lb_proto); - - if (fib_entry->fe_prefix.fp_proto != fp) + flow_hash_proto = dpo_proto_to_fib(lb_proto); + if (fib_entry->fe_prefix.fp_proto != flow_hash_proto) { - fhc = fib_table_get_default_flow_hash_config(fp); + fhc = fib_table_get_default_flow_hash_config(flow_hash_proto); } else { - fhc = fib_table_get_flow_hash_config(fib_entry->fe_fib_index, fp); + fhc = fib_table_get_flow_hash_config(fib_entry->fe_fib_index, + flow_hash_proto); } + dpo_set(dpo_lb, DPO_LOAD_BALANCE, lb_proto, diff --git a/src/vnet/fib/fib_entry_src_special.c b/src/vnet/fib/fib_entry_src_special.c index c3e4fe5eba7..a2493bb5be9 100644 --- a/src/vnet/fib/fib_entry_src_special.c +++ b/src/vnet/fib/fib_entry_src_special.c @@ -68,4 +68,5 @@ fib_entry_src_special_register (void) fib_entry_src_register(FIB_SOURCE_CLASSIFY, &special_src_vft); fib_entry_src_register(FIB_SOURCE_AE, &special_src_vft); fib_entry_src_register(FIB_SOURCE_PROXY, &special_src_vft); + fib_entry_src_register(FIB_SOURCE_BIER, &special_src_vft); } diff --git a/src/vnet/fib/fib_node.h b/src/vnet/fib/fib_node.h index 6d26bdd3671..d4c96c90b77 100644 --- a/src/vnet/fib/fib_node.h +++ b/src/vnet/fib/fib_node.h @@ -45,6 +45,8 @@ typedef enum fib_node_type_t_ { FIB_NODE_TYPE_VXLAN_GPE_TUNNEL, FIB_NODE_TYPE_GENEVE_TUNNEL, FIB_NODE_TYPE_UDP_ENCAP, + FIB_NODE_TYPE_BIER_FMASK, + FIB_NODE_TYPE_BIER_ENTRY, /** * Marker. New types before this one. leave the test last. */ @@ -70,6 +72,8 @@ typedef enum fib_node_type_t_ { [FIB_NODE_TYPE_MAP_E] = "map-e", \ [FIB_NODE_TYPE_VXLAN_GPE_TUNNEL] = "vxlan-gpe-tunnel", \ [FIB_NODE_TYPE_UDP_ENCAP] = "udp-encap", \ + [FIB_NODE_TYPE_BIER_FMASK] = "bier-fmask", \ + [FIB_NODE_TYPE_BIER_ENTRY] = "bier-entry", \ } /** diff --git a/src/vnet/fib/fib_path.c b/src/vnet/fib/fib_path.c index 4fccca80672..3e031929419 100644 --- a/src/vnet/fib/fib_path.c +++ b/src/vnet/fib/fib_path.c @@ -24,6 +24,7 @@ #include <vnet/dpo/interface_rx_dpo.h> #include <vnet/dpo/mpls_disposition.h> #include <vnet/dpo/l2_bridge_dpo.h> +#include <vnet/dpo/drop_dpo.h> #include <vnet/adj/adj.h> #include <vnet/adj/adj_mcast.h> @@ -37,6 +38,9 @@ #include <vnet/fib/fib_urpf_list.h> #include <vnet/fib/mpls_fib.h> #include <vnet/udp/udp_encap.h> +#include <vnet/bier/bier_fmask.h> +#include <vnet/bier/bier_table.h> +#include <vnet/bier/bier_imp.h> /** * Enurmeration of path types @@ -83,9 +87,21 @@ typedef enum fib_path_type_t_ { */ FIB_PATH_TYPE_RECEIVE, /** + * bier-imp. it's via a BIER imposition. + */ + FIB_PATH_TYPE_BIER_IMP, + /** + * bier-fmask. it's via a BIER ECMP-table. + */ + FIB_PATH_TYPE_BIER_TABLE, + /** + * bier-fmask. it's via a BIER f-mask. + */ + FIB_PATH_TYPE_BIER_FMASK, + /** * Marker. Add new types before this one, then update it. */ - FIB_PATH_TYPE_LAST = FIB_PATH_TYPE_RECEIVE, + FIB_PATH_TYPE_LAST = FIB_PATH_TYPE_BIER_FMASK, } __attribute__ ((packed)) fib_path_type_t; /** @@ -103,10 +119,15 @@ typedef enum fib_path_type_t_ { [FIB_PATH_TYPE_INTF_RX] = "intf-rx", \ [FIB_PATH_TYPE_UDP_ENCAP] = "udp-encap", \ [FIB_PATH_TYPE_RECEIVE] = "receive", \ + [FIB_PATH_TYPE_BIER_IMP] = "bier-imp", \ + [FIB_PATH_TYPE_BIER_TABLE] = "bier-table", \ + [FIB_PATH_TYPE_BIER_FMASK] = "bier-fmask", \ } -#define FOR_EACH_FIB_PATH_TYPE(_item) \ - for (_item = FIB_PATH_TYPE_FIRST; _item <= FIB_PATH_TYPE_LAST; _item++) +#define FOR_EACH_FIB_PATH_TYPE(_item) \ + for (_item = FIB_PATH_TYPE_FIRST; \ + _item <= FIB_PATH_TYPE_LAST; \ + _item++) /** * Enurmeration of path operational (i.e. derived) attributes @@ -252,12 +273,42 @@ typedef struct fib_path_t_ { mpls_eos_bit_t fp_eos; }; } fp_nh; - /** - * The FIB table index in which to find the next-hop. - */ - fib_node_index_t fp_tbl_id; + union { + /** + * The FIB table index in which to find the next-hop. + */ + fib_node_index_t fp_tbl_id; + /** + * The BIER FIB the fmask is in + */ + index_t fp_bier_fib; + }; } recursive; struct { + /** + * The next-hop + */ + ip46_address_t fp_nh; + /** + * The BIER FIB the fmask is in + */ + index_t fp_bier_fib; + } bier_fmask; + struct { + /** + * The BIER table's ID + */ + bier_table_id_t fp_bier_tbl; + } bier_table; + struct { + /** + * The BIER imposition object + * this is part of the path's key, since the index_t + * of an imposition object is the object's key. + */ + index_t fp_bier_imp; + } bier_imp; + struct { /** * The FIB index in which to perfom the next lookup */ @@ -311,11 +362,21 @@ typedef struct fib_path_t_ { */ fib_path_oper_flags_t fp_oper_flags; - /** - * the resolving via fib. not part of the union, since it it not part - * of the path's hash. - */ - fib_node_index_t fp_via_fib; + union { + /** + * the resolving via fib. not part of the union, since it it not part + * of the path's hash. + */ + fib_node_index_t fp_via_fib; + /** + * the resolving bier-fmask + */ + index_t fp_via_bier_fmask; + /** + * the resolving bier-table + */ + index_t fp_via_bier_tbl; + }; /** * The Data-path objects through which this path resolves for IP. @@ -494,6 +555,31 @@ format_fib_path (u8 * s, va_list * args) case FIB_PATH_TYPE_UDP_ENCAP: s = format (s, " UDP-encap ID:%d", path->udp_encap.fp_udp_encap_id); break; + case FIB_PATH_TYPE_BIER_TABLE: + s = format (s, "via bier-table:[%U}", + format_bier_table_id, + &path->bier_table.fp_bier_tbl); + s = format (s, " via-dpo:[%U:%d]", + format_dpo_type, path->fp_dpo.dpoi_type, + path->fp_dpo.dpoi_index); + break; + case FIB_PATH_TYPE_BIER_FMASK: + s = format (s, "via %U", + format_ip46_address, + &path->bier_fmask.fp_nh, + IP46_TYPE_ANY); + s = format (s, " in BIER-fib:%d", + path->bier_fmask.fp_bier_fib, + path->fp_via_fib); + s = format (s, " via-fmask:%d", path->fp_via_bier_fmask); + s = format (s, " via-dpo:[%U:%d]", + format_dpo_type, path->fp_dpo.dpoi_type, + path->fp_dpo.dpoi_index); + break; + case FIB_PATH_TYPE_BIER_IMP: + s = format (s, "via %U", format_bier_imp, + path->bier_imp.fp_bier_imp, 0, BIER_SHOW_BRIEF); + break; case FIB_PATH_TYPE_RECEIVE: case FIB_PATH_TYPE_INTF_RX: case FIB_PATH_TYPE_SPECIAL: @@ -742,6 +828,28 @@ fib_path_recursive_adj_update (fib_path_t *path, } /* + * re-evaulate the forwarding state for a via fmask path + */ +static void +fib_path_bier_fmask_update (fib_path_t *path, + dpo_id_t *dpo) +{ + bier_fmask_contribute_forwarding(path->fp_via_bier_fmask, dpo); + + /* + * if we are stakcing on the drop, then the path is not resolved + */ + if (dpo_is_drop(dpo)) + { + path->fp_oper_flags &= ~FIB_PATH_OPER_FLAG_RESOLVED; + } + else + { + path->fp_oper_flags |= FIB_PATH_OPER_FLAG_RESOLVED; + } +} + +/* * fib_path_is_permanent_drop * * Return !0 if the path is configured to permanently drop, @@ -786,6 +894,20 @@ fib_path_unresolve (fib_path_t *path) path->fp_via_fib = FIB_NODE_INDEX_INVALID; } break; + case FIB_PATH_TYPE_BIER_FMASK: + if (FIB_NODE_INDEX_INVALID != path->fp_via_bier_fmask) + { + bier_fmask_child_remove(path->fp_via_bier_fmask, + path->fp_sibling); + path->fp_via_bier_fmask = FIB_NODE_INDEX_INVALID; + } + break; + case FIB_PATH_TYPE_BIER_IMP: + bier_imp_unlock(path->fp_dpo.dpoi_index); + break; + case FIB_PATH_TYPE_BIER_TABLE: + bier_table_ecmp_unlock(path->fp_via_bier_tbl); + break; case FIB_PATH_TYPE_ATTACHED_NEXT_HOP: adj_child_remove(path->fp_dpo.dpoi_index, path->fp_sibling); @@ -890,6 +1012,30 @@ fib_path_back_walk_notify (fib_node_t *node, return (FIB_NODE_BACK_WALK_CONTINUE); } break; + case FIB_PATH_TYPE_BIER_FMASK: + if (FIB_NODE_BW_REASON_FLAG_EVALUATE & ctx->fnbw_reason) + { + /* + * update to use the BIER fmask's new forwading + */ + fib_path_bier_fmask_update(path, &path->fp_dpo); + } + if ((FIB_NODE_BW_REASON_FLAG_ADJ_UPDATE & ctx->fnbw_reason) || + (FIB_NODE_BW_REASON_FLAG_ADJ_DOWN & ctx->fnbw_reason)) + { + /* + * ADJ updates (complete<->incomplete) do not need to propagate to + * recursive entries. + * The only reason its needed as far back as here, is that the adj + * and the incomplete adj are a different DPO type, so the LBs need + * to re-stack. + * If this walk was quashed in the fib_entry, then any non-fib_path + * children (like tunnels that collapse out the LB when they stack) + * would not see the update. + */ + return (FIB_NODE_BACK_WALK_CONTINUE); + } + break; case FIB_PATH_TYPE_ATTACHED_NEXT_HOP: /* FIXME comment @@ -1043,6 +1189,8 @@ FIXME comment case FIB_PATH_TYPE_SPECIAL: case FIB_PATH_TYPE_RECEIVE: case FIB_PATH_TYPE_EXCLUSIVE: + case FIB_PATH_TYPE_BIER_TABLE: + case FIB_PATH_TYPE_BIER_IMP: /* * these path types have no parents. so to be * walked from one is unexpected. @@ -1164,6 +1312,22 @@ fib_path_create (fib_node_index_t pl_index, path->deag.fp_tbl_id = rpath->frp_fib_index; path->deag.fp_rpf_id = rpath->frp_rpf_id; } + else if (rpath->frp_flags & FIB_ROUTE_PATH_BIER_FMASK) + { + path->fp_type = FIB_PATH_TYPE_BIER_FMASK; + path->bier_fmask.fp_nh = rpath->frp_addr; + path->bier_fmask.fp_bier_fib = rpath->frp_bier_fib_index; + } + else if (rpath->frp_flags & FIB_ROUTE_PATH_BIER_IMP) + { + path->fp_type = FIB_PATH_TYPE_BIER_IMP; + path->bier_imp.fp_bier_imp = rpath->frp_bier_imp; + } + else if (rpath->frp_flags & FIB_ROUTE_PATH_BIER_TABLE) + { + path->fp_type = FIB_PATH_TYPE_BIER_TABLE; + path->bier_table.fp_bier_tbl = rpath->frp_bier_tbl; + } else if (~0 != rpath->frp_sw_if_index) { if (ip46_address_is_zero(&rpath->frp_addr)) @@ -1204,7 +1368,7 @@ fib_path_create (fib_node_index_t pl_index, { path->recursive.fp_nh.fp_ip = rpath->frp_addr; } - path->recursive.fp_tbl_id = rpath->frp_fib_index; + path->recursive.fp_tbl_id = rpath->frp_fib_index; } } @@ -1386,6 +1550,24 @@ fib_path_cmp_i (const fib_path_t *path1, res = (path1->recursive.fp_tbl_id - path2->recursive.fp_tbl_id); } break; + case FIB_PATH_TYPE_BIER_FMASK: + res = ip46_address_cmp(&path1->bier_fmask.fp_nh, + &path2->bier_fmask.fp_nh); + + if (0 == res) + { + res = (path1->bier_fmask.fp_bier_fib - + path2->bier_fmask.fp_bier_fib); + } + break; + case FIB_PATH_TYPE_BIER_IMP: + res = (path1->bier_imp.fp_bier_imp - + path2->bier_imp.fp_bier_imp); + break; + case FIB_PATH_TYPE_BIER_TABLE: + res = bier_table_id_cmp(&path1->bier_table.fp_bier_tbl, + &path2->bier_table.fp_bier_tbl); + break; case FIB_PATH_TYPE_DEAG: res = (path1->deag.fp_tbl_id - path2->deag.fp_tbl_id); if (0 == res) @@ -1510,6 +1692,22 @@ fib_path_cmp_w_route_path (fib_node_index_t path_index, res = (path->recursive.fp_tbl_id - rpath->frp_fib_index); } break; + case FIB_PATH_TYPE_BIER_FMASK: + res = ip46_address_cmp(&path->bier_fmask.fp_nh, + &rpath->frp_addr); + + if (0 == res) + { + res = (path->bier_fmask.fp_bier_fib - rpath->frp_bier_fib_index); + } + break; + case FIB_PATH_TYPE_BIER_IMP: + res = (path->bier_imp.fp_bier_imp - rpath->frp_bier_imp); + break; + case FIB_PATH_TYPE_BIER_TABLE: + res = bier_table_id_cmp(&path->bier_table.fp_bier_tbl, + &rpath->frp_bier_tbl); + break; case FIB_PATH_TYPE_INTF_RX: res = (path->intf_rx.fp_interface - rpath->frp_sw_if_index); break; @@ -1623,6 +1821,9 @@ fib_path_recursive_loop_detect (fib_node_index_t path_index, case FIB_PATH_TYPE_INTF_RX: case FIB_PATH_TYPE_UDP_ENCAP: case FIB_PATH_TYPE_EXCLUSIVE: + case FIB_PATH_TYPE_BIER_FMASK: + case FIB_PATH_TYPE_BIER_TABLE: + case FIB_PATH_TYPE_BIER_IMP: /* * these path types cannot be part of a loop, since they are the leaves * of the graph. @@ -1742,6 +1943,53 @@ fib_path_resolve (fib_node_index_t path_index) break; } + case FIB_PATH_TYPE_BIER_FMASK: + { + /* + * Find the BIER f-mask to link to + */ + bier_fmask_id_t fmid = { + .bfmi_nh = path->bier_fmask.fp_nh, + .bfmi_hdr_type = BIER_HDR_O_MPLS, + }; + + ASSERT(FIB_NODE_INDEX_INVALID == path->fp_via_bier_fmask); + + path->fp_via_bier_fmask = bier_fmask_db_find(path->bier_fmask.fp_bier_fib, + &fmid); + + /* + * become a dependent child of the entry so the path is + * informed when the forwarding for the entry changes. + */ + path->fp_sibling = bier_fmask_child_add(path->fp_via_bier_fmask, + FIB_NODE_TYPE_PATH, + fib_path_get_index(path)); + + fib_path_bier_fmask_update(path, &path->fp_dpo); + + break; + } + case FIB_PATH_TYPE_BIER_IMP: + bier_imp_lock(path->bier_imp.fp_bier_imp); + bier_imp_contribute_forwarding(path->bier_imp.fp_bier_imp, + DPO_PROTO_IP4, + &path->fp_dpo); + break; + case FIB_PATH_TYPE_BIER_TABLE: + { + /* + * Find/create the BIER table to link to + */ + ASSERT(FIB_NODE_INDEX_INVALID == path->fp_via_bier_tbl); + + path->fp_via_bier_tbl = + bier_table_ecmp_create_and_lock(&path->bier_table.fp_bier_tbl); + + bier_table_contribute_forwarding(path->fp_via_bier_tbl, + &path->fp_dpo); + break; + } case FIB_PATH_TYPE_SPECIAL: /* * Resolve via the drop @@ -1833,11 +2081,45 @@ fib_path_get_resolving_interface (fib_node_index_t path_index) case FIB_PATH_TYPE_SPECIAL: case FIB_PATH_TYPE_DEAG: case FIB_PATH_TYPE_EXCLUSIVE: + case FIB_PATH_TYPE_BIER_FMASK: + case FIB_PATH_TYPE_BIER_TABLE: + case FIB_PATH_TYPE_BIER_IMP: break; } return (~0); } +index_t +fib_path_get_resolving_index (fib_node_index_t path_index) +{ + fib_path_t *path; + + path = fib_path_get(path_index); + + switch (path->fp_type) + { + case FIB_PATH_TYPE_ATTACHED_NEXT_HOP: + case FIB_PATH_TYPE_ATTACHED: + case FIB_PATH_TYPE_RECEIVE: + case FIB_PATH_TYPE_INTF_RX: + case FIB_PATH_TYPE_SPECIAL: + case FIB_PATH_TYPE_DEAG: + case FIB_PATH_TYPE_EXCLUSIVE: + break; + case FIB_PATH_TYPE_UDP_ENCAP: + return (path->udp_encap.fp_udp_encap_id); + case FIB_PATH_TYPE_RECURSIVE: + return (path->fp_via_fib); + case FIB_PATH_TYPE_BIER_FMASK: + return (path->fp_via_bier_fmask); + case FIB_PATH_TYPE_BIER_TABLE: + return (path->fp_via_bier_tbl); + case FIB_PATH_TYPE_BIER_IMP: + return (path->bier_imp.fp_bier_imp); + } + return (~0); +} + adj_index_t fib_path_get_adj (fib_node_index_t path_index) { @@ -1877,6 +2159,23 @@ fib_path_get_preference (fib_node_index_t path_index) return (path->fp_preference); } +u32 +fib_path_get_rpf_id (fib_node_index_t path_index) +{ + fib_path_t *path; + + path = fib_path_get(path_index); + + ASSERT(path); + + if (FIB_PATH_CFG_FLAG_RPF_ID & path->fp_cfg_flags) + { + return (path->deag.fp_rpf_id); + } + + return (~0); +} + /** * @brief Contribute the path's adjacency to the list passed. * By calling this function over all paths, recursively, a child @@ -1937,6 +2236,9 @@ fib_path_contribute_urpf (fib_node_index_t path_index, case FIB_PATH_TYPE_RECEIVE: case FIB_PATH_TYPE_INTF_RX: case FIB_PATH_TYPE_UDP_ENCAP: + case FIB_PATH_TYPE_BIER_FMASK: + case FIB_PATH_TYPE_BIER_TABLE: + case FIB_PATH_TYPE_BIER_IMP: /* * these path types don't link to an adj */ @@ -1979,6 +2281,9 @@ fib_path_stack_mpls_disp (fib_node_index_t path_index, case FIB_PATH_TYPE_UDP_ENCAP: case FIB_PATH_TYPE_EXCLUSIVE: case FIB_PATH_TYPE_SPECIAL: + case FIB_PATH_TYPE_BIER_FMASK: + case FIB_PATH_TYPE_BIER_TABLE: + case FIB_PATH_TYPE_BIER_IMP: break; } } @@ -2036,8 +2341,9 @@ fib_path_contribute_forwarding (fib_node_index_t path_index, } case FIB_FORW_CHAIN_TYPE_MCAST_IP4: case FIB_FORW_CHAIN_TYPE_MCAST_IP6: - break; - } + case FIB_FORW_CHAIN_TYPE_BIER: + break; + } break; case FIB_PATH_TYPE_RECURSIVE: switch (fct) @@ -2048,6 +2354,7 @@ fib_path_contribute_forwarding (fib_node_index_t path_index, case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS: case FIB_FORW_CHAIN_TYPE_MCAST_IP4: case FIB_FORW_CHAIN_TYPE_MCAST_IP6: + case FIB_FORW_CHAIN_TYPE_BIER: fib_path_recursive_adj_update(path, fct, dpo); break; case FIB_FORW_CHAIN_TYPE_ETHERNET: @@ -2056,6 +2363,47 @@ fib_path_contribute_forwarding (fib_node_index_t path_index, break; } break; + case FIB_PATH_TYPE_BIER_TABLE: + switch (fct) + { + case FIB_FORW_CHAIN_TYPE_BIER: + bier_table_contribute_forwarding(path->fp_via_bier_tbl, dpo); + break; + case FIB_FORW_CHAIN_TYPE_MPLS_EOS: + case FIB_FORW_CHAIN_TYPE_UNICAST_IP4: + case FIB_FORW_CHAIN_TYPE_UNICAST_IP6: + case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS: + case FIB_FORW_CHAIN_TYPE_MCAST_IP4: + case FIB_FORW_CHAIN_TYPE_MCAST_IP6: + case FIB_FORW_CHAIN_TYPE_ETHERNET: + case FIB_FORW_CHAIN_TYPE_NSH: + ASSERT(0); + break; + } + break; + case FIB_PATH_TYPE_BIER_FMASK: + switch (fct) + { + case FIB_FORW_CHAIN_TYPE_BIER: + fib_path_bier_fmask_update(path, dpo); + break; + case FIB_FORW_CHAIN_TYPE_MPLS_EOS: + case FIB_FORW_CHAIN_TYPE_UNICAST_IP4: + case FIB_FORW_CHAIN_TYPE_UNICAST_IP6: + case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS: + case FIB_FORW_CHAIN_TYPE_MCAST_IP4: + case FIB_FORW_CHAIN_TYPE_MCAST_IP6: + case FIB_FORW_CHAIN_TYPE_ETHERNET: + case FIB_FORW_CHAIN_TYPE_NSH: + ASSERT(0); + break; + } + break; + case FIB_PATH_TYPE_BIER_IMP: + bier_imp_contribute_forwarding(path->bier_imp.fp_bier_imp, + fib_forw_chain_type_to_dpo_proto(fct), + dpo); + break; case FIB_PATH_TYPE_DEAG: switch (fct) { @@ -2074,6 +2422,8 @@ fib_path_contribute_forwarding (fib_node_index_t path_index, break; case FIB_FORW_CHAIN_TYPE_MCAST_IP4: case FIB_FORW_CHAIN_TYPE_MCAST_IP6: + case FIB_FORW_CHAIN_TYPE_BIER: + break; case FIB_FORW_CHAIN_TYPE_ETHERNET: case FIB_FORW_CHAIN_TYPE_NSH: ASSERT(0); @@ -2097,6 +2447,7 @@ fib_path_contribute_forwarding (fib_node_index_t path_index, case FIB_FORW_CHAIN_TYPE_MPLS_EOS: case FIB_FORW_CHAIN_TYPE_ETHERNET: case FIB_FORW_CHAIN_TYPE_NSH: + case FIB_FORW_CHAIN_TYPE_BIER: { adj_index_t ai; @@ -2263,6 +2614,10 @@ fib_path_encode (fib_node_index_t path_list_index, api_rpath->rpath.frp_sw_if_index = path->attached_next_hop.fp_interface; api_rpath->rpath.frp_addr = path->attached_next_hop.fp_nh; break; + case FIB_PATH_TYPE_BIER_FMASK: + api_rpath->rpath.frp_fib_index = path->bier_fmask.fp_bier_fib; + api_rpath->rpath.frp_addr = path->bier_fmask.fp_nh; + break; case FIB_PATH_TYPE_SPECIAL: break; case FIB_PATH_TYPE_DEAG: diff --git a/src/vnet/fib/fib_path.h b/src/vnet/fib/fib_path.h index 3a0544c1c42..861bda95933 100644 --- a/src/vnet/fib/fib_path.h +++ b/src/vnet/fib/fib_path.h @@ -35,6 +35,7 @@ #include <vnet/fib/fib_types.h> #include <vnet/adj/adj_types.h> +#include <vnet/bier/bier_types.h> /** * Enurmeration of path configuration attributes @@ -173,8 +174,10 @@ extern adj_index_t fib_path_get_adj(fib_node_index_t path_index); extern int fib_path_recursive_loop_detect(fib_node_index_t path_index, fib_node_index_t **entry_indicies); extern u32 fib_path_get_resolving_interface(fib_node_index_t fib_entry_index); +extern index_t fib_path_get_resolving_index(fib_node_index_t path_index); extern u16 fib_path_get_weight(fib_node_index_t path_index); extern u16 fib_path_get_preference(fib_node_index_t path_index); +extern u32 fib_path_get_rpf_id(fib_node_index_t path_index); extern void fib_path_module_init(void); extern fib_path_list_walk_rc_t fib_path_encode(fib_node_index_t path_list_index, diff --git a/src/vnet/fib/fib_path_list.c b/src/vnet/fib/fib_path_list.c index f30fd7ea0a4..f9477406ed3 100644 --- a/src/vnet/fib/fib_path_list.c +++ b/src/vnet/fib/fib_path_list.c @@ -583,6 +583,11 @@ fib_path_list_get_n_paths (fib_node_index_t path_list_index) { fib_path_list_t *path_list; + if (FIB_NODE_INDEX_INVALID == path_list_index) + { + return (0); + } + path_list = fib_path_list_get(path_list_index); return (vec_len(path_list->fpl_paths)); diff --git a/src/vnet/fib/fib_table.c b/src/vnet/fib/fib_table.c index d5625d83673..4dd6e7c5421 100644 --- a/src/vnet/fib/fib_table.c +++ b/src/vnet/fib/fib_table.c @@ -953,6 +953,7 @@ fib_table_get_flow_hash_config (u32 fib_index, return (fib->ft_flow_hash_config); } + flow_hash_config_t fib_table_get_default_flow_hash_config (fib_protocol_t proto) { diff --git a/src/vnet/fib/fib_table.h b/src/vnet/fib/fib_table.h index 6c2640458b9..d2522aa50c8 100644 --- a/src/vnet/fib/fib_table.h +++ b/src/vnet/fib/fib_table.h @@ -697,7 +697,7 @@ extern u32 fib_table_create_and_lock(fib_protocol_t proto, * The index of the FIB * * @paran proto - * The protocol of the FIB (and thus the entries therein) + * The protocol the packets the flow hash will be calculated for. * * @return The flow hash config */ diff --git a/src/vnet/fib/fib_test.c b/src/vnet/fib/fib_test.c index 66a38236216..2658eb27811 100644 --- a/src/vnet/fib/fib_test.c +++ b/src/vnet/fib/fib_test.c @@ -13,6 +13,7 @@ * limitations under the License. */ +#include <vnet/fib/fib_test.h> #include <vnet/fib/ip6_fib.h> #include <vnet/fib/ip4_fib.h> #include <vnet/fib/mpls_fib.h> @@ -544,15 +545,31 @@ fib_test_validate_lb_v (const load_balance_t *lb, dpo->dpoi_index, exp->lb.lb); break; - case FT_LB_SPECIAL: - FIB_TEST_I((DPO_DROP == dpo->dpoi_type), - "bucket %d stacks on %U", - bucket, - format_dpo_type, dpo->dpoi_type); - FIB_TEST_LB((exp->special.adj == dpo->dpoi_index), - "bucket %d stacks on drop %d", + case FT_LB_BIER_TABLE: + FIB_TEST_LB((DPO_BIER_TABLE == dpo->dpoi_type), + "bucket %d stacks on %U", + bucket, + format_dpo_type, dpo->dpoi_type); + FIB_TEST_LB((exp->bier.table == dpo->dpoi_index), + "bucket %d stacks on lb %d", + bucket, + exp->bier.table); + break; + case FT_LB_BIER_FMASK: + FIB_TEST_LB((DPO_BIER_FMASK == dpo->dpoi_type), + "bucket %d stacks on %U", + bucket, + format_dpo_type, dpo->dpoi_type); + FIB_TEST_LB((exp->bier.fmask == dpo->dpoi_index), + "bucket %d stacks on lb %d", bucket, - exp->special.adj); + exp->bier.fmask); + break; + case FT_LB_DROP: + FIB_TEST_LB((DPO_DROP == dpo->dpoi_type), + "bucket %d stacks on %U", + bucket, + format_dpo_type, dpo->dpoi_type); break; } } @@ -560,6 +577,29 @@ fib_test_validate_lb_v (const load_balance_t *lb, } int +fib_test_validate_lb (const dpo_id_t *dpo, + u16 n_buckets, + ...) +{ + const load_balance_t *lb; + va_list ap; + int res; + + va_start(ap, n_buckets); + + FIB_TEST_LB((DPO_LOAD_BALANCE == dpo->dpoi_type), + "Entry links to %U", + format_dpo_type, dpo->dpoi_type); + lb = load_balance_get(dpo->dpoi_index); + + res = fib_test_validate_lb_v(lb, n_buckets, &ap); + + va_end(ap); + + return (res); +} + +int fib_test_validate_entry (fib_node_index_t fei, fib_forward_chain_type_t fct, int n_buckets, @@ -6678,13 +6718,10 @@ fib_test_label (void) * remove the other path with a valid label */ fib_test_lb_bucket_t bucket_drop = { - .type = FT_LB_SPECIAL, - .special = { - .adj = DPO_PROTO_IP4, - }, + .type = FT_LB_DROP, }; fib_test_lb_bucket_t mpls_bucket_drop = { - .type = FT_LB_SPECIAL, + .type = FT_LB_DROP, .special = { .adj = DPO_PROTO_MPLS, }, @@ -8497,13 +8534,13 @@ lfib_test (void) * A recursive via a label that does not exist */ fib_test_lb_bucket_t bucket_drop = { - .type = FT_LB_SPECIAL, + .type = FT_LB_DROP, .special = { .adj = DPO_PROTO_IP4, }, }; fib_test_lb_bucket_t mpls_bucket_drop = { - .type = FT_LB_SPECIAL, + .type = FT_LB_DROP, .special = { .adj = DPO_PROTO_MPLS, }, diff --git a/src/vnet/fib/fib_test.h b/src/vnet/fib/fib_test.h index a9463d60830..f3d8346aab3 100644 --- a/src/vnet/fib/fib_test.h +++ b/src/vnet/fib/fib_test.h @@ -17,16 +17,24 @@ #define __FIB_TEST_H__ #include <vnet/fib/fib_types.h> +#include <vnet/mpls/mpls_types.h> +#include <vnet/fib/fib_types.h> +#include <vnet/mpls/packet.h> +#include <vnet/dpo/load_balance.h> +#include <vnet/adj/adj_types.h> +#include <vnet/dpo/replicate_dpo.h> typedef enum fib_test_lb_bucket_type_t_ { FT_LB_LABEL_O_ADJ, FT_LB_LABEL_STACK_O_ADJ, FT_LB_LABEL_O_LB, FT_LB_O_LB, - FT_LB_SPECIAL, - FT_LB_ADJ, FT_LB_INTF, FT_LB_L2, + FT_LB_BIER_TABLE, + FT_LB_BIER_FMASK, + FT_LB_DROP, + FT_LB_ADJ, } fib_test_lb_bucket_type_t; typedef struct fib_test_lb_bucket_t_ { @@ -68,6 +76,13 @@ typedef struct fib_test_lb_bucket_t_ { { index_t adj; } special; + struct + { + union { + index_t table; + index_t fmask; + }; + } bier; }; } fib_test_lb_bucket_t; @@ -105,9 +120,14 @@ extern int fib_test_validate_lb_v(const load_balance_t *lb, u16 n_buckets, va_list *ap); +extern int fib_test_validate_lb(const dpo_id_t *dpo, + u16 n_buckets, + ...); + extern int fib_test_validate_entry(fib_node_index_t fei, fib_forward_chain_type_t fct, int n_buckets, ...); + #endif diff --git a/src/vnet/fib/fib_types.c b/src/vnet/fib/fib_types.c index d84642d29da..656966b2331 100644 --- a/src/vnet/fib/fib_types.c +++ b/src/vnet/fib/fib_types.c @@ -272,6 +272,8 @@ fib_forw_chain_type_from_dpo_proto (dpo_proto_t proto) return (FIB_FORW_CHAIN_TYPE_ETHERNET); case DPO_PROTO_NSH: return (FIB_FORW_CHAIN_TYPE_NSH); + case DPO_PROTO_BIER: + return (FIB_FORW_CHAIN_TYPE_BIER); } ASSERT(0); return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4); @@ -293,6 +295,7 @@ fib_forw_chain_type_to_link_type (fib_forward_chain_type_t fct) case FIB_FORW_CHAIN_TYPE_NSH: return (VNET_LINK_NSH); case FIB_FORW_CHAIN_TYPE_MPLS_EOS: + case FIB_FORW_CHAIN_TYPE_BIER: /* * insufficient information to to convert */ @@ -319,6 +322,8 @@ fib_forw_chain_type_to_dpo_proto (fib_forward_chain_type_t fct) return (DPO_PROTO_ETHERNET); case FIB_FORW_CHAIN_TYPE_NSH: return (DPO_PROTO_NSH); + case FIB_FORW_CHAIN_TYPE_BIER: + return (DPO_PROTO_BIER); case FIB_FORW_CHAIN_TYPE_MPLS_EOS: case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS: return (DPO_PROTO_MPLS); diff --git a/src/vnet/fib/fib_types.h b/src/vnet/fib/fib_types.h index ec7f1b374dd..0a4b1699609 100644 --- a/src/vnet/fib/fib_types.h +++ b/src/vnet/fib/fib_types.h @@ -20,6 +20,7 @@ #include <vnet/ip/ip6_packet.h> #include <vnet/mpls/packet.h> #include <vnet/dpo/dpo.h> +#include <vnet/bier/bier_types.h> /** * A typedef of a node index. @@ -50,6 +51,12 @@ typedef enum fib_protocol_t_ { #define FIB_PROTOCOL_MAX (FIB_PROTOCOL_MPLS + 1) /** + * Definition outside of enum so it does not need to be included in non-defaulted + * switch statements + */ +#define FIB_PROTOCOL_IP_MAX (FIB_PROTOCOL_IP6 + 1) + +/** * Not part of the enum so it does not have to be handled in switch statements */ #define FIB_PROTOCOL_NONE (FIB_PROTOCOL_MAX+1) @@ -89,6 +96,10 @@ typedef enum fib_forward_chain_type_t_ { */ FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS, /** + * Contribute an object that is to be used to forward BIER packets. + */ + FIB_FORW_CHAIN_TYPE_BIER, + /** * Contribute an object that is to be used to forward end-of-stack * MPLS packets. This is a convenient ID for clients. A real EOS chain * must be pay-load protocol specific. This @@ -117,6 +128,7 @@ typedef enum fib_forward_chain_type_t_ { #define FIB_FORW_CHAINS { \ [FIB_FORW_CHAIN_TYPE_ETHERNET] = "ethernet", \ + [FIB_FORW_CHAIN_TYPE_BIER] = "bier", \ [FIB_FORW_CHAIN_TYPE_UNICAST_IP4] = "unicast-ip4", \ [FIB_FORW_CHAIN_TYPE_UNICAST_IP6] = "unicast-ip6", \ [FIB_FORW_CHAIN_TYPE_MCAST_IP4] = "multicast-ip4", \ @@ -231,6 +243,11 @@ extern dpo_proto_t fib_proto_to_dpo(fib_protocol_t fib_proto); extern fib_protocol_t dpo_proto_to_fib(dpo_proto_t dpo_proto); /** + * Convert from BIER next-hop proto to FIB proto + */ +extern fib_protocol_t bier_hdr_proto_to_fib(bier_hdr_proto_id_t bproto); + +/** * Enurmeration of special path/entry types */ typedef enum fib_special_type_t_ { @@ -311,6 +328,18 @@ typedef enum fib_route_path_flags_t_ * A path via a UDP encap object. */ FIB_ROUTE_PATH_UDP_ENCAP = (1 << 9), + /** + * A path that resolves via a BIER F-Mask + */ + FIB_ROUTE_PATH_BIER_FMASK = (1 << 10), + /** + * A path that resolves via a BIER [ECMP] Table + */ + FIB_ROUTE_PATH_BIER_TABLE = (1 << 11), + /** + * A path that resolves via a BIER impostion object + */ + FIB_ROUTE_PATH_BIER_IMP = (1 << 12), } fib_route_path_flags_t; /** @@ -349,47 +378,73 @@ typedef struct fib_route_path_t_ { dpo_proto_t frp_proto; union { - /** - * The next-hop address. - * Will be NULL for attached paths. - * Will be all zeros for attached-next-hop paths on a p2p interface - * Will be all zeros for a deag path. - */ - ip46_address_t frp_addr; - struct { + union { + /** + * The next-hop address. + * Will be NULL for attached paths. + * Will be all zeros for attached-next-hop paths on a p2p interface + * Will be all zeros for a deag path. + */ + ip46_address_t frp_addr; + + struct { + /** + * The MPLS local Label to reursively resolve through. + * This is valid when the path type is MPLS. + */ + mpls_label_t frp_local_label; + /** + * EOS bit for the resolving label + */ + mpls_eos_bit_t frp_eos; + }; + }; + union { + /** + * The interface. + * Will be invalid for recursive paths. + */ + u32 frp_sw_if_index; + /** + * The RPF-ID + */ + fib_rpf_id_t frp_rpf_id; + }; + union { + /** + * The FIB index to lookup the nexthop + * Only valid for recursive paths. + */ + u32 frp_fib_index; + /** + * The BIER table to resolve the fmask in + */ + u32 frp_bier_fib_index; + }; /** - * The MPLS local Label to reursively resolve through. - * This is valid when the path type is MPLS. + * The outgoing MPLS label Stack. NULL implies no label. */ - mpls_label_t frp_local_label; - /** - * EOS bit for the resolving label - */ - mpls_eos_bit_t frp_eos; + mpls_label_t *frp_label_stack; }; - }; - union { /** - * The interface. - * Will be invalid for recursive paths. + * A path that resolves via a BIER Table. + * This would be for a MPLS label at a BIER midpoint or tail */ - u32 frp_sw_if_index; + bier_table_id_t frp_bier_tbl; + /** - * The RPF-ID + * A path via a BIER imposition object. + * Present in an mfib path list */ - fib_rpf_id_t frp_rpf_id; + index_t frp_bier_imp; + /** * UDP encap ID */ u32 frp_udp_encap_id; }; /** - * The FIB index to lookup the nexthop - * Only valid for recursive paths. - */ - u32 frp_fib_index; - /** * [un]equal cost path weight */ u8 frp_weight; @@ -403,10 +458,6 @@ typedef struct fib_route_path_t_ { * flags on the path */ fib_route_path_flags_t frp_flags; - /** - * The outgoing MPLS label Stack. NULL implies no label. - */ - mpls_label_t *frp_label_stack; } fib_route_path_t; /** diff --git a/src/vnet/ip/ip.api b/src/vnet/ip/ip.api index 85e4b8e04c3..e9c9f55a6f6 100644 --- a/src/vnet/ip/ip.api +++ b/src/vnet/ip/ip.api @@ -19,7 +19,7 @@ called through a shared memory interface. */ -vl_api_version 1.0.0 +vl_api_version 1.0.1 /** \brief Add / del table request A table can be added multiple times, but need be deleted only once. @@ -68,6 +68,7 @@ define ip_fib_dump typeonly manual_print manual_endian define fib_path { u32 sw_if_index; + u32 table_id; u8 weight; u8 preference; u8 is_local; @@ -426,7 +427,7 @@ autoreply define ip_add_del_route @param context - sender context, to match reply w/ request @param sw_if_index - software index of the new vlan's parent interface @param vrf_id - fib table /vrf associated with the route - + @param next_hop_afi - Use dpo_proto_t FIXME */ autoreply define ip_mroute_add_del @@ -438,7 +439,9 @@ autoreply define ip_mroute_add_del u32 entry_flags; u32 itf_flags; u32 rpf_id; + u32 bier_imp; u16 grp_address_length; + u8 next_hop_afi; u8 create_vrf_if_needed; u8 is_add; u8 is_ipv6; diff --git a/src/vnet/ip/ip_api.c b/src/vnet/ip/ip_api.c index 20e19205756..0fdb167312e 100644 --- a/src/vnet/ip/ip_api.c +++ b/src/vnet/ip/ip_api.c @@ -464,7 +464,7 @@ send_ip_mfib_details (unix_shared_memory_queue_t * q, if (!mp) return; memset (mp, 0, sizeof (*mp)); - mp->_vl_msg_id = ntohs (VL_API_IP_FIB_DETAILS); + mp->_vl_msg_id = ntohs (VL_API_IP_MFIB_DETAILS); mp->context = context; mp->rpf_id = mfib_entry->mfe_rpf_id; @@ -563,7 +563,7 @@ send_ip6_mfib_details (vpe_api_main_t * am, if (!mp) return; memset (mp, 0, sizeof (*mp)); - mp->_vl_msg_id = ntohs (VL_API_IP6_FIB_DETAILS); + mp->_vl_msg_id = ntohs (VL_API_IP6_MFIB_DETAILS); mp->context = context; mp->table_id = htonl (table_id); @@ -1283,40 +1283,45 @@ mroute_add_del_handler (u8 is_add, u8 is_local, u32 fib_index, const mfib_prefix_t * prefix, + dpo_proto_t nh_proto, u32 entry_flags, fib_rpf_id_t rpf_id, - u32 next_hop_sw_if_index, u32 itf_flags) + u32 next_hop_sw_if_index, u32 itf_flags, u32 bier_imp) { stats_dslock_with_hint (1 /* release hint */ , 2 /* tag */ ); fib_route_path_t path = { .frp_sw_if_index = next_hop_sw_if_index, - .frp_proto = fib_proto_to_dpo (prefix->fp_proto), + .frp_proto = nh_proto, }; if (is_local) path.frp_flags |= FIB_ROUTE_PATH_LOCAL; - - if (!is_local && ~0 == next_hop_sw_if_index) + if (DPO_PROTO_BIER == nh_proto) + { + path.frp_bier_imp = bier_imp; + path.frp_flags = FIB_ROUTE_PATH_BIER_IMP; + } + else if (!is_local && ~0 == next_hop_sw_if_index) { mfib_table_entry_update (fib_index, prefix, MFIB_SOURCE_API, rpf_id, entry_flags); + goto done; + } + + if (is_add) + { + mfib_table_entry_path_update (fib_index, prefix, + MFIB_SOURCE_API, &path, itf_flags); } else { - if (is_add) - { - mfib_table_entry_path_update (fib_index, prefix, - MFIB_SOURCE_API, &path, itf_flags); - } - else - { - mfib_table_entry_path_remove (fib_index, prefix, - MFIB_SOURCE_API, &path); - } + mfib_table_entry_path_remove (fib_index, prefix, + MFIB_SOURCE_API, &path); } +done: stats_dsunlock (); return (0); } @@ -1325,9 +1330,11 @@ static int api_mroute_add_del_t_handler (vl_api_ip_mroute_add_del_t * mp) { fib_protocol_t fproto; + dpo_proto_t nh_proto; u32 fib_index; int rv; + nh_proto = mp->next_hop_afi; fproto = (mp->is_ipv6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4); rv = add_del_mroute_check (fproto, mp->table_id, @@ -1360,10 +1367,12 @@ api_mroute_add_del_t_handler (vl_api_ip_mroute_add_del_t * mp) return (mroute_add_del_handler (mp->is_add, mp->is_local, fib_index, &pfx, + nh_proto, ntohl (mp->entry_flags), ntohl (mp->rpf_id), ntohl (mp->next_hop_sw_if_index), - ntohl (mp->itf_flags))); + ntohl (mp->itf_flags), + ntohl (mp->bier_imp))); } void diff --git a/src/vnet/mfib/mfib_entry.c b/src/vnet/mfib/mfib_entry.c index 2302b9a1707..aaecf0f6eb6 100644 --- a/src/vnet/mfib/mfib_entry.c +++ b/src/vnet/mfib/mfib_entry.c @@ -300,7 +300,7 @@ mfib_entry_src_find (const mfib_entry_t *mfib_entry, static mfib_entry_src_t * mfib_entry_src_find_or_create (mfib_entry_t *mfib_entry, - mfib_source_t source) + mfib_source_t source) { mfib_entry_src_t *esrc; @@ -535,6 +535,7 @@ mfib_entry_src_collect_forwarding (fib_node_index_t pl_index, case FIB_FORW_CHAIN_TYPE_MPLS_EOS: case FIB_FORW_CHAIN_TYPE_ETHERNET: case FIB_FORW_CHAIN_TYPE_NSH: + case FIB_FORW_CHAIN_TYPE_BIER: ASSERT(0); break; } diff --git a/src/vnet/mpls/mpls.api b/src/vnet/mpls/mpls.api index 3c817db1306..f9171bfe22e 100644 --- a/src/vnet/mpls/mpls.api +++ b/src/vnet/mpls/mpls.api @@ -13,7 +13,7 @@ * limitations under the License. */ -vl_api_version 1.0.0 +vl_api_version 1.0.1 /** \brief Bind/Unbind an MPLS local label to an IP prefix. i.e. create a per-prefix label entry. @@ -114,6 +114,7 @@ define mpls_tunnel_dump typeonly manual_print manual_endian define fib_path2 { u32 sw_if_index; + u32 table_id; u8 weight; u8 preference; u8 is_local; diff --git a/src/vnet/mpls/mpls_api.c b/src/vnet/mpls/mpls_api.c index a55daa2b0c4..f345ca6aeea 100644 --- a/src/vnet/mpls/mpls_api.c +++ b/src/vnet/mpls/mpls_api.c @@ -366,7 +366,7 @@ typedef struct mpls_tunnel_send_walk_ctx_t_ static void send_mpls_tunnel_entry (u32 mti, void *arg) { - fib_route_path_encode_t *api_rpaths, *api_rpath; + fib_route_path_encode_t *api_rpaths = NULL, *api_rpath; mpls_tunnel_send_walk_ctx_t *ctx; vl_api_mpls_tunnel_details_t *mp; const mpls_tunnel_t *mt; diff --git a/src/vnet/srv6/sr_policy_rewrite.c b/src/vnet/srv6/sr_policy_rewrite.c index 2f90993a4be..514dd6584d0 100755 --- a/src/vnet/srv6/sr_policy_rewrite.c +++ b/src/vnet/srv6/sr_policy_rewrite.c @@ -367,7 +367,7 @@ update_lb (ip6_sr_policy_t * sr_policy) /* Add FIB entry for BSID */ fhc = fib_table_get_flow_hash_config (sr_policy->fib_table, - dpo_proto_to_fib (DPO_PROTO_IP6)); + FIB_PROTOCOL_IP6); dpo_set (&sr_policy->bsid_dpo, DPO_LOAD_BALANCE, DPO_PROTO_IP6, load_balance_create (0, DPO_PROTO_IP6, fhc)); diff --git a/src/vnet/vnet_all_api_h.h b/src/vnet/vnet_all_api_h.h index c71af079e11..e5f580496b1 100644 --- a/src/vnet/vnet_all_api_h.h +++ b/src/vnet/vnet_all_api_h.h @@ -62,6 +62,7 @@ #include <vnet/tcp/tcp.api.h> #include <vnet/dns/dns.api.h> #include <vnet/udp/udp.api.h> +#include <vnet/bier/bier.api.h> /* * fd.io coding-style-patch-verification: ON diff --git a/src/vpp/api/api.c b/src/vpp/api/api.c index 36953aec623..c0c6f61f95b 100644 --- a/src/vpp/api/api.c +++ b/src/vpp/api/api.c @@ -87,6 +87,7 @@ #include <vnet/dpo/lookup_dpo.h> #include <vnet/dpo/classify_dpo.h> #include <vnet/dpo/ip_null_dpo.h> + #define vl_typedefs /* define message structures */ #include <vpp/api/vpe_all_api_h.h> #undef vl_typedefs diff --git a/src/vpp/api/vpe.api b/src/vpp/api/vpe.api index f0f6f41f7eb..0807e4b3985 100644 --- a/src/vpp/api/vpe.api +++ b/src/vpp/api/vpe.api @@ -51,6 +51,7 @@ vl_api_version 1.0.0 * COP APIs: see ... /src/vnet/cop/{cop.api, cop_api.c} * POLICER APIs: see ... /src/vnet/policer/{policer.api, policer_api.c} * STATS APIs: see .../src/vpp/stats/{stats.api, stats.c} + * BIER APIs: see ... /src/vnet/policer/{bier.api, bier_api.c} */ /** \brief Create a new subinterface with the given vlan id diff --git a/test/patches/scapy-2.3.3/bier.patch b/test/patches/scapy-2.3.3/bier.patch new file mode 100644 index 00000000000..024805d0501 --- /dev/null +++ b/test/patches/scapy-2.3.3/bier.patch @@ -0,0 +1,45 @@ +diff --git a/scapy/contrib/bier.py b/scapy/contrib/bier.py +new file mode 100644 +index 0000000..e173cdb +--- /dev/null ++++ b/scapy/contrib/bier.py +@@ -0,0 +1,39 @@ ++# http://trac.secdev.org/scapy/ticket/31 ++ ++# scapy.contrib.description = MPLS ++# scapy.contrib.status = loads ++ ++from scapy.packet import * ++from scapy.fields import * ++from scapy.layers.inet import IP ++from scapy.layers.inet6 import IPv6 ++ ++class BIERLength: ++ BIER_LEN_64 = 0 ++ BIER_LEN_128 = 1 ++ BIER_LEN_256 = 2 ++ ++ ++ ++BIERnhcls = { 1: "MPLS", ++ 2: "MPLS", ++ 4: "IPv4", ++ 5: "IPv6" } ++ ++class BIER(Packet): ++ name = "BIER" ++ fields_desc = [ BitField("id", 5, 4), ++ BitField("version", 0, 4), ++ BitField("length", 0, 4), ++ BitField("entropy", 0, 20), ++ BitField("OAM", 0, 2), ++ BitField("RSV", 0, 2), ++ BitField("DSCP", 0, 6), ++ BitEnumField("Proto", 2, 6, BIERnhcls), ++ ShortField("BFRID", 0), ++ StrFixedLenField("BitString", ++ chr(255)*32, 32) ] ++ ++ ++bind_layers(BIER, IP, Proto=4) ++bind_layers(BIER, IPv6, Proto=5) diff --git a/test/patches/scapy-2.3.3/mpls.py.patch b/test/patches/scapy-2.3.3/mpls.py.patch index f63a70a3cd7..f3293e8eee2 100644 --- a/test/patches/scapy-2.3.3/mpls.py.patch +++ b/test/patches/scapy-2.3.3/mpls.py.patch @@ -1,16 +1,35 @@ diff --git a/scapy/contrib/mpls.py b/scapy/contrib/mpls.py -index 640a0c5..6af1d4a 100644 +index 640a0c5..944723a 100644 --- a/scapy/contrib/mpls.py +++ b/scapy/contrib/mpls.py -@@ -18,6 +18,8 @@ class MPLS(Packet): +@@ -6,6 +6,7 @@ + from scapy.packet import Packet, bind_layers, Padding + from scapy.fields import BitField,ByteField + from scapy.layers.inet import IP ++from scapy.contrib.bier import BIER + from scapy.layers.inet6 import IPv6 + from scapy.layers.l2 import Ether, GRE + +@@ -17,9 +18,12 @@ class MPLS(Packet): def guess_payload_class(self, payload): - if len(payload) >= 1: -+ if not self.s: -+ return MPLS - ip_version = (ord(payload[0]) >> 4) & 0xF - if ip_version == 4: - return IP +- if len(payload) >= 1: +- ip_version = (ord(payload[0]) >> 4) & 0xF +- if ip_version == 4: +- return IP +- elif ip_version == 6: +- return IPv6 +- return Padding ++ if not self.s: ++ return MPLS ++ ip_version = (ord(payload[0]) >> 4) & 0xF ++ if ip_version == 4: ++ return IP ++ elif ip_version == 5: ++ return BIER ++ elif ip_version == 6: ++ return IPv6 ++ return Padding @@ -27,3 +29,4 @@ class MPLS(Packet): bind_layers(Ether, MPLS, type=0x8847) diff --git a/test/test_bier.py b/test/test_bier.py new file mode 100644 index 00000000000..1a4567bd656 --- /dev/null +++ b/test/test_bier.py @@ -0,0 +1,390 @@ +#!/usr/bin/env python + +import unittest +import socket + +from framework import VppTestCase, VppTestRunner +from vpp_ip_route import VppIpRoute, VppRoutePath, VppMplsRoute, \ + VppMplsTable, VppIpMRoute, VppMRoutePath, VppIpTable, \ + MRouteEntryFlags, MRouteItfFlags, MPLS_LABEL_INVALID, DpoProto +from vpp_bier import * + +from scapy.packet import Raw +from scapy.layers.l2 import Ether +from scapy.layers.inet import IP, UDP, ICMP +from scapy.layers.inet6 import IPv6 +from scapy.contrib.mpls import MPLS +from scapy.contrib.bier import * + + +class TestBFIB(VppTestCase): + """ BIER FIB Test Case """ + + def test_bfib(self): + """ BFIB Unit Tests """ + error = self.vapi.cli("test bier") + + if error: + self.logger.critical(error) + self.assertEqual(error.find("Failed"), -1) + + +class TestBier(VppTestCase): + """ BIER Test Case """ + + def setUp(self): + super(TestBier, self).setUp() + + # create 2 pg interfaces + self.create_pg_interfaces(range(3)) + + # create the default MPLS table + self.tables = [] + tbl = VppMplsTable(self, 0) + tbl.add_vpp_config() + self.tables.append(tbl) + + tbl = VppIpTable(self, 10) + tbl.add_vpp_config() + self.tables.append(tbl) + + # setup both interfaces + for i in self.pg_interfaces: + if i == self.pg2: + i.set_table_ip4(10) + i.admin_up() + i.config_ip4() + i.resolve_arp() + i.enable_mpls() + + def tearDown(self): + for i in self.pg_interfaces: + i.disable_mpls() + i.unconfig_ip4() + i.set_table_ip4(0) + i.admin_down() + super(TestBier, self).tearDown() + + def send_and_assert_no_replies(self, intf, pkts, remark): + intf.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + for i in self.pg_interfaces: + i.assert_nothing_captured(remark=remark) + + def send_and_expect(self, input, pkts, output): + self.vapi.cli("trace add bier-mpls-lookup 10") + input.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + rx = output.get_capture(len(pkts)) + + def test_bier_midpoint(self): + """BIER midpoint""" + + # + # Add a BIER table for sub-domain 0, set 0, and BSL 256 + # + bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256) + bt = VppBierTable(self, bti, 77) + bt.add_vpp_config() + + # + # A packet with no bits set gets dropped + # + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + MPLS(label=77, ttl=255) / + BIER(length=BIERLength.BIER_LEN_256, + BitString=chr(0)*64) / + IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6) / + UDP(sport=1234, dport=1234) / + Raw()) + pkts = [p] + + self.send_and_assert_no_replies(self.pg0, pkts, + "Empty Bit-String") + + # + # Add a BIER route for each bit-position in the table via a different + # next-hop. Testing whether the BIER walk and replicate forwarding + # function works for all bit posisitons. + # + nh_routes = [] + bier_routes = [] + for i in range(1, 256): + nh = "10.0.%d.%d" % (i / 255, i % 255) + nh_routes.append(VppIpRoute(self, nh, 32, + [VppRoutePath(self.pg1.remote_ip4, + self.pg1.sw_if_index, + labels=[2000+i])])) + nh_routes[-1].add_vpp_config() + + bier_routes.append(VppBierRoute(self, bti, i, nh, 100+i)) + bier_routes[-1].add_vpp_config() + + # + # A packet with all bits set gets spat out to BP:1 + # + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + MPLS(label=77, ttl=255) / + BIER(length=BIERLength.BIER_LEN_256) / + IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6) / + UDP(sport=1234, dport=1234) / + Raw()) + pkts = [p] + + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg1.get_capture(255) + + for rxp in rx: + # + # The packets are not required to be sent in bit-position order + # when we setup the routes above we used the bit-position to + # construct the out-label. so use that here to determine the BP + # + olabel = rxp[MPLS] + bp = olabel.label - 2000 + + blabel = olabel[MPLS].payload + self.assertEqual(blabel.label, 100+bp) + + bier_hdr = blabel[MPLS].payload + + self.assertEqual(bier_hdr.id, 5) + self.assertEqual(bier_hdr.version, 0) + self.assertEqual(bier_hdr.length, BIERLength.BIER_LEN_256) + self.assertEqual(bier_hdr.entropy, 0) + self.assertEqual(bier_hdr.OAM, 0) + self.assertEqual(bier_hdr.RSV, 0) + self.assertEqual(bier_hdr.DSCP, 0) + self.assertEqual(bier_hdr.Proto, 5) + + # The bit-string should consist only of the BP given by i. + i = 0 + bitstring = "" + bpi = bp - 1 + while (i < bpi/8): + bitstring = chr(0) + bitstring + i += 1 + bitstring = chr(1 << bpi % 8) + bitstring + + while len(bitstring) < 32: + bitstring = chr(0) + bitstring + + self.assertEqual(len(bitstring), len(bier_hdr.BitString)) + self.assertEqual(bitstring, bier_hdr.BitString) + + def test_bier_head(self): + """BIER head""" + + # + # Add a BIER table for sub-domain 0, set 0, and BSL 256 + # + bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256) + bt = VppBierTable(self, bti, 77) + bt.add_vpp_config() + + # + # 2 bit positions via two next hops + # + nh1 = "10.0.0.1" + nh2 = "10.0.0.2" + ip_route_1 = VppIpRoute(self, nh1, 32, + [VppRoutePath(self.pg1.remote_ip4, + self.pg1.sw_if_index, + labels=[2001])]) + ip_route_2 = VppIpRoute(self, nh2, 32, + [VppRoutePath(self.pg1.remote_ip4, + self.pg1.sw_if_index, + labels=[2002])]) + ip_route_1.add_vpp_config() + ip_route_2.add_vpp_config() + + bier_route_1 = VppBierRoute(self, bti, 1, nh1, 101) + bier_route_2 = VppBierRoute(self, bti, 2, nh2, 102) + bier_route_1.add_vpp_config() + bier_route_2.add_vpp_config() + + # + # An imposition object with both bit-positions set + # + bi = VppBierImp(self, bti, 333, chr(0x3) * 32) + bi.add_vpp_config() + + # + # Add a multicast route that will forward into the BIER doamin + # + route_ing_232_1_1_1 = VppIpMRoute( + self, + "0.0.0.0", + "232.1.1.1", 32, + MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE, + paths=[VppMRoutePath(self.pg0.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT), + VppMRoutePath(0xffffffff, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD, + proto=DpoProto.DPO_PROTO_BIER, + bier_imp=bi.bi_index)]) + route_ing_232_1_1_1.add_vpp_config() + + # + # inject a packet an IP. We expect it to be BIER encapped, + # replicated. + # + p = (Ether(dst=self.pg0.local_mac, + src=self.pg0.remote_mac) / + IP(src="1.1.1.1", dst="232.1.1.1") / + UDP(sport=1234, dport=1234)) + + self.pg0.add_stream([p]) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg1.get_capture(2) + + def test_bier_tail(self): + """BIER Tail""" + + # + # Add a BIER table for sub-domain 0, set 0, and BSL 256 + # + bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256) + bt = VppBierTable(self, bti, 77) + bt.add_vpp_config() + + # + # disposition table + # + bdt = VppBierDispTable(self, 8) + bdt.add_vpp_config() + + # + # BIER route in table that's for-us + # + bier_route_1 = VppBierRoute(self, bti, 1, "0.0.0.0", 0, + disp_table=8) + bier_route_1.add_vpp_config() + + # + # An entry in the disposition table + # + bier_de_1 = VppBierDispEntry(self, bdt.id, 99, + BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4, + "0.0.0.0", 0, rpf_id=8192) + bier_de_1.add_vpp_config() + + # + # A multicast route to forward post BIER disposition + # + route_eg_232_1_1_1 = VppIpMRoute( + self, + "0.0.0.0", + "232.1.1.1", 32, + MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE, + paths=[VppMRoutePath(self.pg1.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)]) + route_eg_232_1_1_1.add_vpp_config() + route_eg_232_1_1_1.update_rpf_id(8192) + + # + # A packet with all bits set gets spat out to BP:1 + # + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + MPLS(label=77, ttl=255) / + BIER(length=BIERLength.BIER_LEN_256, BFRID=99) / + IP(src="1.1.1.1", dst="232.1.1.1") / + UDP(sport=1234, dport=1234) / + Raw()) + + self.send_and_expect(self.pg0, [p], self.pg1) + + def test_bier_e2e(self): + """ BIER end-to-end """ + + # + # Add a BIER table for sub-domain 0, set 0, and BSL 256 + # + bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256) + bt = VppBierTable(self, bti, 77) + bt.add_vpp_config() + + # + # Impostion Sets bit string 101010101.... + # sender 333 + # + bi = VppBierImp(self, bti, 333, chr(0x5) * 32) + bi.add_vpp_config() + + # + # Add a multicast route that will forward into the BIER doamin + # + route_ing_232_1_1_1 = VppIpMRoute( + self, + "0.0.0.0", + "232.1.1.1", 32, + MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE, + paths=[VppMRoutePath(self.pg0.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT), + VppMRoutePath(0xffffffff, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD, + proto=DpoProto.DPO_PROTO_BIER, + bier_imp=bi.bi_index)]) + route_ing_232_1_1_1.add_vpp_config() + + # + # disposition table 8 + # + bdt = VppBierDispTable(self, 8) + bdt.add_vpp_config() + + # + # BIER route in table that's for-us, resolving through + # disp table 8. + # + bier_route_1 = VppBierRoute(self, bti, 1, "0.0.0.0", + MPLS_LABEL_INVALID, + disp_table=8) + bier_route_1.add_vpp_config() + + # + # An entry in the disposition table for sender 333 + # lookup in VRF 10 + # + bier_de_1 = VppBierDispEntry(self, bdt.id, 333, + BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4, + "0.0.0.0", 10, rpf_id=8192) + bier_de_1.add_vpp_config() + + # + # Add a multicast route that will forward the traffic + # post-disposition + # + route_eg_232_1_1_1 = VppIpMRoute( + self, + "0.0.0.0", + "232.1.1.1", 32, + MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE, + table_id=10, + paths=[VppMRoutePath(self.pg1.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)]) + route_eg_232_1_1_1.add_vpp_config() + route_eg_232_1_1_1.update_rpf_id(8192) + + # + # inject a packet in VRF-0. We expect it to be BIER encapped, + # replicated, then hit the disposition and be forwarded + # out of VRF 10, i.e. on pg1 + # + p = (Ether(dst=self.pg0.local_mac, + src=self.pg0.remote_mac) / + IP(src="1.1.1.1", dst="232.1.1.1") / + UDP(sport=1234, dport=1234)) + + self.send_and_expect(self.pg0, p*65, self.pg1) + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) diff --git a/test/test_ip_mcast.py b/test/test_ip_mcast.py index 7cad683cac5..8ca92df26b2 100644 --- a/test/test_ip_mcast.py +++ b/test/test_ip_mcast.py @@ -5,7 +5,7 @@ import unittest from framework import VppTestCase, VppTestRunner from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint from vpp_ip_route import VppIpMRoute, VppMRoutePath, VppMFibSignal, \ - MRouteItfFlags, MRouteEntryFlags, VppIpTable + MRouteItfFlags, MRouteEntryFlags, VppIpTable, DpoProto from scapy.packet import Raw from scapy.layers.l2 import Ether @@ -324,10 +324,6 @@ class TestIPMcast(VppTestCase): self.verify_capture_ip4(self.pg6, tx) self.verify_capture_ip4(self.pg7, tx) - route_232_1_1_1.remove_vpp_config() - route_1_1_1_1_232_1_1_1.remove_vpp_config() - route_232.remove_vpp_config() - def test_ip6_mcast(self): """ IPv6 Multicast Replication """ @@ -354,13 +350,17 @@ class TestIPMcast(VppTestCase): "ff01::1", 128, MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE, [VppMRoutePath(self.pg0.sw_if_index, - MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT), + MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT, + proto=DpoProto.DPO_PROTO_IP6), VppMRoutePath(self.pg1.sw_if_index, - MRouteItfFlags.MFIB_ITF_FLAG_FORWARD), + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD, + proto=DpoProto.DPO_PROTO_IP6), VppMRoutePath(self.pg2.sw_if_index, - MRouteItfFlags.MFIB_ITF_FLAG_FORWARD), + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD, + proto=DpoProto.DPO_PROTO_IP6), VppMRoutePath(self.pg3.sw_if_index, - MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)], + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD, + proto=DpoProto.DPO_PROTO_IP6)], is_ip6=1) route_ff01_1.add_vpp_config() @@ -374,11 +374,14 @@ class TestIPMcast(VppTestCase): "ff01::1", 256, MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE, [VppMRoutePath(self.pg0.sw_if_index, - MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT), + MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT, + proto=DpoProto.DPO_PROTO_IP6), VppMRoutePath(self.pg1.sw_if_index, - MRouteItfFlags.MFIB_ITF_FLAG_FORWARD), + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD, + proto=DpoProto.DPO_PROTO_IP6), VppMRoutePath(self.pg2.sw_if_index, - MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)], + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD, + proto=DpoProto.DPO_PROTO_IP6)], is_ip6=1) route_2001_ff01_1.add_vpp_config() @@ -392,9 +395,11 @@ class TestIPMcast(VppTestCase): "ff01::", 16, MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE, [VppMRoutePath(self.pg0.sw_if_index, - MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT), + MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT, + proto=DpoProto.DPO_PROTO_IP6), VppMRoutePath(self.pg1.sw_if_index, - MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)], + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD, + proto=DpoProto.DPO_PROTO_IP6)], is_ip6=1) route_ff01.add_vpp_config() @@ -474,10 +479,6 @@ class TestIPMcast(VppTestCase): self.pg3.assert_nothing_captured( remark="IP multicast packets forwarded on PG3") - route_ff01.remove_vpp_config() - route_ff01_1.remove_vpp_config() - route_2001_ff01_1.remove_vpp_config() - def _mcast_connected_send_stream(self, dst_ip): self.vapi.cli("clear trace") tx = self.create_stream_ip4(self.pg0, @@ -584,8 +585,10 @@ class TestIPMcast(VppTestCase): signal_232_1_1_1_itf_0.compare(signals[1]) signal_232_1_1_2_itf_0.compare(signals[0]) - route_232_1_1_1.remove_vpp_config() - route_232_1_1_2.remove_vpp_config() + route_232_1_1_1.update_entry_flags( + MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE) + route_232_1_1_2.update_entry_flags( + MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE) def test_ip_mcast_signal(self): """ IP Multicast Signal """ @@ -679,11 +682,6 @@ class TestIPMcast(VppTestCase): signals = self.vapi.mfib_signal_dump() self.assertEqual(0, len(signals)) - # - # Cleanup - # - route_232_1_1_1.remove_vpp_config() - def test_ip_mcast_vrf(self): """ IP Multicast Replication in non-default table""" @@ -733,11 +731,14 @@ class TestIPMcast(VppTestCase): "ff01::1", 256, MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE, [VppMRoutePath(self.pg8.sw_if_index, - MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT), + MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT, + proto=DpoProto.DPO_PROTO_IP6), VppMRoutePath(self.pg1.sw_if_index, - MRouteItfFlags.MFIB_ITF_FLAG_FORWARD), + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD, + proto=DpoProto.DPO_PROTO_IP6), VppMRoutePath(self.pg2.sw_if_index, - MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)], + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD, + proto=DpoProto.DPO_PROTO_IP6)], table_id=10, is_ip6=1) route_2001_ff01_1.add_vpp_config() diff --git a/test/vpp_bier.py b/test/vpp_bier.py new file mode 100644 index 00000000000..58c4f7248da --- /dev/null +++ b/test/vpp_bier.py @@ -0,0 +1,267 @@ +""" + BIER Tables and Routes +""" + +import socket +from vpp_object import VppObject + + +class BIER_HDR_PAYLOAD: + BIER_HDR_PROTO_MPLS_DOWN_STREAM = 1 + BIER_HDR_PROTO_MPLS_UP_STREAM = 2 + BIER_HDR_PROTO_ETHERNET = 3 + BIER_HDR_PROTO_IPV4 = 4 + BIER_HDR_PROTO_IPV6 = 5 + BIER_HDR_PROTO_VXLAN = 6 + BIER_HDR_PROTO_CTRL = 7 + BIER_HDR_PROTO_OAM = 8 + + +class VppBierTableID(): + def __init__(self, set_id, sub_domain_id, hdr_len_id): + self.set_id = set_id + self.sub_domain_id = sub_domain_id + self.hdr_len_id = hdr_len_id + + +def find_bier_table(test, bti): + tables = test.vapi.bier_table_dump() + for t in tables: + if bti.set_id == t.bt_tbl_id.bt_set \ + and bti.sub_domain_id == t.bt_tbl_id.bt_sub_domain \ + and bti.hdr_len_id == t.bt_tbl_id.bt_hdr_len_id: + return True + return False + + +def find_bier_route(test, bti, bp): + routes = test.vapi.bier_route_dump(bti) + for r in routes: + if bti.set_id == r.br_tbl_id.bt_set \ + and bti.sub_domain_id == r.br_tbl_id.bt_sub_domain \ + and bti.hdr_len_id == r.br_tbl_id.bt_hdr_len_id \ + and bp == r.br_bp: + return True + return False + + +def find_bier_disp_table(test, bdti): + tables = test.vapi.bier_disp_table_dump() + for t in tables: + if bdti == t.bdt_tbl_id: + return True + return False + + +def find_bier_disp_entry(test, bdti, bp): + entries = test.vapi.bier_disp_entry_dump(bdti) + for e in entries: + if bp == e.bde_bp \ + and bdti == e.bde_tbl_id: + return True + return False + + +def find_bier_imp(test, bti, bp): + imps = test.vapi.bier_imp_dump() + for i in imps: + if bti.set_id == i.bi_tbl_id.bt_set \ + and bti.sub_domain_id == i.bi_tbl_id.bt_sub_domain \ + and bti.hdr_len_id == i.bi_tbl_id.bt_hdr_len_id \ + and bp == i.bi_src: + return True + return False + + +class VppBierTable(VppObject): + """ + BIER Table + """ + + def __init__(self, test, id, mpls_label): + self._test = test + self.id = id + self.mpls_label = mpls_label + + def add_vpp_config(self): + self._test.vapi.bier_table_add_del( + self.id, + self.mpls_label, + is_add=1) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.bier_table_add_del( + self.id, + self.mpls_label, + is_add=0) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "bier-table;[%d:%d:%d]" % (self.id.set_id, + self.id.sub_domain_id, + self.id.hdr_len_id) + + def query_vpp_config(self): + return find_bier_table(self._test, self.id) + + +class VppBierRoute(VppObject): + """ + BIER route + """ + + def __init__(self, test, tbl_id, bp, nh, out_label, + disp_table=0): + self._test = test + self.tbl_id = tbl_id + self.out_label = out_label + self.bp = bp + self.disp_table = disp_table + self.nh = socket.inet_pton(socket.AF_INET, nh) + + def add_vpp_config(self): + self._test.vapi.bier_route_add_del( + self.tbl_id, + self.bp, + self.nh, + self.out_label, + self.disp_table, + is_add=1) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.bier_route_add_del( + self.tbl_id, + self.bp, + self.nh, + self.out_label, + self.disp_table, + is_add=0) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "bier-route;[%d:%d:%d:%d]" % (self.tbl_id.set_id, + self.tbl_id.sub_domain_id, + self.tbl_id.hdr_len_id, + self.bp) + + def query_vpp_config(self): + return find_bier_route(self._test, self.tbl_id, self.bp) + + +class VppBierImp(VppObject): + """ + BIER route + """ + + def __init__(self, test, tbl_id, src, ibytes): + self._test = test + self.tbl_id = tbl_id + self.ibytes = ibytes + self.src = src + + def add_vpp_config(self): + res = self._test.vapi.bier_imp_add( + self.tbl_id, + self.src, + self.ibytes) + self.bi_index = res.bi_index + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.bier_imp_del( + self.bi_index) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "bier-imp;[%d:%d:%d:%d]" % (self.tbl_id.set_id, + self.tbl_id.sub_domain_id, + self.tbl_id.hdr_len_id, + self.src) + + def query_vpp_config(self): + return find_bier_imp(self._test, self.tbl_id, self.src) + + +class VppBierDispTable(VppObject): + """ + BIER Disposition Table + """ + + def __init__(self, test, id): + self._test = test + self.id = id + + def add_vpp_config(self): + self._test.vapi.bier_disp_table_add_del( + self.id, + is_add=1) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.bier_disp_table_add_del( + self.id, + is_add=0) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "bier-disp-table;[%d]" % (self.id) + + def query_vpp_config(self): + return find_bier_disp_table(self._test, self.id) + + +class VppBierDispEntry(VppObject): + """ + BIER Disposition Entry + """ + + def __init__(self, test, tbl_id, bp, payload_proto, nh, nh_tbl, + rpf_id=~0): + self._test = test + self.tbl_id = tbl_id + self.nh_tbl = nh_tbl + self.bp = bp + self.payload_proto = payload_proto + self.rpf_id = rpf_id + self.nh = socket.inet_pton(socket.AF_INET, nh) + + def add_vpp_config(self): + self._test.vapi.bier_disp_entry_add_del( + self.tbl_id, + self.bp, + self.payload_proto, + self.nh, + self.nh_tbl, + self.rpf_id, + is_add=1) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.bier_disp_entry_add_del( + self.tbl_id, + self.bp, + self.payload_proto, + self.nh, + self.nh_tbl, + self.rpf_id, + is_add=0) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "bier-disp-entry;[%d:%d]" % (self.tbl_id, + self.bp) + + def query_vpp_config(self): + return find_bier_disp_entry(self._test, self.tbl_id, self.bp) diff --git a/test/vpp_ip_route.py b/test/vpp_ip_route.py index 34aa5428d18..3bc3b65111c 100644 --- a/test/vpp_ip_route.py +++ b/test/vpp_ip_route.py @@ -34,7 +34,8 @@ class DpoProto: DPO_PROTO_IP6 = 1 DPO_PROTO_MPLS = 2 DPO_PROTO_ETHERNET = 3 - DPO_PROTO_NSH = 4 + DPO_PROTO_BIER = 4 + DPO_PROTO_NSH = 5 def find_route(test, ip_addr, len, table_id=0, inet=AF_INET): @@ -138,10 +139,15 @@ class VppRoutePath(object): class VppMRoutePath(VppRoutePath): - def __init__(self, nh_sw_if_index, flags): - super(VppMRoutePath, self).__init__("0.0.0.0", - nh_sw_if_index) + def __init__(self, nh_sw_if_index, flags, + proto=DpoProto.DPO_PROTO_IP4, + bier_imp=0): + super(VppMRoutePath, self).__init__( + "::" if proto is DpoProto.DPO_PROTO_IP6 else "0.0.0.0", + nh_sw_if_index, + proto=proto) self.nh_i_flags = flags + self.bier_imp = bier_imp class VppIpRoute(VppObject): @@ -283,8 +289,10 @@ class VppIpMRoute(VppObject): self.grp_addr, self.grp_addr_len, self.e_flags, + path.proto, path.nh_itf, path.nh_i_flags, + bier_imp=path.bier_imp, rpf_id=self.rpf_id, table_id=self.table_id, is_ipv6=self.is_ip6) @@ -296,6 +304,7 @@ class VppIpMRoute(VppObject): self.grp_addr, self.grp_addr_len, self.e_flags, + path.proto, path.nh_itf, path.nh_i_flags, table_id=self.table_id, @@ -308,6 +317,7 @@ class VppIpMRoute(VppObject): self.grp_addr, self.grp_addr_len, self.e_flags, + 0, 0xffffffff, 0, table_id=self.table_id, @@ -319,6 +329,7 @@ class VppIpMRoute(VppObject): self.grp_addr, self.grp_addr_len, self.e_flags, + 0, 0xffffffff, 0, rpf_id=self.rpf_id, @@ -334,16 +345,21 @@ class VppIpMRoute(VppObject): self.grp_addr, self.grp_addr_len, self.e_flags, + path.proto, path.nh_itf, path.nh_i_flags, table_id=self.table_id, is_ipv6=self.is_ip6) def query_vpp_config(self): - dump = self._test.vapi.ip_fib_dump() + if self.is_ip6: + dump = self._test.vapi.ip6_mfib_dump() + else: + dump = self._test.vapi.ip_mfib_dump() for e in dump: - if self.grp_addr == e.address \ + if self.grp_addr == e.grp_address \ and self.grp_addr_len == e.address_length \ + and self.src_addr == e.src_address \ and self.table_id == e.table_id: return True return False diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 5085ff2801e..893ef0d89b9 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -2057,14 +2057,17 @@ class VppPapiProvider(object): grp_address, grp_address_length, e_flags, + next_hop_afi, next_hop_sw_if_index, i_flags, + bier_imp=0, rpf_id=0, table_id=0, is_add=1, is_ipv6=0, is_local=0): """ + IP Multicast Route add/del """ return self.api( self.papi.ip_mroute_add_del, @@ -2076,6 +2079,8 @@ class VppPapiProvider(object): 'is_add': is_add, 'is_ipv6': is_ipv6, 'is_local': is_local, + 'bier_imp': bier_imp, + 'next_hop_afi': next_hop_afi, 'grp_address_length': grp_address_length, 'grp_address': grp_address, 'src_address': src_address}) @@ -2086,6 +2091,9 @@ class VppPapiProvider(object): def ip_mfib_dump(self): return self.api(self.papi.ip_mfib_dump, {}) + def ip6_mfib_dump(self): + return self.api(self.papi.ip6_mfib_dump, {}) + def lisp_enable_disable(self, is_enabled): return self.api( self.papi.lisp_enable_disable, @@ -2634,3 +2642,114 @@ class VppPapiProvider(object): 'nh': nh, 'is_add': is_add, 'is_ip6': is_ip6}) + + def bier_table_add_del(self, + bti, + mpls_label, + is_add=1): + """ BIER Table add/del """ + return self.api( + self.papi.bier_table_add_del, + {'bt_tbl_id': {"bt_set": bti.set_id, + "bt_sub_domain": bti.sub_domain_id, + "bt_hdr_len_id": bti.hdr_len_id}, + 'bt_label': mpls_label, + 'bt_is_add': is_add}) + + def bier_table_dump(self): + return self.api(self.papi.bier_table_dump, {}) + + def bier_route_add_del(self, + bti, + bp, + next_hop, + next_hop_label, + next_hop_table_id, + next_hop_is_ip4=1, + is_add=1): + """ BIER Route add/del """ + return self.api( + self.papi.bier_route_add_del, + {'br_tbl_id': {"bt_set": bti.set_id, + "bt_sub_domain": bti.sub_domain_id, + "bt_hdr_len_id": bti.hdr_len_id}, + 'br_bp': bp, + 'br_n_paths': 1, + 'br_paths': [{'next_hop': next_hop, + 'afi': 0, + 'n_labels': 1, + 'table_id': next_hop_table_id, + 'label_stack': [next_hop_label]}], + 'br_is_add': is_add}) + + def bier_route_dump(self, bti): + return self.api( + self.papi.bier_route_dump, + {'br_tbl_id': {"bt_set": bti.set_id, + "bt_sub_domain": bti.sub_domain_id, + "bt_hdr_len_id": bti.hdr_len_id}}) + + def bier_imp_add(self, + bti, + src, + ibytes, + is_add=1): + """ BIER Imposition Add """ + return self.api( + self.papi.bier_imp_add, + {'bi_tbl_id': {"bt_set": bti.set_id, + "bt_sub_domain": bti.sub_domain_id, + "bt_hdr_len_id": bti.hdr_len_id}, + 'bi_src': src, + 'bi_n_bytes': len(ibytes), + 'bi_bytes': ibytes}) + + def bier_imp_del(self, bi_index): + """ BIER Imposition del """ + return self.api( + self.papi.bier_imp_del, + {'bi_index': bi_index}) + + def bier_imp_dump(self): + return self.api(self.papi.bier_imp_dump, {}) + + def bier_disp_table_add_del(self, + bdti, + is_add=1): + """ BIER Disposition Table add/del """ + return self.api( + self.papi.bier_disp_table_add_del, + {'bdt_tbl_id': bdti, + 'bdt_is_add': is_add}) + + def bier_disp_table_dump(self): + return self.api(self.papi.bier_disp_table_dump, {}) + + def bier_disp_entry_add_del(self, + bdti, + bp, + payload_proto, + next_hop, + next_hop_tbl_id=0, + next_hop_rpf_id=~0, + next_hop_is_ip4=1, + is_add=1): + """ BIER Route add/del """ + return self.api( + self.papi.bier_disp_entry_add_del, + {'bde_tbl_id': bdti, + 'bde_bp': bp, + 'bde_payload_proto': payload_proto, + 'bde_n_paths': 1, + 'bde_paths': [{'next_hop': next_hop, + 'table_id': next_hop_tbl_id, + 'afi': 0, + 'rpf_id': next_hop_rpf_id, + 'n_labels': 0, + 'label_stack': [0]}], + 'bde_is_add': is_add}) + + def bier_disp_entry_dump(self, bdti): + return self.api( + self.papi.bier_disp_entry_dump, + {'bde_tbl_id': bdti}) |