Parcourir la source

[3688] Pass DHCPv4Exchange objects instead of query/resp in the dhcpv4_srv.

Marcin Siodelski il y a 10 ans
Parent
commit
587363708b

Fichier diff supprimé car celui-ci est trop grand
+ 215 - 281
src/bin/dhcp4/dhcp4_srv.cc


+ 152 - 60
src/bin/dhcp4/dhcp4_srv.h

@@ -43,6 +43,104 @@ public:
         isc::Exception(file, line, what) { };
 };
 
+/// @brief DHCPv4 message exchange.
+///
+/// This class represents the DHCPv4 message exchange. The message exchange
+/// consists of the single client message, server response to this message
+/// and the mechanisms to generate the server's response. The server creates
+/// the instance of the @c DHCPv4Exchange for each inbound message that it
+/// accepts for processing.
+///
+/// The use of the @c DHCPv4Exchange object as a central repository of
+/// information about the message exchange simplifies the API of the
+/// @c Dhcpv4Srv class.
+///
+/// Another benefit of using this class is that different methods of the
+/// @c Dhcpv4Srv may share information. For example, the constructor of this
+/// class selects the subnet and multiple methods of @c Dhcpv4Srv use this
+/// subnet, without the need to select it again.
+///
+/// @todo This is the initial version of this class. In the future a lot of
+/// code from the @c Dhcpv4Srv class will be migrated here.
+class DHCPv4Exchange {
+public:
+    /// @brief Constructor.
+    ///
+    /// The constructor selects the subnet for the query and checks for the
+    /// static host reservations for the client which has sent the message.
+    /// The information about the reservations is stored in the
+    /// @c AllocEngine::ClientContext4 object, which can be obtained by
+    /// calling the @c getContext.
+    ///
+    /// @param alloc_engine Pointer to the instance of the Allocation Engine
+    /// used by the server.
+    /// @param query Pointer to the client message.
+    DHCPv4Exchange(const AllocEnginePtr& alloc_engine, const Pkt4Ptr& query);
+
+    /// @brief Initializes the instance of the response message.
+    ///
+    /// The type of the response depends on the type of the query message.
+    /// For the DHCPDISCOVER the DHCPOFFER is created. For the DHCPREQUEST
+    /// and DHCPINFORM the DHCPACK is created. For the DHCPRELEASE the
+    /// response is not initialized.
+    void initResponse();
+
+    /// @brief Selects the subnet for the message processing.
+    ///
+    /// The pointer to the selected subnet is stored in the @c ClientContext4
+    /// structure.
+    void selectSubnet();
+
+    /// @brief Selects the subnet for the message processing.
+    ///
+    /// @todo This variant of the @c selectSubnet method is static and public so
+    /// as it may be invoked by the @c Dhcpv4Srv object. This is temporary solution
+    /// and the function will go away once the server code fully supports the use
+    /// of this class and it obtains the subnet from the context returned by the
+    /// @c getContext method.
+    ///
+    /// @param query Pointer to the client's message.
+    /// @return Pointer to the selected subnet or NULL if no suitable subnet
+    /// has been found.
+    static Subnet4Ptr selectSubnet(const Pkt4Ptr& query);
+
+    /// @brief Returns the pointer to the query from the client.
+    Pkt4Ptr getQuery() const {
+        return (query_);
+    }
+
+    /// @brief Returns the pointer to the server's response.
+    ///
+    /// The returned pointer is NULL if the query type is DHCPRELEASE or DHCPDECLINE.
+    Pkt4Ptr getResponse() const {
+        return (resp_);
+    }
+
+    /// @brief Removes the response message by resetting the pointer to NULL.
+    void deleteResponse() {
+        resp_.reset();
+    }
+
+    /// @brief Returns the copy of the context for the Allocation engine.
+    AllocEngine::ClientContext4Ptr getContext() const {
+        return (context_);
+    }
+
+private:
+    /// @brief Pointer to the allocation engine used by the server.
+    AllocEnginePtr alloc_engine_;
+    /// @brief Pointer to the DHCPv4 message sent by the client.
+    Pkt4Ptr query_;
+    /// @brief Pointer to the DHCPv4 message to be sent to the client.
+    Pkt4Ptr resp_;
+    /// @brief Context for use with allocation engine.
+    AllocEngine::ClientContext4Ptr context_;
+};
+
+/// @brief Type representing the pointer to the @c DHCPv4Exchange.
+typedef boost::shared_ptr<DHCPv4Exchange> DHCPv4ExchangePtr;
+
+
 /// @brief DHCPv4 server service.
 ///
 /// This singleton class represents DHCPv4 server. It contains all
