VPP-196 LISP L2/L3 tunnel multihoming
[vpp.git] / vnet / vnet / lisp-gpe / ip_forward.c
index 83c52e3..47f3f7b 100644 (file)
@@ -109,6 +109,16 @@ ip4_sd_fib_add_del_src_route (lisp_gpe_main_t * lgm,
 
   old_adj_index = fib->old_hash_values[0];
 
+  /* Avoid spurious reference count increments */
+  if (old_adj_index == adj_index
+      && adj_index != ~0
+      && !(a->flags & IP4_ROUTE_FLAG_KEEP_OLD_ADJACENCY))
+    {
+      ip_adjacency_t * adj = ip_get_adjacency (lm, adj_index);
+      if (adj->share_count > 0)
+        adj->share_count --;
+    }
+
   ip4_fib_mtrie_add_del_route (fib, a->dst_address, dst_address_length,
                                is_del ? old_adj_index : adj_index,
                                is_del);
@@ -174,6 +184,26 @@ ip4_sd_fib_clear_src_fib (lisp_gpe_main_t * lgm, ip4_fib_t * fib)
   }
 }
 
+static u8
+ip4_fib_is_empty (ip4_fib_t * fib)
+{
+  u8 fib_is_empty;
+  int i;
+
+  fib_is_empty = 1;
+  for (i = ARRAY_LEN (fib->adj_index_by_dst_address) - 1; i >= 0; i--)
+    {
+      uword * hash = fib->adj_index_by_dst_address[i];
+      uword n_elts = hash_elts (hash);
+      if (n_elts)
+        {
+          fib_is_empty = 0;
+          break;
+        }
+    }
+  return fib_is_empty;
+}
+
 static int
 ip4_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix,
                           ip_prefix_t * src_prefix, u32 table_id,
@@ -202,10 +232,29 @@ ip4_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix,
       /* insert dst prefix to ip4 fib, if it's not in yet */
       if (p == 0)
         {
+          /* allocate and init src ip4 fib */
+          pool_get(lgm->ip4_src_fibs, src_fib);
+          ip4_mtrie_init (&src_fib->mtrie);
+
+          /* configure adjacency */
+          memset(&dst_adj, 0, sizeof(dst_adj));
+
+          /* reuse rewrite header to store pointer to src fib */
+          dst_adj.rewrite_header.sw_if_index = src_fib - lgm->ip4_src_fibs;
+
           /* dst adj should point to lisp gpe lookup */
-          dst_adj = add_adj[0];
           dst_adj.lookup_next_index = lgm->ip4_lookup_next_lgpe_ip4_lookup;
 
+          /* explicit_fib_index is used in IP6 FIB lookup, don't reuse it */
+          dst_adj.explicit_fib_index = ~0;
+          dst_adj.n_adj = 1;
+
+          /* make sure we have different signatures for adj in different tables
+           * but with the same lookup_next_index and for adj in the same table
+           * but associated to different destinations */
+          dst_adj.if_address_index = table_id;
+          dst_adj.indirect.next_hop.ip4 = dst;
+
           memset(&a, 0, sizeof(a));
           a.flags = IP4_ROUTE_FLAG_TABLE_ID;
           a.table_index_or_table_id = table_id; /* vrf */
@@ -221,21 +270,15 @@ ip4_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix,
           /* lookup dst adj to obtain the adj index */
           p = ip4_get_route (lgm->im4, table_id, 0, dst.as_u8,
                              dst_address_length);
-          if (p == 0)
+
+          /* make sure insertion succeeded */
+          if (CLIB_DEBUG)
             {
-              clib_warning("Failed to insert dst route for eid %U!",
-                           format_ip4_address_and_length, dst.as_u8,
-                           dst_address_length);
-              return -1;
+              ASSERT(p != 0);
+              dst_adjp = ip_get_adjacency (lgm->lm4, p[0]);
+              ASSERT(dst_adjp->rewrite_header.sw_if_index
+                      == dst_adj.rewrite_header.sw_if_index);
             }
-
-          /* allocate and init src ip4 fib */
-          pool_get(lgm->ip4_src_fibs, src_fib);
-          ip4_mtrie_init (&src_fib->mtrie);
-
-          /* reuse rewrite header to store pointer to src fib */
-          dst_adjp = ip_get_adjacency (lgm->lm4, p[0]);
-          dst_adjp->rewrite_header.sw_if_index = src_fib - lgm->ip4_src_fibs;
         }
     }
   else
