summaryrefslogtreecommitdiffstats
path: root/src/vnet/gre
AgeCommit message (Expand)AuthorFilesLines
2019-04-08fixing typosJim Thompson1-1/+1
2019-03-26ADJ: midchain delegate to performing stackingNeale Ranns2-94/+7
2019-03-26Convert GRE nodes to new buffer APIs and multiarchBenoƮt Ganne2-618/+470
2019-03-13VPP-1576: fix a set of coverity warningsDave Barach1-2/+2
2019-03-05gre: migrate old MULTIARCH macros to VLIB_NODE_FNFilip Tehlar2-19/+20
2018-12-07FIB recusrion loop checks traverse midchain adjacenciesNeale Ranns2-39/+12
2018-10-23c11 safe string handling supportDave Barach4-9/+9
2018-10-19vppinfra: add atomic macros for __sync builtinsSirshak Das1-3/+3
2018-09-25L2 BD: introduce a BD interface on which to send UU packetsNeale Ranns1-1/+2
2018-09-19GRE: fix 4o6 and 6o4 adj stackingNeale Ranns1-3/+3
2018-09-08L2 BVI/FIB: Update L2 FIB table when BVI's MAC changesNeale Ranns1-0/+1
2018-07-23fix vector index range checksEyal Bari1-1/+1
2018-07-11avoid using thread local storage for thread indexDamjan Marion1-1/+1
2018-06-11MTU: Software interface / Per-protocol MTU supportOle Troan1-4/+1
2018-05-23VPP-1283: IPv6 PMTU missing MTU value in ICMP6 message.Ole Troan1-2/+2
2018-05-04Harmonize vec/pool_get_aligned object sizes and alignment requestsDave Barach1-0/+5
2018-04-13Revert "MTU: Setting of MTU on software interface (instead of hardware interf...Damjan Marion1-1/+5
2018-04-13MTU: Setting of MTU on software interface (instead of hardware interface)Ole Troan1-5/+1
2018-04-12Fixes for 'make UNATTENDED=yes CC=clang CXX=clang verify'Neale Ranns1-21/+1
2018-03-01Fix ERSPAN encap to set EN bits in the header and add test caseJohn Lo2-9/+12
2018-02-16Minor VXLAN/GRE Tunnel Encap Optimization and CleanupJohn Lo2-15/+20
2018-02-15Optimize GRE Tunnel and add support for ERSPAN encapJohn Lo7-338/+677
2018-01-30Allow the provider of a midchain adjacency to pass context data that is retur...Neale Ranns1-4/+7
2018-01-23VPPAPIGEN: vppapigen replacement in Python PLY.Ole Troan1-1/+1
2018-01-13Improve "show xxx tunnel" and "show int addr" outputJohn Lo1-23/+7
2018-01-11api: remove transport specific code from handlersFlorin Coras1-10/+8
2018-01-09api: refactor vlibmemoryFlorin Coras1-2/+2
2018-01-09GRE tunnel key should use fib_index instead of fib_id (VPP-1118)John Lo1-12/+1
2018-01-06GRE tunnel key should use fib_index instead of fib_id (VPP-1118)John Lo1-2/+11
2017-12-16GRE tunnel key includes the FIB tableNeale Ranns4-137/+219
2017-12-13GRE: fix single loop decap and add testNeale Ranns1-1/+1
2017-11-27VPP-253 Coding standards cleanup - vnet/vnet/greSwarup Nayak6-878/+968
2017-11-26FIB: store the node type not the function pointer.Neale Ranns1-2/+0
2017-10-24Add extern to *_main global variable declarations in header files.Dave Wallace1-1/+1
2017-10-09vppapigen: support per-file (major,minor,patch) version stampsDave Barach1-0/+2
2017-10-04[aarch64] Fixes CLI crashes on dpaa2 platform.Christophe Fontaine1-1/+1
2017-04-13Remove unsed parameter from fib_table_entry_special_add() (only used in FIB t...Neale Ranns1-2/+1
2017-04-06Use thread local storage for thread indexDamjan Marion1-4/+4
2017-04-05GRE over IPv6Ciara Loftus5-165/+533
2017-04-04Fix issue with gre protocol registration when running multithreadedDamjan Marion2-39/+15
2017-04-03Adjacency layout change and move to vnet/adjNeale Ranns1-2/+2
2017-03-16vlib: make runtime_data thread-localDamjan Marion1-5/+21
2017-03-09Tests to target holes in adjacency and DPO test coverageNeale Ranns1-1/+3
2017-02-22VPP-635: CLI Memory leak with invalid parameterBilly McFall1-10/+25
2017-02-13Basic support for LISP-GPE encapsulated NSH packetsFlorin Coras1-0/+3
2016-12-28Reorganize source tree to use single autotools instanceDamjan Marion9-0/+2243
.Inserted */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #75715e } /* Generic.Subheading */ .highlight .kc { color: #66d9ef } /* Keyword.Constant */ .highlight .kd { color: #66d9ef } /* Keyword.Declaration */ .highlight .kn { color: #f92672 } /* Keyword.Namespace */ .highlight .kp { color: #66d9ef } /* Keyword.Pseudo */ .highlight .kr { color: #66d9ef } /* Keyword.Reserved */ .highlight .kt { color: #66d9ef } /* Keyword.Type */ .highlight .ld { color: #e6db74 } /* Literal.Date */ .highlight .m { color: #ae81ff } /* Literal.Number */ .highlight .s { color: #e6db74 } /* Literal.String */ .highlight .na { color: #a6e22e } /* Name.Attribute */ .highlight .nb { color: #f8f8f2 } /* Name.Builtin */ .highlight .nc { color: #a6e22e } /* Name.Class */ .highlight .no { color: #66d9ef } /* Name.Constant */ .highlight .nd { color: #a6e22e } /* Name.Decorator */ .highlight .ni { color: #f8f8f2 } /* Name.Entity */ .highlight .ne { color: #a6e22e } /* Name.Exception */ .highlight .nf { color: #a6e22e } /* Name.Function */ .highlight .nl { color: #f8f8f2 } /* Name.Label */ .highlight .nn { color: #f8f8f2 } /* Name.Namespace */ .highlight .nx { color: #a6e22e } /* Name.Other */ .highlight .py { color: #f8f8f2 } /* Name.Property */ .highlight .nt { color: #f92672 } /* Name.Tag */ .highlight .nv { color: #f8f8f2 } /* Name.Variable */ .highlight .ow { color: #f92672 } /* Operator.Word */ .highlight .w { color: #f8f8f2 } /* Text.Whitespace */ .highlight .mb { color: #ae81ff } /* Literal.Number.Bin */ .highlight .mf { color: #ae81ff } /* Literal.Number.Float */ .highlight .mh { color: #ae81ff } /* Literal.Number.Hex */ .highlight .mi { color: #ae81ff } /* Literal.Number.Integer */ .highlight .mo { color: #ae81ff } /* Literal.Number.Oct */ .highlight .sa { color: #e6db74 } /* Literal.String.Affix */ .highlight .sb { color: #e6db74 } /* Literal.String.Backtick */ .highlight .sc { color: #e6db74 } /* Literal.String.Char */ .highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */ .highlight .sd { color: #e6db74 } /* Literal.String.Doc */ .highlight .s2 { color: #e6db74 } /* Literal.String.Double */ .highlight .se { color: #ae81ff } /* Literal.String.Escape */ .highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ .highlight .si { color: #e6db74 } /* Literal.String.Interpol */ .highlight .sx { color: #e6db74 } /* Literal.String.Other */ .highlight .sr { color: #e6db74 } /* Literal.String.Regex */ .highlight .s1 { color: #e6db74 } /* Literal.String.Single */ .highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ .highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #a6e22e } /* Name.Function.Magic */ .highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ .highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ .highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ .highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */ .highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ } @media (prefers-color-scheme: light) { .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ }
#!/usr/bin/env python

import unittest

from framework import VppTestCase, VppTestRunner
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

#
# 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
# correctness handling this over-flow.
# It's also an odd number so we hit any single loops.
#
N_PKTS_IN_STREAM = 91


class TestMFIB(VppTestCase):
    """ MFIB Test Case """

    def setUp(self):
        super(TestMFIB, self).setUp()

    def test_mfib(self):
        """ MFIB Unit Tests """
        error = self.vapi.cli("test mfib")

        if error:
            self.logger.critical(error)
        self.assertEqual(error.find("Failed"), -1)


class TestIPMcast(VppTestCase):
    """ IP Multicast Test Case """

    def setUp(self):
        super(TestIPMcast, self).setUp()

        # create 8 pg interfaces
        self.create_pg_interfaces(range(9))

        # setup interfaces
        for i in self.pg_interfaces[:8]:
            i.admin_up()
            i.config_ip4()
            i.config_ip6()
            i.resolve_arp()
            i.resolve_ndp()

        # 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 = []
        # 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):
            pkts.append(p)
        return pkts

    def create_stream_ip6(self, src_if, src_ip, dst_ip):
        pkts = []
        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) /
                 IPv6(src=src_ip, dst=dst_ip) /
                 UDP(sport=1234, dport=1234) /
                 Raw(payload))
            info.data = p.copy()
            pkts.append(p)
        return pkts

    def verify_filter(self, capture, sent):
        if not len(capture) == len(sent):
            # filter out any IPv6 RAs from the captur
            for p in capture:
                if (p.haslayer(IPv6)):
                    capture.remove(p)
        return capture

    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)

            self.assertEqual(len(capture), len(sent))

            for i in range(len(capture)):
                tx = sent[i]
                rx = capture[i]

                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
                self.assertEqual(eth.dst, dst_mac)

                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)

        except:
            raise

    def verify_capture_ip6(self, rx_if, sent):
        capture = rx_if.get_capture(len(sent))

        self.assertEqual(len(capture), len(sent))

        for i in range(len(capture)):
            tx = sent[i]
            rx = capture[i]

            eth = rx[Ether]
            self.assertEqual(eth.type, 0x86DD)

            tx_ip = tx[IPv6]
            rx_ip = rx[IPv6]

            # check the MAC address on the RX'd packet is correctly formed
            self.assertEqual(eth.dst, getmacbyip6(rx_ip.dst))

            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.hlim + 1, tx_ip.hlim)

    def test_ip_mcast(self):
        """ IP Multicast Replication """

        #
        # a stream that matches the default route. gets dropped.
        #
        self.vapi.cli("clear trace")
        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()

        self.pg0.assert_nothing_captured(
            remark="IP multicast packets forwarded on default route")

        #
        # A (*,G).
        # 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,
            "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),
             VppMRoutePath(self.pg1.sw_if_index,
                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
             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()

        #
        # 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.pg0.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)])
        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
        #
        route_232 = VppIpMRoute(
            self,
            "0.0.0.0",
            "232.0.0.0", 8,
            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)])
        route_232.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.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->7
        self.verify_capture_ip4(self.pg1, tx)
        self.verify_capture_ip4(self.pg2, 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 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)

        # 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
        # of that bit in the mac rewrite
        #
        self.vapi.cli("clear trace")
        tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.255.255.255")
        self.pg0.add_stream(tx)

        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()

        # We expect replications on Pg1 only
        self.verify_capture_ip4(self.pg1, tx)

        # no replications on Pg0, Pg2 not Pg3
        self.pg0.assert_nothing_captured(
            remark="IP multicast packets forwarded on PG0")
        self.pg2.assert_nothing_captured(
            remark="IP multicast packets forwarded on PG2")
        self.pg3.assert_nothing_captured(
            remark="IP multicast packets forwarded on PG3")

        #
        # a stream that matches the route for (*,232.1.1.1)
        #
        self.vapi.cli("clear trace")
        tx = self.create_stream_ip4(self.pg0, "1.1.1.2", "232.1.1.1")
        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.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")

    def test_ip6_mcast(self):
        """ IPv6 Multicast Replication """

        #
        # a stream that matches the default route. gets dropped.
        #
        self.vapi.cli("clear trace")
        tx = self.create_stream_ip6(self.pg0, "2001::1", "ff01::1")
        self.pg0.add_stream(tx)

        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()

        self.pg0.assert_nothing_captured(
            remark="IPv6 multicast packets forwarded on default route")

        #
        # A (*,G).
        # one accepting interface, pg0, 3 forwarding interfaces
        #
        route_ff01_1 = VppIpMRoute(
            self,
            "::",
            "ff01::1", 128,
            MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
            [VppMRoutePath(self.pg0.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),
             VppMRoutePath(self.pg3.sw_if_index,
                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
                           proto=DpoProto.DPO_PROTO_IP6)],
            is_ip6=1)
        route_ff01_1.add_vpp_config()

        #
        # 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.pg0.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)],
            is_ip6=1)
        route_2001_ff01_1.add_vpp_config()

        #
        # An (*,G/m).
        # one accepting interface, pg0, 1 forwarding interface
        #
        route_ff01 = VppIpMRoute(
            self,
            "::",
            "ff01::", 16,
            MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
            [VppMRoutePath(self.pg0.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)],
            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.pg0.add_stream(tx)

        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()

        # We expect replications on Pg1
        self.verify_capture_ip6(self.pg1, tx)

        # no replications on Pg0, Pg3
        self.pg0.assert_nothing_captured(
            remark="IP multicast packets forwarded on PG0")
        self.pg2.assert_nothing_captured(
            remark="IP multicast packets forwarded on PG2")
        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)
        #
        self.vapi.cli("clear trace")
        tx = self.create_stream_ip6(self.pg0, "2002::2", "ff01::1")
        self.pg0.add_stream(tx)

        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()

        # We expect replications on Pg1, 2, 3.
        self.verify_capture_ip6(self.pg1, tx)
        self.verify_capture_ip6(self.pg2, tx)
        self.verify_capture_ip6(self.pg3, tx)

        # no replications on Pg0
        self.pg0.assert_nothing_captured(
            remark="IPv6 multicast packets forwarded on PG0")

        #
        # a stream that matches the route for (2001::1, ff00::1)
        #
        self.vapi.cli("clear trace")
        tx = self.create_stream_ip6(self.pg0, "2001::1", "ff01::1")
        self.pg0.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)

        # no replications on Pg0, 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")

    def _mcast_connected_send_stream(self, dst_ip):
        self.vapi.cli("clear trace")
        tx = self.create_stream_ip4(self.pg0,
                                    self.pg0.remote_ip4,
                                    dst_ip)
        self.pg0.add_stream(tx)

        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()

        # We expect replications on Pg1.
        self.verify_capture_ip4(self.pg1, tx)

        return tx

    def test_ip_mcast_connected(self):
        """ IP Multicast Connected Source check """

        #
        # A (*,G).
        # one accepting interface, pg0, 1 forwarding interfaces
        #
        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),
             VppMRoutePath(self.pg1.sw_if_index,
                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])

        route_232_1_1_1.add_vpp_config()
        route_232_1_1_1.update_entry_flags(
            MRouteEntryFlags.MFIB_ENTRY_FLAG_CONNECTED)

        #
        # Now the (*,G) is present, send from connected source
        #
        tx = self._mcast_connected_send_stream("232.1.1.1")

        #
        # Constrct a representation of the signal we expect on pg0
        #
        signal_232_1_1_1_itf_0 = VppMFibSignal(self,
                                               route_232_1_1_1,
                                               self.pg0.sw_if_index,
                                               tx[0])

        #
        # read the only expected signal
        #
        signals = self.vapi.mfib_signal_dump()

        self.assertEqual(1, len(signals))

        signal_232_1_1_1_itf_0.compare(signals[0])

        #
        # reading the signal allows for the generation of another
        # so send more packets and expect the next signal
        #
        tx = self._mcast_connected_send_stream("232.1.1.1")

        signals = self.vapi.mfib_signal_dump()
        self.assertEqual(1, len(signals))
        signal_232_1_1_1_itf_0.compare(signals[0])

        #
        # A Second entry with connected check
        # one accepting interface, pg0, 1 forwarding interfaces
        #
        route_232_1_1_2 = VppIpMRoute(
            self,
            "0.0.0.0",
            "232.1.1.2", 32,
            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)])

        route_232_1_1_2.add_vpp_config()
        route_232_1_1_2.update_entry_flags(
            MRouteEntryFlags.MFIB_ENTRY_FLAG_CONNECTED)

        #
        # Send traffic to both entries. One read should net us two signals
        #
        signal_232_1_1_2_itf_0 = VppMFibSignal(self,
                                               route_232_1_1_2,
                                               self.pg0.sw_if_index,
                                               tx[0])
        tx = self._mcast_connected_send_stream("232.1.1.1")
        tx2 = self._mcast_connected_send_stream("232.1.1.2")

        #
        # read the only expected signal
        #
        signals = self.vapi.mfib_signal_dump()

        self.assertEqual(2, len(signals))

        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.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 """

        #
        # A (*,G).
        # one accepting interface, pg0, 1 forwarding interfaces
        #
        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),
             VppMRoutePath(self.pg1.sw_if_index,
                           MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])

        route_232_1_1_1.add_vpp_config()
        route_232_1_1_1.update_entry_flags(
            MRouteEntryFlags.MFIB_ENTRY_FLAG_SIGNAL)

        #
        # Now the (*,G) is present, send from connected source
        #
        tx = self._mcast_connected_send_stream("232.1.1.1")

        #
        # Constrct a representation of the signal we expect on pg0
        #
        signal_232_1_1_1_itf_0 = VppMFibSignal(self,
                                               route_232_1_1_1,
                                               self.pg0.sw_if_index,
                                               tx[0])

        #
        # read the only expected signal
        #
        signals = self.vapi.mfib_signal_dump()

        self.assertEqual(1, len(signals))

        signal_232_1_1_1_itf_0.compare(signals[0])

        #
        # reading the signal allows for the generation of another
        # so send more packets and expect the next signal
        #
        tx = self._mcast_connected_send_stream("232.1.1.1")

        signals = self.vapi.mfib_signal_dump()
        self.assertEqual(1, len(signals))
        signal_232_1_1_1_itf_0.compare(signals[0])

        #
        # Set the negate-signal on the accepting interval - the signals
        # should stop
        #
        route_232_1_1_1.update_path_flags(
            self.pg0.sw_if_index,
            (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()
        self.assertEqual(0, len(signals))

        #
        # Clear the SIGNAL flag on the entry and the signals should
        # come back since the interface is still NEGATE-SIGNAL
        #
        route_232_1_1_1.update_entry_flags(
            MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE)

        tx = self._mcast_connected_send_stream("232.1.1.1")

        signals = self.vapi.mfib_signal_dump()
        self.assertEqual(1, len(signals))
        signal_232_1_1_1_itf_0.compare(signals[0])

        #
        # Lastly remove the NEGATE-SIGNAL from the interface and the
        # signals should stop
        #
        route_232_1_1_1.update_path_flags(self.pg0.sw_if_index,
                                          MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT)

        tx = self._mcast_connected_send_stream("232.1.1.1")
        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()

        #
        # a stream that matches the route for (2001::1, ff00::1)
        #
        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__':
    unittest.main(testRunner=VppTestRunner)