+ if (vnet_hw_interface_get_flags (vnet_get_main (), t->hw_if_index) &
+ VNET_HW_INTERFACE_FLAG_LINK_UP)
+ {
+ adj_nbr_midchain_stack (ai,
+ fib_entry_contribute_ip_forwarding
+ (fib_entry_index));
+ }
+ else
+ {
+ adj_nbr_midchain_unstack (ai);
+ }
+}
+
+static void
+sixrd_tunnel_stack (adj_index_t ai, u32 fib_index)
+{
+ dpo_id_t dpo = DPO_INVALID;
+ sixrd_main_t *sm = &sixrd_main;
+ ip_adjacency_t *adj = adj_get (ai);
+ u32 sw_if_index = adj->rewrite_header.sw_if_index;
+
+ if ((vec_len (sm->tunnel_index_by_sw_if_index) < sw_if_index) ||
+ (~0 == sm->tunnel_index_by_sw_if_index[sw_if_index]))
+ return;
+
+ if ((vec_len (sm->tunnel_index_by_sw_if_index) < sw_if_index) ||
+ (sm->tunnel_index_by_sw_if_index[sw_if_index] == ~0))
+ return;
+
+ lookup_dpo_add_or_lock_w_fib_index (fib_index, DPO_PROTO_IP4,
+ LOOKUP_UNICAST, LOOKUP_INPUT_DST_ADDR,
+ LOOKUP_TABLE_FROM_CONFIG, &dpo);
+ adj_nbr_midchain_stack (ai, &dpo);
+}
+
+const static ip46_address_t sixrd_special_nh = {
+ .ip6 = {
+ .as_u64 = {
+ [0] = 0xffffffffffffffff,
+ [1] = 0xffffffffffffffff,
+ },
+ },
+};
+
+static void
+sixrd_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai)
+{
+ ip_adjacency_t *adj = adj_get (ai);
+ sixrd_tunnel_t *t = find_tunnel_by_sw_if_index (sw_if_index);
+
+ if (!memcmp (&sixrd_special_nh,
+ &adj->sub_type.nbr.next_hop, sizeof (sixrd_special_nh)))
+ {
+ adj_nbr_midchain_update_rewrite (ai, sixrd_fixup, t, ADJ_FLAG_NONE,
+ sixrd_build_rewrite (vnm, sw_if_index,
+ adj_get_link_type
+ (ai), NULL));
+ sixrd_tunnel_stack (ai, t->fib_index);
+ }
+ else
+ {
+ sixrd_adj_delegate_t *sixrd_ad;
+ ip4_address_t da4;
+
+ da4.as_u32 =
+ sixrd_get_addr_net (t, adj->sub_type.nbr.next_hop.as_u64[0]);
+
+ fib_prefix_t pfx = {
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_len = 32,
+ .fp_addr = {.ip4 = da4,}
+ ,
+ };
+
+ adj_nbr_midchain_update_rewrite
+ (ai, ip6ip_fixup, t,
+ ADJ_FLAG_NONE,
+ sixrd_build_rewrite (vnm, sw_if_index,
+ adj_get_link_type (ai), NULL));
+
+ sixrd_ad =
+ sixrd_adj_from_base (adj_delegate_get (adj, sixrd_adj_delegate_type));
+ if (NULL == sixrd_ad)
+ {
+ pool_get (sixrd_adj_delegate_pool, sixrd_ad);
+ fib_node_init (&sixrd_ad->sixrd_node, sixrd_fib_node_type);
+ sixrd_ad->adj_index = ai;
+ sixrd_ad->sixrd_fib_entry_index =
+ fib_table_entry_special_add (t->fib_index, &pfx,
+ FIB_SOURCE_RR, FIB_ENTRY_FLAG_NONE);
+ sixrd_ad->sixrd_sibling =
+ fib_entry_child_add (sixrd_ad->sixrd_fib_entry_index,
+ sixrd_fib_node_type,
+ sixrd_ad - sixrd_adj_delegate_pool);
+
+ adj_delegate_add (adj,
+ sixrd_adj_delegate_type,
+ sixrd_ad - sixrd_adj_delegate_pool);
+
+ ip6ip_tunnel_stack (ai, sixrd_ad->sixrd_fib_entry_index);
+ }
+ }
+}
+
+
+clib_error_t *
+sixrd_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
+{
+ /* Always up */
+ vnet_hw_interface_set_flags (vnm, hw_if_index,
+ VNET_HW_INTERFACE_FLAG_LINK_UP);
+ return /* no error */ 0;
+}
+
+/* *INDENT-OFF* */
+VNET_HW_INTERFACE_CLASS (sixrd_hw_interface_class) =
+{
+ .name = "ip6ip-6rd",
+ .build_rewrite = sixrd_build_rewrite,
+ .update_adjacency = sixrd_update_adj,
+};
+
+VNET_DEVICE_CLASS (sixrd_device_class) =
+{
+ .name = "ip6ip-6rd",
+ .admin_up_down_function = sixrd_interface_admin_up_down,
+#ifdef SOON
+ .clear counter = 0;
+#endif
+};
+/* *INDENT-ON* */
+
+static int
+sixrd_create_tunnel (ip6_address_t * ip6_prefix, u8 ip6_prefix_len,
+ ip4_address_t * ip4_prefix, u8 ip4_prefix_len,
+ ip4_address_t * ip4_src, u16 mtu, bool security_check,
+ u32 fib_index, u32 * sixrd_tunnel_index)
+{
+ sixrd_main_t *sm = &sixrd_main;
+ sixrd_tunnel_t *t;
+
+ if (fib_index == ~0)
+ return VNET_API_ERROR_NO_SUCH_FIB;
+
+ if ((ip6_prefix_len + 32 - ip4_prefix_len) > 64)
+ return VNET_API_ERROR_INVALID_VALUE;
+
+ /* Tunnel already configured */
+ if (find_tunnel (ip6_prefix))
+ return VNET_API_ERROR_INVALID_VALUE;
+ /* Tunnel already configured for this source */
+ if (find_tunnel_by_ip4_address (ip4_src))
+ return VNET_API_ERROR_INVALID_VALUE;
+
+ /* Get tunnel index */
+ pool_get_aligned (sm->tunnels, t, CLIB_CACHE_LINE_BYTES);
+ memset (t, 0, sizeof (*t));
+ t->tunnel_index = t - sm->tunnels;
+
+ /* Init tunnel struct */
+ t->ip4_prefix.as_u32 = ip4_prefix->as_u32;
+ t->ip4_prefix_len = ip4_prefix_len;
+ t->ip6_prefix = *ip6_prefix;
+ t->ip6_prefix_len = ip6_prefix_len;
+ t->ip4_src = *ip4_src;
+ t->mtu = mtu ? mtu : SIXRD_DEFAULT_MTU;
+ t->security_check = security_check;
+
+ t->shift = (ip4_prefix_len < 32) ?
+ 64 - ip6_prefix_len - (32 - ip4_prefix_len) : 0;
+
+ /* Create interface */
+ u32 hw_if_index = vnet_register_interface (vnet_get_main (),
+ sixrd_device_class.index,
+ t->tunnel_index,
+ sixrd_hw_interface_class.index,
+ t->tunnel_index);
+
+ /* Default the interface to up and enable IPv6 (payload) */
+ vnet_hw_interface_t *hi =
+ vnet_get_hw_interface (vnet_get_main (), hw_if_index);
+ t->hw_if_index = hw_if_index;
+ t->fib_index = fib_index;
+ t->sw_if_index = hi->sw_if_index;
+
+ hi->max_l3_packet_bytes[VLIB_RX] = hi->max_l3_packet_bytes[VLIB_TX] =
+ t->mtu;
+
+ vec_validate_init_empty (sm->tunnel_index_by_sw_if_index, hi->sw_if_index,
+ ~0);
+ sm->tunnel_index_by_sw_if_index[hi->sw_if_index] = t->tunnel_index;
+
+ hash_set (sm->tunnel_by_ip, ip4_src->as_u32, t->tunnel_index);
+
+ vnet_hw_interface_set_flags (vnet_get_main (), hw_if_index,
+ VNET_HW_INTERFACE_FLAG_LINK_UP);
+ vnet_sw_interface_set_flags (vnet_get_main (), hi->sw_if_index,
+ VNET_SW_INTERFACE_FLAG_ADMIN_UP);
+
+ /* Create IPv6 route/adjacency */
+ fib_prefix_t pfx6 = {
+ .fp_proto = FIB_PROTOCOL_IP6,
+ .fp_len = t->ip6_prefix_len,
+ .fp_addr = {.ip6 = t->ip6_prefix,}
+ ,