#!/usr/bin/env python
import subprocess
import unittest
import tempfile
import time
import resource
from collections import deque
from threading import Thread
from inspect import getdoc
from hook import StepHook, PollHook
from vpp_pg_interface import VppPGInterface
from vpp_lo_interface import VppLoInterface
from vpp_papi_provider import VppPapiProvider
from scapy.packet import Raw
from logging import FileHandler, DEBUG
from log import *
"""
Test framework module.
The module provides a set of tools for constructing and running tests and
representing the results.
"""
class _PacketInfo(object):
"""Private class to create packet info object.
Help process information about the next packet.
Set variables to default values.
"""
#: Store the index of the packet.
index = -1
#: Store the index of the source packet generator interface of the packet.
src = -1
#: Store the index of the destination packet generator interface
#: of the packet.
dst = -1
#: Store the copy of the former packet.
data = None
def __eq__(self, other):
index = self.index == other.index
src = self.src == other.src
dst = self.dst == other.dst
data = self.data == other.data
return index and src and
@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.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 python3
import unittest
from framework import VppTestCase, VppTestRunner
from vpp_ip import DpoProto
from vpp_ip_route import VppIpMRoute, VppMRoutePath, VppMFibSignal, \
VppIpTable, FibPathProto
from vpp_gre_interface import VppGreInterface
from vpp_papi import VppEnum
from scapy.packet import Raw
from scapy.layers.l2 import Ether, GRE
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 """
@classmethod
def setUpClass(cls):
super(TestMFIB, cls).setUpClass()
@classmethod
def tearDownClass(cls):
super(TestMFIB, cls).tearDownClass()
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.assertNotIn("Failed", error)
class TestIPMcast(VppTestCase):
""" IP Multicast Test Case """
@classmethod
def setUpClass(cls):
super(TestIPMcast, cls).setUpClass()
@classmethod
def tearDownClass(cls):
super(TestIPMcast, cls).tearDownClass()
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=getmacbyip(dst_ip), 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(b'\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=getmacbyip6(dst_ip), 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 capture
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 """
MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
#
# a stream that matches the default route. gets dropped.
#
self.vapi.cli("clear trace")
self.vapi.cli("packet mac-filter pg0 on")
self.vapi.cli("packet mac-filter pg1 on")
self.vapi.cli("packet mac-filter pg2 on")
self.vapi.cli("packet mac-filter pg4 on")
self.vapi.cli("packet mac-filter pg5 on")
self.vapi.cli("packet mac-filter pg6 on")
self.vapi.cli("packet mac-filter pg7 on")
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 replicate 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_API_ENTRY_FLAG_NONE,
[VppMRoutePath(self.pg0.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
VppMRoutePath(self.pg1.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
VppMRoutePath(self.pg2.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
VppMRoutePath(self.pg3.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
VppMRoutePath(self.pg4.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
VppMRoutePath(self.pg5.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
VppMRoutePath(self.pg6.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
VppMRoutePath(self.pg7.sw_if_index,
MRouteItfFlags.MFIB_API_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", 27, # any grp-len is ok when src is set
MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
[VppMRoutePath(self.pg0.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
VppMRoutePath(self.pg1.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
VppMRoutePath(self.pg2.sw_if_index,
MRouteItfFlags.MFIB_API_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_API_ENTRY_FLAG_NONE,
[VppMRoutePath(self.pg0.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
VppMRoutePath(self.pg1.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
nh=self.pg1.remote_ip4),
VppMRoutePath(self.pg2.sw_if_index,
MRouteItfFlags.MFIB_API_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_API_ENTRY_FLAG_NONE,
[VppMRoutePath(self.pg0.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
VppMRoutePath(self.pg1.sw_if_index,
MRouteItfFlags.MFIB_API_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()
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)
# 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)
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
# 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)
self.assertEqual(route_232.get_stats()['packets'], len(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")
self.vapi.cli("packet mac-filter pg0 off")
self.vapi.cli("packet mac-filter pg1 off")
self.vapi.cli("packet mac-filter pg2 off")
self.vapi.cli("packet mac-filter pg4 off")
self.vapi.cli("packet mac-filter pg5 off")
self.vapi.cli("packet mac-filter pg6 off")
self.vapi.cli("packet mac-filter pg7 off")
def test_ip6_mcast(self):
""" IPv6 Multicast Replication """
MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
self.vapi.cli("packet mac-filter pg0 on")
self.vapi.cli("packet mac-filter pg1 on")
self.vapi.cli("packet mac-filter pg2 on")
self.vapi.cli("packet mac-filter pg4 on")
self.vapi.cli("packet mac-filter pg5 on")
self.vapi.cli("packet mac-filter pg6 on")
self.vapi.cli("packet mac-filter pg7 on")
#
# 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_API_ENTRY_FLAG_NONE,
[VppMRoutePath(self.pg0.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT,
proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
VppMRoutePath(self.pg1.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
VppMRoutePath(self.pg2.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
VppMRoutePath(self.pg3.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)])
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", 0, # any grp-len is ok when src is set
MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
[VppMRoutePath(self.pg0.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT,
proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
VppMRoutePath(self.pg1.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
VppMRoutePath(self.pg2.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)])
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_API_ENTRY_FLAG_NONE,
[VppMRoutePath(self.pg0.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT,
proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
VppMRoutePath(self.pg1.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)])
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")
self.vapi.cli("packet mac-filter pg0 off")
self.vapi.cli("packet mac-filter pg1 off")
self.vapi.cli("packet mac-filter pg2 off")
self.vapi.cli("packet mac-filter pg4 off")
self.vapi.cli("packet mac-filter pg5 off")
self.vapi.cli("packet mac-filter pg6 off")
self.vapi.cli("packet mac-filter pg7 off")
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 """
MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
#
# 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_API_ENTRY_FLAG_NONE,
[VppMRoutePath(self.pg0.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
VppMRoutePath(self.pg1.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
route_232_1_1_1.add_vpp_config()
route_232_1_1_1.update_entry_flags(
MRouteEntryFlags.MFIB_API_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_API_ENTRY_FLAG_NONE,
[VppMRoutePath(self.pg0.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
VppMRoutePath(self.pg1.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
route_232_1_1_2.add_vpp_config()
route_232_1_1_2.update_entry_flags(
MRouteEntryFlags.MFIB_API_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_API_ENTRY_FLAG_NONE)
route_232_1_1_2.update_entry_flags(
MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE)
def test_ip_mcast_signal(self):
""" IP Multicast Signal """
MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
#
# 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_API_ENTRY_FLAG_NONE,
[VppMRoutePath(self.pg0.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
VppMRoutePath(self.pg1.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
route_232_1_1_1.add_vpp_config()
route_232_1_1_1.update_entry_flags(
MRouteEntryFlags.MFIB_API_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_API_ITF_FLAG_ACCEPT |
MRouteItfFlags.MFIB_API_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_API_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_API_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"""
MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
#
# 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_API_ENTRY_FLAG_NONE,
[VppMRoutePath(self.pg8.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
VppMRoutePath(self.pg1.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
VppMRoutePath(self.pg2.sw_if_index,
MRouteItfFlags.MFIB_API_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_ip_mcast_gre(self):
""" IP Multicast Replication over GRE"""
MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
gre_if_1 = VppGreInterface(
self,
self.pg1.local_ip4,
self.pg1.remote_ip4).add_vpp_config()
gre_if_2 = VppGreInterface(
self,
self.pg2.local_ip4,
self.pg2.remote_ip4).add_vpp_config()
gre_if_3 = VppGreInterface(
self,
self.pg3.local_ip4,
self.pg3.remote_ip4).add_vpp_config()
gre_if_1.admin_up()
gre_if_1.config_ip4()
gre_if_2.admin_up()
gre_if_2.config_ip4()
gre_if_3.admin_up()
gre_if_3.config_ip4()
#
# 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.2.2.2", 64,
MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
[VppMRoutePath(gre_if_1.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
VppMRoutePath(gre_if_2.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
VppMRoutePath(gre_if_3.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
route_1_1_1_1_232_1_1_1.add_vpp_config()
#
# a stream that matches the route for (1.1.1.1,232.2.2.2)
# small packets
#
tx = (Ether(dst=self.pg1.local_mac,
src=self.pg1.remote_mac) /
IP(src=self.pg1.remote_ip4,
dst=self.pg1.local_ip4) /
GRE() /
IP(src="1.1.1.1", dst="232.2.2.2") /
UDP(sport=1234, dport=1234) /
Raw(b'\a5' * 64)) * 63
self.vapi.cli("clear trace")
self.pg1.add_stream(tx)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
# We expect replications on Pg2 & 3
# check the encap headers are as expected based on the egress tunnel
rxs = self.pg2.get_capture(len(tx))
for rx in rxs:
self.assertEqual(rx[IP].src, gre_if_2.t_src)
self.assertEqual(rx[IP].dst, gre_if_2.t_dst)
self.assert_packet_checksums_valid(rx)
rxs = self.pg3.get_capture(len(tx))
for rx in rxs:
self.assertEqual(rx[IP].src, gre_if_3.t_src)
self.assertEqual(rx[IP].dst, gre_if_3.t_dst)
self.assert_packet_checksums_valid(rx)
def test_ip6_mcast_gre(self):
""" IP6 Multicast Replication over GRE"""
MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
gre_if_1 = VppGreInterface(
self,
self.pg1.local_ip4,
self.pg1.remote_ip4).add_vpp_config()
gre_if_2 = VppGreInterface(
self,
self.pg2.local_ip4,
self.pg2.remote_ip4).add_vpp_config()
gre_if_3 = VppGreInterface(
self,
self.pg3.local_ip4,
self.pg3.remote_ip4).add_vpp_config()
gre_if_1.admin_up()
gre_if_1.config_ip6()
gre_if_2.admin_up()
gre_if_2.config_ip6()
gre_if_3.admin_up()
gre_if_3.config_ip6()
#
# An (S,G).
# one accepting interface, pg0, 2 forwarding interfaces
#
route_1_1_FF_1 = VppIpMRoute(
self,
"1::1",
"FF00::1", 256,
MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
[VppMRoutePath(gre_if_1.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
VppMRoutePath(gre_if_2.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
VppMRoutePath(gre_if_3.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
route_1_1_FF_1.add_vpp_config()
#
# a stream that matches the route for (1::1, FF::1)
# small packets
#
tx = (Ether(dst=self.pg1.local_mac,
src=self.pg1.remote_mac) /
IP(src=self.pg1.remote_ip4,
dst=self.pg1.local_ip4) /
GRE() /
IPv6(src="1::1", dst="FF00::1") /
UDP(sport=1234, dport=1234) /
Raw(b'\a5' * 64)) * 63
self.vapi.cli("clear trace")
self.pg1.add_stream(tx)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
# We expect replications on Pg2 & 3
# check the encap headers are as expected based on the egress tunnel
rxs = self.pg2.get_capture(len(tx))
for rx in rxs:
self.assertEqual(rx[IP].src, gre_if_2.t_src)
self.assertEqual(rx[IP].dst, gre_if_2.t_dst)
self.assert_packet_checksums_valid(rx)
rxs = self.pg3.get_capture(len(tx))
for rx in rxs:
self.assertEqual(rx[IP].src, gre_if_3.t_src)
self.assertEqual(rx[IP].dst, gre_if_3.t_dst)
self.assert_packet_checksums_valid(rx)
def test_ip6_mcast_vrf(self):
""" IPv6 Multicast Replication in non-default table"""
MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
#
# An (S,G).
# one accepting interface, pg0, 2 forwarding interfaces
#
route_2001_ff01_1 = VppIpMRoute(
self,
"2001::1",
"ff01::1", 256,
MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
[VppMRoutePath(self.pg8.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT,
proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
VppMRoutePath(self.pg1.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
VppMRoutePath(self.pg2.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)],
table_id=10)
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 """
MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
#
# 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_API_ENTRY_FLAG_NONE,
[VppMRoutePath(self.pg0.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT |
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
VppMRoutePath(self.pg1.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT |
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
VppMRoutePath(self.pg2.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT |
MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
VppMRoutePath(self.pg3.sw_if_index,
MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT |
MRouteItfFlags.MFIB_API_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)