コード例 #1
0
    def _verify_acl_traffic(self, setup, direction, ptfadapter, pkt, dropped,
                            ip_version):
        exp_pkt = self.expected_mask_routed_packet(pkt, ip_version)

        if ip_version == "ipv4":
            downstream_dst_port = DOWNSTREAM_IP_PORT_MAP.get(
                pkt[packet.IP].dst)
        else:
            downstream_dst_port = DOWNSTREAM_IP_PORT_MAP.get(
                pkt[packet.IPv6].dst)
        ptfadapter.dataplane.flush()
        testutils.send(ptfadapter, self.src_port, pkt)
        if direction == "uplink->downlink" and downstream_dst_port:
            if dropped:
                testutils.verify_no_packet(ptfadapter, exp_pkt,
                                           downstream_dst_port)
            else:
                testutils.verify_packet(ptfadapter, exp_pkt,
                                        downstream_dst_port)
        else:
            if dropped:
                testutils.verify_no_packet_any(ptfadapter,
                                               exp_pkt,
                                               ports=self.get_dst_ports(
                                                   setup, direction))
            else:
                testutils.verify_packet_any_port(ptfadapter,
                                                 exp_pkt,
                                                 ports=self.get_dst_ports(
                                                     setup, direction),
                                                 timeout=20)
コード例 #2
0
ファイル: mclag_helpers.py プロジェクト: vmorokhx/sonic-mgmt
def generate_and_verify_traffic(duthost1,
                                duthost2,
                                ptfadapter,
                                ptfhost,
                                src_port,
                                dst_ip,
                                router_mac,
                                get_routes,
                                collect,
                                down_link_on_dut=None,
                                pkt_action=ACTION_FORWARD):
    """
    Generate traffic, send and verify it
    Args:
        duthost1: DUT host object
        duthost2: DUT host object
        ptfadapter: PTF adapter
        ptfhost: PTF host object
        src_port: Source port from which pkt will be sent
        dst_ip: Destination ip address
        get_routes: Dict with routes for each DUT
        collect: Fixture which collects main info about link connection
        down_link_on_dut: Name of DUT on which link is down
        pkt_action: Action to verify, forward or drop
    """
    router1_mac = duthost1.facts["router_mac"]
    router2_mac = duthost2.facts["router_mac"]
    dst_ports = get_dst_port(duthost1, duthost2, get_routes, dst_ip, collect)
    src_port = get_port_number(ptfhost, src_port)
    pkt = craft_pkt(ptfadapter, router_mac, src_port, dst_ip)
    expected_src_mac = router1_mac if dst_ports == collect[
        duthost1.hostname]['vm_link_on_ptf'] else router2_mac

    exp_pkt = pkt.copy()
    if down_link_on_dut:
        exp_ttl = predict_exp_ttl(duthost1, duthost2, dst_ip, down_link_on_dut)
        exp_pkt[packet.IP].ttl = exp_ttl
    exp_pkt[packet.Ether].src = unicode(expected_src_mac)

    exp_pkt = mask.Mask(exp_pkt)
    exp_pkt.set_do_not_care_scapy(packet.Ether, "dst")
    exp_pkt.set_do_not_care_scapy(packet.IP, "id")
    exp_pkt.set_do_not_care_scapy(packet.IP, "chksum")
    exp_pkt.set_do_not_care_scapy(packet.TCP, "chksum")
    if not down_link_on_dut:
        exp_pkt.set_do_not_care_scapy(packet.IP, "ttl")

    ptfadapter.dataplane.flush()
    time.sleep(2)

    logger.info(
        "Sending pkt from port {} to dst_ip = {}, expected dst_port = {}".
        format(src_port, dst_ip, dst_ports))
    logger.info(pkt.sprintf("%Ether.src% %IP.src% -> %Ether.dst% %IP.dst%"))
    testutils.send(ptfadapter, src_port, pkt)

    if pkt_action == ACTION_FORWARD:
        testutils.verify_packet(ptfadapter, exp_pkt, dst_ports)
    elif pkt_action == ACTION_DROP:
        testutils.verify_no_packet(ptfadapter, exp_pkt, dst_ports)
