+#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,
+ /**
+ * the fixup function is standard IP4o4 header
+ */
+ ADJ_ATTR_MIDCHAIN_FIXUP_IP4O4_HDR,
+ /**
+ * the fixup function performs the flow hash
+ * this means the flow hash is performed on the inner
+ * header, where the entropy is higher.
+ */
+ ADJ_ATTR_MIDCHAIN_FIXUP_FLOW_HASH,
+} 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", \
+ [ADJ_ATTR_MIDCHAIN_FIXUP_IP4O4_HDR] = "midchain-ip4o4-hdr-fixup", \
+ [ADJ_ATTR_MIDCHAIN_FIXUP_FLOW_HASH] = "midchain-flow-hash", \
+}
+
+#define FOR_EACH_ADJ_ATTR(_attr) \
+ for (_attr = ADJ_ATTR_SYNC_WALK_ACTIVE; \
+ _attr <= ADJ_ATTR_MIDCHAIN_FIXUP_FLOW_HASH; \
+ _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),
+ ADJ_FLAG_MIDCHAIN_FIXUP_IP4O4_HDR = (1 << ADJ_ATTR_MIDCHAIN_FIXUP_IP4O4_HDR),
+ ADJ_FLAG_MIDCHAIN_FIXUP_FLOW_HASH = (1 << ADJ_ATTR_MIDCHAIN_FIXUP_FLOW_HASH),
+} __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;
+ /**
+ * feature [arc] config index
+ */
+ u32 ia_cfg_index;
+
+ 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;
+
+ /** spare space */
+ u8 __ia_midchain_pad[4];
+
+ } 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
+ {
+ fib_prefix_t rx_pfx;
+ } glean;
+ } sub_type;
+
+ CLIB_CACHE_LINE_ALIGN_MARK (cacheline1);
+
+ /** Rewrite in second and third cache lines */
+ VNET_DECLARE_REWRITE;
+
+ /**
+ * more control plane members that do not fit on the first cacheline
+ */
+ CLIB_CACHE_LINE_ALIGN_MARK (cacheline3);
+
+ /**
+ * 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;
+
+ /**
+ * 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;
+
+ /**
+ * Free space on the fourth cacheline (not used in the DP)
+ */
+ u8 __ia_pad[48];
+} 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");
+#if defined __x86_64__
+STATIC_ASSERT ((STRUCT_OFFSET_OF (ip_adjacency_t, cacheline3) ==
+ 3 * CLIB_CACHE_LINE_BYTES),
+ "IP adjacency cacheline 3 is more than one cacheline size offset");
+/* An adj fits into 4 cachelines on your average machine */
+STATIC_ASSERT_SIZEOF (ip_adjacency_t, 4 * 64);
+#endif