diff options
Diffstat (limited to 'src/test/VppManager_test.cpp')
-rw-r--r-- | src/test/VppManager_test.cpp | 1841 |
1 files changed, 1841 insertions, 0 deletions
diff --git a/src/test/VppManager_test.cpp b/src/test/VppManager_test.cpp new file mode 100644 index 0000000..bdae4ba --- /dev/null +++ b/src/test/VppManager_test.cpp @@ -0,0 +1,1841 @@ +/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */ +/* + * Test suite for class VppManager + * + * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +#include <memory> + +#include <boost/asio/ip/host_name.hpp> +#include <boost/optional.hpp> +#include <boost/test/unit_test.hpp> + +#include <modelgbp/gbp/HashingAlgorithmEnumT.hpp> +#include <modelgbp/gbp/L3IfTypeEnumT.hpp> +#include <modelgbp/gbp/SecGroup.hpp> + +#include <vom/acl_ethertype.hpp> +#include <vom/acl_l2_list.hpp> +#include <vom/acl_l3_list.hpp> +#include <vom/bridge_domain.hpp> +#include <vom/bridge_domain_arp_entry.hpp> +#include <vom/bridge_domain_entry.hpp> +#include <vom/dhcp_client.hpp> +#include <vom/gbp_contract.hpp> +#include <vom/gbp_endpoint.hpp> +#include <vom/gbp_endpoint_group.hpp> +#include <vom/gbp_ext_itf.hpp> +#include <vom/gbp_subnet.hpp> +#include <vom/gbp_vxlan.hpp> +#include <vom/hw.hpp> +#include <vom/igmp_binding.hpp> +#include <vom/igmp_listen.hpp> +#include <vom/inspect.hpp> +#include <vom/interface.hpp> +#include <vom/interface_cmds.hpp> +#include <vom/l2_binding.hpp> +#include <vom/l2_emulation.hpp> +#include <vom/l3_binding.hpp> +#include <vom/lldp_binding.hpp> +#include <vom/lldp_global.hpp> +#include <vom/nat_binding.hpp> +#include <vom/nat_static.hpp> +#include <vom/neighbour.hpp> +#include <vom/route.hpp> +#include <vom/route_domain.hpp> +#include <vom/stat_reader.hpp> +#include <vom/sub_interface.hpp> + +#include "VppManager.hpp" +#include "opflexagent/test/ModbFixture.h" +#include <opflexagent/logging.h> + +using namespace VOM; +using namespace opflexagent; +using boost::asio::ip::address; +using boost::asio::ip::address_v4; + +BOOST_AUTO_TEST_SUITE(vpp) + +struct MockStatReader : public stat_reader +{ + int + connect() + { + } + + void + disconnect() + { + } + + void + read() + { + } +}; + +class MockCmdQ : public HW::cmd_q +{ + public: + MockCmdQ() + : handle(0) + , m_mutex() + { + } + ~MockCmdQ() + { + } + + void + enqueue(cmd *c) + { + std::shared_ptr<cmd> sp(c); + m_cmds.push(sp); + } + void + enqueue(std::queue<cmd *> &cmds) + { + cmd *c; + + while (!cmds.empty()) + { + c = cmds.front(); + cmds.pop(); + + std::shared_ptr<cmd> sp(c); + m_cmds.push(sp); + } + } + void + enqueue(std::shared_ptr<cmd> c) + { + m_cmds.push(c); + } + + void + dequeue(cmd *f) + { + } + + void + dequeue(std::shared_ptr<cmd> cmd) + { + } + + rc_t + write() + { + /* + * the unit tests are executed in thread x and the VppManager + * task queue executes in thread y. both call write() when + * objects are destroyed, even though the objects in the + * test case do not issue commands. Which thread runs write + * is not important. + * N.B. this is an artefact of the way the unit-tests are + * structered and run, this does not afflict the real system + * where *all* objects are created and destroyed with the + * VppManager taskQueue context. + */ + std::lock_guard<std::mutex> lg(m_mutex); + + std::shared_ptr<cmd> c; + + while (!m_cmds.empty()) + { + c = m_cmds.front(); + m_cmds.pop(); + handle_cmd(c.get()); + } + + return (rc_t::OK); + } + + /** + * Blocking Connect to VPP - call once at bootup + */ + bool + connect() + { + return true; + } + + void + disconnect() + { + } + + private: + void + handle_cmd(cmd *c) + { + { + auto ac = + dynamic_cast<interface::create_cmd<vapi::Af_packet_create> *>( + c); + if (NULL != ac) + { + HW::item<handle_t> res(++handle, rc_t::OK); + ac->item() = res; + } + } + { + auto ac = + dynamic_cast<interface::create_cmd<vapi::Create_vlan_subif> *>( + c); + if (NULL != ac) + { + HW::item<handle_t> res(++handle, rc_t::OK); + ac->item() = res; + } + } + + c->succeeded(); + } + uint32_t handle; + + std::mutex m_mutex; + + std::queue<std::shared_ptr<cmd>> m_cmds; +}; + +template <typename T> +bool +is_match(const T &expected) +{ + std::shared_ptr<T> actual = T::find(expected.key()); + + if (!actual) return false; + + return (expected == *actual); +} + +template <typename T> +bool +is_present(const T &search) +{ + std::shared_ptr<T> actual = T::find(search.key()); + + if (!actual) return false; + + return (true); +} + +#define WAIT_FOR1(stmt) WAIT_FOR((stmt), 100) + +template <typename T> +static void +print_obj(const T &obj, const std::string &s) +{ + LOG(ERROR) << s << obj.to_string(); +} + +#define WAIT_FOR_MATCH(obj) \ + WAIT_FOR_ONFAIL(is_match(obj), 100, print_obj(obj, "Not Found: ")) +#define WAIT_FOR_NOT_PRESENT(obj) \ + WAIT_FOR_ONFAIL(!is_present(obj), 100, print_obj(obj, "Still present: ")) + +class VppManagerFixture : public ModbFixture +{ + public: + typedef opflex::ofcore::OFConstants::OpflexElementMode opflex_elem_t; + + public: + VppManagerFixture(opflex_elem_t mode = opflex_elem_t::INVALID_MODE) + : ModbFixture(mode) + , vMac{0x00, 0x11, 0x22, 0x33, 0x44, 0x55} + , policyMgr(agent.getPolicyManager()) + , vppQ() + , vppSR() + , vppManager(agent, idGen, &vppQ, &vppSR) + , inspector() + { + createVppObjects(); + WAIT_FOR(policyMgr.groupExists(epg0->getURI()), 500); + WAIT_FOR(policyMgr.getBDForGroup(epg0->getURI()) != boost::none, 500); + + WAIT_FOR(policyMgr.groupExists(epg1->getURI()), 500); + WAIT_FOR(policyMgr.getRDForGroup(epg1->getURI()) != boost::none, 500); + + vppManager.uplink().set("opflex-itf", 4093, "opflex-host"); + vppManager.setVirtualRouter(true, true, vMac.to_string()); + } + + virtual ~VppManagerFixture() + { + vppManager.stop(); + agent.stop(); + } + + void + createVppObjects() + { + using opflex::modb::Mutator; + using namespace modelgbp; + using namespace modelgbp::gbp; + using namespace modelgbp::gbpe; + + /* + * create EPGs and forwarding objects + * VPP Rnederer support the opnstack variant of the opflex model + * one EPG per-BD, one subnet per BD. + */ + Mutator mutator(framework, policyOwner); + config = universe->addPlatformConfig("default"); + config->setMulticastGroupIP("224.1.1.1"); + + fd0 = space->addGbpFloodDomain("fd0"); + fd0->setUnknownFloodMode(UnknownFloodModeEnumT::CONST_HWPROXY); + fd1 = space->addGbpFloodDomain("fd1"); + fd1->setUnknownFloodMode(UnknownFloodModeEnumT::CONST_FLOOD); + bd0 = space->addGbpBridgeDomain("bd0"); + bd0->addGbpeInstContext()->setEncapId(0xAA); + bd0->addGbpeInstContext()->setMulticastGroupIP("224.1.1.1"); + + bd1 = space->addGbpBridgeDomain("bd1"); + rd0 = space->addGbpRoutingDomain("rd0"); + rd0->addGbpeInstContext()->setEncapId(0xBB); + ret_pol0 = space->addGbpeEndpointRetention("ret-pol0"); + ret_pol0->setRemoteEpAgingInterval(90); + + fd0->addGbpFloodDomainToNetworkRSrc()->setTargetBridgeDomain( + bd0->getURI()); + fd0ctx = fd0->addGbpeFloodContext(); + fd1->addGbpFloodDomainToNetworkRSrc()->setTargetBridgeDomain( + bd1->getURI()); + + bd0->addGbpBridgeDomainToNetworkRSrc()->setTargetRoutingDomain( + rd0->getURI()); + bd1->addGbpBridgeDomainToNetworkRSrc()->setTargetRoutingDomain( + rd0->getURI()); + + subnetsfd0 = space->addGbpSubnets("subnetsfd0"); + subnetsfd0_1 = subnetsfd0->addGbpSubnet("subnetsfd0_1"); + subnetsfd0_1->setAddress("10.20.44.1") + .setPrefixLen(24) + .setVirtualRouterIp("10.20.44.1"); + subnetsfd0_2 = subnetsfd0->addGbpSubnet("subnetsfd0_2"); + subnetsfd0_2->setAddress("2001:db8::") + .setPrefixLen(32) + .setVirtualRouterIp("2001:db8::1"); + fd0->addGbpForwardingBehavioralGroupToSubnetsRSrc()->setTargetSubnets( + subnetsfd0->getURI()); + rd0->addGbpRoutingDomainToIntSubnetsRSrc( + subnetsfd0->getURI().toString()); + + subnetsfd1 = space->addGbpSubnets("subnetsfd1"); + subnetsfd1_1 = subnetsfd0->addGbpSubnet("subnetsfd1_1"); + subnetsfd1_1->setAddress("10.20.45.0") + .setPrefixLen(24) + .setVirtualRouterIp("10.20.45.1"); + fd1->addGbpForwardingBehavioralGroupToSubnetsRSrc()->setTargetSubnets( + subnetsfd1->getURI()); + rd0->addGbpRoutingDomainToIntSubnetsRSrc( + subnetsfd1->getURI().toString()); + + epg0 = space->addGbpEpGroup("epg0"); + epg0->addGbpEpGroupToNetworkRSrc()->setTargetBridgeDomain( + bd0->getURI()); + epg0->addGbpEpGroupToNetworkRSrc()->setTargetFloodDomain(fd0->getURI()); + epg0->addGbpeInstContext()->setEncapId(0xA0A); + epg0->addGbpeInstContext()->setClassid(0xBA); + epg0->addGbpeInstContext() + ->addGbpeInstContextToEpRetentionRSrc() + ->setTargetEndpointRetention(ret_pol0->getURI()); + + epg1 = space->addGbpEpGroup("epg1"); + epg1->addGbpEpGroupToNetworkRSrc()->setTargetBridgeDomain( + bd1->getURI()); + epg1->addGbpeInstContext()->setEncapId(0xA0B); + epg1->addGbpeInstContext()->setClassid(0xB0B); + + epg2 = space->addGbpEpGroup("epg2"); + epg2->addGbpeInstContext()->setClassid(0xCA); + epg3 = space->addGbpEpGroup("epg3"); + epg3->addGbpeInstContext()->setClassid(0xCB); + + /* + * L3Out objects + */ + ext_rd0 = space->addGbpRoutingDomain("ext_rd0"); + ext_rd0->addGbpeInstContext()->setEncapId(0x010203); + ext_dom = ext_rd0->addGbpL3ExternalDomain("ext_dom0"); + ext_net0 = ext_dom->addGbpL3ExternalNetwork("ext_dom0_net0"); + ext_net0->addGbpeInstContext()->setClassid(1234); + ext_net0->addGbpExternalSubnet("ext_dom0_net0_sub0") + ->setAddress("105.0.0.0") + .setPrefixLen(24); + ext_net0->addGbpExternalSubnet("ext_dom0_net0_sub1") + ->setAddress("106.0.0.0") + .setPrefixLen(24); + ext_net1 = ext_dom->addGbpL3ExternalNetwork("ext_dom1_net0"); + ext_net1->addGbpeInstContext()->setClassid(1235); + ext_net1->addGbpExternalSubnet("ext_dom0_net1_sub0") + ->setAddress("107.0.0.0") + .setPrefixLen(24); + ext_net1->addGbpExternalSubnet("ext_dom0_net1_sub1") + ->setAddress("108.0.0.0") + .setPrefixLen(24); + ext_bd0 = space->addGbpExternalL3BridgeDomain("ext_bd0"); + ext_bd0->addGbpeInstContext()->setEncapId(1133); + ext_bd0->addGbpeInstContext()->setClassid(2233); + ext_bd0->addGbpeInstContext()->setMulticastGroupIP("224.1.2.2"); + ext_bd0->addGbpExternalL3BridgeDomainToVrfRSrc() + ->setTargetRoutingDomain(ext_rd0->getURI()); + ext_node0 = space->addGbpExternalNode("ext_node0"); + ext_itf0 = space->addGbpExternalInterface("ext_itf0"); + ext_itf0->setAddress("10.30.0.1"); + ext_itf0->setEncap(1144); + // ext_itf0->addGbpeInstContext()->setClassid(2244); + ext_itf0->setMac(opflex::modb::MAC("00:00:00:00:80:00")); + ext_itf0->setIfInstT(L3IfTypeEnumT::CONST_EXTSVI); + ext_itf0->addGbpExternalInterfaceToExtl3bdRSrc() + ->setTargetExternalL3BridgeDomain(ext_bd0->getURI()); + ext_itf0->addGbpExternalInterfaceToL3outRSrc() + ->setTargetL3ExternalDomain(ext_dom->getURI()); + + /* static_route1 = ext_node0->addGbpStaticRoute("static_route1"); */ + /* static_route1->addGbpStaticRouteToVrfRSrc()->setTargetRoutingDomain( + */ + /* ext_rd0->getURI()); */ + /* static_route1->setAddress("101.101.0.0"); */ + /* static_route1->setPrefixLen(16); */ + /* static_route1->addGbpStaticNextHop("100.100.100.2"); */ + /* static_route1->addGbpStaticNextHop("100.100.100.4"); */ + + mutator.commit(); + + /* create endpoints */ + ep0.reset(new Endpoint("0-0-0-0")); + ep0->setInterfaceName("port80"); + ep0->setMAC(opflex::modb::MAC("00:00:00:00:80:00")); + ep0->addIP("10.20.44.2"); + ep0->addIP("10.20.44.3"); + ep0->addIP("2001:db8::2"); + ep0->addIP("2001:db8::3"); + ep0->addAnycastReturnIP("10.20.44.2"); + ep0->addAnycastReturnIP("2001:db8::2"); + ep0->setEgURI(epg0->getURI()); + epSrc.updateEndpoint(*ep0); + + ep1.reset(new Endpoint("0-0-0-1")); + ep1->setMAC(opflex::modb::MAC("00:00:00:00:00:01")); + ep1->addIP("10.20.45.21"); + ep1->setEgURI(epg0->getURI()); + epSrc.updateEndpoint(*ep1); + + ep2.reset(new Endpoint("0-0-0-2")); + ep2->setMAC(opflex::modb::MAC("00:00:00:00:00:02")); + ep2->addIP("10.20.45.21"); + ep2->setInterfaceName("port11"); + ep2->setEgURI(epg1->getURI()); + epSrc.updateEndpoint(*ep2); + + ep3.reset(new Endpoint("0-0-0-3")); + ep3->setMAC(opflex::modb::MAC("00:00:00:00:00:03")); + ep3->addIP("10.20.45.31"); + ep3->setInterfaceName("eth3"); + ep3->setEgURI(epg1->getURI()); + epSrc.updateEndpoint(*ep3); + + ep4.reset(new Endpoint("0-0-0-4")); + ep4->setMAC(opflex::modb::MAC("00:00:00:00:00:04")); + ep4->addIP("10.20.45.41"); + ep4->setInterfaceName("port40"); + ep4->setAccessIfaceVlan(1000); + ep4->setEgURI(epg1->getURI()); + epSrc.updateEndpoint(*ep4); + + ext_ep0.reset(new Endpoint("0-0-e-0")); + ext_ep0->setMAC(opflex::modb::MAC("00:00:00:00:0E:00")); + ext_ep0->addIP("10.30.0.2"); + ext_ep0->setInterfaceName("port-e-00"); + ext_ep0->setEgURI(ext_itf0->getURI()); + ext_ep0->setExtInterfaceURI(ext_itf0->getURI()); + ext_ep0->setExtNodeURI(ext_node0->getURI()); + ext_ep0->setExternal(); + epSrc.updateEndpoint(*ext_ep0); + } + + void + createNatObjects() + { + using std::shared_ptr; + using namespace modelgbp::gbp; + using namespace opflex::modb; + + shared_ptr<modelgbp::policy::Space> common; + shared_ptr<FloodDomain> fd_ext; + shared_ptr<BridgeDomain> bd_ext; + shared_ptr<Subnets> subnets_ext; + shared_ptr<L3ExternalDomain> l3ext; + + Mutator mutator(framework, policyOwner); + common = universe->addPolicySpace("common"); + bd_ext = common->addGbpBridgeDomain("bd_ext"); + rd_ext = common->addGbpRoutingDomain("rd_ext"); + fd_ext = common->addGbpFloodDomain("fd_ext"); + + fd_ext->addGbpFloodDomainToNetworkRSrc()->setTargetBridgeDomain( + bd_ext->getURI()); + bd_ext->addGbpBridgeDomainToNetworkRSrc()->setTargetRoutingDomain( + rd_ext->getURI()); + + subnets_ext = common->addGbpSubnets("subnets_ext"); + subnets_ext->addGbpSubnet("subnet_ext4") + ->setAddress("5.5.5.0") + .setPrefixLen(24); + + bd_ext->addGbpForwardingBehavioralGroupToSubnetsRSrc() + ->setTargetSubnets(subnets_ext->getURI()); + rd_ext->addGbpRoutingDomainToIntSubnetsRSrc( + subnets_ext->getURI().toString()); + + epg_nat = common->addGbpEpGroup("nat-epg"); + epg_nat->addGbpeInstContext()->setEncapId(0x424); + epg_nat->addGbpeInstContext()->setClassid(0x44); + epg_nat->addGbpEpGroupToNetworkRSrc()->setTargetFloodDomain( + fd_ext->getURI()); + + l3ext = rd0->addGbpL3ExternalDomain("ext"); + l3ext_net = l3ext->addGbpL3ExternalNetwork("outside"); + l3ext_net->addGbpExternalSubnet("outside") + ->setAddress("5.5.0.0") + .setPrefixLen(16); + mutator.commit(); + + Endpoint::IPAddressMapping ipm4("91c5b217-d244-432c-922d-533c6c036ab3"); + ipm4.setMappedIP("10.20.44.2"); + ipm4.setFloatingIP("5.5.5.5"); + ipm4.setEgURI(epg_nat->getURI()); + ep0->addIPAddressMapping(ipm4); + epSrc.updateEndpoint(*ep0); + + WAIT_FOR(policyMgr.getRDForGroup(epg_nat->getURI()) != boost::none, + 500); + PolicyManager::subnet_vector_t sns; + WAIT_FOR_DO(sns.size() == 1, 500, sns.clear(); + policyMgr.getSubnetsForGroup(epg_nat->getURI(), sns)); + } + + void + assignEpg0ToFd0() + { + PolicyManager::subnet_vector_t sns; + opflex::modb::Mutator mutator(framework, policyOwner); + epg0->addGbpEpGroupToNetworkRSrc()->setTargetFloodDomain(fd0->getURI()); + mutator.commit(); + + WAIT_FOR1(policyMgr.getFDForGroup(epg0->getURI()) != boost::none); + WAIT_FOR_DO(sns.size() == 3, 500, sns.clear(); + policyMgr.getSubnetsForGroup(epg0->getURI(), sns)); + WAIT_FOR1( + (PolicyManager::getRouterIpForSubnet(*sns[1]) != boost::none)); + } + + void + do_dhcp() + { + host = boost::asio::ip::address::from_string("192.168.1.1"); + router = boost::asio::ip::address::from_string("192.168.1.2"); + + route::prefix_t pfx(host, 24); + mac_address_t mac("00:00:11:22:33:44"); + + /* + * boot phase so the VPP/host address is learnt + */ + interface v_phy("opflex-itf", + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + sub_interface v_sub(v_phy, interface::admin_state_t::UP, 4093); + + WAIT_FOR_MATCH(v_phy); + WAIT_FOR_MATCH(v_sub); + + std::string fqdn = boost::asio::ip::host_name(); + WAIT_FOR_MATCH(dhcp_client(v_sub, fqdn)); + WAIT_FOR_MATCH(lldp_global(fqdn, 5, 2)); + WAIT_FOR_MATCH(lldp_binding(v_phy, "uplink-interface")); + + std::shared_ptr<dhcp_client::lease_t> lease = + std::make_shared<dhcp_client::lease_t>(dhcp_client::state_t::BOUND, + v_sub.singular(), + router, + pfx, + boost::asio::ip::host_name(), + mac); + + vppManager.uplink().handle_dhcp_event(lease); + } + + void + removeEpg(std::shared_ptr<modelgbp::gbp::EpGroup> epg) + { + opflex::modb::Mutator m2(framework, policyOwner); + epg->remove(); + m2.commit(); + WAIT_FOR1(!policyMgr.groupExists(epg->getURI())); + } + + std::vector<boost::asio::ip::address> + getEPIps(std::shared_ptr<Endpoint> ep) + { + std::vector<boost::asio::ip::address> ipAddresses; + boost::system::error_code ec; + + for (const std::string &ipStr : ep->getIPs()) + { + boost::asio::ip::address addr = + boost::asio::ip::address::from_string(ipStr, ec); + if (!ec) + { + ipAddresses.push_back(addr); + } + } + + return ipAddresses; + } + + address host, router; + std::shared_ptr<Endpoint> ep5, ext_ep0; + std::shared_ptr<modelgbp::gbp::BridgeDomain> bd2; + std::shared_ptr<modelgbp::gbp::EpGroup> epg_nat; + std::shared_ptr<modelgbp::gbp::L3ExternalNetwork> l3ext_net; + std::shared_ptr<modelgbp::gbp::RoutingDomain> rd_ext, ext_rd0; + std::shared_ptr<modelgbp::gbp::ExternalNode> ext_node0; + std::shared_ptr<modelgbp::gbp::ExternalL3BridgeDomain> ext_bd0; + std::shared_ptr<modelgbp::gbp::ExternalInterface> ext_itf0; + std::shared_ptr<modelgbp::epdr::LocalRoute> static_route1; + std::shared_ptr<modelgbp::gbp::L3ExternalDomain> ext_dom; + std::shared_ptr<modelgbp::gbp::L3ExternalNetwork> ext_net0; + std::shared_ptr<modelgbp::gbp::L3ExternalNetwork> ext_net1; + std::shared_ptr<modelgbp::gbpe::EndpointRetention> ret_pol0; + + mac_address_t vMac; + PolicyManager &policyMgr; + IdGenerator idGen; + MockCmdQ vppQ; + MockStatReader vppSR; + + VPP::VppManager vppManager; + + /** + * To assist in checking the state that is present manually do + * + * inspector.handle_input("all", std::cout); + * + * in any of the test-cases + */ + inspect inspector; +}; + +class VppStitchedManagerFixture : public VppManagerFixture +{ + public: + VppStitchedManagerFixture() + { + vppManager.start(); + } + ~VppStitchedManagerFixture() + { + vppManager.stop(); + } +}; + +class VppTransportManagerFixture : public VppManagerFixture +{ + public: + VppTransportManagerFixture() + : VppManagerFixture(opflex_elem_t::TRANSPORT_MODE) + { + framework.setElementMode( + opflex::ofcore::OFConstants::OpflexElementMode::TRANSPORT_MODE); + boost::system::error_code ec; + boost::asio::ip::address_v4 proxyAddress; + + proxyAddress = + boost::asio::ip::address_v4::from_string("44.44.44.44", ec); + framework.setV4Proxy(proxyAddress); + proxyAddress = + boost::asio::ip::address_v4::from_string("66.66.66.66", ec); + framework.setV6Proxy(proxyAddress); + proxyAddress = + boost::asio::ip::address_v4::from_string("55.55.55.55", ec); + framework.setMacProxy(proxyAddress); + vppManager.start(); + } + ~VppTransportManagerFixture() + { + vppManager.stop(); + } +}; + +BOOST_FIXTURE_TEST_CASE(start, VppStitchedManagerFixture) +{ + /* + * Validate the presence of the uplink state built at startup/boot + * - the physical unplink interface + * - the control VLAN sub-interface + * - DHCP configuration on the sub-interface + * - LLDP config on the physical interface + */ + interface v_phy("opflex-itf", + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + sub_interface v_sub(v_phy, interface::admin_state_t::UP, 4093); + + WAIT_FOR_MATCH(v_phy); + WAIT_FOR_MATCH(v_sub); + + std::string fqdn = boost::asio::ip::host_name(); + WAIT_FOR_MATCH(dhcp_client(v_sub, fqdn)); + WAIT_FOR_MATCH(lldp_global(fqdn, 5, 2)); + WAIT_FOR_MATCH(lldp_binding(v_phy, "uplink-interface")); +} + +BOOST_FIXTURE_TEST_CASE(endpoint_group_add_del, VppStitchedManagerFixture) +{ + vppManager.egDomainUpdated(epg0->getURI()); + // vppManager.domainUpdated(modelgbp::gbp::RoutingDomain::CLASS_ID, + // rd0->getURI()); + + /* + * Check for a bridge domain 100 + */ + bridge_domain v_bd_epg0(100, bridge_domain::learning_mode_t::OFF); + WAIT_FOR_MATCH(v_bd_epg0); + + /* + * check for the presence of a VOM route-domain matching the EPG's + * ID's are offset by 100. + */ + route_domain v_rd(100); + WAIT_FOR_MATCH(v_rd); + + /* + * After waiting for the route-domain to be created + * all other state should now be present + */ + + /* + * Find the BVI interface. the BVI's name includes the bridge-domain ID + * the interface has a dependency on the route domain, so we 'new' the + * interface so we can control its lifetime. + */ + interface *v_bvi_epg0 = new interface( + "bvi-100", interface::type_t::BVI, interface::admin_state_t::UP, v_rd); + v_bvi_epg0->set(vMac); + + WAIT_FOR_MATCH(*v_bvi_epg0); + + /* + * the BVI is put in the bridge-domain + */ + WAIT_FOR_MATCH(l2_binding(*v_bvi_epg0, v_bd_epg0)); + + /* + * The EPG uplink interface, also bound to BD=1 + */ + interface v_phy("opflex-itf", + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + sub_interface v_upl_epg0(v_phy, interface::admin_state_t::UP, 0xA0A); + WAIT_FOR_MATCH(v_upl_epg0); + WAIT_FOR_MATCH(l2_binding(v_upl_epg0, v_bd_epg0)); + + gbp_bridge_domain *v_gbd0 = new gbp_bridge_domain(v_bd_epg0, *v_bvi_epg0); + WAIT_FOR_MATCH(*v_gbd0); + + gbp_endpoint_group *v_epg0 = + new gbp_endpoint_group(0xA0A, 0xBA, v_upl_epg0, v_rd, *v_gbd0); + v_epg0->set({120}); + WAIT_FOR_MATCH(*v_epg0); + + /* + * Add EPG0 into FD0 to assign it subnets + */ + assignEpg0ToFd0(); + vppManager.egDomainUpdated(epg0->getURI()); + + /* + * An entry in the L2FIB for the BVI + */ + WAIT_FOR_MATCH(bridge_domain_entry(v_bd_epg0, vMac, *v_bvi_epg0)); + + /* + * check for an L3 binding and BD ARP for all of the router IPs + */ + WAIT_FOR_MATCH( + l3_binding(*v_bvi_epg0, {address::from_string("10.20.44.1")})); + WAIT_FOR_MATCH(bridge_domain_arp_entry( + v_bd_epg0, address::from_string("10.20.44.1"), vMac)); + WAIT_FOR_MATCH( + l3_binding(*v_bvi_epg0, {address::from_string("2001:db8::1")})); + WAIT_FOR_MATCH(bridge_domain_arp_entry( + v_bd_epg0, address::from_string("2001:db8::1"), vMac)); + + /* + * there should be a route for each of those sub-nets via the epg-uplink + */ + WAIT_FOR_MATCH(gbp_subnet(v_rd, + {address::from_string("10.20.44.0"), 24}, + gbp_subnet::type_t::STITCHED_INTERNAL)); + WAIT_FOR_MATCH(gbp_subnet(v_rd, + {address::from_string("2001:db8::"), 32}, + gbp_subnet::type_t::STITCHED_INTERNAL)); + + /* + * Routing-domain update. This should be a no-op change. Verify the subnets + * still exist. + */ + vppManager.domainUpdated(modelgbp::gbp::RoutingDomain::CLASS_ID, + rd0->getURI()); + WAIT_FOR_MATCH(gbp_subnet(v_rd, + {address::from_string("10.20.44.0"), 24}, + gbp_subnet::type_t::STITCHED_INTERNAL)); + WAIT_FOR_MATCH(gbp_subnet(v_rd, + {address::from_string("2001:db8::"), 32}, + gbp_subnet::type_t::STITCHED_INTERNAL)); + + /* + * Add a second group, same BD different RD + */ + vppManager.egDomainUpdated(epg1->getURI()); + /* //assignEpg0ToFd0(sns); */ + + bridge_domain v_bd_epg1(101, bridge_domain::learning_mode_t::OFF); + WAIT_FOR_MATCH(v_bd_epg1); + + interface *v_bvi_epg1 = new interface( + "bvi-101", interface::type_t::BVI, interface::admin_state_t::UP, v_rd); + v_bvi_epg1->set(vMac); + WAIT_FOR_MATCH(*v_bvi_epg1); + + sub_interface v_upl_epg1(v_phy, interface::admin_state_t::UP, 0xA0B); + WAIT_FOR_MATCH(v_upl_epg1); + WAIT_FOR_MATCH(l2_binding(v_upl_epg1, v_bd_epg1)); + gbp_bridge_domain *v_gbd1 = new gbp_bridge_domain(v_bd_epg1, *v_bvi_epg1); + WAIT_FOR_MATCH(*v_gbd1); + gbp_endpoint_group *v_epg1 = + new gbp_endpoint_group(0xA0B, 0xB0B, v_upl_epg1, v_rd, *v_gbd1); + v_epg1->set({120}); + WAIT_FOR_MATCH(*v_epg1); + + WAIT_FOR_MATCH(gbp_subnet(v_rd, + {address::from_string("10.20.44.0"), 24}, + gbp_subnet::type_t::STITCHED_INTERNAL)); + WAIT_FOR_MATCH(gbp_subnet(v_rd, + {address::from_string("10.20.45.0"), 24}, + gbp_subnet::type_t::STITCHED_INTERNAL)); + WAIT_FOR_MATCH(gbp_subnet(v_rd, + {address::from_string("2001:db8::"), 32}, + gbp_subnet::type_t::STITCHED_INTERNAL)); + + /* + * add a new subnet to the opflex route-domain + * we expect the subnet to show up in each of the VPP RDs + */ + opflex::modb::Mutator mutator(framework, policyOwner); + std::shared_ptr<modelgbp::gbp::Subnet> subnetsfd1_2; + subnetsfd1 = space->addGbpSubnets("subnetsfd1"); + subnetsfd1_2 = subnetsfd0->addGbpSubnet("subnetsfd1_2"); + subnetsfd1_2->setAddress("10.20.46.0").setPrefixLen(24); + fd1->addGbpForwardingBehavioralGroupToSubnetsRSrc()->setTargetSubnets( + subnetsfd1->getURI()); + rd0->addGbpRoutingDomainToIntSubnetsRSrc(subnetsfd1->getURI().toString()); + mutator.commit(); + vppManager.domainUpdated(modelgbp::gbp::RoutingDomain::CLASS_ID, + rd0->getURI()); + + WAIT_FOR_MATCH(gbp_subnet(v_rd, + {address::from_string("10.20.44.0"), 24}, + gbp_subnet::type_t::STITCHED_INTERNAL)); + WAIT_FOR_MATCH(gbp_subnet(v_rd, + {address::from_string("10.20.45.0"), 24}, + gbp_subnet::type_t::STITCHED_INTERNAL)); + WAIT_FOR_MATCH(gbp_subnet(v_rd, + {address::from_string("10.20.46.0"), 24}, + gbp_subnet::type_t::STITCHED_INTERNAL)); + WAIT_FOR_MATCH(gbp_subnet(v_rd, + {address::from_string("2001:db8::"), 32}, + gbp_subnet::type_t::STITCHED_INTERNAL)); + + /* + * withdraw the route domain. + */ + opflex::modb::Mutator m1(framework, policyOwner); + rd0->remove(); + m1.commit(); + vppManager.domainUpdated(modelgbp::gbp::RoutingDomain::CLASS_ID, + rd0->getURI()); + + /* + * Withdraw the EPGs, all the state above should be gone + */ + removeEpg(epg0); + vppManager.egDomainUpdated(epg0->getURI()); + removeEpg(epg1); + vppManager.egDomainUpdated(epg1->getURI()); + + WAIT_FOR_NOT_PRESENT(gbp_subnet(v_rd, + {address::from_string("10.20.44.0"), 24}, + gbp_subnet::type_t::STITCHED_INTERNAL)); + WAIT_FOR_NOT_PRESENT(gbp_subnet(v_rd, + {address::from_string("10.20.45.0"), 24}, + gbp_subnet::type_t::STITCHED_INTERNAL)); + WAIT_FOR_NOT_PRESENT(gbp_subnet(v_rd, + {address::from_string("10.20.46.0"), 24}, + gbp_subnet::type_t::STITCHED_INTERNAL)); + WAIT_FOR_NOT_PRESENT(gbp_subnet(v_rd, + {address::from_string("2001:db8::"), 32}, + gbp_subnet::type_t::STITCHED_INTERNAL)); + + WAIT_FOR_NOT_PRESENT(*v_epg0); + delete v_epg0; + WAIT_FOR_NOT_PRESENT(*v_epg1); + delete v_epg1; + + WAIT_FOR_NOT_PRESENT(*v_gbd0); + delete v_gbd0; + WAIT_FOR_NOT_PRESENT(*v_gbd1); + delete v_gbd1; + + WAIT_FOR_NOT_PRESENT(l2_binding(v_upl_epg0, v_bd_epg0)); + WAIT_FOR_NOT_PRESENT(l2_binding(*v_bvi_epg0, v_bd_epg0)); + WAIT_FOR_NOT_PRESENT(*v_bvi_epg0); + delete v_bvi_epg0; + + WAIT_FOR_NOT_PRESENT(l2_binding(v_upl_epg1, v_bd_epg1)); + WAIT_FOR_NOT_PRESENT(l2_binding(*v_bvi_epg1, v_bd_epg1)); + WAIT_FOR_NOT_PRESENT(*v_bvi_epg1); + delete v_bvi_epg1; + + /* + * If the RDs have gone we can be sure the routes have too. + */ + WAIT_FOR_NOT_PRESENT(v_upl_epg0); + WAIT_FOR_NOT_PRESENT(v_bd_epg0); + WAIT_FOR_NOT_PRESENT(v_upl_epg1); + WAIT_FOR_NOT_PRESENT(v_bd_epg1); + WAIT_FOR_NOT_PRESENT(v_rd); +} + +BOOST_FIXTURE_TEST_CASE(endpoint_add_del, VppStitchedManagerFixture) +{ + assignEpg0ToFd0(); + vppManager.egDomainUpdated(epg0->getURI()); + vppManager.endpointUpdated(ep0->getUUID()); + + mac_address_t v_mac_ep0("00:00:00:00:80:00"); + mac_address_t v_mac_ep2("00:00:00:00:00:02"); + mac_address_t v_mac_ep4("00:00:00:00:00:04"); + /* + * Check for a bridge domain 100 and route domain 100. + */ + bridge_domain v_bd_epg0(100, bridge_domain::learning_mode_t::OFF); + WAIT_FOR_MATCH(v_bd_epg0); + route_domain v_rd(100); + WAIT_FOR_MATCH(v_rd); + interface v_phy("opflex-itf", + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + sub_interface v_upl_epg0(v_phy, interface::admin_state_t::UP, 0xA0A); + WAIT_FOR_MATCH(v_upl_epg0); + WAIT_FOR_MATCH(l2_binding(v_upl_epg0, v_bd_epg0)); + + interface *v_bvi_epg0 = new interface( + "bvi-100", interface::type_t::BVI, interface::admin_state_t::UP, v_rd); + v_bvi_epg0->set(vMac); + WAIT_FOR_MATCH(*v_bvi_epg0); + + gbp_bridge_domain *v_gbd0 = new gbp_bridge_domain(v_bd_epg0, *v_bvi_epg0); + WAIT_FOR_MATCH(*v_gbd0); + + gbp_endpoint_group *v_epg0 = + new gbp_endpoint_group(0xA0A, 0xBA, v_upl_epg0, v_rd, *v_gbd0); + v_epg0->set({120}); + WAIT_FOR_MATCH(*v_epg0); + + /* + * Find the EP's interface + */ + interface *v_itf_ep0 = new interface("port80", + interface::type_t::AFPACKET, + interface::admin_state_t::UP, + v_rd); + WAIT_FOR_MATCH(*v_itf_ep0); + + /* + * the Endpoint + */ + WAIT_FOR_MATCH(gbp_endpoint(*v_itf_ep0, getEPIps(ep0), v_mac_ep0, *v_epg0)); + + /* + * An Another EP in another EPG + */ + vppManager.egDomainUpdated(epg1->getURI()); + vppManager.endpointUpdated(ep2->getUUID()); + + bridge_domain v_bd_epg1(101, bridge_domain::learning_mode_t::OFF); + WAIT_FOR_MATCH(v_bd_epg1); + + interface *v_itf_ep2 = new interface("port11", + interface::type_t::AFPACKET, + interface::admin_state_t::UP, + v_rd); + WAIT_FOR_MATCH(*v_itf_ep2); + interface *v_bvi_epg1 = new interface( + "bvi-101", interface::type_t::BVI, interface::admin_state_t::UP, v_rd); + v_bvi_epg1->set(vMac); + WAIT_FOR_MATCH(*v_bvi_epg1); + sub_interface v_upl_epg1(v_phy, interface::admin_state_t::UP, 0xA0B); + WAIT_FOR_MATCH(v_upl_epg1); + + gbp_bridge_domain *v_gbd1 = new gbp_bridge_domain(v_bd_epg1, *v_bvi_epg1); + WAIT_FOR_MATCH(*v_gbd1); + gbp_endpoint_group *v_epg1 = + new gbp_endpoint_group(0xA0B, 0xB0B, v_upl_epg1, v_rd, *v_gbd1); + v_epg1->set({120}); + WAIT_FOR_MATCH(*v_epg1); + + WAIT_FOR_MATCH(gbp_endpoint(*v_itf_ep2, getEPIps(ep2), v_mac_ep2, *v_epg1)); + + /* + * remove EP0 + */ + epSrc.removeEndpoint(ep0->getUUID()); + vppManager.endpointUpdated(ep0->getUUID()); + + for (auto &ipAddr : getEPIps(ep0)) + { + WAIT_FOR_NOT_PRESENT( + bridge_domain_arp_entry(v_bd_epg0, ipAddr, v_mac_ep0)); + WAIT_FOR_NOT_PRESENT(neighbour(*v_bvi_epg0, ipAddr, v_mac_ep0)); + WAIT_FOR_NOT_PRESENT( + route::ip_route(v_rd, {ipAddr}, {ipAddr, *v_bvi_epg0})); + } + WAIT_FOR_NOT_PRESENT(bridge_domain_entry(v_bd_epg0, v_mac_ep0, *v_itf_ep0)); + WAIT_FOR_NOT_PRESENT(l2_binding(*v_itf_ep0, v_bd_epg0)); + WAIT_FOR_NOT_PRESENT(*v_itf_ep0); + delete v_itf_ep0; + + /* + * should still have state from EP2 + */ + WAIT_FOR_MATCH(gbp_endpoint(*v_itf_ep2, getEPIps(ep2), v_mac_ep2, *v_epg1)); + + /* + * remove the rest of the state + */ + epSrc.removeEndpoint(ep2->getUUID()); + vppManager.endpointUpdated(ep2->getUUID()); + removeEpg(epg0); + vppManager.egDomainUpdated(epg0->getURI()); + + /* + * An Another EP in another EPG - trunk port + */ + vppManager.egDomainUpdated(epg1->getURI()); + vppManager.endpointUpdated(ep4->getUUID()); + + WAIT_FOR_MATCH(v_bd_epg1); + + interface *v_itf_ep4 = new interface( + "port40", interface::type_t::AFPACKET, interface::admin_state_t::UP); + WAIT_FOR_MATCH(*v_itf_ep4); + interface *v_trunk_itf_ep4 = + new sub_interface(*v_itf_ep4, interface::admin_state_t::UP, v_rd, 1000); + WAIT_FOR_MATCH(*v_trunk_itf_ep4); + WAIT_FOR_MATCH(*v_bvi_epg1); + WAIT_FOR_MATCH(v_upl_epg1); + WAIT_FOR_MATCH(*v_epg1); + + WAIT_FOR_MATCH( + gbp_endpoint(*v_trunk_itf_ep4, getEPIps(ep4), v_mac_ep4, *v_epg1)); + + epSrc.removeEndpoint(ep4->getUUID()); + vppManager.endpointUpdated(ep4->getUUID()); + + delete v_itf_ep2; + delete v_trunk_itf_ep4; + delete v_itf_ep4; + + removeEpg(epg1); + vppManager.egDomainUpdated(epg1->getURI()); + + /* + * withdraw the route domain. + */ + opflex::modb::Mutator m1(framework, policyOwner); + rd0->remove(); + m1.commit(); + + vppManager.domainUpdated(modelgbp::gbp::RoutingDomain::CLASS_ID, + rd0->getURI()); + + WAIT_FOR_NOT_PRESENT(*v_epg0); + delete v_epg0; + WAIT_FOR_NOT_PRESENT(*v_epg1); + delete v_epg1; + + WAIT_FOR_NOT_PRESENT(*v_gbd0); + delete v_gbd0; + WAIT_FOR_NOT_PRESENT(*v_gbd1); + delete v_gbd1; + + WAIT_FOR_NOT_PRESENT(l2_binding(v_upl_epg0, v_bd_epg0)); + WAIT_FOR_NOT_PRESENT(l2_binding(*v_bvi_epg0, v_bd_epg0)); + WAIT_FOR_NOT_PRESENT(*v_bvi_epg0); + delete v_bvi_epg0; + + WAIT_FOR_NOT_PRESENT(l2_binding(v_upl_epg1, v_bd_epg1)); + WAIT_FOR_NOT_PRESENT(l2_binding(*v_bvi_epg1, v_bd_epg1)); + WAIT_FOR_NOT_PRESENT(*v_bvi_epg1); + delete v_bvi_epg1; + + /* + * if the RD has gone then so have all the rest of the routes. + */ + WAIT_FOR_NOT_PRESENT(v_bd_epg0); + WAIT_FOR_NOT_PRESENT(v_bd_epg1); + WAIT_FOR_NOT_PRESENT(v_rd); +} + +BOOST_FIXTURE_TEST_CASE(trans_endpoint_group_add_del, + VppTransportManagerFixture) +{ + address_v4 spine_mac, spine_v4, spine_v6, bd_mc; + address host, router; + + host = boost::asio::ip::address::from_string("192.168.1.1"); + router = boost::asio::ip::address::from_string("192.168.1.2"); + + route::prefix_t pfx(host, 24); + mac_address_t mac("00:00:11:22:33:44"); + + framework.getMacProxy(spine_mac); + framework.getV4Proxy(spine_v4); + framework.getV6Proxy(spine_v6); + + bd_mc = boost::asio::ip::address_v4::from_string("224.1.1.1"); + do_dhcp(); + + /* + * boot phase so the VPP/host address is learnt + */ + interface v_phy("opflex-itf", + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + sub_interface v_sub(v_phy, interface::admin_state_t::UP, 4093); + + WAIT_FOR_MATCH(v_phy); + WAIT_FOR_MATCH(v_sub); + + /* + * create an endpoint group + */ + vppManager.egDomainUpdated(epg0->getURI()); + + /* + * Check for a bridge domain 100 + */ + bridge_domain v_bd(100, bridge_domain::learning_mode_t::OFF); + WAIT_FOR_MATCH(v_bd); + + /* + * check for the presence of a VOM route-domain matching the EPG's + * ID's are offset by 100. + */ + route_domain v_rd(100); + WAIT_FOR_MATCH(v_rd); + + interface *v_bvi = new interface( + "bvi-100", interface::type_t::BVI, interface::admin_state_t::UP, v_rd); + v_bvi->set(vMac); + + WAIT_FOR_MATCH(*v_bvi); + + /* + * the interfaces to the spine proxy. + * for the BD with VNI=0xAA and the RD VNI=0xBB + */ + vxlan_tunnel *vt_mac = + new vxlan_tunnel(host, spine_mac, 0xAA, vxlan_tunnel::mode_t::GBP_L2); + WAIT_FOR_MATCH(*vt_mac); + vxlan_tunnel *vt_mc = new vxlan_tunnel( + host, bd_mc, 0xAA, v_sub, vxlan_tunnel::mode_t::GBP_L2); + WAIT_FOR_MATCH(*vt_mc); + vxlan_tunnel *vt_v4 = + new vxlan_tunnel(host, spine_v4, 0xBB, vxlan_tunnel::mode_t::GBP_L2); + WAIT_FOR_MATCH(*vt_v4); + vxlan_tunnel *vt_v6 = + new vxlan_tunnel(host, spine_v6, 0xBB, vxlan_tunnel::mode_t::GBP_L2); + WAIT_FOR_MATCH(*vt_v6); + + gbp_bridge_domain *v_gbd = + new gbp_bridge_domain(v_bd, *v_bvi, *vt_mac, *vt_mc); + WAIT_FOR_MATCH(*v_gbd); + gbp_route_domain *v_grd = new gbp_route_domain(v_rd, *vt_v4, *vt_v6); + WAIT_FOR_MATCH(*v_grd); + + gbp_endpoint_group *v_epg = + new gbp_endpoint_group(0xA0A, 0xBA, *v_grd, *v_gbd); + v_epg->set({120}); + WAIT_FOR_MATCH(*v_epg); + + WAIT_FOR_MATCH(gbp_vxlan(0xAA, *v_gbd, host.to_v4())); + WAIT_FOR_MATCH(gbp_vxlan(0xBB, *v_grd, host.to_v4())); + + /* + * mcast vxlan tunnels bound to BD + */ + boost::asio::ip::address bd_mcast = + boost::asio::ip::address::from_string("224.1.1.1"); + + vxlan_tunnel vt_bd_mcast( + host, bd_mcast, 0xAA, v_sub, vxlan_tunnel::mode_t::GBP_L2); + WAIT_FOR_MATCH(vt_bd_mcast); + WAIT_FOR_MATCH(l2_binding(vt_bd_mcast, v_bd)); + + igmp_binding igmp_b(v_sub); + WAIT_FOR_MATCH(igmp_b); + WAIT_FOR_MATCH(igmp_listen(igmp_b, bd_mcast.to_v4())); + + removeEpg(epg0); + vppManager.egDomainUpdated(epg0->getURI()); + + WAIT_FOR_NOT_PRESENT(*v_epg); + delete v_epg; + + WAIT_FOR_NOT_PRESENT(*v_gbd); + WAIT_FOR_NOT_PRESENT(*v_grd); + delete v_gbd; + delete v_grd; + + WAIT_FOR_NOT_PRESENT(*v_bvi); + WAIT_FOR_NOT_PRESENT(*vt_mac); + WAIT_FOR_NOT_PRESENT(*vt_v4); + WAIT_FOR_NOT_PRESENT(*vt_v6); + delete vt_mac; + delete vt_v4; + delete vt_v6; + delete v_bvi; +} + +BOOST_FIXTURE_TEST_CASE(ext_itf, VppTransportManagerFixture) +{ + address_v4 spine_v4, spine_v6; + + framework.getV4Proxy(spine_v4); + framework.getV6Proxy(spine_v6); + + do_dhcp(); + vppManager.externalInterfaceUpdated(ext_itf0->getURI()); + + interface v_phy("opflex-itf", + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + sub_interface v_sub(v_phy, interface::admin_state_t::UP, 4093); + + WAIT_FOR_MATCH(v_phy); + WAIT_FOR_MATCH(v_sub); + + route_domain v_rd(100); + WAIT_FOR_MATCH(v_rd); + + bridge_domain v_bd(100, bridge_domain::learning_mode_t::OFF); + WAIT_FOR_MATCH(v_bd); + + std::shared_ptr<interface> v_bvi = std::make_shared<interface>( + "bvi-100", interface::type_t::BVI, interface::admin_state_t::UP, v_rd); + l2_address_t l2addr(mac_address_t("00:00:00:00:80:00")); + v_bvi->set(l2addr); + + WAIT_FOR_MATCH(*v_bvi); + + boost::asio::ip::address bd_mcast = + boost::asio::ip::address::from_string("224.1.2.2"); + + std::shared_ptr<vxlan_tunnel> vt_bd_mcast = std::make_shared<vxlan_tunnel>( + host, bd_mcast, 1133, v_sub, vxlan_tunnel::mode_t::GBP_L2); + WAIT_FOR_MATCH(*vt_bd_mcast); + igmp_binding igmp_b(v_sub); + WAIT_FOR_MATCH(igmp_b); + WAIT_FOR_MATCH(igmp_listen(igmp_b, bd_mcast.to_v4())); + + gbp_bridge_domain *v_gbd = + new gbp_bridge_domain(v_bd, v_bvi, {}, vt_bd_mcast); + WAIT_FOR_MATCH(*v_gbd); + + vxlan_tunnel *vt_v4 = new vxlan_tunnel( + host, spine_v4, 0x010203, vxlan_tunnel::mode_t::GBP_L2); + WAIT_FOR_MATCH(*vt_v4); + vxlan_tunnel *vt_v6 = new vxlan_tunnel( + host, spine_v6, 0x010203, vxlan_tunnel::mode_t::GBP_L2); + WAIT_FOR_MATCH(*vt_v6); + + gbp_route_domain *v_grd = new gbp_route_domain(v_rd, *vt_v4, *vt_v6); + WAIT_FOR_MATCH(*v_grd); + + /* 0x80000064 is the internally generated EPG-ID for each Ext-net */ + gbp_ext_itf *v_ei = new gbp_ext_itf(*v_bvi, *v_gbd, *v_grd); + WAIT_FOR_MATCH(*v_ei); + + WAIT_FOR_MATCH(gbp_subnet(v_rd, {"105.0.0.0", 24}, 1234)); + WAIT_FOR_MATCH(gbp_subnet(v_rd, {"106.0.0.0", 24}, 1234)); + WAIT_FOR_MATCH(gbp_subnet(v_rd, {"107.0.0.0", 24}, 1235)); + WAIT_FOR_MATCH(gbp_subnet(v_rd, {"108.0.0.0", 24}, 1235)); + + /* Add an EP */ + vppManager.externalEndpointUpdated(ext_ep0->getUUID()); + + mac_address_t v_mac_ext_ep0("00:00:00:00:0E:00"); + + interface *v_itf_ext_ep0 = new interface("port-e-00", + interface::type_t::AFPACKET, + interface::admin_state_t::UP, + v_rd); + WAIT_FOR_MATCH(*v_itf_ext_ep0); + // VNID of the EXT-itf and sclass of the BD + gbp_endpoint_group *v_epg0 = + new gbp_endpoint_group(0xdeadbeaf, 2233, *v_grd, *v_gbd); + v_epg0->set({120}); + WAIT_FOR_MATCH(*v_epg0); + + gbp_endpoint *v_ep = new gbp_endpoint(*v_itf_ext_ep0, + getEPIps(ext_ep0), + v_mac_ext_ep0, + *v_epg0, + gbp_endpoint::flags_t::EXTERNAL); + WAIT_FOR_MATCH(*v_ep); + + /* cleanup */ + epSrc.removeEndpoint(ext_ep0->getUUID()); + vppManager.externalEndpointUpdated(ext_ep0->getUUID()); + + { + opflex::modb::Mutator m2(framework, policyOwner); + ext_itf0->remove(); + m2.commit(); + } + vppManager.externalInterfaceUpdated(ext_itf0->getURI()); + + WAIT_FOR_NOT_PRESENT(*v_ep); + delete v_ep; + WAIT_FOR_NOT_PRESENT(*v_epg0); + delete v_epg0; + WAIT_FOR_NOT_PRESENT(gbp_subnet(v_rd, {"108.0.0.0", 24}, 1235)); + WAIT_FOR_NOT_PRESENT(*v_ei); + delete v_ei; + + WAIT_FOR_NOT_PRESENT(*v_grd); + WAIT_FOR_NOT_PRESENT(*v_gbd); + + delete v_grd; + delete vt_v4; + delete vt_v6; +} + +/* BOOST_FIXTURE_TEST_CASE(static_route, VppTransportManagerFixture) */ +/* { */ +/* vppManager.localRouteUpdated(static_route1->getURI()); */ + +/* route_domain v_rd(100); */ +/* WAIT_FOR_MATCH(v_rd); */ + +/* route::prefix_t pfx(boost::asio::ip::address::from_string("101.101.0.0"), + */ +/* 16); */ + +/* boost::asio::ip::address nh1, nh2, nh3; */ +/* nh1 = boost::asio::ip::address::from_string("100.100.100.2"); */ +/* nh2 = boost::asio::ip::address::from_string("100.100.100.3"); */ +/* nh3 = boost::asio::ip::address::from_string("100.100.100.4"); */ + +/* route::ip_route v_route(v_rd, pfx); */ +/* v_route.add({v_rd, nh1}); */ +/* v_route.add({v_rd, nh2}); */ +/* v_route.add({v_rd, nh3}); */ + +/* WAIT_FOR_MATCH(v_route); */ + +/* opflex::modb::Mutator m1(framework, policyOwner); */ +/* static_route1->remove(); */ +/* m1.commit(); */ +/* vppManager.localRouteUpdated(static_route1->getURI()); */ + +/* WAIT_FOR_NOT_PRESENT(v_route); */ +/* } */ + +BOOST_FIXTURE_TEST_CASE(secGroup, VppStitchedManagerFixture) +{ + using modelgbp::gbpe::L24Classifier; + using namespace modelgbp::gbp; + createObjects(); + createPolicyObjects(); + + PolicyManager::rule_list_t lrules; + vppManager.egDomainUpdated(epg0->getURI()); + vppManager.endpointUpdated(ep0->getUUID()); + + std::shared_ptr<SecGroup> secGrp1, secGrp2; + { + opflex::modb::Mutator mutator(framework, policyOwner); + secGrp1 = space->addGbpSecGroup("secgrp1"); + secGrp1->addGbpSecGroupSubject("1_subject1") + ->addGbpSecGroupRule("1_1_rule1") + ->setDirection(DirectionEnumT::CONST_IN) + .setOrder(100) + .addGbpRuleToClassifierRSrc(classifier1->getURI().toString()); + secGrp1->addGbpSecGroupSubject("1_subject1") + ->addGbpSecGroupRule("1_1_rule2") + ->setDirection(DirectionEnumT::CONST_IN) + .setOrder(150) + .addGbpRuleToClassifierRSrc(classifier8->getURI().toString()); + secGrp1->addGbpSecGroupSubject("1_subject1") + ->addGbpSecGroupRule("1_1_rule3") + ->setDirection(DirectionEnumT::CONST_IN) + .setOrder(200) + .addGbpRuleToClassifierRSrc(classifier6->getURI().toString()); + secGrp1->addGbpSecGroupSubject("1_subject1") + ->addGbpSecGroupRule("1_1_rule4") + ->setDirection(DirectionEnumT::CONST_IN) + .setOrder(300) + .addGbpRuleToClassifierRSrc(classifier7->getURI().toString()); + mutator.commit(); + } + + ep0->addSecurityGroup(secGrp1->getURI()); + epSrc.updateEndpoint(*ep0); + + WAIT_FOR_DO(lrules.size() == 4, 500, lrules.clear(); + policyMgr.getSecGroupRules(secGrp1->getURI(), lrules)); + + vppManager.endpointUpdated(ep0->getUUID()); + + route_domain v_rd(100); + WAIT_FOR1(is_match(v_rd)); + + /* + * Find the EP's interface + */ + interface *v_itf = new interface("port80", + interface::type_t::AFPACKET, + interface::admin_state_t::UP, + v_rd); + WAIT_FOR1(is_match(*v_itf)); + + ACL::ethertype_rule_t e1(ethertype_t::IPV4, direction_t::OUTPUT); + ACL::ethertype_rule_t e2(ethertype_t::IPV6, direction_t::OUTPUT); + ACL::ethertype_rule_t e3(ethertype_t::IPV4, direction_t::OUTPUT); + ACL::ethertype_rule_t e4(ethertype_t::IPV4, direction_t::OUTPUT); + + ACL::acl_ethertype::ethertype_rules_t e_rules = {e1, e2, e3, e4}; + + WAIT_FOR1(is_match(ACL::acl_ethertype(*v_itf, e_rules))); + + ACL::action_t act = ACL::action_t::PERMIT; + ACL::l3_rule rule1(8192, + act, + route::prefix_t::ZERO, + route::prefix_t::ZERO, + 6, + 0, + 65535, + 80, + 65535, + 0, + 0); + ACL::l3_rule rule2(8064, + act, + route::prefix_t::ZEROv6, + route::prefix_t::ZEROv6, + 6, + 0, + 65535, + 80, + 65535, + 0, + 0); + ACL::l3_rule rule3(7808, + act, + route::prefix_t::ZERO, + route::prefix_t::ZERO, + 6, + 22, + 65535, + 0, + 65535, + 3, + 3); + ACL::l3_rule rule4(7680, + act, + route::prefix_t::ZERO, + route::prefix_t::ZERO, + 6, + 21, + 65535, + 0, + 65535, + 16, + 16); + ACL::l3_list::rules_t rules({rule1, rule2, rule3, rule4}); + + boost::hash<std::string> string_hash; + const std::string secGrpKey = + std::to_string(string_hash("/PolicyUniverse/PolicySpace/" + "tenant0/GbpSecGroup/secgrp1/")); + + WAIT_FOR1(is_match(ACL::l3_list(secGrpKey + "-out", rules))); + + { + opflex::modb::Mutator mutator(framework, policyOwner); + secGrp2 = space->addGbpSecGroup("secgrp2"); + secGrp2->addGbpSecGroupSubject("2_subject1") + ->addGbpSecGroupRule("2_1_rule1") + ->addGbpRuleToClassifierRSrc(classifier0->getURI().toString()); + secGrp2->addGbpSecGroupSubject("2_subject1") + ->addGbpSecGroupRule("2_1_rule2") + ->setDirection(DirectionEnumT::CONST_BIDIRECTIONAL) + .setOrder(20) + .addGbpRuleToClassifierRSrc(classifier5->getURI().toString()); + secGrp2->addGbpSecGroupSubject("2_subject1") + ->addGbpSecGroupRule("2_1_rule3") + ->setDirection(DirectionEnumT::CONST_OUT) + .setOrder(30) + .addGbpRuleToClassifierRSrc(classifier9->getURI().toString()); + mutator.commit(); + } + + ep0->addSecurityGroup(secGrp2->getURI()); + epSrc.updateEndpoint(*ep0); + + lrules.clear(); + WAIT_FOR_DO(lrules.size() == 2, 500, lrules.clear(); + policyMgr.getSecGroupRules(secGrp2->getURI(), lrules)); + + vppManager.endpointUpdated(ep0->getUUID()); + + ACL::ethertype_rule_t e6(ethertype_t::FCOE, direction_t::OUTPUT); + ACL::ethertype_rule_t e7(ethertype_t::FCOE, direction_t::INPUT); + ACL::ethertype_rule_t e8(ethertype_t::IPV4, direction_t::INPUT); + + ACL::acl_ethertype::ethertype_rules_t e_rules2 = { + e1, e2, e3, e4, e6, e7, e8}; + + WAIT_FOR_MATCH(ACL::acl_ethertype(*v_itf, e_rules2)); + + act = ACL::action_t::PERMITANDREFLEX; + ACL::l3_rule rule5(8064, + act, + route::prefix_t::ZERO, + route::prefix_t::ZERO, + 6, + 0, + 65535, + 22, + 65535, + 0, + 0); + ACL::l3_list::rules_t rules2({rule5}); + + const std::string secGrpKey2 = std::to_string( + string_hash("/PolicyUniverse/PolicySpace/" + "tenant0/GbpSecGroup/secgrp1/,/PolicyUniverse/" + "PolicySpace/tenant0/GbpSecGroup/secgrp2/")); + + WAIT_FOR1(is_match(ACL::l3_list(secGrpKey2 + "-in", rules2))); + + delete v_itf; +} + +BOOST_FIXTURE_TEST_CASE(policy, VppStitchedManagerFixture) +{ + createObjects(); + createPolicyObjects(); + PolicyManager::uri_set_t egs; + WAIT_FOR_DO(egs.size() == 2, 1000, egs.clear(); + policyMgr.getContractProviders(con1->getURI(), egs)); + egs.clear(); + WAIT_FOR_DO(egs.size() == 2, 500, egs.clear(); + policyMgr.getContractConsumers(con1->getURI(), egs)); + egs.clear(); + + WAIT_FOR_DO(egs.size() == 2, 500, egs.clear(); + policyMgr.getContractIntra(con2->getURI(), egs)); + + /* add con2 */ + vppManager.contractUpdated(con2->getURI()); + + ACL::action_t act = ACL::action_t::PERMIT; + ACL::l3_rule rule1(8192, + act, + route::prefix_t::ZERO, + route::prefix_t::ZERO, + 6, + 0, + 65535, + 80, + 65535, + 0, + 0); + ACL::l3_list::rules_t rules1({rule1}); + + gbp_contract::gbp_rules_t grules1 = {{8192, gbp_rule::action_t::PERMIT}}; + gbp_contract::ethertype_set_t allowed1 = {ethertype_t::IPV4, + ethertype_t::FCOE}; + + /* add con1 */ + vppManager.contractUpdated(con1->getURI()); + + ACL::l3_rule rule2(8192, + act, + route::prefix_t::ZERO, + route::prefix_t::ZERO, + 6, + 0, + 65535, + 80, + 65535, + 0, + 0); + ACL::l3_rule rule3(7936, + act, + route::prefix_t::ZERO, + route::prefix_t::ZERO, + 6, + 22, + 65535, + 0, + 65535, + 3, + 3); + ACL::l3_rule rule4(7808, + act, + route::prefix_t::ZERO, + route::prefix_t::ZERO, + 6, + 21, + 65535, + 0, + 65535, + 16, + 16); + ACL::l3_list::rules_t rules2({rule2, rule3, rule4}); + + ACL::l3_list outAcl2(con1->getURI().toString() + "out", rules2); + WAIT_FOR_MATCH(outAcl2); + + gbp_contract::gbp_rules_t grules2 = {{8192, gbp_rule::action_t::PERMIT}, + {7936, gbp_rule::action_t::PERMIT}, + {7808, gbp_rule::action_t::PERMIT}}; + gbp_contract::ethertype_set_t allowed2 = {ethertype_t::IPV4, + ethertype_t::ARP}; + + WAIT_FOR1(is_match(gbp_contract(202, 186, outAcl2, grules2, allowed2))); + WAIT_FOR1(is_match(gbp_contract(202, 187, outAcl2, grules2, allowed2))); + WAIT_FOR1(is_match(gbp_contract(203, 186, outAcl2, grules2, allowed2))); + WAIT_FOR1(is_match(gbp_contract(203, 187, outAcl2, grules2, allowed2))); +} + +BOOST_FIXTURE_TEST_CASE(policyPortRange, VppStitchedManagerFixture) +{ + createObjects(); + createPolicyObjects(); + + PolicyManager::uri_set_t egs; + WAIT_FOR_DO(egs.size() == 1, 1000, egs.clear(); + policyMgr.getContractProviders(con3->getURI(), egs)); + egs.clear(); + WAIT_FOR_DO(egs.size() == 1, 500, egs.clear(); + policyMgr.getContractConsumers(con3->getURI(), egs)); + PolicyManager::rule_list_t rules; + WAIT_FOR_DO(rules.size() == 3, 500, rules.clear(); + policyMgr.getContractRules(con3->getURI(), rules)); + + vppManager.contractUpdated(con3->getURI()); + ACL::ethertype_rule_t e1(ethertype_t::IPV4, direction_t::OUTPUT); + ACL::ethertype_rule_t e2(ethertype_t::IPV4, direction_t::OUTPUT); + ACL::ethertype_rule_t e3(ethertype_t::IPV4, direction_t::OUTPUT); + + ACL::acl_ethertype::ethertype_rules_t e_rules = {e1, e2, e3}; + + ACL::action_t act = ACL::action_t::PERMIT; + ACL::action_t act1 = ACL::action_t::DENY; + ACL::l3_rule rule1(8192, + act1, + route::prefix_t::ZERO, + route::prefix_t::ZERO, + 6, + 0, + 65535, + 80, + 85, + 0, + 0); + ACL::l3_rule rule2(8064, + act, + route::prefix_t::ZERO, + route::prefix_t::ZERO, + 6, + 66, + 69, + 94, + 95, + 0, + 0); + ACL::l3_rule rule3(7936, + act, + route::prefix_t::ZERO, + route::prefix_t::ZERO, + 1, + 10, + 10, + 5, + 5, + 0, + 0); + ACL::l3_list::rules_t rules1({rule1, rule2, rule3}); + + ACL::l3_list outAcl(con3->getURI().toString() + "out", rules1); + WAIT_FOR_MATCH(outAcl); + + gbp_contract::gbp_rules_t grules = {{8192, gbp_rule::action_t::PERMIT}, + {7936, gbp_rule::action_t::PERMIT}, + {7808, gbp_rule::action_t::PERMIT}}; + + gbp_contract::ethertype_set_t allowed = {ethertype_t::IPV4, + ethertype_t::ARP}; + + WAIT_FOR1(is_match(gbp_contract(187, 186, outAcl, grules, allowed))); +} + +BOOST_FIXTURE_TEST_CASE(policyRedirect, VppTransportManagerFixture) +{ + using modelgbp::gbpe::L24Classifier; + using namespace modelgbp; + using namespace modelgbp::gbp; + using namespace std; + + createObjects(); + createPolicyObjects(); + + shared_ptr<Contract> con5; + shared_ptr<L24Classifier> classifier11; + shared_ptr<RedirectAction> action3; + shared_ptr<RedirectDestGroup> redirDstGrp1; + shared_ptr<RedirectDestGroup> redirDstGrp2; + shared_ptr<RedirectDest> redirDst1; + shared_ptr<RedirectDest> redirDst2; + shared_ptr<RedirectDest> redirDst3; + shared_ptr<RedirectDest> redirDst4; + shared_ptr<RedirectDest> redirDst5; + + opflex::modb::Mutator mutator(framework, policyOwner); + classifier11 = space->addGbpeL24Classifier("classifier11"); + classifier11->setEtherT(l2::EtherTypeEnumT::CONST_IPV4) + .setProt(6 /* TCP */) + .setDFromPort(80); + + redirDstGrp1 = space->addGbpRedirectDestGroup("redirDstGrp1"); + redirDstGrp1->setHashAlgo(HashingAlgorithmEnumT::CONST_SYMMETRIC); + redirDstGrp1->setResilientHashEnabled(1); + redirDst1 = redirDstGrp1->addGbpRedirectDest("redirDst1"); + redirDst2 = redirDstGrp1->addGbpRedirectDest("redirDst2"); + opflex::modb::MAC mac1("00:01:02:03:04:05"), mac2("01:02:03:04:05:06"); + redirDst1->setIp("1.1.1.1"); + redirDst1->setMac(mac1); + redirDst1->addGbpRedirectDestToDomainRSrcBridgeDomain( + bd0->getURI().toString()); + redirDst1->addGbpRedirectDestToDomainRSrcRoutingDomain( + rd0->getURI().toString()); + redirDst2->setIp("2.2.2.2"); + redirDst2->setMac(mac2); + redirDst2->addGbpRedirectDestToDomainRSrcBridgeDomain( + bd0->getURI().toString()); + redirDst2->addGbpRedirectDestToDomainRSrcRoutingDomain( + rd0->getURI().toString()); + action3 = space->addGbpRedirectAction("action3"); + action3->addGbpRedirectActionToDestGrpRSrc()->setTargetRedirectDestGroup( + redirDstGrp1->getURI()); + redirDstGrp2 = space->addGbpRedirectDestGroup("redirDstGrp2"); + redirDst4 = redirDstGrp2->addGbpRedirectDest("redirDst4"); + opflex::modb::MAC mac3("02:03:04:05:06:07"), mac4("03:04:05:06:07:08"); + redirDst4->setIp("4.4.4.4"); + redirDst4->setMac(mac4); + redirDst4->addGbpRedirectDestToDomainRSrcBridgeDomain( + bd0->getURI().toString()); + redirDst4->addGbpRedirectDestToDomainRSrcRoutingDomain( + rd0->getURI().toString()); + + con5 = space->addGbpContract("contract5"); + con5->addGbpSubject("5_subject1") + ->addGbpRule("5_1_rule1") + ->setDirection(DirectionEnumT::CONST_IN) + .setOrder(100) + .addGbpRuleToClassifierRSrc(classifier11->getURI().toString()); + con5->addGbpSubject("5_subject1") + ->addGbpRule("5_1_rule1") + ->addGbpRuleToActionRSrcRedirectAction(action3->getURI().toString()); + + epg0->addGbpEpGroupToProvContractRSrc(con5->getURI().toString()); + epg1->addGbpEpGroupToConsContractRSrc(con5->getURI().toString()); + mutator.commit(); + + PolicyManager::uri_set_t egs; + WAIT_FOR_DO(egs.size() == 1, 1000, egs.clear(); + policyMgr.getContractProviders(con5->getURI(), egs)); + egs.clear(); + WAIT_FOR_DO(egs.size() == 1, 500, egs.clear(); + policyMgr.getContractConsumers(con5->getURI(), egs)); + + vppManager.contractUpdated(con5->getURI()); + WAIT_FOR(policyMgr.contractExists(con5->getURI()), 500); + + mac_address_t vmac1("00:01:02:03:04:05"); + mac_address_t vmac2("01:02:03:04:05:06"); + gbp_rule::next_hop_t nh1(address::from_string("1.1.1.1"), vmac1, 100, 100); + gbp_rule::next_hop_t nh2(address::from_string("2.2.2.2"), vmac2, 100, 100); + gbp_rule::next_hops_t nhs({nh1, nh2}); + gbp_rule::next_hop_set_t next_hop_set(gbp_rule::hash_mode_t::SYMMETRIC, + nhs); + gbp_rule gr(8192, next_hop_set, gbp_rule::action_t::REDIRECT); + gbp_contract::gbp_rules_t gbp_rules = {gr}; + + gbp_contract::ethertype_set_t e_rules = {ethertype_t::IPV4}; + + ACL::action_t act = ACL::action_t::PERMIT; + ACL::l3_rule rule1(8192, + act, + route::prefix_t::ZERO, + route::prefix_t::ZERO, + 6, + 0, + 65535, + 80, + 65535, + 0, + 0); + ACL::l3_list::rules_t rules1({rule1}); + + ACL::l3_list outAcl(con5->getURI().toString() + "out", rules1); + WAIT_FOR_MATCH(outAcl); + + gbp_contract gbpc(187, 186, outAcl, gbp_rules, e_rules); + + WAIT_FOR1(is_match(gbpc)); +} + +BOOST_AUTO_TEST_SUITE_END() + +/* + * Local Variables: + * eval: (c-set-style "llvm.org") + * End: + */ |