コード例 #3
0
def mux_sim_check_downstream(active_tor, standby_tor, ptfadapter,
                             active_tor_ping_tgt_ip, standby_tor_ping_tgt_ip,
                             active_tor_exp_pkt, standby_tor_exp_pkt,
                             ptf_port_index, tor_mux_intf):
    failed = False
    reason = ''
    host = None
    ping_cmd = 'ping -I {} {} -c 1 -W 1; true'

    # Clear ARP tables to start in consistent state
    active_tor.shell("ip neigh flush all")
    standby_tor.shell("ip neigh flush all")

    # Ping from both ToRs, expect only message from active ToR to reach PTF
    active_tor.shell(ping_cmd.format(tor_mux_intf, active_tor_ping_tgt_ip))
    try:
        testutils.verify_packet(ptfadapter, active_tor_exp_pkt, ptf_port_index)
    except AssertionError:
        failed = True
        reason = 'Packet from active ToR {} not received'.format(active_tor)
        host = active_tor.hostname
        return failed, reason, host

    standby_tor.shell(ping_cmd.format(tor_mux_intf, standby_tor_ping_tgt_ip))
    try:
        testutils.verify_no_packet(ptfadapter, standby_tor_exp_pkt,
                                   ptf_port_index)
    except AssertionError:
        failed = True
        reason = 'Packet from standby ToR {} received'.format(standby_tor)
        host = standby_tor.hostname

    return failed, reason, host
コード例 #4
0
def send_and_verify_traffic(ptfadapter,
                            pkt,
                            exp_pkt,
                            src_port,
                            dst_port,
                            pkt_action=ACTION_FORWARD):
    """
    Send traffic and verify that traffic was received

    Args:
        ptfadapter: PTF adapter
        pkt: Packet that should be sent
        exp_pkt: Expected packet
        src_port: Source port
        dst_port: Destination port
        pkt_action: Packet action (forward or drop)
    """

    ptfadapter.dataplane.flush()
    logger.info("Send packet from port {} to port {}".format(
        src_port, dst_port))
    testutils.send(ptfadapter, src_port, pkt)

    if pkt_action == ACTION_FORWARD:
        testutils.verify_packet(ptfadapter, exp_pkt, dst_port)
    elif pkt_action == ACTION_DROP:
        testutils.verify_no_packet(ptfadapter, exp_pkt, dst_port)
コード例 #5
0
def send_and_verify_packet(ptfadapter, pkt, exp_pkt, tx_port, rx_port,
                           expected_action):
    """
    Send packet with ptfadapter and verify if packet is forwarded or dropped as expected.
    """
    ptfadapter.dataplane.flush()
    testutils.send(ptfadapter, pkt=pkt, port_id=tx_port)
    if expected_action == FORWARD:
        testutils.verify_packet(ptfadapter,
                                pkt=exp_pkt,
                                port_id=rx_port,
                                timeout=5)
    else:
        testutils.verify_no_packet(ptfadapter,
                                   pkt=exp_pkt,
                                   port_id=rx_port,
                                   timeout=5)
