MPLS Unifom mode 81/10781/7
authorNeale Ranns <nranns@cisco.com>
Fri, 23 Feb 2018 13:29:09 +0000 (05:29 -0800)
committerDamjan Marion <dmarion.lists@gmail.com>
Fri, 9 Mar 2018 11:59:58 +0000 (11:59 +0000)
- support both pipe and uniform modes for all MPLS LSP
- all API programming for output-labels requires that the mode (and associated data) is specificed
   - API changes in MPLS, BIER and IP are involved
- new DPO [sub] types for MPLS labels to handle the two modes.

Change-Id: I87b76401e996f10dfbdbe4552ff6b19af958783c
Signed-off-by: Neale Ranns <nranns@cisco.com>
47 files changed:
src/vat/api_format.c
src/vnet/adj/adj_l2.c
src/vnet/bier/bier.api
src/vnet/bier/bier_api.c
src/vnet/bier/bier_fmask.c
src/vnet/bier/bier_output.c
src/vnet/bier/bier_test.c
src/vnet/buffer.h
src/vnet/dpo/dpo.h
src/vnet/dpo/mpls_disposition.c
src/vnet/dpo/mpls_disposition.h
src/vnet/dpo/mpls_label_dpo.c
src/vnet/dpo/mpls_label_dpo.h
src/vnet/fib/fib_api.h
src/vnet/fib/fib_entry_src.c
src/vnet/fib/fib_path.c
src/vnet/fib/fib_path.h
src/vnet/fib/fib_path_ext.c
src/vnet/fib/fib_path_ext.h
src/vnet/fib/fib_table.c
src/vnet/fib/fib_table.h
src/vnet/fib/fib_test.c
src/vnet/fib/fib_test.h
src/vnet/fib/fib_types.api
src/vnet/fib/fib_types.c
src/vnet/fib/fib_types.h
src/vnet/ip/ip.api
src/vnet/ip/ip6_packet.h
src/vnet/ip/ip_api.c
src/vnet/mfib/mfib_test.c
src/vnet/mpls/mpls.api
src/vnet/mpls/mpls.c
src/vnet/mpls/mpls_api.c
src/vnet/mpls/mpls_input.c
src/vnet/mpls/mpls_tunnel.c
src/vnet/mpls/mpls_types.h
src/vnet/mpls/packet.h
src/vnet/srmpls/sr_mpls_policy.c
src/vnet/srmpls/sr_mpls_steering.c
src/vnet/vxlan/vxlan.api
test/test_bier.py
test/test_mpls.py
test/test_udp.py
test/vpp_bier.py
test/vpp_ip_route.py
test/vpp_mpls_tunnel_interface.py
test/vpp_papi_provider.py

index dc7111d..776f350 100644 (file)
@@ -8872,7 +8872,7 @@ api_bier_route_add_del (vat_main_t * vam)
     }
 
   /* Construct the API message */
-  M2 (BIER_ROUTE_ADD_DEL, mp, sizeof (vl_api_fib_path3_t));
+  M2 (BIER_ROUTE_ADD_DEL, mp, sizeof (vl_api_fib_path_t));
 
   mp->br_is_add = is_add;
   mp->br_tbl_id.bt_set = set;
@@ -8881,7 +8881,7 @@ api_bier_route_add_del (vat_main_t * vam)
   mp->br_bp = ntohs (bp);
   mp->br_n_paths = 1;
   mp->br_paths[0].n_labels = 1;
-  mp->br_paths[0].label_stack[0] = ntohl (next_hop_out_label);
+  mp->br_paths[0].label_stack[0].label = ntohl (next_hop_out_label);
   mp->br_paths[0].afi = (next_hop_proto_is_ip4 ? 0 : 1);
 
   if (next_hop_proto_is_ip4)
@@ -19634,7 +19634,7 @@ api_netmap_delete (vat_main_t * vam)
 }
 
 static void
