mroute routers in the stats segment
[vpp.git] / test / test_ip_mcast.py
index 34ee417..1a39e9a 100644 (file)
@@ -3,39 +3,23 @@
 import unittest
 
 from framework import VppTestCase, VppTestRunner
 import unittest
 
 from framework import VppTestCase, VppTestRunner
-from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint
-from vpp_ip_route import VppIpMRoute, VppMRoutePath, VppMFibSignal
+from vpp_ip import DpoProto
+from vpp_ip_route import VppIpMRoute, VppMRoutePath, VppMFibSignal, \
+    MRouteItfFlags, MRouteEntryFlags, VppIpTable
 
 from scapy.packet import Raw
 from scapy.layers.l2 import Ether
 from scapy.layers.inet import IP, UDP, getmacbyip
 from scapy.layers.inet6 import IPv6, getmacbyip6
 
 from scapy.packet import Raw
 from scapy.layers.l2 import Ether
 from scapy.layers.inet import IP, UDP, getmacbyip
 from scapy.layers.inet6 import IPv6, getmacbyip6
-from util import ppp
-
-
-class MRouteItfFlags:
-    MFIB_ITF_FLAG_NONE = 0
-    MFIB_ITF_FLAG_NEGATE_SIGNAL = 1
-    MFIB_ITF_FLAG_ACCEPT = 2
-    MFIB_ITF_FLAG_FORWARD = 4
-    MFIB_ITF_FLAG_SIGNAL_PRESENT = 8
-    MFIB_ITF_FLAG_INTERNAL_COPY = 16
-
-
-class MRouteEntryFlags:
-    MFIB_ENTRY_FLAG_NONE = 0
-    MFIB_ENTRY_FLAG_SIGNAL = 1
-    MFIB_ENTRY_FLAG_DROP = 2
-    MFIB_ENTRY_FLAG_CONNECTED = 4
-    MFIB_ENTRY_FLAG_INHERIT_ACCEPT = 8
 
 #
 
 #
-# The number of packets sent is set to 90 so that when we replicate more than 3
+# The number of packets sent is set to 91 so that when we replicate more than 3
 # times, which we do for some entries, we will generate more than 256 packets
 # to the next node in the VLIB graph. Thus we are testing the code's
 # times, which we do for some entries, we will generate more than 256 packets
 # to the next node in the VLIB graph. Thus we are testing the code's
-# correctness handling this over-flow
+# correctness handling this over-flow.
+# It's also an odd number so we hit any single loops.
 #
 #
-N_PKTS_IN_STREAM = 90
+N_PKTS_IN_STREAM = 91
 
 
 class TestMFIB(VppTestCase):
 
 
 class TestMFIB(VppTestCase):
@@ -59,27 +43,49 @@ class TestIPMcast(VppTestCase):
     def setUp(self):
         super(TestIPMcast, self).setUp()
 
     def setUp(self):
         super(TestIPMcast, self).setUp()
 
-        # create 4 pg interfaces
-        self.create_pg_interfaces(range(4))
+        # create 8 pg interfaces
+        self.create_pg_interfaces(range(9))
 
         # setup interfaces
 
         # setup interfaces
-        for i in self.pg_interfaces:
+        for i in self.pg_interfaces[:8]:
             i.admin_up()
             i.config_ip4()
             i.config_ip6()
             i.resolve_arp()
             i.resolve_ndp()
 
             i.admin_up()
             i.config_ip4()
             i.config_ip6()
             i.resolve_arp()
             i.resolve_ndp()
 
-    def create_stream_ip4(self, src_if, src_ip, dst_ip):
+        # one more in a vrf
+        tbl4 = VppIpTable(self, 10)
+        tbl4.add_vpp_config()
+        self.pg8.set_table_ip4(10)
+        self.pg8.config_ip4()
+
+        tbl6 = VppIpTable(self, 10, is_ip6=1)
+        tbl6.add_vpp_config()
+        self.pg8.set_table_ip6(10)
+        self.pg8.config_ip6()
+
+    def tearDown(self):
+        for i in self.pg_interfaces:
+            i.unconfig_ip4()
+            i.unconfig_ip6()
+            i.admin_down()
+
+        self.pg8.set_table_ip4(0)
+        self.pg8.set_table_ip6(0)
+        super(TestIPMcast, self).tearDown()
+
+    def create_stream_ip4(self, src_if, src_ip, dst_ip, payload_size=0):
         pkts = []
         pkts = []
