IP mcast: allow unicast address as a next-hop 74/6974/6
authorNeale Ranns <nranns@cisco.com>
Thu, 1 Jun 2017 14:45:05 +0000 (07:45 -0700)
committerDamjan Marion <dmarion.lists@gmail.com>
Tue, 17 Apr 2018 22:29:46 +0000 (22:29 +0000)
Change-Id: I5e679f2601e37688f2768620479dc2efb7d19ca3
Signed-off-by: Neale Ranns <nranns@cisco.com>
src/vnet/fib/fib_path.c
src/vnet/ip/ip.api
src/vnet/ip/ip_api.c
src/vnet/ip/lookup.c
src/vnet/mfib/mfib_entry.c
src/vnet/mfib/mfib_itf.c
src/vnet/mfib/mfib_itf.h
src/vnet/mfib/mfib_test.c
test/test_ip_mcast.py
test/vpp_ip_route.py
test/vpp_papi_provider.py

index 8cfe86a..e974e31 100644 (file)
@@ -2323,6 +2323,8 @@ fib_path_contribute_forwarding (fib_node_index_t path_index,
            case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS:
            case FIB_FORW_CHAIN_TYPE_ETHERNET:
            case FIB_FORW_CHAIN_TYPE_NSH:
+           case FIB_FORW_CHAIN_TYPE_MCAST_IP4:
+           case FIB_FORW_CHAIN_TYPE_MCAST_IP6:
            {
                adj_index_t ai;
 
@@ -2338,8 +2340,6 @@ fib_path_contribute_forwarding (fib_node_index_t path_index,
 
                break;
            }
-           case FIB_FORW_CHAIN_TYPE_MCAST_IP4:
-           case FIB_FORW_CHAIN_TYPE_MCAST_IP6:
            case FIB_FORW_CHAIN_TYPE_BIER:
                break;
            }
index 3802492..bf16c18 100644 (file)
@@ -446,6 +446,7 @@ autoreply define ip_mroute_add_del
   u8 is_local;
   u8 grp_address[16];
   u8 src_address[16];
+  u8 nh_address[16];
 };
 
 /** \brief Dump IP multicast fib table
index 69ff719..b4d942b 100644 (file)
@@ -1208,13 +1208,15 @@ mroute_add_del_handler (u8 is_add,
                        dpo_proto_t nh_proto,
                        u32 entry_flags,
                        fib_rpf_id_t rpf_id,
-                       u32 next_hop_sw_if_index, u32 itf_flags, u32 bier_imp)
+                       u32 next_hop_sw_if_index,
+                       ip46_address_t * nh, u32 itf_flags, u32 bier_imp)
 {
   stats_dslock_with_hint (1 /* release hint */ , 2 /* tag */ );
 
   fib_route_path_t path = {
     .frp_sw_if_index = next_hop_sw_if_index,
     .frp_proto = nh_proto,
+    .frp_addr = *nh,
   };
 
   if (is_local)
@@ -1253,6 +1255,7 @@ api_mroute_add_del_t_handler (vl_api_ip_mroute_add_del_t * mp)
 {
   fib_protocol_t fproto;
   dpo_proto_t nh_proto;
+  ip46_address_t nh;
   u32 fib_index;
   int rv;
 
@@ -1277,6 +1280,8 @@ api_mroute_add_del_t_handler (vl_api_ip_mroute_add_del_t * mp)
                   sizeof (pfx.fp_grp_addr.ip4));
       clib_memcpy (&pfx.fp_src_addr.ip4, mp->src_address,
                   sizeof (pfx.fp_src_addr.ip4));
+      memset (&nh.ip6, 0, sizeof (nh.ip6));
+      clib_memcpy (&nh.ip4, mp->nh_address, sizeof (nh.ip4));
     }
   else
     {
@@ -1284,6 +1289,7 @@ api_mroute_add_del_t_handler (vl_api_ip_mroute_add_del_t * mp)
                   sizeof (pfx.fp_grp_addr.ip6));
       clib_memcpy (&pfx.fp_src_addr.ip6, mp->src_address,
                   sizeof (pfx.fp_src_addr.ip6));