-vl_api_mpls_fib_path_print (vat_main_t * vam, vl_api_fib_path2_t * fp)
+vl_api_mpls_fib_path_print (vat_main_t * vam, vl_api_fib_path_t * fp)
 {
   if (fp->afi == IP46_TYPE_IP6)
     print (vam->ofp,
@@ -19654,7 +19654,7 @@ vl_api_mpls_fib_path_print (vat_main_t * vam, vl_api_fib_path2_t * fp)
 
 static void
 vl_api_mpls_fib_path_json_print (vat_json_node_t * node,
-                                vl_api_fib_path2_t * fp)
+                                vl_api_fib_path_t * fp)
 {
   struct in_addr ip4;
   struct in6_addr ip6;
@@ -19683,7 +19683,7 @@ vl_api_mpls_tunnel_details_t_handler (vl_api_mpls_tunnel_details_t * mp)
 {
   vat_main_t *vam = &vat_main;
   int count = ntohl (mp->mt_count);
-  vl_api_fib_path2_t *fp;
+  vl_api_fib_path_t *fp;
   i32 i;
 
   print (vam->ofp, "[%d]: sw_if_index %d via:",
@@ -19707,7 +19707,7 @@ vl_api_mpls_tunnel_details_t_handler_json (vl_api_mpls_tunnel_details_t * mp)
   vat_main_t *vam = &vat_main;
   vat_json_node_t *node = NULL;
   int count = ntohl (mp->mt_count);
-  vl_api_fib_path2_t *fp;
+  vl_api_fib_path_t *fp;
   i32 i;
 
   if (VAT_JSON_ARRAY != vam->json_tree.type)
@@ -19773,7 +19773,7 @@ vl_api_mpls_fib_details_t_handler (vl_api_mpls_fib_details_t * mp)
 {
   vat_main_t *vam = &vat_main;
   int count = ntohl (mp->count);
-  vl_api_fib_path2_t *fp;
+  vl_api_fib_path_t *fp;
   int i;
 
   print (vam->ofp,
@@ -19793,7 +19793,7 @@ static void vl_api_mpls_fib_details_t_handler_json
   vat_main_t *vam = &vat_main;
   int count = ntohl (mp->count);
   vat_json_node_t *node = NULL;
-  vl_api_fib_path2_t *fp;
+  vl_api_fib_path_t *fp;
   int i;
 
   if (VAT_JSON_ARRAY != vam->json_tree.type)
index 132ceb2..09bf468 100644 (file)
@@ -91,6 +91,11 @@ adj_l2_rewrite_inline (vlib_main_t * vm,
            rw_len0 = adj0[0].rewrite_header.data_bytes;
            vnet_buffer(p0)->ip.save_rewrite_length = rw_len0;
             vnet_buffer(p0)->sw_if_index[VLIB_TX] = adj0->rewrite_header.sw_if_index;
+            /* since we are coming out of the L2 world, where the vlib_buffer
+             * union is used for other things, make sure it is clean for
+             * MPLS from now on.
+             */
+            vnet_buffer(p0)->mpls.first = 0;
 
            vlib_increment_combined_counter(&adjacency_counters,
                                             thread_index,
index 446863c..d07379e 100644 (file)
@@ -18,7 +18,7 @@
     This file defines vpp BIER control-plane API messages which are generally
     called through a shared memory interface.
 */
-option version = "1.0.0";
+option version = "1.1.0";
 import "vnet/fib/fib_types.api";
 
 /** \brief BIER Table Indentifier
@@ -84,7 +84,7 @@ autoreply define bier_route_add_del
   u8 br_is_replace;
   vl_api_bier_table_id_t br_tbl_id;
   u8 br_n_paths;
-  vl_api_fib_path3_t br_paths[br_n_paths];
+  vl_api_fib_path_t br_paths[br_n_paths];
 };
 
 define bier_route_dump
@@ -101,7 +101,7 @@ define bier_route_details
   u16 br_bp;
   vl_api_bier_table_id_t br_tbl_id;
   u32 br_n_paths;
-  vl_api_fib_path3_t br_paths[br_n_paths];
+  vl_api_fib_path_t br_paths[br_n_paths];
 };
 
 /** \brief BIER Imposition Add
@@ -211,7 +211,7 @@ autoreply define bier_disp_entry_add_del
   u8 bde_is_add;
   u8 bde_payload_proto;
   u8 bde_n_paths;
-  vl_api_fib_path3_t bde_paths[bde_n_paths];
+  vl_api_fib_path_t bde_paths[bde_n_paths];
 };
 
 define bier_disp_entry_dump
@@ -229,7 +229,7 @@ define bier_disp_entry_details
   u8 bde_is_add;
   u8 bde_payload_proto;
   u8 bde_n_paths;
-  vl_api_fib_path3_t bde_paths[bde_n_paths];
+  vl_api_fib_path_t bde_paths[bde_n_paths];
 };
 
 /*
index 4b1d1c2..77b2cab 100644 (file)
@@ -202,8 +202,16 @@ vl_api_bier_route_add_del_t_handler (vl_api_bier_route_add_del_t * mp)
                      mp->br_paths[ii].n_labels - 1);
         for (jj = 0; jj < mp->br_paths[ii].n_labels; jj++)
         {
-            brpath->frp_label_stack[jj] =
-                ntohl(mp->br_paths[ii].label_stack[jj]);
+            brpath->frp_label_stack[jj].fml_value =
+                ntohl(mp->br_paths[ii].label_stack[jj].label);
+            brpath->frp_label_stack[jj].fml_ttl =
+                mp->br_paths[ii].label_stack[jj].ttl;
+            brpath->frp_label_stack[jj].fml_exp =
+                mp->br_paths[ii].label_stack[jj].exp;
+            brpath->frp_label_stack[jj].fml_mode =
+                (mp->br_paths[ii].label_stack[jj].is_uniform ?
+                 FIB_MPLS_LSP_MODE_UNIFORM :
+                 FIB_MPLS_LSP_MODE_PIPE);
         }
 
         if (mp->br_paths[ii].is_udp_encap)
@@ -275,11 +283,11 @@ send_bier_route_details (const bier_table_t *bt,
     fib_route_path_encode_t *api_rpaths = NULL, *api_rpath;
     bier_route_details_walk_t *ctx = args;
     vl_api_bier_route_details_t *mp;
-    vl_api_fib_path3_t *fp;
+    vl_api_fib_path_t *fp;
     u32 n_paths, m_size;
 
     n_paths = fib_path_list_get_n_paths(be->be_path_list);
-    m_size = sizeof(*mp) + (n_paths * sizeof(vl_api_fib_path3_t));
+    m_size = sizeof(*mp) + (n_paths * sizeof(vl_api_fib_path_t));
     mp = vl_msg_api_alloc(m_size);
     if (!mp)
         return;
@@ -636,7 +644,7 @@ send_bier_disp_entry_details (const bier_disp_table_t *bdt,
     bier_disp_entry_details_walk_t *ctx = args;
     vl_api_bier_disp_entry_details_t *mp;
     bier_hdr_proto_id_t pproto;
-    vl_api_fib_path3_t *fp;
+    vl_api_fib_path_t *fp;
     u32 n_paths, m_size;
 
     FOR_EACH_BIER_HDR_PROTO(pproto)
@@ -645,7 +653,7 @@ send_bier_disp_entry_details (const bier_disp_table_t *bdt,
         if (INDEX_INVALID != pl)
         {
             n_paths = fib_path_list_get_n_paths(pl);
-            m_size = sizeof(*mp) + (n_paths * sizeof(vl_api_fib_path3_t));
+            m_size = sizeof(*mp) + (n_paths * sizeof(vl_api_fib_path_t));
             mp = vl_msg_api_alloc(m_size);
             if (!mp)
                 return;
index 2fc24dc..31d884a 100644 (file)
@@ -177,16 +177,13 @@ bier_fmask_init (bier_fmask_t *bfm,
 
     if (!(bfm->bfm_flags & BIER_FMASK_FLAG_DISP))
     {
-        /*
-         * leave this label in host byte order so we can OR in the TTL
-         */
         if (NULL != rpaths->frp_label_stack)
         {
-            olabel = rpaths->frp_label_stack[0];
+            olabel = rpaths->frp_label_stack[0].fml_value;
             vnet_mpls_uc_set_label(&bfm->bfm_label, olabel);
             vnet_mpls_uc_set_exp(&bfm->bfm_label, 0);
             vnet_mpls_uc_set_s(&bfm->bfm_label, 1);
-            vnet_mpls_uc_set_ttl(&bfm->bfm_label, 0);
+            vnet_mpls_uc_set_ttl(&bfm->bfm_label, 64);
             bfm->bfm_flags |= BIER_FMASK_FLAG_MPLS;
         }
         else
@@ -207,7 +204,9 @@ bier_fmask_init (bier_fmask_t *bfm,
             vnet_mpls_uc_set_label(&bfm->bfm_label, id);
             vnet_mpls_uc_set_s(&bfm->bfm_label, 1);
             vnet_mpls_uc_set_exp(&bfm->bfm_label, 0);
+            vnet_mpls_uc_set_ttl(&bfm->bfm_label, 64);
         }
+        bfm->bfm_label = clib_host_to_net_u32(bfm->bfm_label);
     }
 
     bfm->bfm_pl = fib_path_list_create((FIB_PATH_LIST_FLAG_SHARED |
index db115d3..01eeffe 100644 (file)
@@ -140,8 +140,8 @@ bier_output (vlib_main_t * vm,
                 h0 = vlib_buffer_get_current(b0);
                 
                 h0[0] = bfm0->bfm_label;
-                vnet_mpls_uc_set_ttl(h0, vnet_buffer(b0)->mpls.ttl - 1);
-                h0[0] = clib_host_to_net_u32(h0[0]);
+
+                ((char*)h0)[3]= vnet_buffer(b0)->mpls.ttl - 1;
             }
 
             /*
index 08a8c55..6c7af82 100644 (file)
@@ -316,7 +316,10 @@ bier_test_mpls_spf (void)
         .frp_bier_fib_index = bti,
         .frp_sw_if_index = ~0,
     };
-    vec_add1(path_1_1_1_1.frp_label_stack, 500);
+    fib_mpls_label_t fml_500 = {
+        .fml_value = 500,
+    };
+    vec_add1(path_1_1_1_1.frp_label_stack, fml_500);
     vec_add1(paths_1_1_1_1, path_1_1_1_1);
     const fib_prefix_t pfx_1_1_1_1_s_32 = {
         .fp_addr = nh_1_1_1_1,
@@ -389,8 +392,10 @@ bier_test_mpls_spf (void)
             .ttl = 255,
         },
     };
-    mpls_label_t *out_lbl_99 = NULL;
-    vec_add1(out_lbl_99, 99);
+    fib_mpls_label_t *out_lbl_99 = NULL, fml_99 = {
+        .fml_value = 99,
+    };
+    vec_add1(out_lbl_99, fml_99);
 
     fei = fib_table_entry_update_one_path(0,
                                           &pfx_1_1_1_1_s_32,
@@ -443,8 +448,10 @@ bier_test_mpls_spf (void)
             .ttl = 255,
         },
     };
-    mpls_label_t *out_lbl_100 = NULL;
-    vec_add1(out_lbl_100, 100);
+    fib_mpls_label_t *out_lbl_100 = NULL, fml_100 = {
+        .fml_value = 100,
+    };
+    vec_add1(out_lbl_100, fml_100);
 
     fei = fib_table_entry_path_add(0,
                                    &pfx_1_1_1_1_s_32,
@@ -505,13 +512,18 @@ bier_test_mpls_spf (void)
         .frp_bier_fib_index = bti,
         .frp_sw_if_index = ~0,
     };
-    vec_add1(path_1_1_1_2.frp_label_stack, 501);
+    fib_mpls_label_t fml_501 = {
+        .fml_value = 501,
+    };
+    vec_add1(path_1_1_1_2.frp_label_stack, fml_501);
     vec_add1(paths_1_1_1_2, path_1_1_1_2);
     input_paths_1_1_1_2 = vec_dup(paths_1_1_1_2);
     index_t bei_3;
 
-    mpls_label_t *out_lbl_101 = NULL;
-    vec_add1(out_lbl_101, 101);
+    fib_mpls_label_t *out_lbl_101 = NULL, fml_101 = {
+        .fml_value = 101,
+    };
+    vec_add1(out_lbl_101, fml_101);
     fei = fib_table_entry_path_add(0,
                                    &pfx_1_1_1_2_s_32,
                                    FIB_SOURCE_API,
@@ -605,7 +617,7 @@ bier_test_mpls_spf (void)
      * add the via back
      */
     out_lbl_101 = NULL;
-    vec_add1(out_lbl_101, 101);
+    vec_add1(out_lbl_101, fml_101);
     fei = fib_table_entry_path_add(0,
                                    &pfx_1_1_1_2_s_32,
                                    FIB_SOURCE_API,
index 5aedb43..7a4bc24 100644 (file)
@@ -181,6 +181,8 @@ typedef struct
      */
     struct
     {
+      /* do not overlay w/ ip.adj_index[0,1] nor flow hash */
+      u32 pad[VLIB_N_RX_TX + 1];
       u8 ttl;
       u8 exp;
       u8 first;
index afee458..4d48478 100644 (file)
@@ -111,8 +111,8 @@ typedef enum dpo_type_t_ {
     DPO_LOOKUP,
     DPO_LISP_CP,
     DPO_CLASSIFY,
-    DPO_MPLS_LABEL,
-    DPO_MPLS_DISPOSITION,
+    DPO_MPLS_DISPOSITION_PIPE,
+    DPO_MPLS_DISPOSITION_UNIFORM,
     DPO_MFIB_ENTRY,
     DPO_INTERFACE_RX,
     DPO_INTERFACE_TX,
@@ -146,8 +146,8 @@ typedef enum dpo_type_t_ {
     [DPO_REPLICATE] = "dpo-replicate", \
     [DPO_LISP_CP] = "dpo-lisp-cp",     \
     [DPO_CLASSIFY] = "dpo-classify",   \
-    [DPO_MPLS_LABEL] = "dpo-mpls-label", \
-    [DPO_MPLS_DISPOSITION] = "dpo-mpls-diposition", \
+    [DPO_MPLS_DISPOSITION_PIPE] = "dpo-mpls-diposition-pipe", \
+    [DPO_MPLS_DISPOSITION_UNIFORM] = "dpo-mpls-diposition-uniform", \
     [DPO_MFIB_ENTRY] = "dpo-mfib-entry", \
     [DPO_INTERFACE_RX] = "dpo-interface-rx",   \
     [DPO_INTERFACE_TX] = "dpo-interface-tx",   \
index 77429de..2956e54 100644 (file)
@@ -42,38 +42,55 @@ mpls_disp_dpo_get_index (mpls_disp_dpo_t *mdd)
     return (mdd - mpls_disp_dpo_pool);
 }
 
-index_t
+void
 mpls_disp_dpo_create (dpo_proto_t payload_proto,
                       fib_rpf_id_t rpf_id,
-                      const dpo_id_t *dpo)
+                      fib_mpls_lsp_mode_t mode,
+                      const dpo_id_t *parent,
+                      dpo_id_t *dpo)
 {
     mpls_disp_dpo_t *mdd;
+    dpo_type_t dtype;
 
     mdd = mpls_disp_dpo_alloc();
 
     mdd->mdd_payload_proto = payload_proto;
     mdd->mdd_rpf_id = rpf_id;
-
-    dpo_stack(DPO_MPLS_DISPOSITION,
+    mdd->mdd_mode = mode;
+    dtype = (FIB_MPLS_LSP_MODE_PIPE == mode ?
+             DPO_MPLS_DISPOSITION_PIPE :
+             DPO_MPLS_DISPOSITION_UNIFORM);
+
+    /*
+     * stack this disposition object on the parent given
+     */
+    dpo_stack(dtype,
               mdd->mdd_payload_proto,
               &mdd->mdd_dpo,
-              dpo);
-
-    return (mpls_disp_dpo_get_index(mdd));
+              parent);
+
+    /*
+     * set up the return DPO to refer to this object
+     */
+    dpo_set(dpo,
+            dtype,
+            payload_proto,
+            mpls_disp_dpo_get_index(mdd));
 }
 
 u8*
 format_mpls_disp_dpo (u8 *s, va_list *args)
 {
-    index_t index = va_arg (*args, index_t);
-    u32 indent = va_arg (*args, u32);
+    index_t index = va_arg(*args, index_t);
+    u32 indent = va_arg(*args, u32);
     mpls_disp_dpo_t *mdd;
 
     mdd = mpls_disp_dpo_get(index);
 
-    s = format(s, "mpls-disposition:[%d]:[%U]",
+    s = format(s, "mpls-disposition:[%d]:[%U, %U]",
                index,
-               format_dpo_proto, mdd->mdd_payload_proto);
+               format_dpo_proto, mdd->mdd_payload_proto,
+               format_fib_mpls_lsp_mode, mdd->mdd_mode);
 
     s = format(s, "\n%U", format_white_space, indent);
     s = format(s, "%U", format_dpo_id, &mdd->mdd_dpo, indent+2);
@@ -116,25 +133,41 @@ typedef struct mpls_label_disposition_trace_t_
     index_t mdd;
 } mpls_label_disposition_trace_t;
 
-extern vlib_node_registration_t ip4_mpls_label_disposition_node;
-extern vlib_node_registration_t ip6_mpls_label_disposition_node;
+extern vlib_node_registration_t ip4_mpls_label_disposition_pipe_node;
+extern vlib_node_registration_t ip6_mpls_label_disposition_pipe_node;
+extern vlib_node_registration_t ip4_mpls_label_disposition_uniform_node;
+extern vlib_node_registration_t ip6_mpls_label_disposition_uniform_node;
 
 always_inline uword
 mpls_label_disposition_inline (vlib_main_t * vm,
-                              vlib_node_runtime_t * node,
-                              vlib_frame_t * from_frame,
-                              u8 payload_is_ip4,
-                              u8 payload_is_ip6)
+                               vlib_node_runtime_t * node,
+                               vlib_frame_t * from_frame,
+                               u8 payload_is_ip4,
+                               u8 payload_is_ip6,
+                               fib_mpls_lsp_mode_t mode)
 {
     u32 n_left_from, next_index, * from, * to_next;
     vlib_node_runtime_t *error_node;
 
     if (payload_is_ip4)
-        error_node = vlib_node_get_runtime (vm, ip4_mpls_label_disposition_node.index);
+    {
+        if (FIB_MPLS_LSP_MODE_PIPE == mode)
+            error_node =
+                vlib_node_get_runtime(vm, ip4_mpls_label_disposition_pipe_node.index);
+        else
+            error_node =
+                vlib_node_get_runtime(vm, ip4_mpls_label_disposition_uniform_node.index);
+    }
     else
-        error_node = vlib_node_get_runtime (vm, ip6_mpls_label_disposition_node.index);
-
-    from = vlib_frame_vector_args (from_frame);
+    {
+        if (FIB_MPLS_LSP_MODE_PIPE == mode)
+            error_node =
+                vlib_node_get_runtime(vm, ip6_mpls_label_disposition_uniform_node.index);
+        else
+            error_node =
+                vlib_node_get_runtime(vm, ip6_mpls_label_disposition_uniform_node.index);
+    }
+    from = vlib_frame_vector_args(from_frame);
     n_left_from = from_frame->n_vectors;
 
     next_index = node->cached_next_index;
@@ -159,14 +192,14 @@ mpls_label_disposition_inline (vlib_main_t * vm,
             {
                 vlib_buffer_t * p2, * p3;
 
-                p2 = vlib_get_buffer (vm, from[2]);
-                p3 = vlib_get_buffer (vm, from[3]);
+                p2 = vlib_get_buffer(vm, from[2]);
+                p3 = vlib_get_buffer(vm, from[3]);
 
-                vlib_prefetch_buffer_header (p2, STORE);
-                vlib_prefetch_buffer_header (p3, STORE);
+                vlib_prefetch_buffer_header(p2, STORE);
+                vlib_prefetch_buffer_header(p3, STORE);
 
-                CLIB_PREFETCH (p2->data, sizeof (ip6_header_t), STORE);
-                CLIB_PREFETCH (p3->data, sizeof (ip6_header_t), STORE);
+                CLIB_PREFETCH(p2->data, sizeof(ip6_header_t), STORE);
+                CLIB_PREFETCH(p3->data, sizeof(ip6_header_t), STORE);
             }
 
             from += 2;
@@ -174,8 +207,8 @@ mpls_label_disposition_inline (vlib_main_t * vm,
             n_left_from -= 2;
             n_left_to_next -= 2;
 
-            b0 = vlib_get_buffer (vm, bi0);
-            b1 = vlib_get_buffer (vm, bi1);
+            b0 = vlib_get_buffer(vm, bi0);
+            b1 = vlib_get_buffer(vm, bi1);
 
             /* dst lookup was done by ip4 lookup */
             mddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
@@ -190,30 +223,62 @@ mpls_label_disposition_inline (vlib_main_t * vm,
             {
                 ip4_header_t *ip0, *ip1;
 
-                ip0 = vlib_buffer_get_current (b0);
-                ip1 = vlib_buffer_get_current (b1);
+                ip0 = vlib_buffer_get_current(b0);
+                ip1 = vlib_buffer_get_current(b1);
 
                 /*
                  * IPv4 input checks on the exposed IP header
                  * including checksum
                  */
-                ip4_input_check_x2 (vm, error_node,
-                                    b0, b1, ip0, ip1,
-                                    &next0, &next1, 1);
+                ip4_input_check_x2(vm, error_node,
+                                   b0, b1, ip0, ip1,
+                                   &next0, &next1, 1);
+
+                if (FIB_MPLS_LSP_MODE_UNIFORM == mode)
+                {
+                    /*
+                     * Copy the TTL from the MPLS packet into the
+                     * exposed IP. recalc the chksum
+                     */
+                    ip0->ttl = vnet_buffer(b0)->mpls.ttl;
+                    ip1->ttl = vnet_buffer(b1)->mpls.ttl;
+                    ip0->tos = mpls_exp_to_ip_dscp(vnet_buffer(b0)->mpls.exp);
+                    ip1->tos = mpls_exp_to_ip_dscp(vnet_buffer(b1)->mpls.exp);
+
+                    ip0->checksum = ip4_header_checksum(ip0);
+                    ip1->checksum = ip4_header_checksum(ip1);
+                }
             }
             else if (payload_is_ip6)
             {
                 ip6_header_t *ip0, *ip1;
 
-                ip0 = vlib_buffer_get_current (b0);
-                ip1 = vlib_buffer_get_current (b1);
+                ip0 = vlib_buffer_get_current(b0);
+                ip1 = vlib_buffer_get_current(b1);
 
                 /*
                  * IPv6 input checks on the exposed IP header
                  */
-                ip6_input_check_x2 (vm, error_node,
-                                    b0, b1, ip0, ip1,
-                                    &next0, &next1);
+                ip6_input_check_x2(vm, error_node,
+                                   b0, b1, ip0, ip1,
+                                   &next0, &next1);
+
+                if (FIB_MPLS_LSP_MODE_UNIFORM == mode)
+                {
+                    /*
+                     * Copy the TTL from the MPLS packet into the
+                     * exposed IP
+                     */
+                    ip0->hop_limit = vnet_buffer(b0)->mpls.ttl;
+                    ip1->hop_limit = vnet_buffer(b1)->mpls.ttl;
+
+                    ip6_set_traffic_class_network_order(
+                        ip0,
+                        mpls_exp_to_ip_dscp(vnet_buffer(b0)->mpls.exp));
+                    ip6_set_traffic_class_network_order(
+                        ip1,
+                        mpls_exp_to_ip_dscp(vnet_buffer(b1)->mpls.exp));
+                }
             }
 
             vnet_buffer(b0)->ip.adj_index[VLIB_TX] = mdd0->mdd_dpo.dpoi_index;
@@ -224,14 +289,14 @@ mpls_label_disposition_inline (vlib_main_t * vm,
             if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
             {
                 mpls_label_disposition_trace_t *tr =
-                    vlib_add_trace (vm, node, b0, sizeof (*tr));
+                    vlib_add_trace(vm, node, b0, sizeof(*tr));
 
                 tr->mdd = mddi0;
             }
             if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
             {
                 mpls_label_disposition_trace_t *tr =
-                    vlib_add_trace (vm, node, b1, sizeof (*tr));
+                    vlib_add_trace(vm, node, b1, sizeof(*tr));
                 tr->mdd = mddi1;
             }
 
@@ -254,7 +319,7 @@ mpls_label_disposition_inline (vlib_main_t * vm,
             n_left_from -= 1;
             n_left_to_next -= 1;
 
-            b0 = vlib_get_buffer (vm, bi0);
+            b0 = vlib_get_buffer(vm, bi0);
 
             /* dst lookup was done by ip4 lookup */
             mddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
@@ -265,24 +330,48 @@ mpls_label_disposition_inline (vlib_main_t * vm,
             {
                 ip4_header_t *ip0;
 
-                ip0 = vlib_buffer_get_current (b0);
+                ip0 = vlib_buffer_get_current(b0);
 
                 /*
                  * IPv4 input checks on the exposed IP header
                  * including checksum
                  */
-                ip4_input_check_x1 (vm, error_node, b0, ip0, &next0, 1);
+                ip4_input_check_x1(vm, error_node, b0, ip0, &next0, 1);
+
+                if (FIB_MPLS_LSP_MODE_UNIFORM == mode)
+                {
+                    /*
+                     * Copy the TTL from the MPLS packet into the
+                     * exposed IP. recalc the chksum
+                     */
+                    ip0->ttl = vnet_buffer(b0)->mpls.ttl;
+                    ip0->tos = mpls_exp_to_ip_dscp(vnet_buffer(b0)->mpls.exp);
+                    ip0->checksum = ip4_header_checksum(ip0);
+                }
             }
             else if (payload_is_ip6)
             {
                 ip6_header_t *ip0;
 
-                ip0 = vlib_buffer_get_current (b0);
+                ip0 = vlib_buffer_get_current(b0);
 
                 /*
                  * IPv6 input checks on the exposed IP header
                  */
-                ip6_input_check_x1 (vm, error_node, b0, ip0, &next0);
+                ip6_input_check_x1(vm, error_node, b0, ip0, &next0);
+
+                if (FIB_MPLS_LSP_MODE_UNIFORM == mode)
+                {
+                    /*
+                     * Copy the TTL from the MPLS packet into the
+                     * exposed IP
+                     */
+                    ip0->hop_limit = vnet_buffer(b0)->mpls.ttl;
+
+                    ip6_set_traffic_class_network_order(
+                        ip0,
+                        mpls_exp_to_ip_dscp(vnet_buffer(b0)->mpls.exp));
+                }
             }
 
             vnet_buffer(b0)->ip.adj_index[VLIB_TX] = mdd0->mdd_dpo.dpoi_index;
@@ -291,14 +380,14 @@ mpls_label_disposition_inline (vlib_main_t * vm,
             if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
             {
                 mpls_label_disposition_trace_t *tr =
-                    vlib_add_trace (vm, node, b0, sizeof (*tr));
+                    vlib_add_trace(vm, node, b0, sizeof(*tr));
                 tr->mdd = mddi0;
             }
 
             vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
                                             n_left_to_next, bi0, next0);
         }
-        vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+        vlib_put_next_frame(vm, node, next_index, n_left_to_next);
     }
     return from_frame->n_vectors;
 }
@@ -306,57 +395,103 @@ mpls_label_disposition_inline (vlib_main_t * vm,
 static u8 *
 format_mpls_label_disposition_trace (u8 * s, va_list * args)
 {
-    CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
-    CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
-    CLIB_UNUSED (mpls_label_disposition_trace_t * t);
+    CLIB_UNUSED(vlib_main_t * vm) = va_arg(*args, vlib_main_t *);
+    CLIB_UNUSED(vlib_node_t * node) = va_arg(*args, vlib_node_t *);
+    CLIB_UNUSED(mpls_label_disposition_trace_t * t);
 
-    t = va_arg (*args, mpls_label_disposition_trace_t *);
+    t = va_arg(*args, mpls_label_disposition_trace_t *);
 
     s = format(s, "disp:%d", t->mdd);
     return (s);
 }
 
 static uword
-ip4_mpls_label_disposition (vlib_main_t * vm,
-                           vlib_node_runtime_t * node,
-                           vlib_frame_t * frame)
+ip4_mpls_label_disposition_pipe (vlib_main_t * vm,
+                                 vlib_node_runtime_t * node,
+                                 vlib_frame_t * frame)
 {
-    return (mpls_label_disposition_inline(vm, node, frame, 1, 0));
+    return (mpls_label_disposition_inline(vm, node, frame, 1, 0,
+                                          FIB_MPLS_LSP_MODE_PIPE));
 }
 
-VLIB_REGISTER_NODE (ip4_mpls_label_disposition_node) = {
-    .function = ip4_mpls_label_disposition,
-    .name = "ip4-mpls-label-disposition",
-    .vector_size = sizeof (u32),
+VLIB_REGISTER_NODE(ip4_mpls_label_disposition_pipe_node) = {
+    .function = ip4_mpls_label_disposition_pipe,
+    .name = "ip4-mpls-label-disposition-pipe",
+    .vector_size = sizeof(u32),
 
     .format_trace = format_mpls_label_disposition_trace,
     .sibling_of = "ip4-input",
     .n_errors = IP4_N_ERROR,
     .error_strings = ip4_error_strings,
 };
-VLIB_NODE_FUNCTION_MULTIARCH (ip4_mpls_label_disposition_node,
-                              ip4_mpls_label_disposition)
+VLIB_NODE_FUNCTION_MULTIARCH(ip4_mpls_label_disposition_pipe_node,
+                              ip4_mpls_label_disposition_pipe)
 
 static uword
-ip6_mpls_label_disposition (vlib_main_t * vm,
-                           vlib_node_runtime_t * node,
-                           vlib_frame_t * frame)
+ip6_mpls_label_disposition_pipe (vlib_main_t * vm,
+                                 vlib_node_runtime_t * node,
+                                 vlib_frame_t * frame)
 {
-    return (mpls_label_disposition_inline(vm, node, frame, 0, 1));
+    return (mpls_label_disposition_inline(vm, node, frame, 0, 1,
+                                          FIB_MPLS_LSP_MODE_PIPE));
 }
 
-VLIB_REGISTER_NODE (ip6_mpls_label_disposition_node) = {
-    .function = ip6_mpls_label_disposition,
-    .name = "ip6-mpls-label-disposition",
-    .vector_size = sizeof (u32),
+VLIB_REGISTER_NODE(ip6_mpls_label_disposition_pipe_node) = {
+    .function = ip6_mpls_label_disposition_pipe,
+    .name = "ip6-mpls-label-disposition-pipe",
+    .vector_size = sizeof(u32),
 
     .format_trace = format_mpls_label_disposition_trace,
     .sibling_of = "ip6-input",
     .n_errors = IP6_N_ERROR,
     .error_strings = ip6_error_strings,
 };
-VLIB_NODE_FUNCTION_MULTIARCH (ip6_mpls_label_disposition_node,
-                              ip6_mpls_label_disposition)
+VLIB_NODE_FUNCTION_MULTIARCH(ip6_mpls_label_disposition_pipe_node,
+                             ip6_mpls_label_disposition_pipe)
+
+static uword
+ip4_mpls_label_disposition_uniform (vlib_main_t * vm,
+                                 vlib_node_runtime_t * node,
+                                 vlib_frame_t * frame)
+{
+    return (mpls_label_disposition_inline(vm, node, frame, 1, 0,
+                                          FIB_MPLS_LSP_MODE_UNIFORM));
+}
+
+VLIB_REGISTER_NODE(ip4_mpls_label_disposition_uniform_node) = {
+    .function = ip4_mpls_label_disposition_uniform,
+    .name = "ip4-mpls-label-disposition-uniform",
+    .vector_size = sizeof(u32),
+
+    .format_trace = format_mpls_label_disposition_trace,
+    .sibling_of = "ip4-input",
+    .n_errors = IP4_N_ERROR,
+    .error_strings = ip4_error_strings,
+};
+VLIB_NODE_FUNCTION_MULTIARCH(ip4_mpls_label_disposition_uniform_node,
+                             ip4_mpls_label_disposition_uniform)
+
+static uword
+ip6_mpls_label_disposition_uniform (vlib_main_t * vm,
+                                    vlib_node_runtime_t * node,
+                                    vlib_frame_t * frame)
+{
+    return (mpls_label_disposition_inline(vm, node, frame, 0, 1,
+                                          FIB_MPLS_LSP_MODE_UNIFORM));
+}
+
+VLIB_REGISTER_NODE(ip6_mpls_label_disposition_uniform_node) = {
+    .function = ip6_mpls_label_disposition_uniform,
+    .name = "ip6-mpls-label-disposition-uniform",
+    .vector_size = sizeof(u32),
+
+    .format_trace = format_mpls_label_disposition_trace,
+    .sibling_of = "ip6-input",
+    .n_errors = IP6_N_ERROR,
+    .error_strings = ip6_error_strings,
+};
+VLIB_NODE_FUNCTION_MULTIARCH(ip6_mpls_label_disposition_uniform_node,
+                             ip6_mpls_label_disposition_uniform)
 
 static void
 mpls_disp_dpo_mem_show (void)
@@ -374,25 +509,44 @@ const static dpo_vft_t mdd_vft = {
     .dv_mem_show = mpls_disp_dpo_mem_show,
 };
 
-const static char* const mpls_label_disp_ip4_nodes[] =
+const static char* const mpls_label_disp_pipe_ip4_nodes[] =
+{
+    "ip4-mpls-label-disposition-pipe",
+    NULL,
+};
+const static char* const mpls_label_disp_pipe_ip6_nodes[] =
+{
+    "ip6-mpls-label-disposition-pipe",
+    NULL,
+};
+const static char* const * const mpls_label_disp_pipe_nodes[DPO_PROTO_NUM] =
+{
+    [DPO_PROTO_IP4]  = mpls_label_disp_pipe_ip4_nodes,
+    [DPO_PROTO_IP6]  = mpls_label_disp_pipe_ip6_nodes,
+};
+
+const static char* const mpls_label_disp_uniform_ip4_nodes[] =
 {
-    "ip4-mpls-label-disposition",
+    "ip4-mpls-label-disposition-uniform",
     NULL,
 };
-const static char* const mpls_label_disp_ip6_nodes[] =
+const static char* const mpls_label_disp_uniform_ip6_nodes[] =
 {
-    "ip6-mpls-label-disposition",
+    "ip6-mpls-label-disposition-uniform",
     NULL,
 };
-const static char* const * const mpls_label_disp_nodes[DPO_PROTO_NUM] =
+const static char* const * const mpls_label_disp_uniform_nodes[DPO_PROTO_NUM] =
 {
-    [DPO_PROTO_IP4]  = mpls_label_disp_ip4_nodes,
-    [DPO_PROTO_IP6]  = mpls_label_disp_ip6_nodes,
+    [DPO_PROTO_IP4]  = mpls_label_disp_uniform_ip4_nodes,
+    [DPO_PROTO_IP6]  = mpls_label_disp_uniform_ip6_nodes,
 };
 
 
 void
-mpls_disp_dpo_module_init (void)
+mpls_disp_dpo_module_init(void)
 {
-    dpo_register(DPO_MPLS_DISPOSITION, &mdd_vft, mpls_label_disp_nodes);
+    dpo_register(DPO_MPLS_DISPOSITION_PIPE, &mdd_vft,
+                 mpls_label_disp_pipe_nodes);
+    dpo_register(DPO_MPLS_DISPOSITION_UNIFORM, &mdd_vft,
+                 mpls_label_disp_uniform_nodes);
 }
index 9c01508..9c3cc46 100644 (file)
@@ -45,6 +45,11 @@ typedef struct mpls_disp_dpo_t
      * Number of locks/users of the label
      */
     u16 mdd_locks;
+
+    /**
+     * LSP mode
+     */
+    fib_mpls_lsp_mode_t mdd_mode;
 } mpls_disp_dpo_t;
 
 /**
@@ -60,11 +65,15 @@ _Static_assert((sizeof(mpls_disp_dpo_t) <= CLIB_CACHE_LINE_BYTES),
  *
  * @param payload_proto The ptocool of the payload packets that will
  *                      be imposed with this label header.
+ * @param rpf_id The RPF ID the packet will aquire - only for mcast
+ * @param mode The LSP mode; pipe or uniform
  * @param dpo The parent of the created MPLS label object
  */
-extern index_t mpls_disp_dpo_create(dpo_proto_t payload_proto,
-                                    fib_rpf_id_t rpf_id,
-                                    const dpo_id_t *dpo);
+extern void mpls_disp_dpo_create(dpo_proto_t payload_proto,
+                                 fib_rpf_id_t rpf_id,
+                                 fib_mpls_lsp_mode_t mode,
+                                 const dpo_id_t *parent,
+                                 dpo_id_t *dpo);
 
 extern u8* format_mpls_disp_dpo(u8 *s, va_list *args);
 
index fa5177a..954d637 100644 (file)
  */
 mpls_label_dpo_t *mpls_label_dpo_pool;
 
+/**
+ * Strings for the flags
+ */
+const char* mpls_label_dpo_attr_names[] = MPLS_LABEL_DPO_ATTR_NAMES;
+
+/**
+ * registered DPO types for each of the label sub-types. And there's a
+ * subtype for each of the flag combinations.
+ */
+static dpo_type_t mpls_label_dpo_types[1 << MPLS_LABEL_DPO_ATTR_MAX];
+
 static mpls_label_dpo_t *
 mpls_label_dpo_alloc (void)
 {
@@ -42,70 +53,138 @@ mpls_label_dpo_get_index (mpls_label_dpo_t *mld)
     return (mld - mpls_label_dpo_pool);
 }
 
-index_t
-mpls_label_dpo_create (mpls_label_t *label_stack,
+void
+mpls_label_dpo_create (fib_mpls_label_t *label_stack,
                        mpls_eos_bit_t eos,
-                       u8 ttl,
-                       u8 exp,
                        dpo_proto_t payload_proto,
-                      const dpo_id_t *dpo)
+                       mpls_label_dpo_flags_t flags,
+                      const dpo_id_t *parent,
+                       dpo_id_t *dpo)
 {
     mpls_label_dpo_t *mld;
+    dpo_type_t dtype;
     u32 ii;
 
+    if ((DPO_PROTO_IP4 != payload_proto) &&
+        (DPO_PROTO_IP6 != payload_proto))
+    {
+        /*
+         * remove unsupported configuration
+         */
+        flags &= ~MPLS_LABEL_DPO_FLAG_NO_IP_TTL_DECR;
+    }
+
     mld = mpls_label_dpo_alloc();
+    mld->mld_flags = flags;
+    dtype = mpls_label_dpo_types[flags];
 
     if (MPLS_LABEL_DPO_MAX_N_LABELS < vec_len(label_stack))
     {
         clib_warning("Label stack size exceeded");
-        dpo_stack(DPO_MPLS_LABEL,
+        dpo_stack(dtype,
                   mld->mld_payload_proto,
                   &mld->mld_dpo,
                   drop_dpo_get(DPO_PROTO_MPLS));
-        return (mpls_label_dpo_get_index(mld));
     }
+    else
+    {
+        mld->mld_n_labels = vec_len(label_stack);
+        mld->mld_n_hdr_bytes = mld->mld_n_labels * sizeof(mld->mld_hdr[0]);
+        mld->mld_payload_proto = payload_proto;
+
+        /*
+         * construct label rewrite headers for each value passed.
+         * get the header in network byte order since we will paint it
+         * on a packet in the data-plane
+         */
+        for (ii = 0; ii < mld->mld_n_labels-1; ii++)
+        {
+            vnet_mpls_uc_set_label(&mld->mld_hdr[ii].label_exp_s_ttl,
+                                   label_stack[ii].fml_value);
+            vnet_mpls_uc_set_exp(&mld->mld_hdr[ii].label_exp_s_ttl,
+                                 label_stack[ii].fml_exp);
+            vnet_mpls_uc_set_s(&mld->mld_hdr[ii].label_exp_s_ttl,
+                               MPLS_NON_EOS);
+            if (0 != label_stack[ii].fml_ttl)
+            {
+                vnet_mpls_uc_set_ttl(&mld->mld_hdr[ii].label_exp_s_ttl,
+                                     label_stack[ii].fml_ttl);
+            }
+            else
+            {
+                vnet_mpls_uc_set_ttl(&mld->mld_hdr[ii].label_exp_s_ttl,
+                                     MPLS_LABEL_DEFAULT_TTL);
+            }
+            mld->mld_hdr[ii].label_exp_s_ttl =
+                clib_host_to_net_u32(mld->mld_hdr[ii].label_exp_s_ttl);
+        }
 
-    mld->mld_n_labels = vec_len(label_stack);
-    mld->mld_n_hdr_bytes = mld->mld_n_labels * sizeof(mld->mld_hdr[0]);
-    mld->mld_payload_proto = payload_proto;
+        /*
+         * the inner most label
+         */
+        ii = mld->mld_n_labels-1;
+
+        vnet_mpls_uc_set_label(&mld->mld_hdr[ii].label_exp_s_ttl,
+                               label_stack[ii].fml_value);
+        vnet_mpls_uc_set_exp(&mld->mld_hdr[ii].label_exp_s_ttl,
+                             label_stack[ii].fml_exp);
+        vnet_mpls_uc_set_s(&mld->mld_hdr[ii].label_exp_s_ttl, eos);
+        if (0 != label_stack[ii].fml_ttl)
+        {
+            vnet_mpls_uc_set_ttl(&mld->mld_hdr[ii].label_exp_s_ttl,
+                                 label_stack[ii].fml_ttl);
+        }
+        else
+        {
+            vnet_mpls_uc_set_ttl(&mld->mld_hdr[ii].label_exp_s_ttl,
+                                 MPLS_LABEL_DEFAULT_TTL);
+        }
+        mld->mld_hdr[ii].label_exp_s_ttl =
+            clib_host_to_net_u32(mld->mld_hdr[ii].label_exp_s_ttl);
 
-    /*
-     * construct label rewrite headers for each value value passed.
-     * get the header in network byte order since we will paint it
-     * on a packet in the data-plane
-     */
+        /*
+         * pipe/uniform mode is only supported for the bottom of stack label
+         */
+        if (FIB_MPLS_LSP_MODE_UNIFORM == label_stack[ii].fml_mode)
+        {
+            mld->mld_flags |= MPLS_LABEL_DPO_FLAG_UNIFORM_MODE;
+        }
+        else
+        {
+            mld->mld_flags &= ~MPLS_LABEL_DPO_FLAG_UNIFORM_MODE;
+        }
+        dtype = mpls_label_dpo_types[mld->mld_flags];
 
-    for (ii = 0; ii < mld->mld_n_labels-1; ii++)
-    {
-       vnet_mpls_uc_set_label(&mld->mld_hdr[ii].label_exp_s_ttl, label_stack[ii]);
-       vnet_mpls_uc_set_ttl(&mld->mld_hdr[ii].label_exp_s_ttl, 255);
-       vnet_mpls_uc_set_exp(&mld->mld_hdr[ii].label_exp_s_ttl, 0);
-       vnet_mpls_uc_set_s(&mld->mld_hdr[ii].label_exp_s_ttl, MPLS_NON_EOS);
-       mld->mld_hdr[ii].label_exp_s_ttl =
-           clib_host_to_net_u32(mld->mld_hdr[ii].label_exp_s_ttl);
+        /*
+         * stack this label object on its parent.
+         */
+        dpo_stack(dtype,
+                  mld->mld_payload_proto,
+                  &mld->mld_dpo,
+                  parent);
     }
 
-    /*
-     * the inner most label
-     */
-    ii = mld->mld_n_labels-1;
+    dpo_set(dpo,
+            dtype,
+            mld->mld_payload_proto,
+            mpls_label_dpo_get_index(mld));
+}
 
-    vnet_mpls_uc_set_label(&mld->mld_hdr[ii].label_exp_s_ttl, label_stack[ii]);
-    vnet_mpls_uc_set_ttl(&mld->mld_hdr[ii].label_exp_s_ttl, ttl);
-    vnet_mpls_uc_set_exp(&mld->mld_hdr[ii].label_exp_s_ttl, exp);
-    vnet_mpls_uc_set_s(&mld->mld_hdr[ii].label_exp_s_ttl, eos);
-    mld->mld_hdr[ii].label_exp_s_ttl =
-       clib_host_to_net_u32(mld->mld_hdr[ii].label_exp_s_ttl);
+u8*
+format_mpls_label_dpo_flags (u8 *s, va_list *args)
+{
+    mpls_label_dpo_flags_t flags = va_arg (*args, int);
+    mpls_label_dpo_attr_t attr;
 
-    /*
-     * stack this label objct on its parent.
-     */
-    dpo_stack(DPO_MPLS_LABEL,
-              mld->mld_payload_proto,
-              &mld->mld_dpo,
-              dpo);
+    FOR_EACH_MPLS_LABEL_DPO_ATTR(attr)
+    {
+        if ((1 << attr) & flags)
+        {
+            s = format(s, "%s,", mpls_label_dpo_attr_names[attr]);
+        }
+    }
 
-    return (mpls_label_dpo_get_index(mld));
+    return (s);
 }
 
 u8*
@@ -117,17 +196,18 @@ format_mpls_label_dpo (u8 *s, va_list *args)
     mpls_label_dpo_t *mld;
     u32 ii;
 
-    s = format(s, "mpls-label:[%d]:", index);
-
     if (pool_is_free_index(mpls_label_dpo_pool, index))
     {
         /*
          * the packet trace can be printed after the DPO has been deleted
          */
-        return (s);
+        return (format(s, "mpls-label[???,%d]:", index));
     }
 
     mld = mpls_label_dpo_get(index);
+    s = format(s, "mpls-label[%U%d]:",
+               format_mpls_label_dpo_flags,
+               (int) mld->mld_flags, index);
 
     for (ii = 0; ii < mld->mld_n_labels; ii++)
     {
@@ -178,12 +258,21 @@ typedef struct mpls_label_imposition_trace_t_
      * The MPLS header imposed
      */
     mpls_unicast_header_t hdr;
+
+    /**
+     * TTL imposed - only valid for uniform LSPs
+     */
+    u8 ttl;
+
+    /**
+     * TTL imposed - only valid for uniform LSPs
+     */
+    u8 exp;
 } mpls_label_imposition_trace_t;
 
 always_inline mpls_unicast_header_t *
 mpls_label_paint (vlib_buffer_t * b0,
-                  mpls_label_dpo_t *mld0,
-                  u8 ttl0)
+                  mpls_label_dpo_t *mld0)
 {
     mpls_unicast_header_t *hdr0;
 
@@ -201,19 +290,74 @@ mpls_label_paint (vlib_buffer_t * b0,
         clib_memcpy(hdr0, mld0->mld_hdr, mld0->mld_n_hdr_bytes);
         hdr0 = hdr0 + (mld0->mld_n_labels - 1);
     }
+
+    return (hdr0);
+}
+
+/**
+ * Paint on an MPLS label and fixup the TTL
+ */
+always_inline mpls_unicast_header_t *
+mpls_label_paint_w_ttl (vlib_buffer_t * b0,
+                        mpls_label_dpo_t *mld0,
+                        u8 ttl0)
+{
+    mpls_unicast_header_t *hdr0;
+
+    hdr0 = mpls_label_paint(b0, mld0);
+
     /* fixup the TTL for the inner most label */
     ((char*)hdr0)[3] = ttl0;
 
     return (hdr0);
 }
 
+/**
+ * Paint on an MPLS label and fixup the TTL and EXP bits.
+ */
+always_inline mpls_unicast_header_t *
+mpls_label_paint_w_ttl_exp (vlib_buffer_t * b0,
+                            mpls_label_dpo_t *mld0,
+                            u8 ttl0,
+                            u8 exp0)
+{
+    mpls_unicast_header_t *hdr0;
+
+    hdr0 = mpls_label_paint_w_ttl(b0, mld0, ttl0);
+
+    /* fixup the EXP for the inner most label */
+    ((char*)hdr0)[2] |= (exp0 << 1);
+
+    return (hdr0);
+}
+
+/**
+ * Paint on an MPLS label and fixup the TTL and EXP bits
+ * When the EXP bits are *already* bit shift to the correct place in
+ * in the 2nd byte (i.e. they were read from another label)
+ */
+always_inline mpls_unicast_header_t *
+mpls_label_paint_w_ttl_mpls_exp (vlib_buffer_t * b0,
+                                 mpls_label_dpo_t *mld0,
+                                 u8 ttl0,
+                                 u8 exp0)
+{
+    mpls_unicast_header_t *hdr0;
+
+    hdr0 = mpls_label_paint_w_ttl(b0, mld0, ttl0);
+
+    /* fixup the EXP for the inner most label */
+    ((char*)hdr0)[2] |= exp0;
+
+    return (hdr0);
+}
+
 always_inline uword
 mpls_label_imposition_inline (vlib_main_t * vm,
                               vlib_node_runtime_t * node,
                               vlib_frame_t * from_frame,
-                              u8 payload_is_ip4,
-                              u8 payload_is_ip6,
-                              u8 payload_is_ethernet)
+                              const dpo_proto_t dproto,
+                              const mpls_label_dpo_flags_t flags)
 {
     u32 n_left_from, next_index, * from, * to_next;
 
@@ -235,7 +379,8 @@ mpls_label_imposition_inline (vlib_main_t * vm,
             mpls_label_dpo_t *mld0, *mld1, *mld2, *mld3;
             vlib_buffer_t * b0, *b1, * b2, *b3;
             u32 next0, next1, next2, next3;
-            u8 ttl0, ttl1,ttl2, ttl3 ;
+            u8 ttl0, ttl1, ttl2, ttl3;
+            u8 exp0, exp1, exp2, exp3;
 
             bi0 = to_next[0] = from[0];
             bi1 = to_next[1] = from[1];
@@ -282,141 +427,247 @@ mpls_label_imposition_inline (vlib_main_t * vm,
             mld2 = mpls_label_dpo_get(mldi2);
             mld3 = mpls_label_dpo_get(mldi3);
 
-            if (payload_is_ip4)
+            if (DPO_PROTO_MPLS != dproto)
             {
                 /*
-                 * decrement the TTL on ingress to the LSP
+                 * These are the non-MPLS payload imposition cases
                  */
-                ip4_header_t * ip0 = vlib_buffer_get_current(b0);
-                ip4_header_t * ip1 = vlib_buffer_get_current(b1);
-                ip4_header_t * ip2 = vlib_buffer_get_current(b2);
-                ip4_header_t * ip3 = vlib_buffer_get_current(b3);
-                u32 checksum0;
-                u32 checksum1;
-                u32 checksum2;
-                u32 checksum3;
-
-                checksum0 = ip0->checksum + clib_host_to_net_u16 (0x0100);
-                checksum1 = ip1->checksum + clib_host_to_net_u16 (0x0100);
-                checksum2 = ip2->checksum + clib_host_to_net_u16 (0x0100);
-                checksum3 = ip3->checksum + clib_host_to_net_u16 (0x0100);
-
-                checksum0 += checksum0 >= 0xffff;
-                checksum1 += checksum1 >= 0xffff;
-                checksum2 += checksum2 >= 0xffff;
-                checksum3 += checksum3 >= 0xffff;
-
-                ip0->checksum = checksum0;
-                ip1->checksum = checksum1;
-                ip2->checksum = checksum2;
-                ip3->checksum = checksum3;
-
-                ip0->ttl -= 1;
-                ip1->ttl -= 1;
-                ip2->ttl -= 1;
-                ip3->ttl -= 1;
-
-                ttl1 = ip1->ttl;
-                ttl0 = ip0->ttl;
-                ttl3 = ip3->ttl;
-                ttl2 = ip2->ttl;
-            }
-            else if (payload_is_ip6)
-            {
-                /*
-                 * decrement the TTL on ingress to the LSP
-                 */
-                ip6_header_t * ip0 = vlib_buffer_get_current(b0);
-                ip6_header_t * ip1 = vlib_buffer_get_current(b1);
-                ip6_header_t * ip2 = vlib_buffer_get_current(b2);
-                ip6_header_t * ip3 = vlib_buffer_get_current(b3);
-
-                ip0->hop_limit -= 1;
-                ip1->hop_limit -= 1;
-                ip2->hop_limit -= 1;
-                ip3->hop_limit -= 1;
-
-                ttl0 = ip0->hop_limit;
-                ttl1 = ip1->hop_limit;
-                ttl2 = ip2->hop_limit;
-                ttl3 = ip3->hop_limit;
-            }
-            else if (payload_is_ethernet)
-            {
+                if (DPO_PROTO_IP4 == dproto)
+                {
+                    ip4_header_t * ip0 = vlib_buffer_get_current(b0);
+                    ip4_header_t * ip1 = vlib_buffer_get_current(b1);
+                    ip4_header_t * ip2 = vlib_buffer_get_current(b2);
+                    ip4_header_t * ip3 = vlib_buffer_get_current(b3);
+
+                    if (!(MPLS_LABEL_DPO_FLAG_NO_IP_TTL_DECR & flags))
+                    {
+                        /*
+                         * decrement the TTL on ingress to the LSP
+                         */
+                        u32 checksum0;
+                        u32 checksum1;
+                        u32 checksum2;
+                        u32 checksum3;
+
+                        checksum0 = ip0->checksum + clib_host_to_net_u16 (0x0100);
+                        checksum1 = ip1->checksum + clib_host_to_net_u16 (0x0100);
+                        checksum2 = ip2->checksum + clib_host_to_net_u16 (0x0100);
+                        checksum3 = ip3->checksum + clib_host_to_net_u16 (0x0100);
+
+                        checksum0 += checksum0 >= 0xffff;
+                        checksum1 += checksum1 >= 0xffff;
+                        checksum2 += checksum2 >= 0xffff;
+                        checksum3 += checksum3 >= 0xffff;
+
+                        ip0->checksum = checksum0;
+                        ip1->checksum = checksum1;
+                        ip2->checksum = checksum2;
+                        ip3->checksum = checksum3;
+
+                        ip0->ttl -= 1;
+                        ip1->ttl -= 1;
+                        ip2->ttl -= 1;
+                        ip3->ttl -= 1;
+                    }
+
+                    if (flags & MPLS_LABEL_DPO_FLAG_UNIFORM_MODE)
+                    {
+                        ttl1 = ip1->ttl;
+                        ttl0 = ip0->ttl;
+                        ttl3 = ip3->ttl;
+                        ttl2 = ip2->ttl;
+                        /* by default copy the 3 most significant bits */
+                        exp0 = ip_dscp_to_mpls_exp(ip0->tos);
+                        exp1 = ip_dscp_to_mpls_exp(ip1->tos);
+                        exp2 = ip_dscp_to_mpls_exp(ip2->tos);
+                        exp3 = ip_dscp_to_mpls_exp(ip3->tos);
+                    }
+                }
+                else if (DPO_PROTO_IP6 == dproto)
+                {
+                    /*
+                     * decrement the TTL on ingress to the LSP
+                     */
+                    ip6_header_t * ip0 = vlib_buffer_get_current(b0);
+                    ip6_header_t * ip1 = vlib_buffer_get_current(b1);
+                    ip6_header_t * ip2 = vlib_buffer_get_current(b2);
+                    ip6_header_t * ip3 = vlib_buffer_get_current(b3);
+
+                    if (!(MPLS_LABEL_DPO_FLAG_NO_IP_TTL_DECR & flags))
+                    {
+                        ip0->hop_limit -= 1;
+                        ip1->hop_limit -= 1;
+                        ip2->hop_limit -= 1;
+                        ip3->hop_limit -= 1;
+                    }
+                    if (flags & MPLS_LABEL_DPO_FLAG_UNIFORM_MODE)
+                    {
+                        ttl0 = ip0->hop_limit;
+                        ttl1 = ip1->hop_limit;
+                        ttl2 = ip2->hop_limit;
+                        ttl3 = ip3->hop_limit;
+                        /* by default copy the 3 most significant bits */
+                        exp0 = ip_dscp_to_mpls_exp(
+                            ip6_traffic_class_network_order(ip0));
+                        exp1 = ip_dscp_to_mpls_exp(
+                            ip6_traffic_class_network_order(ip1));
+                        exp2 = ip_dscp_to_mpls_exp(
+                            ip6_traffic_class_network_order(ip2));
+                        exp3 = ip_dscp_to_mpls_exp(
+                            ip6_traffic_class_network_order(ip3));
+                    }
+                }
+                else
+                {
+                    /*
+                     * nothing to change in the ethernet header
+                     */
+                    ttl0 = ttl1 = ttl2 = ttl3 = MPLS_LABEL_DEFAULT_TTL;
+                    exp0 = exp1 = exp2 = exp3 = MPLS_LABEL_DEFAULT_EXP;
+                }
                 /*
-                 * nothing to chang ein the ethernet header
+                 * These are the non-MPLS payload imposition cases.
+                 * Based on the LSP mode either, for uniform, copy down the TTL
+                 * and EXP from the payload or, for pipe mode, slap on the value
+                 * requested from config
                  */
-                ttl0 = ttl1 = ttl2 = ttl3 = 255;
+                if (flags & MPLS_LABEL_DPO_FLAG_UNIFORM_MODE)
+                {
+                    hdr0 = mpls_label_paint_w_ttl_exp(b0, mld0, ttl0, exp0);
+                    hdr1 = mpls_label_paint_w_ttl_exp(b1, mld1, ttl1, exp1);
+                    hdr2 = mpls_label_paint_w_ttl_exp(b2, mld2, ttl2, exp2);
+                    hdr3 = mpls_label_paint_w_ttl_exp(b3, mld3, ttl3, exp3);
+                }
+                else
+                {
+                    hdr0 = mpls_label_paint(b0, mld0);
+                    hdr1 = mpls_label_paint(b1, mld1);
+                    hdr2 = mpls_label_paint(b2, mld2);
+                    hdr3 = mpls_label_paint(b3, mld3);
+                }
             }
             else
             {
                 /*
                  * else, the packet to be encapped is an MPLS packet
+                 * there are two cases to consider:
+                 *  1 - this is an MPLS label swap at an LSP midpoint.
+                 *      recognisable because mpls.first = 1. In this case the
+                 *      TTL must be set to the current value -1.
+                 *  2 - The MPLS packet is recursing (or being injected into)
+                 *      this LSP, in which case the pipe/uniform rules apply
+                 *
                  */
                 if (PREDICT_TRUE(vnet_buffer(b0)->mpls.first))
                 {
                     /*
-                     * The first label to be imposed on the packet. this is a label swap.
-                     * in which case we stashed the TTL and EXP bits in the
-                     * packet in the lookup node
+                     * The first label to be imposed on the packet. this is a
+                     * label swap.in which case we stashed the TTL and EXP bits
+                     * in the packet in the lookup node
                      */
                     ASSERT(0 != vnet_buffer (b0)->mpls.ttl);
 
                     ttl0 = vnet_buffer(b0)->mpls.ttl - 1;
+                    exp0 = vnet_buffer(b0)->mpls.exp;
+                    hdr0 = mpls_label_paint_w_ttl_exp(b0, mld0, ttl0, exp0);
                 }
                 else
                 {
                     /*
-                     * not the first label. implying we are recusring down a chain of
-                     * output labels.
-                     * Each layer is considered a new LSP - hence the TTL is reset.
+                     * not the first label. implying we are recusring down a
+                     * chain of output labels. Each layer is considered a new
+                     * LSP - hence the TTL/EXP are pipe/uniform handled
                      */
-                    ttl0 = 255;
+                    if (flags & MPLS_LABEL_DPO_FLAG_UNIFORM_MODE)
+                    {
+                        hdr0 = vlib_buffer_get_current(b0);
+                        ttl0 = ((u8*)hdr0)[3];
+                        exp0 = ((u8*)hdr0)[2] & 0xe;
+                        hdr0 = mpls_label_paint_w_ttl_mpls_exp(b0, mld0, ttl0, exp0);
+                    }
+                    else
+                    {
+                        hdr0 = mpls_label_paint(b0, mld0);
+                    }
                 }
                 if (PREDICT_TRUE(vnet_buffer(b1)->mpls.first))
                 {
-                    ASSERT(1 != vnet_buffer (b1)->mpls.ttl);
+                    ASSERT(0 != vnet_buffer (b1)->mpls.ttl);
+
                     ttl1 = vnet_buffer(b1)->mpls.ttl - 1;
+                    exp1 = vnet_buffer(b1)->mpls.exp;
+                    hdr1 = mpls_label_paint_w_ttl_exp(b1, mld1, ttl1, exp1);
                 }
                 else
                 {
-                    ttl1 = 255;
+                    if (flags & MPLS_LABEL_DPO_FLAG_UNIFORM_MODE)
+                    {
+                        hdr1 = vlib_buffer_get_current(b1);
+                        ttl1 = ((u8*)hdr1)[3];
+                        exp1 = ((u8*)hdr1)[2] & 0xe;
+                        hdr1 = mpls_label_paint_w_ttl_mpls_exp(b1, mld1, ttl1, exp1);
+                    }
+                    else
+                    {
+                        hdr1 = mpls_label_paint(b1, mld1);
+                    }
                 }
                 if (PREDICT_TRUE(vnet_buffer(b2)->mpls.first))
                 {
-                    ASSERT(1 != vnet_buffer (b2)->mpls.ttl);
+                    ASSERT(0 != vnet_buffer (b2)->mpls.ttl);
 
                     ttl2 = vnet_buffer(b2)->mpls.ttl - 1;
+                    exp2 = vnet_buffer(b2)->mpls.exp;
+                    hdr2 = mpls_label_paint_w_ttl_exp(b2, mld2, ttl2, exp2);
                 }
                 else
                 {
-                    ttl2 = 255;
+                    if (flags & MPLS_LABEL_DPO_FLAG_UNIFORM_MODE)
+                    {
+                        hdr2 = vlib_buffer_get_current(b2);
+                        ttl2 = ((u8*)hdr2)[3];
+                        exp2 = ((u8*)hdr2)[2] & 0xe;
+                        hdr2 = mpls_label_paint_w_ttl_mpls_exp(b2, mld2, ttl2, exp2);
+                    }
+                    else
+                    {
+                        hdr2 = mpls_label_paint(b2, mld2);
+                    }
                 }
                 if (PREDICT_TRUE(vnet_buffer(b3)->mpls.first))
                 {
-                    ASSERT(1 != vnet_buffer (b3)->mpls.ttl);
+                    ASSERT(0 != vnet_buffer (b3)->mpls.ttl);
+
                     ttl3 = vnet_buffer(b3)->mpls.ttl - 1;
+                    exp3 = vnet_buffer(b0)->mpls.exp;
+                    hdr3 = mpls_label_paint_w_ttl_exp(b3, mld3, ttl3, exp3);
                 }
                 else
                 {
-                    ttl3 = 255;
+                    if (flags & MPLS_LABEL_DPO_FLAG_UNIFORM_MODE)
+                    {
+                        hdr3 = vlib_buffer_get_current(b3);
+                        ttl3 = ((u8*)hdr3)[3];
+                        exp3 = ((u8*)hdr3)[2] & 0xe;
+                        hdr3 = mpls_label_paint_w_ttl_mpls_exp(b3, mld3, ttl3, exp3);
+                    }
+                    else
+                    {
+                        hdr3 = mpls_label_paint(b3, mld3);
+                    }
                 }
-            }
-            vnet_buffer(b0)->mpls.first = 0;
-            vnet_buffer(b1)->mpls.first = 0;
-            vnet_buffer(b2)->mpls.first = 0;
-            vnet_buffer(b3)->mpls.first = 0;
 
-            /* Paint the MPLS header */
-            hdr0 = mpls_label_paint(b0, mld0, ttl0);
-            hdr1 = mpls_label_paint(b1, mld1, ttl1);
-            hdr2 = mpls_label_paint(b2, mld2, ttl2);
-            hdr3 = mpls_label_paint(b3, mld3, ttl3);
+                vnet_buffer(b0)->mpls.first = 0;
+                vnet_buffer(b1)->mpls.first = 0;
+                vnet_buffer(b2)->mpls.first = 0;
+                vnet_buffer(b3)->mpls.first = 0;
+            }
 
             next0 = mld0->mld_dpo.dpoi_next_node;
             next1 = mld1->mld_dpo.dpoi_next_node;
             next2 = mld2->mld_dpo.dpoi_next_node;
             next3 = mld3->mld_dpo.dpoi_next_node;
+
             vnet_buffer(b0)->ip.adj_index[VLIB_TX] = mld0->mld_dpo.dpoi_index;
             vnet_buffer(b1)->ip.adj_index[VLIB_TX] = mld1->mld_dpo.dpoi_index;
             vnet_buffer(b2)->ip.adj_index[VLIB_TX] = mld2->mld_dpo.dpoi_index;
@@ -427,24 +678,60 @@ mpls_label_imposition_inline (vlib_main_t * vm,
                 mpls_label_imposition_trace_t *tr =
                     vlib_add_trace (vm, node, b0, sizeof (*tr));
                 tr->hdr = *hdr0;
+                if (flags & MPLS_LABEL_DPO_FLAG_UNIFORM_MODE)
+                {
+                    tr->ttl = ttl0;
+                    tr->exp = exp0;
+                }
+                else
+                {
+                    tr->ttl = tr->exp = 0;
+                }
             }
             if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
             {
                 mpls_label_imposition_trace_t *tr =
                     vlib_add_trace (vm, node, b1, sizeof (*tr));
                 tr->hdr = *hdr1;
+                if (flags & MPLS_LABEL_DPO_FLAG_UNIFORM_MODE)
+                {
+                    tr->ttl = ttl1;
+                    tr->exp = exp1;
+                }
+                else
+                {
+                    tr->ttl = tr->exp = 0;
+                }
             }
             if (PREDICT_FALSE(b2->flags & VLIB_BUFFER_IS_TRACED))
             {
                 mpls_label_imposition_trace_t *tr =
                     vlib_add_trace (vm, node, b2, sizeof (*tr));
                 tr->hdr = *hdr2;
+                if (flags & MPLS_LABEL_DPO_FLAG_UNIFORM_MODE)
+                {
+                    tr->ttl = ttl2;
+                    tr->exp = exp2;
+                }
+                else
+                {
+                    tr->ttl = tr->exp = 0;
+                }
             }
             if (PREDICT_FALSE(b3->flags & VLIB_BUFFER_IS_TRACED))
             {
                 mpls_label_imposition_trace_t *tr =
                     vlib_add_trace (vm, node, b3, sizeof (*tr));
                 tr->hdr = *hdr3;
+                if (flags & MPLS_LABEL_DPO_FLAG_UNIFORM_MODE)
+                {
+                    tr->ttl = ttl3;
+                    tr->exp = exp3;
+                }
+                else
+                {
+                    tr->ttl = tr->exp = 0;
+                }
             }
 
             vlib_validate_buffer_enqueue_x4(vm, node, next_index, to_next,
@@ -459,8 +746,8 @@ mpls_label_imposition_inline (vlib_main_t * vm,
             mpls_label_dpo_t *mld0;
             vlib_buffer_t * b0;
             u32 bi0, mldi0;
+            u8 ttl0, exp0;
             u32 next0;
-            u8 ttl;
 
             bi0 = from[0];
             to_next[0] = bi0;
@@ -475,67 +762,99 @@ mpls_label_imposition_inline (vlib_main_t * vm,
             mldi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
             mld0 = mpls_label_dpo_get(mldi0);
 
-            if (payload_is_ip4)
+            if (DPO_PROTO_MPLS != dproto)
             {
-                /*
-                 * decrement the TTL on ingress to the LSP
-                 */
-                ip4_header_t * ip0 = vlib_buffer_get_current(b0);
-                u32 checksum0;
-
-                checksum0 = ip0->checksum + clib_host_to_net_u16 (0x0100);
-                checksum0 += checksum0 >= 0xffff;
+                if (DPO_PROTO_IP4 == dproto)
+                {
+                    /*
+                     * decrement the TTL on ingress to the LSP
+                     */
+                    ip4_header_t * ip0 = vlib_buffer_get_current(b0);
+                    if (!(MPLS_LABEL_DPO_FLAG_NO_IP_TTL_DECR & flags))
+                    {
+                        u32 checksum0;
+
+                        checksum0 = ip0->checksum + clib_host_to_net_u16 (0x0100);
+                        checksum0 += checksum0 >= 0xffff;
+
+                        ip0->checksum = checksum0;
+                        ip0->ttl -= 1;
+                    }
+                    if (flags & MPLS_LABEL_DPO_FLAG_UNIFORM_MODE)
+                    {
+                        ttl0 = ip0->ttl;
+                        exp0 = ip_dscp_to_mpls_exp(ip0->tos);
+                    }
+                }
+                else if (DPO_PROTO_IP6 == dproto)
+                {
+                    /*
+                     * decrement the TTL on ingress to the LSP
+                     */
+                    ip6_header_t * ip0 = vlib_buffer_get_current(b0);
+
+                    if (!(MPLS_LABEL_DPO_FLAG_NO_IP_TTL_DECR & flags))
+                    {
+                        ip0->hop_limit -= 1;
+                    }
+                    if (flags & MPLS_LABEL_DPO_FLAG_UNIFORM_MODE)
+                    {
+                        ttl0 = ip0->hop_limit;
+                        exp0 = ip_dscp_to_mpls_exp(
+                            ip6_traffic_class_network_order(ip0));
+                    }
+                }
+                else
+                {
+                    /*
+                     * nothing to change in the ethernet header
+                     */
+                    ttl0 = MPLS_LABEL_DEFAULT_TTL;
+                    exp0 = MPLS_LABEL_DEFAULT_EXP;
+                }
 
-                ip0->checksum = checksum0;
-                ip0->ttl -= 1;
-                ttl = ip0->ttl;
-            }
-            else if (payload_is_ip6)
-            {
                 /*
-                 * decrement the TTL on ingress to the LSP
+                 * These are the non-MPLS payload imposition cases.
+                 * Based on the LSP mode either, for uniform, copy down the TTL
+                 * from the payload or, for pipe mode, slap on the value
+                 * requested from config
                  */
-                ip6_header_t * ip0 = vlib_buffer_get_current(b0);
-
-                ip0->hop_limit -= 1;
-                ttl = ip0->hop_limit;
+                if (flags & MPLS_LABEL_DPO_FLAG_UNIFORM_MODE)
+                {
+                    hdr0 = mpls_label_paint_w_ttl_exp(b0, mld0, ttl0, exp0);
+                }
+                else
+                {
+                    hdr0 = mpls_label_paint(b0, mld0);
+                }
             }
             else
             {
-                /*
-                 * else, the packet to be encapped is an MPLS packet
-                 */
-                if (vnet_buffer(b0)->mpls.first)
+                if (PREDICT_TRUE(vnet_buffer(b0)->mpls.first))
                 {
-                    /*
-                     * The first label to be imposed on the packet. this is a label swap.
-                     * in which case we stashed the TTL and EXP bits in the
-                     * packet in the lookup node
-                     */
                     ASSERT(0 != vnet_buffer (b0)->mpls.ttl);
 
-                    ttl = vnet_buffer(b0)->mpls.ttl - 1;
+                    ttl0 = vnet_buffer(b0)->mpls.ttl - 1;
+                    exp0 = vnet_buffer(b0)->mpls.exp;
+                    hdr0 = mpls_label_paint_w_ttl_exp(b0, mld0, ttl0, exp0);
                 }
                 else
                 {
-                    /*
-                     * not the first label. implying we are recusring down a chain of
-                     * output labels.
-                     * Each layer is considered a new LSP - hence the TTL is reset.
-                     */
-                    ttl = 255;
+                    if (flags & MPLS_LABEL_DPO_FLAG_UNIFORM_MODE)
+                    {
+                        hdr0 = vlib_buffer_get_current(b0);
+                        ttl0 = ((u8*)hdr0)[3];
+                        exp0 = ((u8*)hdr0)[2] & 0xe;
+                        hdr0 = mpls_label_paint_w_ttl_mpls_exp(b0, mld0, ttl0, exp0);
+                    }
+                    else
+                    {
+                        hdr0 = mpls_label_paint(b0, mld0);
+                    }
                 }
-            }
-            vnet_buffer(b0)->mpls.first = 0;
-
-            /* Paint the MPLS header */
-            vlib_buffer_advance(b0, -(mld0->mld_n_hdr_bytes));
-            hdr0 = vlib_buffer_get_current(b0);
-            clib_memcpy(hdr0, mld0->mld_hdr, mld0->mld_n_hdr_bytes);
 
-            /* fixup the TTL for the inner most label */
-            hdr0 = hdr0 + (mld0->mld_n_labels - 1);
-            ((char*)hdr0)[3] = ttl;
+                vnet_buffer(b0)->mpls.first = 0;
+            }
 
             next0 = mld0->mld_dpo.dpoi_next_node;
             vnet_buffer(b0)->ip.adj_index[VLIB_TX] = mld0->mld_dpo.dpoi_index;
@@ -545,7 +864,16 @@ mpls_label_imposition_inline (vlib_main_t * vm,
                 mpls_label_imposition_trace_t *tr =
                     vlib_add_trace (vm, node, b0, sizeof (*tr));
                 tr->hdr = *hdr0;
-            }
+                if (flags & MPLS_LABEL_DPO_FLAG_UNIFORM_MODE)
+                {
+                    tr->ttl = ttl0;
+                    tr->exp = exp0;
+                }
+                else
+                {
+                    tr->ttl = tr->exp = 0;
+                }
+           }
 
             vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
                                             n_left_to_next, bi0, next0);
@@ -575,16 +903,18 @@ format_mpls_label_imposition_trace (u8 * s, va_list * args)
 }
 
 static uword
-mpls_label_imposition (vlib_main_t * vm,
-                       vlib_node_runtime_t * node,
-                       vlib_frame_t * frame)
+mpls_mpls_label_imposition_pipe (vlib_main_t * vm,
+                                 vlib_node_runtime_t * node,
+                                 vlib_frame_t * frame)
 {
-    return (mpls_label_imposition_inline(vm, node, frame, 0, 0, 0));
+    return (mpls_label_imposition_inline(vm, node, frame,
+                                         DPO_PROTO_MPLS,
+                                         MPLS_LABEL_DPO_FLAG_NONE));
 }
 
-VLIB_REGISTER_NODE (mpls_label_imposition_node) = {
-    .function = mpls_label_imposition,
-    .name = "mpls-label-imposition",
+VLIB_REGISTER_NODE (mpls_mpls_label_imposition_pipe_node) = {
+    .function = mpls_mpls_label_imposition_pipe,
+    .name = "mpls-label-imposition-pipe",
     .vector_size = sizeof (u32),
 
     .format_trace = format_mpls_label_imposition_trace,
@@ -593,20 +923,22 @@ VLIB_REGISTER_NODE (mpls_label_imposition_node) = {
         [0] = "mpls-drop",
     }
 };
-VLIB_NODE_FUNCTION_MULTIARCH (mpls_label_imposition_node,
-                              mpls_label_imposition)
+VLIB_NODE_FUNCTION_MULTIARCH (mpls_mpls_label_imposition_pipe_node,
+                              mpls_mpls_label_imposition_pipe)
 
 static uword
-ip4_mpls_label_imposition (vlib_main_t * vm,
-                           vlib_node_runtime_t * node,
-                           vlib_frame_t * frame)
+ip4_mpls_label_imposition_pipe (vlib_main_t * vm,
+                                vlib_node_runtime_t * node,
+                                vlib_frame_t * frame)
 {
-    return (mpls_label_imposition_inline(vm, node, frame, 1, 0, 0));
+    return (mpls_label_imposition_inline(vm, node, frame,
+                                         DPO_PROTO_IP4,
+                                         MPLS_LABEL_DPO_FLAG_NONE));
 }
 
-VLIB_REGISTER_NODE (ip4_mpls_label_imposition_node) = {
-    .function = ip4_mpls_label_imposition,
-    .name = "ip4-mpls-label-imposition",
+VLIB_REGISTER_NODE (ip4_mpls_label_imposition_pipe_node) = {
+    .function = ip4_mpls_label_imposition_pipe,
+    .name = "ip4-mpls-label-imposition-pipe",
     .vector_size = sizeof (u32),
 
     .format_trace = format_mpls_label_imposition_trace,
@@ -615,20 +947,22 @@ VLIB_REGISTER_NODE (ip4_mpls_label_imposition_node) = {
         [0] = "ip4-drop",
     }
 };
-VLIB_NODE_FUNCTION_MULTIARCH (ip4_mpls_label_imposition_node,
-                              ip4_mpls_label_imposition)
+VLIB_NODE_FUNCTION_MULTIARCH (ip4_mpls_label_imposition_pipe_node,
+                              ip4_mpls_label_imposition_pipe)
 
 static uword
-ip6_mpls_label_imposition (vlib_main_t * vm,
-                           vlib_node_runtime_t * node,
-                           vlib_frame_t * frame)
+ip6_mpls_label_imposition_pipe (vlib_main_t * vm,
+                                vlib_node_runtime_t * node,
+                                vlib_frame_t * frame)
 {
-    return (mpls_label_imposition_inline(vm, node, frame, 0, 1, 0));
+    return (mpls_label_imposition_inline(vm, node, frame,
+                                         DPO_PROTO_IP6,
+                                         MPLS_LABEL_DPO_FLAG_NONE));
 }
 
-VLIB_REGISTER_NODE (ip6_mpls_label_imposition_node) = {
-    .function = ip6_mpls_label_imposition,
-    .name = "ip6-mpls-label-imposition",
+VLIB_REGISTER_NODE (ip6_mpls_label_imposition_pipe_node) = {
+    .function = ip6_mpls_label_imposition_pipe,
+    .name = "ip6-mpls-label-imposition-pipe",
     .vector_size = sizeof (u32),
 
     .format_trace = format_mpls_label_imposition_trace,
@@ -637,20 +971,22 @@ VLIB_REGISTER_NODE (ip6_mpls_label_imposition_node) = {
         [0] = "ip6-drop",
     }
 };
-VLIB_NODE_FUNCTION_MULTIARCH (ip6_mpls_label_imposition_node,
-                              ip6_mpls_label_imposition)
+VLIB_NODE_FUNCTION_MULTIARCH (ip6_mpls_label_imposition_pipe_node,
+                              ip6_mpls_label_imposition_pipe)
 
 static uword
-ethernet_mpls_label_imposition (vlib_main_t * vm,
-                                vlib_node_runtime_t * node,
-                                vlib_frame_t * frame)
+ethernet_mpls_label_imposition_pipe (vlib_main_t * vm,
+                                     vlib_node_runtime_t * node,
+                                     vlib_frame_t * frame)
 {
-    return (mpls_label_imposition_inline(vm, node, frame, 0, 0, 1));
+    return (mpls_label_imposition_inline(vm, node, frame,
+                                         DPO_PROTO_ETHERNET,
+                                         MPLS_LABEL_DPO_FLAG_NONE));
 }
 
-VLIB_REGISTER_NODE (ethernet_mpls_label_imposition_node) = {
-    .function = ethernet_mpls_label_imposition,
-    .name = "ethernet-mpls-label-imposition",
+VLIB_REGISTER_NODE (ethernet_mpls_label_imposition_pipe_node) = {
+    .function = ethernet_mpls_label_imposition_pipe,
+    .name = "ethernet-mpls-label-imposition-pipe",
     .vector_size = sizeof (u32),
 
     .format_trace = format_mpls_label_imposition_trace,
@@ -659,8 +995,205 @@ VLIB_REGISTER_NODE (ethernet_mpls_label_imposition_node) = {
         [0] = "error-drop",
     }
 };
-VLIB_NODE_FUNCTION_MULTIARCH (ethernet_mpls_label_imposition_node,
-                              ethernet_mpls_label_imposition)
+
+VLIB_NODE_FUNCTION_MULTIARCH (ethernet_mpls_label_imposition_pipe_node,
+                              ethernet_mpls_label_imposition_pipe)
+
+static uword
+mpls_mpls_label_imposition_uniform (vlib_main_t * vm,
+                                    vlib_node_runtime_t * node,
+                                    vlib_frame_t * frame)
+{
+    return (mpls_label_imposition_inline(vm, node, frame,
+                                         DPO_PROTO_MPLS,
+                                         MPLS_LABEL_DPO_FLAG_UNIFORM_MODE));
+}
+
+VLIB_REGISTER_NODE (mpls_mpls_label_imposition_uniform_node) = {
+    .function = mpls_mpls_label_imposition_uniform,
+    .name = "mpls-label-imposition-uniform",
+    .vector_size = sizeof (u32),
+
+    .format_trace = format_mpls_label_imposition_trace,
+    .n_next_nodes = 1,
+    .next_nodes = {
+        [0] = "mpls-drop",
+    }
+};
+VLIB_NODE_FUNCTION_MULTIARCH (mpls_mpls_label_imposition_uniform_node,
+                              mpls_mpls_label_imposition_uniform)
+
+static uword
+ip4_mpls_label_imposition_uniform (vlib_main_t * vm,
+                                   vlib_node_runtime_t * node,
+                                   vlib_frame_t * frame)
+{
+    return (mpls_label_imposition_inline(vm, node, frame,
+                                         DPO_PROTO_IP4,
+                                         MPLS_LABEL_DPO_FLAG_UNIFORM_MODE));
+}
+
+VLIB_REGISTER_NODE (ip4_mpls_label_imposition_uniform_node) = {
+    .function = ip4_mpls_label_imposition_uniform,
+    .name = "ip4-mpls-label-imposition-uniform",
+    .vector_size = sizeof (u32),
+
+    .format_trace = format_mpls_label_imposition_trace,
+    .n_next_nodes = 1,
+    .next_nodes = {
+        [0] = "ip4-drop",
+    }
+};
+VLIB_NODE_FUNCTION_MULTIARCH (ip4_mpls_label_imposition_uniform_node,
+                              ip4_mpls_label_imposition_uniform)
+
+static uword
+ip6_mpls_label_imposition_uniform (vlib_main_t * vm,
+                                   vlib_node_runtime_t * node,
+                                   vlib_frame_t * frame)
+{
+    return (mpls_label_imposition_inline(vm, node, frame,
+                                         DPO_PROTO_IP6,
+                                         MPLS_LABEL_DPO_FLAG_UNIFORM_MODE));
+}
+
+VLIB_REGISTER_NODE (ip6_mpls_label_imposition_uniform_node) = {
+    .function = ip6_mpls_label_imposition_uniform,
+    .name = "ip6-mpls-label-imposition-uniform",
+    .vector_size = sizeof (u32),
+
+    .format_trace = format_mpls_label_imposition_trace,
+    .n_next_nodes = 1,
+    .next_nodes = {
+        [0] = "ip6-drop",
+    }
+};
+VLIB_NODE_FUNCTION_MULTIARCH (ip6_mpls_label_imposition_uniform_node,
+                              ip6_mpls_label_imposition_uniform)
+
+static uword
+ethernet_mpls_label_imposition_uniform (vlib_main_t * vm,
+                                        vlib_node_runtime_t * node,
+                                        vlib_frame_t * frame)
+{
+    return (mpls_label_imposition_inline(vm, node, frame,
+                                         DPO_PROTO_ETHERNET,
+                                         MPLS_LABEL_DPO_FLAG_UNIFORM_MODE));
+}
+
+VLIB_REGISTER_NODE (ethernet_mpls_label_imposition_uniform_node) = {
+    .function = ethernet_mpls_label_imposition_uniform,
+    .name = "ethernet-mpls-label-imposition-uniform",
+    .vector_size = sizeof (u32),
+
+    .format_trace = format_mpls_label_imposition_trace,
+    .n_next_nodes = 1,
+    .next_nodes = {
+        [0] = "error-drop",
+    }
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (ethernet_mpls_label_imposition_uniform_node,
+                              ethernet_mpls_label_imposition_uniform)
+
+static uword
+ip4_mpls_label_imposition_pipe_no_ip_ttl_decr (vlib_main_t * vm,
+                                               vlib_node_runtime_t * node,
+                                               vlib_frame_t * frame)
+{
+    return (mpls_label_imposition_inline(vm, node, frame,
+                                         DPO_PROTO_IP4,
+                                         MPLS_LABEL_DPO_FLAG_NO_IP_TTL_DECR));
+}
+
+VLIB_REGISTER_NODE (ip4_mpls_label_imposition_pipe_no_ip_ttl_decr_node) = {
+    .function = ip4_mpls_label_imposition_pipe_no_ip_ttl_decr,
+    .name = "ip4-mpls-label-imposition-pipe-no-ip-ttl-decr",
+    .vector_size = sizeof (u32),
+
+    .format_trace = format_mpls_label_imposition_trace,
+    .n_next_nodes = 1,
+    .next_nodes = {
+        [0] = "ip4-drop",
+    }
+};
+VLIB_NODE_FUNCTION_MULTIARCH (ip4_mpls_label_imposition_pipe_no_ip_ttl_decr_node,
+                              ip4_mpls_label_imposition_pipe_no_ip_ttl_decr)
+
+static uword
+ip6_mpls_label_imposition_pipe_no_ip_ttl_decr (vlib_main_t * vm,
+                                               vlib_node_runtime_t * node,
+                                               vlib_frame_t * frame)
+{
+    return (mpls_label_imposition_inline(vm, node, frame,
+                                         DPO_PROTO_IP6,
+                                         MPLS_LABEL_DPO_FLAG_NO_IP_TTL_DECR));
+}
+
+VLIB_REGISTER_NODE (ip6_mpls_label_imposition_pipe_no_ip_ttl_decr_node) = {
+    .function = ip6_mpls_label_imposition_pipe_no_ip_ttl_decr,
+    .name = "ip6-mpls-label-imposition-pipe-no-ip-ttl-decr",
+    .vector_size = sizeof (u32),
+
+    .format_trace = format_mpls_label_imposition_trace,
+    .n_next_nodes = 1,
+    .next_nodes = {
+        [0] = "ip6-drop",
+    }
+};
+VLIB_NODE_FUNCTION_MULTIARCH (ip6_mpls_label_imposition_pipe_no_ip_ttl_decr_node,
+                              ip6_mpls_label_imposition_pipe_no_ip_ttl_decr)
+
+static uword
+ip4_mpls_label_imposition_uniform_no_ip_ttl_decr (vlib_main_t * vm,
+                                                  vlib_node_runtime_t * node,
+                                                  vlib_frame_t * frame)
+{
+    return (mpls_label_imposition_inline(vm, node, frame,
+                                         DPO_PROTO_IP4,
+                                         (MPLS_LABEL_DPO_FLAG_UNIFORM_MODE |
+                                          MPLS_LABEL_DPO_FLAG_NO_IP_TTL_DECR)));
+}
+
+VLIB_REGISTER_NODE (ip4_mpls_label_imposition_uniform_no_ip_ttl_decr_node) = {
+    .function = ip4_mpls_label_imposition_uniform_no_ip_ttl_decr,
+    .name = "ip4-mpls-label-imposition-uniform-no-ip-ttl-decr",
+    .vector_size = sizeof (u32),
+
+    .format_trace = format_mpls_label_imposition_trace,
+    .n_next_nodes = 1,
+    .next_nodes = {
+        [0] = "ip4-drop",
+    }
+};
+VLIB_NODE_FUNCTION_MULTIARCH (ip4_mpls_label_imposition_uniform_no_ip_ttl_decr_node,
+                              ip4_mpls_label_imposition_uniform_no_ip_ttl_decr)
+
+static uword
+ip6_mpls_label_imposition_uniform_no_ip_ttl_decr (vlib_main_t * vm,
+                                                  vlib_node_runtime_t * node,
+                                                  vlib_frame_t * frame)
+{
+    return (mpls_label_imposition_inline(vm, node, frame,
+                                         DPO_PROTO_IP6,
+                                         (MPLS_LABEL_DPO_FLAG_UNIFORM_MODE |
+                                          MPLS_LABEL_DPO_FLAG_NO_IP_TTL_DECR)));
+}
+
+VLIB_REGISTER_NODE (ip6_mpls_label_imposition_uniform_no_ip_ttl_decr_node) = {
+    .function = ip6_mpls_label_imposition_uniform_no_ip_ttl_decr,
+    .name = "ip6-mpls-label-imposition-uniform-no-ip-ttl-decr",
+    .vector_size = sizeof (u32),
+
+    .format_trace = format_mpls_label_imposition_trace,
+    .n_next_nodes = 1,
+    .next_nodes = {
+        [0] = "ip6-drop",
+    }
+};
+VLIB_NODE_FUNCTION_MULTIARCH (ip6_mpls_label_imposition_uniform_no_ip_ttl_decr_node,
+                              ip6_mpls_label_imposition_uniform_no_ip_ttl_decr)
+
 
 static void
 mpls_label_dpo_mem_show (void)
@@ -678,38 +1211,118 @@ const static dpo_vft_t mld_vft = {
     .dv_mem_show = mpls_label_dpo_mem_show,
 };
 
-const static char* const mpls_label_imp_ip4_nodes[] =
+const static char* const mpls_label_imp_pipe_ip4_nodes[] =
 {
-    "ip4-mpls-label-imposition",
+    "ip4-mpls-label-imposition-pipe",
     NULL,
 };
-const static char* const mpls_label_imp_ip6_nodes[] =
+const static char* const mpls_label_imp_pipe_ip6_nodes[] =
 {
-    "ip6-mpls-label-imposition",
+    "ip6-mpls-label-imposition-pipe",
     NULL,
 };
-const static char* const mpls_label_imp_mpls_nodes[] =
+const static char* const mpls_label_imp_pipe_mpls_nodes[] =
 {
-    "mpls-label-imposition",
+    "mpls-label-imposition-pipe",
     NULL,
 };
-const static char* const mpls_label_imp_ethernet_nodes[] =
+const static char* const mpls_label_imp_pipe_ethernet_nodes[] =
 {
-    "ethernet-mpls-label-imposition",
+    "ethernet-mpls-label-imposition-pipe",
     NULL,
 };
 
-const static char* const * const mpls_label_imp_nodes[DPO_PROTO_NUM] =
+const static char* const * const mpls_label_imp_pipe_nodes[DPO_PROTO_NUM] =
 {
-    [DPO_PROTO_IP4]  = mpls_label_imp_ip4_nodes,
-    [DPO_PROTO_IP6]  = mpls_label_imp_ip6_nodes,
-    [DPO_PROTO_MPLS] = mpls_label_imp_mpls_nodes,
-    [DPO_PROTO_ETHERNET] = mpls_label_imp_ethernet_nodes,
+    [DPO_PROTO_IP4]  = mpls_label_imp_pipe_ip4_nodes,
+    [DPO_PROTO_IP6]  = mpls_label_imp_pipe_ip6_nodes,
+    [DPO_PROTO_MPLS] = mpls_label_imp_pipe_mpls_nodes,
+    [DPO_PROTO_ETHERNET] = mpls_label_imp_pipe_ethernet_nodes,
 };
 
+const static char* const mpls_label_imp_uniform_ip4_nodes[] =
+{
+    "ip4-mpls-label-imposition-uniform",
+    NULL,
+};
+const static char* const mpls_label_imp_uniform_ip6_nodes[] =
+{
+    "ip6-mpls-label-imposition-uniform",
+    NULL,
+};
+const static char* const mpls_label_imp_uniform_mpls_nodes[] =
+{
+    "mpls-label-imposition-uniform",
+    NULL,
+};
+const static char* const mpls_label_imp_uniform_ethernet_nodes[] =
+{
+    "ethernet-mpls-label-imposition-uniform",
+    NULL,
+};
+
+const static char* const * const mpls_label_imp_uniform_nodes[DPO_PROTO_NUM] =
+{
+    [DPO_PROTO_IP4]  = mpls_label_imp_uniform_ip4_nodes,
+    [DPO_PROTO_IP6]  = mpls_label_imp_uniform_ip6_nodes,
+    [DPO_PROTO_MPLS] = mpls_label_imp_uniform_mpls_nodes,
+    [DPO_PROTO_ETHERNET] = mpls_label_imp_uniform_ethernet_nodes,
+};
+
+const static char* const mpls_label_imp_pipe_no_ip_tll_decr_ip4_nodes[] =
+{
+    "ip4-mpls-label-imposition-pipe-no-ip-ttl-decr",
+    NULL,
+};
+const static char* const mpls_label_imp_pipe_no_ip_tll_decr_ip6_nodes[] =
+{
+    "ip6-mpls-label-imposition-pipe-no-ip-ttl-decr",
+    NULL,
+};
+
+const static char* const * const mpls_label_imp_pipe_no_ip_tll_decr_nodes[DPO_PROTO_NUM] =
+{
+    [DPO_PROTO_IP4]  = mpls_label_imp_pipe_no_ip_tll_decr_ip4_nodes,
+    [DPO_PROTO_IP6]  = mpls_label_imp_pipe_no_ip_tll_decr_ip6_nodes,
+};
+
+const static char* const mpls_label_imp_uniform_no_ip_tll_decr_ip4_nodes[] =
+{
+    "ip4-mpls-label-imposition-uniform-no-ip-ttl-decr",
+    NULL,
+};
+const static char* const mpls_label_imp_uniform_no_ip_tll_decr_ip6_nodes[] =
+{
+    "ip6-mpls-label-imposition-uniform-no-ip-ttl-decr",
+    NULL,
+};
+
+const static char* const * const mpls_label_imp_uniform_no_ip_tll_decr_nodes[DPO_PROTO_NUM] =
+{
+    [DPO_PROTO_IP4]  = mpls_label_imp_uniform_no_ip_tll_decr_ip4_nodes,
+    [DPO_PROTO_IP6]  = mpls_label_imp_uniform_no_ip_tll_decr_ip6_nodes,
+};
 
 void
 mpls_label_dpo_module_init (void)
 {
-    dpo_register(DPO_MPLS_LABEL, &mld_vft, mpls_label_imp_nodes);
+    mpls_label_dpo_types[MPLS_LABEL_DPO_FLAG_NONE] =
+        dpo_register_new_type(&mld_vft,
+                              mpls_label_imp_pipe_nodes);
+    mpls_label_dpo_types[MPLS_LABEL_DPO_FLAG_NO_IP_TTL_DECR] =
+        dpo_register_new_type(&mld_vft,
+                              mpls_label_imp_pipe_no_ip_tll_decr_nodes);
+    mpls_label_dpo_types[MPLS_LABEL_DPO_FLAG_UNIFORM_MODE] =
+        dpo_register_new_type(&mld_vft,
+                              mpls_label_imp_uniform_nodes);
+    mpls_label_dpo_types[MPLS_LABEL_DPO_FLAG_UNIFORM_MODE |
+                         MPLS_LABEL_DPO_FLAG_NO_IP_TTL_DECR] =
+        dpo_register_new_type(&mld_vft,
+                              mpls_label_imp_uniform_no_ip_tll_decr_nodes);
+}
+
+dpo_type_t
+mpls_label_dpo_get_type (mpls_label_dpo_flags_t flags)
+{
+    return (mpls_label_dpo_types[flags]);
 }
index 8494d26..98c88f7 100644 (file)
 #include <vnet/mpls/packet.h>
 #include <vnet/dpo/dpo.h>
 
+/**
+ * Flags present on an MPLS label sourced path-extension
+ */
+typedef enum mpls_label_dpo_attr_t_
+{
+    /**
+     * Do not decrement the TTL of IP packet during imposition
+     */
+    MPLS_LABEL_DPO_ATTR_NO_IP_TTL_DECR,
+    MPLS_LABEL_DPO_ATTR_UNIFORM_MODE,
+} mpls_label_dpo_attr_t;
+
+#define MPLS_LABEL_DPO_ATTR_MAX (MPLS_LABEL_DPO_ATTR_UNIFORM_MODE+1)
+
+typedef enum mpls_label_dpo_flags_t_
+{
+    MPLS_LABEL_DPO_FLAG_NONE = 0,
+    MPLS_LABEL_DPO_FLAG_NO_IP_TTL_DECR = (1 << MPLS_LABEL_DPO_ATTR_NO_IP_TTL_DECR),
+    MPLS_LABEL_DPO_FLAG_UNIFORM_MODE = (1 << MPLS_LABEL_DPO_ATTR_UNIFORM_MODE),
+} __attribute__ ((packed)) mpls_label_dpo_flags_t;
+
+#define MPLS_LABEL_DPO_ATTR_NAMES {                               \
+    [MPLS_LABEL_DPO_ATTR_NO_IP_TTL_DECR] = "no-ip-tll-decr",      \
+    [MPLS_LABEL_DPO_ATTR_UNIFORM_MODE]   = "uniform-mode",        \
+}
+
+#define FOR_EACH_MPLS_LABEL_DPO_ATTR(_item)                \
+    for (_item = MPLS_LABEL_DPO_ATTR_NO_IP_TTL_DECR;       \
+         _item <= MPLS_LABEL_DPO_ATTR_UNIFORM_MODE;        \
+         _item++)
+
+/**
+ * Format the flags variable
+ */
+extern u8* format_mpls_label_dpo_flags(u8 *s, va_list *args);
 
 /**
  * Maximum number of labels in one DPO
  */
 #define MPLS_LABEL_DPO_MAX_N_LABELS 12
+
 /**
  * A representation of an MPLS label for imposition in the data-path
  */
@@ -46,10 +82,15 @@ typedef struct mpls_label_dpo_t
      */
     dpo_proto_t mld_payload_proto;
 
+    /**
+     * Flags
+     */
+    mpls_label_dpo_flags_t mld_flags;
+
     /**
      * Size of the label stack
      */
-    u16 mld_n_labels;
+    u8 mld_n_labels;
 
     /**
      * Cached amount of header bytes to paint
@@ -75,18 +116,17 @@ STATIC_ASSERT((sizeof(mpls_label_dpo_t) <= CLIB_CACHE_LINE_BYTES),
  *
  * @param label_stack The stack if labels to impose, outer most label first
  * @param eos The inner most label's EOS bit
- * @param ttl The inner most label's TTL bit
- * @param exp The inner most label's EXP bit
  * @param payload_proto The ptocool of the payload packets that will
  *                      be imposed with this label header.
- * @param dpo The parent of the created MPLS label object
+ * @param parent The parent of the created MPLS label object
+ * @param dpo The MPLS label DPO created
  */
-extern index_t mpls_label_dpo_create(mpls_label_t *label_stack,
-                                     mpls_eos_bit_t eos,
-                                     u8 ttl,
-                                     u8 exp,
-                                     dpo_proto_t payload_proto,
-                                    const dpo_id_t *dpo);
+extern void mpls_label_dpo_create(fib_mpls_label_t *label_stack,
+                                  mpls_eos_bit_t eos,
+                                  dpo_proto_t payload_proto,
+                                  mpls_label_dpo_flags_t flags,
+                                  const dpo_id_t *paremt,
+                                  dpo_id_t *dpo);
 
 extern u8* format_mpls_label_dpo(u8 *s, va_list *args);
 
@@ -104,4 +144,9 @@ mpls_label_dpo_get (index_t index)
 
 extern void mpls_label_dpo_module_init(void);
 
+/*
+ * test function to get the registered DPO type for the flags
+ */
+extern dpo_type_t mpls_label_dpo_get_type(mpls_label_dpo_flags_t flags);
+
 #endif
index bbe4eaa..d10ba00 100644 (file)
@@ -53,7 +53,7 @@ add_del_route_t_handler (u8 is_multipath,
                         u16 next_hop_weight,
                         u16 next_hop_preference,
                         mpls_label_t next_hop_via_label,
-                        mpls_label_t * next_hop_out_label_stack);
+                        fib_mpls_label_t * next_hop_out_label_stack);
 
 void
 copy_fib_next_hop (fib_route_path_encode_t * api_rpath,
index 6dc0c73..ec8c739 100644 (file)
@@ -212,6 +212,7 @@ static int
 fib_entry_src_valid_out_label (mpls_label_t label)
 {
     return ((MPLS_LABEL_IS_REAL(label) ||
+             MPLS_LABEL_POP == label ||
              MPLS_IETF_IPV4_EXPLICIT_NULL_LABEL == label ||
              MPLS_IETF_IPV6_EXPLICIT_NULL_LABEL == label ||
              MPLS_IETF_IMPLICIT_NULL_LABEL == label));
@@ -330,6 +331,7 @@ fib_entry_src_get_path_forwarding (fib_node_index_t path_index,
                                            &nh->path_dpo);
             fib_path_stack_mpls_disp(path_index,
                                      fib_prefix_get_payload_proto(&ctx->fib_entry->fe_prefix),
+                                     FIB_MPLS_LSP_MODE_PIPE,
                                      &nh->path_dpo);
 
             break;
@@ -391,7 +393,7 @@ fib_entry_src_collect_forwarding (fib_node_index_t pl_index,
         switch (path_ext->fpe_type)
         {
         case FIB_PATH_EXT_MPLS:
-            if (fib_entry_src_valid_out_label(path_ext->fpe_label_stack[0]))
+            if (fib_entry_src_valid_out_label(path_ext->fpe_label_stack[0].fml_value))
             {
                 /*
                  * found a matching extension. stack it to obtain the forwarding
index b4f9971..a859390 100644 (file)
@@ -1349,7 +1349,8 @@ fib_path_create (fib_node_index_t pl_index,
            {
                path->fp_type = FIB_PATH_TYPE_DEAG;
                path->deag.fp_tbl_id = rpath->frp_fib_index;
-           }           
+                path->deag.fp_rpf_id = ~0;
+           }
        }
        else
        {
@@ -2238,6 +2239,7 @@ fib_path_contribute_urpf (fib_node_index_t path_index,
 void
 fib_path_stack_mpls_disp (fib_node_index_t path_index,
                           dpo_proto_t payload_proto,
+                          fib_mpls_lsp_mode_t mode,
                           dpo_id_t *dpo)
 {
     fib_path_t *path;
@@ -2253,10 +2255,8 @@ fib_path_stack_mpls_disp (fib_node_index_t path_index,
         dpo_id_t tmp = DPO_INVALID;
 
         dpo_copy(&tmp, dpo);
-        dpo_set(dpo,
-                DPO_MPLS_DISPOSITION,
-                payload_proto,
-                mpls_disp_dpo_create(payload_proto, ~0, &tmp));
+
+        mpls_disp_dpo_create(payload_proto, ~0, mode, &tmp, dpo);
         dpo_reset(&tmp);
         break;
     }                
@@ -2265,12 +2265,10 @@ fib_path_stack_mpls_disp (fib_node_index_t path_index,
         dpo_id_t tmp = DPO_INVALID;
 
         dpo_copy(&tmp, dpo);
-        dpo_set(dpo,
-                DPO_MPLS_DISPOSITION,
-                payload_proto,
-                mpls_disp_dpo_create(payload_proto,
-                                     path->deag.fp_rpf_id,
-                                     &tmp));
+
+        mpls_disp_dpo_create(payload_proto,
+                             path->deag.fp_rpf_id,
+                             mode, &tmp, dpo);
         dpo_reset(&tmp);
         break;
     }
index 70b2f50..28ec10a 100644 (file)
@@ -159,6 +159,7 @@ extern load_balance_path_t * fib_path_append_nh_for_multipath_hash(
     load_balance_path_t *hash_key);
 extern void fib_path_stack_mpls_disp(fib_node_index_t path_index,
                                      dpo_proto_t payload_proto,
+                                     fib_mpls_lsp_mode_t mode,
                                      dpo_id_t *dpo);
 extern void fib_path_contribute_forwarding(fib_node_index_t path_index,
                                           fib_forward_chain_type_t type,
index a285ba0..6b5b841 100644 (file)
@@ -25,6 +25,7 @@
 #include <vnet/fib/fib_internal.h>
 
 const char *fib_path_ext_adj_flags_names[] = FIB_PATH_EXT_ADJ_ATTR_NAMES;
+const char *fib_path_ext_mpls_flags_names[] = FIB_PATH_EXT_MPLS_ATTR_NAMES;
 
 u8 *
 format_fib_path_ext (u8 * s, va_list * args)
@@ -38,30 +39,46 @@ format_fib_path_ext (u8 * s, va_list * args)
 
     switch (path_ext->fpe_type)
     {
-    case FIB_PATH_EXT_MPLS:
-        s = format(s, "labels:",
+    case FIB_PATH_EXT_MPLS: {
+        fib_path_ext_mpls_attr_t attr;
+
+        if (path_ext->fpe_mpls_flags)
+        {
+            s = format(s, "mpls-flags:[");
+
+            FOR_EACH_PATH_EXT_MPLS_ATTR(attr)
+            {
+                if ((1<<attr) & path_ext->fpe_mpls_flags) {
+                    s = format(s, "%s", fib_path_ext_mpls_flags_names[attr]);
+                }
+            }
+            s = format(s, "]");
+        }
+        s = format(s, " labels:[",
                    path_ext->fpe_path_index);
         for (ii = 0; ii < vec_len(path_ext->fpe_path.frp_label_stack); ii++)
         {
-            s = format(s, "%U ",
-                       format_mpls_unicast_label,
-                       path_ext->fpe_path.frp_label_stack[ii]);
+            s = format(s, "[%U]",
+                       format_fib_mpls_label,
+                       &path_ext->fpe_path.frp_label_stack[ii]);
         }
+        s = format(s, "]");
         break;
+    }
     case FIB_PATH_EXT_ADJ: {
         fib_path_ext_adj_attr_t attr;
 
-        s = format(s, "adj-flags:");
         if (path_ext->fpe_adj_flags)
         {
+            s = format(s, "adj-flags:[");
             FOR_EACH_PATH_EXT_ADJ_ATTR(attr)
             {
-                s = format(s, "%s", fib_path_ext_adj_flags_names[attr]);
+                if ((1<<attr) & path_ext->fpe_adj_flags)
+                {
+                    s = format(s, "%s", fib_path_ext_adj_flags_names[attr]);
+                }
             }
-        }
-        else
-        {
-            s = format(s, "None");
+            s = format(s, "]");
         }
         break;
     }
@@ -121,12 +138,28 @@ fib_path_ext_init (fib_path_ext_t *path_ext,
 
 /**
  * @brief Return true if the label stack is implicit null
+ * imp-null and pop equate to the same this as this level -
+ * the label is coming off.
  */
 static int
 fib_path_ext_is_imp_null (fib_path_ext_t *path_ext)
 {
     return ((1 == vec_len(path_ext->fpe_label_stack)) &&
-           (MPLS_IETF_IMPLICIT_NULL_LABEL == path_ext->fpe_label_stack[0]));
+           ((MPLS_IETF_IMPLICIT_NULL_LABEL == path_ext->fpe_label_stack[0].fml_value) ||
+             (MPLS_LABEL_POP == path_ext->fpe_label_stack[0].fml_value)));
+}
+
+mpls_label_dpo_flags_t
+fib_path_ext_mpls_flags_to_mpls_label (fib_path_ext_mpls_flags_t fpe_flags)
+{
+    mpls_label_dpo_flags_t ml_flags = MPLS_LABEL_DPO_FLAG_NONE;
+
+    if (fpe_flags &FIB_PATH_EXT_MPLS_FLAG_NO_IP_TTL_DECR)
+    {
+        ml_flags |= MPLS_LABEL_DPO_FLAG_NO_IP_TTL_DECR;
+    }
+
+    return (ml_flags);
 }
 
 load_balance_path_t *
@@ -236,24 +269,25 @@ fib_path_ext_stack (fib_path_ext_t *path_ext,
              * we pickup the correct MPLS imposition nodes to do
              * ip[46] processing.
              */
+            dpo_id_t parent = DPO_INVALID;
             dpo_proto_t chain_proto;
             mpls_eos_bit_t eos;
-            index_t mldi;
 
             eos = (child_fct == FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS ?
                    MPLS_NON_EOS :
                    MPLS_EOS);
             chain_proto = fib_forw_chain_type_to_dpo_proto(child_fct);
 
-            mldi = mpls_label_dpo_create(path_ext->fpe_label_stack,
-                                         eos, 255, 0,
-                                         chain_proto,
-                                         &nh->path_dpo);
+            dpo_copy(&parent, &nh->path_dpo);
+            mpls_label_dpo_create(path_ext->fpe_label_stack,
+                                  eos,
+                                  chain_proto,
+                                  fib_path_ext_mpls_flags_to_mpls_label(
+                                      path_ext->fpe_mpls_flags),
+                                  &parent,
+                                  &nh->path_dpo);
 
-           dpo_set(&nh->path_dpo,
-                   DPO_MPLS_LABEL,
-                    chain_proto,
-                    mldi);
+           dpo_reset(&parent);
        }
         else if (child_fct == FIB_FORW_CHAIN_TYPE_MPLS_EOS)
         {
@@ -262,6 +296,7 @@ fib_path_ext_stack (fib_path_ext_t *path_ext,
              */
             fib_path_stack_mpls_disp(nh->path_index,
                                      fib_forw_chain_type_to_dpo_proto(parent_fct),
+                                     path_ext->fpe_label_stack[0].fml_mode,
                                      &nh->path_dpo);
         }
     }
index d07941c..b49fd97 100644 (file)
@@ -59,6 +59,32 @@ typedef enum fib_path_ext_adj_flags_t_
          _item <= FIB_PATH_EXT_ADJ_ATTR_REFINES_COVER;  \
          _item++)
 
+/**
+ * Flags present on an MPLS label sourced path-extension
+ */
+typedef enum fib_path_ext_mpls_attr_t_
+{
+    /**
+     * Do not decrement the TTL of IP packet during imposition
+     */
+    FIB_PATH_EXT_MPLS_ATTR_NO_IP_TTL_DECR,
+} fib_path_ext_mpls_attr_t;
+
+typedef enum fib_path_ext_mpls_flags_t_
+{
+    FIB_PATH_EXT_MPLS_FLAG_NONE = 0,
+    FIB_PATH_EXT_MPLS_FLAG_NO_IP_TTL_DECR = (1 << FIB_PATH_EXT_MPLS_ATTR_NO_IP_TTL_DECR),
+} fib_path_ext_mpls_flags_t;
+
+#define FIB_PATH_EXT_MPLS_ATTR_NAMES {                               \
+    [FIB_PATH_EXT_MPLS_ATTR_NO_IP_TTL_DECR] = "no-ip-tll-decr",      \
+}
+
+#define FOR_EACH_PATH_EXT_MPLS_ATTR(_item)                \
+    for (_item = FIB_PATH_EXT_MPLS_ATTR_NO_IP_TTL_DECR;   \
+         _item <= FIB_PATH_EXT_MPLS_ATTR_NO_IP_TTL_DECR;  \
+         _item++)
+
 /**
  * A path extension is a per-entry addition to the forwarding information
  * when packets are sent for that entry over that path.
@@ -86,6 +112,12 @@ typedef struct fib_path_ext_t_
          * Flags describing the adj state
          */
         fib_path_ext_adj_flags_t fpe_adj_flags;
+        /**
+         * For an MPLS type extension
+         *
+         * Flags describing the mpls state
+         */
+        fib_path_ext_mpls_flags_t fpe_mpls_flags;
     };
 
     /**
@@ -98,7 +130,7 @@ typedef struct fib_path_ext_t_
      * position in the path-list.
      */
     fib_node_index_t fpe_path_index;
-} __attribute__ ((packed))  fib_path_ext_t;
+} __attribute__ ((packed)) fib_path_ext_t;
 
 extern u8 * format_fib_path_ext(u8 * s, va_list * args);
 
index e917348..324a35f 100644 (file)
@@ -520,7 +520,7 @@ fib_table_entry_path_add (u32 fib_index,
                          u32 next_hop_sw_if_index,
                          u32 next_hop_fib_index,
                          u32 next_hop_weight,
-                         mpls_label_t *next_hop_labels,
+                         fib_mpls_label_t *next_hop_labels,
                          fib_route_path_flags_t path_flags)
 {
     fib_route_path_t path = {
@@ -770,7 +770,7 @@ fib_table_entry_update_one_path (u32 fib_index,
                                 u32 next_hop_sw_if_index,
                                 u32 next_hop_fib_index,
                                 u32 next_hop_weight,
-                                mpls_label_t *next_hop_labels,
+                                fib_mpls_label_t *next_hop_labels,
                                 fib_route_path_flags_t path_flags)
 {
     fib_node_index_t fib_entry_index;
index ddc00e5..ffad3c4 100644 (file)
@@ -338,7 +338,7 @@ extern fib_node_index_t fib_table_entry_path_add(u32 fib_index,
                                                 u32 next_hop_sw_if_index,
                                                 u32 next_hop_fib_index,
                                                 u32 next_hop_weight,
-                                                mpls_label_t *next_hop_label_stack,
+                                                fib_mpls_label_t *next_hop_label_stack,
                                                 fib_route_path_flags_t pf);
 /**
  * @brief
@@ -521,7 +521,7 @@ extern fib_node_index_t fib_table_entry_update_one_path(u32 fib_index,
                                                        u32 next_hop_sw_if_index,
                                                        u32 next_hop_fib_index,
                                                        u32 next_hop_weight,
-                                                       mpls_label_t *next_hop_label_stack,
+                                                       fib_mpls_label_t *next_hop_label_stack,
                                                        fib_route_path_flags_t pf);
 
 /**
index 2a30b3c..8f7bba0 100644 (file)
@@ -301,7 +301,8 @@ fib_test_validate_rep_v (const replicate_t *rep,
            {
                const mpls_label_dpo_t *mld;
                 mpls_label_t hdr;
-               FIB_TEST_LB((DPO_MPLS_LABEL == dpo->dpoi_type),
+               FIB_TEST_LB((mpls_label_dpo_get_type(MPLS_LABEL_DPO_FLAG_NONE)
+                             == dpo->dpoi_type),
                             "bucket %d stacks on %U",
                             bucket,
                             format_dpo_type, dpo->dpoi_type);
@@ -375,14 +376,19 @@ fib_test_validate_lb_v (const load_balance_t *lb,
        case FT_LB_LABEL_STACK_O_ADJ:
            {
                const mpls_label_dpo_t *mld;
+                mpls_label_dpo_flags_t mf;
                 mpls_label_t hdr;
                u32 ii;
 
-               FIB_TEST_LB((DPO_MPLS_LABEL == dpo->dpoi_type),
+                mf = ((exp->label_stack_o_adj.mode ==
+                       FIB_MPLS_LSP_MODE_UNIFORM) ?
+                      MPLS_LABEL_DPO_FLAG_UNIFORM_MODE :
+                      MPLS_LABEL_DPO_FLAG_NONE);
+               FIB_TEST_LB((mpls_label_dpo_get_type(mf) == dpo->dpoi_type),
                           "bucket %d stacks on %U",
                           bucket,
                           format_dpo_type, dpo->dpoi_type);
-           
+
                mld = mpls_label_dpo_get(dpo->dpoi_index);
 
                FIB_TEST_LB(exp->label_stack_o_adj.label_stack_size == mld->mld_n_labels,
@@ -433,7 +439,8 @@ fib_test_validate_lb_v (const load_balance_t *lb,
            {
                const mpls_label_dpo_t *mld;
                 mpls_label_t hdr;
-               FIB_TEST_LB((DPO_MPLS_LABEL == dpo->dpoi_type),
+               FIB_TEST_LB((mpls_label_dpo_get_type(MPLS_LABEL_DPO_FLAG_NONE)
+                             == dpo->dpoi_type),
                           "bucket %d stacks on %U",
                           bucket,
                           format_dpo_type, dpo->dpoi_type);
@@ -468,13 +475,18 @@ fib_test_validate_lb_v (const load_balance_t *lb,
        case FT_LB_LABEL_O_LB:
            {
                const mpls_label_dpo_t *mld;
+                mpls_label_dpo_flags_t mf;
                 mpls_label_t hdr;
 
-               FIB_TEST_LB((DPO_MPLS_LABEL == dpo->dpoi_type),
+                mf = ((exp->label_o_lb.mode ==
+                       FIB_MPLS_LSP_MODE_UNIFORM) ?
+                      MPLS_LABEL_DPO_FLAG_UNIFORM_MODE :
+                      MPLS_LABEL_DPO_FLAG_NONE);
+               FIB_TEST_LB((mpls_label_dpo_get_type(mf) == dpo->dpoi_type),
                           "bucket %d stacks on %U",
                           bucket,
                           format_dpo_type, dpo->dpoi_type);
-           
+
                mld = mpls_label_dpo_get(dpo->dpoi_index);
                 hdr = clib_net_to_host_u32(mld->mld_hdr[0].label_exp_s_ttl);
 
@@ -515,15 +527,15 @@ fib_test_validate_lb_v (const load_balance_t *lb,
                        bucket,
                        exp->adj.adj);
            break;
-       case FT_LB_MPLS_DISP_O_ADJ:
+       case FT_LB_MPLS_DISP_PIPE_O_ADJ:
         {
             const mpls_disp_dpo_t *mdd;
 
-            FIB_TEST_I((DPO_MPLS_DISPOSITION == dpo->dpoi_type),
+            FIB_TEST_I((DPO_MPLS_DISPOSITION_PIPE == dpo->dpoi_type),
                       "bucket %d stacks on %U",
                       bucket,
                       format_dpo_type, dpo->dpoi_type);
-           
+
             mdd = mpls_disp_dpo_get(dpo->dpoi_index);
 
             dpo = &mdd->mdd_dpo;
@@ -6332,8 +6344,10 @@ fib_test_label (void)
            .eos = MPLS_NON_EOS,
        },
     };
-    mpls_label_t *l99 = NULL;
-    vec_add1(l99, 99);
+    fib_mpls_label_t *l99 = NULL, fml99 = {
+        .fml_value = 99,
+    };
+    vec_add1(l99, fml99);
 
     fib_table_entry_update_one_path(fib_index,
                                    &pfx_1_1_1_1_s_32,
@@ -6371,8 +6385,10 @@ fib_test_label (void)
            .adj = ai_mpls_10_10_11_1,
        },
     };
-    mpls_label_t *l_imp_null = NULL;
-    vec_add1(l_imp_null, MPLS_IETF_IMPLICIT_NULL_LABEL);
+    fib_mpls_label_t *l_imp_null = NULL, fml_imp_null = {
+        .fml_value =  MPLS_IETF_IMPLICIT_NULL_LABEL,
+    };
+    vec_add1(l_imp_null, fml_imp_null);
 
     fei = fib_table_entry_path_add(fib_index,
                                   &pfx_1_1_1_1_s_32,
@@ -6413,7 +6429,7 @@ fib_test_label (void)
        .fp_eos = MPLS_NON_EOS,
     };
     fib_test_lb_bucket_t disp_o_10_10_11_1 = {
-       .type = FT_LB_MPLS_DISP_O_ADJ,
+       .type = FT_LB_MPLS_DISP_PIPE_O_ADJ,
        .adj = {
            .adj = ai_v4_10_10_11_1,
        },
@@ -6458,7 +6474,7 @@ fib_test_label (void)
        },
     };
     fib_test_lb_bucket_t disp_o_10_10_11_2 = {
-       .type = FT_LB_MPLS_DISP_O_ADJ,
+       .type = FT_LB_MPLS_DISP_PIPE_O_ADJ,
        .adj = {
            .adj = ai_v4_10_10_11_2,
        },
@@ -6540,24 +6556,27 @@ fib_test_label (void)
            .lb = non_eos_1_1_1_1.dpoi_index,
            .label = 1600,
            .eos = MPLS_EOS,
+            .mode = FIB_MPLS_LSP_MODE_UNIFORM,
        },
     };
-    mpls_label_t *l1600 = NULL;
-    vec_add1(l1600, 1600);
+    fib_mpls_label_t *l1600 = NULL, fml1600 = {
+        .fml_value = 1600,
+        .fml_mode = FIB_MPLS_LSP_MODE_UNIFORM,
+    };
+    vec_add1(l1600, fml1600);
 
-    fib_table_entry_update_one_path(fib_index,
-                                   &pfx_2_2_2_2_s_32,
-                                   FIB_SOURCE_API,
-                                   FIB_ENTRY_FLAG_NONE,
-                                   DPO_PROTO_IP4,
-                                   &pfx_1_1_1_1_s_32.fp_addr,
-                                   ~0,
-                                   fib_index,
-                                   1,
-                                   l1600,
-                                   FIB_ROUTE_PATH_FLAG_NONE);
+    fei = fib_table_entry_update_one_path(fib_index,
+                                          &pfx_2_2_2_2_s_32,
+                                          FIB_SOURCE_API,
+                                          FIB_ENTRY_FLAG_NONE,
+                                          DPO_PROTO_IP4,
+                                          &pfx_1_1_1_1_s_32.fp_addr,
+                                          ~0,
+                                          fib_index,
+                                          1,
+                                          l1600,
+                                          FIB_ROUTE_PATH_FLAG_NONE);
 
-    fei = fib_table_lookup(fib_index, &pfx_2_2_2_2_s_32);
     FIB_TEST(fib_test_validate_entry(fei, 
                                     FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
                                     1,
@@ -6811,7 +6830,7 @@ fib_test_label (void)
      * add back the path with the valid label
      */
     l99 = NULL;
-    vec_add1(l99, 99);
+    vec_add1(l99, fml99);
 
     fib_table_entry_path_add(fib_index,
                             &pfx_1_1_1_1_s_32,
@@ -6941,8 +6960,10 @@ fib_test_label (void)
            .eos = MPLS_EOS,
        },
     };
-    mpls_label_t *l101 = NULL;
-    vec_add1(l101, 101);
+    fib_mpls_label_t *l101 = NULL, fml101 = {
+        .fml_value = 101,
+    };
+    vec_add1(l101, fml101);
 
     fei = fib_table_entry_update_one_path(fib_index,
                                          &pfx_1_1_1_2_s_32,
@@ -6981,8 +7002,10 @@ fib_test_label (void)
            .eos = MPLS_EOS,
        },
     };
-    mpls_label_t *l1601 = NULL;
-    vec_add1(l1601, 1601);
+    fib_mpls_label_t *l1601 = NULL, fml1601 = {
+        .fml_value = 1601,
+    };
+    vec_add1(l1601, fml1601);
 
     l1600_eos_o_1_1_1_1.label_o_lb.lb = non_eos_1_1_1_1.dpoi_index;
 
@@ -7012,7 +7035,7 @@ fib_test_label (void)
      * the LB for the recursive can use an imp-null
      */
     l_imp_null = NULL;
-    vec_add1(l_imp_null, MPLS_IETF_IMPLICIT_NULL_LABEL);
+    vec_add1(l_imp_null, fml_imp_null);
 
     fei = fib_table_entry_update_one_path(fib_index,
                                          &pfx_1_1_1_2_s_32,
@@ -7176,11 +7199,11 @@ fib_test_label (void)
            .eos = MPLS_EOS,
        },
     };
-    mpls_label_t *label_stack = NULL;
+    fib_mpls_label_t *label_stack = NULL;
     vec_validate(label_stack, 7);
     for (ii = 0; ii < 8; ii++)
     {
-       label_stack[ii] = ii + 200;
+       label_stack[ii].fml_value = ii + 200;
     }
 
     fei = fib_table_entry_update_one_path(fib_index,
@@ -8255,12 +8278,13 @@ static int
 lfib_test (void)
 {
     const mpls_label_t deag_label = 50;
+    dpo_id_t dpo = DPO_INVALID;
+    const mpls_disp_dpo_t *mdd;
     const u32 lfib_index = 0;
     const u32 fib_index = 0;
-    dpo_id_t dpo = DPO_INVALID;
+    const lookup_dpo_t *lkd;
     const dpo_id_t *dpo1;
     fib_node_index_t lfe;
-    lookup_dpo_t *lkd;
     test_main_t *tm;
     int lb_count;
     adj_index_t ai_mpls_10_10_10_1;
@@ -8327,7 +8351,6 @@ lfib_test (void)
              format_mpls_eos_bit, MPLS_EOS,
              format_dpo_proto, lkd->lkd_proto);
 
-
     /*
      * A route deag route for EOS
      */
@@ -8358,7 +8381,14 @@ lfib_test (void)
                                    FIB_FORW_CHAIN_TYPE_MPLS_EOS,
                                    &dpo);
     dpo1 = load_balance_get_bucket(dpo.dpoi_index, 0);
-    lkd = lookup_dpo_get(dpo1->dpoi_index);
+    mdd = mpls_disp_dpo_get(dpo1->dpoi_index);
+
+    FIB_TEST((FIB_MPLS_LSP_MODE_PIPE == mdd->mdd_mode),
+             "%U/%U disp is pipe mode",
+             format_mpls_unicast_label, deag_label,
+             format_mpls_eos_bit, MPLS_EOS);
+
+    lkd = lookup_dpo_get(mdd->mdd_dpo.dpoi_index);
 
     FIB_TEST((fib_index == lkd->lkd_fib_index),
               "%U/%U is deag in %d %U",
@@ -8383,6 +8413,70 @@ lfib_test (void)
               "%U/%U not present",
               format_mpls_unicast_label, deag_label,
               format_mpls_eos_bit, MPLS_EOS);
+    dpo_reset(&dpo);
+
+    /*
+     * A route deag route for EOS with LSP mode uniform
+     */
+    fib_mpls_label_t *l_pops = NULL, l_pop = {
+        .fml_value = MPLS_LABEL_POP,
+        .fml_mode = FIB_MPLS_LSP_MODE_UNIFORM,
+    };
+    vec_add1(l_pops, l_pop);
+    lfe = fib_table_entry_path_add(lfib_index,
+                                  &pfx,
+                                  FIB_SOURCE_CLI,
+                                  FIB_ENTRY_FLAG_NONE,
+                                  DPO_PROTO_IP4,
+                                  &zero_addr,
+                                  ~0,
+                                  fib_index,
+                                  1,
+                                  l_pops,
+                                  FIB_ROUTE_PATH_FLAG_NONE);
+
+    FIB_TEST((lfe == fib_table_lookup(lfib_index, &pfx)),
+              "%U/%U present",
+              format_mpls_unicast_label, deag_label,
+              format_mpls_eos_bit, MPLS_EOS);
+
+    fib_entry_contribute_forwarding(lfe,
+                                   FIB_FORW_CHAIN_TYPE_MPLS_EOS,
+                                   &dpo);
+    dpo1 = load_balance_get_bucket(dpo.dpoi_index, 0);
+    mdd = mpls_disp_dpo_get(dpo1->dpoi_index);
+
+    FIB_TEST((FIB_MPLS_LSP_MODE_UNIFORM == mdd->mdd_mode),
+             "%U/%U disp is uniform mode",
+             format_mpls_unicast_label, deag_label,
+             format_mpls_eos_bit, MPLS_EOS);
+
+    lkd = lookup_dpo_get(mdd->mdd_dpo.dpoi_index);
+
+    FIB_TEST((fib_index == lkd->lkd_fib_index),
+              "%U/%U is deag in %d %U",
+             format_mpls_unicast_label, deag_label,
+             format_mpls_eos_bit, MPLS_EOS,
+             lkd->lkd_fib_index,
+             format_dpo_id, &dpo, 0);
+    FIB_TEST((LOOKUP_INPUT_DST_ADDR == lkd->lkd_input),
+             "%U/%U is dst deag",
+             format_mpls_unicast_label, deag_label,
+             format_mpls_eos_bit, MPLS_EOS);
+    FIB_TEST((DPO_PROTO_IP4 == lkd->lkd_proto),
+             "%U/%U is %U dst deag",
+             format_mpls_unicast_label, deag_label,
+             format_mpls_eos_bit, MPLS_EOS,
+             format_dpo_proto, lkd->lkd_proto);
+
+    fib_table_entry_delete_index(lfe, FIB_SOURCE_CLI);
+
+    FIB_TEST((FIB_NODE_INDEX_INVALID == fib_table_lookup(lfib_index,
+                                                        &pfx)),
+              "%U/%U not present",
+              format_mpls_unicast_label, deag_label,
+              format_mpls_eos_bit, MPLS_EOS);
+    dpo_reset(&dpo);
 
     /*
      * A route deag route for non-EOS
@@ -8460,11 +8554,15 @@ lfib_test (void)
     };
     dpo_id_t neos_1200 = DPO_INVALID;
     dpo_id_t ip_1200 = DPO_INVALID;
-    mpls_label_t *l200 = NULL;
-    vec_add1(l200, 200);
-    vec_add1(l200, 300);
-    vec_add1(l200, 400);
-    vec_add1(l200, 500);
+    fib_mpls_label_t *l200 = NULL;
+    u32 ii;
+    for (ii = 0; ii < 4; ii++)
+    {
+        fib_mpls_label_t fml = {
+            .fml_value = 200 + (ii * 100),
+        };
+        vec_add1(l200, fml);
+    };
 
     lfe = fib_table_entry_update_one_path(fib_index,
                                          &pfx_1200,
@@ -8523,8 +8621,10 @@ lfib_test (void)
            .ip4.as_u32 = clib_host_to_net_u32(0x02020204),
        },
     };
-    mpls_label_t *l999 = NULL;
-    vec_add1(l999, 999);
+    fib_mpls_label_t *l999 = NULL, fml_999 = {
+        .fml_value = 999,
+    };
+    vec_add1(l999, fml_999);
     rpaths[0].frp_label_stack = l999,
 
     fib_table_entry_path_add2(fib_index,
@@ -8690,8 +8790,10 @@ lfib_test (void)
             .adj = idpo.dpoi_index,
         },
     };
-    mpls_label_t *l3300 = NULL;
-    vec_add1(l3300, 3300);
+    fib_mpls_label_t *l3300 = NULL, fml_3300 = {
+        .fml_value = 3300,
+    };
+    vec_add1(l3300, fml_3300);
 
     lfe = fib_table_entry_update_one_path(lfib_index,
                                          &pfx_3500,
@@ -8772,6 +8874,9 @@ lfib_test (void)
                                      0, 1);
     mpls_table_delete(MPLS_FIB_DEFAULT_TABLE_ID, FIB_SOURCE_API);
 
+    FIB_TEST(0 == pool_elts(mpls_disp_dpo_pool),
+            "mpls_disp_dpo resources freed %d of %d",
+             0, pool_elts(mpls_disp_dpo_pool));
     FIB_TEST(lb_count == pool_elts(load_balance_pool),
             "Load-balance resources freed %d of %d",
              lb_count, pool_elts(load_balance_pool));
index 0309b3f..8ac0683 100644 (file)
@@ -29,7 +29,7 @@ typedef enum fib_test_lb_bucket_type_t_ {
     FT_LB_LABEL_STACK_O_ADJ,
     FT_LB_LABEL_O_LB,
     FT_LB_O_LB,
-    FT_LB_MPLS_DISP_O_ADJ,
+    FT_LB_MPLS_DISP_PIPE_O_ADJ,
     FT_LB_INTF,
     FT_LB_L2,
     FT_LB_BIER_TABLE,
@@ -47,6 +47,7 @@ typedef struct fib_test_lb_bucket_t_ {
        {
            mpls_eos_bit_t eos;
            mpls_label_t label;
+            fib_mpls_lsp_mode_t mode;
            u8 ttl;
            adj_index_t adj;
        } label_o_adj;
@@ -54,6 +55,7 @@ typedef struct fib_test_lb_bucket_t_ {
        {
            mpls_eos_bit_t eos;
            mpls_label_t label_stack[8];
+            fib_mpls_lsp_mode_t mode;
            u8 label_stack_size;
            u8 ttl;
            adj_index_t adj;
@@ -62,6 +64,7 @@ typedef struct fib_test_lb_bucket_t_ {
        {
            mpls_eos_bit_t eos;
            mpls_label_t label;
+            fib_mpls_lsp_mode_t mode;
            u8 ttl;
            index_t lb;
        } label_o_lb;
index 61a0898..fde2c33 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+/** \brief MPLS label
+*/
+typeonly define fib_mpls_label
+{
+  u8 is_uniform;
+  u32 label;
+  u8 ttl;
+  u8 exp;
+};
 
 /** \brief FIB path
     @param sw_if_index - index of the interface
@@ -20,6 +30,8 @@
     is prefered
     @param is_local - local if non-zero, else remote
     @param is_drop - Drop the packet
+    @param is_unreach - Drop the packet and rate limit send ICMP unreachable
+    @param is_prohibit - Drop the packet and rate limit send ICMP prohibited
     @param is_udp_encap - The path describes a UDP-o-IP encapsulation.
     @param afi - the afi of the next hop, IP46_TYPE_IP4=1, IP46_TYPE_IP6=2
     @param next_hop[16] - the next hop address
@@ -27,7 +39,7 @@
                          that has a unique identifier. e.g. the UDP
                          encap object
 */
-typeonly define fib_path3
+typeonly define fib_path
 {
   u32 sw_if_index;
   u32 table_id;
@@ -36,10 +48,12 @@ typeonly define fib_path3
   u8 is_local;
   u8 is_drop;
   u8 is_udp_encap;
+  u8 is_unreach;
+  u8 is_prohibit;
   u8 afi;
   u8 next_hop[16];
   u32 next_hop_id;
   u32 rpf_id;
   u8 n_labels;
-  u32 label_stack[16];
+  vl_api_fib_mpls_label_t label_stack[16];
 };
index f38c815..8b1faf5 100644 (file)
@@ -25,6 +25,7 @@
 static const char* fib_protocol_names[] = FIB_PROTOCOLS;
 static const char* vnet_link_names[] = VNET_LINKS;
 static const char* fib_forw_chain_names[] = FIB_FORW_CHAINS;
+static const char* fib_mpls_lsp_mode_names[] = FIB_MPLS_LSP_MODES;
 
 u8 *
 format_fib_protocol (u8 * s, va_list * ap)
@@ -50,6 +51,30 @@ format_fib_forw_chain_type (u8 * s, va_list * args)
     return (format (s, "%s", fib_forw_chain_names[fct]));
 }
 
+u8 *
+format_fib_mpls_lsp_mode(u8 *s, va_list *ap)
+{
+    fib_mpls_lsp_mode_t mode = va_arg(*ap, int);
+
+    return (format (s, "%s", fib_mpls_lsp_mode_names[mode])); 
+}
+
+u8 *
+format_fib_mpls_label (u8 *s, va_list *ap)
+{
+    fib_mpls_label_t *label = va_arg(*ap, fib_mpls_label_t *);
+
+    s = format(s, "%U %U ttl:%d exp:%d",
+               format_mpls_unicast_label,
+               label->fml_value,
+               format_fib_mpls_lsp_mode,
+               label->fml_mode,
+               label->fml_ttl,
+               label->fml_exp);
+
+    return (s);
+}
+
 void
 fib_prefix_from_ip46_addr (const ip46_address_t *addr,
                           fib_prefix_t *pfx)
@@ -307,6 +332,29 @@ fib_forw_chain_type_to_link_type (fib_forward_chain_type_t fct)
     return (VNET_LINK_IP4);
 }
 
+fib_forward_chain_type_t
+fib_forw_chain_type_from_link_type (vnet_link_t link_type)
+{
+    switch (link_type)
+    {
+    case VNET_LINK_IP4:
+        return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4);
+    case VNET_LINK_IP6:
+        return (FIB_FORW_CHAIN_TYPE_UNICAST_IP6);
+    case VNET_LINK_MPLS:
+        return (FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS);
+    case VNET_LINK_ETHERNET:
+        return (FIB_FORW_CHAIN_TYPE_ETHERNET);
+    case VNET_LINK_NSH:
+        return (FIB_FORW_CHAIN_TYPE_NSH);
+    case VNET_LINK_ARP:
+        break;
+    }
+
+    ASSERT(0);
+    return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4);
+}
+
 dpo_proto_t
 fib_forw_chain_type_to_dpo_proto (fib_forward_chain_type_t fct)
 {
@@ -475,7 +523,10 @@ unformat_fib_route_path (unformat_input_t * input, va_list * args)
             while (unformat (input, "%U",
                              unformat_mpls_unicast_label, &out_label))
             {
-                vec_add1(rpath->frp_label_stack, out_label);
+                fib_mpls_label_t fml = {
+                    .fml_value = out_label,
+                };
+                vec_add1(rpath->frp_label_stack, fml);
             }
         }
         else if (unformat (input, "%U",
index 75ed779..7f186ac 100644 (file)
@@ -163,10 +163,15 @@ typedef enum fib_forward_chain_type_t_ {
         _item++)
 
 /**
- * @brief Convert from a chain type to the adjacencies link type
+ * @brief Convert from a chain type to the adjacency's link type
  */
 extern vnet_link_t fib_forw_chain_type_to_link_type(fib_forward_chain_type_t fct);
 
+/**
+ * @brief Convert from a adjacency's link type to chain type
+ */
+extern fib_forward_chain_type_t fib_forw_chain_type_from_link_type(vnet_link_t lt);
+
 /**
  * @brief Convert from a payload-protocol to a chain type.
  */
@@ -372,6 +377,64 @@ typedef u32 fib_rpf_id_t;
 
 #define MFIB_RPF_ID_NONE (0)
 
+/**
+ * MPLS LSP mode - only valid at the head and tail
+ */
+typedef enum fib_mpls_lsp_mode_t_
+{
+    /**
+     * Pipe Mode - the default.
+     *  TTL and DSCP markings are not carried between the layers
+     */
+    FIB_MPLS_LSP_MODE_PIPE,
+    /**
+     * Uniform mode.
+     *  TTL and DSCP are copied between the layers
+     */
+    FIB_MPLS_LSP_MODE_UNIFORM,
+} __attribute__((packed)) fib_mpls_lsp_mode_t;
+
+#define FIB_MPLS_LSP_MODES {                   \
+    [FIB_MPLS_LSP_MODE_PIPE]     = "pipe",             \
+    [FIB_MPLS_LSP_MODE_UNIFORM]  = "uniform",   \
+}
+
+/**
+ * Format an LSP mode type
+ */
+extern u8 * format_fib_mpls_lsp_mode(u8 *s, va_list *ap);
+
+/**
+ * Configuration for each label value in the output-stack
+ */
+typedef struct fib_mpls_label_t_
+{
+    /**
+     * The label value
+     */
+    mpls_label_t fml_value;
+
+    /**
+     * The LSP mode
+     */
+    fib_mpls_lsp_mode_t fml_mode;
+
+    /**
+     * TTL. valid only at imposition.
+     */
+    u8 fml_ttl;
+
+    /**
+     * EXP bits; valid only at imposition.
+     */
+    u8 fml_exp;
+} fib_mpls_label_t;
+
+/**
+ * Format an MPLS label
+ */
+extern u8 * format_fib_mpls_label(u8 *s, va_list *ap);
+
 /**
  * @brief 
  * A representation of a path as described by a route producer.
@@ -444,7 +507,7 @@ typedef struct fib_route_path_t_ {
             /**
              * The outgoing MPLS label Stack. NULL implies no label.
              */
-            mpls_label_t *frp_label_stack;
+            fib_mpls_label_t *frp_label_stack;
         };
         /**
          * A path that resolves via a BIER Table.
index b94d6d7..282f531 100644 (file)
@@ -19,7 +19,8 @@
     called through a shared memory interface. 
 */
 
-option version = "1.0.1";
+option version = "1.1.0";
+import "vnet/fib/fib_types.api";
 
 /** \brief Add / del table request
            A table can be added multiple times, but need be deleted only once.
@@ -52,33 +53,6 @@ define ip_fib_dump
   u32 context;
 };
 
-/** \brief FIB path
-    @param sw_if_index - index of the interface
-    @param weight - The weight, for UCMP
-    @param preference - The preference of the path. lowest preference is prefered
-    @param is_local - local if non-zero, else remote
-    @param is_drop - Drop the packet
-    @param is_unreach - Drop the packet and rate limit send ICMP unreachable
-    @param is_prohibit - Drop the packet and rate limit send ICMP prohibited
-    @param afi - the afi of the next hop, IP46_TYPE_IP4=1, IP46_TYPE_IP6=2
-    @param next_hop[16] - the next hop address
-
-    WARNING: this type is replicated, pending cleanup completion
-*/
-typeonly manual_print manual_endian define fib_path
-{
-  u32 sw_if_index;
-  u32 table_id;
-  u8 weight;
-  u8 preference;
-  u8 is_local;
-  u8 is_drop;
-  u8 is_unreach;
-  u8 is_prohibit;
-  u8 afi;
-  u8 next_hop[16];
-};
-
 /** \brief IP FIB table response
     @param table_id - IP fib table id
     @address_length - mask length
@@ -420,7 +394,7 @@ autoreply define ip_add_del_route
   u8 next_hop_address[16];
   u8 next_hop_n_out_labels;
   u32 next_hop_via_label;
-  u32 next_hop_out_label_stack[next_hop_n_out_labels];
+  vl_api_fib_mpls_label_t next_hop_out_label_stack[next_hop_n_out_labels];
 };
 
 /** \brief Add / del route request
index 76e3c1f..a02f8b2 100644 (file)
@@ -342,11 +342,27 @@ typedef struct
 } ip6_header_t;
 
 always_inline u8
-ip6_traffic_class (ip6_header_t * i)
+ip6_traffic_class (const ip6_header_t * i)
 {
   return (i->ip_version_traffic_class_and_flow_label & 0x0FF00000) >> 20;
 }
 
+static_always_inline u8
+ip6_traffic_class_network_order (const ip6_header_t * ip6)
+{
+  return (clib_net_to_host_u32 (ip6->ip_version_traffic_class_and_flow_label)
+         & 0x0ff00000) >> 20;
+}
+
+static_always_inline void
+ip6_set_traffic_class_network_order (ip6_header_t * ip6, u8 dscp)
+{
+  u32 tmp =
+    clib_net_to_host_u32 (ip6->ip_version_traffic_class_and_flow_label);
+  tmp |= (dscp << 20);
+  ip6->ip_version_traffic_class_and_flow_label = clib_host_to_net_u32 (tmp);
+}
+
 always_inline void *
 ip6_next_header (ip6_header_t * i)
 {
index 60fa2fa..726d24c 100644 (file)
@@ -867,7 +867,7 @@ add_del_route_t_handler (u8 is_multipath,
                         u16 next_hop_weight,
                         u16 next_hop_preference,
                         mpls_label_t next_hop_via_label,
-                        mpls_label_t * next_hop_out_label_stack)
+                        fib_mpls_label_t * next_hop_out_label_stack)
 {
   vnet_classify_main_t *cm = &vnet_classify_main;
   fib_route_path_flags_t path_flags = FIB_ROUTE_PATH_FLAG_NONE;
@@ -1071,7 +1071,7 @@ static int
 ip4_add_del_route_t_handler (vl_api_ip_add_del_route_t * mp)
 {
   u32 fib_index, next_hop_fib_index;
-  mpls_label_t *label_stack = NULL;
+  fib_mpls_label_t *label_stack = NULL;
   int rv, ii, n_labels;;
 
   rv = add_del_route_check (FIB_PROTOCOL_IP4,
@@ -1097,13 +1097,19 @@ ip4_add_del_route_t_handler (vl_api_ip_add_del_route_t * mp)
   n_labels = mp->next_hop_n_out_labels;
   if (n_labels == 0)
     ;
-  else if (1 == n_labels)
-    vec_add1 (label_stack, ntohl (mp->next_hop_out_label_stack[0]));
   else
     {
       vec_validate (label_stack, n_labels - 1);
       for (ii = 0; ii < n_labels; ii++)
-       label_stack[ii] = ntohl (mp->next_hop_out_label_stack[ii]);
+       {
+         label_stack[ii].fml_value =
+           ntohl (mp->next_hop_out_label_stack[ii].label);
+         label_stack[ii].fml_ttl = mp->next_hop_out_label_stack[ii].ttl;
+         label_stack[ii].fml_exp = mp->next_hop_out_label_stack[ii].exp;
+         label_stack[ii].fml_mode =
+           (mp->next_hop_out_label_stack[ii].is_uniform ?
+            FIB_MPLS_LSP_MODE_UNIFORM : FIB_MPLS_LSP_MODE_PIPE);
+       }
     }
 
   return (add_del_route_t_handler (mp->is_multipath,
@@ -1133,8 +1139,8 @@ ip4_add_del_route_t_handler (vl_api_ip_add_del_route_t * mp)
 static int
 ip6_add_del_route_t_handler (vl_api_ip_add_del_route_t * mp)
 {
+  fib_mpls_label_t *label_stack = NULL;
   u32 fib_index, next_hop_fib_index;
-  mpls_label_t *label_stack = NULL;
   int rv, ii, n_labels;;
 
   rv = add_del_route_check (FIB_PROTOCOL_IP6,
@@ -1160,13 +1166,19 @@ ip6_add_del_route_t_handler (vl_api_ip_add_del_route_t * mp)
   n_labels = mp->next_hop_n_out_labels;
   if (n_labels == 0)
     ;
-  else if (1 == n_labels)
-    vec_add1 (label_stack, ntohl (mp->next_hop_out_label_stack[0]));
   else
     {
       vec_validate (label_stack, n_labels - 1);
       for (ii = 0; ii < n_labels; ii++)
-       label_stack[ii] = ntohl (mp->next_hop_out_label_stack[ii]);
+       {
+         label_stack[ii].fml_value =
+           ntohl (mp->next_hop_out_label_stack[ii].label);
+         label_stack[ii].fml_ttl = mp->next_hop_out_label_stack[ii].ttl;
+         label_stack[ii].fml_exp = mp->next_hop_out_label_stack[ii].exp;
+         label_stack[ii].fml_mode =
+           (mp->next_hop_out_label_stack[ii].is_uniform ?
+            FIB_MPLS_LSP_MODE_UNIFORM : FIB_MPLS_LSP_MODE_PIPE);
+       }
     }
 
   return (add_del_route_t_handler (mp->is_multipath,
index 2cb663a..f60d428 100644 (file)
@@ -1108,8 +1108,10 @@ mfib_test_i (fib_protocol_t PROTO,
            .eos = MPLS_EOS,
        },
     };
-    mpls_label_t *l3300 = NULL;
-    vec_add1(l3300, 3300);
+    fib_mpls_label_t *l3300 = NULL, fml3300 = {
+        .fml_value = 3300,
+    };
+    vec_add1(l3300, fml3300);
 
     /*
      * MPLS enable an interface so we get the MPLS table created
index 572ac91..6047d25 100644 (file)
@@ -13,7 +13,8 @@
  * limitations under the License.
  */
 
-option version = "1.0.1";
+option version = "1.1.0";
+import "vnet/fib/fib_types.api";
 
 /** \brief Bind/Unbind an MPLS local label to an IP prefix. i.e. create
            a per-prefix label entry.
@@ -40,21 +41,6 @@ autoreply define mpls_ip_bind_unbind
   u8 mb_address[16];
 };
 
-/** \brief MPLS tunnel Add / del route
-    @param client_index - opaque cookie to identify the sender
-    @param context - sender context, to match reply w/ request
-    @param mt_is_add - Is this a route add or delete
-    @param mt_sw_if_index - The SW interface index of the tunnel to delete
-    @param mt_is_multicast - Is the tunnel's underlying LSP multicast
-    @param mt_next_hop_proto_is_ip4 - The next-hop is IPV4
-    @param mt_next_hop_weight - The weight, for UCMP
-    @param mt_next_hop_preference - The preference
-    @param mt_next_hop[16] - the nextop address
-    @param mt_next_hop_sw_if_index - the next-hop SW interface
-    @param mt_next_hop_table_id - the next-hop table-id (if appropriate)
-    @param mt_next_hop_n_out_labels - the number of next-hop output labels
-    @param mt_next_hop_out_label_stack - the next-hop output label stack,  outer most first
-*/
 define mpls_tunnel_add_del
 {
   u32 client_index;
@@ -70,7 +56,7 @@ define mpls_tunnel_add_del
   u8 mt_next_hop_n_out_labels;
   u32 mt_next_hop_sw_if_index;
   u32 mt_next_hop_table_id;
-  u32 mt_next_hop_out_label_stack[mt_next_hop_n_out_labels];
+  vl_api_fib_mpls_label_t mt_next_hop_out_label_stack[mt_next_hop_n_out_labels];
 };
 
 /** \brief Reply for MPLS tunnel add / del request
@@ -96,34 +82,6 @@ define mpls_tunnel_dump
   i32 tunnel_index;
 };
 
-/** \brief FIB path
-    @param sw_if_index - index of the interface
-    @param weight - The weight, for UCMP
-    @param is_local - local if non-zero, else remote
-    @param is_drop - Drop the packet
-    @param is_unreach - Drop the packet and rate limit send ICMP unreachable
-    @param is_prohibit - Drop the packet and rate limit send ICMP prohibited
-    @param afi - the afi of the next hop, IP46_TYPE_IP4=1, IP46_TYPE_IP6=2
-    @param next_hop[16] - the next hop address
-
-    WARNING: this type is replicated, pending cleanup completion
-
-*/
-typeonly manual_print manual_endian define fib_path2
-{
-  u32 sw_if_index;
-  u32 table_id;
-  u8 weight;
-  u8 preference;
-  u8 is_local;
-  u8 is_drop;
-  u8 is_unreach;
-  u8 is_prohibit;
-  u8 afi;
-  u8 next_hop[16];
-  u32 labels[16];
-};
-
 /** \brief mpls tunnel details
 */
 manual_endian manual_print define mpls_tunnel_details
@@ -134,7 +92,7 @@ manual_endian manual_print define mpls_tunnel_details
   u8 mt_l2_only;
   u8 mt_is_multicast;
   u32 mt_count;
-  vl_api_fib_path2_t mt_paths[mt_count];
+  vl_api_fib_path_t mt_paths[mt_count];
 };
 
 /** \brief MPLS Route Add / del route
@@ -207,7 +165,7 @@ autoreply define mpls_route_add_del
   u32 mr_next_hop_sw_if_index;
   u32 mr_next_hop_table_id;
   u32 mr_next_hop_via_label;
-  u32 mr_next_hop_out_label_stack[mr_next_hop_n_out_labels];
+  vl_api_fib_mpls_label_t mr_next_hop_out_label_stack[mr_next_hop_n_out_labels];
 };
 
 /** \brief Dump MPLS fib table
@@ -234,7 +192,7 @@ manual_endian manual_print define mpls_fib_details
   u8  eos_bit;
   u32 label;
   u32 count;
-  vl_api_fib_path2_t path[count];
+  vl_api_fib_path_t path[count];
 };
 
 /** \brief Enable or Disable MPLS on and interface
index 25957fb..be72d3f 100644 (file)
@@ -47,6 +47,9 @@ u8 * format_mpls_unicast_label (u8 * s, va_list * args)
   case MPLS_IETF_GAL_LABEL:
       s = format (s, "%s", MPLS_IETF_GAL_STRING);
       break;
+  case MPLS_LABEL_POP:
+      s = format (s, "pop");
+      break;
   default:
       s = format (s, "%d", label);
       break;
index 36fa610..169ee40 100644 (file)
@@ -170,8 +170,8 @@ static int
 mpls_route_add_del_t_handler (vnet_main_t * vnm,
                              vl_api_mpls_route_add_del_t * mp)
 {
+  fib_mpls_label_t *label_stack = NULL;
   u32 fib_index, next_hop_fib_index;
-  mpls_label_t *label_stack = NULL;
   int rv, ii, n_labels;;
 
   fib_prefix_t pfx = {
@@ -211,13 +211,19 @@ mpls_route_add_del_t_handler (vnet_main_t * vnm,
   n_labels = mp->mr_next_hop_n_out_labels;
   if (n_labels == 0)
     ;
-  else if (1 == n_labels)
-    vec_add1 (label_stack, ntohl (mp->mr_next_hop_out_label_stack[0]));
   else
     {
       vec_validate (label_stack, n_labels - 1);
       for (ii = 0; ii < n_labels; ii++)
-       label_stack[ii] = ntohl (mp->mr_next_hop_out_label_stack[ii]);
+       {
+         label_stack[ii].fml_value =
+           ntohl (mp->mr_next_hop_out_label_stack[ii].label);
+         label_stack[ii].fml_ttl = mp->mr_next_hop_out_label_stack[ii].ttl;
+         label_stack[ii].fml_exp = mp->mr_next_hop_out_label_stack[ii].exp;
+         label_stack[ii].fml_mode =
+           (mp->mr_next_hop_out_label_stack[ii].is_uniform ?
+            FIB_MPLS_LSP_MODE_UNIFORM : FIB_MPLS_LSP_MODE_PIPE);
+       }
     }
 
   /* *INDENT-OFF* */
@@ -323,8 +329,16 @@ vl_api_mpls_tunnel_add_del_t_handler (vl_api_mpls_tunnel_add_del_t * mp)
   if (mp->mt_is_add)
     {
       for (ii = 0; ii < mp->mt_next_hop_n_out_labels; ii++)
-       vec_add1 (rpath.frp_label_stack,
-                 ntohl (mp->mt_next_hop_out_label_stack[ii]));
+       {
+         fib_mpls_label_t fml = {
+           .fml_value = ntohl (mp->mt_next_hop_out_label_stack[ii].label),
+           .fml_ttl = mp->mt_next_hop_out_label_stack[ii].ttl,
+           .fml_exp = mp->mt_next_hop_out_label_stack[ii].exp,
+           .fml_mode = (mp->mt_next_hop_out_label_stack[ii].is_uniform ?
+                        FIB_MPLS_LSP_MODE_UNIFORM : FIB_MPLS_LSP_MODE_PIPE),
+         };
+         vec_add1 (rpath.frp_label_stack, fml);
+       }
     }
 
   vec_add1 (rpaths, rpath);
@@ -388,7 +402,7 @@ send_mpls_tunnel_entry (u32 mti, void *arg)
   mpls_tunnel_send_walk_ctx_t *ctx;
   vl_api_mpls_tunnel_details_t *mp;
   const mpls_tunnel_t *mt;
-  vl_api_fib_path2_t *fp;
+  vl_api_fib_path_t *fp;
   u32 n;
 
   ctx = arg;
@@ -399,8 +413,8 @@ send_mpls_tunnel_entry (u32 mti, void *arg)
   mt = mpls_tunnel_get (mti);
   n = fib_path_list_get_n_paths (mt->mt_path_list);
 
-  mp = vl_msg_api_alloc (sizeof (*mp) + n * sizeof (vl_api_fib_path2_t));
-  memset (mp, 0, sizeof (*mp) + n * sizeof (vl_api_fib_path2_t));
+  mp = vl_msg_api_alloc (sizeof (*mp) + n * sizeof (vl_api_fib_path_t));
+  memset (mp, 0, sizeof (*mp) + n * sizeof (vl_api_fib_path_t));
 
   mp->_vl_msg_id = ntohs (VL_API_MPLS_TUNNEL_DETAILS);
   mp->context = ctx->context;
@@ -456,7 +470,7 @@ send_mpls_fib_details (vpe_api_main_t * am,
 {
   vl_api_mpls_fib_details_t *mp;
   fib_route_path_encode_t *api_rpath;
-  vl_api_fib_path2_t *fp;
+  vl_api_fib_path_t *fp;
   int path_count;
 
   path_count = vec_len (api_rpaths);
index 86ad8bb..d1881d4 100644 (file)
@@ -51,10 +51,11 @@ format_mpls_input_trace (u8 * s, va_list * args)
   foreach_mpls_input_next;
 #undef _
   
-  s = format (s, "MPLS: next %s[%d]  label %d ttl %d", 
+  s = format (s, "MPLS: next %s[%d]  label %d ttl %d exp %d", 
               next_name, t->next_index,
              vnet_mpls_uc_get_label(label),
-             vnet_mpls_uc_get_ttl(label));
+             vnet_mpls_uc_get_ttl(label),
+             vnet_mpls_uc_get_exp(label));
 
   return s;
 }
@@ -74,21 +75,13 @@ mpls_input_inline (vlib_main_t * vm,
                    vlib_frame_t * from_frame)
 {
   u32 n_left_from, next_index, * from, * to_next;
-  mpls_input_runtime_t * rt;
-  mpls_main_t * mm;
+  mpls_main_t * mm = &mpls_main;
   u32 thread_index = vlib_get_thread_index();
   vlib_simple_counter_main_t * cm;
   vnet_main_t * vnm = vnet_get_main();
 
   from = vlib_frame_vector_args (from_frame);
   n_left_from = from_frame->n_vectors;
-  rt = vlib_node_get_runtime_data (vm, mpls_input_node.index);
-  mm = rt->mpls_main;
-  /* 
-   * Force an initial lookup every time, in case the control-plane
-   * changed the label->FIB mapping.
-   */
-  rt->last_label = ~0;
 
   next_index = node->cached_next_index;
 
@@ -279,18 +272,11 @@ VLIB_NODE_FUNCTION_MULTIARCH (mpls_input_node, mpls_input)
 static void
 mpls_setup_nodes (vlib_main_t * vm)
 {
-  mpls_input_runtime_t * rt;
   pg_node_t * pn;
 
   pn = pg_get_node (mpls_input_node.index);
   pn->unformat_edit = unformat_pg_mpls_header;
 
-  rt = vlib_node_get_runtime_data (vm, mpls_input_node.index);
-  rt->last_label = (u32) ~0;
-  rt->last_inner_fib_index = 0;
-  rt->last_outer_fib_index = 0;
-  rt->mpls_main = &mpls_main;
-
   ethernet_register_input_type (vm, ETHERNET_TYPE_MPLS,
                                 mpls_input_node.index);
 }
@@ -309,16 +295,3 @@ static clib_error_t * mpls_input_init (vlib_main_t * vm)
 }
 
 VLIB_INIT_FUNCTION (mpls_input_init);
-
-static clib_error_t * mpls_input_worker_init (vlib_main_t * vm)
-{
-  mpls_input_runtime_t * rt;
-  rt = vlib_node_get_runtime_data (vm, mpls_input_node.index);
-  rt->last_label = (u32) ~0;
-  rt->last_inner_fib_index = 0;
-  rt->last_outer_fib_index = 0;
-  rt->mpls_main = &mpls_main;
-  return 0;
-}
-
-VLIB_WORKER_INIT_FUNCTION (mpls_input_worker_init);
index 8ed2c40..c2067d8 100644 (file)
@@ -123,6 +123,12 @@ mpls_tunnel_collect_forwarding (fib_node_index_t pl_index,
     path_ext = fib_path_ext_list_find_by_path_index(&ctx->mt->mt_path_exts,
                                                     path_index);
 
+    /*
+     * we don't want IP TTL decrements for packets hitting the MPLS labels
+     * we stack on, since the IP TTL decrement is done by the adj
+     */
+    path_ext->fpe_mpls_flags |= FIB_PATH_EXT_MPLS_FLAG_NO_IP_TTL_DECR;
+
     if (NULL != path_ext)
     {
         /*
@@ -273,9 +279,8 @@ mpls_tunnel_stack (adj_index_t ai)
 
         mpls_tunnel_mk_lb(mt,
                           adj->ia_link,
-                          (VNET_LINK_MPLS == adj_get_link_type(ai) ?
-                           FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS:
-                           FIB_FORW_CHAIN_TYPE_MPLS_EOS),
+                          fib_forw_chain_type_from_link_type(
+                              adj_get_link_type(ai)),
                           &dpo);
 
         adj_nbr_midchain_stack(ai, &dpo);
@@ -521,6 +526,11 @@ mpls_tunnel_tx (vlib_main_t * vm,
           b0 = vlib_get_buffer(vm, bi0);
 
           vnet_buffer(b0)->ip.adj_index[VLIB_TX] = mt->mt_l2_lb.dpoi_index;
+          /* since we are coming out of the L2 world, where the vlib_buffer
+           * union is used for other things, make sure it is clean for
+           * MPLS from now on.
+           */
+          vnet_buffer(b0)->mpls.first = 0;
 
           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
             {
index f1c3191..c21bdf1 100644 (file)
 
 #define MPLS_LABEL_INVALID (MPLS_IETF_MAX_LABEL+1)
 
+/**
+ * A value that is explicit about the end of the LSP. Specifying
+ * a label value is needed when the mode configuration (pipe/uniform)
+ * is also requested.
+ * imp-null implies a label swap. pop can be used for a deag.
+ */
+#define MPLS_LABEL_POP (MPLS_IETF_MAX_LABEL+2)
+
 #define MPLS_LABEL_IS_REAL(_lbl) \
     (((_lbl) > MPLS_IETF_MIN_UNRES_LABEL) &&   \
      ((_lbl) <= MPLS_IETF_MAX_UNRES_LABEL))
index bc67445..ca6ac40 100644 (file)
@@ -42,6 +42,32 @@ typedef enum mpls_eos_bit_t_
     [MPLS_EOS] = "eos",                                \
 }
 
+/**
+ * The Default TTL added to MPLS label headers when no other value is available
+ */
+#define MPLS_LABEL_DEFAULT_TTL 64
+
+/**
+ * The Default EXP added to MPLS label headers when no other value is available
+ */
+#define MPLS_LABEL_DEFAULT_EXP 0
+
+/**
+ * When in uniform mode convert an IPv[46] DSCP value to an MPLS EXP value
+ */
+static inline u8 ip_dscp_to_mpls_exp (u8 tos)
+{
+    return (tos >> 5);
+}
+
+/**
+ * When in uniform mode convert an MPLS EXP value to an IPv[46] DSCP value
+ */
+static inline u8 mpls_exp_to_ip_dscp (u8 exp)
+{
+    return (exp << 5);
+}
+
 #define FOR_EACH_MPLS_EOS_BIT(_eos) \
     for (_eos = MPLS_NON_EOS; _eos <= MPLS_EOS; _eos++)
 
index 1a5ba6c..4a56324 100755 (executable)
@@ -34,9 +34,6 @@
 #include <vnet/srmpls/sr_mpls.h>
 #include <vnet/fib/mpls_fib.h>
 #include <vnet/dpo/dpo.h>
-#include <vnet/dpo/replicate_dpo.h>
-#include <vnet/dpo/mpls_label_dpo.h>
-#include <vnet/dpo/lookup_dpo.h>
 #include <vnet/ip/ip.h>
 
 #include <vppinfra/error.h>
@@ -763,7 +760,11 @@ sr_mpls_policy_assign_endpoint_color (mpls_label_t bsid,
                              FIB_SOURCE_SR,
                              FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT, paths);
 
-      vec_add1 (path.frp_label_stack, MPLS_IETF_IMPLICIT_NULL_LABEL);
+      fib_mpls_label_t fml = {
+       .fml_value = MPLS_IETF_IMPLICIT_NULL_LABEL,
+      };
+
+      vec_add1 (path.frp_label_stack, fml);
       pfx.fp_eos = MPLS_NON_EOS;
       path.frp_eos = MPLS_NON_EOS;
 
index 0bd3466..8bb072c 100755 (executable)
@@ -301,7 +301,10 @@ compute_sr_te_automated_steering_fib_entry (mpls_sr_steering_policy_t *
 
   if (steer_pl->vpn_label != (u32) ~ 0)
     {
-      vec_add1 (path.frp_label_stack, steer_pl->vpn_label);
+      fib_mpls_label_t fml = {
+       .fml_value = steer_pl->vpn_label,
+      };
+      vec_add1 (path.frp_label_stack, fml);
       path.frp_eos = MPLS_NON_EOS;
     }
 
@@ -480,7 +483,12 @@ sr_mpls_steering_policy_add (mpls_label_t bsid, u32 table_id,
       fib_route_path_t *paths = NULL;
 
       if (steer_pl->vpn_label != (u32) ~ 0)
-       vec_add1 (path.frp_label_stack, steer_pl->vpn_label);
+       {
+         fib_mpls_label_t fml = {
+           .fml_value = steer_pl->vpn_label,
+         };
+         vec_add1 (path.frp_label_stack, fml);
+       }
 
       /* FIB API calls - Recursive route through the BindingSID */
       if (traffic_type == SR_STEER_IPV6)
index 3a07d92..dae96af 100644 (file)
@@ -28,7 +28,6 @@ option version = "1.1.0";
     @param decap_next_index - Name of decap next graph node
     @param vni - The VXLAN Network Identifier, uint24
 */
-
 define vxlan_add_del_tunnel
 {
   u32 client_index;
index ae7b46a..c7ec0ee 100644 (file)
@@ -6,7 +6,8 @@ import socket
 from framework import VppTestCase, VppTestRunner, running_extended_tests
 from vpp_ip_route import VppIpRoute, VppRoutePath, VppMplsRoute, \
     VppMplsTable, VppIpMRoute, VppMRoutePath, VppIpTable, \
-    MRouteEntryFlags, MRouteItfFlags, MPLS_LABEL_INVALID, DpoProto
+    MRouteEntryFlags, MRouteItfFlags, MPLS_LABEL_INVALID, DpoProto, \
+    VppMplsLabel
 from vpp_bier import *
 from vpp_udp_encap import *
 
@@ -99,15 +100,17 @@ class TestBier(VppTestCase):
         bier_routes = []
         for i in range(1, max_bp+1):
             nh = "10.0.%d.%d" % (i / 255, i % 255)
-            nh_routes.append(VppIpRoute(self, nh, 32,
-                                        [VppRoutePath(self.pg1.remote_ip4,
-                                                      self.pg1.sw_if_index,
-                                                      labels=[2000+i])]))
+            nh_routes.append(
+                VppIpRoute(self, nh, 32,
+                           [VppRoutePath(self.pg1.remote_ip4,
+                                         self.pg1.sw_if_index,
+                                         labels=[VppMplsLabel(2000+i)])]))
             nh_routes[-1].add_vpp_config()
 
-            bier_routes.append(VppBierRoute(self, bti, i,
-                                            [VppRoutePath(nh, 0xffffffff,
-                                                          labels=[100+i])]))
+            bier_routes.append(
+                VppBierRoute(self, bti, i,
+                             [VppRoutePath(nh, 0xffffffff,
+                                           labels=[VppMplsLabel(100+i)])]))
             bier_routes[-1].add_vpp_config()
 
         #
@@ -216,20 +219,20 @@ class TestBier(VppTestCase):
         ip_route_1 = VppIpRoute(self, nh1, 32,
                                 [VppRoutePath(self.pg1.remote_ip4,
                                               self.pg1.sw_if_index,
-                                              labels=[2001])])
+                                              labels=[VppMplsLabel(2001)])])
         ip_route_2 = VppIpRoute(self, nh2, 32,
                                 [VppRoutePath(self.pg1.remote_ip4,
                                               self.pg1.sw_if_index,
-                                              labels=[2002])])
+                                              labels=[VppMplsLabel(2002)])])
         ip_route_1.add_vpp_config()
         ip_route_2.add_vpp_config()
 
         bier_route_1 = VppBierRoute(self, bti, 1,
                                     [VppRoutePath(nh1, 0xffffffff,
-                                                  labels=[101])])
+                                                  labels=[VppMplsLabel(101)])])
         bier_route_2 = VppBierRoute(self, bti, 2,
                                     [VppRoutePath(nh2, 0xffffffff,
-                                                  labels=[102])])
+                                                  labels=[VppMplsLabel(102)])])
         bier_route_1.add_vpp_config()
         bier_route_2.add_vpp_config()
 
@@ -561,7 +564,7 @@ class TestBier(VppTestCase):
         ip_route = VppIpRoute(self, nh1, 32,
                               [VppRoutePath(self.pg1.remote_ip4,
                                             self.pg1.sw_if_index,
-                                            labels=[2001])])
+                                            labels=[VppMplsLabel(2001)])])
         ip_route.add_vpp_config()
 
         udp_encap = VppUdpEncap(self, 4,
index aa5e674..33fed68 100644 (file)
@@ -6,7 +6,8 @@ import socket
 from framework import VppTestCase, VppTestRunner
 from vpp_ip_route import VppIpRoute, VppRoutePath, VppMplsRoute, \
     VppMplsIpBind, VppIpMRoute, VppMRoutePath, \
-    MRouteItfFlags, MRouteEntryFlags, DpoProto, VppIpTable, VppMplsTable
+    MRouteItfFlags, MRouteEntryFlags, DpoProto, VppIpTable, VppMplsTable, \
+    VppMplsLabel, MplsLspMode
 from vpp_mpls_tunnel_interface import VppMPLSTunnelInterface
 
 from scapy.packet import Raw
@@ -25,7 +26,7 @@ def verify_filter(capture, sent):
     return capture
 
 
-def verify_mpls_stack(tst, rx, mpls_labels, ttl=255, num=0):
+def verify_mpls_stack(tst, rx, mpls_labels):
     # the rx'd packet has the MPLS label popped
     eth = rx[Ether]
     tst.assertEqual(eth.type, 0x8847)
@@ -33,12 +34,10 @@ def verify_mpls_stack(tst, rx, mpls_labels, ttl=255, num=0):
     rx_mpls = rx[MPLS]
 
     for ii in range(len(mpls_labels)):
-        tst.assertEqual(rx_mpls.label, mpls_labels[ii])
-        tst.assertEqual(rx_mpls.cos, 0)
-        if ii == num:
-            tst.assertEqual(rx_mpls.ttl, ttl)
-        else:
-            tst.assertEqual(rx_mpls.ttl, 255)
+        tst.assertEqual(rx_mpls.label, mpls_labels[ii].value)
+        tst.assertEqual(rx_mpls.cos, mpls_labels[ii].exp)
+        tst.assertEqual(rx_mpls.ttl, mpls_labels[ii].ttl)
+
         if ii == len(mpls_labels) - 1:
             tst.assertEqual(rx_mpls.s, 1)
         else:
@@ -102,11 +101,11 @@ class TestMPLS(VppTestCase):
             self,
             src_if,
             mpls_labels,
-            mpls_ttl=255,
             ping=0,
             ip_itf=None,
             dst_ip=None,
             chksum=None,
+            ip_ttl=64,
             n=257):
         self.reset_packet_infos()
         pkts = []
@@ -116,22 +115,24 @@ class TestMPLS(VppTestCase):
             p = Ether(dst=src_if.local_mac, src=src_if.remote_mac)
 
             for ii in range(len(mpls_labels)):
-                if ii == len(mpls_labels) - 1:
-                    p = p / MPLS(label=mpls_labels[ii], ttl=mpls_ttl, s=1)
-                else:
-                    p = p / MPLS(label=mpls_labels[ii], ttl=mpls_ttl, s=0)
+                p = p / MPLS(label=mpls_labels[ii].value,
+                             ttl=mpls_labels[ii].ttl,
+                             cos=mpls_labels[ii].exp)
             if not ping:
                 if not dst_ip:
-                    p = (p / IP(src=src_if.local_ip4, dst=src_if.remote_ip4) /
+                    p = (p / IP(src=src_if.local_ip4,
+                                dst=src_if.remote_ip4,
+                                ttl=ip_ttl) /
                          UDP(sport=1234, dport=1234) /
                          Raw(payload))
                 else:
-                    p = (p / IP(src=src_if.local_ip4, dst=dst_ip) /
+                    p = (p / IP(src=src_if.local_ip4, dst=dst_ip, ttl=ip_ttl) /
                          UDP(sport=1234, dport=1234) /
                          Raw(payload))
             else:
                 p = (p / IP(src=ip_itf.remote_ip4,
-                            dst=ip_itf.local_ip4) /
+                            dst=ip_itf.local_ip4,
+                            ttl=ip_ttl) /
                      ICMP())
 
             if chksum:
@@ -140,39 +141,58 @@ class TestMPLS(VppTestCase):
             pkts.append(p)
         return pkts
 
-    def create_stream_ip4(self, src_if, dst_ip):
+    def create_stream_ip4(self, src_if, dst_ip, ip_ttl=64, ip_dscp=0):
         self.reset_packet_infos()
         pkts = []
         for i in range(0, 257):
             info = self.create_packet_info(src_if, src_if)
             payload = self.info_to_payload(info)
             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
-                 IP(src=src_if.remote_ip4, dst=dst_ip) /
+                 IP(src=src_if.remote_ip4, dst=dst_ip,
+                    ttl=ip_ttl, tos=ip_dscp) /
                  UDP(sport=1234, dport=1234) /
                  Raw(payload))
             info.data = p.copy()
             pkts.append(p)
         return pkts
 
-    def create_stream_labelled_ip6(self, src_if, mpls_label, mpls_ttl,
-                                   dst_ip=None, hlim=64):
-        if dst_ip is None:
-            dst_ip = src_if.remote_ip6
+    def create_stream_ip6(self, src_if, dst_ip, ip_ttl=64, ip_dscp=0):
         self.reset_packet_infos()
         pkts = []
         for i in range(0, 257):
             info = self.create_packet_info(src_if, src_if)
             payload = self.info_to_payload(info)
             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
-                 MPLS(label=mpls_label, ttl=mpls_ttl) /
-                 IPv6(src=src_if.remote_ip6, dst=dst_ip, hlim=hlim) /
+                 IPv6(src=src_if.remote_ip6, dst=dst_ip,
+                      hlim=ip_ttl, tc=ip_dscp) /
                  UDP(sport=1234, dport=1234) /
                  Raw(payload))
             info.data = p.copy()
             pkts.append(p)
         return pkts
 
-    def verify_capture_ip4(self, src_if, capture, sent, ping_resp=0):
+    def create_stream_labelled_ip6(self, src_if, mpls_labels,
+                                   hlim=64, dst_ip=None):
+        if dst_ip is None:
+            dst_ip = src_if.remote_ip6
+        self.reset_packet_infos()
+        pkts = []
+        for i in range(0, 257):
+            info = self.create_packet_info(src_if, src_if)
+            payload = self.info_to_payload(info)
+            p = Ether(dst=src_if.local_mac, src=src_if.remote_mac)
+            for l in mpls_labels:
+                p = p / MPLS(label=l.value, ttl=l.ttl, cos=l.exp)
+
+            p = p / (IPv6(src=src_if.remote_ip6, dst=dst_ip, hlim=hlim) /
+                     UDP(sport=1234, dport=1234) /
+                     Raw(payload))
+            info.data = p.copy()
+            pkts.append(p)
+        return pkts
+
+    def verify_capture_ip4(self, src_if, capture, sent, ping_resp=0,
+                           ip_ttl=None, ip_dscp=0):
         try:
             capture = verify_filter(capture, sent)
 
@@ -192,8 +212,12 @@ class TestMPLS(VppTestCase):
                 if not ping_resp:
                     self.assertEqual(rx_ip.src, tx_ip.src)
                     self.assertEqual(rx_ip.dst, tx_ip.dst)
-                    # IP processing post pop has decremented the TTL
-                    self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
+                    self.assertEqual(rx_ip.tos, ip_dscp)
+                    if not ip_ttl:
+                        # IP processing post pop has decremented the TTL
+                        self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
+                    else:
+                        self.assertEqual(rx_ip.ttl, ip_ttl)
                 else:
                     self.assertEqual(rx_ip.src, tx_ip.dst)
                     self.assertEqual(rx_ip.dst, tx_ip.src)
@@ -202,7 +226,7 @@ class TestMPLS(VppTestCase):
             raise
 
     def verify_capture_labelled_ip4(self, src_if, capture, sent,
-                                    mpls_labels):
+                                    mpls_labels, ip_ttl=None):
         try:
             capture = verify_filter(capture, sent)
 
@@ -214,22 +238,46 @@ class TestMPLS(VppTestCase):
                 tx_ip = tx[IP]
                 rx_ip = rx[IP]
 
-                # the MPLS TTL is copied from the IP
-                verify_mpls_stack(self, rx, mpls_labels, rx_ip.ttl,
-                                  len(mpls_labels) - 1)
+                verify_mpls_stack(self, rx, mpls_labels)
 
                 self.assertEqual(rx_ip.src, tx_ip.src)
                 self.assertEqual(rx_ip.dst, tx_ip.dst)
-                # IP processing post pop has decremented the TTL
-                self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
+                if not ip_ttl:
+                    # IP processing post pop has decremented the TTL
+                    self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
+                else:
+                    self.assertEqual(rx_ip.ttl, ip_ttl)
+
+        except:
+            raise
+
+    def verify_capture_labelled_ip6(self, src_if, capture, sent,
+                                    mpls_labels, ip_ttl=None):
+        try:
+            capture = verify_filter(capture, sent)
+
+            self.assertEqual(len(capture), len(sent))
+
+            for i in range(len(capture)):
+                tx = sent[i]
+                rx = capture[i]
+                tx_ip = tx[IPv6]
+                rx_ip = rx[IPv6]
+
+                verify_mpls_stack(self, rx, mpls_labels)
+
+                self.assertEqual(rx_ip.src, tx_ip.src)
+                self.assertEqual(rx_ip.dst, tx_ip.dst)
+                if not ip_ttl:
+                    # IP processing post pop has decremented the TTL
+                    self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim)
+                else:
+                    self.assertEqual(rx_ip.hlim, ip_ttl)
 
         except:
             raise
 
-    def verify_capture_tunneled_ip4(self, src_if, capture, sent, mpls_labels,
-                                    ttl=255, top=None):
-        if top is None:
-            top = len(mpls_labels) - 1
+    def verify_capture_tunneled_ip4(self, src_if, capture, sent, mpls_labels):
         try:
             capture = verify_filter(capture, sent)
 
@@ -241,8 +289,7 @@ class TestMPLS(VppTestCase):
                 tx_ip = tx[IP]
                 rx_ip = rx[IP]
 
-                # the MPLS TTL is 255 since it enters a new tunnel
-                verify_mpls_stack(self, rx, mpls_labels, ttl, top)
+                verify_mpls_stack(self, rx, mpls_labels)
 
                 self.assertEqual(rx_ip.src, tx_ip.src)
                 self.assertEqual(rx_ip.dst, tx_ip.dst)
@@ -253,7 +300,7 @@ class TestMPLS(VppTestCase):
             raise
 
     def verify_capture_labelled(self, src_if, capture, sent,
-                                mpls_labels, ttl=254, num=0):
+                                mpls_labels):
         try:
             capture = verify_filter(capture, sent)
 
@@ -261,11 +308,12 @@ class TestMPLS(VppTestCase):
 
             for i in range(len(capture)):
                 rx = capture[i]
-                verify_mpls_stack(self, rx, mpls_labels, ttl, num)
+                verify_mpls_stack(self, rx, mpls_labels)
         except:
             raise
 
-    def verify_capture_ip6(self, src_if, capture, sent):
+    def verify_capture_ip6(self, src_if, capture, sent,
+                           ip_hlim=None, ip_dscp=0):
         try:
             self.assertEqual(len(capture), len(sent))
 
@@ -282,8 +330,12 @@ class TestMPLS(VppTestCase):
 
                 self.assertEqual(rx_ip.src, tx_ip.src)
                 self.assertEqual(rx_ip.dst, tx_ip.dst)
+                self.assertEqual(rx_ip.tc,  ip_dscp)
                 # IP processing post pop has decremented the TTL
-                self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim)
+                if not ip_hlim:
+                    self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim)
+                else:
+                    self.assertEqual(rx_ip.hlim, ip_hlim)
 
         except:
             raise