コード例 #6
0
ファイル: checks.py プロジェクト: vmittal-msft/sonic-mgmt
    def _check(*args, **kwargs):
        """
        @summary: Checks if the OVS bridge mux simulator is functioning correctly
        @return: A dictionary containing the testing result of the PTF interface tested:
            {
                'failed': <True/False>,
                'failed_reason': <reason string>,
                'intf': '<PTF interface name> mux simulator'
            }
        """
        results = {
            'failed': False,
            'failed_reason': '',
            'check_item': '{} mux simulator'.format(ptf_server_intf)
        }

        logger.info(
            "Checking mux simulator status for PTF interface {}".format(
                ptf_server_intf))
        ptf_port_index = int(ptf_server_intf.replace('eth', ''))
        recover_all_directions(tor_mux_intf)

        upper_tor_intf_mac, upper_tor_mgmt_ip = get_arp_pkt_info(
            upper_tor_host)
        lower_tor_intf_mac, lower_tor_mgmt_ip = get_arp_pkt_info(
            lower_tor_host)

        upper_tor_ping_tgt_ip = '10.10.10.1'
        lower_tor_ping_tgt_ip = '10.10.10.2'
        ptf_arp_tgt_ip = '10.10.10.3'
        ping_cmd = 'ping -I {} {} -c 1 -W 1; true'

        upper_tor_exp_pkt = testutils.simple_arp_packet(
            eth_dst='ff:ff:ff:ff:ff:ff',
            eth_src=upper_tor_intf_mac,
            ip_snd=upper_tor_mgmt_ip,
            ip_tgt=upper_tor_ping_tgt_ip,
            hw_snd=upper_tor_intf_mac)
        lower_tor_exp_pkt = testutils.simple_arp_packet(
            eth_dst='ff:ff:ff:ff:ff:ff',
            eth_src=lower_tor_intf_mac,
            ip_snd=lower_tor_mgmt_ip,
            ip_tgt=lower_tor_ping_tgt_ip,
            hw_snd=lower_tor_intf_mac)

        ptf_arp_pkt = testutils.simple_arp_packet(ip_tgt=ptf_arp_tgt_ip,
                                                  ip_snd=ptf_arp_tgt_ip,
                                                  arp_op=2)

        # Clear ARP tables to start in consistent state
        upper_tor_host.shell("ip neigh flush all")
        lower_tor_host.shell("ip neigh flush all")

        # Run tests with upper ToR active
        toggle_simulator_port_to_upper_tor(tor_mux_intf)

        try:
            pytest_assert(check_simulator_read_side(tor_mux_intf) == 1)
        except AssertionError:
            results['failed'] = True
            results[
                'failed_reason'] = 'Unable to switch active link to upper ToR'
            return results

        # Ping from both ToRs, expect only message from upper ToR to reach PTF
        upper_tor_host.shell(
            ping_cmd.format(tor_mux_intf, upper_tor_ping_tgt_ip))
        try:
            testutils.verify_packet(ptfadapter, upper_tor_exp_pkt,
                                    ptf_port_index)
        except AssertionError:
            results['failed'] = True
            results[
                'failed_reason'] = 'Packet from active upper ToR not received'
            return results

        lower_tor_host.shell(
            ping_cmd.format(tor_mux_intf, lower_tor_ping_tgt_ip))
        try:
            testutils.verify_no_packet(ptfadapter, lower_tor_exp_pkt,
                                       ptf_port_index)
        except AssertionError:
            results['failed'] = True
            results['failed_reason'] = 'Packet from standby lower ToR received'
            return results

        # Send dummy ARP packets from PTF to ToR. Ensure that ARP is learned on both ToRs
        upper_tor_host.shell("ip neigh flush all")
        lower_tor_host.shell("ip neigh flush all")
        testutils.send_packet(ptfadapter, ptf_port_index, ptf_arp_pkt)

        upper_tor_arp_table = upper_tor_host.switch_arptable(
        )['ansible_facts']['arptable']['v4']
        lower_tor_arp_table = lower_tor_host.switch_arptable(
        )['ansible_facts']['arptable']['v4']
        try:
            pytest_assert(ptf_arp_tgt_ip in upper_tor_arp_table)
        except AssertionError:
            results['failed'] = True
            results[
                'failed_reason'] = 'Packet from PTF not received on active upper ToR'
            return results

        try:
            pytest_assert(ptf_arp_tgt_ip in lower_tor_arp_table)
        except AssertionError:
            results['failed'] = True
            results[
                'failed_reason'] = 'Packet from PTF not received on standby lower ToR'
            return results

        # Repeat all tests with lower ToR active
        toggle_simulator_port_to_lower_tor(tor_mux_intf)
        try:
            pytest_assert(check_simulator_read_side(tor_mux_intf) == 2)
        except AssertionError:
            results['failed'] = True
            results[
                'failed_reason'] = 'Unable to switch active link to lower ToR'
            return results

        lower_tor_host.shell(
            ping_cmd.format(tor_mux_intf, lower_tor_ping_tgt_ip))
        try:
            testutils.verify_packet(ptfadapter, lower_tor_exp_pkt,
                                    ptf_port_index)
        except AssertionError:
            results['failed'] = True
            results[
                'failed_reason'] = 'Packet from active lower ToR not received'
            return results

        upper_tor_host.shell(
            ping_cmd.format(tor_mux_intf, upper_tor_ping_tgt_ip))
        try:
            testutils.verify_no_packet(ptfadapter, upper_tor_exp_pkt,
                                       ptf_port_index)
        except AssertionError:
            results['failed'] = True
            results['failed_reason'] = 'Packet from standby upper ToR received'
            return results

        upper_tor_host.shell("ip neigh flush all")
        lower_tor_host.shell("ip neigh flush all")
        testutils.send_packet(ptfadapter, ptf_port_index, ptf_arp_pkt)

        upper_tor_arp_table = upper_tor_host.switch_arptable(
        )['ansible_facts']['arptable']['v4']
        lower_tor_arp_table = lower_tor_host.switch_arptable(
        )['ansible_facts']['arptable']['v4']
        try:
            pytest_assert(ptf_arp_tgt_ip in upper_tor_arp_table)
        except AssertionError:
            results['failed'] = True
            results[
                'failed_reason'] = 'Packet from PTF not received on standby upper ToR'
            return results

        try:
            pytest_assert(ptf_arp_tgt_ip in lower_tor_arp_table)
        except AssertionError:
            results['failed'] = True
            results[
                'failed_reason'] = 'Packet from PTF not received on active lower ToR'
            return results

        logger.info('Finished mux simulator check')
        return results