+      clib_memcpy (&nh.ip6, mp->nh_address, sizeof (nh.ip6));
     }
 
   return (mroute_add_del_handler (mp->is_add,
@@ -1293,6 +1299,7 @@ api_mroute_add_del_t_handler (vl_api_ip_mroute_add_del_t * mp)
                                  ntohl (mp->entry_flags),
                                  ntohl (mp->rpf_id),
                                  ntohl (mp->next_hop_sw_if_index),
+                                 &nh,
                                  ntohl (mp->itf_flags),
                                  ntohl (mp->bier_imp)));
 }
index 216af4c..a26dc12 100644 (file)
@@ -899,14 +899,30 @@ vnet_ip_mroute_cmd (vlib_main_t * vm,
          pfx.fp_proto = FIB_PROTOCOL_IP6;
          pfx.fp_len = 128;
        }
+      else if (unformat (line_input, "via %U %U",
+                        unformat_ip4_address, &rpath.frp_addr.ip4,
+                        unformat_vnet_sw_interface, vnm,
+                        &rpath.frp_sw_if_index))
+       {
+         rpath.frp_weight = 1;
+       }
+      else if (unformat (line_input, "via %U %U",
+                        unformat_ip6_address, &rpath.frp_addr.ip6,
+                        unformat_vnet_sw_interface, vnm,
+                        &rpath.frp_sw_if_index))
+       {
+         rpath.frp_weight = 1;
+       }
       else if (unformat (line_input, "via %U",
                         unformat_vnet_sw_interface, vnm,
                         &rpath.frp_sw_if_index))
        {
+         memset (&rpath.frp_addr, 0, sizeof (rpath.frp_addr));
          rpath.frp_weight = 1;
        }
       else if (unformat (line_input, "via local"))
        {
+         memset (&rpath.frp_addr, 0, sizeof (rpath.frp_addr));
          rpath.frp_sw_if_index = ~0;
          rpath.frp_weight = 1;
          rpath.frp_flags |= FIB_ROUTE_PATH_LOCAL;
index bc7f7de..a88f375 100644 (file)
@@ -217,7 +217,10 @@ format_mfib_entry (u8 * s, va_list * args)
     ({
         s = format(s, "\n  %U", format_mfib_itf, mfi);
     }));