@@ -323,22 +375,18 @@ class TestMPLS(VppTestCase):
         route_32_eos = VppMplsRoute(self, 32, 1,
                                     [VppRoutePath(self.pg0.remote_ip4,
                                                   self.pg0.sw_if_index,
-                                                  labels=[33])])
+                                                  labels=[VppMplsLabel(33)])])
         route_32_eos.add_vpp_config()
 
         #
         # a stream that matches the route for 10.0.0.1
         # PG0 is in the default table
         #
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip4(self.pg0, [32])
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg0.get_capture()
-        self.verify_capture_labelled(self.pg0, rx, tx, [33])
+        tx = self.create_stream_labelled_ip4(self.pg0,
+                                             [VppMplsLabel(32, ttl=32, exp=1)])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_labelled(self.pg0, rx, tx,
+                                     [VppMplsLabel(33, ttl=31, exp=1)])
 
         #
         # A simple MPLS xconnect - non-eos label in label out
@@ -346,22 +394,38 @@ class TestMPLS(VppTestCase):
         route_32_neos = VppMplsRoute(self, 32, 0,
                                      [VppRoutePath(self.pg0.remote_ip4,
                                                    self.pg0.sw_if_index,
-                                                   labels=[33])])
+                                                   labels=[VppMplsLabel(33)])])
         route_32_neos.add_vpp_config()
 
         #
         # a stream that matches the route for 10.0.0.1
         # PG0 is in the default table
         #
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip4(self.pg0, [32, 99])
-        self.pg0.add_stream(tx)
+        tx = self.create_stream_labelled_ip4(self.pg0,
+                                             [VppMplsLabel(32, ttl=21, exp=7),
+                                              VppMplsLabel(99)])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_labelled(self.pg0, rx, tx,
+                                     [VppMplsLabel(33, ttl=20, exp=7),
+                                      VppMplsLabel(99)])
 
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
+        #
+        # A simple MPLS xconnect - non-eos label in label out, uniform mode
+        #
+        route_42_neos = VppMplsRoute(
+            self, 42, 0,
+            [VppRoutePath(self.pg0.remote_ip4,
+                          self.pg0.sw_if_index,
+                          labels=[VppMplsLabel(43, MplsLspMode.UNIFORM)])])
+        route_42_neos.add_vpp_config()
 