@@ -269,15 +367,15 @@ protected:
     bool acceptServerId(const Pkt4Ptr& pkt) const;
     //@}
 
-    /// @brief verifies if specified packet meets RFC requirements
+    /// @brief Verifies if specified packet meets RFC requirements
     ///
     /// Checks if mandatory option is really there, that forbidden option
     /// is not there, and that client-id or server-id appears only once.
     ///
-    /// @param pkt packet to be checked
+    /// @param ex DHCPv4 exchange holding the client's message to be checked.
     /// @param serverid expectation regarding server-id option
     /// @throw RFCViolation if any issues are detected
-    static void sanityCheck(const Pkt4Ptr& pkt, RequirementLevel serverid);
+    static void sanityCheck(const DHCPv4Exchange& ex, RequirementLevel serverid);
 
     /// @brief Processes incoming DISCOVER and returns response.
     ///
@@ -327,18 +425,18 @@ protected:
     /// Some fields are copied from client's message into server's response,
     /// e.g. client HW address, number of hops, transaction-id etc.
     ///
-    /// @param question any message sent by client
-    /// @param answer any message server is going to send as response
-    void copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer);
+    /// @param ex The exchange holding both the client's message and the
+    /// server's response.
+    void copyDefaultFields(DHCPv4Exchange& ex);
 
     /// @brief Appends options requested by client.
     ///
     /// This method assigns options that were requested by client
     /// (sent in PRL) or are enforced by server.
     ///
-    /// @param question DISCOVER or REQUEST message from a client.
-    /// @param msg outgoing message (options will be added here)
-    void appendRequestedOptions(const Pkt4Ptr& question, Pkt4Ptr& msg);
+    /// @param ex The exchange holding both the client's message and the
+    /// server's response.
+    void appendRequestedOptions(DHCPv4Exchange& ex);
 
     /// @brief Appends requested vendor options as requested by client.
     ///
@@ -348,9 +446,9 @@ protected:
     /// options, each with unique vendor-id). Vendor options are requested
     /// using separate options within their respective vendor-option spaces.
     ///
-    /// @param question DISCOVER or REQUEST message from a client.
-    /// @param answer outgoing message (options will be added here)
-    void appendRequestedVendorOptions(const Pkt4Ptr& question, Pkt4Ptr& answer);
+    /// @param ex The exchange holding both the client's message and the
+    /// server's response.
+    void appendRequestedVendorOptions(DHCPv4Exchange& ex);
 
     /// @brief Assigns a lease and appends corresponding options
     ///
@@ -358,28 +456,27 @@ protected:
     /// client and assigning it. Options corresponding to the lease
     /// are added to specific message.
     ///
-    /// @param question DISCOVER or REQUEST message from client
-
-    /// @param answer OFFER or ACK/NAK message (lease options will be
-    /// added here)
-    /// 
-    /// This method may reset the @c answer shared pointer to indicate
-    /// that the response should not be sent to the client. The caller
-    /// must check if the @c answer is null after calling this method.
-    void assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer);
+    /// This method may reset the pointer to the response in the @c ex object
+    /// to indicate that the response should not be sent to the client.
+    /// The caller must check if the response is is null after calling
+    /// this method.
+    ///
+    /// The response type in the @c ex object may be set to DHCPACK or DHCPNAK.
+    ///
+    /// @param ex DHCPv4 exchange holding the client's message to be checked.
+    void assignLease(DHCPv4Exchange& ex);
 
     /// @brief Append basic options if they are not present.
     ///
     /// This function adds the following basic options if they
-    /// are not yet added to the message:
+    /// are not yet added to the response message:
     /// - Subnet Mask,
     /// - Router,
     /// - Name Server,
     /// - Domain Name.
     ///
-    /// @param question DISCOVER or REQUEST message from a client.
-    /// @param msg the message to add options to.
-    void appendBasicOptions(const Pkt4Ptr& question, Pkt4Ptr& msg);
+    /// @param ex DHCPv4 exchange holding the client's message to be checked.
+    void appendBasicOptions(DHCPv4Exchange& ex);
 
     /// @brief Processes Client FQDN and Hostname Options sent by a client.
     ///