-    s = format(s, "\n  RPF-ID:%d", mfib_entry->mfe_rpf_id);
+    if (MFIB_RPF_ID_NONE != mfib_entry->mfe_rpf_id)
+    {
+        s = format(s, "\n  RPF-ID:%d", mfib_entry->mfe_rpf_id);
+    }
     s = format(s, "\n  %U-chain\n  %U",
                format_fib_forw_chain_type,
                mfib_entry_get_default_chain_type(mfib_entry),
@@ -835,9 +838,9 @@ mfib_entry_path_update (fib_node_index_t mfib_entry_index,
 {
     fib_node_index_t path_index;
     mfib_path_ext_t *path_ext;
-    mfib_itf_flags_t old, new;
     mfib_entry_t *mfib_entry;
     mfib_entry_src_t *msrc;
+    mfib_itf_flags_t old;
 
     mfib_entry = mfib_entry_get(mfib_entry_index);
     ASSERT(NULL != mfib_entry);
@@ -873,37 +876,32 @@ mfib_entry_path_update (fib_node_index_t mfib_entry_index,
     {
         mfib_itf_t *mfib_itf;
 
-        new = itf_flags;
-
-        if (old != new)
+        if (old != itf_flags)
         {
-            if (MFIB_ITF_FLAG_NONE == new)
-            {
-                /*
-                 * no more interface flags on this path, remove
-                 * from the data-plane set
-                 */
-                mfib_entry_itf_remove(msrc, rpath[0].frp_sw_if_index);
-            }
-            else if (MFIB_ITF_FLAG_NONE == old)
+            /*
+             * change of flag contributions
+             */
+            mfib_itf = mfib_entry_itf_find(msrc->mfes_itfs,
+                                           rpath[0].frp_sw_if_index);
+
+            if (NULL == mfib_itf)
             {
-                /*
-                 * This interface is now contributing
-                 */
                 mfib_entry_itf_add(msrc,
                                    rpath[0].frp_sw_if_index,
-                                   mfib_itf_create(rpath[0].frp_sw_if_index,
-                                                   itf_flags));
+                                   mfib_itf_create(path_index, itf_flags));
             }
             else
             {
-                /*
-                 * change of flag contributions
-                 */
-                mfib_itf = mfib_entry_itf_find(msrc->mfes_itfs,
-                                               rpath[0].frp_sw_if_index);
-                /* Seen by packets inflight */
-                mfib_itf->mfi_flags = new;
+                if (mfib_itf_update(mfib_itf,
+                                    path_index,
+                                    itf_flags))
+                {
+                    /*
+                     * no more interface flags on this path, remove
+                     * from the data-plane set
+                     */
+                    mfib_entry_itf_remove(msrc, rpath[0].frp_sw_if_index);
+                }
             }
         }
     }
@@ -952,7 +950,21 @@ mfib_entry_path_remove (fib_node_index_t mfib_entry_index,
         mfib_path_ext_remove(msrc, path_index);
         if (~0 != rpath[0].frp_sw_if_index)
         {
-            mfib_entry_itf_remove(msrc, rpath[0].frp_sw_if_index);
+            mfib_itf_t *mfib_itf;
+
+            mfib_itf = mfib_entry_itf_find(msrc->mfes_itfs,
+                                           rpath[0].frp_sw_if_index);
+
+            if (mfib_itf_update(mfib_itf,
+                                path_index,
+                                MFIB_ITF_FLAG_NONE))
+            {
+                /*
+                 * no more interface flags on this path, remove
+                 * from the data-plane set
+                 */
+                mfib_entry_itf_remove(msrc, rpath[0].frp_sw_if_index);
+            }
         }
     }
 
index f77b40e..33ef987 100644 (file)
 
 #include <vnet/mfib/mfib_itf.h>
 #include <vnet/mfib/mfib_signal.h>
+#include <vnet/fib/fib_path.h>
 
 mfib_itf_t *mfib_itf_pool;
 
 index_t
-mfib_itf_create (u32 sw_if_index,
+mfib_itf_create (fib_node_index_t path_index,
                  mfib_itf_flags_t mfi_flags)
 {
     mfib_itf_t *mfib_itf;
@@ -29,16 +30,89 @@ mfib_itf_create (u32 sw_if_index,
     pool_get_aligned(mfib_itf_pool, mfib_itf,
                      CLIB_CACHE_LINE_BYTES);
 
-    mfib_itf->mfi_sw_if_index = sw_if_index;
-    mfib_itf->mfi_flags = mfi_flags;
+    mfib_itf->mfi_sw_if_index = fib_path_get_resolving_interface(path_index);
     mfib_itf->mfi_si = INDEX_INVALID;
 
+    /*
+     * add the path index to the per-path hash
+     */
+    mfib_itf->mfi_hash = hash_set(mfib_itf->mfi_hash, path_index, mfi_flags);
+
+    /*
+     * the combined flags from all the paths is from just the one contributor
+     */
+    mfib_itf->mfi_flags = mfi_flags;
+
     return (mfib_itf - mfib_itf_pool);
 }
 
+static mfib_itf_flags_t
+mfib_itf_mk_flags (const mfib_itf_t *mfib_itf)
+{
+    mfib_itf_flags_t combined_flags, flags;
+    fib_node_index_t *path_index;
+
+    combined_flags = MFIB_ITF_FLAG_NONE;
+
+    hash_foreach(path_index, flags, mfib_itf->mfi_hash,
+    {
+        combined_flags |= flags;
+    });
+
+    return (combined_flags);
+}
+
+int
+mfib_itf_update (mfib_itf_t *mfib_itf,
+                 fib_node_index_t path_index,
+                 mfib_itf_flags_t mfi_flags)
+{
+    /*
+     * add or remove the path index to the per-path hash
+     */
+    if (MFIB_ITF_FLAG_NONE == mfi_flags)
+    {
+        hash_unset(mfib_itf->mfi_hash, path_index);
+    }
+    else
+    {
+        mfib_itf->mfi_hash = hash_set(mfib_itf->mfi_hash,
+                                      path_index,
+                                      mfi_flags);
+    }
+
+    /*
+     * re-generate the combined flags from all the paths.
+     */
+    mfib_itf->mfi_flags = mfib_itf_mk_flags(mfib_itf);
+
+    /*
+     * The interface can be removed if there are no more flags
+     */
+    return (MFIB_ITF_FLAG_NONE == mfib_itf->mfi_flags);
+}
+
+static void
+mfib_itf_hash_flush (mfib_itf_t *mfi)
+{
+    fib_node_index_t path_index, *path_indexp, *all = NULL;
+    mfib_itf_flags_t flags;
+
+    hash_foreach(path_index, flags, mfi->mfi_hash,
+    {
+        vec_add1(all, path_index);
+    });
+
+    vec_foreach(path_indexp, all)
+    {
+        hash_unset(mfi->mfi_hash, *path_indexp);
+    };
+}
+
 void
 mfib_itf_delete (mfib_itf_t *mfi)
 {
+    mfib_itf_hash_flush(mfi);
     mfib_signal_remove_itf(mfi);
     pool_put(mfib_itf_pool, mfi);
 }
index 5f26a47..fe39c89 100644 (file)
@@ -25,7 +25,7 @@
 typedef struct mfib_itf_t_
 {
     /**
-     * @brief Falags on the entry
+     * @brief Forwarding Flags on the entry - checked in the data-path
      */
     mfib_itf_flags_t mfi_flags;
 
@@ -38,22 +38,43 @@ typedef struct mfib_itf_t_
      * The index of the signal in the pending list
      */
     u32 mfi_si;
+
+    /**
+     * A hash table of path-inidices that are contributing flags to this interface.
+     * Since paths with next-hops can be on the same interface and each of those
+     * paths can contribute different flags, we need to maintain the flag
+     * contribution from each path, and use a combination for forwarding.
+     */
+    uword *mfi_hash;
 } mfib_itf_t;
 
+/**
+ * update an interface from a path.
+ * returns 1 if the entry is removed, i.e. has no flags left, as a result
+ * of the update.
+ */
+extern int mfib_itf_update(mfib_itf_t *itf,
+                           fib_node_index_t path_index,
+                           mfib_itf_flags_t mfi_flags);
 
-extern index_t mfib_itf_create(u32 sw_if_index,
+extern index_t mfib_itf_create(fib_node_index_t path_index,
                                mfib_itf_flags_t mfi_flags);
-extern void mfib_itf_delete(mfib_itf_t *mfi);
+
+extern void mfib_itf_delete(mfib_itf_t *itf);
 
 extern u8 *format_mfib_itf(u8 * s, va_list * args);
 
 extern mfib_itf_t *mfib_itf_pool;
 
+/**
+ * Get the MFIB interface representation
+ */
 static inline mfib_itf_t *
 mfib_itf_get (index_t mi)
 {
     return (pool_elt_at_index(mfib_itf_pool, mi));
 }
+
 static inline index_t
 mfib_itf_get_index (const mfib_itf_t *mfi)
 {
index 8c75e34..a94b308 100644 (file)
@@ -347,11 +347,14 @@ mfib_test_i (fib_protocol_t PROTO,
              const mfib_prefix_t *pfx_star_g_1,
              const mfib_prefix_t *pfx_star_g_2,
              const mfib_prefix_t *pfx_star_g_3,
-             const mfib_prefix_t *pfx_star_g_slash_m)
+             const mfib_prefix_t *pfx_star_g_slash_m,
+             const fib_prefix_t *pfx_itf,
+             const ip46_address_t *addr_nbr1,
+             const ip46_address_t *addr_nbr2)
 {
     fib_node_index_t mfei, mfei_dflt, mfei_no_f, mfei_s_g, mfei_g_1, mfei_g_2, mfei_g_3, mfei_g_m;
     u32 fib_index, n_entries, n_itfs, n_reps, n_pls;
-    fib_node_index_t ai_1, ai_2, ai_3;
+    fib_node_index_t ai_1, ai_2, ai_3, ai_nbr1, ai_nbr2;
     test_main_t *tm;
     int res;
 
@@ -374,12 +377,33 @@ mfib_test_i (fib_protocol_t PROTO,
     ai_3 = adj_mcast_add_or_lock(PROTO,
                                  LINKT,
                                  tm->hw[3]->sw_if_index);
+    ai_nbr1 = adj_nbr_add_or_lock(PROTO,
+                                  LINKT,
+                                  addr_nbr1,
+                                  tm->hw[0]->sw_if_index);
+    ai_nbr2 = adj_nbr_add_or_lock(PROTO,
+                                  LINKT,
+                                  addr_nbr2,
+                                  tm->hw[0]->sw_if_index);
 
     MFIB_TEST(3 == adj_mcast_db_size(), "3 MCAST adjs");
 
     /* Find or create FIB table 11 */
     fib_index = mfib_table_find_or_create_and_lock(PROTO, 11, MFIB_SOURCE_API);
 
+    fib_table_entry_update_one_path(0,
+                                    pfx_itf,
+                                   FIB_SOURCE_INTERFACE,
+                                   (FIB_ENTRY_FLAG_CONNECTED |
+                                    FIB_ENTRY_FLAG_ATTACHED),
+                                   DPO_PROTO_IP4,
+                                   NULL,
+                                   tm->hw[0]->sw_if_index,
+                                   ~0, // invalid fib index
+                                   1, // weight
+                                   NULL,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+
     mfib_prefix_t pfx_dft = {
         .fp_len = 0,
         .fp_proto = PROTO,
@@ -1042,6 +1066,69 @@ mfib_test_i (fib_protocol_t PROTO,
               "%U Gone",
               format_mfib_prefix, pfx_star_g_slash_m);
 
+    /*
+     * Entries with paths via unicast next-hops
+     */
+    fib_route_path_t path_via_nbr1 = {
+        .frp_proto = fib_proto_to_dpo(PROTO),
+        .frp_addr = *addr_nbr1,
+        .frp_sw_if_index = tm->hw[0]->sw_if_index,
+        .frp_fib_index = ~0,
+        .frp_weight = 0,
+        .frp_flags = 0,
+    };
+    fib_route_path_t path_via_nbr2 = {
+        .frp_proto = fib_proto_to_dpo(PROTO),
+        .frp_addr = *addr_nbr2,
+        .frp_sw_if_index = tm->hw[0]->sw_if_index,
+        .frp_fib_index = ~0,
+        .frp_weight = 0,
+        .frp_flags = 0,
+    };
+
+    mfei_g_1 = mfib_table_entry_path_update(fib_index,
+                                            pfx_star_g_1,
+                                            MFIB_SOURCE_API,
+                                            &path_via_nbr1,
+                                            (MFIB_ITF_FLAG_FORWARD));
+    mfei_g_1 = mfib_table_entry_path_update(fib_index,
+                                            pfx_star_g_1,
+                                            MFIB_SOURCE_API,
+                                            &path_via_nbr2,
+                                            (MFIB_ITF_FLAG_FORWARD));
+    MFIB_TEST(!mfib_test_entry(mfei_g_1,
+                               MFIB_ENTRY_FLAG_NONE,
+                               2,
+                               DPO_ADJACENCY_INCOMPLETE, ai_nbr1,
+                               DPO_ADJACENCY_INCOMPLETE, ai_nbr2),
+              "%U replicate OK",
+              format_mfib_prefix, pfx_star_g_1);
+    MFIB_TEST_NS(!mfib_test_entry_itf(mfei_g_1, tm->hw[0]->sw_if_index,
+                                      MFIB_ITF_FLAG_FORWARD));
+
+    mfib_table_entry_path_remove(fib_index,
+                                 pfx_star_g_1,
+                                 MFIB_SOURCE_API,
+                                 &path_via_nbr1);
+
+    MFIB_TEST(!mfib_test_entry(mfei_g_1,
+                               MFIB_ENTRY_FLAG_NONE,
+                               1,
+                               DPO_ADJACENCY_INCOMPLETE, ai_nbr2),
+              "%U replicate OK",
+              format_mfib_prefix, pfx_star_g_1);
+    MFIB_TEST_NS(!mfib_test_entry_itf(mfei_g_1, tm->hw[0]->sw_if_index,
+                                      MFIB_ITF_FLAG_FORWARD));
+
+    mfib_table_entry_path_remove(fib_index,
+                                 pfx_star_g_1,
+                                 MFIB_SOURCE_API,
+                                 &path_via_nbr2);
+    mfei = mfib_table_lookup_exact_match(fib_index, pfx_star_g_1);
+    MFIB_TEST(FIB_NODE_INDEX_INVALID == mfei,
+              "%U Gone",
+              format_mfib_prefix, pfx_star_g_1);
+
     /*
      * Add a prefix as a special/exclusive route
      */
@@ -1216,6 +1303,8 @@ mfib_test_i (fib_protocol_t PROTO,
     adj_unlock(ai_1);
     adj_unlock(ai_2);
     adj_unlock(ai_3);
+    adj_unlock(ai_nbr1);
+    adj_unlock(ai_nbr2);
 
     /*
      * MPLS disable the interface
@@ -1225,6 +1314,11 @@ mfib_test_i (fib_protocol_t PROTO,
                                      0, 0);
     mpls_table_delete(MPLS_FIB_DEFAULT_TABLE_ID, FIB_SOURCE_API);
 
+    /*
+     * remove the connected
+     */
+    fib_table_entry_delete(0, pfx_itf, FIB_SOURCE_INTERFACE);
+
     /*
      * test we've leaked no resources
      */
@@ -1303,7 +1397,19 @@ mfib_test_v4 (void)
             .ip4.as_u32 = 0,
         },
     };
-
+    const fib_prefix_t pfx_itf = {
+        .fp_len = 24,
+        .fp_proto = FIB_PROTOCOL_IP4,
+        .fp_addr = {
+            .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a0a),
+        },
+    };
+    const ip46_address_t nbr1 = {
+        .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a0b),
+    };
+    const ip46_address_t nbr2 = {
+        .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a0c),
+    };
     return (mfib_test_i(FIB_PROTOCOL_IP4,
                         VNET_LINK_IP4,
                         &pfx_224_s_8,
@@ -1311,7 +1417,10 @@ mfib_test_v4 (void)
                         &pfx_239_1_1_1,
                         &pfx_239_1_1_2,
                         &pfx_239_1_1_3,
-                        &pfx_239));
+                        &pfx_239,
+                        &pfx_itf,
+                        &nbr1,
+                        &nbr2));
 }
 
 static int
@@ -1371,6 +1480,22 @@ mfib_test_v6 (void)
             .ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000000),
         },
     };
