summaryrefslogtreecommitdiffstats
path: root/test/test_l2bd_arp_term.py
blob: c64d5d4f01c57d70ae368eb1499a572cc26c07d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/*
 * Copyright (c) 2018 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef __MFIB_ENTRY_DELEGATE_T__
#define __MFIB_ENTRY_DELEGATE_T__

#include <vnet/fib/fib_node.h>

/**
 * Delegate types
 */
typedef enum mfib_entry_delegate_type_t_ {
    /**
     * Dependency list of covered entries.
     * these are more specific entries that are interested in changes
     * to their respective cover
     */
    MFIB_ENTRY_DELEGATE_COVERED,
} mfib_entry_delegate_type_t;

#define FOR_EACH_MFIB_DELEGATE(_entry, _fdt, _fed, _body)      \
{                                                              \
    for (_fdt = MFIB_ENTRY_DELEGATE_CHAIN_UNICAST_IP4;         \
         _fdt <= MFIB_ENTRY_DELEGATE_ATTACHED_EXPORT;          \
         _fdt++)                                               \
    {                                                          \
        _fed = mfib_entry_delegate_get(_entry, _fdt);          \
        if (NULL != _fed) {                                    \
            _body;                                             \
        }                                                      \
    }                                                          \
}

/**
 * A Delagate is a means to implmenet the Delagation design pattern; the extension of an
 * objects functionality through the composition of, and delgation to, other objects.
 * These 'other' objects are delegates. Delagates are thus attached to other MFIB objects
 * to extend their functionality.
 */
typedef struct mfib_entry_delegate_t_
{
    /**
     * The MFIB entry object to which the delagate is attached
     */
    fib_node_index_t mfd_entry_index;

    /**
     * The delagate type
     */
    mfib_entry_delegate_type_t mfd_type;

    /**
     * A union of data for the different delegate types
     * These delegates are stored in a sparse vector on the entry, so they
     * must all be of the same size.
     */
    union
    {
        /**
         * For the cover tracking. The node list;
         */
        fib_node_list_t mfd_list;
    };
} mfib_entry_delegate_t;


@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
""" L2BD ARP term Test """

import unittest
import random
import copy

from socket import AF_INET, AF_INET6, inet_pton, inet_ntop

from scapy.packet import Raw
from scapy.layers.l2 import Ether, ARP
from scapy.layers.inet import IP
from scapy.utils6 import in6_getnsma, in6_getnsmac, in6_ptop, in6_islladdr, \
    in6_mactoifaceid, in6_ismaddr
from scapy.layers.inet6 import IPv6, UDP, ICMPv6ND_NS, ICMPv6ND_RS, \
    ICMPv6ND_RA, ICMPv6NDOptSrcLLAddr, getmacbyip6, ICMPv6MRD_Solicitation, \
    ICMPv6NDOptMTU, ICMPv6NDOptSrcLLAddr, ICMPv6NDOptPrefixInfo, \
    ICMPv6ND_NA, ICMPv6NDOptDstLLAddr, ICMPv6DestUnreach, icmp6types

from framework import VppTestCase, VppTestRunner
from util import Host, ppp