-        rx = self.pg0.get_capture()
-        self.verify_capture_labelled(self.pg0, rx, tx, [33, 99])
+        tx = self.create_stream_labelled_ip4(self.pg0,
+                                             [VppMplsLabel(42, ttl=21, exp=7),
+                                              VppMplsLabel(99)])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_labelled(self.pg0, rx, tx,
+                                     [VppMplsLabel(43, ttl=20, exp=7),
+                                      VppMplsLabel(99)])
 
         #
         # An MPLS xconnect - EOS label in IP out
@@ -372,25 +436,40 @@ class TestMPLS(VppTestCase):
                                                   labels=[])])
         route_33_eos.add_vpp_config()
 
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip4(self.pg0, [33])
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg0.get_capture()
+        tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(33)])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
         self.verify_capture_ip4(self.pg0, rx, tx)
 
         #
         # disposed packets have an invalid IPv4 checkusm
         #
-        tx = self.create_stream_labelled_ip4(self.pg0, [33],
+        tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(33)],
                                              dst_ip=self.pg0.remote_ip4,
                                              n=65,
                                              chksum=1)
         self.send_and_assert_no_replies(self.pg0, tx, "Invalid Checksum")
 
+        #
+        # An MPLS xconnect - EOS label in IP out, uniform mode
+        #
+        route_3333_eos = VppMplsRoute(
+            self, 3333, 1,
+            [VppRoutePath(self.pg0.remote_ip4,
+                          self.pg0.sw_if_index,
+                          labels=[VppMplsLabel(3, MplsLspMode.UNIFORM)])])
+        route_3333_eos.add_vpp_config()
+
+        tx = self.create_stream_labelled_ip4(
+            self.pg0,
+            [VppMplsLabel(3333, ttl=55, exp=3)])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_ip4(self.pg0, rx, tx, ip_ttl=54, ip_dscp=0x60)
+        tx = self.create_stream_labelled_ip4(
+            self.pg0,
+            [VppMplsLabel(3333, ttl=66, exp=4)])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_ip4(self.pg0, rx, tx, ip_ttl=65, ip_dscp=0x80)
+
         #
         # An MPLS xconnect - EOS label in IPv6 out
         #