+        # default to small packet sizes
+        p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
+             IP(src=src_ip, dst=dst_ip) /
+             UDP(sport=1234, dport=1234))
+        if not payload_size:
+            payload_size = 64 - len(p)
+            p = p / Raw('\xa5' * payload_size)
+
         for i in range(0, N_PKTS_IN_STREAM):
         for i in range(0, N_PKTS_IN_STREAM):
-            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_ip, dst=dst_ip) /
-                 UDP(sport=1234, dport=1234) /
-                 Raw(payload))
-            info.data = p.copy()
             pkts.append(p)
         return pkts
 
             pkts.append(p)
         return pkts
 
@@ -104,8 +110,8 @@ class TestIPMcast(VppTestCase):
                     capture.remove(p)
         return capture
 
                     capture.remove(p)
         return capture
 
-    def verify_capture_ip4(self, src_if, sent):
-        rxd = self.pg1.get_capture(N_PKTS_IN_STREAM)
+    def verify_capture_ip4(self, rx_if, sent, dst_mac=None):
+        rxd = rx_if.get_capture(len(sent))
 
         try:
             capture = self.verify_filter(rxd, sent)
 
         try:
             capture = self.verify_filter(rxd, sent)
@@ -116,15 +122,17 @@ class TestIPMcast(VppTestCase):
                 tx = sent[i]
                 rx = capture[i]
 
                 tx = sent[i]
                 rx = capture[i]
 
-                # the rx'd packet has the MPLS label popped
                 eth = rx[Ether]
                 self.assertEqual(eth.type, 0x800)
 
                 tx_ip = tx[IP]
                 rx_ip = rx[IP]
 
                 eth = rx[Ether]
                 self.assertEqual(eth.type, 0x800)
 
                 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
                 # 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)
 
                 self.assertEqual(rx_ip.src, tx_ip.src)
                 self.assertEqual(rx_ip.dst, tx_ip.dst)
@@ -134,8 +142,8 @@ class TestIPMcast(VppTestCase):
         except:
             raise
 
         except:
             raise
 
-    def verify_capture_ip6(self, src_if, sent):
-        capture = self.pg1.get_capture(N_PKTS_IN_STREAM)
+    def verify_capture_ip6(self, rx_if, sent):
+        capture = rx_if.get_capture(len(sent))
 
         self.assertEqual(len(capture), len(sent))
 
 
         self.assertEqual(len(capture), len(sent))
 
@@ -143,7 +151,6 @@ class TestIPMcast(VppTestCase):
             tx = sent[i]
             rx = capture[i]
 
             tx = sent[i]
             rx = capture[i]
 
-            # the rx'd packet has the MPLS label popped
             eth = rx[Ether]
             self.assertEqual(eth.type, 0x86DD)
 
             eth = rx[Ether]
             self.assertEqual(eth.type, 0x86DD)
 
@@ -176,7 +183,9 @@ class TestIPMcast(VppTestCase):
 
         #
         # A (*,G).
 
         #
         # A (*,G).
