123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 |
- // Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- /// @file dhcp6_test_utils.h
- ///
- /// @brief This file contains utility classes used for DHCPv6 server testing
- #ifndef DHCP6_TEST_UTILS_H
- #define DHCP6_TEST_UTILS_H
- #include <gtest/gtest.h>
- #include <dhcp/pkt6.h>
- #include <dhcp/option6_ia.h>
- #include <dhcp/option6_iaaddr.h>
- #include <dhcp/option6_iaprefix.h>
- #include <dhcp/option_int_array.h>
- #include <dhcp/option_custom.h>
- #include <dhcp/iface_mgr.h>
- #include <dhcpsrv/cfgmgr.h>
- #include <dhcpsrv/lease_mgr.h>
- #include <dhcpsrv/lease_mgr_factory.h>
- #include <dhcp6/dhcp6_srv.h>
- #include <hooks/hooks_manager.h>
- #include <config/ccsession.h>
- #include <list>
- namespace isc {
- namespace test {
- /// @brief "naked" Dhcpv6Srv class that exposes internal members
- class NakedDhcpv6Srv: public isc::dhcp::Dhcpv6Srv {
- public:
- NakedDhcpv6Srv(uint16_t port) : isc::dhcp::Dhcpv6Srv(port) {
- // Open the "memfile" database for leases
- std::string memfile = "type=memfile";
- isc::dhcp::LeaseMgrFactory::create(memfile);
- }
- /// @brief fakes packet reception
- /// @param timeout ignored
- ///
- /// The method receives all packets queued in receive
- /// queue, one after another. Once the queue is empty,
- /// it initiates the shutdown procedure.
- ///
- /// See fake_received_ field for description
- virtual isc::dhcp::Pkt6Ptr receivePacket(int /*timeout*/) {
- // If there is anything prepared as fake incoming
- // traffic, use it
- if (!fake_received_.empty()) {
- isc::dhcp::Pkt6Ptr pkt = fake_received_.front();
- fake_received_.pop_front();
- return (pkt);
- }
- // If not, just trigger shutdown and
- // return immediately
- shutdown();
- return (isc::dhcp::Pkt6Ptr());
- }
- /// @brief fake packet sending
- ///
- /// Pretend to send a packet, but instead just store
- /// it in fake_send_ list where test can later inspect
- /// server's response.
- virtual void sendPacket(const isc::dhcp::Pkt6Ptr& pkt) {
- fake_sent_.push_back(pkt);
- }
- /// @brief adds a packet to fake receive queue
- ///
- /// See fake_received_ field for description
- void fakeReceive(const isc::dhcp::Pkt6Ptr& pkt) {
- fake_received_.push_back(pkt);
- }
- virtual ~NakedDhcpv6Srv() {
- // Close the lease database
- isc::dhcp::LeaseMgrFactory::destroy();
- }
- using Dhcpv6Srv::processSolicit;
- using Dhcpv6Srv::processRequest;
- using Dhcpv6Srv::processRenew;
- using Dhcpv6Srv::processRelease;
- using Dhcpv6Srv::processClientFqdn;
- using Dhcpv6Srv::createNameChangeRequests;
- using Dhcpv6Srv::createRemovalNameChangeRequest;
- using Dhcpv6Srv::createStatusCode;
- using Dhcpv6Srv::selectSubnet;
- using Dhcpv6Srv::testServerID;
- using Dhcpv6Srv::sanityCheck;
- using Dhcpv6Srv::classifyPacket;
- using Dhcpv6Srv::loadServerID;
- using Dhcpv6Srv::writeServerID;
- using Dhcpv6Srv::unpackOptions;
- using Dhcpv6Srv::name_change_reqs_;
- /// @brief packets we pretend to receive
- ///
- /// Instead of setting up sockets on interfaces that change between
- /// OSes, it is much easier to fake packet reception. This is a list
- /// of packets that we pretend to have received. You can schedule
- /// new packets to be received using fakeReceive() and
- /// NakedDhcpv6Srv::receivePacket() methods.
- std::list<isc::dhcp::Pkt6Ptr> fake_received_;
- std::list<isc::dhcp::Pkt6Ptr> fake_sent_;
- };
- static const char* DUID_FILE = "server-id-test.txt";
- // test fixture for any tests requiring blank/empty configuration
- // serves as base class for additional tests
- class NakedDhcpv6SrvTest : public ::testing::Test {
- public:
- NakedDhcpv6SrvTest() : rcode_(-1) {
- // it's ok if that fails. There should not be such a file anyway
- unlink(DUID_FILE);
- const isc::dhcp::IfaceMgr::IfaceCollection& ifaces =
- isc::dhcp::IfaceMgr::instance().getIfaces();
- // There must be some interface detected
- if (ifaces.empty()) {
- // We can't use ASSERT in constructor
- ADD_FAILURE() << "No interfaces detected.";
- }
- valid_iface_ = ifaces.begin()->getName();
- }
- // Generate IA_NA or IA_PD option with specified parameters
- boost::shared_ptr<isc::dhcp::Option6IA> generateIA
- (uint16_t type, uint32_t iaid, uint32_t t1, uint32_t t2);
- /// @brief generates interface-id option, based on text
- ///
- /// @param iface_id textual representation of the interface-id content
- ///
- /// @return pointer to the option object
- isc::dhcp::OptionPtr generateInterfaceId(const std::string& iface_id) {
- isc::dhcp::OptionBuffer tmp(iface_id.begin(), iface_id.end());
- return (isc::dhcp::OptionPtr
- (new isc::dhcp::Option(isc::dhcp::Option::V6,
- D6O_INTERFACE_ID, tmp)));
- }
- // Generate client-id option
- isc::dhcp::OptionPtr generateClientId(size_t duid_size = 32) {
- isc::dhcp::OptionBuffer clnt_duid(duid_size);
- for (int i = 0; i < duid_size; i++) {
- clnt_duid[i] = 100 + i;
- }
- duid_ = isc::dhcp::DuidPtr(new isc::dhcp::DUID(clnt_duid));
- return (isc::dhcp::OptionPtr
- (new isc::dhcp::Option(isc::dhcp::Option::V6, D6O_CLIENTID,
- clnt_duid.begin(),
- clnt_duid.begin() + duid_size)));
- }
- // Checks if server response (ADVERTISE or REPLY) includes proper
- // server-id.
- void checkServerId(const isc::dhcp::Pkt6Ptr& rsp,
- const isc::dhcp::OptionPtr& expected_srvid)
- {
- // check that server included its server-id
- isc::dhcp::OptionPtr tmp = rsp->getOption(D6O_SERVERID);
- EXPECT_EQ(tmp->getType(), expected_srvid->getType() );
- ASSERT_EQ(tmp->len(), expected_srvid->len() );
- EXPECT_TRUE(tmp->getData() == expected_srvid->getData());
- }
- // Checks if server response (ADVERTISE or REPLY) includes proper
- // client-id.
- void checkClientId(const isc::dhcp::Pkt6Ptr& rsp,
- const isc::dhcp::OptionPtr& expected_clientid)
- {
- // check that server included our own client-id
- isc::dhcp::OptionPtr tmp = rsp->getOption(D6O_CLIENTID);
- ASSERT_TRUE(tmp);
- EXPECT_EQ(expected_clientid->getType(), tmp->getType());
- ASSERT_EQ(expected_clientid->len(), tmp->len());
- // check that returned client-id is valid
- EXPECT_TRUE(expected_clientid->getData() == tmp->getData());
- }
- // Checks if server response is a NAK
- void checkNakResponse(const isc::dhcp::Pkt6Ptr& rsp,
- uint8_t expected_message_type,
- uint32_t expected_transid,
- uint16_t expected_status_code)
- {
- // Check if we get response at all
- checkResponse(rsp, expected_message_type, expected_transid);
- // Check that IA_NA was returned
- isc::dhcp::OptionPtr option_ia_na = rsp->getOption(D6O_IA_NA);
- ASSERT_TRUE(option_ia_na);
- // check that the status is no address available
- boost::shared_ptr<isc::dhcp::Option6IA> ia =
- boost::dynamic_pointer_cast<isc::dhcp::Option6IA>(option_ia_na);
- ASSERT_TRUE(ia);
- checkIA_NAStatusCode(ia, expected_status_code);
- }
- // Checks that server rejected IA_NA, i.e. that it has no addresses and
- // that expected status code really appears there. In some limited cases
- // (reply to RELEASE) it may be used to verify positive case, where
- // IA_NA response is expected to not include address.
- //
- // Status code indicates type of error encountered (in theory it can also
- // indicate success, but servers typically don't send success status
- // as this is the default result and it saves bandwidth)
- void checkIA_NAStatusCode
- (const boost::shared_ptr<isc::dhcp::Option6IA>& ia,
- uint16_t expected_status_code)
- {
- // Make sure there is no address assigned.
- EXPECT_FALSE(ia->getOption(D6O_IAADDR));
- // T1, T2 should be zeroed
- EXPECT_EQ(0, ia->getT1());
- EXPECT_EQ(0, ia->getT2());
- isc::dhcp::OptionCustomPtr status =
- boost::dynamic_pointer_cast<isc::dhcp::OptionCustom>
- (ia->getOption(D6O_STATUS_CODE));
- // It is ok to not include status success as this is the default
- // behavior
- if (expected_status_code == STATUS_Success && !status) {
- return;
- }
- EXPECT_TRUE(status);
- if (status) {
- // We don't have dedicated class for status code, so let's
- // just interpret first 2 bytes as status. Remainder of the
- // status code option content is just a text explanation
- // what went wrong.
- EXPECT_EQ(static_cast<uint16_t>(expected_status_code),
- status->readInteger<uint16_t>(0));
- }
- }
- void checkMsgStatusCode(const isc::dhcp::Pkt6Ptr& msg,
- uint16_t expected_status)
- {
- isc::dhcp::OptionCustomPtr status =
- boost::dynamic_pointer_cast<isc::dhcp::OptionCustom>
- (msg->getOption(D6O_STATUS_CODE));
- // It is ok to not include status success as this is the default
- // behavior
- if (expected_status == STATUS_Success && !status) {
- return;
- }
- EXPECT_TRUE(status);
- if (status) {
- // We don't have dedicated class for status code, so let's
- // just interpret first 2 bytes as status. Remainder of the
- // status code option content is just a text explanation
- // what went wrong.
- EXPECT_EQ(static_cast<uint16_t>(expected_status),
- status->readInteger<uint16_t>(0));
- }
- }
- // Basic checks for generated response (message type and transaction-id).
- void checkResponse(const isc::dhcp::Pkt6Ptr& rsp,
- uint8_t expected_message_type,
- uint32_t expected_transid) {
- ASSERT_TRUE(rsp);
- EXPECT_EQ(expected_message_type, rsp->getType());
- EXPECT_EQ(expected_transid, rsp->getTransid());
- }
- virtual ~NakedDhcpv6SrvTest() {
- // Let's clean up if there is such a file.
- unlink(DUID_FILE);
- isc::hooks::HooksManager::preCalloutsLibraryHandle()
- .deregisterAllCallouts("buffer6_receive");
- isc::hooks::HooksManager::preCalloutsLibraryHandle()
- .deregisterAllCallouts("buffer6_send");
- isc::hooks::HooksManager::preCalloutsLibraryHandle()
- .deregisterAllCallouts("lease6_renew");
- isc::hooks::HooksManager::preCalloutsLibraryHandle()
- .deregisterAllCallouts("lease6_release");
- isc::hooks::HooksManager::preCalloutsLibraryHandle()
- .deregisterAllCallouts("pkt6_receive");
- isc::hooks::HooksManager::preCalloutsLibraryHandle()
- .deregisterAllCallouts("pkt6_send");
- isc::hooks::HooksManager::preCalloutsLibraryHandle()
- .deregisterAllCallouts("subnet6_select");
- };
- // A DUID used in most tests (typically as client-id)
- isc::dhcp::DuidPtr duid_;
- int rcode_;
- isc::data::ConstElementPtr comment_;
- // Name of a valid network interface
- std::string valid_iface_;
- };
- // Provides suport for tests against a preconfigured subnet6
- // extends upon NakedDhcp6SrvTest
- class Dhcpv6SrvTest : public NakedDhcpv6SrvTest {
- public:
- /// Name of the server-id file (used in server-id tests)
- /// @brief Constructor that initalizes a simple default configuration
- ///
- /// Sets up a single subnet6 with one pool for addresses and second
- /// pool for prefixes.
- Dhcpv6SrvTest() {
- subnet_ = isc::dhcp::Subnet6Ptr
- (new isc::dhcp::Subnet6(isc::asiolink::IOAddress("2001:db8:1::"),
- 48, 1000, 2000, 3000, 4000));
- pool_ = isc::dhcp::Pool6Ptr
- (new isc::dhcp::Pool6(isc::dhcp::Lease::TYPE_NA,
- isc::asiolink::IOAddress("2001:db8:1:1::"),
- 64));
- subnet_->addPool(pool_);
- isc::dhcp::CfgMgr::instance().deleteSubnets6();
- isc::dhcp::CfgMgr::instance().addSubnet6(subnet_);
- // configure PD pool
- pd_pool_ = isc::dhcp::Pool6Ptr
- (new isc::dhcp::Pool6(isc::dhcp::Lease::TYPE_PD,
- isc::asiolink::IOAddress("2001:db8:1:2::"),
- 64, 80));
- subnet_->addPool(pd_pool_);
- }
- /// @brief destructor
- ///
- /// Removes existing configuration.
- ~Dhcpv6SrvTest() {
- isc::dhcp::CfgMgr::instance().deleteSubnets6();
- };
- /// @brief Checks that server response (ADVERTISE or REPLY) contains proper
- /// IA_NA option
- ///
- /// @param rsp server's response
- /// @param expected_iaid expected IAID value
- /// @param expected_t1 expected T1 value
- /// @param expected_t2 expected T2 value
- /// @return IAADDR option for easy chaining with checkIAAddr method
- boost::shared_ptr<isc::dhcp::Option6IAAddr>
- checkIA_NA(const isc::dhcp::Pkt6Ptr& rsp, uint32_t expected_iaid,
- uint32_t expected_t1, uint32_t expected_t2);
- /// @brief Checks that server response (ADVERTISE or REPLY) contains proper
- /// IA_PD option
- ///
- /// @param rsp server's response
- /// @param expected_iaid expected IAID value
- /// @param expected_t1 expected T1 value
- /// @param expected_t2 expected T2 value
- /// @return IAPREFIX option for easy chaining with checkIAAddr method
- boost::shared_ptr<isc::dhcp::Option6IAPrefix>
- checkIA_PD(const isc::dhcp::Pkt6Ptr& rsp, uint32_t expected_iaid,
- uint32_t expected_t1, uint32_t expected_t2);
- // Check that generated IAADDR option contains expected address
- // and lifetime values match the configured subnet
- void checkIAAddr(const boost::shared_ptr<isc::dhcp::Option6IAAddr>& addr,
- const isc::asiolink::IOAddress& expected_addr,
- isc::dhcp::Lease::Type type) {
- // Check that the assigned address is indeed from the configured pool.
- // Note that when comparing addresses, we compare the textual
- // representation. IOAddress does not support being streamed to
- // an ostream, which means it can't be used in EXPECT_EQ.
- EXPECT_TRUE(subnet_->inPool(type, addr->getAddress()));
- EXPECT_EQ(expected_addr.toText(), addr->getAddress().toText());
- EXPECT_EQ(addr->getPreferred(), subnet_->getPreferred());
- EXPECT_EQ(addr->getValid(), subnet_->getValid());
- }
- // Checks if the lease sent to client is present in the database
- // and is valid when checked agasint the configured subnet
- isc::dhcp::Lease6Ptr checkLease
- (const isc::dhcp::DuidPtr& duid, const isc::dhcp::OptionPtr& ia_na,
- boost::shared_ptr<isc::dhcp::Option6IAAddr> addr);
- /// @brief Verifies received IAPrefix option
- ///
- /// Verifies if the received IAPrefix option matches the lease in the
- /// database.
- ///
- /// @param duid client's DUID
- /// @param ia_pd IA_PD option that contains the IAPRefix option
- /// @param prefix pointer to the IAPREFIX option
- /// @return corresponding IPv6 lease (if found)
- isc::dhcp::Lease6Ptr checkPdLease
- (const isc::dhcp::DuidPtr& duid, const isc::dhcp::OptionPtr& ia_pd,
- boost::shared_ptr<isc::dhcp::Option6IAPrefix> prefix);
- /// @brief Creates a message with specified IA
- ///
- /// A utility function that creates a message of the specified type with
- /// a specified container (IA_NA or IA_PD) and an address or prefix
- /// inside it.
- ///
- /// @param message_type type of the message (e.g. DHCPV6_SOLICIT)
- /// @param lease_type type of a lease (TYPE_NA or TYPE_PD)
- /// @param addr address or prefix to use in IADDRESS or IAPREFIX options
- /// @param prefix_len length of the prefix (used for prefixes only)
- /// @param iaid IA identifier (used in IA_XX option)
- /// @return created message
- isc::dhcp::Pkt6Ptr
- createMessage(uint8_t message_type, isc::dhcp::Lease::Type lease_type,
- const isc::asiolink::IOAddress& addr,
- const uint8_t prefix_len, uint32_t iaid);
- /// @brief Performs basic (positive) RENEW test
- ///
- /// See renewBasic and pdRenewBasic tests for detailed explanation.
- /// In essence the test attempts to perform a successful RENEW scenario.
- ///
- /// This method does not throw, but uses gtest macros to signify failures.
- ///
- /// @param type type (TYPE_NA or TYPE_PD)
- /// @param existing_addr address to be preinserted into the database
- /// @param renew_addr address being sent in RENEW
- /// @param prefix_len length of the prefix (128 for addresses)
- void
- testRenewBasic(isc::dhcp::Lease::Type type,
- const std::string& existing_addr,
- const std::string& renew_addr, const uint8_t prefix_len);
- /// @brief Performs negative RENEW test
- ///
- /// See renewReject and pdRenewReject tests for detailed explanation.
- /// In essence the test attempts to perform couple failed RENEW scenarios.
- ///
- /// This method does not throw, but uses gtest macros to signify failures.
- ///
- /// @param type type (TYPE_NA or TYPE_PD)
- /// @param addr address being sent in RENEW
- void
- testRenewReject(isc::dhcp::Lease::Type type,
- const isc::asiolink::IOAddress& addr);
- /// @brief Performs basic (positive) RELEASE test
- ///
- /// See releaseBasic and pdReleaseBasic tests for detailed explanation.
- /// In essence the test attempts to perform a successful RELEASE scenario.
- ///
- /// This method does not throw, but uses gtest macros to signify failures.
- ///
- /// @param type type (TYPE_NA or TYPE_PD)
- /// @param existing address to be preinserted into the database
- /// @param release_addr address being sent in RELEASE
- void
- testReleaseBasic(isc::dhcp::Lease::Type type,
- const isc::asiolink::IOAddress& existing,
- const isc::asiolink::IOAddress& release_addr);
- /// @brief Performs negative RELEASE test
- ///
- /// See releaseReject and pdReleaseReject tests for detailed
- /// explanation. In essence the test attempts to perform couple
- /// failed RELEASE scenarios.
- ///
- /// This method does not throw, but uses gtest macros to signify failures.
- ///
- /// @param type type (TYPE_NA or TYPE_PD)
- /// @param addr address being sent in RELEASE
- void
- testReleaseReject(isc::dhcp::Lease::Type type,
- const isc::asiolink::IOAddress& addr);
- // see wireshark.cc for descriptions
- // The descriptions are too large and too closely related to the
- // code, so it is kept in .cc rather than traditionally in .h
- isc::dhcp::Pkt6Ptr captureSimpleSolicit();
- isc::dhcp::Pkt6Ptr captureRelayedSolicit();
- isc::dhcp::Pkt6Ptr captureDocsisRelayedSolicit();
- isc::dhcp::Pkt6Ptr captureeRouterRelayedSolicit();
- /// @brief Auxiliary method that sets Pkt6 fields
- ///
- /// Used to reconstruct captured packets. Sets UDP ports, interface names,
- /// and other fields to some believable values.
- /// @param pkt packet that will have its fields set
- void captureSetDefaultFields(const isc::dhcp::Pkt6Ptr& pkt);
- /// A subnet used in most tests
- isc::dhcp::Subnet6Ptr subnet_;
- /// A normal, non-temporary pool used in most tests
- isc::dhcp::Pool6Ptr pool_;
- /// A prefix pool used in most tests
- isc::dhcp::Pool6Ptr pd_pool_;
- };
- }; // end of isc::test namespace
- }; // end of isc namespace
- #endif // DHCP6_TEST_UTILS_H
|