@@ -402,33 +481,18 @@ class TestMPLS(VppTestCase):
                           proto=DpoProto.DPO_PROTO_IP6)])
         route_333_eos.add_vpp_config()
 
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip6(self.pg0, [333], 64)
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg0.get_capture()
+        tx = self.create_stream_labelled_ip6(self.pg0, [VppMplsLabel(333)])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
         self.verify_capture_ip6(self.pg0, rx, tx)
 
         #
         # disposed packets have an TTL expired
         #
-        tx = self.create_stream_labelled_ip6(self.pg0, [333], 64,
+        tx = self.create_stream_labelled_ip6(self.pg0,
+                                             [VppMplsLabel(333, ttl=64)],
                                              dst_ip=self.pg1.remote_ip6,
                                              hlim=1)
-
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip6(self.pg0, [333], 64,
-                                             dst_ip=self.pg1.remote_ip6,
-                                             hlim=0)
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg0.get_capture()
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
         self.verify_capture_ip6_icmp(self.pg0, rx, tx)
 
         #
@@ -438,33 +502,39 @@ class TestMPLS(VppTestCase):
             self, 334, 1,
             [VppRoutePath(self.pg0.remote_ip6,
                           self.pg0.sw_if_index,
-                          labels=[3],
+                          labels=[VppMplsLabel(3)],
                           proto=DpoProto.DPO_PROTO_IP6)])
         route_334_eos.add_vpp_config()
 
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip6(self.pg0, [334], 64)
-        self.pg0.add_stream(tx)
+        tx = self.create_stream_labelled_ip6(self.pg0,
+                                             [VppMplsLabel(334, ttl=64)])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_ip6(self.pg0, rx, tx)
 
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
+        #
+        # An MPLS xconnect - EOS label in IPv6 out w imp-null in uniform mode
+        #
+        route_335_eos = VppMplsRoute(
+            self, 335, 1,
+            [VppRoutePath(self.pg0.remote_ip6,
+                          self.pg0.sw_if_index,
+                          labels=[VppMplsLabel(3, MplsLspMode.UNIFORM)],
+                          proto=DpoProto.DPO_PROTO_IP6)])
+        route_335_eos.add_vpp_config()
 
