blob: e22e7ecd58a17558586027f75ef407bbbe2a9f20 [file] [log] [blame]
/*
* 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.
*/
/**
* An adjacency is a representation of an attached L3 peer.
*
* Adjacency Sub-types:
* - neighbour: a representation of an attached L3 peer.
* Key:{addr,interface,link/ether-type}
* SHARED
* - glean: used to drive ARP/ND for packets destined to a local sub-net.
* 'glean' mean use the packet's destination address as the target
* address in the ARP packet.
* UNSHARED. Only one per-interface.
* - midchain: a neighbour adj on a virtual/tunnel interface.
*
* The API to create and update the adjacency is very sub-type specific. This
* is intentional as it encourages the user to carefully consider which adjacency
* sub-type they are really using, and hence assign it data in the appropriate
* sub-type space in the union of sub-types. This prevents the adj becoming a
* disorganised dumping group for 'my features needs a u16 somewhere' data. It
* is important to enforce this approach as space in the adjacency is a premium,
* as we need it to fit in 1 cache line.
*
* the API is also based around an index to an adjacency not a raw pointer. This
* is so the user doesn't suffer the same limp inducing firearm injuries that
* the author suffered as the adjacencies can realloc.
*/
#ifndef __ADJ_H__
#define __ADJ_H__
#include <vnet/adj/adj_types.h>
#include <vnet/adj/adj_nbr.h>
#include <vnet/adj/adj_glean.h>
#include <vnet/adj/rewrite.h>
/** @brief Common (IP4/IP6) next index stored in adjacency. */
typedef enum
{
/** Adjacency to drop this packet. */
IP_LOOKUP_NEXT_DROP,
/** Adjacency to punt this packet. */
IP_LOOKUP_NEXT_PUNT,
/** This packet is for one of our own IP addresses. */
IP_LOOKUP_NEXT_LOCAL,
/** This packet matches an "incomplete adjacency" and packets
need to be passed to ARP to find rewrite string for
this destination. */
IP_LOOKUP_NEXT_ARP,
/** This packet matches an "interface route" and packets
need to be passed to ARP to find rewrite string for
this destination. */
IP_LOOKUP_NEXT_GLEAN,
/** This packet is to be rewritten and forwarded to the next
processing node. This is typically the output interface but
might be another node for further output processing. */
IP_LOOKUP_NEXT_REWRITE,
/** This packets follow a mid-chain adjacency */
IP_LOOKUP_NEXT_MIDCHAIN,
/** This packets needs to go to ICMP error */
IP_LOOKUP_NEXT_ICMP_ERROR,
/** Multicast Adjacency. */
IP_LOOKUP_NEXT_MCAST,
/** Broadcast Adjacency. */
IP_LOOKUP_NEXT_BCAST,
/** Multicast Midchain Adjacency. An Adjacency for sending multicast packets
* on a tunnel/virtual interface */
IP_LOOKUP_NEXT_MCAST_MIDCHAIN,
IP_LOOKUP_N_NEXT,
} __attribute__ ((packed)) ip_lookup_next_t;
typedef enum
{
IP4_LOOKUP_N_NEXT = IP_LOOKUP_N_NEXT,
} ip4_lookup_next_t;
typedef enum
{
/* Hop-by-hop header handling */
IP6_LOOKUP_NEXT_HOP_BY_HOP = IP_LOOKUP_N_NEXT,
IP6_LOOKUP_NEXT_ADD_HOP_BY_HOP,
IP6_LOOKUP_NEXT_POP_HOP_BY_HOP,
IP6_LOOKUP_N_NEXT,
} ip6_lookup_next_t;
#define IP4_LOOKUP_NEXT_NODES { \
[IP_LOOKUP_NEXT_DROP] = "ip4-drop", \
[IP_LOOKUP_NEXT_PUNT] = "ip4-punt", \
[IP_LOOKUP_NEXT_LOCAL] = "ip4-local", \
[IP_LOOKUP_NEXT_ARP] = "ip4-arp", \
[IP_LOOKUP_NEXT_GLEAN] = "ip4-glean", \
[IP_LOOKUP_NEXT_REWRITE] = "ip4-rewrite", \
[IP_LOOKUP_NEXT_MCAST] = "ip4-rewrite-mcast", \
[IP_LOOKUP_NEXT_BCAST] = "ip4-rewrite-bcast", \
[IP_LOOKUP_NEXT_MIDCHAIN] = "ip4-midchain", \
[IP_LOOKUP_NEXT_MCAST_MIDCHAIN] = "ip4-mcast-midchain", \
[IP_LOOKUP_NEXT_ICMP_ERROR] = "ip4-icmp-error", \
}
#define IP6_LOOKUP_NEXT_NODES { \
[IP_LOOKUP_NEXT_DROP] = "ip6-drop", \
[IP_LOOKUP_NEXT_PUNT] = "ip6-punt", \
[IP_LOOKUP_NEXT_LOCAL] = "ip6-local", \
[IP_LOOKUP_NEXT_ARP] = "ip6-discover-neighbor", \
[IP_LOOKUP_NEXT_GLEAN] = "ip6-glean", \
[IP_LOOKUP_NEXT_REWRITE] = "ip6-rewrite", \
[IP_LOOKUP_NEXT_BCAST] = "ip6-rewrite-bcast", \
[IP_LOOKUP_NEXT_MCAST] = "ip6-rewrite-mcast", \
[IP_LOOKUP_NEXT_MIDCHAIN] = "ip6-midchain", \
[IP_LOOKUP_NEXT_MCAST_MIDCHAIN] = "ip6-mcast-midchain", \
[IP_LOOKUP_NEXT_ICMP_ERROR] = "ip6-icmp-error", \
[IP6_LOOKUP_NEXT_HOP_BY_HOP] = "ip6-hop-by-hop", \
[IP6_LOOKUP_NEXT_ADD_HOP_BY_HOP] = "ip6-add-hop-by-hop", \
[IP6_LOOKUP_NEXT_POP_HOP_BY_HOP] = "ip6-pop-hop-by-hop", \
}
/**
* The special broadcast address (to construct a broadcast adjacency
*/
extern const ip46_address_t ADJ_BCAST_ADDR;
/**
* Forward declaration
*/
struct ip_adjacency_t_;
/**
* @brief A function type for post-rewrite fixups on midchain adjacency
*/
typedef void (*adj_midchain_fixup_t) (vlib_main_t * vm,
const struct ip_adjacency_t_ * adj,
vlib_buffer_t * b0,
const void *data);
/**
* @brief Flags on an IP adjacency
*/
typedef enum adj_attr_t_
{
/**
* Currently a sync walk is active. Used to prevent re-entrant walking
*/
ADJ_ATTR_SYNC_WALK_ACTIVE = 0,
/**
* Packets TX through the midchain do not increment the interface
* counters. This should be used when the adj is associated with an L2
* interface and that L2 interface is in a bridge domain. In that case
* the packet will have traversed the interface's TX node, and hence have
* been counted, before it traverses ths midchain
*/
ADJ_ATTR_MIDCHAIN_NO_COUNT,
/**
* When stacking midchains on a fib-entry extract the choice from the
* load-balance returned based on an IP hash of the adj's rewrite
*/
ADJ_ATTR_MIDCHAIN_IP_STACK,
/**
* If the midchain were to stack on its FIB entry a loop would form.
*/
ADJ_ATTR_MIDCHAIN_LOOPED,
} adj_attr_t;
#define ADJ_ATTR_NAMES { \
[ADJ_ATTR_SYNC_WALK_ACTIVE] = "walk-active", \
[ADJ_ATTR_MIDCHAIN_NO_COUNT] = "midchain-no-count", \
[ADJ_ATTR_MIDCHAIN_IP_STACK] = "midchain-ip-stack", \
[ADJ_ATTR_MIDCHAIN_LOOPED] = "midchain-looped", \
}
#define FOR_EACH_ADJ_ATTR(_attr) \
for (_attr = ADJ_ATTR_SYNC_WALK_ACTIVE; \
_attr <= ADJ_ATTR_MIDCHAIN_LOOPED; \
_attr++)
/**
* @brief Flags on an IP adjacency
*/
typedef enum adj_flags_t_
{
ADJ_FLAG_NONE = 0,
ADJ_FLAG_SYNC_WALK_ACTIVE = (1 << ADJ_ATTR_SYNC_WALK_ACTIVE),
ADJ_FLAG_MIDCHAIN_NO_COUNT = (1 << ADJ_ATTR_MIDCHAIN_NO_COUNT),
ADJ_FLAG_MIDCHAIN_IP_STACK = (1 << ADJ_ATTR_MIDCHAIN_IP_STACK),
ADJ_FLAG_MIDCHAIN_LOOPED = (1 << ADJ_ATTR_MIDCHAIN_LOOPED),
} __attribute__ ((packed)) adj_flags_t;
/**
* @brief Format adjacency flags
*/
extern u8* format_adj_flags(u8 * s, va_list * args);
/**
* @brief IP unicast adjacency.
* @note cache aligned.
*
* An adjacency is a representation of a peer on a particular link.
*/
typedef struct ip_adjacency_t_
{
CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
/**
* Linkage into the FIB node graph. First member since this type
* has 8 byte alignment requirements.
*/
fib_node_t ia_node;
/**
* Next hop after ip4-lookup.
* This is not accessed in the rewrite nodes.
* 1-bytes
*/
ip_lookup_next_t lookup_next_index;
/**
* link/ether-type
* 1 bytes
*/
vnet_link_t ia_link;
/**
* The protocol of the neighbor/peer. i.e. the protocol with
* which to interpret the 'next-hop' attributes of the sub-types.
* 1-bytes
*/
fib_protocol_t ia_nh_proto;
/**
* Flags on the adjacency
* 1-bytes
*/
adj_flags_t ia_flags;
union
{
/**
* IP_LOOKUP_NEXT_ARP/IP_LOOKUP_NEXT_REWRITE
*
* neighbour adjacency sub-type;
*/
struct
{
ip46_address_t next_hop;
} nbr;
/**
* IP_LOOKUP_NEXT_MIDCHAIN
*
* A nbr adj that is also recursive. Think tunnels.
* A nbr adj can transition to be of type MIDCHAIN
* so be sure to leave the two structs with the next_hop
* fields aligned.
*/
struct
{
/**
* The recursive next-hop.
* This field MUST be at the same memory location as
* sub_type.nbr.next_hop
*/
ip46_address_t next_hop;
/**
* The next DPO to use
*/
dpo_id_t next_dpo;
/**
* A function to perform the post-rewrite fixup
*/
adj_midchain_fixup_t fixup_func;
/**
* Fixup data passed back to the client in the fixup function
*/
const void *fixup_data;
/**
* the FIB entry this midchain resolves through. required for recursive
* loop detection.
*/
fib_node_index_t fei;
} midchain;
/**
* IP_LOOKUP_NEXT_GLEAN
*
* Glean the address to ARP for from the packet's destination.
* Technically these aren't adjacencies, i.e. they are not a
* representation of a peer. One day we might untangle this coupling
* and use a new Glean DPO.
*/
struct
{
ip46_address_t receive_addr;
} glean;
} sub_type;
CLIB_CACHE_LINE_ALIGN_MARK (cacheline1);
/* Rewrite in second/third cache lines */
VNET_DECLARE_REWRITE;
/**
* more control plane members that do not fit on the first cacheline
*/
/**
* A sorted vector of delegates
*/
struct adj_delegate_t_ *ia_delegates;
/**
* The VLIB node in which this adj is used to forward packets
*/
u32 ia_node_index;
} ip_adjacency_t;
STATIC_ASSERT ((STRUCT_OFFSET_OF (ip_adjacency_t, cacheline0) == 0),
"IP adjacency cacheline 0 is not offset");
STATIC_ASSERT ((STRUCT_OFFSET_OF (ip_adjacency_t, cacheline1) ==
CLIB_CACHE_LINE_BYTES),
"IP adjacency cacheline 1 is more than one cacheline size offset");
/**
* @brief
* Take a reference counting lock on the adjacency
*/
extern void adj_lock(adj_index_t adj_index);
/**
* @brief
* Release a reference counting lock on the adjacency
*/
extern void adj_unlock(adj_index_t adj_index);
/**
* @brief
* Add a child dependent to an adjacency. The child will
* thus be informed via its registered back-walk function
* when the adjacency state changes.
*/
extern u32 adj_child_add(adj_index_t adj_index,
fib_node_type_t type,
fib_node_index_t child_index);
/**
* @brief
* Remove a child dependent
*/
extern void adj_child_remove(adj_index_t adj_index,
u32 sibling_index);
/**
* @brief Walk the Adjacencies on a given interface
*/
extern void adj_walk (u32 sw_if_index,
adj_walk_cb_t cb,
void *ctx);
/**
* @brief Return the link type of the adjacency
*/
extern vnet_link_t adj_get_link_type (adj_index_t ai);
/**
* @brief Return the sw interface index of the adjacency.
*/
extern u32 adj_get_sw_if_index (adj_index_t ai);
/**
* @brief Return true if the adjacency is 'UP', i.e. can be used for forwarding.
* 0 is down, !0 is up.
*/
extern int adj_is_up (adj_index_t ai);
/**
* @brief Return the link type of the adjacency
*/
extern const u8* adj_get_rewrite (adj_index_t ai);
/**
* @brief descend the FIB graph looking for loops
*
* @param ai
* The adj index to traverse
*
* @param entry_indicies)
* A pointer to a vector of FIB entries already visited.
*/
extern int adj_recursive_loop_detect (adj_index_t ai,
fib_node_index_t **entry_indicies);
/**
* @brief
* The global adjacency pool. Exposed for fast/inline data-plane access
*/
extern ip_adjacency_t *adj_pool;
/**
* @brief
* Adjacency packet counters
*/
extern vlib_combined_counter_main_t adjacency_counters;
/**
* @brief Global Config for enabling per-adjacency counters
* This is configurable because it comes with a non-negligible
* performance cost. */
extern int adj_per_adj_counters;
/**
* @brief
* Get a pointer to an adjacency object from its index
*/
static inline ip_adjacency_t *
adj_get (adj_index_t adj_index)
{
return (pool_elt_at_index(adj_pool, adj_index));
}
static inline int
adj_is_valid(adj_index_t adj_index)
{
return !(pool_is_free_index(adj_pool, adj_index));
}
/**
* @brief Get the global configuration option for enabling per-adj counters
*/
static inline int
adj_are_counters_enabled (void)
{
return (adj_per_adj_counters);
}
#endif