コード例 #7
0
ファイル: simple_firewall_test.py プロジェクト: usha1830/p4c
 def send_server_packet_no_receive(self, packet):
     send_packet(self, (0, 2), packet)
     verify_no_packet(self, packet, port_id=1)
コード例 #8
0
ファイル: simple_firewall_test.py プロジェクト: usha1830/p4c
 def send_client_packet_no_receive(self, packet):
     send_packet(self, (0, 1), packet)
     verify_no_packet(self, packet, port_id=2)
コード例 #9
0
def test_l2_mac_move_1(npu, dataplane):
    """
    Description:
    Create VLAN 100 and add member ports 1, 2 and 3. Send packet on port 1 with
    src_mac=MAC1 and dst_mac=MAC2. Verify MAC1 is learnt on port 1 and packet is flooded
    to other member ports (2 and 3 in this example). Send packet on port 2 with src_mac=MAC2
    and dst_mac=MAC1. Verify MAC2 is learnt on port 2. After learning, verify that packet
    from port 1 is only forwarded to port 2 and not to port 3. Repeat the test by sending
    same packet (src_mac=MAC2 and dst_mac=MAC1), on port 3. Verify that station movement
    occurred and MAC2 is learnt on port 3. Packet from port 1 destined to MAC2 must be forwarded
    to port 3 and not to port 2 after the MAC-movement.

    Test scenario:
    1. Create VLAN 100 and associate 3 untagged ports (port 1,2,3) as the member port of VLAN 100
    2. Set attribute value of "Port VLAN ID 100" for the ports 1, 2, 3
    3. Send packet from port 1 with src_mac=MAC1 and dst_mac=MAC2
    4. Send packet from port 2 with src_mac=MAC2 and dst_mac=MAC1
    5. After MAC learning, repeat step 3 and verify that packet from port 1 is only forwarded to port 2 and not to port 3
    6. Send packet (src_mac=MAC2 and dst_mac=MAC1) on port 3
    7. Send packet (src_mac=MAC1 and dst_mac=MAC2) from port 1
    8. Clean up configuration
    """
    vlan_id = "100"
    macs = ['00:11:11:11:11:11', '00:22:22:22:22:22']
    max_port = 3
    vlan_mbr_oids = []
    vlan_oid = npu.create(SaiObjType.VLAN, ["SAI_VLAN_ATTR_VLAN_ID", vlan_id])

    for idx in range(max_port):
        npu.remove_vlan_member(npu.default_vlan_oid, npu.dot1q_bp_oids[idx])
        vlan_mbr = npu.create_vlan_member(vlan_oid, npu.dot1q_bp_oids[idx],
                                          "SAI_VLAN_TAGGING_MODE_UNTAGGED")
        vlan_mbr_oids.append(vlan_mbr)
        npu.set(npu.port_oids[idx], ["SAI_PORT_ATTR_PORT_VLAN_ID", vlan_id])

    try:
        if npu.run_traffic:

            pkt1 = simple_tcp_packet(eth_dst=macs[1],
                                     eth_src=macs[0],
                                     ip_dst='192.168.0.2',
                                     ip_src='192.168.0.1',
                                     ip_id=105,
                                     ip_ttl=64)

            pkt2 = simple_tcp_packet(eth_dst=macs[0],
                                     eth_src=macs[1],
                                     ip_dst='192.168.0.1',
                                     ip_src='192.168.0.2',
                                     ip_id=105,
                                     ip_ttl=64)
            send_packet(dataplane, 0, pkt1)
            verify_packets(dataplane, pkt1, [1, 2])

            send_packet(dataplane, 1, pkt2)
            verify_packets(dataplane, pkt2, [0])

            time.sleep(1)

            send_packet(dataplane, 0, pkt1)
            verify_packets(dataplane, pkt1, [1])
            verify_no_packet(dataplane, pkt1, 2)

            time.sleep(1)

            send_packet(dataplane, 2, pkt2)
            verify_packets(dataplane, pkt2, [0])

            time.sleep(1)

            send_packet(dataplane, 0, pkt1)
            verify_packets(dataplane, pkt1, [2])
            verify_no_packet(dataplane, pkt1, 1)

    finally:
        npu.flush_fdb_entries(["SAI_FDB_FLUSH_ATTR_BV_ID", vlan_oid])
        for idx in range(max_port):
            npu.remove(vlan_mbr_oids[idx])
            npu.create_vlan_member(npu.default_vlan_oid,
                                   npu.dot1q_bp_oids[idx],
                                   "SAI_VLAN_TAGGING_MODE_UNTAGGED")
            npu.set(npu.port_oids[idx],
                    ["SAI_PORT_ATTR_PORT_VLAN_ID", npu.default_vlan_id])

        npu.remove(vlan_oid)