@@ -258,12 +301,24 @@ ip4_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix,
   a.adj_index = ~0;
   a.flags |= is_add ? IP4_ROUTE_FLAG_ADD : IP4_ROUTE_FLAG_DEL;
   a.add_adj = add_adj;
-  a.n_add_adj = 1;
+  a.n_add_adj = is_add ? 1 : 0;
   /* if src prefix is null, add 0/0 */
   a.dst_address_length = src_address_length;
   a.dst_address = src;
   ip4_sd_fib_add_del_src_route (lgm, &a);
 
+  /* make sure insertion succeeded */
+  if (CLIB_DEBUG && is_add)
+    {
+      uword * sai;
+      ip_adjacency_t * src_adjp;
+      sai = ip4_sd_get_src_route (lgm, dst_adjp->rewrite_header.sw_if_index,
+                                  &src, src_address_length);
+      ASSERT(sai != 0);
+      src_adjp = ip_get_adjacency(lgm->lm4, sai[0]);
+      ASSERT(src_adjp->if_address_index == add_adj->if_address_index);
+    }
+
   /* if a delete, check if there are elements left in the src fib */
   if (!is_add)
     {
@@ -273,7 +328,7 @@ ip4_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix,
         return 0;
 
       /* if there's nothing left */
-      if (ARRAY_LEN(src_fib->adj_index_by_dst_address) == 0)
+      if (ip4_fib_is_empty(src_fib))
         {
           /* remove the src fib ..  */
           pool_put(lgm->ip4_src_fibs, src_fib);
@@ -325,30 +380,23 @@ static u32
 ip6_sd_get_src_route (lisp_gpe_main_t * lgm, u32 src_fib_index,
                       ip6_address_t * src, u32 address_length)
 {
-  int i, len;
   int rv;
   BVT(clib_bihash_kv) kv, value;
   ip6_src_fib_t * fib = pool_elt_at_index (lgm->ip6_src_fibs, src_fib_index);
 
-  len = vec_len (fib->prefix_lengths_in_search_order);
-
-  for (i = 0; i < len; i++)
-    {
-      int dst_address_length = fib->prefix_lengths_in_search_order[i];
-      ip6_address_t * mask;
+  ip6_address_t * mask;
 
-      ASSERT(dst_address_length >= 0 && dst_address_length <= 128);
+  ASSERT(address_length <= 128);
 
-      mask = &fib->fib_masks[dst_address_length];
+  mask = &fib->fib_masks[address_length];
 
-      kv.key[0] = src->as_u64[0] & mask->as_u64[0];
-      kv.key[1] = src->as_u64[1] & mask->as_u64[1];
-      kv.key[2] = dst_address_length;
+  kv.key[0] = src->as_u64[0] & mask->as_u64[0];
+  kv.key[1] = src->as_u64[1] & mask->as_u64[1];
+  kv.key[2] = address_length;
 
-      rv = BV(clib_bihash_search_inline_2)(&fib->ip6_lookup_table, &kv, &value);
-      if (rv == 0)
-        return value.value;
-    }
+  rv = BV(clib_bihash_search_inline_2)(&fib->ip6_lookup_table, &kv, &value);
+  if (rv == 0)
+    return value.value;
 
   return 0;
 }
@@ -519,10 +567,29 @@ ip6_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix,
       /* insert dst prefix to ip6 fib, if it's not in yet */
       if (adj_index == 0)
         {
+          /* allocate and init src ip6 fib */
+          pool_get(lgm->ip6_src_fibs, src_fib);
+          memset(src_fib, 0, sizeof(src_fib[0]));
+          ip6_src_fib_init (src_fib);
+
+          memset(&dst_adj, 0, sizeof(dst_adj));
+
+          /* reuse rewrite header to store pointer to src fib */
+          dst_adj.rewrite_header.sw_if_index = src_fib - lgm->ip6_src_fibs;
+
           /* dst adj should point to lisp gpe ip lookup */
-          dst_adj = add_adj[0];
           dst_adj.lookup_next_index = lgm->ip6_lookup_next_lgpe_ip6_lookup;
 
+          /* explicit_fib_index is used in IP6 FIB lookup, don't reuse it */
+          dst_adj.explicit_fib_index = ~0;
+          dst_adj.n_adj = 1;
+
+          /* make sure we have different signatures for adj in different tables
+           * but with the same lookup_next_index and for adj in the same table
+           * but associated to different destinations */
+          dst_adj.if_address_index = table_id;
+          dst_adj.indirect.next_hop.ip6 = dst;
+
           memset(&a, 0, sizeof(a));
           a.flags = IP6_ROUTE_FLAG_TABLE_ID;
           a.table_index_or_table_id = table_id; /* vrf */
@@ -539,16 +606,14 @@ ip6_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix,
           adj_index = ip6_get_route (lgm->im6, table_id, 0, &dst,
                                      dst_address_length);
 
-          ASSERT(adj_index != 0);
-
-          /* allocate and init src ip6 fib */
-          pool_get(lgm->ip6_src_fibs, src_fib);
-          memset(src_fib, 0, sizeof(src_fib[0]));
-          ip6_src_fib_init (src_fib);
-
-          /* reuse rewrite header to store pointer to src fib */
-          dst_adjp = ip_get_adjacency (lgm->lm6, adj_index);
-          dst_adjp->rewrite_header.sw_if_index = src_fib - lgm->ip6_src_fibs;
+          /* make sure insertion succeeded */
+          if (CLIB_DEBUG)
+            {
+              ASSERT(adj_index != 0);
+              dst_adjp = ip_get_adjacency (lgm->lm6, adj_index);
+              ASSERT(dst_adjp->rewrite_header.sw_if_index
+                      == dst_adj.rewrite_header.sw_if_index);
+            }
         }
     }
   else
@@ -556,8 +621,7 @@ ip6_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix,
       if (adj_index == 0)
         {
           clib_warning("Trying to delete inexistent dst route for %U. Aborting",
-                       format_ip6_address_and_length, dst.as_u8,
-                       dst_address_length);
+                       format_ip_prefix, dst_prefix);
           return -1;
         }
     }
