+static gre_tunnel_t *
+gre_tunnel_db_find (const ip4_address_t *src,
+ const ip4_address_t *dst,
+ u32 out_fib_index)
+{
+ gre_main_t * gm = &gre_main;
+ uword * p;
+ u64 key;
+
+ key = gre_mk_key(src, dst, out_fib_index);
+
+ p = hash_get (gm->tunnel_by_key, key);
+
+ if (NULL == p)
+ return (NULL);
+
+ return (pool_elt_at_index (gm->tunnels, p[0]));
+}
+
+static void
+gre_tunnel_db_add (const gre_tunnel_t *t)
+{
+ gre_main_t * gm = &gre_main;
+ u64 key;
+
+ key = gre_mk_key(&t->tunnel_src, &t->tunnel_dst, t->outer_fib_index);
+ hash_set (gm->tunnel_by_key, key, t - gm->tunnels);
+}
+
+static void
+gre_tunnel_db_remove (const gre_tunnel_t *t)
+{
+ gre_main_t * gm = &gre_main;
+ u64 key;
+
+ key = gre_mk_key(&t->tunnel_src, &t->tunnel_dst, t->outer_fib_index);
+ hash_unset (gm->tunnel_by_key, key);
+}
+
+static gre_tunnel_t *
+gre_tunnel_from_fib_node (fib_node_t *node)
+{
+#if (CLIB_DEBUG > 0)
+ ASSERT(FIB_NODE_TYPE_GRE_TUNNEL == node->fn_type);
+#endif
+ return ((gre_tunnel_t*) (((char*)node) -
+ STRUCT_OFFSET_OF(gre_tunnel_t, node)));
+}
+
+/*
+ * gre_tunnel_stack
+ *
+ * 'stack' (resolve the recursion for) the tunnel's midchain adjacency
+ */
+static void
+gre_tunnel_stack (gre_tunnel_t *gt)
+{
+ fib_link_t linkt;
+
+ /*
+ * find the adjacency that is contributed by the FIB entry
+ * that this tunnel resovles via, and use it as the next adj
+ * in the midchain
+ */
+ FOR_EACH_FIB_LINK(linkt)
+ {
+ if (ADJ_INDEX_INVALID != gt->adj_index[linkt])
+ {
+ adj_nbr_midchain_stack(
+ gt->adj_index[linkt],
+ fib_entry_contribute_ip_forwarding(gt->fib_entry_index));
+ }
+ }
+}
+
+/**
+ * Function definition to backwalk a FIB node
+ */
+static fib_node_back_walk_rc_t
+gre_tunnel_back_walk (fib_node_t *node,
+ fib_node_back_walk_ctx_t *ctx)
+{
+ gre_tunnel_stack(gre_tunnel_from_fib_node(node));
+
+ return (FIB_NODE_BACK_WALK_CONTINUE);
+}
+
+/**
+ * Function definition to get a FIB node from its index
+ */
+static fib_node_t*
+gre_tunnel_fib_node_get (fib_node_index_t index)
+{
+ gre_tunnel_t * gt;
+ gre_main_t * gm;
+
+ gm = &gre_main;
+ gt = pool_elt_at_index(gm->tunnels, index);
+
+ return (>->node);
+}
+
+/**
+ * Function definition to inform the FIB node that its last lock has gone.
+ */
+static void
+gre_tunnel_last_lock_gone (fib_node_t *node)
+{
+ /*
+ * The MPLS GRE tunnel is a root of the graph. As such
+ * it never has children and thus is never locked.
+ */
+ ASSERT(0);
+}
+
+/*
+ * Virtual function table registered by MPLS GRE tunnels
+ * for participation in the FIB object graph.
+ */
+const static fib_node_vft_t gre_vft = {
+ .fnv_get = gre_tunnel_fib_node_get,
+ .fnv_last_lock = gre_tunnel_last_lock_gone,
+ .fnv_back_walk = gre_tunnel_back_walk,
+};
+
+static int
+gre_proto_from_fib_link (fib_link_t link)
+{
+ switch (link)
+ {
+ case FIB_LINK_IP4:
+ return (GRE_PROTOCOL_ip4);
+ case FIB_LINK_IP6:
+ return (GRE_PROTOCOL_ip6);
+ case FIB_LINK_MPLS:
+ return (GRE_PROTOCOL_mpls_unicast);
+ }
+ ASSERT(0);
+ return (GRE_PROTOCOL_ip4);
+}
+
+static u8 *
+gre_rewrite (gre_tunnel_t * t,
+ fib_link_t link)
+{
+ ip4_and_gre_header_t * h0;
+ u8 * rewrite_data = 0;
+
+ vec_validate_init_empty (rewrite_data, sizeof (*h0) - 1, 0);
+
+ h0 = (ip4_and_gre_header_t *) rewrite_data;
+
+ if (t->teb)
+ {
+ h0->gre.protocol = clib_net_to_host_u16(GRE_PROTOCOL_teb);
+ }
+ else
+ {
+ h0->gre.protocol = clib_host_to_net_u16(gre_proto_from_fib_link(link));
+ }
+
+ h0->ip4.ip_version_and_header_length = 0x45;
+ h0->ip4.ttl = 254;
+ h0->ip4.protocol = IP_PROTOCOL_GRE;
+ /* $$$ fixup ip4 header length and checksum after-the-fact */
+ h0->ip4.src_address.as_u32 = t->tunnel_src.as_u32;
+ h0->ip4.dst_address.as_u32 = t->tunnel_dst.as_u32;
+ h0->ip4.checksum = ip4_header_checksum (&h0->ip4);
+
+ return (rewrite_data);
+}
+
+static int
+vnet_gre_tunnel_add (vnet_gre_add_del_tunnel_args_t *a,
+ u32 * sw_if_indexp)