/* * Copyright (c) 2016 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include /* * Vector Hash tables of neighbour (traditional) adjacencies * Key: interface(for the vector index), address (and its proto), * link-type/ether-type. */ static uword **adj_nbr_tables[FIB_PROTOCOL_IP_MAX]; typedef struct adj_nbr_key_t_ { ip46_address_t ank_ip; u64 ank_linkt; } adj_nbr_key_t; #define ADJ_NBR_SET_KEY(_key, _lt, _nh) \ { \ ip46_address_copy(&(_key).ank_ip, (_nh)); \ _key.ank_linkt = (_lt); \ } #define ADJ_NBR_ITF_OK(_proto, _itf) \ (((_itf) < vec_len(adj_nbr_tables[_proto])) && \ (NULL != adj_nbr_tables[_proto][(_itf)])) #define ADJ_NBR_ASSERT_NH_PROTO(nh_proto, err) \ do { \ ASSERT (nh_proto < FIB_PROTOCOL_IP_MAX); \ const fib_protocol_t nh_proto__ = (nh_proto); \ if (nh_proto__ >= FIB_PROTOCOL_IP_MAX) \ { \ clib_warning ("BUG: protocol %d > %d\n", \ (int)nh_proto__, \ FIB_PROTOCOL_IP_MAX); \ return err; \ } \ } while (0) static void adj_nbr_insert (fib_protocol_t nh_proto, vnet_link_t link_type, const ip46_address_t *nh_addr, u32 sw_if_index, adj_index_t adj_index) { adj_nbr_key_t kv; ADJ_NBR_ASSERT_NH_PROTO (nh_proto,); if (sw_if_index >= vec_len(adj_nbr_tables[nh_proto])) { vec_validate(adj_nbr_tables[nh_proto], sw_if_index); } if (NULL == adj_nbr_tables[nh_proto][sw_if_index]) { adj_nbr_tables[nh_proto][sw_if_index] = hash_create_mem(0, sizeof(adj_nbr_key_t), sizeof(adj_index_t)); } ADJ_NBR_SET_KEY(kv, link_type, nh_addr); hash_set_mem_alloc (&adj_nbr_tables[nh_proto][sw_if_index], &kv, adj_index); } void adj_nbr_remove (adj_index_t ai, fib_protocol_t nh_proto, vnet_link_t link_type, const ip46_address_t *nh_addr, u32 sw_if_index) { adj_nbr_key_t kv; ADJ_NBR_ASSERT_NH_PROTO (nh_proto,); if (!ADJ_NBR_ITF_OK(nh_proto, sw_if_index)) return; ADJ_NBR_SET_KEY(kv, link_type, nh_addr); hash_unset_mem_free(&adj_nbr_tables[nh_proto][sw_if_index], &kv); if (0 == hash_elts(adj_nbr_tables[nh_proto][sw_if_index])) { hash_free(adj_nbr_tables[nh_proto][sw_if_index]); } } adj_index_t adj_nbr_find (fib_protocol_t nh_proto, vnet_link_t link_type, const ip46_address_t *nh_addr, u32 sw_if_index) { adj_nbr_key_t kv; uword *p; ADJ_NBR_ASSERT_NH_PROTO (nh_proto, ADJ_INDEX_INVALID); ADJ_NBR_SET_KEY(kv, link_type, nh_addr); if (!ADJ_NBR_ITF_OK(nh_proto, sw_if_index)) return (ADJ_INDEX_INVALID); p = hash_get_mem(adj_nbr_tables[nh_proto][sw_if_index], &kv); if (p) { return (p[0]); } return (ADJ_INDEX_INVALID); } static inline u32 adj_get_nd_node (fib_protocol_t proto) { switch (proto) { case FIB_PROTOCOL_IP4: return (ip4_arp_node.index); case FIB_PROTOCOL_IP6: return (ip6_discover_neighbor_node.index); case FIB_PROTOCOL_MPLS: break; } ASSERT(0); return (ip4_arp_node.index); } /** * @brief Check and set feature flags if o/p interface has any o/p features. */ static void adj_nbr_evaluate_feature (adj_index_t ai) { ip_adjacency_t *adj; vnet_feature_main_t *fm = &feature_main; i16 feature_count; u8 arc_index; u32 sw_if_index; adj = adj_get(ai); switch (adj->ia_link) { case VNET_LINK_IP4: arc_index = ip4_main.lookup_main.output_feature_arc_index; break; case VNET_LINK_IP6: arc_index = ip6_main.lookup_main.output_feature_arc_index; break; case VNET_LINK_MPLS: arc_index = mpls_main.output_feature_arc_index; break; default: return; } sw_if_index = adj->rewrite_header.sw_if_index; if (vec_len(fm->feature_count_by_sw_if_index[arc_index]) > sw_if_index) { feature_count = fm->feature_count_by_sw_if_index[arc_index][sw_if_index]; if (feature_count > 0) { vnet_feature_config_main_t *cm; adj->rewrite_header.flags |= VNET_REWRITE_HAS_FEATURES; cm = &fm->feature_config_mains[arc_index]; adj->ia_cfg_index = vec_elt (cm->config_index_by_sw_if_index, sw_if_index); } } return; } static ip_adjacency_t* adj_nbr_alloc (fib_protocol_t nh_proto, vnet_link_t link_type, const ip46_address_t *nh_addr, u32 sw_if_index) { ip_adjacency_t *adj; adj = adj_alloc(nh_proto); adj_nbr_insert(nh_proto, link_type, nh_addr, sw_if_index, adj_get_index(adj)); /* * since we just added the ADJ we have no rewrite string for it, * so its for ARP */ adj->lookup_next_index = IP_LOOKUP_NEXT_ARP; adj->sub_type.nbr.next_hop = *nh_addr; adj->ia_link = link_type; adj->ia_nh_proto = nh_proto; adj->rewrite_header.sw_if_index = sw_if_index; vnet_rewrite_update_mtu(vnet_get_main(), adj->ia_link, &adj->rewrite_header); adj_nbr_evaluate_feature (adj_get_index(adj)); return (adj); } /* * adj_nbr_add_or_lock * * Add an adjacency for the neighbour requested. * * The key for an adj is: * - the Next-hops protocol (i.e. v4 or v6) * - the address of the next-hop * - the interface the next-hop is reachable through */ adj_index_t adj_nbr_add_or_lock (fib_protocol_t nh_proto, vnet_link_t link_type, const ip46_address_t *nh_addr, u32 sw_if_index) { adj_index_t adj_index; adj_index = adj_nbr_find(nh_proto, link_type, nh_addr, sw_if_index); if (ADJ_INDEX_INVALID == adj_index) { ip_adjacency_t *adj; vnet_main_t *vnm; vnm = vnet_get_main(); adj = adj_nbr_alloc(nh_proto, link_type, nh_addr, sw_if_index); adj_index = adj_get_index(adj); adj_lock(adj_index); if (ip46_address_is_equal(&ADJ_BCAST_ADDR, nh_addr)) { adj->lookup_next_index = IP_LOOKUP_NEXT_BCAST; } vnet_rewrite_init(vnm, sw_if_index, link_type, adj_get_nd_node(nh_proto), vnet_tx_node_index_for_sw_interface(vnm, sw_if_index), &adj->rewrite_header); /* * we need a rewrite where the destination IP address is converted * to the appropriate link-layer address. This is interface specific. * So ask the interface to do it. */ vnet_update_adjacency_for_sw_interface(vnm, sw_if_index, adj_index); } else { adj_lock(adj_index); } adj_delegate_adj_created(adj_get(adj_index)); return (adj_index); } adj_index_t adj_nbr_add_or_lock_w_rewrite (fib_protocol_t nh_proto, vnet_link_t link_type, const ip46_address_t *nh_addr, u32 sw_if_index, u8 *rewrite) { adj_index_t adj_index; adj_index = adj_nbr_find(nh_proto, link_type, nh_addr, sw_if_index); if (ADJ_INDEX_INVALID == adj_index) { ip_adjacency_t *adj; adj = adj_nbr_alloc(nh_proto, link_type, nh_addr, sw_if_index); adj->rewrite_header.sw_if_index = sw_if_index; adj_index = adj_get_index(adj); } adj_lock(adj_index); adj_nbr_update_rewrite(adj_index, ADJ_NBR_REWRITE_FLAG_COMPLETE, rewrite); adj_delegate_adj_created(adj_get(adj_index)); return (adj_index); } /** * adj_nbr_update_rewrite * * Update the adjacency's rewrite string. A NULL string implies the * rewrite is reset (i.e. when ARP/ND entry is gone). * NB: the adj being updated may be handling traffic in the DP. */ void adj_nbr_update_rewrite (adj_index_t adj_index, adj_nbr_rewrite_flag_t flags, u8 *rewrite) { ip_adjacency_t *adj; ASSERT(ADJ_INDEX_INVALID != adj_index); adj = adj_get(adj_index); if (flags & ADJ_NBR_REWRITE_FLAG_COMPLETE) { /* * update the adj's rewrite string and build the arc * from the rewrite node to the interface's TX node */ adj_nbr_update_rewrite_internal(adj, IP_LOOKUP_NEXT_REWRITE, adj_get_rewrite_node(adj->ia_link), vnet_tx_node_index_for_sw
/*
 * 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 __FIB_TEST_H__
#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_MPLS_DISP_O_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_ {
    fib_test_lb_bucket_type_t type;

    union
    {
	struct
	{
	    mpls_eos_bit_t eos;
	    mpls_label_t label;
	    u8 ttl;
	    adj_index_t adj;
	} label_o_adj;
	struct
	{
	    mpls_eos_bit_t eos;
	    mpls_label_t label_stack[8];
	    u8 label_stack_size;
	    u8 ttl;
	    adj_index_t adj;
	} label_stack_o_adj;
	struct
	{
	    mpls_eos_bit_t eos;
	    mpls_label_t label;
	    u8 ttl;
	    index_t lb;
	} label_o_lb;
	struct
	{
	    index_t adj;
	} adj;
	struct
	{
	    index_t lb;
	} lb;
	struct
	{
	    index_t adj;
	} special;
        struct
        {
            union {
                index_t table;
                index_t fmask;
            };
	} bier;
    };
} fib_test_lb_bucket_t;

typedef enum fib_test_rep_bucket_type_t_ {
    FT_REP_LABEL_O_ADJ,
    FT_REP_DISP_MFIB_LOOKUP,
    FT_REP_INTF,
} fib_test_rep_bucket_type_t;

typedef struct fib_test_rep_bucket_t_ {
    fib_test_rep_bucket_type_t type;

    union
    {
	struct
	{
	    mpls_eos_bit_t eos;
	    mpls_label_t label;
	    u8 ttl;
	    adj_index_t adj;
	} label_o_adj;
 	struct
	{
	    adj_index_t adj;
	} adj;
   };
} fib_test_rep_bucket_t;


extern int fib_test_validate_rep_v(const replicate_t *rep,
                                   u16 n_buckets,
                                   va_list *ap);

extern int fib_test_validate_lb_v(const load_balance_t *lb,
                                  int n_buckets,
                                  va_list *ap);

extern int fib_test_validate_lb(const dpo_id_t *dpo,
				int n_buckets,
				...);

extern int fib_test_validate_entry(fib_node_index_t fei,
                                   fib_forward_chain_type_t fct,
                                   int n_buckets,
                                   ...);


#endif
t_hop, adj_proto_to_46(adj->ia_nh_proto)); s = format (s, "%U", format_vnet_rewrite, &adj->rewrite_header, sizeof (adj->rewrite_data), 0); return (s); } static void adj_dpo_lock (dpo_id_t *dpo) { adj_lock(dpo->dpoi_index); } static void adj_dpo_unlock (dpo_id_t *dpo) { adj_unlock(dpo->dpoi_index); } static void adj_mem_show (void) { fib_show_memory_usage("Adjacency", pool_elts(adj_pool), pool_len(adj_pool), sizeof(ip_adjacency_t)); } const static dpo_vft_t adj_nbr_dpo_vft = { .dv_lock = adj_dpo_lock, .dv_unlock = adj_dpo_unlock, .dv_format = format_adj_nbr, .dv_mem_show = adj_mem_show, .dv_get_urpf = adj_dpo_get_urpf, }; const static dpo_vft_t adj_nbr_incompl_dpo_vft = { .dv_lock = adj_dpo_lock, .dv_unlock = adj_dpo_unlock, .dv_format = format_adj_nbr_incomplete, .dv_get_urpf = adj_dpo_get_urpf, }; /** * @brief The per-protocol VLIB graph nodes that are assigned to an adjacency * object. * * this means that these graph nodes are ones from which a nbr is the * parent object in the DPO-graph. */ const static char* const nbr_ip4_nodes[] = { "ip4-rewrite", NULL, }; const static char* const nbr_ip6_nodes[] = { "ip6-rewrite", NULL, }; const static char* const nbr_mpls_nodes[] = { "mpls-output", NULL, }; const static char* const nbr_ethernet_nodes[] = { "adj-l2-rewrite", NULL, }; const static char* const * const nbr_nodes[DPO_PROTO_NUM] = { [DPO_PROTO_IP4] = nbr_ip4_nodes, [DPO_PROTO_IP6] = nbr_ip6_nodes, [DPO_PROTO_MPLS] = nbr_mpls_nodes, [DPO_PROTO_ETHERNET] = nbr_ethernet_nodes, }; const static char* const nbr_incomplete_ip4_nodes[] = { "ip4-arp", NULL, }; const static char* const nbr_incomplete_ip6_nodes[] = { "ip6-discover-neighbor", NULL, }; const static char* const nbr_incomplete_mpls_nodes[] = { "mpls-adj-incomplete", NULL, }; const static char* const * const nbr_incomplete_nodes[DPO_PROTO_NUM] = { [DPO_PROTO_IP4] = nbr_incomplete_ip4_nodes, [DPO_PROTO_IP6] = nbr_incomplete_ip6_nodes, [DPO_PROTO_MPLS] = nbr_incomplete_mpls_nodes, }; void adj_nbr_module_init (void) { dpo_register(DPO_ADJACENCY, &adj_nbr_dpo_vft, nbr_nodes); dpo_register(DPO_ADJACENCY_INCOMPLETE, &adj_nbr_incompl_dpo_vft, nbr_incomplete_nodes); }