-        # one accepting interface, pg0, 3 forwarding interfaces
+        # one accepting interface, pg0, 7 forwarding interfaces
+        #  many forwarding interfaces test the case where the replicare DPO
+        #  needs to use extra cache lines for the buckets.
         #
         route_232_1_1_1 = VppIpMRoute(
             self,
         #
         route_232_1_1_1 = VppIpMRoute(
             self,
@@ -190,6 +199,14 @@ class TestIPMcast(VppTestCase):
              VppMRoutePath(self.pg2.sw_if_index,
                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
              VppMRoutePath(self.pg3.sw_if_index,
              VppMRoutePath(self.pg2.sw_if_index,
                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
              VppMRoutePath(self.pg3.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
+             VppMRoutePath(self.pg4.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
+             VppMRoutePath(self.pg5.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
+             VppMRoutePath(self.pg6.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
+             VppMRoutePath(self.pg7.sw_if_index,
                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
         route_232_1_1_1.add_vpp_config()
 
                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
         route_232_1_1_1.add_vpp_config()
 
@@ -210,6 +227,26 @@ class TestIPMcast(VppTestCase):
                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
         route_1_1_1_1_232_1_1_1.add_vpp_config()
 
                            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
         #
         # An (*,G/m).
         # one accepting interface, pg0, 1 forwarding interfaces
@@ -227,6 +264,7 @@ class TestIPMcast(VppTestCase):
 
         #
         # a stream that matches the route for (1.1.1.1,232.1.1.1)
 
         #
         # a stream that matches the route for (1.1.1.1,232.1.1.1)
+        #  small packets
         #
         self.vapi.cli("clear trace")
         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1")
         #
         self.vapi.cli("clear trace")
         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1")
@@ -235,7 +273,10 @@ class TestIPMcast(VppTestCase):
         self.pg_enable_capture(self.pg_interfaces)
         self.pg_start()
 
         self.pg_enable_capture(self.pg_interfaces)
         self.pg_start()
 
-        # We expect replications on Pg1, 2,
+        self.assertEqual(route_1_1_1_1_232_1_1_1.get_stats()['packets'],
+                         len(tx))
+
+        # 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.pg1, tx)
         self.verify_capture_ip4(self.pg2, tx)
 
@@ -245,6 +286,51 @@ class TestIPMcast(VppTestCase):
         self.pg3.assert_nothing_captured(
             remark="IP multicast packets forwarded on PG3")
 
         self.pg3.assert_nothing_captured(
             remark="IP multicast packets forwarded on PG3")
 
+        #
+        # a stream that matches the route for (1.1.1.1,232.1.1.1)
+        #  large packets
+        #
+        self.vapi.cli("clear trace")
+        tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1",
+                                    payload_size=1024)
+        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)
+        self.verify_capture_ip4(self.pg2, tx)
+
+        self.assertEqual(route_1_1_1_1_232_1_1_1.get_stats()['packets'],
+                         2*len(tx))
+
+        # no replications on Pg0
+        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 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
         #
         # 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
@@ -259,6 +345,7 @@ class TestIPMcast(VppTestCase):
 
         # We expect replications on Pg1 only
         self.verify_capture_ip4(self.pg1, tx)
 
         # We expect replications on Pg1 only
         self.verify_capture_ip4(self.pg1, tx)
+        self.assertEqual(route_232.get_stats()['packets'], len(tx))
 
         # no replications on Pg0, Pg2 not Pg3
         self.pg0.assert_nothing_captured(
 
         # no replications on Pg0, Pg2 not Pg3
         self.pg0.assert_nothing_captured(
@@ -278,19 +365,19 @@ class TestIPMcast(VppTestCase):
         self.pg_enable_capture(self.pg_interfaces)
         self.pg_start()
 
         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)
         self.verify_capture_ip4(self.pg1, tx)
         self.verify_capture_ip4(self.pg2, tx)
         self.verify_capture_ip4(self.pg3, tx)
+        self.verify_capture_ip4(self.pg4, tx)
+        self.verify_capture_ip4(self.pg5, tx)
+        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")
 
 
         # no replications on Pg0
         self.pg0.assert_nothing_captured(
             remark="IP multicast packets forwarded on PG0")
 
-        route_232_1_1_1.remove_vpp_config()
-        route_1_1_1_1_232_1_1_1.remove_vpp_config()
-        route_232.remove_vpp_config()
-
     def test_ip6_mcast(self):
         """ IPv6 Multicast Replication """
 
     def test_ip6_mcast(self):
         """ IPv6 Multicast Replication """
 
@@ -317,13 +404,17 @@ class TestIPMcast(VppTestCase):
             "ff01::1", 128,
             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
             [VppMRoutePath(self.pg0.sw_if_index,
             "ff01::1", 128,
             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
             [VppMRoutePath(self.pg0.sw_if_index,
-                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
+                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT,
+                           proto=DpoProto.DPO_PROTO_IP6),
              VppMRoutePath(self.pg1.sw_if_index,
              VppMRoutePath(self.pg1.sw_if_index,
-                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
+                           proto=DpoProto.DPO_PROTO_IP6),
              VppMRoutePath(self.pg2.sw_if_index,
              VppMRoutePath(self.pg2.sw_if_index,
-                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
+                           proto=DpoProto.DPO_PROTO_IP6),
              VppMRoutePath(self.pg3.sw_if_index,
              VppMRoutePath(self.pg3.sw_if_index,
-                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
+                           proto=DpoProto.DPO_PROTO_IP6)],
             is_ip6=1)
         route_ff01_1.add_vpp_config()
 
             is_ip6=1)
         route_ff01_1.add_vpp_config()
 
@@ -337,11 +428,14 @@ class TestIPMcast(VppTestCase):
             "ff01::1", 256,
             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
             [VppMRoutePath(self.pg0.sw_if_index,
             "ff01::1", 256,
             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
             [VppMRoutePath(self.pg0.sw_if_index,
-                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
+                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT,
+                           proto=DpoProto.DPO_PROTO_IP6),
              VppMRoutePath(self.pg1.sw_if_index,
              VppMRoutePath(self.pg1.sw_if_index,
-                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
+                           proto=DpoProto.DPO_PROTO_IP6),
              VppMRoutePath(self.pg2.sw_if_index,
              VppMRoutePath(self.pg2.sw_if_index,
-                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
+                           proto=DpoProto.DPO_PROTO_IP6)],
             is_ip6=1)
         route_2001_ff01_1.add_vpp_config()
 
             is_ip6=1)
         route_2001_ff01_1.add_vpp_config()
 
@@ -355,14 +449,25 @@ class TestIPMcast(VppTestCase):
             "ff01::", 16,
             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
             [VppMRoutePath(self.pg0.sw_if_index,
             "ff01::", 16,
             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
             [VppMRoutePath(self.pg0.sw_if_index,
-                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
+                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT,
+                           proto=DpoProto.DPO_PROTO_IP6),
              VppMRoutePath(self.pg1.sw_if_index,
              VppMRoutePath(self.pg1.sw_if_index,
-                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
+                           proto=DpoProto.DPO_PROTO_IP6)],
             is_ip6=1)
         route_ff01.add_vpp_config()
 
         #
         # a stream that matches the route for (*, ff01::/16)
             is_ip6=1)
         route_ff01.add_vpp_config()
 
         #
         # a stream that matches the route for (*, ff01::/16)
+        # sent on the non-accepting interface
+        #
+        self.vapi.cli("clear trace")
+        tx = self.create_stream_ip6(self.pg1, "2002::1", "ff01:2::255")
+        self.send_and_assert_no_replies(self.pg1, tx, "RPF miss")
+
+        #
+        # a stream that matches the route for (*, ff01::/16)
+        # sent on the accepting interface
         #
         self.vapi.cli("clear trace")
         tx = self.create_stream_ip6(self.pg0, "2002::1", "ff01:2::255")
         #
         self.vapi.cli("clear trace")
         tx = self.create_stream_ip6(self.pg0, "2002::1", "ff01:2::255")
@@ -382,6 +487,22 @@ class TestIPMcast(VppTestCase):
         self.pg3.assert_nothing_captured(
             remark="IP multicast packets forwarded on PG3")
 
         self.pg3.assert_nothing_captured(
             remark="IP multicast packets forwarded on PG3")
 
+        #
+        # Bounce the interface and it should still work
+        #
+        self.pg1.admin_down()
+        self.pg0.add_stream(tx)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg1.assert_nothing_captured(
+            remark="IP multicast packets forwarded on down PG1")
+
+        self.pg1.admin_up()
+        self.pg0.add_stream(tx)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.verify_capture_ip6(self.pg1, tx)
+
         #
         # a stream that matches the route for (*,ff01::1)
         #
         #
         # a stream that matches the route for (*,ff01::1)
         #
@@ -421,10 +542,6 @@ class TestIPMcast(VppTestCase):
         self.pg3.assert_nothing_captured(
             remark="IP multicast packets forwarded on PG3")
 
         self.pg3.assert_nothing_captured(
             remark="IP multicast packets forwarded on PG3")
 
-        route_ff01.remove_vpp_config()
-        route_ff01_1.remove_vpp_config()
-        route_2001_ff01_1.remove_vpp_config()
-
     def _mcast_connected_send_stream(self, dst_ip):
         self.vapi.cli("clear trace")
         tx = self.create_stream_ip4(self.pg0,
     def _mcast_connected_send_stream(self, dst_ip):
         self.vapi.cli("clear trace")
         tx = self.create_stream_ip4(self.pg0,
@@ -531,8 +648,10 @@ class TestIPMcast(VppTestCase):
         signal_232_1_1_1_itf_0.compare(signals[1])
         signal_232_1_1_2_itf_0.compare(signals[0])
 
         signal_232_1_1_1_itf_0.compare(signals[1])
         signal_232_1_1_2_itf_0.compare(signals[0])
 
-        route_232_1_1_1.remove_vpp_config()
-        route_232_1_1_2.remove_vpp_config()
+        route_232_1_1_1.update_entry_flags(
+            MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE)
+        route_232_1_1_2.update_entry_flags(
+            MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE)
 
     def test_ip_mcast_signal(self):
         """ IP Multicast Signal """
 
     def test_ip_mcast_signal(self):
         """ IP Multicast Signal """
@@ -596,6 +715,7 @@ class TestIPMcast(VppTestCase):
             (MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
              MRouteItfFlags.MFIB_ITF_FLAG_NEGATE_SIGNAL))
 
             (MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
              MRouteItfFlags.MFIB_ITF_FLAG_NEGATE_SIGNAL))
 
+        self.vapi.cli("clear trace")
         tx = self._mcast_connected_send_stream("232.1.1.1")
 
         signals = self.vapi.mfib_signal_dump()
         tx = self._mcast_connected_send_stream("232.1.1.1")
 
         signals = self.vapi.mfib_signal_dump()
@@ -625,10 +745,118 @@ class TestIPMcast(VppTestCase):
         signals = self.vapi.mfib_signal_dump()
         self.assertEqual(0, len(signals))
 
         signals = self.vapi.mfib_signal_dump()
         self.assertEqual(0, len(signals))
 
+    def test_ip_mcast_vrf(self):
+        """ IP Multicast Replication in non-default table"""
+
+        #
+        # An (S,G).
+        # one accepting interface, pg0, 2 forwarding interfaces
+        #
+        route_1_1_1_1_232_1_1_1 = VppIpMRoute(
+            self,
+            "1.1.1.1",
+            "232.1.1.1", 64,
+            MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
+            [VppMRoutePath(self.pg8.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
+             VppMRoutePath(self.pg1.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
+             VppMRoutePath(self.pg2.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
+            table_id=10)
+        route_1_1_1_1_232_1_1_1.add_vpp_config()
+
+        #
+        # a stream that matches the route for (1.1.1.1,232.1.1.1)
+        #  small packets
+        #
+        self.vapi.cli("clear trace")
+        tx = self.create_stream_ip4(self.pg8, "1.1.1.1", "232.1.1.1")
+        self.pg8.add_stream(tx)
+
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        # We expect replications on Pg1 & 2
+        self.verify_capture_ip4(self.pg1, tx)
+        self.verify_capture_ip4(self.pg2, tx)
+
+    def test_ip6_mcast_vrf(self):
+        """ IPv6 Multicast Replication in non-default table"""
+
+        #
+        # An (S,G).
+        # one accepting interface, pg0, 2 forwarding interfaces
+        #
+        route_2001_ff01_1 = VppIpMRoute(
+            self,
+            "2001::1",
+            "ff01::1", 256,
+            MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
+            [VppMRoutePath(self.pg8.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT,
+                           proto=DpoProto.DPO_PROTO_IP6),
+             VppMRoutePath(self.pg1.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
+                           proto=DpoProto.DPO_PROTO_IP6),
+             VppMRoutePath(self.pg2.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
+                           proto=DpoProto.DPO_PROTO_IP6)],
+            table_id=10,
+            is_ip6=1)
+        route_2001_ff01_1.add_vpp_config()
+
         #
         #
-        # Cleanup
+        # a stream that matches the route for (2001::1, ff00::1)
         #
         #
-        route_232_1_1_1.remove_vpp_config()
+        self.vapi.cli("clear trace")
+        tx = self.create_stream_ip6(self.pg8, "2001::1", "ff01::1")
+        self.pg8.add_stream(tx)
+
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        # We expect replications on Pg1, 2,
+        self.verify_capture_ip6(self.pg1, tx)
+        self.verify_capture_ip6(self.pg2, tx)
+
+    def test_bidir(self):
+        """ IP Multicast Bi-directional """
+
+        #
+        # A (*,G). The set of accepting interfaces matching the forwarding
+        #
+        route_232_1_1_1 = VppIpMRoute(
+            self,
+            "0.0.0.0",
+            "232.1.1.1", 32,
+            MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
+            [VppMRoutePath(self.pg0.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
+             VppMRoutePath(self.pg1.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
+             VppMRoutePath(self.pg2.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
+             VppMRoutePath(self.pg3.sw_if_index,
+                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
+                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
+        route_232_1_1_1.add_vpp_config()
+
+        tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1")
+        self.pg0.add_stream(tx)
+
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        # We expect replications on Pg1, 2, 3, but not on pg0
+        self.verify_capture_ip4(self.pg1, tx)
+        self.verify_capture_ip4(self.pg2, tx)
+        self.verify_capture_ip4(self.pg3, tx)
+        self.pg0.assert_nothing_captured(
+            remark="IP multicast packets forwarded on PG0")
 
 
 if __name__ == '__main__':
 
 
 if __name__ == '__main__':