diff options
Diffstat (limited to 'src/plugins/lisp/lisp-gpe/lisp_gpe_fwd_entry.c')
-rw-r--r-- | src/plugins/lisp/lisp-gpe/lisp_gpe_fwd_entry.c | 1595 |
1 files changed, 1595 insertions, 0 deletions
diff --git a/src/plugins/lisp/lisp-gpe/lisp_gpe_fwd_entry.c b/src/plugins/lisp/lisp-gpe/lisp_gpe_fwd_entry.c new file mode 100644 index 00000000000..334164c2583 --- /dev/null +++ b/src/plugins/lisp/lisp-gpe/lisp_gpe_fwd_entry.c @@ -0,0 +1,1595 @@ +/* + * 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 <lisp/lisp-gpe/lisp_gpe_fwd_entry.h> +#include <lisp/lisp-gpe/lisp_gpe_adjacency.h> +#include <lisp/lisp-gpe/lisp_gpe_tenant.h> +#include <lisp/lisp-cp/lisp_cp_dpo.h> +#include <vnet/fib/fib_table.h> +#include <vnet/fib/fib_entry.h> +#include <vnet/fib/fib_path_list.h> +#include <vnet/fib/ip6_fib.h> +#include <vnet/fib/ip4_fib.h> +#include <vnet/dpo/drop_dpo.h> +#include <vnet/dpo/lookup_dpo.h> +#include <vnet/dpo/load_balance.h> +#include <vnet/adj/adj_midchain.h> + +/** + * @brief Add route to IP4 or IP6 Destination FIB. + * + * Add a route to the destination FIB that results in the lookup + * in the SRC FIB. The SRC FIB is created is it does not yet exist. + * + * @param[in] dst_table_id Destination FIB Table-ID + * @param[in] dst_prefix Destination IP prefix. + * + * @return src_fib_index The index/ID of the SRC FIB created. + */ +static u32 +ip_dst_fib_add_route (u32 dst_fib_index, const ip_prefix_t * dst_prefix) +{ + fib_node_index_t src_fib_index; + fib_prefix_t dst_fib_prefix; + fib_node_index_t dst_fei; + + ASSERT (NULL != dst_prefix); + + ip_prefix_to_fib_prefix (dst_prefix, &dst_fib_prefix); + + /* + * lookup the destination prefix in the VRF table and retrieve the + * LISP associated data + */ + dst_fei = fib_table_lookup_exact_match (dst_fib_index, &dst_fib_prefix); + + /* + * If the FIB entry is not present, or not LISP sourced, add it + */ + if (dst_fei == FIB_NODE_INDEX_INVALID || + NULL == fib_entry_get_source_data (dst_fei, FIB_SOURCE_LISP)) + { + dpo_id_t src_lkup_dpo = DPO_INVALID; + + /* create a new src FIB. */ + src_fib_index = + fib_table_create_and_lock (dst_fib_prefix.fp_proto, + FIB_SOURCE_LISP, + "LISP-src for [%d,%U]", + dst_fib_index, + format_fib_prefix, &dst_fib_prefix); + /* + * add src fib default route + */ + fib_prefix_t prefix = { + .fp_proto = dst_fib_prefix.fp_proto, + }; + fib_table_entry_special_dpo_add (src_fib_index, &prefix, + FIB_SOURCE_LISP, + FIB_ENTRY_FLAG_EXCLUSIVE, + lisp_cp_dpo_get (fib_proto_to_dpo + (dst_fib_prefix.fp_proto))); + /* + * create a data-path object to perform the source address lookup + * in the SRC FIB + */ + lookup_dpo_add_or_lock_w_fib_index (src_fib_index, + (ip_prefix_version (dst_prefix) == + AF_IP6 ? DPO_PROTO_IP6 : + DPO_PROTO_IP4), + LOOKUP_UNICAST, + LOOKUP_INPUT_SRC_ADDR, + LOOKUP_TABLE_FROM_CONFIG, + &src_lkup_dpo); + + /* + * add the entry to the destination FIB that uses the lookup DPO + */ + dst_fei = fib_table_entry_special_dpo_add (dst_fib_index, + &dst_fib_prefix, + FIB_SOURCE_LISP, + FIB_ENTRY_FLAG_EXCLUSIVE, + &src_lkup_dpo); + + /* + * the DPO is locked by the FIB entry, and we have no further + * need for it. + */ + dpo_unlock (&src_lkup_dpo); + + /* + * save the SRC FIB index on the entry so we can retrieve it for + * subsequent routes. + */ + fib_entry_set_source_data (dst_fei, FIB_SOURCE_LISP, &src_fib_index); + } + else + { + /* + * destination FIB entry already present + */ + src_fib_index = *(u32 *) fib_entry_get_source_data (dst_fei, + FIB_SOURCE_LISP); + } + + return (src_fib_index); +} + +/** + * @brief Del route to IP4 or IP6 SD FIB. + * + * Remove routes from both destination and source FIBs. + * + * @param[in] src_fib_index The index/ID of the SRC FIB + * @param[in] src_prefix Source IP prefix. + * @param[in] dst_fib_index The index/ID of the DST FIB + * @param[in] dst_prefix Destination IP prefix. + */ +static void +ip_src_dst_fib_del_route (u32 src_fib_index, + const ip_prefix_t * src_prefix, + u32 dst_fib_index, const ip_prefix_t * dst_prefix) +{ + fib_prefix_t dst_fib_prefix, src_fib_prefix; + u8 have_default = 0; + u32 n_entries; + + ASSERT (NULL != dst_prefix); + ASSERT (NULL != src_prefix); + + ip_prefix_to_fib_prefix (dst_prefix, &dst_fib_prefix); + ip_prefix_to_fib_prefix (src_prefix, &src_fib_prefix); + + fib_table_entry_delete (src_fib_index, &src_fib_prefix, FIB_SOURCE_LISP); + + /* check if only default left or empty */ + fib_prefix_t default_pref = { + .fp_proto = dst_fib_prefix.fp_proto + }; + + if (fib_table_lookup_exact_match (src_fib_index, + &default_pref) != FIB_NODE_INDEX_INVALID) + have_default = 1; + + n_entries = fib_table_get_num_entries (src_fib_index, + src_fib_prefix.fp_proto, + FIB_SOURCE_LISP); + if (n_entries == 0 || (have_default && n_entries == 1)) + { + /* + * remove src FIB default route + */ + if (have_default) + fib_table_entry_special_remove (src_fib_index, &default_pref, + FIB_SOURCE_LISP); + + /* + * there's nothing left now, unlock the source FIB and the + * destination route + */ + fib_table_entry_special_remove (dst_fib_index, + &dst_fib_prefix, FIB_SOURCE_LISP); + fib_table_unlock (src_fib_index, src_fib_prefix.fp_proto, + FIB_SOURCE_LISP); + } +} + +/** + * @brief Add route to IP4 or IP6 SRC FIB. + * + * Adds a route to in the LISP SRC FIB with the result of the route + * being the DPO passed. + * + * @param[in] src_fib_index The index/ID of the SRC FIB + * @param[in] src_prefix Source IP prefix. + * @param[in] src_dpo The DPO the route will link to. + * + * @return fib index of the inserted prefix + */ +static fib_node_index_t +ip_src_fib_add_route_w_dpo (u32 src_fib_index, + const ip_prefix_t * src_prefix, + const dpo_id_t * src_dpo) +{ + fib_node_index_t fei = ~0; + fib_prefix_t src_fib_prefix; + + ip_prefix_to_fib_prefix (src_prefix, &src_fib_prefix); + + /* + * add the entry into the source fib. + */ + fib_node_index_t src_fei; + + src_fei = fib_table_lookup_exact_match (src_fib_index, &src_fib_prefix); + + if (FIB_NODE_INDEX_INVALID == src_fei || + !fib_entry_is_sourced (src_fei, FIB_SOURCE_LISP)) + { + fei = fib_table_entry_special_dpo_add (src_fib_index, + &src_fib_prefix, + FIB_SOURCE_LISP, + FIB_ENTRY_FLAG_EXCLUSIVE, + src_dpo); + } + return fei; +} + +static fib_route_path_t * +lisp_gpe_mk_fib_paths (const lisp_fwd_path_t * paths) +{ + const lisp_gpe_adjacency_t *ladj; + fib_route_path_t *rpaths = NULL; + fib_protocol_t fp; + u8 best_priority; + u32 ii; + + vec_validate (rpaths, vec_len (paths) - 1); + + best_priority = paths[0].priority; + + vec_foreach_index (ii, paths) + { + if (paths[0].priority != best_priority) + break; + + ladj = lisp_gpe_adjacency_get (paths[ii].lisp_adj); + + fp = ip_address_to_46 (&ladj->remote_rloc, &rpaths[ii].frp_addr); + + rpaths[ii].frp_proto = fib_proto_to_dpo (fp); + rpaths[ii].frp_sw_if_index = ladj->sw_if_index; + rpaths[ii].frp_weight = (paths[ii].weight ? paths[ii].weight : 1); + } + + ASSERT (0 != vec_len (rpaths)); + + return (rpaths); +} + +/** + * @brief Add route to IP4 or IP6 SRC FIB. + * + * Adds a route to in the LISP SRC FIB for the tunnel. + * + * @param[in] src_fib_index The index/ID of the SRC FIB + * @param[in] src_prefix Source IP prefix. + * @param[in] paths The paths from which to construct the + * load balance + */ +static fib_node_index_t +ip_src_fib_add_route (u32 src_fib_index, + const ip_prefix_t * src_prefix, + const lisp_fwd_path_t * paths) +{ + fib_prefix_t src_fib_prefix; + fib_route_path_t *rpaths; + + ip_prefix_to_fib_prefix (src_prefix, &src_fib_prefix); + + rpaths = lisp_gpe_mk_fib_paths (paths); + + fib_node_index_t fib_entry_index = + fib_table_entry_update (src_fib_index, &src_fib_prefix, FIB_SOURCE_LISP, + FIB_ENTRY_FLAG_NONE, rpaths); + vec_free (rpaths); + return fib_entry_index; +} + +static void +gpe_native_fwd_add_del_lfe (lisp_gpe_fwd_entry_t * lfe, u8 is_add) +{ + lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main (); + u8 found = 0, ip_version; + u32 *lfei, new_lfei; + ip_version = ip_prefix_version (&lfe->key->rmt.ippref); + + new_lfei = lfe - lgm->lisp_fwd_entry_pool; + vec_foreach (lfei, lgm->native_fwd_lfes[ip_version]) + { + lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, lfei[0]); + if (lfei[0] == new_lfei) + { + found = 1; + break; + } + } + + if (is_add) + { + if (!found) + vec_add1 (lgm->native_fwd_lfes[ip_version], new_lfei); + } + else + { + if (found) + vec_del1 (lgm->native_fwd_lfes[ip_version], lfei[0]); + } +} + +static index_t +create_fib_entries (lisp_gpe_fwd_entry_t * lfe) +{ + fib_node_index_t fi; + fib_entry_t *fe; + lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main (); + dpo_proto_t dproto; + ip_prefix_t ippref; + fib_prefix_t fib_prefix; + u8 ip_version = ip_prefix_version (&lfe->key->rmt.ippref); + dproto = (ip_version == AF_IP4 ? DPO_PROTO_IP4 : DPO_PROTO_IP6); + + if (lfe->is_src_dst) + { + lfe->src_fib_index = ip_dst_fib_add_route (lfe->eid_fib_index, + &lfe->key->rmt.ippref); + memcpy (&ippref, &lfe->key->lcl.ippref, sizeof (ippref)); + } + else + { + lfe->src_fib_index = lfe->eid_fib_index; + memcpy (&ippref, &lfe->key->rmt.ippref, sizeof (ippref)); + } + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE == lfe->type) + { + dpo_id_t dpo = DPO_INVALID; + + switch (lfe->action) + { + case LISP_FORWARD_NATIVE: + /* TODO handle route overlaps with fib and default route */ + if (vec_len (lgm->native_fwd_rpath[ip_version])) + { + ip_prefix_to_fib_prefix (&lfe->key->rmt.ippref, &fib_prefix); + fi = fib_table_entry_update (lfe->eid_fib_index, &fib_prefix, + FIB_SOURCE_LISP, + FIB_ENTRY_FLAG_NONE, + lgm->native_fwd_rpath[ip_version]); + gpe_native_fwd_add_del_lfe (lfe, 1); + goto done; + } + case LISP_NO_ACTION: + /* TODO update timers? */ + case LISP_SEND_MAP_REQUEST: + /* insert tunnel that always sends map-request */ + dpo_copy (&dpo, lisp_cp_dpo_get (dproto)); + break; + case LISP_DROP: + /* for drop fwd entries, just add route, no need to add encap tunnel */ + dpo_copy (&dpo, drop_dpo_get (dproto)); + break; + } + fi = ip_src_fib_add_route_w_dpo (lfe->src_fib_index, &ippref, &dpo); + dpo_reset (&dpo); + } + else + { + fi = ip_src_fib_add_route (lfe->src_fib_index, &ippref, lfe->paths); + } +done: + fe = fib_entry_get (fi); + return fe->fe_lb.dpoi_index; +} + +static void +delete_fib_entries (lisp_gpe_fwd_entry_t * lfe) +{ + fib_prefix_t dst_fib_prefix; + + if (lfe->is_src_dst) + ip_src_dst_fib_del_route (lfe->src_fib_index, + &lfe->key->lcl.ippref, + lfe->eid_fib_index, &lfe->key->rmt.ippref); + else + { + ip_prefix_to_fib_prefix (&lfe->key->rmt.ippref, &dst_fib_prefix); + fib_table_entry_delete (lfe->src_fib_index, &dst_fib_prefix, + FIB_SOURCE_LISP); + gpe_native_fwd_add_del_lfe (lfe, 0); + } +} + +static lisp_gpe_fwd_entry_t * +find_fwd_entry (lisp_gpe_main_t * lgm, + vnet_lisp_gpe_add_del_fwd_entry_args_t * a, + lisp_gpe_fwd_entry_key_t * key) +{ + uword *p; + + clib_memset (key, 0, sizeof (*key)); + + if (GID_ADDR_IP_PREFIX == gid_address_type (&a->rmt_eid)) + { + /* + * the ip version of the source is not set to ip6 when the + * source is all zeros. force it. + */ + ip_prefix_version (&gid_address_ippref (&a->lcl_eid)) = + ip_prefix_version (&gid_address_ippref (&a->rmt_eid)); + } + + gid_to_dp_address (&a->rmt_eid, &key->rmt); + gid_to_dp_address (&a->lcl_eid, &key->lcl); + key->vni = a->vni; + + p = hash_get_mem (lgm->lisp_gpe_fwd_entries, key); + + if (NULL != p) + { + return (pool_elt_at_index (lgm->lisp_fwd_entry_pool, p[0])); + } + return (NULL); +} + +static int +lisp_gpe_fwd_entry_path_sort (void *a1, void *a2) +{ + lisp_fwd_path_t *p1 = a1, *p2 = a2; + + return (p1->priority - p2->priority); +} + +static void +lisp_gpe_fwd_entry_mk_paths (lisp_gpe_fwd_entry_t * lfe, + vnet_lisp_gpe_add_del_fwd_entry_args_t * a) +{ + lisp_fwd_path_t *path; + u32 index; + + vec_validate (lfe->paths, vec_len (a->locator_pairs) - 1); + + vec_foreach_index (index, a->locator_pairs) + { + path = &lfe->paths[index]; + + path->priority = a->locator_pairs[index].priority; + path->weight = a->locator_pairs[index].weight; + + path->lisp_adj = + lisp_gpe_adjacency_find_or_create_and_lock (&a->locator_pairs + [index], + a->dp_table, lfe->key->vni); + } + vec_sort_with_function (lfe->paths, lisp_gpe_fwd_entry_path_sort); +} + +void +vnet_lisp_gpe_add_fwd_counters (vnet_lisp_gpe_add_del_fwd_entry_args_t * a, + u32 fwd_entry_index) +{ + const lisp_gpe_adjacency_t *ladj; + lisp_fwd_path_t *path; + lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main (); + u8 *placeholder_elt; + lisp_gpe_fwd_entry_t *lfe; + lisp_gpe_fwd_entry_key_t fe_key; + lisp_stats_key_t key; + + lfe = find_fwd_entry (lgm, a, &fe_key); + + if (!lfe) + return; + + if (LISP_GPE_FWD_ENTRY_TYPE_NORMAL != lfe->type) + return; + + clib_memset (&key, 0, sizeof (key)); + key.fwd_entry_index = fwd_entry_index; + + vec_foreach (path, lfe->paths) + { + ladj = lisp_gpe_adjacency_get (path->lisp_adj); + key.tunnel_index = ladj->tunnel_index; + lisp_stats_key_t *key_copy = clib_mem_alloc (sizeof (*key_copy)); + memcpy (key_copy, &key, sizeof (*key_copy)); + pool_get (lgm->placeholder_stats_pool, placeholder_elt); + hash_set_mem (lgm->lisp_stats_index_by_key, key_copy, + placeholder_elt - lgm->placeholder_stats_pool); + + vlib_validate_combined_counter (&lgm->counters, + placeholder_elt - + lgm->placeholder_stats_pool); + vlib_zero_combined_counter (&lgm->counters, + placeholder_elt - + lgm->placeholder_stats_pool); + } +} + +/** + * @brief Add/Delete LISP IP forwarding entry. + * + * creation of forwarding entries for IP LISP overlay: + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] a Parameters for building the forwarding entry. + * + * @return 0 on success. + */ +static int +add_ip_fwd_entry (lisp_gpe_main_t * lgm, + vnet_lisp_gpe_add_del_fwd_entry_args_t * a) +{ + lisp_gpe_fwd_entry_key_t key; + lisp_gpe_fwd_entry_t *lfe; + fib_protocol_t fproto; + + lfe = find_fwd_entry (lgm, a, &key); + + if (NULL != lfe) + /* don't support updates */ + return VNET_API_ERROR_INVALID_VALUE; + + pool_get (lgm->lisp_fwd_entry_pool, lfe); + clib_memset (lfe, 0, sizeof (*lfe)); + lfe->key = clib_mem_alloc (sizeof (key)); + memcpy (lfe->key, &key, sizeof (key)); + + hash_set_mem (lgm->lisp_gpe_fwd_entries, lfe->key, + lfe - lgm->lisp_fwd_entry_pool); + a->fwd_entry_index = lfe - lgm->lisp_fwd_entry_pool; + + fproto = (AF_IP4 == ip_prefix_version (&fid_addr_ippref (&lfe->key->rmt)) ? + FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6); + + lfe->type = (a->is_negative ? + LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE : + LISP_GPE_FWD_ENTRY_TYPE_NORMAL); + lfe->tenant = lisp_gpe_tenant_find_or_create (lfe->key->vni); + lfe->eid_table_id = a->table_id; + lfe->eid_fib_index = fib_table_find_or_create_and_lock (fproto, + lfe->eid_table_id, + FIB_SOURCE_LISP); + lfe->is_src_dst = a->is_src_dst; + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) + { + lisp_gpe_fwd_entry_mk_paths (lfe, a); + } + else + { + lfe->action = a->action; + } + + lfe->dpoi_index = create_fib_entries (lfe); + return (0); +} + +static void +del_ip_fwd_entry_i (lisp_gpe_main_t * lgm, lisp_gpe_fwd_entry_t * lfe) +{ + lisp_fwd_path_t *path; + fib_protocol_t fproto; + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) + { + vec_foreach (path, lfe->paths) + { + lisp_gpe_adjacency_unlock (path->lisp_adj); + } + } + + delete_fib_entries (lfe); + + fproto = (AF_IP4 == ip_prefix_version (&fid_addr_ippref (&lfe->key->rmt)) ? + FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6); + fib_table_unlock (lfe->eid_fib_index, fproto, FIB_SOURCE_LISP); + + hash_unset_mem (lgm->lisp_gpe_fwd_entries, lfe->key); + clib_mem_free (lfe->key); + pool_put (lgm->lisp_fwd_entry_pool, lfe); +} + +/** + * @brief Add/Delete LISP IP forwarding entry. + * + * removal of forwarding entries for IP LISP overlay: + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] a Parameters for building the forwarding entry. + * + * @return 0 on success. + */ +static int +del_ip_fwd_entry (lisp_gpe_main_t * lgm, + vnet_lisp_gpe_add_del_fwd_entry_args_t * a) +{ + lisp_gpe_fwd_entry_key_t key; + lisp_gpe_fwd_entry_t *lfe; + + lfe = find_fwd_entry (lgm, a, &key); + + if (NULL == lfe) + /* no such entry */ + return VNET_API_ERROR_INVALID_VALUE; + + del_ip_fwd_entry_i (lgm, lfe); + + return (0); +} + +static void +make_mac_fib_key (BVT (clib_bihash_kv) * kv, u16 bd_index, u8 src_mac[6], + u8 dst_mac[6]) +{ + kv->key[0] = (((u64) bd_index) << 48) | mac_to_u64 (dst_mac); + kv->key[1] = mac_to_u64 (src_mac); + kv->key[2] = 0; +} + +/** + * @brief Lookup L2 SD FIB entry + * + * Does a vni + dest + source lookup in the L2 LISP FIB. If the lookup fails + * it tries a second time with source set to 0 (i.e., a simple dest lookup). + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] bd_index Bridge domain index. + * @param[in] src_mac Source mac address. + * @param[in] dst_mac Destination mac address. + * + * @return index of mapping matching the lookup key. + */ +index_t +lisp_l2_fib_lookup (lisp_gpe_main_t * lgm, u16 bd_index, u8 src_mac[6], + u8 dst_mac[6]) +{ + int rv; + BVT (clib_bihash_kv) kv, value; + + make_mac_fib_key (&kv, bd_index, src_mac, dst_mac); + rv = BV (clib_bihash_search_inline_2) (&lgm->l2_fib, &kv, &value); + + /* no match, try with src 0, catch all for dst */ + if (rv != 0) + { + kv.key[1] = 0; + rv = BV (clib_bihash_search_inline_2) (&lgm->l2_fib, &kv, &value); + if (rv == 0) + return value.value; + } + else + return value.value; + + return lisp_gpe_main.l2_lb_cp_lkup.dpoi_index; +} + +/** + * @brief Add/del L2 SD FIB entry + * + * Inserts value in L2 FIB keyed by vni + dest + source. If entry is + * overwritten the associated value is returned. + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] bd_index Bridge domain index. + * @param[in] src_mac Source mac address. + * @param[in] dst_mac Destination mac address. + * @param[in] val Value to add. + * @param[in] is_add Add/del flag. + * + * @return ~0 or value of overwritten entry. + */ +static u32 +lisp_l2_fib_add_del_entry (u16 bd_index, u8 src_mac[6], + u8 dst_mac[6], const dpo_id_t * dpo, u8 is_add) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + BVT (clib_bihash_kv) kv, value; + u32 old_val = ~0; + + make_mac_fib_key (&kv, bd_index, src_mac, dst_mac); + + if (BV (clib_bihash_search) (&lgm->l2_fib, &kv, &value) == 0) + old_val = value.value; + + if (!is_add) + BV (clib_bihash_add_del) (&lgm->l2_fib, &kv, 0 /* is_add */ ); + else + { + kv.value = dpo->dpoi_index; + BV (clib_bihash_add_del) (&lgm->l2_fib, &kv, 1 /* is_add */ ); + } + return old_val; +} + +#define L2_FIB_DEFAULT_HASH_NUM_BUCKETS (64 * 1024) +#define L2_FIB_DEFAULT_HASH_MEMORY_SIZE (32<<20) + +static void +l2_fib_init (lisp_gpe_main_t * lgm) +{ + index_t lbi; + + BV (clib_bihash_init) (&lgm->l2_fib, "l2 fib", + 1 << max_log2 (L2_FIB_DEFAULT_HASH_NUM_BUCKETS), + L2_FIB_DEFAULT_HASH_MEMORY_SIZE); + + /* + * the result from a 'miss' in a L2 Table + */ + lbi = load_balance_create (1, DPO_PROTO_ETHERNET, 0); + load_balance_set_bucket (lbi, 0, lisp_cp_dpo_get (DPO_PROTO_ETHERNET)); + + dpo_set (&lgm->l2_lb_cp_lkup, DPO_LOAD_BALANCE, DPO_PROTO_ETHERNET, lbi); +} + +static void +del_l2_fwd_entry_i (lisp_gpe_main_t * lgm, lisp_gpe_fwd_entry_t * lfe) +{ + lisp_fwd_path_t *path; + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) + { + vec_foreach (path, lfe->paths) + { + lisp_gpe_adjacency_unlock (path->lisp_adj); + } + fib_path_list_child_remove (lfe->l2.path_list_index, + lfe->l2.child_index); + } + + lisp_l2_fib_add_del_entry (lfe->l2.eid_bd_index, + fid_addr_mac (&lfe->key->lcl), + fid_addr_mac (&lfe->key->rmt), NULL, 0); + + hash_unset_mem (lgm->lisp_gpe_fwd_entries, lfe->key); + clib_mem_free (lfe->key); + pool_put (lgm->lisp_fwd_entry_pool, lfe); +} + +/** + * @brief Delete LISP L2 forwarding entry. + * + * Coordinates the removal of forwarding entries for L2 LISP overlay: + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] a Parameters for building the forwarding entry. + * + * @return 0 on success. + */ +static int +del_l2_fwd_entry (lisp_gpe_main_t * lgm, + vnet_lisp_gpe_add_del_fwd_entry_args_t * a) +{ + lisp_gpe_fwd_entry_key_t key; + lisp_gpe_fwd_entry_t *lfe; + + lfe = find_fwd_entry (lgm, a, &key); + + if (NULL == lfe) + return VNET_API_ERROR_INVALID_VALUE; + + del_l2_fwd_entry_i (lgm, lfe); + + return (0); +} + +/** + * @brief Construct and insert the forwarding information used by an L2 entry + */ +static void +lisp_gpe_l2_update_fwding (lisp_gpe_fwd_entry_t * lfe) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + dpo_id_t dpo = DPO_INVALID; + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) + { + fib_path_list_contribute_forwarding (lfe->l2.path_list_index, + FIB_FORW_CHAIN_TYPE_ETHERNET, + FIB_PATH_LIST_FWD_FLAG_NONE, + &lfe->l2.dpo); + dpo_copy (&dpo, &lfe->l2.dpo); + } + else + { + switch (lfe->action) + { + case SEND_MAP_REQUEST: + dpo_copy (&dpo, &lgm->l2_lb_cp_lkup); + break; + case NO_ACTION: + case FORWARD_NATIVE: + case DROP: + dpo_copy (&dpo, drop_dpo_get (DPO_PROTO_ETHERNET)); + } + } + + /* add entry to l2 lisp fib */ + lisp_l2_fib_add_del_entry (lfe->l2.eid_bd_index, + fid_addr_mac (&lfe->key->lcl), + fid_addr_mac (&lfe->key->rmt), &dpo, 1); + lfe->dpoi_index = dpo.dpoi_index; + + dpo_reset (&dpo); +} + +/** + * @brief Add LISP L2 forwarding entry. + * + * Coordinates the creation of forwarding entries for L2 LISP overlay: + * creates lisp-gpe tunnel and injects new entry in Source/Dest L2 FIB. + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] a Parameters for building the forwarding entry. + * + * @return 0 on success. + */ +static int +add_l2_fwd_entry (lisp_gpe_main_t * lgm, + vnet_lisp_gpe_add_del_fwd_entry_args_t * a) +{ + lisp_gpe_fwd_entry_key_t key; + bd_main_t *bdm = &bd_main; + lisp_gpe_fwd_entry_t *lfe; + uword *bd_indexp; + + bd_indexp = hash_get (bdm->bd_index_by_bd_id, a->bd_id); + if (!bd_indexp) + { + clib_warning ("bridge domain %d doesn't exist", a->bd_id); + return -1; + } + + lfe = find_fwd_entry (lgm, a, &key); + + if (NULL != lfe) + /* don't support updates */ + return VNET_API_ERROR_INVALID_VALUE; + + pool_get (lgm->lisp_fwd_entry_pool, lfe); + clib_memset (lfe, 0, sizeof (*lfe)); + lfe->key = clib_mem_alloc (sizeof (key)); + memcpy (lfe->key, &key, sizeof (key)); + + hash_set_mem (lgm->lisp_gpe_fwd_entries, lfe->key, + lfe - lgm->lisp_fwd_entry_pool); + a->fwd_entry_index = lfe - lgm->lisp_fwd_entry_pool; + + lfe->type = (a->is_negative ? + LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE : + LISP_GPE_FWD_ENTRY_TYPE_NORMAL); + lfe->l2.eid_bd_id = a->bd_id; + lfe->l2.eid_bd_index = bd_indexp[0]; + lfe->tenant = lisp_gpe_tenant_find_or_create (lfe->key->vni); + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) + { + fib_route_path_t *rpaths; + + /* + * Make the sorted array of LISP paths with their resp. adjacency + */ + lisp_gpe_fwd_entry_mk_paths (lfe, a); + + /* + * From the LISP paths, construct a FIB path list that will + * contribute a load-balance. + */ + rpaths = lisp_gpe_mk_fib_paths (lfe->paths); + + lfe->l2.path_list_index = + fib_path_list_create (FIB_PATH_LIST_FLAG_NONE, rpaths); + + /* + * become a child of the path-list so we receive updates when + * its forwarding state changes. this includes an implicit lock. + */ + lfe->l2.child_index = + fib_path_list_child_add (lfe->l2.path_list_index, + FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY, + lfe - lgm->lisp_fwd_entry_pool); + } + else + { + lfe->action = a->action; + } + + lisp_gpe_l2_update_fwding (lfe); + + return 0; +} + +/** + * @brief Lookup NSH SD FIB entry + * + * Does an SPI+SI lookup in the NSH LISP FIB. + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] spi_si SPI + SI. + * + * @return next node index. + */ +const dpo_id_t * +lisp_nsh_fib_lookup (lisp_gpe_main_t * lgm, u32 spi_si_net_order) +{ + int rv; + BVT (clib_bihash_kv) kv, value; + + clib_memset (&kv, 0, sizeof (kv)); + kv.key[0] = spi_si_net_order; + rv = BV (clib_bihash_search_inline_2) (&lgm->nsh_fib, &kv, &value); + + if (rv != 0) + { + return lgm->nsh_cp_lkup; + } + else + { + lisp_gpe_fwd_entry_t *lfe; + lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, value.value); + return &lfe->nsh.choice; + } +} + +/** + * @brief Add/del NSH FIB entry + * + * Inserts value in NSH FIB keyed by SPI+SI. If entry is + * overwritten the associated value is returned. + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] spi_si SPI + SI. + * @param[in] dpo Load balanced mapped to SPI + SI + * + * @return ~0 or value of overwritten entry. + */ +static u32 +lisp_nsh_fib_add_del_entry (u32 spi_si_host_order, u32 lfei, u8 is_add) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + BVT (clib_bihash_kv) kv, value; + u32 old_val = ~0; + + clib_memset (&kv, 0, sizeof (kv)); + kv.key[0] = clib_host_to_net_u32 (spi_si_host_order); + kv.value = 0ULL; + + if (BV (clib_bihash_search) (&lgm->nsh_fib, &kv, &value) == 0) + old_val = value.value; + + if (!is_add) + BV (clib_bihash_add_del) (&lgm->nsh_fib, &kv, 0 /* is_add */ ); + else + { + kv.value = lfei; + BV (clib_bihash_add_del) (&lgm->nsh_fib, &kv, 1 /* is_add */ ); + } + return old_val; +} + +#define NSH_FIB_DEFAULT_HASH_NUM_BUCKETS (64 * 1024) +#define NSH_FIB_DEFAULT_HASH_MEMORY_SIZE (32<<20) + +static void +nsh_fib_init (lisp_gpe_main_t * lgm) +{ + BV (clib_bihash_init) (&lgm->nsh_fib, "nsh fib", + 1 << max_log2 (NSH_FIB_DEFAULT_HASH_NUM_BUCKETS), + NSH_FIB_DEFAULT_HASH_MEMORY_SIZE); + + /* + * the result from a 'miss' in a NSH Table + */ + lgm->nsh_cp_lkup = lisp_cp_dpo_get (DPO_PROTO_NSH); +} + +static void +del_nsh_fwd_entry_i (lisp_gpe_main_t * lgm, lisp_gpe_fwd_entry_t * lfe) +{ + lisp_fwd_path_t *path; + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) + { + vec_foreach (path, lfe->paths) + { + lisp_gpe_adjacency_unlock (path->lisp_adj); + } + fib_path_list_child_remove (lfe->nsh.path_list_index, + lfe->nsh.child_index); + dpo_reset (&lfe->nsh.choice); + } + + lisp_nsh_fib_add_del_entry (fid_addr_nsh (&lfe->key->rmt), (u32) ~ 0, 0); + + hash_unset_mem (lgm->lisp_gpe_fwd_entries, lfe->key); + clib_mem_free (lfe->key); + pool_put (lgm->lisp_fwd_entry_pool, lfe); +} + +/** + * @brief Delete LISP NSH forwarding entry. + * + * Coordinates the removal of forwarding entries for NSH LISP overlay: + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] a Parameters for building the forwarding entry. + * + * @return 0 on success. + */ +static int +del_nsh_fwd_entry (lisp_gpe_main_t * lgm, + vnet_lisp_gpe_add_del_fwd_entry_args_t * a) +{ + lisp_gpe_fwd_entry_key_t key; + lisp_gpe_fwd_entry_t *lfe; + + lfe = find_fwd_entry (lgm, a, &key); + + if (NULL == lfe) + return VNET_API_ERROR_INVALID_VALUE; + + del_nsh_fwd_entry_i (lgm, lfe); + + return (0); +} + +/** + * @brief Construct and insert the forwarding information used by an NSH entry + */ +static void +lisp_gpe_nsh_update_fwding (lisp_gpe_fwd_entry_t * lfe) +{ + lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main (); + dpo_id_t dpo = DPO_INVALID; + vnet_hw_interface_t *hi; + uword *hip; + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) + { + fib_path_list_contribute_forwarding (lfe->nsh.path_list_index, + FIB_FORW_CHAIN_TYPE_NSH, + FIB_PATH_LIST_FWD_FLAG_NONE, + &lfe->nsh.dpo); + + /* + * LISP encap is always the same for this SPI+SI so we do that hash now + * and stack on the choice. + */ + if (DPO_LOAD_BALANCE == lfe->nsh.dpo.dpoi_type) + { + const dpo_id_t *tmp; + const load_balance_t *lb; + int hash; + + lb = load_balance_get (lfe->nsh.dpo.dpoi_index); + hash = fid_addr_nsh (&lfe->key->rmt) % lb->lb_n_buckets; + tmp = + load_balance_get_bucket_i (lb, hash & lb->lb_n_buckets_minus_1); + + dpo_copy (&dpo, tmp); + } + } + else + { + switch (lfe->action) + { + case SEND_MAP_REQUEST: + dpo_copy (&dpo, lgm->nsh_cp_lkup); + break; + case NO_ACTION: + case FORWARD_NATIVE: + case DROP: + dpo_copy (&dpo, drop_dpo_get (DPO_PROTO_NSH)); + } + } + + /* We have only one nsh-lisp interface (no NSH virtualization) */ + hip = hash_get (lgm->nsh_ifaces.hw_if_index_by_dp_table, 0); + if (hip) + { + hi = vnet_get_hw_interface (lgm->vnet_main, hip[0]); + dpo_stack_from_node (hi->tx_node_index, &lfe->nsh.choice, &dpo); + } + /* add entry to nsh lisp fib */ + lisp_nsh_fib_add_del_entry (fid_addr_nsh (&lfe->key->rmt), + lfe - lgm->lisp_fwd_entry_pool, 1); + dpo_reset (&dpo); + +} + +/** + * @brief Add LISP NSH forwarding entry. + * + * Coordinates the creation of forwarding entries for L2 LISP overlay: + * creates lisp-gpe tunnel and injects new entry in Source/Dest L2 FIB. + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] a Parameters for building the forwarding entry. + * + * @return 0 on success. + */ +static int +add_nsh_fwd_entry (lisp_gpe_main_t * lgm, + vnet_lisp_gpe_add_del_fwd_entry_args_t * a) +{ + lisp_gpe_fwd_entry_key_t key; + lisp_gpe_fwd_entry_t *lfe; + + lfe = find_fwd_entry (lgm, a, &key); + + if (NULL != lfe) + /* don't support updates */ + return VNET_API_ERROR_INVALID_VALUE; + + pool_get (lgm->lisp_fwd_entry_pool, lfe); + clib_memset (lfe, 0, sizeof (*lfe)); + lfe->key = clib_mem_alloc (sizeof (key)); + memcpy (lfe->key, &key, sizeof (key)); + + hash_set_mem (lgm->lisp_gpe_fwd_entries, lfe->key, + lfe - lgm->lisp_fwd_entry_pool); + a->fwd_entry_index = lfe - lgm->lisp_fwd_entry_pool; + + lfe->type = (a->is_negative ? + LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE : + LISP_GPE_FWD_ENTRY_TYPE_NORMAL); + lfe->tenant = 0; + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) + { + fib_route_path_t *rpaths; + + /* + * Make the sorted array of LISP paths with their resp. adjacency + */ + lisp_gpe_fwd_entry_mk_paths (lfe, a); + + /* + * From the LISP paths, construct a FIB path list that will + * contribute a load-balance. + */ + rpaths = lisp_gpe_mk_fib_paths (lfe->paths); + + lfe->nsh.path_list_index = + fib_path_list_create (FIB_PATH_LIST_FLAG_NONE, rpaths); + + /* + * become a child of the path-list so we receive updates when + * its forwarding state changes. this includes an implicit lock. + */ + lfe->nsh.child_index = + fib_path_list_child_add (lfe->nsh.path_list_index, + FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY, + lfe - lgm->lisp_fwd_entry_pool); + } + else + { + lfe->action = a->action; + } + + lisp_gpe_nsh_update_fwding (lfe); + + return 0; +} + +/** + * @brief conver from the embedded fib_node_t struct to the LSIP entry + */ +static lisp_gpe_fwd_entry_t * +lisp_gpe_fwd_entry_from_fib_node (fib_node_t * node) +{ + return ((lisp_gpe_fwd_entry_t *) (((char *) node) - + STRUCT_OFFSET_OF (lisp_gpe_fwd_entry_t, + node))); +} + +/** + * @brief Function invoked during a backwalk of the FIB graph + */ +static fib_node_back_walk_rc_t +lisp_gpe_fib_node_back_walk (fib_node_t * node, + fib_node_back_walk_ctx_t * ctx) +{ + lisp_gpe_fwd_entry_t *lfe = lisp_gpe_fwd_entry_from_fib_node (node); + + if (fid_addr_type (&lfe->key->rmt) == FID_ADDR_MAC) + lisp_gpe_l2_update_fwding (lfe); + else if (fid_addr_type (&lfe->key->rmt) == FID_ADDR_NSH) + lisp_gpe_nsh_update_fwding (lfe); + + return (FIB_NODE_BACK_WALK_CONTINUE); +} + +/** + * @brief Get a fib_node_t struct from the index of a LISP fwd entry + */ +static fib_node_t * +lisp_gpe_fwd_entry_get_fib_node (fib_node_index_t index) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + lisp_gpe_fwd_entry_t *lfe; + + lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, index); + + return (&(lfe->node)); +} + +/** + * @brief An indication from the graph that the last lock has gone + */ +static void +lisp_gpe_fwd_entry_fib_node_last_lock_gone (fib_node_t * node) +{ + /* We don't manage the locks of the LISP objects via the graph, since + * this object has no children. so this is a no-op. */ +} + +/** + * @brief Virtual function table to register with FIB for the LISP type + */ +const static fib_node_vft_t lisp_fwd_vft = { + .fnv_get = lisp_gpe_fwd_entry_get_fib_node, + .fnv_last_lock = lisp_gpe_fwd_entry_fib_node_last_lock_gone, + .fnv_back_walk = lisp_gpe_fib_node_back_walk, +}; + +/** + * @brief Forwarding entry create/remove dispatcher. + * + * Calls l2 or l3 forwarding entry add/del function based on input data. + * + * @param[in] a Forwarding entry parameters. + * @param[out] hw_if_indexp NOT USED + * + * @return 0 on success. + */ +int +vnet_lisp_gpe_add_del_fwd_entry (vnet_lisp_gpe_add_del_fwd_entry_args_t * a, + u32 * hw_if_indexp) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + u8 type; + + if (vnet_lisp_gpe_enable_disable_status () == 0) + { + clib_warning ("LISP is disabled!"); + return VNET_API_ERROR_LISP_DISABLED; + } + + type = gid_address_type (&a->rmt_eid); + switch (type) + { + case GID_ADDR_IP_PREFIX: + if (a->is_add) + return add_ip_fwd_entry (lgm, a); + else + return del_ip_fwd_entry (lgm, a); + break; + case GID_ADDR_MAC: + if (a->is_add) + return add_l2_fwd_entry (lgm, a); + else + return del_l2_fwd_entry (lgm, a); + case GID_ADDR_NSH: + if (a->is_add) + return add_nsh_fwd_entry (lgm, a); + else + return del_nsh_fwd_entry (lgm, a); + default: + clib_warning ("Forwarding entries for type %d not supported!", type); + return -1; + } +} + +int +vnet_lisp_flush_stats (void) +{ + lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main (); + vlib_combined_counter_main_t *cm = &lgm->counters; + u32 i; + + if (cm->counters == NULL) + return 0; + + for (i = 0; i < vlib_combined_counter_n_counters (cm); i++) + vlib_zero_combined_counter (cm, i); + + return 0; +} + +static void +lisp_del_adj_stats (lisp_gpe_main_t * lgm, u32 fwd_entry_index, u32 ti) +{ + hash_pair_t *hp; + lisp_stats_key_t key; + void *key_copy; + uword *p; + u8 *s; + + clib_memset (&key, 0, sizeof (key)); + key.fwd_entry_index = fwd_entry_index; + key.tunnel_index = ti; + + p = hash_get_mem (lgm->lisp_stats_index_by_key, &key); + if (p) + { + s = pool_elt_at_index (lgm->placeholder_stats_pool, p[0]); + hp = hash_get_pair (lgm->lisp_stats_index_by_key, &key); + key_copy = (void *) (hp->key); + hash_unset_mem (lgm->lisp_stats_index_by_key, &key); + clib_mem_free (key_copy); + pool_put (lgm->placeholder_stats_pool, s); + } +} + +void +vnet_lisp_gpe_del_fwd_counters (vnet_lisp_gpe_add_del_fwd_entry_args_t * a, + u32 fwd_entry_index) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + lisp_gpe_fwd_entry_key_t fe_key; + lisp_gpe_fwd_entry_t *lfe; + lisp_fwd_path_t *path; + const lisp_gpe_adjacency_t *ladj; + + lfe = find_fwd_entry (lgm, a, &fe_key); + if (!lfe) + return; + + if (LISP_GPE_FWD_ENTRY_TYPE_NORMAL != lfe->type) + return; + + vec_foreach (path, lfe->paths) + { + ladj = lisp_gpe_adjacency_get (path->lisp_adj); + lisp_del_adj_stats (lgm, fwd_entry_index, ladj->tunnel_index); + } +} + +/** + * @brief Flush all the forwrding entries + */ +void +vnet_lisp_gpe_fwd_entry_flush (void) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + lisp_gpe_fwd_entry_t *lfe; + + /* *INDENT-OFF* */ + pool_foreach (lfe, lgm->lisp_fwd_entry_pool, + ({ + switch (fid_addr_type(&lfe->key->rmt)) + { + case FID_ADDR_MAC: + del_l2_fwd_entry_i (lgm, lfe); + break; + case FID_ADDR_IP_PREF: + del_ip_fwd_entry_i (lgm, lfe); + break; + case FID_ADDR_NSH: + del_nsh_fwd_entry_i (lgm, lfe); + break; + } + })); + /* *INDENT-ON* */ +} + +static u8 * +format_lisp_fwd_path (u8 * s, va_list * ap) +{ + lisp_fwd_path_t *lfp = va_arg (*ap, lisp_fwd_path_t *); + + s = format (s, "weight:%d ", lfp->weight); + s = format (s, "adj:[%U]\n", + format_lisp_gpe_adjacency, + lisp_gpe_adjacency_get (lfp->lisp_adj), + LISP_GPE_ADJ_FORMAT_FLAG_NONE); + + return (s); +} + +typedef enum lisp_gpe_fwd_entry_format_flag_t_ +{ + LISP_GPE_FWD_ENTRY_FORMAT_NONE = (0 << 0), + LISP_GPE_FWD_ENTRY_FORMAT_DETAIL = (1 << 1), +} lisp_gpe_fwd_entry_format_flag_t; + + +static u8 * +format_lisp_gpe_fwd_entry (u8 * s, va_list * ap) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + lisp_gpe_fwd_entry_t *lfe = va_arg (*ap, lisp_gpe_fwd_entry_t *); + lisp_gpe_fwd_entry_format_flag_t flags = + va_arg (*ap, lisp_gpe_fwd_entry_format_flag_t); + + s = format (s, "VNI:%d VRF:%d EID: %U -> %U [index:%d]", + lfe->key->vni, lfe->eid_table_id, + format_fid_address, &lfe->key->lcl, + format_fid_address, &lfe->key->rmt, + lfe - lgm->lisp_fwd_entry_pool); + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE == lfe->type) + { + s = format (s, "\n Negative - action:%U", + format_negative_mapping_action, lfe->action); + } + else + { + lisp_fwd_path_t *path; + + s = format (s, "\n via:"); + vec_foreach (path, lfe->paths) + { + s = format (s, "\n %U", format_lisp_fwd_path, path); + } + } + + if (flags & LISP_GPE_FWD_ENTRY_FORMAT_DETAIL) + { + switch (fid_addr_type (&lfe->key->rmt)) + { + case FID_ADDR_MAC: + s = format (s, " fib-path-list:%d\n", lfe->l2.path_list_index); + s = format (s, " dpo:%U\n", format_dpo_id, &lfe->l2.dpo, 0); + break; + case FID_ADDR_NSH: + s = format (s, " fib-path-list:%d\n", lfe->nsh.path_list_index); + s = format (s, " dpo:%U\n", format_dpo_id, &lfe->nsh.dpo, 0); + break; + case FID_ADDR_IP_PREF: + break; + } + } + + return (s); +} + +static clib_error_t * +lisp_gpe_fwd_entry_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + lisp_gpe_fwd_entry_t *lfe; + index_t index; + u32 vni = ~0; + + if (unformat (input, "vni %d", &vni)) + ; + else if (unformat (input, "%d", &index)) + { + if (!pool_is_free_index (lgm->lisp_fwd_entry_pool, index)) + { + lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, index); + + vlib_cli_output (vm, "[%d@] %U", + index, + format_lisp_gpe_fwd_entry, lfe, + LISP_GPE_FWD_ENTRY_FORMAT_DETAIL); + } + else + { + vlib_cli_output (vm, "entry %d invalid", index); + } + + return (NULL); + } + + /* *INDENT-OFF* */ + pool_foreach (lfe, lgm->lisp_fwd_entry_pool, + ({ + if ((vni == ~0) || + (lfe->key->vni == vni)) + vlib_cli_output (vm, "%U", format_lisp_gpe_fwd_entry, lfe, + LISP_GPE_FWD_ENTRY_FORMAT_NONE); + })); + /* *INDENT-ON* */ + + return (NULL); +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (lisp_gpe_fwd_entry_show_command, static) = { + .path = "show gpe entry", + .short_help = "show gpe entry vni <vni> vrf <vrf> [leid <leid>] reid <reid>", + .function = lisp_gpe_fwd_entry_show, +}; +/* *INDENT-ON* */ + +clib_error_t * +lisp_gpe_fwd_entry_init (vlib_main_t * vm) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + clib_error_t *error = NULL; + + if ((error = vlib_call_init_function (vm, lisp_cp_dpo_module_init))) + return (error); + + l2_fib_init (lgm); + nsh_fib_init (lgm); + + fib_node_register_type (FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY, &lisp_fwd_vft); + + return (error); +} + +u32 * +vnet_lisp_gpe_get_fwd_entry_vnis (void) +{ + lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main (); + lisp_gpe_fwd_entry_t *lfe; + u32 *vnis = 0; + + /* *INDENT-OFF* */ + pool_foreach (lfe, lgm->lisp_fwd_entry_pool, + ({ + hash_set (vnis, lfe->key->vni, 0); + })); + /* *INDENT-ON* */ + + return vnis; +} + +lisp_api_gpe_fwd_entry_t * +vnet_lisp_gpe_fwd_entries_get_by_vni (u32 vni) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + lisp_gpe_fwd_entry_t *lfe; + lisp_api_gpe_fwd_entry_t *entries = 0, e; + + /* *INDENT-OFF* */ + pool_foreach (lfe, lgm->lisp_fwd_entry_pool, + ({ + if (lfe->key->vni == vni) + { + clib_memset (&e, 0, sizeof (e)); + e.dp_table = lfe->eid_table_id; + e.vni = lfe->key->vni; + if (lfe->type == LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE) + e.action = lfe->action; + e.fwd_entry_index = lfe - lgm->lisp_fwd_entry_pool; + memcpy (&e.reid, &lfe->key->rmt, sizeof (e.reid)); + memcpy (&e.leid, &lfe->key->lcl, sizeof (e.leid)); + vec_add1 (entries, e); + } + })); + /* *INDENT-ON* */ + + return entries; +} + +int +vnet_lisp_gpe_get_fwd_stats (vnet_lisp_gpe_add_del_fwd_entry_args_t * a, + vlib_counter_t * c) +{ + lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main (); + lisp_gpe_fwd_entry_t *lfe; + lisp_gpe_fwd_entry_key_t unused; + + lfe = find_fwd_entry (lgm, a, &unused); + if (NULL == lfe) + return -1; + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE == lfe->type) + return -1; + + if (~0 == lfe->dpoi_index) + return -1; + + vlib_get_combined_counter (&load_balance_main.lbm_to_counters, + lfe->dpoi_index, c); + return 0; +} + +VLIB_INIT_FUNCTION (lisp_gpe_fwd_entry_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |