#include <vnet/vnet.h>
#include <vnet/pg/pg.h>
#include <vnet/gre/gre.h>
-#include <vnet/mpls-gre/mpls.h>
+#include <vnet/mpls/mpls.h>
+#include <vnet/fib/ip4_fib.h>
+#include <vnet/adj/adj_midchain.h>
+#include <vnet/dpo/classify_dpo.h>
static uword mpls_gre_set_rewrite (vnet_main_t * vnm,
u32 sw_if_index,
.set_rewrite = mpls_eth_set_rewrite,
};
-#define foreach_mpls_post_rewrite_next \
- _ (IP4_LOOKUP, "ip4-lookup")
-
-typedef enum {
-#define _(s,n) MPLS_POST_REWRITE_NEXT_##s,
- foreach_mpls_post_rewrite_next
-#undef _
- MPLS_POST_REWRITE_N_NEXT,
-} mpls_post_rewrite_next_t;
-
+/**
+ * A conversion of DPO next object tpyes to VLIB graph next nodes from
+ * the mpls_post_rewrite node
+ */
+static const int dpo_next_2_mpls_post_rewrite[DPO_LAST] = {
+ [DPO_LOAD_BALANCE] = IP_LOOKUP_NEXT_LOAD_BALANCE,
+};
static uword
mpls_post_rewrite (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * from_frame)
{
+ ip4_main_t * im = &ip4_main;
+ ip_lookup_main_t * lm = &im->lookup_main;
u32 n_left_from, next_index, * from, * to_next;
- u16 old_l0 = 0, old_l1 = 0;
+ u16 old_l0 = 0; //, old_l1 = 0;
from = vlib_frame_vector_args (from_frame);
n_left_from = from_frame->n_vectors;
vlib_get_next_frame (vm, node, next_index,
to_next, n_left_to_next);
- while (n_left_from >= 4 && n_left_to_next >= 2)
- {
- u32 bi0, bi1;
- vlib_buffer_t * b0, * b1;
- ip4_header_t * ip0, * ip1;
- u32 next0 = MPLS_POST_REWRITE_NEXT_IP4_LOOKUP;
- u32 next1 = MPLS_POST_REWRITE_NEXT_IP4_LOOKUP;
- u16 new_l0, new_l1;
- ip_csum_t sum0, sum1;
-
- /* Prefetch next iteration. */
- {
- vlib_buffer_t * p2, * p3;
+ /* while (n_left_from >= 4 && n_left_to_next >= 2) */
+ /* { */
+ /* u32 bi0, bi1; */
+ /* vlib_buffer_t * b0, * b1; */
+ /* ip4_header_t * ip0, * ip1; */
+ /* u32 next0; */
+ /* u32 next1; */
+ /* u16 new_l0, new_l1, adj_index0, adj_index1; */
+ /* ip_csum_t sum0, sum1; */
+ /* ip_adjacency_t *adj0, *adj1; */
+
+ /* /\* Prefetch next iteration. *\/ */
+ /* { */
+ /* vlib_buffer_t * p2, * p3; */
+
+ /* p2 = vlib_get_buffer (vm, from[2]); */
+ /* p3 = vlib_get_buffer (vm, from[3]); */
+
+ /* vlib_prefetch_buffer_header (p2, LOAD); */
+ /* vlib_prefetch_buffer_header (p3, LOAD); */
+
+ /* CLIB_PREFETCH (p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); */
+ /* CLIB_PREFETCH (p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); */
+ /* } */
+
+ /* bi0 = from[0]; */
+ /* bi1 = from[1]; */
+ /* to_next[0] = bi0; */
+ /* to_next[1] = bi1; */
+ /* from += 2; */
+ /* to_next += 2; */
+ /* n_left_to_next -= 2; */
+ /* n_left_from -= 2; */
+
+
+ /* b0 = vlib_get_buffer (vm, bi0); */
+ /* b1 = vlib_get_buffer (vm, bi1); */
+ /* ip0 = vlib_buffer_get_current (b0); */
+ /* ip1 = vlib_buffer_get_current (b1); */
+
+ /* /\* Note: the tunnel rewrite sets up sw_if_index[VLIB_TX] *\/ */
- p2 = vlib_get_buffer (vm, from[2]);
- p3 = vlib_get_buffer (vm, from[3]);
+ /* /\* set the GRE (outer) ip packet length, fix the bloody checksum *\/ */
+ /* sum0 = ip0->checksum; */
+ /* sum1 = ip1->checksum; */
- vlib_prefetch_buffer_header (p2, LOAD);
- vlib_prefetch_buffer_header (p3, LOAD);
+ /* /\* old_l0, old_l1 always 0, see the rewrite setup *\/ */
+ /* new_l0 = */
+ /* clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); */
+ /* new_l1 = */
+ /* clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1)); */
+
+ /* sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t, */
+ /* length /\* changed member *\/); */
+ /* sum1 = ip_csum_update (sum1, old_l1, new_l1, ip4_header_t, */
+ /* length /\* changed member *\/); */
+ /* ip0->checksum = ip_csum_fold (sum0); */
+ /* ip1->checksum = ip_csum_fold (sum1); */
+ /* ip0->length = new_l0; */
+ /* ip1->length = new_l1; */
- CLIB_PREFETCH (p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
- CLIB_PREFETCH (p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
- }
+ /* /\* replace the TX adj in the packet with the next in the chain *\/ */
+ /* adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX]; */
+ /* adj_index1 = vnet_buffer (b1)->ip.adj_index[VLIB_TX]; */
- bi0 = from[0];
- bi1 = from[1];
- to_next[0] = bi0;
- to_next[1] = bi1;
- from += 2;
- to_next += 2;
- n_left_to_next -= 2;
- n_left_from -= 2;
+ /* adj0 = ip_get_adjacency (lm, adj_index0); */
+ /* adj1 = ip_get_adjacency (lm, adj_index1); */
+ /* ASSERT(adj0->sub_type.midchain.adj_index != ADJ_INDEX_INVALID); */
+ /* ASSERT(adj1->sub_type.midchain.adj_index != ADJ_INDEX_INVALID); */
- b0 = vlib_get_buffer (vm, bi0);
- b1 = vlib_get_buffer (vm, bi1);
- ip0 = vlib_buffer_get_current (b0);
- ip1 = vlib_buffer_get_current (b1);
-
- /* Note: the tunnel rewrite sets up sw_if_index[VLIB_TX] */
+ /* adj_index0 = adj0->sub_type.midchain.adj_index; */
+ /* adj_index1 = adj1->sub_type.midchain.adj_index; */
- /* set the GRE (outer) ip packet length, fix the bloody checksum */
- sum0 = ip0->checksum;
- sum1 = ip1->checksum;
+ /* vnet_buffer (b0)->ip.adj_index[VLIB_TX] = adj_index0; */
+ /* vnet_buffer (b1)->ip.adj_index[VLIB_TX] = adj_index1; */
- /* old_l0, old_l1 always 0, see the rewrite setup */
- new_l0 =
- clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
- new_l1 =
- clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1));
-
- sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
- length /* changed member */);
- sum1 = ip_csum_update (sum1, old_l1, new_l1, ip4_header_t,
- length /* changed member */);
- ip0->checksum = ip_csum_fold (sum0);
- ip1->checksum = ip_csum_fold (sum1);
- ip0->length = new_l0;
- ip1->length = new_l1;
+ /* /\* get the next adj in the chain to determine the next graph node *\/ */
+ /* adj0 = ip_get_adjacency (lm, adj_index0); */
+ /* adj1 = ip_get_adjacency (lm, adj_index1); */
+
+ /* next0 = adj0->lookup_next_index; */
+ /* next1 = adj1->lookup_next_index; */
+
+ /* vlib_validate_buffer_enqueue_x2 (vm, node, next_index, */
+ /* to_next, n_left_to_next, */
+ /* bi0, bi1, next0, next1); */
+ /* } */
- vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
- to_next, n_left_to_next,
- bi0, bi1, next0, next1);
- }
-
while (n_left_from > 0 && n_left_to_next > 0)
{
+ ip_adjacency_t * adj0;
u32 bi0;
vlib_buffer_t * b0;
ip4_header_t * ip0;
- u32 next0 = MPLS_POST_REWRITE_NEXT_IP4_LOOKUP;
- u16 new_l0;
+ u32 next0;
+ u16 new_l0, adj_index0;
ip_csum_t sum0;
bi0 = from[0];
ip0->checksum = ip_csum_fold (sum0);
ip0->length = new_l0;
+ /* replace the TX adj in the packet with the next in the chain */
+ adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
+
+ ASSERT(adj_index0);
+
+ adj0 = ip_get_adjacency (lm, adj_index0);
+ ASSERT(adj0->sub_type.midchain.next_dpo.dpoi_index != ADJ_INDEX_INVALID);
+ adj_index0 = adj0->sub_type.midchain.next_dpo.dpoi_index;
+ vnet_buffer (b0)->ip.adj_index[VLIB_TX] = adj_index0;
+
+ /* get the next adj in the chain to determine the next graph node */
+ ASSERT(0);
+ next0 = 0; //adj0->sub_type.midchain.next_dpo.dpoi_next;
+
vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
to_next, n_left_to_next,
bi0, next0);
.runtime_data_bytes = 0,
- .n_next_nodes = MPLS_POST_REWRITE_N_NEXT,
- .next_nodes = {
-#define _(s,n) [MPLS_POST_REWRITE_NEXT_##s] = n,
- foreach_mpls_post_rewrite_next
-#undef _
- },
+ .n_next_nodes = IP_LOOKUP_N_NEXT,
+ .next_nodes = IP4_LOOKUP_NEXT_NODES,
};
VLIB_NODE_FUNCTION_MULTIARCH (mpls_post_rewrite_node, mpls_post_rewrite)
return (rewrite_data);
}
-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)
+u8
+mpls_sw_interface_is_enabled (u32 sw_if_index)
{
- ip4_main_t * im = &ip4_main;
- ip_lookup_main_t * lm = &im->lookup_main;
- mpls_main_t * mm = &mpls_main;
- vnet_main_t * vnm = vnet_get_main();
- ip4_address_t zero;
- mpls_gre_tunnel_t *tp;
- int need_route_add_del = 1;
- u32 inner_fib_index = 0;
- u32 outer_fib_index = 0;
- ip_adjacency_t adj;
- u32 adj_index;
- u8 * rewrite_data;
- int found_tunnel = 0;
- mpls_encap_t * e = 0;
- u32 hw_if_index = ~0;
- vnet_hw_interface_t * hi;
- u32 slot;
- u32 dummy;
-
- zero.as_u32 = 0;
-
- /* No questions, no answers */
- if (tunnel_sw_if_index == 0)
- tunnel_sw_if_index = &dummy;
+ mpls_main_t * mm = &mpls_main;
- *tunnel_sw_if_index = ~0;
+ if (vec_len(mm->mpls_enabled_by_sw_if_index) < sw_if_index)
+ return (0);
- if (inner_fib_id != (u32)~0)
+ return (mm->mpls_enabled_by_sw_if_index[sw_if_index]);
+}
+
+void
+mpls_sw_interface_enable_disable (mpls_main_t * mm,
+ u32 sw_if_index,
+ u8 is_enable)
+{
+ mpls_interface_state_change_callback_t *callback;
+ vlib_main_t * vm = vlib_get_main();
+ ip_config_main_t * cm = &mm->rx_config_mains;
+ vnet_config_main_t * vcm = &cm->config_main;
+ u32 lookup_feature_index;
+ fib_node_index_t lfib_index;
+ u32 ci;
+
+ vec_validate_init_empty (mm->mpls_enabled_by_sw_if_index, sw_if_index, 0);
+
+ /*
+ * enable/disable only on the 1<->0 transition
+ */
+ if (is_enable)
{
- 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 (1 != ++mm->mpls_enabled_by_sw_if_index[sw_if_index])
+ return;
- if (outer_fib_id != 0)
+ lfib_index = fib_table_find_or_create_and_lock(FIB_PROTOCOL_MPLS,
+ MPLS_FIB_DEFAULT_TABLE_ID);
+ vec_validate(mm->fib_index_by_sw_if_index, 0);
+ mm->fib_index_by_sw_if_index[sw_if_index] = lfib_index;
+ }
+ else
{
- 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];
+ ASSERT(mm->mpls_enabled_by_sw_if_index[sw_if_index] > 0);
+ if (0 != --mm->mpls_enabled_by_sw_if_index[sw_if_index])
+ return;
+
+ fib_table_unlock(mm->fib_index_by_sw_if_index[sw_if_index],
+ FIB_PROTOCOL_MPLS);
}
- /* 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
+ vec_validate_init_empty (cm->config_index_by_sw_if_index, sw_if_index, ~0);
+ ci = cm->config_index_by_sw_if_index[sw_if_index];
+
+ lookup_feature_index = mm->mpls_rx_feature_lookup;
+
+ if (is_enable)
+ ci = vnet_config_add_feature (vm, vcm,
+ ci,
+ lookup_feature_index,
+ /* config data */ 0,
+ /* # bytes of config data */ 0);
+ else
+ ci = vnet_config_del_feature (vm, vcm, ci,
+ lookup_feature_index,
+ /* config data */ 0,
+ /* # bytes of config data */ 0);
+
+ cm->config_index_by_sw_if_index[sw_if_index] = ci;
+
+ /*
+ * notify all interested clients of the change of state.
+ */
+ vec_foreach(callback, mm->mpls_interface_state_change_callbacks)
+ {
+ (*callback)(sw_if_index, is_enable);
+ }
+}
+
+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
*/
- 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)
- {
- ip4_fib_t * fib = vec_elt_at_index (im->fibs, inner_fib_index);
- uword * hash = fib->adj_index_by_dst_address[mask_width];
- uword key = intfc->as_u32 & im->fib_masks[mask_width];
- uword *p = hash_get (hash, key);
+ adj_nbr_midchain_stack(mgt->adj_index,
+ fib_entry_contribute_ip_forwarding(mgt->fei));
+}
- found_tunnel = 1;
+/**
+ * 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));
- if (is_add)
- {
- /* A dup, and the route is in the fib. Done */
- if (p || l2_only)
- return 1;
- else
- {
- /* Reinstall the route (and other stuff) */
- e = mpls_encap_by_fib_and_dest (mm, inner_fib_index,
- dst->as_u32);
- if (e == 0)
- return VNET_API_ERROR_NO_SUCH_LABEL;
- goto reinstall_it;
- }
- }
- else
- {
- /* Delete, the route is already gone? */
- if (!p)
- need_route_add_del = 0;
- goto add_del_route;
- }
+ return (FIB_NODE_BACK_WALK_CONTINUE);
+}
- }
- }));
-
- /* Delete, and we can't find the tunnel */
- if (is_add == 0 && found_tunnel == 0)
- return VNET_API_ERROR_NO_SUCH_ENTRY;
+/**
+ * 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;
- e = mpls_encap_by_fib_and_dest (mm, inner_fib_index, dst->as_u32);
- if (e == 0)
- return VNET_API_ERROR_NO_SUCH_LABEL;
+ mm = &mpls_main;
+ mgt = pool_elt_at_index(mm->gre_tunnels, index);
- pool_get(mm->gre_tunnels, tp);
- memset (tp, 0, sizeof (*tp));
+ return (&mgt->mgt_node);
+}
- if (vec_len (mm->free_gre_sw_if_indices) > 0)
+/**
+ * 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)
{
- 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;
+ return (tp);
}
- else
+ 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 = 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);
+ 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;
+ }
- /* ... 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);
+ /* Save a copy of the rewrite data for L2 x-connect */
+ vec_free (tp->rewrite_data);
- ASSERT (slot == MPLS_GRE_OUTPUT_NEXT_POST_REWRITE);
+ 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_post_rewrite_node.index,
+ 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;
- *tunnel_sw_if_index = hi->sw_if_index;
- vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
- VNET_SW_INTERFACE_FLAG_ADMIN_UP);
+ tp = mpls_gre_tunnel_find(src,dst,intfc,inner_fib_index);
- tp->hw_if_index = hw_if_index;
-
- reinstall_it:
- 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;
+ /* Delete, and we can't find the tunnel */
+ if (NULL == tp)
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
- /* Create the adjacency and add to v4 fib */
- memset(&adj, 0, sizeof (adj));
- adj.explicit_fib_index = ~0;
- adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
-
- rewrite_data = mpls_gre_rewrite (mm, tp);
- if (rewrite_data == 0)
+ hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
+
+ if (!l2_only)
{
- 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;
+ /*
+ * 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);
}
-
- /* Save a copy of the rewrite data for L2 x-connect */
- vec_free (tp->rewrite_data);
- tp->rewrite_data = rewrite_data;
+ u64 key = ((u64)tp->tunnel_src.as_u32 << 32 |
+ (u64)tp->tunnel_src.as_u32);
- vnet_rewrite_for_tunnel
- (vnm,
- outer_fib_index /* tx_sw_if_index, aka outer fib ID */,
- ip4_rewrite_node.index,
- mpls_post_rewrite_node.index,
- &adj.rewrite_header,
- rewrite_data, vec_len(rewrite_data));
-
- if (!l2_only)
- ip_add_adjacency (lm, &adj, 1 /* one adj */,
- &adj_index);
+ 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;
- add_del_route:
+ /* No questions, no answers */
+ if (NULL == tunnel_sw_if_index)
+ tunnel_sw_if_index = &dummy;
- if (need_route_add_del && !l2_only)
+ *tunnel_sw_if_index = ~0;
+
+ if (inner_fib_id != (u32)~0)
{
- if (is_add)
- ip4_add_del_route_next_hop (im,
- IP4_ROUTE_FLAG_ADD,
- &tp->intfc_address,
- tp->mask_width,
- &zero /* no next hop */,
- (u32)~0 /* next_hop_sw_if_index */,
- 1 /* weight */,
- adj_index,
- tp->inner_fib_index);
- else
- {
- ip4_add_del_route_args_t a;
- memset (&a, 0, sizeof (a));
-
- a.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_DEL;
- a.table_index_or_table_id = tp->inner_fib_index;
- a.dst_address = tp->intfc_address;
- a.dst_address_length = tp->mask_width;
- a.adj_index = ~0;
-
- ip4_add_del_route (im, &a);
- ip4_maybe_remap_adjacencies (im, tp->inner_fib_index,
- IP4_ROUTE_FLAG_FIB_INDEX);
- }
+ 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 (is_add == 0 && found_tunnel)
+ if (outer_fib_id != 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);
- vec_free (tp->rewrite_data);
- pool_put (mm->gre_tunnels, tp);
+ 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];
}
- return 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));
+ }
}
/*
*/
int vnet_mpls_gre_delete_fib_tunnels (u32 fib_id)
{
- ip4_main_t * im = &ip4_main;
mpls_main_t * mm = &mpls_main;
vnet_main_t * vnm = mm->vnet_main;
mpls_gre_tunnel_t *tp;
u32 fib_index = 0;
- uword * p;
u32 * tunnels_to_delete = 0;
vnet_hw_interface_t * hi;
- ip4_fib_t * fib;
int i;
- p = hash_get (im->fib_index_by_table_id, fib_id);
- if (! p)
+ fib_index = ip4_fib_index_from_table_id(fib_id);
+ if (~0 == fib_index)
return VNET_API_ERROR_NO_SUCH_INNER_FIB;
- fib_index = p[0];
pool_foreach (tp, mm->gre_tunnels,
({
vec_add1 (tunnels_to_delete, tp - mm->gre_tunnels);
}));
- fib = vec_elt_at_index (im->fibs, fib_index);
-
for (i = 0; i < vec_len(tunnels_to_delete); i++) {
tp = pool_elt_at_index (mm->gre_tunnels, tunnels_to_delete[i]);
- uword * hash = fib->adj_index_by_dst_address[tp->mask_width];
- uword key = tp->intfc_address.as_u32 & im->fib_masks[tp->mask_width];
- uword *p = hash_get (hash, key);
- ip4_add_del_route_args_t a;
/* Delete, the route if not already gone */
- if (p && !tp->l2_only)
- {
- memset (&a, 0, sizeof (a));
- a.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_DEL;
- a.table_index_or_table_id = tp->inner_fib_index;
- a.dst_address = tp->intfc_address;
- a.dst_address_length = tp->mask_width;
- a.adj_index = ~0;
- ip4_add_del_route (im, &a);
- ip4_maybe_remap_adjacencies (im, tp->inner_fib_index,
- IP4_ROUTE_FLAG_FIB_INDEX);
- }
+ 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,
.function = show_mpls_tunnel_command_fn,
};
+
/* force inclusion from application's main.c */
clib_error_t *mpls_interface_init (vlib_main_t *vm)
{
clib_error_t * error;
+ fib_node_register_type(FIB_NODE_TYPE_MPLS_GRE_TUNNEL,
+ &mpls_gre_vft);
+
if ((error = vlib_call_init_function (vm, mpls_policy_encap_init)))
return error;
ip_lookup_main_t * lm = &im->lookup_main;
mpls_main_t * mm = &mpls_main;
vnet_main_t * vnm = vnet_get_main();
- ip4_address_t zero;
mpls_eth_tunnel_t *tp;
- int need_route_add_del = 1;
u32 inner_fib_index = 0;
ip_adjacency_t adj;
u32 adj_index;
u32 slot;
u32 dummy;
- zero.as_u32 = 0;
-
if (tunnel_sw_if_index == 0)
tunnel_sw_if_index = &dummy;
*/
if (!memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
&& !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
- && tp->inner_fib_index == inner_fib_index)
+ && tp->inner_fib_index == inner_fib_index
+ && FIB_NODE_INDEX_INVALID != tp->fei)
{
- ip4_fib_t * fib = vec_elt_at_index (im->fibs, inner_fib_index);
- uword * hash = fib->adj_index_by_dst_address[mask_width];
- uword key = intfc->as_u32 & im->fib_masks[mask_width];
- uword *p = hash_get (hash, key);
-
found_tunnel = 1;
if (is_add)
{
- if (p || l2_only)
+ if (l2_only)
return 1;
else
{
}
else
{
- /* Delete, the route is already gone? */
- if (!p)
- need_route_add_del = 0;
+ /* Delete */
goto add_del_route;
}
/* Create the adjacency and add to v4 fib */
memset(&adj, 0, sizeof (adj));
- adj.explicit_fib_index = ~0;
adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
rewrite_data = mpls_ethernet_rewrite (mm, tp);
add_del_route:
- if (need_route_add_del && !l2_only)
+ if (!l2_only)
{
+ const fib_prefix_t pfx = {
+ .fp_addr = {
+ .ip4 = tp->intfc_address,
+ },
+ .fp_len = tp->mask_width,
+ .fp_proto = FIB_PROTOCOL_IP4,
+ };
if (is_add)
- ip4_add_del_route_next_hop (im,
- IP4_ROUTE_FLAG_ADD,
- &tp->intfc_address,
- tp->mask_width,
- &zero /* no next hop */,
- (u32)~0 /* next_hop_sw_if_index */,
- 1 /* weight */,
- adj_index,
- tp->inner_fib_index);
+ tp->fei = fib_table_entry_special_add(tp->inner_fib_index,
+ &pfx,
+ FIB_SOURCE_API,
+ FIB_ENTRY_FLAG_NONE,
+ adj_index);
else
{
- ip4_add_del_route_args_t a;
- memset (&a, 0, sizeof (a));
-
- a.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_DEL;
- a.table_index_or_table_id = tp->inner_fib_index;
- a.dst_address = tp->intfc_address;
- a.dst_address_length = tp->mask_width;
- a.adj_index = ~0;
-
- ip4_add_del_route (im, &a);
- ip4_maybe_remap_adjacencies (im, tp->inner_fib_index,
- IP4_ROUTE_FLAG_FIB_INDEX);
- }
+ fib_table_entry_delete(tp->inner_fib_index, &pfx, FIB_SOURCE_API);
+ tp->fei = FIB_NODE_INDEX_INVALID;
+ }
}
if (is_add == 0 && found_tunnel)
{
u8 is_add)
{
ip4_main_t * im = &ip4_main;
- ip_lookup_main_t * lm = &im->lookup_main;
mpls_main_t * mm = &mpls_main;
vnet_main_t * vnm = vnet_get_main();
- ip4_address_t zero;
mpls_eth_tunnel_t *tp;
- int need_route_add_del = 1;
u32 inner_fib_index = 0;
- ip_adjacency_t adj;
- u32 adj_index;
int found_tunnel = 0;
mpls_encap_t * e = 0;
u32 hw_if_index = ~0;
u32 slot;
u32 dummy;
- zero.as_u32 = 0;
-
if (tunnel_sw_if_index == 0)
tunnel_sw_if_index = &dummy;
*/
if (!memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
&& !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
- && tp->inner_fib_index == inner_fib_index)
+ && tp->inner_fib_index == inner_fib_index
+ && FIB_NODE_INDEX_INVALID != tp->fei)
{
- ip4_fib_t * fib = vec_elt_at_index (im->fibs, inner_fib_index);
- uword * hash = fib->adj_index_by_dst_address[mask_width];
- uword key = intfc->as_u32 & im->fib_masks[mask_width];
- uword *p = hash_get (hash, key);
-
found_tunnel = 1;
if (is_add)
{
- if (p || l2_only)
+ if (l2_only)
return 1;
else
{
}
else
{
- /* Delete, the route is already gone? */
- if (!p)
- need_route_add_del = 0;
+ /* Delete */
goto add_del_route;
}
tp->encap_index = e - mm->encaps;
tp->tx_sw_if_index = tx_sw_if_index;
tp->l2_only = l2_only;
+ tp->fei = FIB_NODE_INDEX_INVALID;
if (new_tunnel_index)
*new_tunnel_index = tp - mm->eth_tunnels;
- /* Create the classify adjacency and add to v4 fib */
- memset(&adj, 0, sizeof (adj));
- adj.explicit_fib_index = ~0;
- adj.lookup_next_index = IP_LOOKUP_NEXT_CLASSIFY;
- adj.classify.table_index = classify_table_index;
-
- if (!l2_only)
- ip_add_adjacency (lm, &adj, 1 /* one adj */,
- &adj_index);
-
add_del_route:
- if (need_route_add_del && !l2_only)
+ if (!l2_only)
{
+ const fib_prefix_t pfx = {
+ .fp_addr = {
+ .ip4 = tp->intfc_address,
+ },
+ .fp_len = tp->mask_width,
+ .fp_proto = FIB_PROTOCOL_IP4,
+ };
+ dpo_id_t dpo = DPO_NULL;
+
if (is_add)
- ip4_add_del_route_next_hop (im,
- IP4_ROUTE_FLAG_ADD,
- &tp->intfc_address,
- tp->mask_width,
- &zero /* no next hop */,
- (u32)~0 /* next_hop_sw_if_index */,
- 1 /* weight */,
- adj_index,
- tp->inner_fib_index);
- else
{
- ip4_add_del_route_args_t a;
- memset (&a, 0, sizeof (a));
-
- a.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_DEL;
- a.table_index_or_table_id = tp->inner_fib_index;
- a.dst_address = tp->intfc_address;
- a.dst_address_length = tp->mask_width;
- a.adj_index = ~0;
-
- ip4_add_del_route (im, &a);
- ip4_maybe_remap_adjacencies (im, tp->inner_fib_index,
- IP4_ROUTE_FLAG_FIB_INDEX);
+ dpo_set(&dpo,
+ DPO_CLASSIFY,
+ DPO_PROTO_IP4,
+ classify_dpo_create(FIB_PROTOCOL_IP4,
+ classify_table_index));
+
+ tp->fei = fib_table_entry_special_dpo_add(tp->inner_fib_index,
+ &pfx,
+ FIB_SOURCE_API,
+ FIB_ENTRY_FLAG_EXCLUSIVE,
+ &dpo);
+ dpo_reset(&dpo);
}
+ else
+ {
+ fib_table_entry_delete(tp->inner_fib_index, &pfx, FIB_SOURCE_API);
+ tp->fei = FIB_NODE_INDEX_INVALID;
+ }
}
if (is_add == 0 && found_tunnel)
{
" classify-table-index <nn>",
.function = create_mpls_ethernet_policy_tunnel_command_fn,
};
+
+static clib_error_t *
+mpls_interface_enable_disable (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ vnet_main_t * vnm = vnet_get_main();
+ clib_error_t * error = 0;
+ u32 sw_if_index, enable;
+
+ sw_if_index = ~0;
+
+ if (! unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
+ {
+ error = clib_error_return (0, "unknown interface `%U'",
+ format_unformat_error, input);
+ goto done;
+ }
+
+ if (unformat (input, "enable"))
+ enable = 1;
+ else if (unformat (input, "disable"))
+ enable = 0;
+ else
+ {
+ error = clib_error_return (0, "expected 'enable' or 'disable'",
+ format_unformat_error, input);
+ goto done;
+ }
+
+ mpls_sw_interface_enable_disable(&mpls_main, sw_if_index, enable);
+
+ done:
+ return error;
+}
+
+VLIB_CLI_COMMAND (set_interface_ip_table_command, static) = {
+ .path = "set interface mpls",
+ .function = mpls_interface_enable_disable,
+ .short_help = "Enable/Disable an interface for MPLS forwarding",
+};