@@ -416,9 +513,9 @@ protected:
     /// This function does not throw. It simply logs the debug message if the
     /// processing of the FQDN or Hostname failed.
     ///
-    /// @param query A DISCOVER or REQUEST message from a client.
-    /// @param [out] answer A response message to be sent to a client.
-    void processClientName(const Pkt4Ptr& query, Pkt4Ptr& answer);
+    /// @param ex The exchange holding both the client's message and the
+    /// server's response.
+    void processClientName(DHCPv4Exchange& ex);
 
     /// @brief this is a prefix added to the contend of vendor-class option
     ///
@@ -437,10 +534,9 @@ private:
     /// the FQDN option to be sent back to the client in the server's
     /// response.
     ///
-    /// @param fqdn An DHCPv4 Client FQDN %Option sent by a client.
-    /// @param [out] answer A response message to be sent to a client.
-    void processClientFqdnOption(const Option4ClientFqdnPtr& fqdn,
-                                 Pkt4Ptr& answer);
+    /// @param ex The exchange holding both the client's message and the
+    /// server's response.
+    void processClientFqdnOption(DHCPv4Exchange& ex);
 
     /// @brief Process Hostname %Option sent by a client.
     ///
@@ -450,11 +546,9 @@ private:
     /// prepare the Hostname option to be sent back to the client in the
     /// server's response.
     ///
-    /// @param opt_hostname An @c OptionString object encapsulating the Hostname
-    /// %Option.
-    /// @param [out] answer A response message to be sent to a client.
-    void processHostnameOption(const OptionStringPtr& opt_hostname,
-                               Pkt4Ptr& answer);
+    /// @param ex The exchange holding both the client's message and the
+    /// server's response.
+    void processHostnameOption(DHCPv4Exchange& ex);
 
 protected:
 
@@ -499,16 +593,13 @@ protected:
     /// @param reply server's response (ACK or NAK)
     void renewLease(const Pkt4Ptr& renew, Pkt4Ptr& reply);
 
-    /// @brief Appends default options to a message
-    ///
-    /// Currently it is only a Message Type option. This function does not add
-    /// the Server Identifier option as this option must be added using
-    /// @c Dhcpv4Srv::appendServerID.
+    /// @brief Appends default options to a message.
     ///
+    /// This method is currently no-op.
     ///
-    /// @param msg message object (options will be added to it)
-    /// @param msg_type specifies message type
-    void appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type);
+    /// @param ex The exchange holding both the client's message and the
+    /// server's response.
+    void appendDefaultOptions(DHCPv4Exchange& ex);
 
     /// @brief Adds server identifier option to the server's response.
     ///
@@ -527,9 +618,9 @@ protected:
     /// @note This method is static because it is not dependent on the class
     /// state.
     ///
-    /// @param [out] response DHCPv4 message to which the server identifier
-    /// option should be added.
-    static void appendServerID(const Pkt4Ptr& response);
+    /// @param ex The exchange holding both the client's message and the
+    /// server's response.
+    static void appendServerID(DHCPv4Exchange& ex);
 
     /// @brief Set IP/UDP and interface parameters for the DHCPv4 response.
     ///
@@ -561,7 +652,10 @@ protected:
     ///
     /// @note This method is static because it is not dependent on the class
     /// state.
-    static void adjustIfaceData(const Pkt4Ptr& query, const Pkt4Ptr& response);
+    ///
+    /// @param ex The exchange holding both the client's message and the
+    /// server's response.
+    static void adjustIfaceData(DHCPv4Exchange& ex);
 
     /// @brief Sets remote addresses for outgoing packet.
     ///
@@ -579,11 +673,9 @@ protected:
     /// @note This method is static because it is not dependent on the class
     /// state.
     ///
-    /// @param question instance of a packet received by a server.
-    /// @param [out] response response packet which addresses are to be
-    /// adjusted.
-    static void adjustRemoteAddr(const Pkt4Ptr& question,
-                                 const Pkt4Ptr& response);
+    /// @param ex The exchange holding both the client's message and the
+    /// server's response.
+    static void adjustRemoteAddr(DHCPv4Exchange& ex);
 
     /// @brief converts server-id to text
     /// Converts content of server-id option to a text representation, e.g.