コード例 #10
0
def test_l2_mtu(npu, dataplane):
    """
    Description:
    Send untagged packet with max MTU size and verify forwarding.
    Add VLAN tag so that the total packet size exceeds the MTU value and verify behavior.
    Default action is to drop packets exceeding configured MTU value.

    Test scenario:
    1. Create VLAN 10 in the database
    2. Fetch the MTU for the port P1, P2, P3
    3. Create two member port (port P1 and P2) as untagged port and port P3 as tagged port of vlan 10
    4. Set MTU size 1500 for port P1, P2 and P3
    5. Set the Port VLAN ID 10 for port P1, P2 and P3
    6. Send untagged TCP packet of size 1400 bytes on port 1.
    7. Untagged packet of size 1400 should be received at port 2 and tagged packet of vlan 10 should be received at port 3.
    8. Send untagged TCP packet of size 1500 bytes on port 1.
    9. Untagged packet of size 1500 should be received at port 2 and tagged packet of vlan 10 should not be received at port 3.
    10. Clean up configuration
    """
    vlan_id = "10"
    port_mtu = "1500"
    port_default_mtu = []
    max_port = 3

    for oid in npu.port_oids[0:max_port]:
        mtu = npu.get(oid, ["SAI_PORT_ATTR_MTU", ""]).value()
        port_default_mtu.append(mtu)

    for oid in npu.dot1q_bp_oids[0:max_port]:
        npu.remove_vlan_member(npu.default_vlan_oid, oid)

    vlan_oid = npu.create(SaiObjType.VLAN, ["SAI_VLAN_ATTR_VLAN_ID", vlan_id])

    npu.create_vlan_member(vlan_oid, npu.dot1q_bp_oids[0],
                           "SAI_VLAN_TAGGING_MODE_UNTAGGED")
    npu.create_vlan_member(vlan_oid, npu.dot1q_bp_oids[1],
                           "SAI_VLAN_TAGGING_MODE_UNTAGGED")
    npu.create_vlan_member(vlan_oid, npu.dot1q_bp_oids[2],
                           "SAI_VLAN_TAGGING_MODE_TAGGED")

    for oid in npu.port_oids[0:max_port]:
        npu.set(oid, ["SAI_PORT_ATTR_MTU", port_mtu])
        npu.set(oid, ["SAI_PORT_ATTR_PORT_VLAN_ID", vlan_id])

    try:
        if npu.run_traffic:
            pkt = simple_tcp_packet(pktlen=1400,
                                    eth_dst='00:22:22:22:22:22',
                                    eth_src='00:11:11:11:11:11',
                                    ip_dst='10.0.0.1',
                                    ip_id=101,
                                    ip_ttl=64)

            tag_pkt = simple_tcp_packet(pktlen=1404,
                                        eth_dst='00:22:22:22:22:22',
                                        eth_src='00:11:11:11:11:11',
                                        dl_vlan_enable=True,
                                        vlan_vid=vlan_id,
                                        ip_dst='10.0.0.1',
                                        ip_id=101,
                                        ip_ttl=64)

            pkt1 = simple_tcp_packet(pktlen=1500,
                                     eth_dst='00:22:22:22:22:22',
                                     eth_src='00:11:11:11:11:11',
                                     ip_dst='10.0.0.1',
                                     ip_id=101,
                                     ip_ttl=64)

            tag_pkt1 = simple_tcp_packet(pktlen=1504,
                                         eth_dst='00:22:22:22:22:22',
                                         eth_src='00:11:11:11:11:11',
                                         dl_vlan_enable=True,
                                         vlan_vid=vlan_id,
                                         ip_dst='10.0.0.1',
                                         ip_id=101,
                                         ip_ttl=64)

            send_packet(dataplane, 0, pkt)
            verify_packet(dataplane, pkt, 1)
            verify_packet(dataplane, tag_pkt, 2)

            send_packet(dataplane, 0, pkt1)
            verify_packet(dataplane, pkt1, 1)
            verify_no_packet(dataplane, tag_pkt1, 2)

    finally:
        for idx, oid in enumerate(npu.port_oids[0:max_port]):
            npu.remove_vlan_member(vlan_oid, npu.dot1q_bp_oids[idx])
            npu.create_vlan_member(npu.default_vlan_oid,
                                   npu.dot1q_bp_oids[idx],
                                   "SAI_VLAN_TAGGING_MODE_UNTAGGED")
            npu.set(oid, ["SAI_PORT_ATTR_PORT_VLAN_ID", npu.default_vlan_id])
            npu.set(oid, ["SAI_PORT_ATTR_MTU", port_default_mtu[idx]])
        npu.remove(vlan_oid)