-        rx = self.pg0.get_capture()
-        self.verify_capture_ip6(self.pg0, rx, tx)
+        tx = self.create_stream_labelled_ip6(
+            self.pg0,
+            [VppMplsLabel(335, ttl=27, exp=4)])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_ip6(self.pg0, rx, tx, ip_hlim=26, ip_dscp=0x80)
 
         #
         # disposed packets have an TTL expired
         #
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip6(self.pg0, [334], 64,
+        tx = self.create_stream_labelled_ip6(self.pg0, [VppMplsLabel(334)],
                                              dst_ip=self.pg1.remote_ip6,
                                              hlim=0)
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg0.get_capture()
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
         self.verify_capture_ip6_icmp(self.pg0, rx, tx)
 
         #
@@ -477,33 +547,51 @@ class TestMPLS(VppTestCase):
                                                    labels=[])])
         route_33_neos.add_vpp_config()
 
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip4(self.pg0, [33, 99])
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-        self.pg0.assert_nothing_captured(
-            remark="MPLS non-EOS packets popped and forwarded")
+        tx = self.create_stream_labelled_ip4(self.pg0,
+                                             [VppMplsLabel(33),
+                                              VppMplsLabel(99)])
+        self.send_and_assert_no_replies(
+            self.pg0, tx,
+            "MPLS non-EOS packets popped and forwarded")
 
         #
         # A recursive EOS x-connect, which resolves through another x-connect