@@ -668,6 +760,12 @@ protected:
     /// @return true if successful, false otherwise (will prevent sending response)
     bool classSpecificProcessing(const Pkt4Ptr& query, const Pkt4Ptr& rsp);
 
+    /// @brief Allocation Engine.
+    /// Pointer to the allocation engine that we are currently using
+    /// It must be a pointer, because we will support changing engines
+    /// during normal operation (e.g. to use different allocators)
+    boost::shared_ptr<AllocEngine> alloc_engine_;
+
 private:
 
     /// @brief Constructs netmask option based on subnet4
@@ -685,12 +783,6 @@ private:
     /// @param errmsg An error message containing a cause of the failure.
     static void ifaceMgrSocket4ErrorHandler(const std::string& errmsg);
 
-    /// @brief Allocation Engine.
-    /// Pointer to the allocation engine that we are currently using
-    /// It must be a pointer, because we will support changing engines
-    /// during normal operation (e.g. to use different allocators)
-    boost::shared_ptr<AllocEngine> alloc_engine_;
-
     uint16_t port_;  ///< UDP port number on which server listens.
     bool use_bcast_; ///< Should broadcast be enabled on sockets (if true).
 

+ 44 - 28
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc

@@ -86,10 +86,10 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelay) {
     req->setIface("eth1");
     req->setIndex(1);
 
-    // Create a response packet. Assume that the new lease have
-    // been created and new address allocated. This address is
-    // stored in yiaddr field.
-    boost::shared_ptr<Pkt4> resp(new Pkt4(DHCPOFFER, 1234));
+    // Create the exchange using the req.
+    DHCPv4Exchange ex = createExchange(req);
+
+    Pkt4Ptr resp = ex.getResponse();
     resp->setYiaddr(IOAddress("192.0.1.100"));
     // Clear the remote address.
     resp->setRemoteAddr(IOAddress("0.0.0.0"));
@@ -97,7 +97,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelay) {
     resp->setHops(req->getHops());
 
     // This function never throws.
-    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
+    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
 
     // Now the destination address should be relay's address.
     EXPECT_EQ("192.0.1.1", resp->getRemoteAddr().toText());
@@ -123,7 +123,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelay) {
     // Clear remote address.
     resp->setRemoteAddr(IOAddress("0.0.0.0"));
 
-    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
+    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
 
     // Response should be sent back to the relay address.
     EXPECT_EQ("192.0.1.50", resp->getRemoteAddr().toText());
@@ -163,8 +163,10 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRenew) {
     req->setIface("eth1");
     req->setIndex(1);
 
-    // Create a response.
-    boost::shared_ptr<Pkt4> resp(new Pkt4(DHCPOFFER, 1234));
+    // Create the exchange using the req.
+    DHCPv4Exchange ex = createExchange(req);
+    Pkt4Ptr resp = ex.getResponse();
+
     // Let's extend the lease for the client in such a way that
     // it will actually get different address. The response
     // should not be sent to this address but rather to ciaddr
@@ -175,7 +177,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRenew) {
     // Copy hops value from the query.
     resp->setHops(req->getHops());
 
-    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
+    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
 
     // Check that server responds to ciaddr
     EXPECT_EQ("192.0.1.15", resp->getRemoteAddr().toText());
@@ -225,8 +227,9 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataSelect) {
     req->setIface("eth1");
     req->setIndex(1);
 
-    // Create a response.
-    boost::shared_ptr<Pkt4> resp(new Pkt4(DHCPOFFER, 1234));
+    // Create the exchange using the req.
+    DHCPv4Exchange ex = createExchange(req);
+    Pkt4Ptr resp = ex.getResponse();
     // Assign some new address for this client.
     resp->setYiaddr(IOAddress("192.0.1.13"));
     // Clear the remote address.
@@ -247,7 +250,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataSelect) {
     // are zero and client has just got new lease, the assigned address is
     // carried in yiaddr. In order to send this address to the client,
     // server must broadcast its response.
-    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
+    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
 
     // Check that the response is sent to broadcast address as the
     // server doesn't have capability to respond directly.
@@ -276,7 +279,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataSelect) {
 
     // Now we expect that the server will send its response to the
     // address assigned for the client.
-    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
+    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
 
     EXPECT_EQ("192.0.1.13", resp->getRemoteAddr().toText());
 }