@@ -571,12 +635,24 @@ ip6_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix,
   a.adj_index = ~0;
   a.flags |= is_add ? IP6_ROUTE_FLAG_ADD : IP6_ROUTE_FLAG_DEL;
   a.add_adj = add_adj;
-  a.n_add_adj = 1;
+  a.n_add_adj = is_add ? 1 : 0;
   /* if src prefix is null, add ::0 */
   a.dst_address_length = src_address_length;
   a.dst_address = src;
   ip6_sd_fib_add_del_src_route (lgm, &a);
 
+  /* make sure insertion succeeded */
+  if (CLIB_DEBUG && is_add)
+    {
+      u32 sai;
+      ip_adjacency_t * src_adjp;
+      sai = ip6_sd_get_src_route (lgm, dst_adjp->rewrite_header.sw_if_index,
+                                  &src, src_address_length);
+      ASSERT(sai != 0);
+      src_adjp = ip_get_adjacency(lgm->lm6, sai);
+      ASSERT(src_adjp->if_address_index == add_adj->if_address_index);
+    }
+
   /* if a delete, check if there are elements left in the src fib */
   if (!is_add)
     {
@@ -669,17 +745,24 @@ ip4_src_fib_lookup_one (lisp_gpe_main_t * lgm, u32 src_fib_index0,
   ip4_fib_mtrie_leaf_t leaf0, leaf1;
   ip4_fib_mtrie_t * mtrie0;
 
-  mtrie0 = &vec_elt_at_index(lgm->ip4_src_fibs, src_fib_index0)->mtrie;
-
-  leaf0 = leaf1 = IP4_FIB_MTRIE_LEAF_ROOT;
-  leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 0);
-  leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 1);
-  leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 2);
-  leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 3);
-
-  /* Handle default route. */
-  leaf0 = (leaf0 == IP4_FIB_MTRIE_LEAF_EMPTY ? mtrie0->default_leaf : leaf0);
-  src_adj_index0[0] = ip4_fib_mtrie_leaf_get_adj_index (leaf0);
+  /* if default route not hit in ip4 lookup */
+  if (PREDICT_TRUE(src_fib_index0 != (u32 ) ~0))
+    {
+      mtrie0 = &vec_elt_at_index(lgm->ip4_src_fibs, src_fib_index0)->mtrie;
+
+      leaf0 = leaf1 = IP4_FIB_MTRIE_LEAF_ROOT;
+      leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 0);
+      leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 1);
+      leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 2);
+      leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 3);
+
+      /* Handle default route. */
+      leaf0 = (leaf0 == IP4_FIB_MTRIE_LEAF_EMPTY) ?
+          mtrie0->default_leaf : leaf0;
+      src_adj_index0[0] = ip4_fib_mtrie_leaf_get_adj_index (leaf0);
+    }
+  else
+    src_adj_index0[0] = ~0;
 }
 
 always_inline void