+        # in pipe mode
         #
         route_34_eos = VppMplsRoute(self, 34, 1,
                                     [VppRoutePath("0.0.0.0",
                                                   0xffffffff,
                                                   nh_via_label=32,
-                                                  labels=[44, 45])])
+                                                  labels=[VppMplsLabel(44),
+                                                          VppMplsLabel(45)])])
         route_34_eos.add_vpp_config()
 
-        tx = self.create_stream_labelled_ip4(self.pg0, [34])
-        self.pg0.add_stream(tx)
+        tx = self.create_stream_labelled_ip4(self.pg0,
+                                             [VppMplsLabel(34, ttl=3)])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_labelled(self.pg0, rx, tx,
+                                     [VppMplsLabel(33),
+                                      VppMplsLabel(44),
+                                      VppMplsLabel(45, ttl=2)])
 
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
+        #
+        # A recursive EOS x-connect, which resolves through another x-connect
+        # in uniform mode
+        #
+        route_35_eos = VppMplsRoute(
+            self, 35, 1,
+            [VppRoutePath("0.0.0.0",
+                          0xffffffff,
+                          nh_via_label=42,
+                          labels=[VppMplsLabel(44)])])
+        route_35_eos.add_vpp_config()
 
-        rx = self.pg0.get_capture()
-        self.verify_capture_labelled(self.pg0, rx, tx, [33, 44, 45], num=2)
+        tx = self.create_stream_labelled_ip4(self.pg0,
+                                             [VppMplsLabel(35, ttl=3)])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_labelled(self.pg0, rx, tx,
+                                     [VppMplsLabel(43, ttl=2),
+                                      VppMplsLabel(44, ttl=2)])
 
         #
         # A recursive non-EOS x-connect, which resolves through another
@@ -513,19 +601,20 @@ class TestMPLS(VppTestCase):
                                      [VppRoutePath("0.0.0.0",
                                                    0xffffffff,
                                                    nh_via_label=32,
-                                                   labels=[44, 46])])
+                                                   labels=[VppMplsLabel(44),
+                                                           VppMplsLabel(46)])])
         route_34_neos.add_vpp_config()
 
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip4(self.pg0, [34, 99])
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg0.get_capture()
+        tx = self.create_stream_labelled_ip4(self.pg0,
+                                             [VppMplsLabel(34, ttl=45),
+                                              VppMplsLabel(99)])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
         # it's the 2nd (counting from 0) label in the stack that is swapped
-        self.verify_capture_labelled(self.pg0, rx, tx, [33, 44, 46, 99], num=2)
+        self.verify_capture_labelled(self.pg0, rx, tx,
+                                     [VppMplsLabel(33),
+                                      VppMplsLabel(44),
+                                      VppMplsLabel(46, ttl=44),
+                                      VppMplsLabel(99)])
 
         #
         # an recursive IP route that resolves through the recursive non-eos
@@ -535,18 +624,16 @@ class TestMPLS(VppTestCase):
                                  [VppRoutePath("0.0.0.0",
                                                0xffffffff,
                                                nh_via_label=34,
-                                               labels=[55])])
+                                               labels=[VppMplsLabel(55)])])
         ip_10_0_0_1.add_vpp_config()
 
-        self.vapi.cli("clear trace")
         tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg0.get_capture()
-        self.verify_capture_labelled_ip4(self.pg0, rx, tx, [33, 44, 46, 55])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_labelled_ip4(self.pg0, rx, tx,
+                                         [VppMplsLabel(33),
+                                          VppMplsLabel(44),
+                                          VppMplsLabel(46),
+                                          VppMplsLabel(55)])
 
         ip_10_0_0_1.remove_vpp_config()
         route_34_neos.remove_vpp_config()
@@ -565,7 +652,7 @@ class TestMPLS(VppTestCase):
         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
                                     [VppRoutePath(self.pg0.remote_ip4,
                                                   self.pg0.sw_if_index,
-                                                  labels=[45])])
+                                                  labels=[VppMplsLabel(45)])])
         route_10_0_0_1.add_vpp_config()
 
         # bind a local label to the route
@@ -573,37 +660,24 @@ class TestMPLS(VppTestCase):
         binding.add_vpp_config()
 
         # non-EOS stream
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip4(self.pg0, [44, 99])
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg0.get_capture()
-        self.verify_capture_labelled(self.pg0, rx, tx, [45, 99])
+        tx = self.create_stream_labelled_ip4(self.pg0,
+                                             [VppMplsLabel(44),
+                                              VppMplsLabel(99)])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_labelled(self.pg0, rx, tx,
+                                     [VppMplsLabel(45, ttl=63),
+                                      VppMplsLabel(99)])
 
         # EOS stream
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip4(self.pg0, [44])
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg0.get_capture()
-        self.verify_capture_labelled(self.pg0, rx, tx, [45])
+        tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(44)])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_labelled(self.pg0, rx, tx,
+                                     [VppMplsLabel(45, ttl=63)])
 
         # IP stream
-        self.vapi.cli("clear trace")
         tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg0.get_capture()
-        self.verify_capture_labelled_ip4(self.pg0, rx, tx, [45])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_labelled_ip4(self.pg0, rx, tx, [VppMplsLabel(45)])
 
         #
         # cleanup
@@ -620,22 +694,16 @@ class TestMPLS(VppTestCase):
         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
                                     [VppRoutePath(self.pg0.remote_ip4,
                                                   self.pg0.sw_if_index,
-                                                  labels=[32])])
+                                                  labels=[VppMplsLabel(32)])])
         route_10_0_0_1.add_vpp_config()
 
         #
         # a stream that matches the route for 10.0.0.1
         # PG0 is in the default table
         #
-        self.vapi.cli("clear trace")
         tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg0.get_capture()
-        self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_labelled_ip4(self.pg0, rx, tx, [VppMplsLabel(32)])
 
         #
         # Add a non-recursive route with a 3 out labels
@@ -643,22 +711,56 @@ class TestMPLS(VppTestCase):
         route_10_0_0_2 = VppIpRoute(self, "10.0.0.2", 32,
                                     [VppRoutePath(self.pg0.remote_ip4,
                                                   self.pg0.sw_if_index,
-                                                  labels=[32, 33, 34])])
+                                                  labels=[VppMplsLabel(32),
+                                                          VppMplsLabel(33),
+                                                          VppMplsLabel(34)])])
         route_10_0_0_2.add_vpp_config()
 
+        tx = self.create_stream_ip4(self.pg0, "10.0.0.2",
+                                    ip_ttl=44, ip_dscp=0xff)
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_labelled_ip4(self.pg0, rx, tx,
+                                         [VppMplsLabel(32),
+                                          VppMplsLabel(33),
+                                          VppMplsLabel(34)],
+                                         ip_ttl=43)
+
         #
-        # a stream that matches the route for 10.0.0.1
-        # PG0 is in the default table
+        # Add a non-recursive route with a single out label in uniform mode
         #
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_ip4(self.pg0, "10.0.0.2")
-        self.pg0.add_stream(tx)
+        route_10_0_0_3 = VppIpRoute(
+            self, "10.0.0.3", 32,
+            [VppRoutePath(self.pg0.remote_ip4,
+                          self.pg0.sw_if_index,
+                          labels=[VppMplsLabel(32,
+                                               mode=MplsLspMode.UNIFORM)])])
+        route_10_0_0_3.add_vpp_config()
 
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
+        tx = self.create_stream_ip4(self.pg0, "10.0.0.3",
+                                    ip_ttl=54, ip_dscp=0xbe)
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_labelled_ip4(self.pg0, rx, tx,
+                                         [VppMplsLabel(32, ttl=53, exp=5)])
 
-        rx = self.pg0.get_capture()
-        self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32, 33, 34])
+        #
+        # Add a IPv6 non-recursive route with a single out label in
+        # uniform mode
+        #
+        route_2001_3 = VppIpRoute(
+            self, "2001::3", 128,
+            [VppRoutePath(self.pg0.remote_ip6,
+                          self.pg0.sw_if_index,
+                          proto=DpoProto.DPO_PROTO_IP6,
+                          labels=[VppMplsLabel(32,
+                                               mode=MplsLspMode.UNIFORM)])],
+            is_ip6=1)
+        route_2001_3.add_vpp_config()
+
+        tx = self.create_stream_ip6(self.pg0, "2001::3",
+                                    ip_ttl=54, ip_dscp=0xbe)
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_labelled_ip6(self.pg0, rx, tx,
+                                         [VppMplsLabel(32, ttl=53, exp=5)])
 
         #
         # add a recursive path, with output label, via the 1 label route
@@ -666,22 +768,18 @@ class TestMPLS(VppTestCase):
         route_11_0_0_1 = VppIpRoute(self, "11.0.0.1", 32,
                                     [VppRoutePath("10.0.0.1",
                                                   0xffffffff,
-                                                  labels=[44])])
+                                                  labels=[VppMplsLabel(44)])])
         route_11_0_0_1.add_vpp_config()
 
         #
         # a stream that matches the route for 11.0.0.1, should pick up
         # the label stack for 11.0.0.1 and 10.0.0.1
         #
-        self.vapi.cli("clear trace")
         tx = self.create_stream_ip4(self.pg0, "11.0.0.1")
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg0.get_capture()
-        self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32, 44])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_labelled_ip4(self.pg0, rx, tx,
+                                         [VppMplsLabel(32),
+                                          VppMplsLabel(44)])
 
         #
         # add a recursive path, with 2 labels, via the 3 label route
@@ -689,23 +787,22 @@ class TestMPLS(VppTestCase):
         route_11_0_0_2 = VppIpRoute(self, "11.0.0.2", 32,
                                     [VppRoutePath("10.0.0.2",
                                                   0xffffffff,
-                                                  labels=[44, 45])])
+                                                  labels=[VppMplsLabel(44),
+                                                          VppMplsLabel(45)])])
         route_11_0_0_2.add_vpp_config()
 
         #
         # a stream that matches the route for 11.0.0.1, should pick up
         # the label stack for 11.0.0.1 and 10.0.0.1
         #
-        self.vapi.cli("clear trace")
         tx = self.create_stream_ip4(self.pg0, "11.0.0.2")
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg0.get_capture()
-        self.verify_capture_labelled_ip4(
-            self.pg0, rx, tx, [32, 33, 34, 44, 45])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
+        self.verify_capture_labelled_ip4(self.pg0, rx, tx,
+                                         [VppMplsLabel(32),
+                                          VppMplsLabel(33),
+                                          VppMplsLabel(34),
+                                          VppMplsLabel(44),
+                                          VppMplsLabel(45)])
 
         #
         # cleanup
@@ -715,16 +812,18 @@ class TestMPLS(VppTestCase):
         route_10_0_0_2.remove_vpp_config()
         route_10_0_0_1.remove_vpp_config()
 
-    def test_tunnel(self):
-        """ MPLS Tunnel Tests """
+    def test_tunnel_pipe(self):
+        """ MPLS Tunnel Tests - Pipe """
 
         #
         # Create a tunnel with a single out label
         #
-        mpls_tun = VppMPLSTunnelInterface(self,
-                                          [VppRoutePath(self.pg0.remote_ip4,
-                                                        self.pg0.sw_if_index,
-                                                        labels=[44, 46])])
+        mpls_tun = VppMPLSTunnelInterface(
+            self,
+            [VppRoutePath(self.pg0.remote_ip4,
+                          self.pg0.sw_if_index,
+                          labels=[VppMplsLabel(44),
+                                  VppMplsLabel(46)])])
         mpls_tun.add_vpp_config()
         mpls_tun.admin_up()
 
@@ -744,7 +843,9 @@ class TestMPLS(VppTestCase):
         self.pg_start()
 
         rx = self.pg0.get_capture()
-        self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [44, 46])
+        self.verify_capture_tunneled_ip4(self.pg0, rx, tx,
+                                         [VppMplsLabel(44),
+                                          VppMplsLabel(46)])
 
         #
         # add a labelled route through the new tunnel
@@ -763,35 +864,88 @@ class TestMPLS(VppTestCase):
         self.pg_start()
 
         rx = self.pg0.get_capture()
-        self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [44, 46, 33],
-                                         ttl=63, top=2)
+        self.verify_capture_tunneled_ip4(self.pg0, rx, tx,
+                                         [VppMplsLabel(44),
+                                          VppMplsLabel(46),
+                                          VppMplsLabel(33, ttl=255)])
 
