-static mpls_gre_tunnel_t *
-mpls_gre_tunnel_from_fib_node (fib_node_t *node)
-{
-#if (CLIB_DEBUG > 0)
- ASSERT(FIB_NODE_TYPE_MPLS_GRE_TUNNEL == node->fn_type);
-#endif
- return ((mpls_gre_tunnel_t*)node);
-}
-
-/*
- * mpls_gre_tunnel_stack
- *
- * 'stack' (resolve the recursion for) the tunnel's midchain adjacency
- */
-static void
-mpls_gre_tunnel_stack (mpls_gre_tunnel_t *mgt)
-{
- /*
- * 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
- */
- adj_nbr_midchain_stack(mgt->adj_index,
- fib_entry_contribute_ip_forwarding(mgt->fei));
-}
-
-/**
- * Function definition to backwalk a FIB node
- */
-static fib_node_back_walk_rc_t
-mpls_gre_tunnel_back_walk (fib_node_t *node,
- fib_node_back_walk_ctx_t *ctx)
-{
- mpls_gre_tunnel_stack(mpls_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*
-mpls_gre_tunnel_fib_node_get (fib_node_index_t index)
-{
- mpls_gre_tunnel_t * mgt;
- mpls_main_t * mm;
-
- mm = &mpls_main;
- mgt = pool_elt_at_index(mm->gre_tunnels, index);
-
- return (&mgt->mgt_node);
-}
-
-/**
- * Function definition to inform the FIB node that its last lock has gone.
- */
-static void
-mpls_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 mpls_gre_vft = {
- .fnv_get = mpls_gre_tunnel_fib_node_get,
- .fnv_last_lock = mpls_gre_tunnel_last_lock_gone,
- .fnv_back_walk = mpls_gre_tunnel_back_walk,
-};
-
-static mpls_gre_tunnel_t *
-mpls_gre_tunnel_find (ip4_address_t *src,
- ip4_address_t *dst,
- ip4_address_t *intfc,
- u32 inner_fib_index)
-{
- mpls_main_t * mm = &mpls_main;
- mpls_gre_tunnel_t *tp;
- int found_tunnel = 0;
-
- /* suppress duplicate mpls interface generation. */
- pool_foreach (tp, mm->gre_tunnels,
- ({
- /*
- * If we have a tunnel which matches (src, dst, intfc/mask)
- * AND the expected route is in the FIB, it's a dup
- */
- if (!memcmp (&tp->tunnel_src, src, sizeof (*src))
- && !memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
- && !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
- && tp->inner_fib_index == inner_fib_index)
- {
- found_tunnel = 1;
- goto found;
- }
- }));
-
-found:
- if (found_tunnel)
- {
- return (tp);
- }
- return (NULL);
-}
-
-int mpls_gre_tunnel_add (ip4_address_t *src,
- ip4_address_t *dst,
- ip4_address_t *intfc,
- u32 mask_width,
- u32 inner_fib_index,
- u32 outer_fib_index,
- u32 * tunnel_sw_if_index,
- u8 l2_only)
-{
- mpls_main_t * mm = &mpls_main;
- gre_main_t * gm = &gre_main;
- vnet_main_t * vnm = vnet_get_main();
- mpls_gre_tunnel_t *tp;
- ip_adjacency_t adj;
- u8 * rewrite_data;
- mpls_encap_t * e = 0;
- u32 hw_if_index = ~0;
- vnet_hw_interface_t * hi;
- u32 slot;
- const ip46_address_t zero_nh = {
- .ip4.as_u32 = 0,
- };
-
- tp = mpls_gre_tunnel_find(src,dst,intfc,inner_fib_index);
-
- /* Add, duplicate */
- if (NULL != tp)
- return VNET_API_ERROR_NO_SUCH_ENTRY;
-
- e = mpls_encap_by_fib_and_dest (mm, inner_fib_index, dst->as_u32);
- if (e == 0)
- return VNET_API_ERROR_NO_SUCH_LABEL;
-
- pool_get(mm->gre_tunnels, tp);
- memset (tp, 0, sizeof (*tp));
- fib_node_init(&tp->mgt_node,
- FIB_NODE_TYPE_MPLS_GRE_TUNNEL);
-
- if (vec_len (mm->free_gre_sw_if_indices) > 0)
- {
- hw_if_index =
- mm->free_gre_sw_if_indices[vec_len(mm->free_gre_sw_if_indices)-1];
- _vec_len (mm->free_gre_sw_if_indices) -= 1;
- hi = vnet_get_hw_interface (vnm, hw_if_index);
- hi->dev_instance = tp - mm->gre_tunnels;
- hi->hw_instance = tp - mm->gre_tunnels;
- }
- else
- {
- hw_if_index = vnet_register_interface
- (vnm, mpls_gre_device_class.index, tp - mm->gre_tunnels,
- mpls_gre_hw_interface_class.index,
- tp - mm->gre_tunnels);
- hi = vnet_get_hw_interface (vnm, hw_if_index);
-
- /* ... to make the IP and L2 x-connect cases identical */
- slot = vlib_node_add_named_next_with_slot
- (vnm->vlib_main, hi->tx_node_index,
- "mpls-post-rewrite", MPLS_GRE_OUTPUT_NEXT_POST_REWRITE);
-
- ASSERT (slot == MPLS_GRE_OUTPUT_NEXT_POST_REWRITE);
- }
-
- *tunnel_sw_if_index = hi->sw_if_index;
- vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
- VNET_SW_INTERFACE_FLAG_ADMIN_UP);
- vec_validate(ip4_main.fib_index_by_sw_if_index, *tunnel_sw_if_index);
- ip4_main.fib_index_by_sw_if_index[*tunnel_sw_if_index] = outer_fib_index;
-
- tp->hw_if_index = hw_if_index;
-
- /* bind the MPLS and IPv4 FIBs to the interface and enable */
- vec_validate(mm->fib_index_by_sw_if_index, hi->sw_if_index);
- mm->fib_index_by_sw_if_index[hi->sw_if_index] = inner_fib_index;
- mpls_sw_interface_enable_disable(mm, hi->sw_if_index, 1);
- ip4_main.fib_index_by_sw_if_index[hi->sw_if_index] = inner_fib_index;
- ip4_sw_interface_enable_disable(hi->sw_if_index, 1);
-
- tp->tunnel_src.as_u32 = src->as_u32;
- tp->tunnel_dst.as_u32 = dst->as_u32;
- tp->intfc_address.as_u32 = intfc->as_u32;
- tp->mask_width = mask_width;
- tp->inner_fib_index = inner_fib_index;
- tp->outer_fib_index = outer_fib_index;
- tp->encap_index = e - mm->encaps;
- tp->l2_only = l2_only;
-
- /* Add the tunnel to the hash table of all GRE tunnels */
- u64 key = (u64)src->as_u32 << 32 | (u64)dst->as_u32;
-
- ASSERT(NULL == hash_get (gm->tunnel_by_key, key));
- hash_set (gm->tunnel_by_key, key, tp - mm->gre_tunnels);
-
- /* Create the adjacency and add to v4 fib */
- memset(&adj, 0, sizeof (adj));
- adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
-
- rewrite_data = mpls_gre_rewrite (mm, tp);
- if (rewrite_data == 0)
- {
- if (*tunnel_sw_if_index != ~0)
- {
- hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
- vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
- 0 /* admin down */);
- vec_add1 (mm->free_gre_sw_if_indices, tp->hw_if_index);
- }
- pool_put (mm->gre_tunnels, tp);
- return VNET_API_ERROR_NO_SUCH_LABEL;
- }
-
- /* Save a copy of the rewrite data for L2 x-connect */
- vec_free (tp->rewrite_data);
-
- tp->rewrite_data = rewrite_data;
-
- if (!l2_only)
- {
- /*
- * source the FIB entry for the tunnel's destination
- * and become a child thereof. The tunnel will then get poked
- * when the forwarding for the entry updates, and the tunnel can
- * re-stack accordingly
- */
- const fib_prefix_t tun_dst_pfx = {
- .fp_len = 32,
- .fp_proto = FIB_PROTOCOL_IP4,
- .fp_addr = {
- .ip4 = *dst,
- }
- };
-
- tp->fei = fib_table_entry_special_add(outer_fib_index,
- &tun_dst_pfx,
- FIB_SOURCE_RR,
- FIB_ENTRY_FLAG_NONE,
- ADJ_INDEX_INVALID);
- tp->sibling_index = fib_entry_child_add(tp->fei,
- FIB_NODE_TYPE_MPLS_GRE_TUNNEL,
- tp - mm->gre_tunnels);
-
- /*
- * create and update the midchain adj this tunnel sources.
- * This is the adj the route we add below will resolve to.
- */
- tp->adj_index = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
- FIB_LINK_IP4,
- &zero_nh,
- hi->sw_if_index);
-
- adj_nbr_midchain_update_rewrite(tp->adj_index,
- mpls_gre_fixup,
- ADJ_MIDCHAIN_FLAG_NONE,
- rewrite_data);
- mpls_gre_tunnel_stack(tp);
-
- /*
- * Update the route for the tunnel's subnet to point through the tunnel
- */
- const fib_prefix_t tun_sub_net_pfx = {
- .fp_len = tp->mask_width,
- .fp_proto = FIB_PROTOCOL_IP4,
- .fp_addr = {
- .ip4 = tp->intfc_address,
- },
- };
-
- fib_table_entry_update_one_path(inner_fib_index,
- &tun_sub_net_pfx,
- FIB_SOURCE_INTERFACE,
- (FIB_ENTRY_FLAG_CONNECTED |
- FIB_ENTRY_FLAG_ATTACHED),
- FIB_PROTOCOL_IP4,
- &zero_nh,
- hi->sw_if_index,
- ~0, // invalid fib index
- 1,
- MPLS_LABEL_INVALID,
- FIB_ROUTE_PATH_FLAG_NONE);
- }
-
- return 0;
-}
-
-static int
-mpls_gre_tunnel_del (ip4_address_t *src,
- ip4_address_t *dst,
- ip4_address_t *intfc,
- u32 mask_width,
- u32 inner_fib_index,
- u32 outer_fib_index,
- u32 * tunnel_sw_if_index,
- u8 l2_only)
-{
- mpls_main_t * mm = &mpls_main;
- vnet_main_t * vnm = vnet_get_main();
- gre_main_t * gm = &gre_main;
- mpls_gre_tunnel_t *tp;
- vnet_hw_interface_t * hi;
-
- tp = mpls_gre_tunnel_find(src,dst,intfc,inner_fib_index);
-
- /* Delete, and we can't find the tunnel */
- if (NULL == tp)
- return VNET_API_ERROR_NO_SUCH_ENTRY;
-
- hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
-
- if (!l2_only)
- {
- /*
- * unsource the FIB entry for the tunnel's destination
- */
- const fib_prefix_t tun_dst_pfx = {
- .fp_len = 32,
- .fp_proto = FIB_PROTOCOL_IP4,
- .fp_addr = {
- .ip4 = *dst,
- }
- };
-
- fib_entry_child_remove(tp->fei,
- tp->sibling_index);
- fib_table_entry_special_remove(outer_fib_index,
- &tun_dst_pfx,
- FIB_SOURCE_RR);
- tp->fei = FIB_NODE_INDEX_INVALID;
- adj_unlock(tp->adj_index);
-
- /*
- * unsource the route for the tunnel's subnet
- */
- const fib_prefix_t tun_sub_net_pfx = {
- .fp_len = tp->mask_width,
- .fp_proto = FIB_PROTOCOL_IP4,
- .fp_addr = {
- .ip4 = tp->intfc_address,
- },
- };
-
- fib_table_entry_delete(inner_fib_index,
- &tun_sub_net_pfx,
- FIB_SOURCE_INTERFACE);
- }
-
- u64 key = ((u64)tp->tunnel_src.as_u32 << 32 |
- (u64)tp->tunnel_src.as_u32);
-
- hash_unset (gm->tunnel_by_key, key);
- mpls_sw_interface_enable_disable(mm, hi->sw_if_index, 0);
- ip4_sw_interface_enable_disable(hi->sw_if_index, 0);
-
- vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
- 0 /* admin down */);
- vec_add1 (mm->free_gre_sw_if_indices, tp->hw_if_index);
- vec_free (tp->rewrite_data);
- fib_node_deinit(&tp->mgt_node);
- pool_put (mm->gre_tunnels, tp);
-
- return 0;
-}
-
-int
-vnet_mpls_gre_add_del_tunnel (ip4_address_t *src,
- ip4_address_t *dst,
- ip4_address_t *intfc,
- u32 mask_width,
- u32 inner_fib_id, u32 outer_fib_id,
- u32 * tunnel_sw_if_index,
- u8 l2_only,
- u8 is_add)
-{
- u32 inner_fib_index = 0;
- u32 outer_fib_index = 0;
- u32 dummy;
- ip4_main_t * im = &ip4_main;
-
- /* No questions, no answers */
- if (NULL == tunnel_sw_if_index)
- tunnel_sw_if_index = &dummy;
-
- *tunnel_sw_if_index = ~0;
-
- if (inner_fib_id != (u32)~0)
- {
- uword * p;
-
- p = hash_get (im->fib_index_by_table_id, inner_fib_id);
- if (! p)
- return VNET_API_ERROR_NO_SUCH_INNER_FIB;
- inner_fib_index = p[0];
- }
-
- if (outer_fib_id != 0)
- {
- uword * p;
-
- p = hash_get (im->fib_index_by_table_id, outer_fib_id);
- if (! p)
- return VNET_API_ERROR_NO_SUCH_FIB;
- outer_fib_index = p[0];
- }
-
- if (is_add)
- {
- return (mpls_gre_tunnel_add(src,dst,intfc, mask_width,
- inner_fib_index,
- outer_fib_index,
- tunnel_sw_if_index,
- l2_only));
- }
- else
- {
- return (mpls_gre_tunnel_del(src,dst,intfc, mask_width,
- inner_fib_index,
- outer_fib_index,
- tunnel_sw_if_index,
- l2_only));
- }
-}
-
-/*
- * Remove all mpls tunnels in the specified fib
- */
-int vnet_mpls_gre_delete_fib_tunnels (u32 fib_id)
-{
- mpls_main_t * mm = &mpls_main;
- vnet_main_t * vnm = mm->vnet_main;
- mpls_gre_tunnel_t *tp;
- u32 fib_index = 0;
- u32 * tunnels_to_delete = 0;
- vnet_hw_interface_t * hi;
- int i;
-
- fib_index = ip4_fib_index_from_table_id(fib_id);
- if (~0 == fib_index)
- return VNET_API_ERROR_NO_SUCH_INNER_FIB;
-
- pool_foreach (tp, mm->gre_tunnels,
- ({
- if (tp->inner_fib_index == fib_index)
- vec_add1 (tunnels_to_delete, tp - mm->gre_tunnels);
- }));
-
- for (i = 0; i < vec_len(tunnels_to_delete); i++) {
- tp = pool_elt_at_index (mm->gre_tunnels, tunnels_to_delete[i]);
-
- /* Delete, the route if not already gone */
- if (FIB_NODE_INDEX_INVALID != tp->fei && !tp->l2_only)
- {
- const fib_prefix_t tun_dst_pfx = {
- .fp_len = 32,
- .fp_proto = FIB_PROTOCOL_IP4,
- .fp_addr = {
- .ip4 = tp->tunnel_dst,
- }
- };
-
- fib_entry_child_remove(tp->fei,
- tp->sibling_index);
- fib_table_entry_special_remove(tp->outer_fib_index,
- &tun_dst_pfx,
- FIB_SOURCE_RR);
- tp->fei = FIB_NODE_INDEX_INVALID;
- adj_unlock(tp->adj_index);
-
- const fib_prefix_t tun_sub_net_pfx = {
- .fp_len = tp->mask_width,
- .fp_proto = FIB_PROTOCOL_IP4,
- .fp_addr = {
- .ip4 = tp->intfc_address,
- },
- };
-
- fib_table_entry_delete(tp->inner_fib_index,
- &tun_sub_net_pfx,
- FIB_SOURCE_INTERFACE);
- }
-
- hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
- vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
- 0 /* admin down */);
- vec_add1 (mm->free_gre_sw_if_indices, tp->hw_if_index);
- vec_free (tp->rewrite_data);
- pool_put (mm->gre_tunnels, tp);
- }
-
- vec_free(tunnels_to_delete);
-
- return (0);
-}
-
-static clib_error_t *
-create_mpls_gre_tunnel_command_fn (vlib_main_t * vm,
- unformat_input_t * input,
- vlib_cli_command_t * cmd)
-{
- unformat_input_t _line_input, * line_input = &_line_input;
- ip4_address_t src, dst, intfc;
- int src_set = 0, dst_set = 0, intfc_set = 0;
- u32 mask_width;
- u32 inner_fib_id = (u32)~0;
- u32 outer_fib_id = 0;
- int rv;
- u8 is_del = 0;
- u8 l2_only = 0;
- u32 tunnel_intfc_sw_if_index = ~0;
-
- /* Get a line of input. */
- if (! unformat_user (input, unformat_line_input, line_input))
- return 0;
-
- while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
- {
- if (unformat (line_input, "src %U",
- unformat_ip4_address, &src))
- src_set = 1;
- else if (unformat (line_input, "dst %U",
- unformat_ip4_address, &dst))
- dst_set = 1;
- else if (unformat (line_input, "intfc %U/%d",
- unformat_ip4_address, &intfc, &mask_width))
- intfc_set = 1;
- else if (unformat (line_input, "inner-fib-id %d", &inner_fib_id))
- ;
- else if (unformat (line_input, "outer-fib-id %d", &outer_fib_id))
- ;
- else if (unformat (line_input, "del"))
- is_del = 1;
- else if (unformat (line_input, "l2-only"))
- l2_only = 1;
- else
- return clib_error_return (0, "unknown input '%U'",
- format_unformat_error, line_input);
- }
-
- if (!src_set)
- return clib_error_return (0, "missing: src <ip-address>");
-
- if (!dst_set)
- return clib_error_return (0, "missing: dst <ip-address>");
-
- if (!intfc_set)
- return clib_error_return (0, "missing: intfc <ip-address>/<mask-width>");
-
-
- rv = vnet_mpls_gre_add_del_tunnel (&src, &dst, &intfc, mask_width,
- inner_fib_id, outer_fib_id,
- &tunnel_intfc_sw_if_index,
- l2_only, !is_del);
-
- switch (rv)
- {
- case 0:
- if (!is_del)
- vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), tunnel_intfc_sw_if_index);
- break;
-
- case VNET_API_ERROR_NO_SUCH_INNER_FIB:
- return clib_error_return (0, "inner fib ID %d doesn't exist\n",
- inner_fib_id);
- case VNET_API_ERROR_NO_SUCH_FIB:
- return clib_error_return (0, "outer fib ID %d doesn't exist\n",
- outer_fib_id);
-
- case VNET_API_ERROR_NO_SUCH_ENTRY:
- return clib_error_return (0, "tunnel not found\n");
-
- case VNET_API_ERROR_NO_SUCH_LABEL:
- /*
- * This happens when there's no MPLS label for the dst address
- * no need for two error messages.
- */
- break;
-
- default:
- return clib_error_return (0, "vnet_mpls_gre_add_del_tunnel returned %d",
- rv);
- }
- return 0;
-}
-
-VLIB_CLI_COMMAND (create_mpls_tunnel_command, static) = {
- .path = "create mpls gre tunnel",
- .short_help =
- "create mpls gre tunnel [del] src <addr> dst <addr> intfc <addr>/<mw>",
- .function = create_mpls_gre_tunnel_command_fn,
-};
-