]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[master] Merge branch 'trac3184' (RAI option echoed back in DHCPv4)
authorTomek Mrugalski <tomasz@isc.org>
Fri, 11 Oct 2013 16:01:24 +0000 (18:01 +0200)
committerTomek Mrugalski <tomasz@isc.org>
Fri, 11 Oct 2013 16:01:24 +0000 (18:01 +0200)
Conflicts:
ChangeLog
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc

1  2 
ChangeLog
src/bin/dhcp4/dhcp4_srv.cc
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
src/bin/dhcp4/tests/dhcp4_test_utils.h

diff --cc ChangeLog
index 77feb7d42f6514bc8e12ef64d389374e7f88db9e,2a2dfe52d517396dc8536efcef378b3b0cac749d..a8af86281f5c3109e7f9fc2686ac0860c8be766c
+++ b/ChangeLog
@@@ -1,12 -1,7 +1,17 @@@
 -6XX.  [bug]           tomek
++690.  [bug]           tomek
+       b10-dhcp4: Relay Agent Info option is now echoed back in
+       DHCPv4 responses.
 -      (Trac #3184, git ABCD)
++      (Trac #3184, git 287389c049518bff66bdf6a5a49bb8768be02d8e)
++
 +689.  [func]*         marcin
 +      b10-dhcp4 and b10-dhcp6 install callback functions which parse options
 +      in the received DHCP packets.
 +      (Trac #3180, git f73fba3cde9421acbeb9486c615900b0af58fa25)
 +
 +688.  [func]          tomek
 +      b10-dhcp6: Prefix Delegation support is now extended to
 +      Renew and Release messages.
 +      (Trac #3153,#3154, git 3207932815f58045acea84ae092e0a5aa7c4bfd7)
  
  687.  [func]          tomek
        b10-dhcp6: Prefix Delegation (IA_PD and IAPREFIX options) is now
Simple merge
index 5ebe47e9ef0fd0abacf535a799a00558c4e3204f,57ef2817a1b7d4ccf2080f593e582f9c87f55eb5..3f2da84f9b9efac0ac9087be4434090134d07c5c
  #include <dhcp/dhcp4.h>
  #include <dhcp/iface_mgr.h>
  #include <dhcp/option.h>
- #include <dhcp/option_int.h>
++#include <dhcp/option_int.h>
 +#include <dhcp/option4_addrlst.h>
 +#include <dhcp/option_custom.h>
 +#include <dhcp/option_int_array.h>
  #include <dhcp/pkt_filter.h>
  #include <dhcp/pkt_filter_inet.h>
  #include <dhcp4/dhcp4_srv.h>
@@@ -52,596 -49,12 +53,113 @@@ using namespace isc::data
  using namespace isc::asiolink;
  using namespace isc::hooks;
  
++#if 0
 +namespace {
 +
 +class NakedDhcpv4Srv: public Dhcpv4Srv {
 +    // "Naked" DHCPv4 server, exposes internal fields
 +public:
 +
 +    /// @brief Constructor.
 +    ///
 +    /// This constructor disables default modes of operation used by the
 +    /// Dhcpv4Srv class:
 +    /// - Send/receive broadcast messages through sockets on interfaces
 +    /// which support broadcast traffic.
 +    /// - Direct DHCPv4 traffic - communication with clients which do not
 +    /// have IP address assigned yet.
 +    ///
 +    /// Enabling these modes requires root privilges so they must be
 +    /// disabled for unit testing.
 +    ///
 +    /// Note, that disabling broadcast options on sockets does not impact
 +    /// the operation of these tests because they use local loopback
 +    /// interface which doesn't have broadcast capability anyway. It rather
 +    /// prevents setting broadcast options on other (broadcast capable)
 +    /// sockets which are opened on other interfaces in Dhcpv4Srv constructor.
 +    ///
 +    /// The Direct DHCPv4 Traffic capability can be disabled here because
 +    /// it is tested with PktFilterLPFTest unittest. The tests which belong
 +    /// to PktFilterLPFTest can be enabled on demand when root privileges can
 +    /// be guaranteed.
 +    ///
 +    /// @param port port number to listen on; the default value 0 indicates
 +    /// that sockets should not be opened.
 +    NakedDhcpv4Srv(uint16_t port = 0)
 +        : Dhcpv4Srv(port, "type=memfile", false, false) {
 +    }
 +
 +    /// @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 Pkt4Ptr receivePacket(int /*timeout*/) {
 +
 +        // If there is anything prepared as fake incoming traffic, use it
 +        if (!fake_received_.empty()) {
 +            Pkt4Ptr pkt = fake_received_.front();
 +            fake_received_.pop_front();
 +            return (pkt);
 +        }
 +
 +        // If not, just trigger shutdown and return immediately
 +        shutdown();
 +        return (Pkt4Ptr());
 +    }
 +
 +    /// @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 Pkt4Ptr& pkt) {
 +        fake_sent_.push_back(pkt);
 +    }
 +
 +    /// @brief adds a packet to fake receive queue
 +    ///
 +    /// See fake_received_ field for description
 +    void fakeReceive(const Pkt4Ptr& pkt) {
 +        fake_received_.push_back(pkt);
 +    }
 +
 +    virtual ~NakedDhcpv4Srv() {
 +    }
 +
 +    /// @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 NakedDhcpv4Srv::receivePacket() methods.
 +    list<Pkt4Ptr> fake_received_;
 +
 +    list<Pkt4Ptr> fake_sent_;
 +
 +    using Dhcpv4Srv::adjustRemoteAddr;
 +    using Dhcpv4Srv::processDiscover;
 +    using Dhcpv4Srv::processRequest;
 +    using Dhcpv4Srv::processRelease;
 +    using Dhcpv4Srv::processDecline;
 +    using Dhcpv4Srv::processInform;
 +    using Dhcpv4Srv::getServerID;
 +    using Dhcpv4Srv::loadServerID;
 +    using Dhcpv4Srv::generateServerID;
 +    using Dhcpv4Srv::writeServerID;
 +    using Dhcpv4Srv::sanityCheck;
 +    using Dhcpv4Srv::srvidToString;
 +    using Dhcpv4Srv::unpackOptions;
 +};
++#endif
 +
+ /// dummy server-id file location
  static const char* SRVID_FILE = "server-id-test.txt";
  
- /// @brief Dummy Packet Filtering class.
- ///
- /// This class reports capability to respond directly to the client which
- /// doesn't have address configured yet.
- ///
- /// All packet and socket handling functions do nothing because they are not
- /// used in unit tests.
- class PktFilterTest : public PktFilter {
- public:
-     /// @brief Reports 'direct response' capability.
-     ///
-     /// @return always true.
-     virtual bool isDirectResponseSupported() const {
-         return (true);
-     }
-     /// Does nothing.
-     virtual int openSocket(const Iface&, const IOAddress&, const uint16_t,
-                            const bool, const bool) {
-         return (0);
-     }
-     /// Does nothing.
-     virtual Pkt4Ptr receive(const Iface&, const SocketInfo&) {
-         return Pkt4Ptr();
-     }
-     /// Does nothing.
-     virtual int send(const Iface&, uint16_t, const Pkt4Ptr&) {
-         return (0);
-     }
- };
- class Dhcpv4SrvTest : public ::testing::Test {
- public:
-     /// @brief Constructor
-     ///
-     /// Initializes common objects used in many tests.
-     /// Also sets up initial configuration in CfgMgr.
-     Dhcpv4SrvTest() :
-         rcode_(-1)
-     {
-         subnet_ = Subnet4Ptr(new Subnet4(IOAddress("192.0.2.0"), 24, 1000,
-                                          2000, 3000));
-         pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"), IOAddress("192.0.2.110")));
-         subnet_->addPool(pool_);
-         CfgMgr::instance().deleteSubnets4();
-         CfgMgr::instance().addSubnet4(subnet_);
-         // Add Router option.
-         Option4AddrLstPtr opt_routers(new Option4AddrLst(DHO_ROUTERS));
-         opt_routers->setAddress(IOAddress("192.0.2.2"));
-         subnet_->addOption(opt_routers, false, "dhcp4");
-         // it's ok if that fails. There should not be such a file anyway
-         unlink(SRVID_FILE);
-         const IfaceMgr::IfaceCollection& ifaces = 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();
-     }
-     virtual ~Dhcpv4SrvTest() {
-     }
-     /// @brief Add 'Parameter Request List' option to the packet.
-     ///
-     /// This function PRL option comprising the following option codes:
-     /// - 5 - Name Server
-     /// - 15 - Domain Name
-     /// - 7 - Log Server
-     /// - 8 - Quotes Server
-     /// - 9 - LPR Server
-     ///
-     /// @param pkt packet to add PRL option to.
-     void addPrlOption(Pkt4Ptr& pkt) {
-         OptionUint8ArrayPtr option_prl =
-             OptionUint8ArrayPtr(new OptionUint8Array(Option::V4,
-                                                      DHO_DHCP_PARAMETER_REQUEST_LIST));
-         // Let's request options that have been configured for the subnet.
-         option_prl->addValue(DHO_DOMAIN_NAME_SERVERS);
-         option_prl->addValue(DHO_DOMAIN_NAME);
-         option_prl->addValue(DHO_LOG_SERVERS);
-         option_prl->addValue(DHO_COOKIE_SERVERS);
-         // Let's also request the option that hasn't been configured. In such
-         // case server should ignore request for this particular option.
-         option_prl->addValue(DHO_LPR_SERVERS);
-         // And add 'Parameter Request List' option into the DISCOVER packet.
-         pkt->addOption(option_prl);
-     }
-     /// @brief Configures options being requested in the PRL option.
-     ///
-     /// The lpr-servers option is NOT configured here although it is
-     /// added to the 'Parameter Request List' option in the
-     /// \ref addPrlOption. When requested option is not configured
-     /// the server should not return it in its response. The goal
-     /// of not configuring the requested option is to verify that
-     /// the server will not return it.
-     void configureRequestedOptions() {
-         // dns-servers
-         Option4AddrLstPtr
-             option_dns_servers(new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS));
-         option_dns_servers->addAddress(IOAddress("192.0.2.1"));
-         option_dns_servers->addAddress(IOAddress("192.0.2.100"));
-         ASSERT_NO_THROW(subnet_->addOption(option_dns_servers, false, "dhcp4"));
-         // domain-name
-         OptionDefinition def("domain-name", DHO_DOMAIN_NAME, OPT_FQDN_TYPE);
-         OptionCustomPtr option_domain_name(new OptionCustom(def, Option::V4));
-         option_domain_name->writeFqdn("example.com");
-         subnet_->addOption(option_domain_name, false, "dhcp4");
-         // log-servers
-         Option4AddrLstPtr option_log_servers(new Option4AddrLst(DHO_LOG_SERVERS));
-         option_log_servers->addAddress(IOAddress("192.0.2.2"));
-         option_log_servers->addAddress(IOAddress("192.0.2.10"));
-         ASSERT_NO_THROW(subnet_->addOption(option_log_servers, false, "dhcp4"));
-         // cookie-servers
-         Option4AddrLstPtr option_cookie_servers(new Option4AddrLst(DHO_COOKIE_SERVERS));
-         option_cookie_servers->addAddress(IOAddress("192.0.2.1"));
-         ASSERT_NO_THROW(subnet_->addOption(option_cookie_servers, false, "dhcp4"));
-     }
-     /// @brief checks that the response matches request
-     /// @param q query (client's message)
-     /// @param a answer (server's message)
-     void messageCheck(const Pkt4Ptr& q, const Pkt4Ptr& a) {
-         ASSERT_TRUE(q);
-         ASSERT_TRUE(a);
-         EXPECT_EQ(q->getHops(),   a->getHops());
-         EXPECT_EQ(q->getIface(),  a->getIface());
-         EXPECT_EQ(q->getIndex(),  a->getIndex());
-         EXPECT_EQ(q->getGiaddr(), a->getGiaddr());
-         // When processing an incoming packet the remote address
-         // is copied as a src address, and the source address is
-         // copied as a remote address to the response.
-         EXPECT_TRUE(q->getLocalHWAddr() == a->getLocalHWAddr());
-         EXPECT_TRUE(q->getRemoteHWAddr() == a->getRemoteHWAddr());
-         // Check that bare minimum of required options are there.
-         // We don't check options requested by a client. Those
-         // are checked elsewhere.
-         EXPECT_TRUE(a->getOption(DHO_SUBNET_MASK));
-         EXPECT_TRUE(a->getOption(DHO_ROUTERS));
-         EXPECT_TRUE(a->getOption(DHO_DHCP_SERVER_IDENTIFIER));
-         EXPECT_TRUE(a->getOption(DHO_DHCP_LEASE_TIME));
-         EXPECT_TRUE(a->getOption(DHO_SUBNET_MASK));
-         EXPECT_TRUE(a->getOption(DHO_DOMAIN_NAME));
-         EXPECT_TRUE(a->getOption(DHO_DOMAIN_NAME_SERVERS));
-         // Check that something is offered
-         EXPECT_TRUE(a->getYiaddr().toText() != "0.0.0.0");
-     }
-     /// @brief Check that requested options are present.
-     ///
-     /// @param pkt packet to be checked.
-     void optionsCheck(const Pkt4Ptr& pkt) {
-         // Check that the requested and configured options are returned
-         // in the ACK message.
-         EXPECT_TRUE(pkt->getOption(DHO_DOMAIN_NAME))
-             << "domain-name not present in the response";
-         EXPECT_TRUE(pkt->getOption(DHO_DOMAIN_NAME_SERVERS))
-             << "dns-servers not present in the response";
-         EXPECT_TRUE(pkt->getOption(DHO_LOG_SERVERS))
-             << "log-servers not present in the response";
-         EXPECT_TRUE(pkt->getOption(DHO_COOKIE_SERVERS))
-             << "cookie-servers not present in the response";
-         // Check that the requested but not configured options are not
-         // returned in the ACK message.
-         EXPECT_FALSE(pkt->getOption(DHO_LPR_SERVERS))
-             << "domain-name present in the response but it is"
-             << " expected not to be present";
-     }
-     /// @brief generates client-id option
-     ///
-     /// Generate client-id option of specified length
-     /// Ids with different lengths are sufficent to generate
-     /// unique ids. If more fine grained control is required,
-     /// tests generate client-ids on their own.
-     /// Sets client_id_ field.
-     /// @param size size of the client-id to be generated
-     OptionPtr generateClientId(size_t size = 4) {
-         OptionBuffer clnt_id(size);
-         for (int i = 0; i < size; i++) {
-             clnt_id[i] = 100 + i;
-         }
-         client_id_ = ClientIdPtr(new ClientId(clnt_id));
-         return (OptionPtr(new Option(Option::V4, DHO_DHCP_CLIENT_IDENTIFIER,
-                                      clnt_id.begin(),
-                                      clnt_id.begin() + size)));
-     }
-     /// @brief generate hardware address
-     ///
-     /// @param size size of the generated MAC address
-     /// @param pointer to Hardware Address object
-     HWAddrPtr generateHWAddr(size_t size = 6) {
-         const uint8_t hw_type = 123; // Just a fake number (typically 6=HTYPE_ETHER, see dhcp4.h)
-         OptionBuffer mac(size);
-         for (int i = 0; i < size; ++i) {
-             mac[i] = 50 + i;
-         }
-         return (HWAddrPtr(new HWAddr(mac, hw_type)));
-     }
-     /// Check that address was returned from proper range, that its lease
-     /// lifetime is correct, that T1 and T2 are returned properly
-     /// @param rsp response to be checked
-     /// @param subnet subnet that should be used to verify assigned address
-     ///        and options
-     /// @param t1_mandatory is T1 mandatory?
-     /// @param t2_mandatory is T2 mandatory?
-     void checkAddressParams(const Pkt4Ptr& rsp, const SubnetPtr subnet,
-                             bool t1_mandatory = false,
-                             bool t2_mandatory = false) {
-         // Technically inPool implies inRange, but let's be on the safe
-         // side and check both.
-         EXPECT_TRUE(subnet->inRange(rsp->getYiaddr()));
-         EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, rsp->getYiaddr()));
-         // Check lease time
-         OptionPtr opt = rsp->getOption(DHO_DHCP_LEASE_TIME);
-         if (!opt) {
-             ADD_FAILURE() << "Lease time option missing in response";
-         } else {
-             EXPECT_EQ(opt->getUint32(), subnet->getValid());
-         }
-         // Check T1 timer
-         opt = rsp->getOption(DHO_DHCP_RENEWAL_TIME);
-         if (opt) {
-             EXPECT_EQ(opt->getUint32(), subnet->getT1());
-         } else {
-             if (t1_mandatory) {
-                 ADD_FAILURE() << "Required T1 option missing";
-             }
-         }
-         // Check T2 timer
-         opt = rsp->getOption(DHO_DHCP_REBINDING_TIME);
-         if (opt) {
-             EXPECT_EQ(opt->getUint32(), subnet->getT2());
-         } else {
-             if (t2_mandatory) {
-                 ADD_FAILURE() << "Required T2 option missing";
-             }
-         }
-     }
-     /// @brief Basic checks for generated response (message type and trans-id).
-     ///
-     /// @param rsp response packet to be validated
-     /// @param expected_message_type expected message type
-     /// @param expected_transid expected transaction-id
-     void checkResponse(const Pkt4Ptr& 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());
-     }
-     /// @brief Checks if the lease sent to client is present in the database
-     ///
-     /// @param rsp response packet to be validated
-     /// @param client_id expected client-identifier (or NULL)
-     /// @param HWAddr expected hardware address (not used now)
-     /// @param expected_addr expected address
-     Lease4Ptr checkLease(const Pkt4Ptr& rsp, const OptionPtr& client_id,
-                          const HWAddrPtr&, const IOAddress& expected_addr) {
-         ClientIdPtr id;
-         if (client_id) {
-             OptionBuffer data = client_id->getData();
-             id.reset(new ClientId(data));
-         }
-         Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(expected_addr);
-         if (!lease) {
-             cout << "Lease for " << expected_addr.toText()
-                  << " not found in the database backend.";
-             return (Lease4Ptr());
-         }
-         EXPECT_EQ(rsp->getYiaddr().toText(), expected_addr.toText());
-         EXPECT_EQ(expected_addr.toText(), lease->addr_.toText());
-         if (client_id) {
-             EXPECT_TRUE(*lease->client_id_ == *id);
-         }
-         EXPECT_EQ(subnet_->getID(), lease->subnet_id_);
-         return (lease);
-     }
-     /// @brief Checks if server response (OFFER, ACK, NAK) includes proper server-id
-     /// @param rsp response packet to be validated
-     /// @param expected_srvid expected value of server-id
-     void checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid) {
-         // Check that server included its server-id
-         OptionPtr opt = rsp->getOption(DHO_DHCP_SERVER_IDENTIFIER);
-         ASSERT_TRUE(opt);
-         EXPECT_EQ(opt->getType(), expected_srvid->getType() );
-         EXPECT_EQ(opt->len(), expected_srvid->len() );
-         EXPECT_TRUE(opt->getData() == expected_srvid->getData());
-     }
-     /// @brief Checks if server response (OFFER, ACK, NAK) includes proper client-id
-     /// @param rsp response packet to be validated
-     /// @param expected_clientid expected value of client-id
-     void checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid) {
-         // check that server included our own client-id
-         OptionPtr opt = rsp->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
-         ASSERT_TRUE(opt);
-         EXPECT_EQ(expected_clientid->getType(), opt->getType());
-         EXPECT_EQ(expected_clientid->len(), opt->len());
-         EXPECT_TRUE(expected_clientid->getData() == opt->getData());
-     }
-     /// @brief Tests if Discover or Request message is processed correctly
-     ///
-     /// @param msg_type DHCPDISCOVER or DHCPREQUEST
-     void testDiscoverRequest(const uint8_t msg_type) {
-         // Create an instance of the tested class.
-         boost::scoped_ptr<NakedDhcpv4Srv> srv(new NakedDhcpv4Srv(0));
-         // Initialize the source HW address.
-         vector<uint8_t> mac(6);
-         for (int i = 0; i < 6; ++i) {
-             mac[i] = i * 10;
-         }
-         // Initialized the destination HW address.
-         vector<uint8_t> dst_mac(6);
-         for (int i = 0; i < 6; ++i) {
-             dst_mac[i] = i * 20;
-         }
-         // Create a DHCP message. It will be used to simulate the
-         // incoming message.
-         boost::shared_ptr<Pkt4> req(new Pkt4(msg_type, 1234));
-         // Create a response message. It will hold a reponse packet.
-         // Initially, set it to NULL.
-         boost::shared_ptr<Pkt4> rsp;
-         // Set the name of the interface on which packet is received.
-         req->setIface("eth0");
-         // Set the interface index. It is just a dummy value and will
-         // not be interpreted.
-         req->setIndex(17);
-         // Set the target HW address. This value is normally used to
-         // construct the data link layer header.
-         req->setRemoteHWAddr(1, 6, dst_mac);
-         // Set the HW address. This value is set on DHCP level (in chaddr).
-         req->setHWAddr(1, 6, mac);
-         // Set local HW address. It is used to construct the data link layer
-         // header.
-         req->setLocalHWAddr(1, 6, mac);
-         // Set target IP address.
-         req->setRemoteAddr(IOAddress("192.0.2.55"));
-         // Set relay address.
-         req->setGiaddr(IOAddress("192.0.2.10"));
-         // We are going to test that certain options are returned
-         // in the response message when requested using 'Parameter
-         // Request List' option. Let's configure those options that
-         // are returned when requested.
-         configureRequestedOptions();
-         if (msg_type == DHCPDISCOVER) {
-             ASSERT_NO_THROW(
-                 rsp = srv->processDiscover(req);
-             );
-             // Should return OFFER
-             ASSERT_TRUE(rsp);
-             EXPECT_EQ(DHCPOFFER, rsp->getType());
-         } else {
-             ASSERT_NO_THROW(
-                 rsp = srv->processRequest(req);
-             );
-             // Should return ACK
-             ASSERT_TRUE(rsp);
-             EXPECT_EQ(DHCPACK, rsp->getType());
-         }
-         messageCheck(req, rsp);
-         // We did not request any options so these should not be present
-         // in the RSP.
-         EXPECT_FALSE(rsp->getOption(DHO_LOG_SERVERS));
-         EXPECT_FALSE(rsp->getOption(DHO_COOKIE_SERVERS));
-         EXPECT_FALSE(rsp->getOption(DHO_LPR_SERVERS));
-         // Repeat the test but request some options.
-         // Add 'Parameter Request List' option.
-         addPrlOption(req);
-         if (msg_type == DHCPDISCOVER) {
-             ASSERT_NO_THROW(
-                 rsp = srv->processDiscover(req);
-             );
-             // Should return non-NULL packet.
-             ASSERT_TRUE(rsp);
-             EXPECT_EQ(DHCPOFFER, rsp->getType());
-         } else {
-             ASSERT_NO_THROW(
-                 rsp = srv->processRequest(req);
-             );
-             // Should return non-NULL packet.
-             ASSERT_TRUE(rsp);
-             EXPECT_EQ(DHCPACK, rsp->getType());
-         }
-         // Check that the requested options are returned.
-         optionsCheck(rsp);
-     }
-     /// @brief This function cleans up after the test.
-     virtual void TearDown() {
-         CfgMgr::instance().deleteSubnets4();
-         // Let's clean up if there is such a file.
-         unlink(SRVID_FILE);
-         // Close all open sockets.
-         IfaceMgr::instance().closeSockets();
-         // Some unit tests override the default packet filtering class, used
-         // by the IfaceMgr. The dummy class, called PktFilterTest, reports the
-         // capability to directly respond to the clients without IP address
-         // assigned. This capability is not supported by the default packet
-         // filtering class: PktFilterInet. Therefore setting the dummy class
-         // allows to test scenarios, when server responds to the broadcast address
-         // on client's request, despite having support for direct response.
-         // The following call restores the use of original packet filtering class
-         // after the test.
-         try {
-             IfaceMgr::instance().setPacketFilter(PktFilterPtr(new PktFilterInet()));
-         } catch (const Exception& ex) {
-             FAIL() << "Failed to restore the default (PktFilterInet) packet filtering"
-                    << " class after the test. Exception has been caught: "
-                    << ex.what();
-         }
-     }
-     /// @brief A subnet used in most tests
-     Subnet4Ptr subnet_;
-     /// @brief A pool used in most tests
-     Pool4Ptr pool_;
-     /// @brief A client-id used in most tests
-     ClientIdPtr client_id_;
-     int rcode_;
-     ConstElementPtr comment_;
-     // Name of a valid network interface
-     string valid_iface_;
- };
+ namespace isc {
+ namespace dhcp {
+ namespace test {
  
  // Sanity check. Verifies that both Dhcpv4Srv and its derived
  // class NakedDhcpv4Srv can be instantiated and destroyed.
index 0000000000000000000000000000000000000000,07fbeb6dd60f0a7319b7a40fa7edae97bec45486..2e39a8be4c2f674be571fcf8a546801990b866dc
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,316 +1,317 @@@
+ // Copyright (C) 2013  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   dhcp4_test_utils.h
+ ///
+ /// @brief  This file contains utility classes used for DHCPv4 server testing
+ #ifndef DHCP4_TEST_UTILS_H
+ #define DHCP4_TEST_UTILS_H
+ #include <gtest/gtest.h>
+ #include <dhcp/pkt4.h>
+ #include <dhcp/pkt_filter.h>
+ #include <dhcp/pkt_filter_inet.h>
+ #include <dhcpsrv/subnet.h>
+ #include <dhcpsrv/lease.h>
+ #include <dhcp4/dhcp4_srv.h>
+ #include <asiolink/io_address.h>
+ #include <config/ccsession.h>
+ #include <list>
+ namespace isc {
+ namespace dhcp {
+ namespace test {
+ /// @brief Dummy Packet Filtering class.
+ ///
+ /// This class reports capability to respond directly to the client which
+ /// doesn't have address configured yet.
+ ///
+ /// All packet and socket handling functions do nothing because they are not
+ /// used in unit tests.
+ class PktFilterTest : public PktFilter {
+ public:
+     /// @brief Reports 'direct response' capability.
+     ///
+     /// @return always true.
+     virtual bool isDirectResponseSupported() const {
+         return (true);
+     }
+     /// Does nothing.
+     virtual int openSocket(const Iface&, const isc::asiolink::IOAddress&,
+                            const uint16_t, const bool, const bool) {
+         return (0);
+     }
+     /// Does nothing.
+     virtual Pkt4Ptr receive(const Iface&, const SocketInfo&) {
+         return Pkt4Ptr();
+     }
+     /// Does nothing.
+     virtual int send(const Iface&, uint16_t, const Pkt4Ptr&) {
+         return (0);
+     }
+ };
+ class Dhcpv4SrvTest : public ::testing::Test {
+ public:
+     /// @brief Constructor
+     ///
+     /// Initializes common objects used in many tests.
+     /// Also sets up initial configuration in CfgMgr.
+     Dhcpv4SrvTest();
+     /// @brief destructor
+     virtual ~Dhcpv4SrvTest() {
+     }
+     /// @brief Add 'Parameter Request List' option to the packet.
+     ///
+     /// This function adds PRL option comprising the following option codes:
+     /// - 5 - Name Server
+     /// - 15 - Domain Name
+     /// - 7 - Log Server
+     /// - 8 - Quotes Server
+     /// - 9 - LPR Server
+     ///
+     /// @param pkt packet to add PRL option to.
+     void addPrlOption(Pkt4Ptr& pkt);
+     /// @brief Configures options being requested in the PRL option.
+     ///
+     /// The lpr-servers option is NOT configured here although it is
+     /// added to the 'Parameter Request List' option in the
+     /// \ref addPrlOption. When requested option is not configured
+     /// the server should not return it in its response. The goal
+     /// of not configuring the requested option is to verify that
+     /// the server will not return it.
+     void configureRequestedOptions();
+     /// @brief checks that the response matches request
+     /// @param q query (client's message)
+     /// @param a answer (server's message)
+     void messageCheck(const Pkt4Ptr& q, const Pkt4Ptr& a);
+     /// @brief Check that requested options are present.
+     ///
+     /// @param pkt packet to be checked.
+     void optionsCheck(const Pkt4Ptr& pkt);
+     /// @brief generates client-id option
+     ///
+     /// Generate client-id option of specified length
+     /// Ids with different lengths are sufficent to generate
+     /// unique ids. If more fine grained control is required,
+     /// tests generate client-ids on their own.
+     /// Sets client_id_ field.
+     /// @param size size of the client-id to be generated
+     OptionPtr generateClientId(size_t size = 4);
+     /// @brief generate hardware address
+     ///
+     /// @param size size of the generated MAC address
+     /// @param pointer to Hardware Address object
+     HWAddrPtr generateHWAddr(size_t size = 6);
+     /// Check that address was returned from proper range, that its lease
+     /// lifetime is correct, that T1 and T2 are returned properly
+     /// @param rsp response to be checked
+     /// @param subnet subnet that should be used to verify assigned address
+     ///        and options
+     /// @param t1_mandatory is T1 mandatory?
+     /// @param t2_mandatory is T2 mandatory?
+     void checkAddressParams(const Pkt4Ptr& rsp, const SubnetPtr subnet,
+                             bool t1_mandatory = false,
+                             bool t2_mandatory = false);
+     /// @brief Basic checks for generated response (message type and trans-id).
+     ///
+     /// @param rsp response packet to be validated
+     /// @param expected_message_type expected message type
+     /// @param expected_transid expected transaction-id
+     void checkResponse(const Pkt4Ptr& rsp, uint8_t expected_message_type,
+                        uint32_t expected_transid);
+     /// @brief Checks if the lease sent to client is present in the database
+     ///
+     /// @param rsp response packet to be validated
+     /// @param client_id expected client-identifier (or NULL)
+     /// @param HWAddr expected hardware address (not used now)
+     /// @param expected_addr expected address
+     Lease4Ptr checkLease(const Pkt4Ptr& rsp, const OptionPtr& client_id,
+                          const HWAddrPtr&,
+                          const isc::asiolink::IOAddress& expected_addr);
+     /// @brief Checks if server response (OFFER, ACK, NAK) includes proper server-id
+     /// @param rsp response packet to be validated
+     /// @param expected_srvid expected value of server-id
+     void checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid);
+     /// @brief Checks if server response (OFFER, ACK, NAK) includes proper client-id
+     /// @param rsp response packet to be validated
+     /// @param expected_clientid expected value of client-id
+     void checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid);
+     /// @brief sets default fields in a captured packet
+     ///
+     /// Sets UDP ports, addresses and interface.
+     ///
+     /// @param pkt packet to have default fields set
+     void captureSetDefaultFields(const Pkt4Ptr& pkt);
+     /// @brief returns captured DISCOVER that went through a relay
+     ///
+     /// See method code for a detailed explanation.
+     ///
+     /// @return relayed DISCOVER
+     Pkt4Ptr captureRelayedDiscover();
+     /// @brief generates a DHCPv4 packet based on provided hex string
+     ///
+     /// @return created packet
+     Pkt4Ptr packetFromCapture(const std::string& hex_string);
+     /// @brief Tests if Discover or Request message is processed correctly
+     ///
+     /// @param msg_type DHCPDISCOVER or DHCPREQUEST
+     void testDiscoverRequest(const uint8_t msg_type);
+     /// @brief This function cleans up after the test.
+     virtual void TearDown();
+     /// @brief A subnet used in most tests
+     Subnet4Ptr subnet_;
+     /// @brief A pool used in most tests
+     Pool4Ptr pool_;
+     /// @brief A client-id used in most tests
+     ClientIdPtr client_id_;
+     int rcode_;
+     isc::data::ConstElementPtr comment_;
+     // Name of a valid network interface
+     std::string valid_iface_;
+ };
+ /// @brief "Naked" DHCPv4 server, exposes internal fields
+ class NakedDhcpv4Srv: public Dhcpv4Srv {
+ public:
+     /// @brief Constructor.
+     ///
+     /// This constructor disables default modes of operation used by the
+     /// Dhcpv4Srv class:
+     /// - Send/receive broadcast messages through sockets on interfaces
+     /// which support broadcast traffic.
+     /// - Direct DHCPv4 traffic - communication with clients which do not
+     /// have IP address assigned yet.
+     ///
+     /// Enabling these modes requires root privilges so they must be
+     /// disabled for unit testing.
+     ///
+     /// Note, that disabling broadcast options on sockets does not impact
+     /// the operation of these tests because they use local loopback
+     /// interface which doesn't have broadcast capability anyway. It rather
+     /// prevents setting broadcast options on other (broadcast capable)
+     /// sockets which are opened on other interfaces in Dhcpv4Srv constructor.
+     ///
+     /// The Direct DHCPv4 Traffic capability can be disabled here because
+     /// it is tested with PktFilterLPFTest unittest. The tests which belong
+     /// to PktFilterLPFTest can be enabled on demand when root privileges can
+     /// be guaranteed.
+     ///
+     /// @param port port number to listen on; the default value 0 indicates
+     /// that sockets should not be opened.
+     NakedDhcpv4Srv(uint16_t port = 0)
+         : Dhcpv4Srv(port, "type=memfile", false, false) {
+     }
+     /// @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 Pkt4Ptr receivePacket(int /*timeout*/) {
+         // If there is anything prepared as fake incoming traffic, use it
+         if (!fake_received_.empty()) {
+             Pkt4Ptr pkt = fake_received_.front();
+             fake_received_.pop_front();
+             return (pkt);
+         }
+         // If not, just trigger shutdown and return immediately
+         shutdown();
+         return (Pkt4Ptr());
+     }
+     /// @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 Pkt4Ptr& pkt) {
+         fake_sent_.push_back(pkt);
+     }
+     /// @brief adds a packet to fake receive queue
+     ///
+     /// See fake_received_ field for description
+     void fakeReceive(const Pkt4Ptr& pkt) {
+         fake_received_.push_back(pkt);
+     }
+     virtual ~NakedDhcpv4Srv() {
+     }
+     /// @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 NakedDhcpv4Srv::receivePacket() methods.
+     std::list<Pkt4Ptr> fake_received_;
+     std::list<Pkt4Ptr> fake_sent_;
+     using Dhcpv4Srv::adjustRemoteAddr;
+     using Dhcpv4Srv::processDiscover;
+     using Dhcpv4Srv::processRequest;
+     using Dhcpv4Srv::processRelease;
+     using Dhcpv4Srv::processDecline;
+     using Dhcpv4Srv::processInform;
+     using Dhcpv4Srv::getServerID;
+     using Dhcpv4Srv::loadServerID;
+     using Dhcpv4Srv::generateServerID;
+     using Dhcpv4Srv::writeServerID;
+     using Dhcpv4Srv::sanityCheck;
+     using Dhcpv4Srv::srvidToString;
++    using Dhcpv4Srv::unpackOptions;
+ };
+ }; // end of isc::dhcp::test namespace
+ }; // end of isc::dhcp namespace
+ }; // end of isc namespace
+ #endif // DHCP4_TEST_UTILS_H