+    const fib_prefix_t pfx_itf = {
+        .fp_len = 64,
+        .fp_proto = FIB_PROTOCOL_IP6,
+        .fp_addr = {
+            .ip6.as_u64[0] = clib_host_to_net_u64(0x2001000000000000),
+            .ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000001),
+        },
+    };
+    const ip46_address_t nbr1 = {
+            .ip6.as_u64[0] = clib_host_to_net_u64(0x2001000000000000),
+            .ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000002),
+    };
+    const ip46_address_t nbr2 = {
+            .ip6.as_u64[0] = clib_host_to_net_u64(0x2001000000000000),
+            .ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000003),
+    };
 
     return (mfib_test_i(FIB_PROTOCOL_IP6,
                         VNET_LINK_IP6,
@@ -1379,7 +1504,10 @@ mfib_test_v6 (void)
                         &pfx_ff_1,
                         &pfx_ff_2,
                         &pfx_ff_3,
-                        &pfx_ff));
+                        &pfx_ff,
+                        &pfx_itf,
+                        &nbr1,
+                        &nbr2));
 }
 
 static clib_error_t *
@@ -1391,6 +1519,12 @@ mfib_test (vlib_main_t * vm,
 
     res += mfib_test_mk_intf(4);
     res += mfib_test_v4();
+
+    if (res)
+    {
+        return clib_error_return(0, "MFIB Unit Test Failed");
+    }
+
     res += mfib_test_v6();
 
     if (res)
index b4554c6..017f062 100644 (file)
@@ -111,7 +111,7 @@ class TestIPMcast(VppTestCase):
                     capture.remove(p)
         return capture
 
-    def verify_capture_ip4(self, rx_if, sent):
+    def verify_capture_ip4(self, rx_if, sent, dst_mac=None):
         rxd = rx_if.get_capture(len(sent))
 
         try:
@@ -129,8 +129,11 @@ class TestIPMcast(VppTestCase):
                 tx_ip = tx[IP]
                 rx_ip = rx[IP]
 
+                if dst_mac is None:
+                    dst_mac = getmacbyip(rx_ip.dst)
+
                 # check the MAC address on the RX'd packet is correctly formed
-                self.assertEqual(eth.dst, getmacbyip(rx_ip.dst))
+                self.assertEqual(eth.dst, dst_mac)
 
                 self.assertEqual(rx_ip.src, tx_ip.src)
                 self.assertEqual(rx_ip.dst, tx_ip.dst)
@@ -225,6 +228,26 @@ class TestIPMcast(VppTestCase):
                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
         route_1_1_1_1_232_1_1_1.add_vpp_config()
 
+        #
+        # An (S,G).
+        # one accepting interface, pg0, 2 forwarding interfaces
+        # that use unicast next-hops
+        #
+        route_1_1_1_1_232_1_1_2 = VppIpMRoute(
+            self,
+            "1.1.1.1",
+            "232.1.1.2", 64,
+            MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
+            [VppMRoutePath(self.pg0.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
+             VppMRoutePath(self.pg1.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
+                           nh=self.pg1.remote_ip4),
+             VppMRoutePath(self.pg2.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
+                           nh=self.pg2.remote_ip4)])
+        route_1_1_1_1_232_1_1_2.add_vpp_config()
+
         #
         # An (*,G/m).
         # one accepting interface, pg0, 1 forwarding interfaces
@@ -283,6 +306,26 @@ class TestIPMcast(VppTestCase):
         self.pg3.assert_nothing_captured(
             remark="IP multicast packets forwarded on PG3")
 
+        #
+        # a stream to the unicast next-hops
+        #
+        self.vapi.cli("clear trace")
+        tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.2")
+        self.pg0.add_stream(tx)
+
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        # We expect replications on Pg1->7
+        self.verify_capture_ip4(self.pg1, tx, dst_mac=self.pg1.remote_mac)
+        self.verify_capture_ip4(self.pg2, tx, dst_mac=self.pg2.remote_mac)
+
+        # no replications on Pg0 nor pg3
+        self.pg0.assert_nothing_captured(
+            remark="IP multicast packets forwarded on PG0")
+        self.pg3.assert_nothing_captured(
+            remark="IP multicast packets forwarded on PG3")
+
         #
         # a stream that matches the route for (*,232.0.0.0/8)
         # Send packets with the 9th bit set so we test the correct clearing
@@ -316,7 +359,7 @@ class TestIPMcast(VppTestCase):
         self.pg_enable_capture(self.pg_interfaces)
         self.pg_start()
 
-        # We expect replications on Pg1, 2, 3.
+        # We expect replications on Pg1->7
         self.verify_capture_ip4(self.pg1, tx)
         self.verify_capture_ip4(self.pg2, tx)
         self.verify_capture_ip4(self.pg3, tx)
@@ -325,6 +368,10 @@ class TestIPMcast(VppTestCase):
         self.verify_capture_ip4(self.pg6, tx)
         self.verify_capture_ip4(self.pg7, tx)
 
+        # no replications on Pg0
+        self.pg0.assert_nothing_captured(
+            remark="IP multicast packets forwarded on PG0")
+
     def test_ip6_mcast(self):
         """ IPv6 Multicast Replication """
 
index 39b2b1a..17a42fe 100644 (file)
@@ -185,12 +185,14 @@ class VppRoutePath(object):
 class VppMRoutePath(VppRoutePath):
 
     def __init__(self, nh_sw_if_index, flags,
+                 nh=None,
                  proto=DpoProto.DPO_PROTO_IP4,
                  bier_imp=0):
-        super(VppMRoutePath, self).__init__(
-            "::" if proto is DpoProto.DPO_PROTO_IP6 else "0.0.0.0",
-            nh_sw_if_index,
-            proto=proto)
+        if not nh:
+            nh = "::" if proto is DpoProto.DPO_PROTO_IP6 else "0.0.0.0"
+        super(VppMRoutePath, self).__init__(nh,
+                                            nh_sw_if_index,
+                                            proto=proto)
         self.nh_i_flags = flags
         self.bier_imp = bier_imp
 
@@ -337,6 +339,7 @@ class VppIpMRoute(VppObject):
                                               self.e_flags,
                                               path.proto,
                                               path.nh_itf,
+                                              path.nh_addr,
                                               path.nh_i_flags,
                                               bier_imp=path.bier_imp,
                                               rpf_id=self.rpf_id,
@@ -352,6 +355,7 @@ class VppIpMRoute(VppObject):
                                               self.e_flags,
                                               path.proto,
                                               path.nh_itf,
+                                              path.nh_addr,
                                               path.nh_i_flags,
                                               table_id=self.table_id,
                                               bier_imp=path.bier_imp,
@@ -366,6 +370,7 @@ class VppIpMRoute(VppObject):
                                           self.e_flags,
                                           0,
                                           0xffffffff,
+                                          "",
                                           0,
                                           table_id=self.table_id,
                                           is_ipv6=self.is_ip6)
@@ -378,6 +383,7 @@ class VppIpMRoute(VppObject):
                                           self.e_flags,
                                           0,
                                           0xffffffff,
+                                          "",
                                           0,
                                           rpf_id=self.rpf_id,
                                           table_id=self.table_id,
@@ -394,6 +400,7 @@ class VppIpMRoute(VppObject):
                                           self.e_flags,
                                           path.proto,
                                           path.nh_itf,
+                                          path.nh_addr,
                                           path.nh_i_flags,
                                           table_id=self.table_id,
                                           is_ipv6=self.is_ip6)
index 6804b4c..44cb7b6 100644 (file)
@@ -2300,6 +2300,7 @@ class VppPapiProvider(object):
                           e_flags,
                           next_hop_afi,
                           next_hop_sw_if_index,
+                          next_hop_address,
                           i_flags,
                           bier_imp=0,
                           rpf_id=0,
@@ -2324,7 +2325,8 @@ class VppPapiProvider(object):
              'next_hop_afi': next_hop_afi,
              'grp_address_length': grp_address_length,
              'grp_address': grp_address,
-             'src_address': src_address})
+             'src_address': src_address,
+             'nh_address': next_hop_address})
 
     def mfib_signal_dump(self):
         return self.api(self.papi.mfib_signal_dump, {})