Labelled attached paths via an MPLS tunnel

Change-Id: Ic86617c9c3217122043656ce2ea70bb106df5b2d
Signed-off-by: Neale Ranns <nranns@cisco.com>
diff --git a/src/vnet/adj/adj.c b/src/vnet/adj/adj.c
index bf44383..f849691 100644
--- a/src/vnet/adj/adj.c
+++ b/src/vnet/adj/adj.c
@@ -64,6 +64,7 @@
     adj->ia_nh_proto = proto;
     adj->ia_flags = 0;
     adj->rewrite_header.sw_if_index = ~0;
+    adj->rewrite_header.flags = 0;
     adj->lookup_next_index = 0;
     adj->ia_delegates = NULL;
 
diff --git a/src/vnet/dpo/dpo.c b/src/vnet/dpo/dpo.c
index dfc2bd9..28aa0c2 100644
--- a/src/vnet/dpo/dpo.c
+++ b/src/vnet/dpo/dpo.c
@@ -189,6 +189,9 @@
 	    break;
 	case IP_LOOKUP_NEXT_MCAST:
 	    dpo->dpoi_type = DPO_ADJACENCY_MCAST;
+            break;
+	case IP_LOOKUP_NEXT_GLEAN:
+	    dpo->dpoi_type = DPO_ADJACENCY_GLEAN;
 	    break;
 	default:
 	    break;
diff --git a/src/vnet/fib/fib_path.c b/src/vnet/fib/fib_path.c
index 255f0dd..274b0ef 100644
--- a/src/vnet/fib/fib_path.c
+++ b/src/vnet/fib/fib_path.c
@@ -588,6 +588,30 @@
     }
 }
 
+static const adj_index_t
+fib_path_attached_get_adj (fib_path_t *path,
+                           vnet_link_t link)
+{
+    if (vnet_sw_interface_is_p2p(vnet_get_main(),
+                                 path->attached.fp_interface))
+    {
+        /*
+         * point-2-point interfaces do not require a glean, since
+         * there is nothing to ARP. Install a rewrite/nbr adj instead
+         */
+        return (adj_nbr_add_or_lock(path->fp_nh_proto,
+                                    link,
+                                    &zero_addr,
+                                    path->attached.fp_interface));
+    }
+    else
+    {
+        return (adj_glean_add_or_lock(path->fp_nh_proto,
+                                      path->attached.fp_interface,
+                                      NULL));
+    }
+}
+
 /*
  * create of update the paths recursive adj
  */
@@ -1559,31 +1583,12 @@
 	{
 	    path->fp_oper_flags &= ~FIB_PATH_OPER_FLAG_RESOLVED;
 	}