@@ -308,15 +311,17 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataBroadcast) {
     // Let's set the broadcast flag.
     req->setFlags(Pkt4::FLAG_BROADCAST_MASK);
 
-    // Create a response.
-    boost::shared_ptr<Pkt4> resp(new Pkt4(DHCPOFFER, 1234));
+    // Create the exchange using the req.
+    DHCPv4Exchange ex = createExchange(req);
+    Pkt4Ptr resp = ex.getResponse();
+
     // Assign some new address for this client.
     resp->setYiaddr(IOAddress("192.0.1.13"));
 
     // Clear the remote address.
     resp->setRemoteAddr(IOAddress("0.0.0.0"));
 
-    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
+    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
 
     // Server must respond to broadcast address when client desired that
     // by setting the broadcast flag in its request.
@@ -340,6 +345,9 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataBroadcast) {
 // This test verifies that exception is thrown of the invalid combination
 // of giaddr and hops is specified in a client's message.
 TEST_F(Dhcpv4SrvTest, adjustIfaceDataInvalid) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
     boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
 
     // The hops and giaddr values are used to determine if the client's
@@ -360,27 +368,32 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataInvalid) {
     req->setIface("eth0");
     req->setIndex(1);
 
-    // Create a response.
-    boost::shared_ptr<Pkt4> resp(new Pkt4(DHCPOFFER, 1234));
+    // Create the exchange using the req.
+    DHCPv4Exchange ex = createExchange(req);
+    Pkt4Ptr resp = ex.getResponse();
+
     // Assign some new address for this client.
     resp->setYiaddr(IOAddress("192.0.2.13"));
 
     // Clear the remote address.
     resp->setRemoteAddr(IOAddress("0.0.0.0"));
 
-    EXPECT_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp), isc::BadValue);
+    EXPECT_THROW(NakedDhcpv4Srv::adjustIfaceData(ex), isc::BadValue);
 }
 
 // This test verifies that the server identifier option is appended to
 // a specified DHCPv4 message and the server identifier is correct.
 TEST_F(Dhcpv4SrvTest, appendServerID) {
-    Pkt4Ptr response(new Pkt4(DHCPDISCOVER, 1234));
+    Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 1234));
+    DHCPv4Exchange ex = createExchange(query);
+    Pkt4Ptr response = ex.getResponse();
+
     // Set a local address. It is required by the function under test
     // to create the Server Identifier option.
     response->setLocalAddr(IOAddress("192.0.3.1"));
 
     // Append the Server Identifier.
-    ASSERT_NO_THROW(NakedDhcpv4Srv::appendServerID(response));
+    ASSERT_NO_THROW(NakedDhcpv4Srv::appendServerID(ex));
 
     // Make sure that the option has been added.
     OptionPtr opt = response->getOption(DHO_DHCP_SERVER_IDENTIFIER);
@@ -783,10 +796,10 @@ TEST_F(Dhcpv4SrvTest, requestEchoClientId) {
     checkClientId(ack, clientid);
 
     CfgMgr::instance().echoClientId(false);
-    ack = srv.processDiscover(dis);
+    ack = srv.processRequest(dis);
 
     // Check if we get response at all
-    checkResponse(ack, DHCPOFFER, 1234);
+    checkResponse(ack, DHCPACK, 1234);
     checkClientId(ack, clientid);
 }
 