class TestL2bdArpTerm(VppTestCase):
    """ L2BD arp termination Test Case """

    @classmethod
    def setUpClass(cls):
        """
        Perform standard class setup (defined by class method setUpClass in
        class VppTestCase) before running the test case, set test case related
        variables and configure VPP.
        """
        super(TestL2bdArpTerm, cls).setUpClass()

        try:
            # Create pg interfaces
            n_bd = 1
            cls.ifs_per_bd = ifs_per_bd = 3
            n_ifs = n_bd * ifs_per_bd
            cls.create_pg_interfaces(range(n_ifs))

            # Set up all interfaces
            for i in cls.pg_interfaces:
                i.admin_up()

            cls.hosts = set()

        except Exception:
            super(TestL2bdArpTerm, cls).tearDownClass()
            raise

    @classmethod
    def tearDownClass(cls):
        super(TestL2bdArpTerm, cls).tearDownClass()

    def setUp(self):
        """
        Clear trace and packet infos before running each test.
        """
        self.reset_packet_infos()
        super(TestL2bdArpTerm, self).setUp()

    def tearDown(self):
        """
        Show various debug prints after each test.
        """
        super(TestL2bdArpTerm, self).tearDown()

    def show_commands_at_teardown(self):
        self.logger.info(self.vapi.ppcli("show l2fib verbose"))
        # many tests delete bridge-domain 1 as the last task.  don't output
        # the details of a non-existent bridge-domain.
        if self.vapi.l2_fib_table_dump(bd_id=1):
            self.logger.info(self.vapi.ppcli("show bridge-domain 1 detail"))

    def add_del_arp_term_hosts(self, entries, bd_id=1, is_add=1, is_ipv6=0):
        for e in entries:
            ip = e.ip4 if is_ipv6 == 0 else e.ip6
            self.vapi.bd_ip_mac_add_del(is_add=is_add,
                                        entry={
                                            'bd_id': bd_id,
                                            'ip': ip,
                                            'mac': e.mac})

    @classmethod
    def mac_list(cls, b6_range):
        return ["00:00:ca:fe:00:%02x" % b6 for b6 in b6_range]

    @classmethod
    def ip4_host(cls, subnet, host, mac):
        return Host(mac=mac,
                    ip4="172.17.1%02u.%u" % (subnet, host))

    @classmethod
    def ip4_hosts(cls, subnet, start, mac_list):
        return {cls.ip4_host(subnet, start + j, mac_list[j])
                for j in range(len(mac_list))}

    @classmethod
    def ip6_host(cls, subnet, host, mac):
        return Host(mac=mac,
                    ip6="fd01:%x::%x" % (subnet, host))

    @classmethod
    def ip6_hosts(cls, subnet, start, mac_list):
        return {cls.ip6_host(subnet, start + j, mac_list[j])
                for j in range(len(mac_list))}

    @classmethod
    def bd_swifs(cls, b):
        n = cls.ifs_per_bd
        start = (b - 1) * n
        return [cls.pg_interfaces[j] for j in range(start, start + n)]

    def bd_add_del(self, bd_id=1, is_add=1):
        if is_add:
            self.vapi.bridge_domain_add_del(bd_id=bd_id, is_add=is_add)
        for swif in self.bd_swifs(bd_id):
            swif_idx = swif.sw_if_index
            self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=swif_idx,
                                                 bd_id=bd_id, enable=is_add)
        if not is_add:
            self.vapi.bridge_domain_add_del(bd_id=bd_id, is_add=is_add)

    @classmethod
    def arp_req(cls, src_host, host):
        return (Ether(dst="ff:ff:ff:ff:ff:ff", src=src_host.mac) /
                ARP(op="who-has",
                    hwsrc=src_host.bin_mac,
                    pdst=host.ip4,
                    psrc=src_host.ip4))

    @classmethod
    def arp_reqs(cls, src_host, entries):
        return [cls.arp_req(src_host, e) for e in entries]

    @classmethod
    def garp_req(cls, host):
        return cls.arp_req(host, host)

    @classmethod
    def garp_reqs(cls, entries):
        return [cls.garp_req(e) for e in entries]

    def arp_resp_host(self, src_host, arp_resp):
        ether = arp_resp[Ether]
        self.assertEqual(ether.dst, src_host.mac)

        arp = arp_resp[ARP]
        self.assertEqual(arp.hwtype, 1)
        self.assertEqual(arp.ptype, 0x800)
        self.assertEqual(arp.hwlen, 6)
        self.assertEqual(arp.plen, 4)
        arp_opts = {"who-has": 1, "is-at": 2}
        self.assertEqual(arp.op, arp_opts["is-at"])
        self.assertEqual(arp.hwdst, src_host.mac)
        self.assertEqual(arp.pdst, src_host.ip4)
        return Host(mac=arp.hwsrc, ip4=arp.psrc)

    def arp_resp_hosts(self, src_host, pkts):
        return {self.arp_resp_host(src_host, p) for p in pkts}

    @staticmethod
    def inttoip4(ip):
        o1 = int(ip / 16777216) % 256
        o2 = int(ip / 65536) % 256
        o3 = int(ip / 256) % 256
        o4 = int(ip) % 256
        return '%s.%s.%s.%s' % (o1, o2, o3, o4)

    def arp_event_host(self, e):
        return Host(str(e.mac), ip4=str(e.ip))

    def arp_event_hosts(self, evs):
        return {self.arp_event_host(e) for e in evs}

    def nd_event_host(self, e):
        return Host(str(e.mac), ip6=str(e.ip))

    def nd_event_hosts(self, evs):
        return {self.nd_event_host(e) for e in evs}

    @classmethod
    def ns_req(cls, src_host, host):
        nsma = in6_getnsma(inet_pton(AF_INET6, "fd10::ffff"))
        d = inet_ntop(AF_INET6, nsma)
        return (Ether(dst="ff:ff:ff:ff:ff:ff", src=src_host.mac) /
                IPv6(dst=d, src=src_host.ip6) /
                ICMPv6ND_NS(tgt=host.ip6) /
                ICMPv6NDOptSrcLLAddr(lladdr=src_host.mac))

    @classmethod
    def ns_reqs_dst(cls, entries, dst_host):
        return [cls.ns_req(e, dst_host) for e in entries]

    @classmethod
    def ns_reqs_src(cls, src_host, entries):
        return [cls.ns_req(src_host, e) for e in entries]

    def na_resp_host(self, src_host, rx):
        self.assertEqual(rx[Ether].dst, src_host.mac)
        self.assertEqual(in6_ptop(rx[IPv6].dst),
                         in6_ptop(src_host.ip6))

        self.assertTrue(rx.haslayer(ICMPv6ND_NA))
        self.assertTrue(rx.haslayer(ICMPv6NDOptDstLLAddr))

        na = rx[ICMPv6ND_NA]
        return Host(mac=na.lladdr, ip6=na.tgt)

    def na_resp_hosts(self, src_host, pkts):
        return {self.na_resp_host(src_host, p) for p in pkts}

    def set_bd_flags(self, bd_id, **args):
        """
        Enable/disable defined feature(s) of the bridge domain.

        :param int bd_id: Bridge domain ID.
        :param list args: List of feature/status pairs. Allowed features: \
        learn, forward, flood, uu_flood and arp_term. Status False means \
        disable, status True means enable the feature.
        :raise: ValueError in case of unknown feature in the input.
        """
        for flag in args:
            if flag == "learn":
                feature_bitmap = 1 << 0
            elif flag == "forward":
                feature_bitmap = 1 << 1
            elif flag == "flood":
                feature_bitmap = 1 << 2
            elif flag == "uu_flood":
                feature_bitmap = 1 << 3
            elif flag == "arp_term":
                feature_bitmap = 1 << 4
            else:
                raise ValueError("Unknown feature used: %s" % flag)
            is_set = 1 if args[flag] else 0
            self.vapi.bridge_flags(bd_id=bd_id, is_set=is_set,
                                   flags=feature_bitmap)
        self.logger.info("Bridge domain ID %d updated" % bd_id)

    def verify_arp(self, src_host, req_hosts, resp_hosts, bd_id=1):
        reqs = self.arp_reqs(src_host, req_hosts)

        for swif in self.bd_swifs(bd_id):
            swif.add_stream(reqs)

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

        for swif in self.bd_swifs(bd_id):
            resp_pkts = swif.get_capture(len(resp_hosts))
            resps = self.arp_resp_hosts(src_host, resp_pkts)
            self.assertEqual(len(resps ^ resp_hosts), 0)

    def verify_nd(self, src_host, req_hosts, resp_hosts, bd_id=1):
        reqs = self.ns_reqs_src(src_host, req_hosts)

        for swif in self.bd_swifs(bd_id):
            swif.add_stream(reqs)

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

        for swif in self.bd_swifs(bd_id):
            resp_pkts = swif.get_capture(len(resp_hosts))
            resps = self.na_resp_hosts(src_host, resp_pkts)
            self.assertEqual(len(resps ^ resp_hosts), 0)

    def test_l2bd_arp_term_01(self):
        """ L2BD arp term - add 5 hosts, verify arp responses
        """
        src_host = self.ip4_host(50, 50, "00:00:11:22:33:44")
        self.bd_add_del(1, is_add=1)
        self.set_bd_flags(1, arp_term=True, flood=False,
                          uu_flood=False, learn=False)
        macs = self.mac_list(range(1, 5))
        hosts = self.ip4_hosts(4, 1, macs)
        self.add_del_arp_term_hosts(hosts, is_add=1)

        self.verify_arp(src_host, hosts, hosts)
        type(self).hosts = hosts

    def test_l2bd_arp_term_02(self):
        """ L2BD arp term - delete 3 hosts, verify arp responses
        """
        src_host = self.ip4_host(50, 50, "00:00:11:22:33:44")
        macs = self.mac_list(range(1, 3))
        deleted = self.ip4_hosts(4, 1, macs)
        self.add_del_arp_term_hosts(deleted, is_add=0)
        remaining = self.hosts - deleted
        self.verify_arp(src_host, self.hosts, remaining)
        type(self).hosts = remaining
        self.bd_add_del(1, is_add=0)

    def test_l2bd_arp_term_03(self):
        """ L2BD arp term - recreate BD1, readd 3 hosts, verify arp responses
        """
        src_host = self.ip4_host(50, 50, "00:00:11:22:33:44")
        self.bd_add_del(1, is_add=1)
        self.set_bd_flags(1, arp_term=True, flood=False,
                          uu_flood=False, learn=False)
        macs = self.mac_list(range(1, 3))
        readded = self.ip4_hosts(4, 1, macs)
        self.add_del_arp_term_hosts(readded, is_add=1)
        self.verify_arp(src_host, self.hosts | readded, readded)
        type(self).hosts = readded

    def test_l2bd_arp_term_04(self):
        """ L2BD arp term - 2 IP4 addrs per host
        """
        src_host = self.ip4_host(50, 50, "00:00:11:22:33:44")
        macs = self.mac_list(range(1, 3))
        sub5_hosts = self.ip4_hosts(5, 1, macs)
        self.add_del_arp_term_hosts(sub5_hosts, is_add=1)
        hosts = self.hosts | sub5_hosts
        self.verify_arp(src_host, hosts, hosts)
        type(self).hosts = hosts
        self.bd_add_del(1, is_add=0)

    def test_l2bd_arp_term_05(self):
        """ L2BD arp term - create and update 10 IP4-mac pairs
        """
        src_host = self.ip4_host(50, 50, "00:00:11:22:33:44")
        self.bd_add_del(1, is_add=1)
        self.set_bd_flags(1, arp_term=True, flood=False,
                          uu_flood=False, learn=False)
        macs1 = self.mac_list(range(10, 20))
        hosts1 = self.ip4_hosts(5, 1, macs1)
        self.add_del_arp_term_hosts(hosts1, is_add=1)
        self.verify_arp(src_host, hosts1, hosts1)
        macs2 = self.mac_list(range(20, 30))
        hosts2 = self.ip4_hosts(5, 1, macs2)
        self.add_del_arp_term_hosts(hosts2, is_add=1)
        self.verify_arp(src_host, hosts1, hosts2)
        self.bd_add_del(1, is_add=0)

    def test_l2bd_arp_term_06(self):
        """ L2BD arp/ND term - hosts with both ip4/ip6
        """
        src_host4 = self.ip4_host(50, 50, "00:00:11:22:33:44")
        src_host6 = self.ip6_host(50, 50, "00:00:11:22:33:44")
        self.bd_add_del(1, is_add=1)
        # enable flood to make sure requests are not flooded
        self.set_bd_flags(1, arp_term=True, flood=True,
                          uu_flood=False, learn=False)
        macs = self.mac_list(range(10, 20))
        hosts6 = self.ip6_hosts(5, 1, macs)
        hosts4 = self.ip4_hosts(5, 1, macs)
        self.add_del_arp_term_hosts(hosts4, is_add=1)
        self.add_del_arp_term_hosts(hosts6, is_add=1, is_ipv6=1)
        self.verify_arp(src_host4, hosts4, hosts4)
        self.verify_nd(src_host6, hosts6, hosts6)
        self.bd_add_del(1, is_add=0)

    def test_l2bd_arp_term_07(self):
        """ L2BD ND term - Add and Del hosts, verify ND replies
        """
        src_host6 = self.ip6_host(50, 50, "00:00:11:22:33:44")
        self.bd_add_del(1, is_add=1)
        self.set_bd_flags(1, arp_term=True, flood=False,
                          uu_flood=False, learn=False)
        macs = self.mac_list(range(10, 20))
        hosts6 = self.ip6_hosts(5, 1, macs)
        self.add_del_arp_term_hosts(hosts6, is_add=1, is_ipv6=1)
        self.verify_nd(src_host6, hosts6, hosts6)
        del_macs = self.mac_list(range(10, 15))
        deleted = self.ip6_hosts(5, 1, del_macs)
        self.add_del_arp_term_hosts(deleted, is_add=0, is_ipv6=1)
        self.verify_nd(src_host6, hosts6, hosts6 - deleted)
        self.bd_add_del(1, is_add=0)

    def test_l2bd_arp_term_08(self):
        """ L2BD ND term - Add and update IP+mac, verify ND replies
        """
        src_host = self.ip6_host(50, 50, "00:00:11:22:33:44")
        self.bd_add_del(1, is_add=1)
        self.set_bd_flags(1, arp_term=True, flood=False,
                          uu_flood=False, learn=False)
        macs1 = self.mac_list(range(10, 20))
        hosts = self.ip6_hosts(5, 1, macs1)
        self.add_del_arp_term_hosts(hosts, is_add=1, is_ipv6=1)
        self.verify_nd(src_host, hosts, hosts)
        macs2 = self.mac_list(range(20, 30))
        updated = self.ip6_hosts(5, 1, macs2)
        self.add_del_arp_term_hosts(updated, is_add=1, is_ipv6=1)
        self.verify_nd(src_host, hosts, updated)
        self.bd_add_del(1, is_add=0)

    def test_l2bd_arp_term_09(self):
        """ L2BD arp term - send garps, verify arp event reports
        """
        self.vapi.want_l2_arp_term_events(enable=1)
        self.bd_add_del(1, is_add=1)
        self.set_bd_flags(1, arp_term=True, flood=False,
                          uu_flood=False, learn=False)
        macs = self.mac_list(range(90, 95))
        hosts = self.ip4_hosts(5, 1, macs)

        garps = self.garp_reqs(hosts)
        self.bd_swifs(1)[0].add_stream(garps)

        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()
        evs = [self.vapi.wait_for_event(1, "l2_arp_term_event")
               for i in range(len(hosts))]
        ev_hosts = self.arp_event_hosts(evs)
        self.assertEqual(len(ev_hosts ^ hosts), 0)

    def test_l2bd_arp_term_10(self):
        """ L2BD arp term - send duplicate garps, verify suppression
        """
        macs = self.mac_list(range(70, 71))
        hosts = self.ip4_hosts(6, 1, macs)

        """ send the packet 5 times expect one event
        """
        garps = self.garp_reqs(hosts) * 5
        self.bd_swifs(1)[0].add_stream(garps)

        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()
        evs = [self.vapi.wait_for_event(1, "l2_arp_term_event")
               for i in range(len(hosts))]
        ev_hosts = self.arp_event_hosts(evs)
        self.assertEqual(len(ev_hosts ^ hosts), 0)

    def test_l2bd_arp_term_11(self):
        """ L2BD arp term - disable ip4 arp events,send garps, verify no events
        """
        self.vapi.want_l2_arp_term_events(enable=0)
        macs = self.mac_list(range(90, 95))
        hosts = self.ip4_hosts(5, 1, macs)

        garps = self.garp_reqs(hosts)
        self.bd_swifs(1)[0].add_stream(garps)

        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()
        self.sleep(1)
        self.assertEqual(len(self.vapi.collect_events()), 0)
        self.bd_add_del(1, is_add=0)

    def test_l2bd_arp_term_12(self):
        """ L2BD ND term - send NS packets verify reports
        """
        self.vapi.want_l2_arp_term_events(enable=1)
        dst_host = self.ip6_host(50, 50, "00:00:11:22:33:44")
        self.bd_add_del(1, is_add=1)
        self.set_bd_flags(1, arp_term=True, flood=False,
                          uu_flood=False, learn=False)
        macs = self.mac_list(range(10, 15))
        hosts = self.ip6_hosts(5, 1, macs)
        reqs = self.ns_reqs_dst(hosts, dst_host)
        self.bd_swifs(1)[0].add_stream(reqs)

        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()
        evs = [self.vapi.wait_for_event(2, "l2_arp_term_event")
               for i in range(len(hosts))]
        ev_hosts = self.nd_event_hosts(evs)
        self.assertEqual(len(ev_hosts ^ hosts), 0)

    def test_l2bd_arp_term_13(self):
        """ L2BD ND term - send duplicate ns, verify suppression
        """
        dst_host = self.ip6_host(50, 50, "00:00:11:22:33:44")
        macs = self.mac_list(range(16, 17))
        hosts = self.ip6_hosts(5, 1, macs)
        reqs = self.ns_reqs_dst(hosts, dst_host) * 5
        self.bd_swifs(1)[0].add_stream(reqs)

        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()
        evs = [self.vapi.wait_for_event(2, "l2_arp_term_event")
               for i in range(len(hosts))]
        ev_hosts = self.nd_event_hosts(evs)
        self.assertEqual(len(ev_hosts ^ hosts), 0)

    def test_l2bd_arp_term_14(self):
        """ L2BD ND term - disable ip4 arp events,send ns, verify no events
        """
        self.vapi.want_l2_arp_term_events(enable=0)
        dst_host = self.ip6_host(50, 50, "00:00:11:22:33:44")
        macs = self.mac_list(range(10, 15))
        hosts = self.ip6_hosts(5, 1, macs)
        reqs = self.ns_reqs_dst(hosts, dst_host)
        self.bd_swifs(1)[0].add_stream(reqs)

        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()
        self.sleep(1)
        self.assertEqual(len(self.vapi.collect_events()), 0)
        self.bd_add_del(1, is_add=0)


if __name__ == '__main__':
    unittest.main(testRunner=VppTestRunner)