-	if (vnet_sw_interface_is_p2p(vnet_get_main(),
-				     path->attached.fp_interface))
-	{
-	    /*
-	     * point-2-point interfaces do not require a glean, since
-	     * there is nothing to ARP. Install a rewrite/nbr adj instead
-	     */
-	    dpo_set(&path->fp_dpo,
-		    DPO_ADJACENCY,
-		    fib_proto_to_dpo(path->fp_nh_proto),
-		    adj_nbr_add_or_lock(
-			path->fp_nh_proto,
-			fib_proto_to_link(path->fp_nh_proto),
-			&zero_addr,
-			path->attached.fp_interface));
-	}
-	else
-	{
-	    dpo_set(&path->fp_dpo,
-		    DPO_ADJACENCY_GLEAN,
-		    fib_proto_to_dpo(path->fp_nh_proto),
-		    adj_glean_add_or_lock(path->fp_nh_proto,
-					  path->attached.fp_interface,
-					  NULL));
-	}
+        dpo_set(&path->fp_dpo,
+                DPO_ADJACENCY,
+                fib_proto_to_dpo(path->fp_nh_proto),
+                fib_path_attached_get_adj(path,
+                                          fib_proto_to_link(path->fp_nh_proto)));
+
 	/*
 	 * become a child of the adjacency so we receive updates
 	 * when the interface state changes
@@ -1969,7 +1974,20 @@
 	    case FIB_FORW_CHAIN_TYPE_MPLS_EOS:
 	    case FIB_FORW_CHAIN_TYPE_ETHERNET:
 	    case FIB_FORW_CHAIN_TYPE_NSH:
-                break;
+                {
+                    adj_index_t ai;
+
+                    /*
+                     * get a appropriate link type adj.
+                     */
+                    ai = fib_path_attached_get_adj(
+                            path,
+                            fib_forw_chain_type_to_link_type(fct));
+                    dpo_set(dpo, DPO_ADJACENCY,
+                            fib_forw_chain_type_to_dpo_proto(fct), ai);
+                    adj_unlock(ai);
+                    break;
+                }
 	    case FIB_FORW_CHAIN_TYPE_MCAST_IP4:
 	    case FIB_FORW_CHAIN_TYPE_MCAST_IP6:
                 {
diff --git a/src/vnet/fib/fib_walk.c b/src/vnet/fib/fib_walk.c
index c570476..7018013 100644
--- a/src/vnet/fib/fib_walk.c
+++ b/src/vnet/fib/fib_walk.c
@@ -322,10 +322,10 @@
 static fib_walk_advance_rc_t
 fib_walk_advance (fib_node_index_t fwi)
 {
-    fib_node_back_walk_ctx_t *ctx, *old;
     fib_node_back_walk_rc_t wrc;
     fib_node_ptr_t sibling;
     fib_walk_t *fwalk;
+    uint n_ctxs, ii;
     int more_elts;
 
     /*
@@ -339,12 +339,20 @@
 
     if (more_elts)
     {
-        old = fwalk->fw_ctx;
 
-	vec_foreach(ctx, fwalk->fw_ctx)
-	{
-	    wrc = fib_node_back_walk_one(&sibling, ctx);
+        /*
+         * loop through the backwalk contexts. This can grow in length
+         * as walks on the same object meet each other. Order is preserved so the
+         * most recently started walk as at the back of the vector.
+         */
+        ii = 0;
+        n_ctxs = vec_len(fwalk->fw_ctx);
 
+        while (ii < n_ctxs)
+        {
+	    wrc = fib_node_back_walk_one(&sibling, &fwalk->fw_ctx[ii]);
+
+            ii++;
 	    fwalk = fib_walk_get(fwi);
 	    fwalk->fw_n_visits++;
 
@@ -356,14 +364,11 @@
 		 */
 		return (FIB_WALK_ADVANCE_MERGE);
 	    }
-            if (old != fwalk->fw_ctx)
-            {
-                /*
-                 * nasty re-entrant addition of a walk has realloc'd the vector
-                 * break out
-                 */
-		return (FIB_WALK_ADVANCE_MERGE);
-	    }
+
+            /*
+             * re-evaluate the number of backwalk contexts we need to process.
+             */
+            n_ctxs = vec_len(fwalk->fw_ctx);
 	}
 	/*
 	 * move foward to the next node to visit
diff --git a/src/vnet/mpls/mpls_tunnel.c b/src/vnet/mpls/mpls_tunnel.c
index d6e85e7..776b23b 100644
--- a/src/vnet/mpls/mpls_tunnel.c
+++ b/src/vnet/mpls/mpls_tunnel.c
@@ -273,7 +273,9 @@
 
         mpls_tunnel_mk_lb(mt,
                           adj->ia_link,
-                          FIB_FORW_CHAIN_TYPE_MPLS_EOS,
+                          (VNET_LINK_MPLS == adj_get_link_type(ai) ?
+                           FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS:
+                           FIB_FORW_CHAIN_TYPE_MPLS_EOS),
                           &dpo);
 
         adj_nbr_midchain_stack(ai, &dpo);