@@ -691,28 +774,39 @@ ip4_src_fib_lookup_two (lisp_gpe_main_t * lgm, u32 src_fib_index0,
   ip4_fib_mtrie_leaf_t leaf0, leaf1;
   ip4_fib_mtrie_t * mtrie0, * mtrie1;
 
-  mtrie0 = &vec_elt_at_index(lgm->ip4_src_fibs, src_fib_index0)->mtrie;
-  mtrie1 = &vec_elt_at_index(lgm->ip4_src_fibs, src_fib_index1)->mtrie;
+  /* if default route not hit in ip4 lookup */
+  if (PREDICT_TRUE(src_fib_index0 != (u32 ) ~0 && src_fib_index1 != (u32 ) ~0))
+    {
+      mtrie0 = &vec_elt_at_index(lgm->ip4_src_fibs, src_fib_index0)->mtrie;
+      mtrie1 = &vec_elt_at_index(lgm->ip4_src_fibs, src_fib_index1)->mtrie;
 
-  leaf0 = leaf1 = IP4_FIB_MTRIE_LEAF_ROOT;
+      leaf0 = leaf1 = IP4_FIB_MTRIE_LEAF_ROOT;
 
-  leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 0);
-  leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, addr1, 0);
+      leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 0);
+      leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, addr1, 0);
 
-  leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 1);
-  leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, addr1, 1);
+      leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 1);
+      leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, addr1, 1);
 
-  leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 2);
-  leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, addr1, 2);
+      leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 2);
+      leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, addr1, 2);
 
-  leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 3);
-  leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, addr1, 3);
+      leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 3);
+      leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, addr1, 3);
 
-  /* Handle default route. */
-  leaf0 = (leaf0 == IP4_FIB_MTRIE_LEAF_EMPTY ? mtrie0->default_leaf : leaf0);
-  leaf1 = (leaf1 == IP4_FIB_MTRIE_LEAF_EMPTY ? mtrie1->default_leaf : leaf1);
-  src_adj_index0[0] = ip4_fib_mtrie_leaf_get_adj_index (leaf0);
-  src_adj_index1[0] = ip4_fib_mtrie_leaf_get_adj_index (leaf1);
+      /* Handle default route. */
+      leaf0 = (leaf0 == IP4_FIB_MTRIE_LEAF_EMPTY) ?
+              mtrie0->default_leaf : leaf0;
+      leaf1 = (leaf1 == IP4_FIB_MTRIE_LEAF_EMPTY) ?
+              mtrie1->default_leaf : leaf1;
+      src_adj_index0[0] = ip4_fib_mtrie_leaf_get_adj_index (leaf0);
+      src_adj_index1[0] = ip4_fib_mtrie_leaf_get_adj_index (leaf1);
+    }
+  else
+    {
+      ip4_src_fib_lookup_one (lgm, src_fib_index0, addr0, src_adj_index0);
+      ip4_src_fib_lookup_one (lgm, src_fib_index1, addr1, src_adj_index1);
+    }
 }
 
 always_inline uword