コード例 #11
0
    def runTest(self):
        in_dmac = 'ee:30:ca:9d:1e:00'
        in_smac = 'ee:cd:00:7e:70:00'
        ip_dst_addr = '224.3.3.3'
        ig_port = 1

        mcast_grp = 91
        port_to_smac = {
            0: '00:de:ad:00:00:ff',
            1: '00:de:ad:11:11:ff',
            2: '00:de:ad:22:22:ff',
            3: '00:de:ad:33:33:ff',
            4: '00:de:ad:44:44:ff',
            5: '00:de:ad:55:55:ff'
        }

        for eg_port, smac in port_to_smac.items():
            logging.info("adding table entry for eg_port=%d -> smac=%s"
                         "" % (eg_port, smac))
            self.table_add(self.key_send_frame(eg_port),
                           self.act_rewrite_mac(smac))

        all_ports = set_of_all_ports()

        # When a packet is sent from ingress to the packet buffer with
        # mcast_grp=91, configure the (egress_port, instance) places
        # to which the packet will be copied.

        # The first parameter is the mcast_grp value.

        # The second is a list of 2-tuples.  The first element of each
        # 2-tuples is the egress port to which the copy should be
        # sent, and the second is the "replication id", also called
        # "egress_rid" in the P4_16 v1model architecture
        # standard_metadata struct, or "instance" in the P4_16 PSA
        # architecture psa_egress_input_metadata_t struct.  That value
        # can be useful if you want to send multiple copies of the
        # same packet out of the same output port, but want each one
        # to be processed differently during egress processing.  If
        # you want that, put multiple pairs with the same egress port
        # in the replication list, but each with a different value of
        # "replication id".
        mcast_grp_recipients = [{
            'eg_port': 2,
            'egress_rid': 5
        }, {
            'eg_port': 5,
            'egress_rid': 75
        }, {
            'eg_port': 1,
            'egress_rid': 111
        }]
        two_tuple_list = [(x['eg_port'], x['egress_rid'])
                          for x in mcast_grp_recipients]
        self.pre_add_mcast_group(mcast_grp, two_tuple_list)

        # Before adding any entries to the table ipv4_mc_route_lookup,
        # the default behavior for sending in an IPv4 packet with a
        # multicast dest address is to drop it.
        pkt = tu.simple_udp_packet(eth_src=in_smac,
                                   eth_dst=in_dmac,
                                   ip_dst=ip_dst_addr,
                                   ip_ttl=64)
        tu.send_packet(self, ig_port, pkt)
        tu.verify_no_other_packets(self)

        # Add a set of table entries that the packet should match, and
        # be forwarded with multicast replication.
        self.table_add(self.key_ipv4_mc_route_lookup(ip_dst_addr, 32),
                       self.act_set_mcast_grp(mcast_grp))

        # Check that the entry is hit, expected source and dest MAC
        # have been written into output packet, TTL has been
        # decremented, and that no other packets are received.
        tu.send_packet(self, ig_port, pkt)

        # See Section 6.4 of RFC 1112 for how the dest MAC address of
        # a forwarded IPv4 multicast packet should be calculated.
        ip_dst_addr_int = bt.ipv4_to_int(ip_dst_addr)
        mask_23_lsbs = (1 << 23) - 1
        exp_dmac_int = 0x01_00_5e_00_00_00 + (ip_dst_addr_int & mask_23_lsbs)
        exp_dmac = int_to_mac_string(exp_dmac_int)

        # Check that a packet appeared on all ports where copies
        # should have been sent.
        ports_with_pkts = set()
        for x in mcast_grp_recipients:
            exp_eg_port = x['eg_port']
            exp_smac = port_to_smac[exp_eg_port]
            exp_pkt = tu.simple_udp_packet(eth_src=exp_smac,
                                           eth_dst=exp_dmac,
                                           ip_dst=ip_dst_addr,
                                           ip_ttl=63)
            logging.info("Expecting output packet on port %s" % (exp_eg_port))
            tu.verify_packet(self, exp_pkt, exp_eg_port)
            ports_with_pkts.add(exp_eg_port)

        # Verify that no packets appeared on any other ports
        for port in all_ports - ports_with_pkts:
            logging.info("Expecting no packet on port %s" % (port))
            tu.verify_no_packet(self, exp_pkt, port)