-    def test_v4_exp_null(self):
-        """ MPLS V4 Explicit NULL test """
+    def test_tunnel_uniform(self):
+        """ MPLS Tunnel Tests - Uniform """
 
         #
-        # The first test case has an MPLS TTL of 0
-        # all packet should be dropped
+        # Create a tunnel with a single out label
+        # The label stack is specified here from outer to inner
         #
-        tx = self.create_stream_labelled_ip4(self.pg0, [0], 0)
+        mpls_tun = VppMPLSTunnelInterface(
+            self,
+            [VppRoutePath(self.pg0.remote_ip4,
+                          self.pg0.sw_if_index,
+                          labels=[VppMplsLabel(44, ttl=32),
+                                  VppMplsLabel(46, MplsLspMode.UNIFORM)])])
+        mpls_tun.add_vpp_config()
+        mpls_tun.admin_up()
+
+        #
+        # add an unlabelled route through the new tunnel
+        #
+        route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
+                                    [VppRoutePath("0.0.0.0",
+                                                  mpls_tun._sw_if_index)])
+        route_10_0_0_3.add_vpp_config()
+
+        self.vapi.cli("clear trace")
+        tx = self.create_stream_ip4(self.pg0, "10.0.0.3", ip_ttl=24)
         self.pg0.add_stream(tx)
 
         self.pg_enable_capture(self.pg_interfaces)
         self.pg_start()
 
-        self.pg0.assert_nothing_captured(remark="MPLS TTL=0 packets forwarded")
+        rx = self.pg0.get_capture()
+        self.verify_capture_tunneled_ip4(self.pg0, rx, tx,
+                                         [VppMplsLabel(44, ttl=32),
+                                          VppMplsLabel(46, ttl=23)])
 
         #
-        # a stream with a non-zero MPLS TTL
-        # PG0 is in the default table
+        # add a labelled route through the new tunnel
         #
-        tx = self.create_stream_labelled_ip4(self.pg0, [0])
+        route_10_0_0_4 = VppIpRoute(
+            self, "10.0.0.4", 32,
+            [VppRoutePath("0.0.0.0",
+                          mpls_tun._sw_if_index,
+                          labels=[VppMplsLabel(33, ttl=47)])])
+        route_10_0_0_4.add_vpp_config()
+
+        self.vapi.cli("clear trace")
+        tx = self.create_stream_ip4(self.pg0, "10.0.0.4")
         self.pg0.add_stream(tx)
 
         self.pg_enable_capture(self.pg_interfaces)
         self.pg_start()
 
         rx = self.pg0.get_capture()
+        self.verify_capture_tunneled_ip4(self.pg0, rx, tx,
+                                         [VppMplsLabel(44, ttl=32),
+                                          VppMplsLabel(46, ttl=47),
+                                          VppMplsLabel(33, ttl=47)])
+
+    def test_v4_exp_null(self):
+        """ MPLS V4 Explicit NULL test """
+
+        #
+        # The first test case has an MPLS TTL of 0
+        # all packet should be dropped
+        #
+        tx = self.create_stream_labelled_ip4(self.pg0,
+                                             [VppMplsLabel(0, ttl=0)])
+        self.send_and_assert_no_replies(self.pg0, tx,
+                                        "MPLS TTL=0 packets forwarded")
+
+        #
+        # a stream with a non-zero MPLS TTL
+        # PG0 is in the default table
+        #
+        tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(0)])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
         self.verify_capture_ip4(self.pg0, rx, tx)
 
         #
@@ -799,15 +953,9 @@ class TestMPLS(VppTestCase):
         # PG1 is in table 1
         # we are ensuring the post-pop lookup occurs in the VRF table
         #
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip4(self.pg1, [0])
-        self.pg1.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg1.get_capture()
-        self.verify_capture_ip4(self.pg0, rx, tx)
+        tx = self.create_stream_labelled_ip4(self.pg1, [VppMplsLabel(0)])
+        rx = self.send_and_expect(self.pg1, tx, self.pg1)
+        self.verify_capture_ip4(self.pg1, rx, tx)
 
     def test_v6_exp_null(self):
         """ MPLS V6 Explicit NULL test """
@@ -816,14 +964,8 @@ class TestMPLS(VppTestCase):
         # a stream with a non-zero MPLS TTL
         # PG0 is in the default table
         #
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip6(self.pg0, 2, 2)
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg0.get_capture()
+        tx = self.create_stream_labelled_ip6(self.pg0, [VppMplsLabel(2)])
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
         self.verify_capture_ip6(self.pg0, rx, tx)
 
         #
@@ -831,14 +973,8 @@ class TestMPLS(VppTestCase):
         # PG1 is in table 1
         # we are ensuring the post-pop lookup occurs in the VRF table
         #
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip6(self.pg1, 2, 2)
-        self.pg1.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg1.get_capture()
+        tx = self.create_stream_labelled_ip6(self.pg1, [VppMplsLabel(2)])
+        rx = self.send_and_expect(self.pg1, tx, self.pg1)
         self.verify_capture_ip6(self.pg0, rx, tx)
 
     def test_deag(self):
@@ -857,15 +993,11 @@ class TestMPLS(VppTestCase):
         # ping an interface in the default table
         # PG0 is in the default table
         #
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip4(self.pg0, [34], ping=1,
+        tx = self.create_stream_labelled_ip4(self.pg0,
+                                             [VppMplsLabel(34)],
+                                             ping=1,
                                              ip_itf=self.pg0)
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg0.get_capture()
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
         self.verify_capture_ip4(self.pg0, rx, tx, ping_resp=1)
 
         #
@@ -882,16 +1014,9 @@ class TestMPLS(VppTestCase):
         # PG0 is in the default table. packet arrive labelled in the
         # default table and egress unlabelled in the non-default
         #
-        self.vapi.cli("clear trace")
         tx = self.create_stream_labelled_ip4(
-            self.pg0, [35], ping=1, ip_itf=self.pg1)
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        packet_count = self.get_packet_count_for_if_idx(self.pg0.sw_if_index)
-        rx = self.pg1.get_capture(packet_count)
+            self.pg0, [VppMplsLabel(35)], ping=1, ip_itf=self.pg1)
+        rx = self.send_and_expect(self.pg0, tx, self.pg1)
         self.verify_capture_ip4(self.pg1, rx, tx, ping_resp=1)
 
         #
@@ -902,15 +1027,11 @@ class TestMPLS(VppTestCase):
                                                    0xffffffff)])
         route_36_neos.add_vpp_config()
 
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip4(self.pg0, [36, 35],
+        tx = self.create_stream_labelled_ip4(self.pg0,
+                                             [VppMplsLabel(36),
+                                              VppMplsLabel(35)],
                                              ping=1, ip_itf=self.pg1)
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg1.get_capture(len(tx))
+        rx = self.send_and_expect(self.pg0, tx, self.pg1)
         self.verify_capture_ip4(self.pg1, rx, tx, ping_resp=1)
 
         route_36_neos.remove_vpp_config()
@@ -948,15 +1069,10 @@ class TestMPLS(VppTestCase):
         # ping an interface in the default table
         # PG0 is in the default table
         #
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip4(self.pg0, [34], n=257,
+        tx = self.create_stream_labelled_ip4(self.pg0,
+                                             [VppMplsLabel(34)],
                                              dst_ip="10.0.0.1")
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg1.get_capture(257)
+        rx = self.send_and_expect(self.pg0, tx, self.pg1)
         self.verify_capture_ip4(self.pg1, rx, tx)
 
     def test_mcast_mid_point(self):
@@ -976,17 +1092,18 @@ class TestMPLS(VppTestCase):
         # Add a mcast entry that replicate to pg2 and pg3
         # and replicate to a interface-rx (like a bud node would)
         #
-        route_3400_eos = VppMplsRoute(self, 3400, 1,
-                                      [VppRoutePath(self.pg2.remote_ip4,
-                                                    self.pg2.sw_if_index,
-                                                    labels=[3401]),
-                                       VppRoutePath(self.pg3.remote_ip4,
-                                                    self.pg3.sw_if_index,
-                                                    labels=[3402]),
-                                       VppRoutePath("0.0.0.0",
-                                                    self.pg1.sw_if_index,
-                                                    is_interface_rx=1)],
-                                      is_multicast=1)
+        route_3400_eos = VppMplsRoute(
+            self, 3400, 1,
+            [VppRoutePath(self.pg2.remote_ip4,
+                          self.pg2.sw_if_index,
+                          labels=[VppMplsLabel(3401)]),
+             VppRoutePath(self.pg3.remote_ip4,
+                          self.pg3.sw_if_index,
+                          labels=[VppMplsLabel(3402)]),
+             VppRoutePath("0.0.0.0",
+                          self.pg1.sw_if_index,
+                          is_interface_rx=1)],
+            is_multicast=1)
         route_3400_eos.add_vpp_config()
 
         #
@@ -994,7 +1111,9 @@ class TestMPLS(VppTestCase):
         # PG0 is in the default table
         #
         self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip4(self.pg0, [3400], n=257,
+        tx = self.create_stream_labelled_ip4(self.pg0,
+                                             [VppMplsLabel(3400, ttl=64)],
+                                             n=257,
                                              dst_ip="10.0.0.1")
         self.pg0.add_stream(tx)
 
@@ -1005,9 +1124,11 @@ class TestMPLS(VppTestCase):
         self.verify_capture_ip4(self.pg1, rx, tx)
 
         rx = self.pg2.get_capture(257)
-        self.verify_capture_labelled(self.pg2, rx, tx, [3401])
+        self.verify_capture_labelled(self.pg2, rx, tx,
+                                     [VppMplsLabel(3401, ttl=63)])
         rx = self.pg3.get_capture(257)
-        self.verify_capture_labelled(self.pg3, rx, tx, [3402])
+        self.verify_capture_labelled(self.pg3, rx, tx,
+                                     [VppMplsLabel(3402, ttl=63)])
 
     def test_mcast_head(self):
         """ MPLS Multicast Head-end """
@@ -1015,14 +1136,15 @@ class TestMPLS(VppTestCase):
         #
         # Create a multicast tunnel with two replications
         #
-        mpls_tun = VppMPLSTunnelInterface(self,
-                                          [VppRoutePath(self.pg2.remote_ip4,
-                                                        self.pg2.sw_if_index,
-                                                        labels=[42]),
-                                           VppRoutePath(self.pg3.remote_ip4,
-                                                        self.pg3.sw_if_index,
-                                                        labels=[43])],
-                                          is_multicast=1)
+        mpls_tun = VppMPLSTunnelInterface(
+            self,
+            [VppRoutePath(self.pg2.remote_ip4,
+                          self.pg2.sw_if_index,
+                          labels=[VppMplsLabel(42)]),
+             VppRoutePath(self.pg3.remote_ip4,
+                          self.pg3.sw_if_index,
+                          labels=[VppMplsLabel(43)])],
+            is_multicast=1)
         mpls_tun.add_vpp_config()
         mpls_tun.admin_up()
 
@@ -1042,9 +1164,9 @@ class TestMPLS(VppTestCase):
         self.pg_start()
 
         rx = self.pg2.get_capture(257)
-        self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [42])
+        self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(42)])
         rx = self.pg3.get_capture(257)
-        self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [43])
+        self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(43)])
 
         #
         # An an IP multicast route via the tunnel
@@ -1070,9 +1192,9 @@ class TestMPLS(VppTestCase):
         self.pg_start()
 
         rx = self.pg2.get_capture(257)
-        self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [42])
+        self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(42)])
         rx = self.pg3.get_capture(257)
-        self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [43])
+        self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(43)])
 
     def test_mcast_ip4_tail(self):
         """ MPLS IPv4 Multicast Tail """
@@ -1112,7 +1234,7 @@ class TestMPLS(VppTestCase):
         # Drop due to interface lookup miss
         #
         self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip4(self.pg0, [34],
+        tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(34)],
                                              dst_ip="232.1.1.1", n=1)
         self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop none")
 
@@ -1121,30 +1243,24 @@ class TestMPLS(VppTestCase):
         #
         route_232_1_1_1.update_rpf_id(55)
 
-        self.vapi.cli("clear trace")
-        tx = self.create_stream_labelled_ip4(self.pg0, [34],
-                                             dst_ip="232.1.1.1", n=257)
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg1.get_capture(257)
+        tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(34)],
+                                             dst_ip="232.1.1.1")
+        rx = self.send_and_expect(self.pg0, tx, self.pg1)
         self.verify_capture_ip4(self.pg1, rx, tx)
 
         #
         # disposed packets have an invalid IPv4 checkusm
         #
-        tx = self.create_stream_labelled_ip4(self.pg0, [34],
+        tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(34)],
                                              dst_ip="232.1.1.1", n=65,
                                              chksum=1)
         self.send_and_assert_no_replies(self.pg0, tx, "Invalid Checksum")
 
         #
-        # set the RPF-ID of the enrtry to not match the input packet's
+        # set the RPF-ID of the entry to not match the input packet's
         #
         route_232_1_1_1.update_rpf_id(56)
-        tx = self.create_stream_labelled_ip4(self.pg0, [34],
+        tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(34)],
                                              dst_ip="232.1.1.1")
         self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
 
@@ -1188,42 +1304,36 @@ class TestMPLS(VppTestCase):
         #
         # Drop due to interface lookup miss
         #
-        tx = self.create_stream_labelled_ip6(self.pg0, [34], 255,
+        tx = self.create_stream_labelled_ip6(self.pg0, [VppMplsLabel(34)],
                                              dst_ip="ff01::1")
+        self.send_and_assert_no_replies(self.pg0, tx, "RPF Miss")
 
         #
         # set the RPF-ID of the enrtry to match the input packet's
         #
         route_ff.update_rpf_id(55)
 
-        tx = self.create_stream_labelled_ip6(self.pg0, [34], 255,
+        tx = self.create_stream_labelled_ip6(self.pg0, [VppMplsLabel(34)],
                                              dst_ip="ff01::1")
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg1.get_capture(257)
+        rx = self.send_and_expect(self.pg0, tx, self.pg1)
         self.verify_capture_ip6(self.pg1, rx, tx)
 
         #
         # disposed packets have hop-limit = 1
         #
-        tx = self.create_stream_labelled_ip6(self.pg0, [34], 255,
-                                             dst_ip="ff01::1", hlim=1)
-        self.pg0.add_stream(tx)
-
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx = self.pg0.get_capture(257)
+        tx = self.create_stream_labelled_ip6(self.pg0,
+                                             [VppMplsLabel(34)],
+                                             dst_ip="ff01::1",
+                                             hlim=1)
+        rx = self.send_and_expect(self.pg0, tx, self.pg0)
         self.verify_capture_ip6_icmp(self.pg0, rx, tx)
 
         #
         # set the RPF-ID of the enrtry to not match the input packet's
         #
         route_ff.update_rpf_id(56)
-        tx = self.create_stream_labelled_ip6(self.pg0, [34], 225,
+        tx = self.create_stream_labelled_ip6(self.pg0,
+                                             [VppMplsLabel(34)],
                                              dst_ip="ff01::1")
         self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
 
@@ -1709,11 +1819,7 @@ class TestMPLSL2(VppTestCase):
         self.pg0.admin_down()
         super(TestMPLSL2, self).tearDown()
 
-    def verify_capture_tunneled_ethernet(self, capture, sent, mpls_labels,
-                                         ttl=255, top=None):
-        if top is None:
-            top = len(mpls_labels) - 1
-
+    def verify_capture_tunneled_ethernet(self, capture, sent, mpls_labels):
         capture = verify_filter(capture, sent)
 
         self.assertEqual(len(capture), len(sent))
@@ -1723,7 +1829,7 @@ class TestMPLSL2(VppTestCase):
             rx = capture[i]
 
             # the MPLS TTL is 255 since it enters a new tunnel
-            verify_mpls_stack(self, rx, mpls_labels, ttl, top)
+            verify_mpls_stack(self, rx, mpls_labels)
 
             tx_eth = tx[Ether]
             rx_eth = Ether(str(rx[MPLS].payload))
@@ -1736,12 +1842,15 @@ class TestMPLSL2(VppTestCase):
 
         #
         # Create an MPLS tunnel that pushes 1 label
+        # For Ethernet over MPLS the uniform mode is irrelevant since ttl/cos
+        # information is not in the packet, but we test it works anyway
         #
-        mpls_tun_1 = VppMPLSTunnelInterface(self,
-                                            [VppRoutePath(self.pg0.remote_ip4,
-                                                          self.pg0.sw_if_index,
-                                                          labels=[42])],
-                                            is_l2=1)
+        mpls_tun_1 = VppMPLSTunnelInterface(
+            self,
+            [VppRoutePath(self.pg0.remote_ip4,
+                          self.pg0.sw_if_index,
+                          labels=[VppMplsLabel(42, MplsLspMode.UNIFORM)])],
+            is_l2=1)
         mpls_tun_1.add_vpp_config()
         mpls_tun_1.admin_up()
 
@@ -1778,37 +1887,32 @@ class TestMPLSL2(VppTestCase):
                  UDP(sport=1234, dport=1234) /
                  Raw('\xa5' * 100))
 
-        self.pg0.add_stream(pcore * 65)
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
+        tx0 = pcore * 65
+        rx0 = self.send_and_expect(self.pg0, tx0, self.pg1)
+        payload = pcore[MPLS].payload
 
-        rx0 = self.pg1.get_capture(65)
-        tx = pcore[MPLS].payload
-
-        self.assertEqual(rx0[0][Ether].dst, tx[Ether].dst)
-        self.assertEqual(rx0[0][Ether].src, tx[Ether].src)
+        self.assertEqual(rx0[0][Ether].dst, payload[Ether].dst)
+        self.assertEqual(rx0[0][Ether].src, payload[Ether].src)
 
         #
         # Inject a packet from the custoer/L2 side
         #
-        self.pg1.add_stream(tx * 65)
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        rx0 = self.pg0.get_capture(65)
+        tx1 = pcore[MPLS].payload * 65
+        rx1 = self.send_and_expect(self.pg1, tx1, self.pg0)
 
-        self.verify_capture_tunneled_ethernet(rx0, tx*65, [42])
+        self.verify_capture_tunneled_ethernet(rx1, tx1, [VppMplsLabel(42)])
 
     def test_vpls(self):
         """ Virtual Private LAN Service """
         #
         # Create an L2 MPLS tunnel
         #
-        mpls_tun = VppMPLSTunnelInterface(self,
-                                          [VppRoutePath(self.pg0.remote_ip4,
-                                                        self.pg0.sw_if_index,
-                                                        labels=[42])],
-                                          is_l2=1)
+        mpls_tun = VppMPLSTunnelInterface(
+            self,
+            [VppRoutePath(self.pg0.remote_ip4,
+                          self.pg0.sw_if_index,
+                          labels=[VppMplsLabel(42)])],
+            is_l2=1)
         mpls_tun.add_vpp_config()
         mpls_tun.admin_up()
 
@@ -1875,7 +1979,8 @@ class TestMPLSL2(VppTestCase):
 
         rx0 = self.pg0.get_capture(65)
 
-        self.verify_capture_tunneled_ethernet(rx0, p_cust*65, [42])
+        self.verify_capture_tunneled_ethernet(rx0, p_cust*65,
+                                              [VppMplsLabel(42)])
 
         #
         # remove interfaces from customers bridge-domain
index 68b023c..322d813 100644 (file)
@@ -2,7 +2,7 @@
 
 from framework import VppTestCase, VppTestRunner
 from vpp_udp_encap import *
-from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable
+from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, VppMplsLabel
 
 from scapy.packet import Raw
 from scapy.layers.l2 import Ether, ARP
@@ -67,12 +67,12 @@ class TestUdpEncap(VppTestCase):
         self.assertEqual(rx[UDP].dport, encap_obj.dst_port)
 
     def validate_inner4(self, rx, tx, ttl=None):
-        self.assertEqual(rx.src, tx[IP].src)
-        self.assertEqual(rx.dst, tx[IP].dst)
+        self.assertEqual(rx[IP].src, tx[IP].src)
+        self.assertEqual(rx[IP].dst, tx[IP].dst)
         if ttl:
-            self.assertEqual(rx.ttl, ttl)
+            self.assertEqual(rx[IP].ttl, ttl)
         else:
-            self.assertEqual(rx.ttl, tx[IP].ttl)
+            self.assertEqual(rx[IP].ttl, tx[IP].ttl)
 
     def validate_inner6(self, rx, tx):
         self.assertEqual(rx.src, tx[IPv6].src)
@@ -208,7 +208,7 @@ class TestUdpEncap(VppTestCase):
                                                   0xFFFFFFFF,
                                                   is_udp_encap=1,
                                                   next_hop_id=1,
-                                                  labels=[66])])
+                                                  labels=[VppMplsLabel(66)])])
         route_4oMPLSo4.add_vpp_config()
 
         p_4omo4 = (Ether(src=self.pg0.remote_mac,
index ef9a9ab..7566b1f 100644 (file)
@@ -4,7 +4,7 @@
 
 import socket
 from vpp_object import VppObject
-from vpp_ip_route import MPLS_LABEL_INVALID, VppRoutePath
+from vpp_ip_route import MPLS_LABEL_INVALID, VppRoutePath, VppMplsLabel
 
 
 class BIER_HDR_PAYLOAD:
@@ -120,11 +120,34 @@ class VppBierRoute(VppObject):
         self.bp = bp
         self.paths = paths
 
+    def encode_paths(self):
+        br_paths = []
+        for p in self.paths:
+            lstack = []
+            for l in p.nh_labels:
+                if type(l) == VppMplsLabel:
+                    lstack.append(l.encode())
+                else:
+                    lstack.append({'label': l, 'ttl': 255})
+            n_labels = len(lstack)
+            while (len(lstack) < 16):
+                lstack.append({})
+            br_paths.append({'next_hop': p.nh_addr,
+                             'weight': 1,
+                             'afi': 0,
+                             'preference': 0,
+                             'table_id': p.nh_table_id,
+                             'next_hop_id': p.next_hop_id,
+                             'is_udp_encap': p.is_udp_encap,
+                             'n_labels': n_labels,
+                             'label_stack': lstack})
+        return br_paths
+
     def add_vpp_config(self):
         self._test.vapi.bier_route_add_del(
             self.tbl_id,
             self.bp,
-            self.paths,
+            self.encode_paths(),
             is_add=1)
         self._test.registry.register(self, self._test.logger)
 
@@ -132,7 +155,7 @@ class VppBierRoute(VppObject):
         self._test.vapi.bier_route_add_del(
             self.tbl_id,
             self.bp,
-            self.paths,
+            self.encode_paths(),
             is_add=0)
 
     def __str__(self):
index 2d34f55..ca0ae1a 100644 (file)
@@ -38,6 +38,11 @@ class DpoProto:
     DPO_PROTO_NSH = 5
 
 
+class MplsLspMode:
+    PIPE = 0
+    UNIFORM = 1
+
+
 def find_route(test, ip_addr, len, table_id=0, inet=AF_INET):
     if inet == AF_INET:
         s = 4
@@ -95,6 +100,21 @@ class VppIpTable(VppObject):
                  self.table_id))
 
 
+class VppMplsLabel(object):
+    def __init__(self, value, mode=MplsLspMode.PIPE, ttl=64, exp=0):
+        self.value = value
+        self.mode = mode
+        self.ttl = ttl
+        self.exp = exp
+
+    def encode(self):
+        is_uniform = 0 if self.mode is MplsLspMode.PIPE else 1
+        return {'label': self.value,
+                'ttl': self.ttl,
+                'exp': self.exp,
+                'is_uniform': is_uniform}
+
+
 class VppRoutePath(object):
 
     def __init__(
@@ -138,6 +158,16 @@ class VppRoutePath(object):
         self.next_hop_id = next_hop_id
         self.is_dvr = is_dvr
 
+    def encode_labels(self):
+        lstack = []
+        for l in self.nh_labels:
+            if type(l) == VppMplsLabel:
+                lstack.append(l.encode())
+            else:
+                lstack.append({'label': l,
+                               'ttl': 255})
+        return lstack
+
 
 class VppMRoutePath(VppRoutePath):
 
@@ -195,15 +225,16 @@ class VppIpRoute(VppObject):
                 is_ipv6=self.is_ip6)
         else:
             for path in self.paths:
+                lstack = path.encode_labels()
+
                 self._test.vapi.ip_add_del_route(
                     self.dest_addr,
                     self.dest_addr_len,
                     path.nh_addr,
                     path.nh_itf,
                     table_id=self.table_id,
-                    next_hop_out_label_stack=path.nh_labels,
-                    next_hop_n_out_labels=len(
-                        path.nh_labels),
+                    next_hop_out_label_stack=lstack,
+                    next_hop_n_out_labels=len(lstack),
                     next_hop_via_label=path.nh_via_label,
                     next_hop_table_id=path.nh_table_id,
                     next_hop_id=path.next_hop_id,
@@ -513,6 +544,8 @@ class VppMplsRoute(VppObject):
     def add_vpp_config(self):
         is_multipath = len(self.paths) > 1
         for path in self.paths:
+            lstack = path.encode_labels()
+
             self._test.vapi.mpls_route_add_del(
                 self.local_label,
                 self.eos_bit,
@@ -524,9 +557,8 @@ class VppMplsRoute(VppObject):
                 table_id=self.table_id,
                 is_interface_rx=path.is_interface_rx,
                 is_rpf_id=path.is_rpf_id,
-                next_hop_out_label_stack=path.nh_labels,
-                next_hop_n_out_labels=len(
-                    path.nh_labels),
+                next_hop_out_label_stack=lstack,
+                next_hop_n_out_labels=len(lstack),
                 next_hop_via_label=path.nh_via_label,
                 next_hop_table_id=path.nh_table_id)
         self._test.registry.register(self, self._test.logger)
index 0542b05..c789c3f 100644 (file)
@@ -1,6 +1,6 @@
 
 from vpp_interface import VppInterface
-from vpp_ip_route import VppRoutePath
+from vpp_ip_route import VppRoutePath, VppMplsLabel
 import socket
 
 
@@ -21,6 +21,8 @@ class VppMPLSTunnelInterface(VppInterface):
     def add_vpp_config(self):
         self._sw_if_index = 0xffffffff
         for path in self.t_paths:
+            lstack = path.encode_labels()
+
             reply = self.test.vapi.mpls_tunnel_add_del(
                 self._sw_if_index,
                 1,  # IPv4 next-hop
@@ -28,8 +30,8 @@ class VppMPLSTunnelInterface(VppInterface):
                 path.nh_itf,
                 path.nh_table_id,
                 path.weight,
-                next_hop_out_label_stack=path.nh_labels,
-                next_hop_n_out_labels=len(path.nh_labels),
+                next_hop_out_label_stack=lstack,
+                next_hop_n_out_labels=len(lstack),
                 is_multicast=self.is_multicast,
                 l2_only=self.is_l2)
             self._sw_if_index = reply.sw_if_index
index 99e320b..18bb1f6 100644 (file)
@@ -1119,7 +1119,6 @@ class VppPapiProvider(object):
         :param next_hop_weight:  (Default value = 1)
 
         """
-
         return self.api(
             self.papi.mpls_route_add_del,
             {'mr_label': label,
@@ -2875,25 +2874,14 @@ class VppPapiProvider(object):
                            paths,
                            is_add=1):
         """ BIER Route add/del """
-        br_paths = []
-        for p in paths:
-            br_paths.append({'next_hop': p.nh_addr,
-                             'weight': 1,
-                             'afi': 0,
-                             'preference': 0,
-                             'table_id': p.nh_table_id,
-                             'next_hop_id': p.next_hop_id,
-                             'is_udp_encap': p.is_udp_encap,
-                             'n_labels': len(p.nh_labels),
-                             'label_stack': p.nh_labels})
         return self.api(
             self.papi.bier_route_add_del,
             {'br_tbl_id': {"bt_set": bti.set_id,
                            "bt_sub_domain": bti.sub_domain_id,
                            "bt_hdr_len_id": bti.hdr_len_id},
              'br_bp': bp,
-             'br_n_paths': len(br_paths),
-             'br_paths': br_paths,
+             'br_n_paths': len(paths),
+             'br_paths': paths,
              'br_is_add': is_add})
 
     def bier_route_dump(self, bti):
@@ -2950,6 +2938,9 @@ class VppPapiProvider(object):
                                 next_hop_is_ip4=1,
                                 is_add=1):
         """ BIER Route add/del """
+        lstack = []
+        while (len(lstack) < 16):
+            lstack.append({})
         return self.api(
             self.papi.bier_disp_entry_add_del,
             {'bde_tbl_id': bdti,
@@ -2961,7 +2952,7 @@ class VppPapiProvider(object):
                             'afi': next_hop_afi,
                             'rpf_id': next_hop_rpf_id,
                             'n_labels': 0,
-                            'label_stack': [0]}],
+                            'label_stack': lstack}],
              'bde_is_add': is_add})
 
     def bier_disp_entry_dump(self, bdti):