@@ -738,8 +832,8 @@ lgpe_ip4_lookup (vlib_main_t * vm, vlib_node_runtime_t * node,
           u32 bi0, bi1;
           vlib_buffer_t * b0, * b1;
           ip4_header_t * ip0, * ip1;
-          u32 dst_adj_index0, src_adj_index0, src_fib_index0, dst_adj_index1,
-              src_adj_index1, src_fib_index1;
+          u32 dst_adj_index0, src_adj_index0, src_fib_index0;
+          u32 dst_adj_index1, src_adj_index1, src_fib_index1;
           ip_adjacency_t * dst_adj0, * src_adj0, * dst_adj1, * src_adj1;
           u32 next0, next1;
 
@@ -784,53 +878,82 @@ lgpe_ip4_lookup (vlib_main_t * vm, vlib_node_runtime_t * node,
           src_fib_index0 = dst_adj0->rewrite_header.sw_if_index;
           src_fib_index1 = dst_adj1->rewrite_header.sw_if_index;
 
-          /* if default route not hit in ip4 lookup */
-          if (PREDICT_TRUE(src_fib_index0 != (u32) ~0
-                           && src_fib_index1 != (u32) ~0))
-            {
-              ip4_src_fib_lookup_two (lgm, src_fib_index0, src_fib_index1,
-                                      &ip0->src_address, &ip1->src_address,
-                                      &src_adj_index0, &src_adj_index1);
+          ip4_src_fib_lookup_two (lgm, src_fib_index0, src_fib_index1,
+                                  &ip0->src_address, &ip1->src_address,
+                                  &src_adj_index0, &src_adj_index1);
 
+          /* if a source fib exists */
+          if (PREDICT_TRUE((u32) ~0 != src_adj_index0
+                           && (u32) ~0 != src_adj_index1))
+            {
               vnet_buffer(b0)->ip.adj_index[VLIB_TX] = src_adj_index0;
               vnet_buffer(b1)->ip.adj_index[VLIB_TX] = src_adj_index1;
 
               src_adj0 = ip_get_adjacency (lgm->lm4, src_adj_index0);
               src_adj1 = ip_get_adjacency (lgm->lm4, src_adj_index1);
 
-              next0 = src_adj0->lookup_next_index;
-              next1 = src_adj1->lookup_next_index;
+              next0 = src_adj0->explicit_fib_index;
+              next1 = src_adj1->explicit_fib_index;
 
               /* prepare buffer for lisp-gpe output node */
               vnet_buffer (b0)->sw_if_index[VLIB_TX] =
                   src_adj0->rewrite_header.sw_if_index;
               vnet_buffer (b1)->sw_if_index[VLIB_TX] =
                   src_adj1->rewrite_header.sw_if_index;
+
+              /* if multipath: saved_lookup_next_index is reused to store
+               * nb of sub-tunnels. If greater than 1, multipath is on.
+               * Note that flow hash should be 0 after ipx lookup! */
+              if (PREDICT_TRUE(src_adj0->saved_lookup_next_index > 1))
+                vnet_buffer (b0)->ip.flow_hash = ip4_compute_flow_hash (
+                    ip0, IP_FLOW_HASH_DEFAULT);
+
+              if (PREDICT_TRUE(src_adj1->saved_lookup_next_index > 1))
+                vnet_buffer (b1)->ip.flow_hash = ip4_compute_flow_hash (
+                    ip1, IP_FLOW_HASH_DEFAULT);
             }
           else
             {
-              if (src_fib_index0 != (u32) ~0)
+              if ((u32) ~0 != src_adj_index0)
                 {
-                  ip4_src_fib_lookup_one (lgm, src_fib_index0,
-                                          &ip0->src_address, &src_adj_index0);
                   vnet_buffer(b0)->ip.adj_index[VLIB_TX] = src_adj_index0;
                   src_adj0 = ip_get_adjacency (lgm->lm4, src_adj_index0);
-                  next0 = src_adj0->lookup_next_index;
+                  next0 = src_adj0->explicit_fib_index;
                   vnet_buffer (b0)->sw_if_index[VLIB_TX] =
                       src_adj0->rewrite_header.sw_if_index;
+
+                  if (PREDICT_TRUE(src_adj0->saved_lookup_next_index > 1))
+                    vnet_buffer (b0)->ip.flow_hash = ip4_compute_flow_hash (
+                        ip0, IP_FLOW_HASH_DEFAULT);
                 }
-              if (src_fib_index1 != (u32) ~0)
+              else
+                {
+                  next0 = LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP;
+                }
+
+              if ((u32) ~0 != src_adj_index1)
                 {
-                  ip4_src_fib_lookup_one (lgm, src_fib_index1,
-                                          &ip1->src_address, &src_adj_index1);
                   vnet_buffer(b1)->ip.adj_index[VLIB_TX] = src_adj_index1;
                   src_adj1 = ip_get_adjacency (lgm->lm4, src_adj_index1);
-                  next1 = src_adj1->lookup_next_index;
+                  next1 = src_adj1->explicit_fib_index;
                   vnet_buffer (b1)->sw_if_index[VLIB_TX] =
                       src_adj1->rewrite_header.sw_if_index;
+                  if (PREDICT_TRUE(src_adj1->saved_lookup_next_index > 1))
+                    vnet_buffer (b1)->ip.flow_hash = ip4_compute_flow_hash (
+                        ip1, IP_FLOW_HASH_DEFAULT);
+                }
+              else
+                {
+                  next1 = LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP;
                 }
             }
 
+          /* mark the packets for CP lookup if needed*/
+          if (PREDICT_FALSE(LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP == next0))
+            vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_IP;
+          if (PREDICT_FALSE(LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP == next1))
+            vnet_buffer (b1)->lisp.overlay_afi = LISP_AFI_IP;
+
           vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next,
                                           n_left_to_next, bi0, bi1, next0,
                                           next1);
@@ -859,20 +982,34 @@ lgpe_ip4_lookup (vlib_main_t * vm, vlib_node_runtime_t * node,
           dst_adj0 = ip_get_adjacency (lgm->lm4, dst_adj_index0);
           src_fib_index0 = dst_adj0->rewrite_header.sw_if_index;
 
-          /* if default route not hit in ip4 lookup */
-          if (PREDICT_TRUE(src_fib_index0 != (u32 ) ~0))
+          /* do src lookup */
+          ip4_src_fib_lookup_one (lgm, src_fib_index0, &ip0->src_address,
+                                  &src_adj_index0);
+
+          /* if a source fib exists */
+          if (PREDICT_TRUE((u32) ~0 != src_adj_index0))
             {
-              /* do src lookup */
-              ip4_src_fib_lookup_one (lgm, src_fib_index0, &ip0->src_address,
-                                      &src_adj_index0);
               vnet_buffer(b0)->ip.adj_index[VLIB_TX] = src_adj_index0;
               src_adj0 = ip_get_adjacency (lgm->lm4, src_adj_index0);
-              next0 = src_adj0->lookup_next_index;
+              next0 = src_adj0->explicit_fib_index;
 
               /* prepare packet for lisp-gpe output node */
               vnet_buffer (b0)->sw_if_index[VLIB_TX] =
                   src_adj0->rewrite_header.sw_if_index;
+
+              /* if multipath: saved_lookup_next_index is reused to store
+               * nb of sub-tunnels. If greater than 1, multipath is on */
+              if (PREDICT_TRUE(src_adj0->saved_lookup_next_index > 1))
+                vnet_buffer (b0)->ip.flow_hash = ip4_compute_flow_hash (
+                    ip0, IP_FLOW_HASH_DEFAULT);
             }
+          else
+            {
+              next0 = LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP;
+            }
+
+          if (PREDICT_FALSE(LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP == next0))
+            vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_IP;
 
           vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
                                           n_left_to_next, bi0, next0);
@@ -929,6 +1066,36 @@ ip6_src_fib_lookup (lisp_gpe_main_t * lgm, u32 src_fib_index,
   return 0;
 }
 
+always_inline void
+ip6_src_fib_lookup_one (lisp_gpe_main_t * lgm, u32 src_fib_index0,
+                        ip6_address_t * addr0, u32 * src_adj_index0)
+{
+  /* if default route not hit in ip6 lookup */
+  if (PREDICT_TRUE(src_fib_index0 != (u32 ) ~0))
+    src_adj_index0[0] = ip6_src_fib_lookup (lgm, src_fib_index0, addr0);
+  else
+    src_adj_index0[0] = ~0;
+}
+
+always_inline void
+ip6_src_fib_lookup_two (lisp_gpe_main_t * lgm, u32 src_fib_index0,
+                        u32 src_fib_index1, ip6_address_t * addr0,
+                        ip6_address_t * addr1, u32 * src_adj_index0,
+                        u32 * src_adj_index1)
+{
+  /* if default route not hit in ip6 lookup */
+  if (PREDICT_TRUE(src_fib_index0 != (u32 ) ~0 && src_fib_index1 != (u32 ) ~0))
+    {
+      src_adj_index0[0] = ip6_src_fib_lookup(lgm, src_fib_index0, addr0);
+      src_adj_index1[0] = ip6_src_fib_lookup(lgm, src_fib_index1, addr1);
+    }
+  else
+    {
+      ip6_src_fib_lookup_one (lgm, src_fib_index0, addr0, src_adj_index0);
+      ip6_src_fib_lookup_one (lgm, src_fib_index1, addr1, src_adj_index1);
+    }
+}
+
 always_inline uword
 lgpe_ip6_lookup (vlib_main_t * vm, vlib_node_runtime_t * node,
                  vlib_frame_t * from_frame)
@@ -998,55 +1165,83 @@ lgpe_ip6_lookup (vlib_main_t * vm, vlib_node_runtime_t * node,
           src_fib_index0 = dst_adj0->rewrite_header.sw_if_index;
           src_fib_index1 = dst_adj1->rewrite_header.sw_if_index;
 
-          /* if default route not hit in ip6 lookup */
-          if (PREDICT_TRUE(src_fib_index0 != (u32) ~0
-                           && src_fib_index1 != (u32) ~0))
-            {
-              /* do src lookup */
-              src_adj_index0 = ip6_src_fib_lookup (lgm, src_fib_index0,
-                                                   &ip0->src_address);
-              src_adj_index1 = ip6_src_fib_lookup (lgm, src_fib_index1,
-                                                   &ip1->src_address);
+          ip6_src_fib_lookup_two (lgm, src_fib_index0, src_fib_index1,
+                                  &ip0->src_address, &ip1->src_address,
+                                  &src_adj_index0, &src_adj_index1);
 
+          /* if a source fib exists */
+          if (PREDICT_TRUE((u32) ~0 != src_adj_index0
+                           && (u32) ~0 != src_adj_index1))
+            {
               vnet_buffer(b0)->ip.adj_index[VLIB_TX] = src_adj_index0;
               vnet_buffer(b1)->ip.adj_index[VLIB_TX] = src_adj_index1;
 
               src_adj0 = ip_get_adjacency (lgm->lm6, src_adj_index0);
               src_adj1 = ip_get_adjacency (lgm->lm6, src_adj_index1);
 
-              next0 = src_adj0->lookup_next_index;
-              next1 = src_adj1->lookup_next_index;
+              next0 = src_adj0->explicit_fib_index;
+              next1 = src_adj1->explicit_fib_index;
 
               /* prepare buffer for lisp-gpe output node */
               vnet_buffer (b0)->sw_if_index[VLIB_TX] =
                   src_adj0->rewrite_header.sw_if_index;
               vnet_buffer (b1)->sw_if_index[VLIB_TX] =
                   src_adj1->rewrite_header.sw_if_index;
+
+              /* if multipath: saved_lookup_next_index is reused to store
+               * nb of sub-tunnels. If greater than 1, multipath is on.
+               * Note that flow hash should be 0 after ipx lookup! */
+              if (PREDICT_TRUE(src_adj0->saved_lookup_next_index > 1))
+                vnet_buffer (b0)->ip.flow_hash = ip6_compute_flow_hash (
+                    ip0, IP_FLOW_HASH_DEFAULT);
+
+              if (PREDICT_TRUE(src_adj1->saved_lookup_next_index > 1))
+                vnet_buffer (b1)->ip.flow_hash = ip6_compute_flow_hash (
+                    ip1, IP_FLOW_HASH_DEFAULT);
             }
           else
             {
-              if (src_fib_index0 != (u32) ~0)
+              if (src_adj_index0 != (u32) ~0)
                 {
-                  src_adj_index0 = ip6_src_fib_lookup (lgm, src_fib_index0,
-                                                       &ip0->src_address);
                   vnet_buffer(b0)->ip.adj_index[VLIB_TX] = src_adj_index0;
                   src_adj0 = ip_get_adjacency (lgm->lm6, src_adj_index0);
-                  next0 = src_adj0->lookup_next_index;
+                  next0 = src_adj0->explicit_fib_index;
                   vnet_buffer (b0)->sw_if_index[VLIB_TX] =
                       src_adj0->rewrite_header.sw_if_index;
+
+                  if (PREDICT_TRUE(src_adj0->saved_lookup_next_index > 1))
+                    vnet_buffer (b0)->ip.flow_hash = ip6_compute_flow_hash (
+                        ip0, IP_FLOW_HASH_DEFAULT);
                 }
-              if (src_fib_index1 != (u32) ~0)
+              else
+                {
+                  next0 = LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP;
+                }
+
+              if (src_adj_index1 != (u32) ~0)
                 {
-                  src_adj_index1 = ip6_src_fib_lookup (lgm, src_fib_index1,
-                                                       &ip1->src_address);
                   vnet_buffer(b1)->ip.adj_index[VLIB_TX] = src_adj_index1;
                   src_adj1 = ip_get_adjacency (lgm->lm6, src_adj_index1);
-                  next1 = src_adj1->lookup_next_index;
+                  next1 = src_adj1->explicit_fib_index;
                   vnet_buffer (b1)->sw_if_index[VLIB_TX] =
                       src_adj1->rewrite_header.sw_if_index;
+
+                  if (PREDICT_TRUE(src_adj1->saved_lookup_next_index > 1))
+                    vnet_buffer (b1)->ip.flow_hash = ip6_compute_flow_hash (
+                        ip1, IP_FLOW_HASH_DEFAULT);
+                }
+              else
+                {
+                  next1 = LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP;
                 }
             }
 
+          /* mark the packets for CP lookup if needed*/
+          if (PREDICT_FALSE(LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP == next0))
+            vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_IP;
+          if (PREDICT_FALSE(LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP == next1))
+            vnet_buffer (b1)->lisp.overlay_afi = LISP_AFI_IP;
+
           vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next,
                                           n_left_to_next, bi0, bi1, next0,
                                           next1);
@@ -1075,21 +1270,35 @@ lgpe_ip6_lookup (vlib_main_t * vm, vlib_node_runtime_t * node,
           dst_adj0 = ip_get_adjacency (lgm->lm6, dst_adj_index0);
           src_fib_index0 = dst_adj0->rewrite_header.sw_if_index;
 
-          /* if default route not hit in ip6 lookup */
-          if (PREDICT_TRUE(src_fib_index0 != (u32 ) ~0))
-            {
-              /* do src lookup */
-              src_adj_index0 = ip6_src_fib_lookup (lgm, src_fib_index0,
-                                                   &ip0->src_address);
+          /* do src lookup */
+          ip6_src_fib_lookup_one (lgm, src_fib_index0, &ip0->src_address,
+                                  &src_adj_index0);
 
+          /* if a source fib exists */
+          if (PREDICT_TRUE(src_adj_index0 != (u32 ) ~0))
+            {
               vnet_buffer(b0)->ip.adj_index[VLIB_TX] = src_adj_index0;
               src_adj0 = ip_get_adjacency (lgm->lm6, src_adj_index0);
-              next0 = src_adj0->lookup_next_index;
+              next0 = src_adj0->explicit_fib_index;
 
               /* prepare packet for lisp-gpe output node */
               vnet_buffer (b0)->sw_if_index[VLIB_TX] =
                   src_adj0->rewrite_header.sw_if_index;
+
+              /* if multipath: saved_lookup_next_index is reused to store
+               * nb of sub-tunnels. If greater than 1, multipath is on */
+              if (PREDICT_TRUE(src_adj0->saved_lookup_next_index > 1))
+                vnet_buffer (b0)->ip.flow_hash = ip6_compute_flow_hash (
+                    ip0, IP_FLOW_HASH_DEFAULT);
             }
+          else
+            {
+              next0 = LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP;
+            }
+
+          /* mark the packets for CP lookup if needed*/
+          if (PREDICT_FALSE(LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP == next0))
+            vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_IP;
 
           vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
                                           n_left_to_next, bi0, next0);