@@ -947,25 +960,28 @@ TEST_F(Dhcpv4SrvTest, sanityCheck) {
     pkt->setHWAddr(generateHWAddr(6));
 
     // Server-id is optional for information-request, so
-    EXPECT_NO_THROW(NakedDhcpv4Srv::sanityCheck(pkt, Dhcpv4Srv::OPTIONAL));
+    EXPECT_NO_THROW(NakedDhcpv4Srv::sanityCheck(createExchange(pkt),
+                                                Dhcpv4Srv::OPTIONAL));
 
     // Empty packet, no server-id
-    EXPECT_THROW(NakedDhcpv4Srv::sanityCheck(pkt, Dhcpv4Srv::MANDATORY),
+    EXPECT_THROW(NakedDhcpv4Srv::sanityCheck(createExchange(pkt), Dhcpv4Srv::MANDATORY),
                  RFCViolation);
 
     pkt->addOption(srv->getServerID());
 
     // Server-id is mandatory and present = no exception
-    EXPECT_NO_THROW(NakedDhcpv4Srv::sanityCheck(pkt, Dhcpv4Srv::MANDATORY));
+    EXPECT_NO_THROW(NakedDhcpv4Srv::sanityCheck(createExchange(pkt),
+                                                Dhcpv4Srv::MANDATORY));
 
     // Server-id is forbidden, but present => exception
-    EXPECT_THROW(NakedDhcpv4Srv::sanityCheck(pkt, Dhcpv4Srv::FORBIDDEN),
+    EXPECT_THROW(NakedDhcpv4Srv::sanityCheck(createExchange(pkt), Dhcpv4Srv::FORBIDDEN),
                  RFCViolation);
 
     // There's no client-id and no HWADDR. Server needs something to
     // identify the client
     pkt->setHWAddr(generateHWAddr(0));
-    EXPECT_THROW(NakedDhcpv4Srv::sanityCheck(pkt, Dhcpv4Srv::MANDATORY),
+    EXPECT_THROW(NakedDhcpv4Srv::sanityCheck(createExchange(pkt),
+                                             Dhcpv4Srv::MANDATORY),
                  RFCViolation);
 }
 

+ 4 - 0
src/bin/dhcp4/tests/dhcp4_test_utils.cc

@@ -587,6 +587,10 @@ Dhcpv4SrvTest::configure(const std::string& config, NakedDhcpv4Srv& srv,
     }
  }
 
+DHCPv4Exchange
+Dhcpv4SrvTest::createExchange(const Pkt4Ptr& query) {
+    return (DHCPv4Exchange(srv_.alloc_engine_, query));
+}
 
 
 }; // end of isc::dhcp::test namespace

+ 4 - 0
src/bin/dhcp4/tests/dhcp4_test_utils.h

@@ -205,6 +205,7 @@ public:
     using Dhcpv4Srv::selectSubnet;
     using Dhcpv4Srv::VENDOR_CLASS_PREFIX;
     using Dhcpv4Srv::shutdown_;
+    using Dhcpv4Srv::alloc_engine_;
 };
 
 class Dhcpv4SrvTest : public ::testing::Test {
@@ -390,6 +391,9 @@ public:
     void configure(const std::string& config, NakedDhcpv4Srv& srv,
                    const bool commit = true);
 
+    /// @brief Create @c DHCPv4Exchange from client's query.
+    DHCPv4Exchange createExchange(const Pkt4Ptr& query);
+
     /// @brief This function cleans up after the test.
     virtual void TearDown();
 

+ 7 - 5
src/bin/dhcp4/tests/fqdn_unittest.cc

@@ -309,12 +309,13 @@ public:
             answer.reset(new Pkt4(DHCPACK, 1234));
 
         }
-        ASSERT_NO_THROW(srv_->processClientName(query, answer));
+        DHCPv4Exchange ex = createExchange(query);
+        ASSERT_NO_THROW(srv_->processClientName(ex));
 
-        Option4ClientFqdnPtr fqdn = getClientFqdnOption(answer);
+        Option4ClientFqdnPtr fqdn = getClientFqdnOption(ex.getResponse());
         ASSERT_TRUE(fqdn);
 
-        checkFqdnFlags(answer, exp_flags);
+        checkFqdnFlags(ex.getResponse(), exp_flags);
 
         EXPECT_EQ(exp_domain_name, fqdn->getDomainName());
         EXPECT_EQ(exp_domain_type, fqdn->getDomainNameType());
@@ -358,9 +359,10 @@ public:
             answer.reset(new Pkt4(DHCPACK, 1234));
 
         }
-        srv_->processClientName(query, answer);
+        DHCPv4Exchange ex = createExchange(query);
+        srv_->processClientName(ex);
 
-        OptionStringPtr hostname = getHostnameOption(answer);
+        OptionStringPtr hostname = getHostnameOption(ex.getResponse());
         return (hostname);
 
     }

+ 3 - 0
src/lib/dhcpsrv/alloc_engine.h

@@ -767,6 +767,9 @@ public:
 
     };
 
+    /// @brief Pointer to the @c ClientContext4.
+    typedef boost::shared_ptr<ClientContext4> ClientContext4Ptr;
+
     /// @brief Returns IPv4 lease.
     ///
     /// This method finds a lease for a client using the following algorithm: