Browse Source

[master] Merge branch 'trac3231'

Conflicts:
	src/lib/dhcp/iface_mgr.cc
Marcin Siodelski 11 years ago
parent
commit
c7a229f150

+ 7 - 12
doc/guide/bind10-guide.xml

@@ -4378,20 +4378,15 @@ Dhcp4/subnet4	[]	list	(default)
       <para>
         The DHCPv4 protocol uses a "server identifier" for clients to be able
         to discriminate between several servers present on the same link: this
-        value is an IPv4 address of the server. When started for the first time,
-        the DHCPv4 server will choose one of its IPv4 addresses as its server-id,
-        and store the chosen value to a file. That file will be read by the server
-        and the contained value used whenever the server is subsequently started.
+        value is an IPv4 address of the server. The server chooses the IPv4 address
+        of the interface on which the message from the client (or relay) has been
+        received. A single server instance will use multiple server identifiers
+        if it is receiving queries on multiple interfaces.
       </para>
       <para>
-        It is unlikely that this parameter should ever need to be changed.
-        However, if such a need arises, stop the server, edit the file and restart
-        the server. (The file is named b10-dhcp4-serverid and by default is
-        stored in the "var" subdirectory of the directory in which BIND 10 is installed.
-        This can be changed when BIND 10 is built by using "--localstatedir"
-        on the "configure" command line.)  The file is a text file that should
-        contain an IPv4 address. Spaces are ignored, and no extra characters are allowed
-        in this file.
+        Currently there is no mechanism to override the default server identifiers
+        by an administrator. In the future, the configuration mechanism will be used
+        to specify the custom server identifier.
       </para>
     </section>
 

+ 0 - 20
src/bin/dhcp4/dhcp4_messages.mes

@@ -289,26 +289,6 @@ both clones use the same client-id.
 % DHCP4_RESPONSE_DATA responding with packet type %1, data is <%2>
 A debug message listing the data returned to the client.
 
-% DHCP4_SERVERID_GENERATED server-id %1 has been generated and will be stored in %2
-This informational messages indicates that the server was not able to
-read its server identifier and has generated a new one. This server-id
-will be stored in a file and will be read (and used) whenever the server
-is restarted. This is normal behavior when the server is started for the
-first time. If this message is printed every time the server is started,
-please check that the server has sufficient permission to write its
-server-id file and that the file is not corrupt.
-
-% DHCP4_SERVERID_LOADED server-id %1 has been loaded from file %2
-This debug message indicates that the server loaded its server identifier.
-That value is sent in all server responses and clients use it to
-discriminate between servers. This is a part of normal startup or
-reconfiguration procedure.
-
-% DHCP4_SERVERID_WRITE_FAIL server was not able to write its ID to file %1
-This warning message indicates that server was not able to write its
-server identifier to a file. The most likely cause is is that the server
-does not have permissions to write the server id file.
-
 % DHCP4_SERVER_FAILED server failed: %1
 The IPv4 DHCP server has encountered a fatal error and is terminating.
 The reason for the failure is included in the message.

+ 77 - 154
src/bin/dhcp4/dhcp4_srv.cc

@@ -37,12 +37,10 @@
 #include <hooks/hooks_manager.h>
 #include <util/strutil.h>
 
-#include <boost/algorithm/string/erase.hpp>
 #include <boost/bind.hpp>
 #include <boost/foreach.hpp>
 
 #include <iomanip>
-#include <fstream>
 
 using namespace isc;
 using namespace isc::asiolink;
@@ -111,38 +109,28 @@ const bool FQDN_REPLACE_CLIENT_NAME = false;
 
 }
 
-/// @brief file name of a server-id file
-///
-/// Server must store its server identifier in persistent storage that must not
-/// change between restarts. This is name of the file that is created in dataDir
-/// (see isc::dhcp::CfgMgr::getDataDir()). It is a text file that uses
-/// regular IPv4 address, e.g. 192.0.2.1. Server will create it during
-/// first run and then use it afterwards.
-static const char* SERVER_ID_FILE = "b10-dhcp4-serverid";
-
-// These are hardcoded parameters. Currently this is a skeleton server that only
-// grants those options and a single, fixed, hardcoded lease.
-
 Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
                      const bool direct_response_desired)
-: serverid_(), shutdown_(true), alloc_engine_(), port_(port),
+: shutdown_(true), alloc_engine_(), port_(port),
     use_bcast_(use_bcast), hook_index_pkt4_receive_(-1),
     hook_index_subnet4_select_(-1), hook_index_pkt4_send_(-1) {
 
     LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
     try {
-        // First call to instance() will create IfaceMgr (it's a singleton)
-        // it may throw something if things go wrong.
-        // The 'true' value of the call to setMatchingPacketFilter imposes
-        // that IfaceMgr will try to use the mechanism to respond directly
-        // to the client which doesn't have address assigned. This capability
-        // may be lacking on some OSes, so there is no guarantee that server
-        // will be able to respond directly.
-        IfaceMgr::instance().setMatchingPacketFilter(direct_response_desired);
-
-        // Open sockets only if port is non-zero. Port 0 is used
-        // for non-socket related testing.
+        // Open sockets only if port is non-zero. Port 0 is used for testing
+        // purposes in two cases:
+        // - when non-socket related testing is performed
+        // - when the particular test supplies its own packet filtering class.
         if (port) {
+            // First call to instance() will create IfaceMgr (it's a singleton)
+            // it may throw something if things go wrong.
+            // The 'true' value of the call to setMatchingPacketFilter imposes
+            // that IfaceMgr will try to use the mechanism to respond directly
+            // to the client which doesn't have address assigned. This capability
+            // may be lacking on some OSes, so there is no guarantee that server
+            // will be able to respond directly.
+            IfaceMgr::instance().setMatchingPacketFilter(direct_response_desired);
+
             // Create error handler. This handler will be called every time
             // the socket opening operation fails. We use this handler to
             // log a warning.
@@ -151,24 +139,6 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
             IfaceMgr::instance().openSockets4(port_, use_bcast_, error_handler);
         }
 
-        string srvid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_ID_FILE);
-        if (loadServerID(srvid_file)) {
-            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_SERVERID_LOADED)
-                .arg(srvidToString(getServerID()))
-                .arg(srvid_file);
-        } else {
-            generateServerID();
-            LOG_INFO(dhcp4_logger, DHCP4_SERVERID_GENERATED)
-                .arg(srvidToString(getServerID()))
-                .arg(srvid_file);
-
-            if (!writeServerID(srvid_file)) {
-                LOG_WARN(dhcp4_logger, DHCP4_SERVERID_WRITE_FAIL)
-                    .arg(srvid_file);
-            }
-
-        }
-
         // Instantiate LeaseMgr
         LeaseMgrFactory::create(dbconfig);
         LOG_INFO(dhcp4_logger, DHCP4_DB_BACKEND_STARTED)
@@ -389,19 +359,6 @@ Dhcpv4Srv::run() {
             continue;
         }
 
-        adjustRemoteAddr(query, rsp);
-
-        if (!rsp->getHops()) {
-            rsp->setRemotePort(DHCP4_CLIENT_PORT);
-        } else {
-            rsp->setRemotePort(DHCP4_SERVER_PORT);
-        }
-
-        rsp->setLocalAddr(query->getLocalAddr());
-        rsp->setLocalPort(DHCP4_SERVER_PORT);
-        rsp->setIface(query->getIface());
-        rsp->setIndex(query->getIndex());
-
         // Specifies if server should do the packing
         bool skip_pack = false;
 
@@ -487,90 +444,6 @@ Dhcpv4Srv::run() {
     return (true);
 }
 
-bool
-Dhcpv4Srv::loadServerID(const std::string& file_name) {
-
-    // load content of the file into a string
-    fstream f(file_name.c_str(), ios::in);
-    if (!f.is_open()) {
-        return (false);
-    }
-
-    string hex_string;
-    f >> hex_string;
-    f.close();
-
-    // remove any spaces
-    boost::algorithm::erase_all(hex_string, " ");
-
-    try {
-        IOAddress addr(hex_string);
-
-        if (!addr.isV4()) {
-            return (false);
-        }
-
-        // Now create server-id option
-        serverid_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER, addr));
-
-    } catch(...) {
-        // any kind of malformed input (empty string, IPv6 address, complete
-        // garbate etc.)
-        return (false);
-    }
-
-    return (true);
-}
-
-void
-Dhcpv4Srv::generateServerID() {
-
-    const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
-
-    // Let's find suitable interface.
-    for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
-         iface != ifaces.end(); ++iface) {
-
-        // Let's don't use loopback.
-        if (iface->flag_loopback_) {
-            continue;
-        }
-
-        // Let's skip downed interfaces. It is better to use working ones.
-        if (!iface->flag_up_) {
-            continue;
-        }
-
-        const Iface::AddressCollection addrs = iface->getAddresses();
-
-        for (Iface::AddressCollection::const_iterator addr = addrs.begin();
-             addr != addrs.end(); ++addr) {
-            if (addr->getFamily() != AF_INET) {
-                continue;
-            }
-
-            serverid_ = OptionPtr(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
-                                                     *addr));
-            return;
-        }
-
-
-    }
-
-    isc_throw(BadValue, "No suitable interfaces for server-identifier found");
-}
-
-bool
-Dhcpv4Srv::writeServerID(const std::string& file_name) {
-    fstream f(file_name.c_str(), ios::out | ios::trunc);
-    if (!f.good()) {
-        return (false);
-    }
-    f << srvidToString(getServerID());
-    f.close();
-    return (true);
-}
-
 string
 Dhcpv4Srv::srvidToString(const OptionPtr& srvid) {
     if (!srvid) {
@@ -690,13 +563,22 @@ Dhcpv4Srv::appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type) {
     // add Message Type Option (type 53)
     msg->setType(msg_type);
 
-    // DHCP Server Identifier (type 54)
-    msg->addOption(getServerID());
-
     // more options will be added here later
 }
 
 void
+Dhcpv4Srv::appendServerID(const Pkt4Ptr& response) {
+    // The source address for the outbound message should have been set already.
+    // This is the address that to the best of the server's knowledge will be
+    // available from the client.
+    // @todo: perhaps we should consider some more sophisticated server id
+    // generation, but for the current use cases, it should be ok.
+    response->addOption(OptionPtr(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
+                                                     response->getLocalAddr()))
+                        );
+}
+
+void
 Dhcpv4Srv::appendRequestedOptions(const Pkt4Ptr& question, Pkt4Ptr& msg) {
 
     // Get the subnet relevant for the client. We will need it
@@ -1269,7 +1151,36 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
 }
 
 void
-Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg) {
+Dhcpv4Srv::adjustIfaceData(const Pkt4Ptr& query, const Pkt4Ptr& response) {
+    adjustRemoteAddr(query, response);
+
+    // For the non-relayed message, the destination port is the client's port.
+    // For the relayed message, the server/relay port is a destination.
+    // Note that the call to this function may throw if invalid combination
+    // of hops and giaddr is found (hops = 0 if giaddr = 0 and hops != 0 if
+    // giaddr != 0). The exception will propagate down and eventually cause the
+    // packet to be discarded.
+    response->setRemotePort(query->isRelayed() ? DHCP4_SERVER_PORT :
+                            DHCP4_CLIENT_PORT);
+
+    // In many cases the query is sent to a broadcast address. This address
+    // appears as a local address in the query message. Therefore we can't
+    // simply copy local address from the query and use it as a source
+    // address for the response. Instead, we have to check what address our
+    // socket is bound to and use it as a source address. This operation
+    // may throw if for some reason the socket is closed.
+    // @todo Consider an optimization that we use local address from
+    // the query if this address is not broadcast.
+    SocketInfo sock_info = IfaceMgr::instance().getSocket(*query);
+    // Set local adddress, port and interface.
+    response->setLocalAddr(sock_info.addr_);
+    response->setLocalPort(DHCP4_SERVER_PORT);
+    response->setIface(query->getIface());
+    response->setIndex(query->getIndex());
+}
+
+void
+Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, const Pkt4Ptr& response) {
     // Let's create static objects representing zeroed and broadcast
     // addresses. We will use them further in this function to test
     // other addresses against them. Since they are static, they will
@@ -1278,22 +1189,22 @@ Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg) {
     static const IOAddress bcast_addr("255.255.255.255");
 
     // If received relayed message, server responds to the relay address.
-    if (question->getGiaddr() != zero_addr) {
-        msg->setRemoteAddr(question->getGiaddr());
+    if (question->isRelayed()) {
+        response->setRemoteAddr(question->getGiaddr());
 
     // If giaddr is 0 but client set ciaddr, server should unicast the
     // response to ciaddr.
     } else if (question->getCiaddr() != zero_addr) {
-        msg->setRemoteAddr(question->getCiaddr());
+        response->setRemoteAddr(question->getCiaddr());
 
     // We can't unicast the response to the client when sending NAK,
     // because we haven't allocated address for him. Therefore,
     // NAK is broadcast.
-    } else if (msg->getType() == DHCPNAK) {
-        msg->setRemoteAddr(bcast_addr);
+    } else if (response->getType() == DHCPNAK) {
+        response->setRemoteAddr(bcast_addr);
 
     // If yiaddr is set it means that we have created a lease for a client.
-    } else if (msg->getYiaddr() != zero_addr) {
+    } else if (response->getYiaddr() != zero_addr) {
         // If the broadcast bit is set in the flags field, we have to
         // send the response to broadcast address. Client may have requested it
         // because it doesn't support reception of messages on the interface
@@ -1302,13 +1213,13 @@ Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg) {
         // directly to a client without address assigned.
         const bool bcast_flag = ((question->getFlags() & Pkt4::FLAG_BROADCAST_MASK) != 0);
         if (!IfaceMgr::instance().isDirectResponseSupported() || bcast_flag) {
-            msg->setRemoteAddr(bcast_addr);
+            response->setRemoteAddr(bcast_addr);
 
         // Client cleared the broadcast bit and we support direct responses
         // so we should unicast the response to a newly allocated address -
         // yiaddr.
         } else {
-            msg->setRemoteAddr(msg->getYiaddr());
+            response->setRemoteAddr(response ->getYiaddr());
 
         }
 
@@ -1316,7 +1227,7 @@ Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg) {
     // found ourselves at this point, the rational thing to do is to respond
     // to the address we got the query from.
     } else {
-        msg->setRemoteAddr(question->getRemoteAddr());
+        response->setRemoteAddr(question->getRemoteAddr());
 
     }
 }
@@ -1361,6 +1272,12 @@ Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
         appendBasicOptions(discover, offer);
     }
 
+    // Set the src/dest IP address, port and interface for the outgoing
+    // packet.
+    adjustIfaceData(discover, offer);
+
+    appendServerID(offer);
+
     return (offer);
 }
 
@@ -1397,6 +1314,12 @@ Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
         appendBasicOptions(request, ack);
     }
 
+    // Set the src/dest IP address, port and interface for the outgoing
+    // packet.
+    adjustIfaceData(request, ack);
+
+    appendServerID(ack);
+
     return (ack);
 }
 

+ 66 - 39
src/bin/dhcp4/dhcp4_srv.h

@@ -175,7 +175,7 @@ protected:
     /// @param pkt packet to be checked
     /// @param serverid expectation regarding server-id option
     /// @throw RFCViolation if any issues are detected
-    void sanityCheck(const Pkt4Ptr& pkt, RequirementLevel serverid);
+    static void sanityCheck(const Pkt4Ptr& pkt, RequirementLevel serverid);
 
     /// @brief Processes incoming DISCOVER and returns response.
     ///
@@ -396,10 +396,68 @@ protected:
 
     /// @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.
+    ///
+    ///
     /// @param msg message object (options will be added to it)
     /// @param msg_type specifies message type
     void appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type);
 
+    /// @brief Adds server identifier option to the server's response.
+    ///
+    /// This method adds a server identifier to the DHCPv4 message. It epxects
+    /// that the local (source) address is set for this message. If address is
+    /// not set, it will throw an exception. This method also expects that the
+    /// server identifier option is not present in the specified message.
+    /// Otherwise, it will throw an exception on attempt to add a duplicate
+    /// server identifier option.
+    ///
+    /// @note This method doesn't throw exceptions by itself but the underlying
+    /// classes being used my throw. The reason for this method to not sanity
+    /// check the specified message is that it is meant to be called internally
+    /// by the @c Dhcpv4Srv class.
+    ///
+    /// @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);
+
+    /// @brief Set IP/UDP and interface parameters for the DHCPv4 response.
+    ///
+    /// This method sets the following parameters for the DHCPv4 message being
+    /// sent to a client:
+    /// - client unicast or a broadcast address,
+    /// - client or relay port,
+    /// - server address,
+    /// - server port,
+    /// - name and index of the interface which is to be used to send the
+    /// message.
+    ///
+    /// Internally it calls the @c Dhcpv4Srv::adjustRemoteAddr to figure
+    /// out the destination address (client unicast address or broadcast
+    /// address).
+    ///
+    /// The destination port is always DHCPv4 client (68) or relay (67) port,
+    /// depending if the response will be sent directly to a client.
+    ///
+    /// The source port is always set to DHCPv4 server port (67).
+    ///
+    /// The interface selected for the response is always the same as the
+    /// one through which the query has been received.
+    ///
+    /// The source address for the response is the IPv4 address assigned to
+    /// the interface being used to send the response. This function uses
+    /// @c IfaceMgr to get the socket bound to the IPv4 address on the
+    /// particular interface.
+    ///
+    /// @note This method is static because it is not dependent on the class
+    /// state.
+    static void adjustIfaceData(const Pkt4Ptr& query, const Pkt4Ptr& response);
+
     /// @brief Sets remote addresses for outgoing packet.
     ///
     /// This method sets the local and remote addresses on outgoing packet.
@@ -413,42 +471,14 @@ protected:
     /// are valid. Make sure that pointers are correct before calling this
     /// function.
     ///
-    /// @param question instance of a packet received by a server.
-    /// @param [out] msg response packet which addresses are to be adjusted.
-    void adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg);
-
-    /// @brief Returns server-identifier option
+    /// @note This method is static because it is not dependent on the class
+    /// state.
     ///
-    /// @return server-id option
-    OptionPtr getServerID() { return serverid_; }
-
-    /// @brief Sets server-identifier.
-    ///
-    /// This method attempts to set server-identifier DUID. It tries to
-    /// load previously stored IP from configuration. If there is no previously
-    /// stored server identifier, it will pick up one address from configured
-    /// and supported network interfaces.
-    ///
-    /// @throws isc::Unexpected Failed to obtain server identifier (i.e. no
-    //          previously stored configuration and no network interfaces available)
-    void generateServerID();
-
-    /// @brief attempts to load server-id from a file
-    ///
-    /// Tries to load duid from a text file. If the load is successful,
-    /// it creates server-id option and stores it in serverid_ (to be used
-    /// later by getServerID()).
-    ///
-    /// @param file_name name of the server-id file to load
-    /// @return true if load was successful, false otherwise
-    bool loadServerID(const std::string& file_name);
-
-    /// @brief attempts to write server-id to a file
-    /// Tries to write server-id content (stored in serverid_) to a text file.
-    ///
-    /// @param file_name name of the server-id file to write
-    /// @return true if write was successful, false otherwise
-    bool writeServerID(const std::string& file_name);
+    /// @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);
 
     /// @brief converts server-id to text
     /// Converts content of server-id option to a text representation, e.g.
@@ -486,9 +516,6 @@ protected:
     /// @return selected subnet (or NULL if no suitable subnet was found)
     isc::dhcp::Subnet4Ptr selectSubnet(const Pkt4Ptr& question);
 
-    /// server DUID (to be sent in server-identifier option)
-    OptionPtr serverid_;
-
     /// indicates if shutdown is in progress. Setting it to true will
     /// initiate server shutdown procedure.
     volatile bool shutdown_;

+ 289 - 161
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc

@@ -44,7 +44,6 @@
 
 #include <boost/scoped_ptr.hpp>
 
-#include <fstream>
 #include <iostream>
 
 #include <arpa/inet.h>
@@ -59,72 +58,28 @@ using namespace isc::dhcp::test;
 
 namespace {
 
-/// dummy server-id file location
-const char* SRVID_FILE = "server-id-test.txt";
-
-// Sanity check. Verifies that both Dhcpv4Srv and its derived
-// class NakedDhcpv4Srv can be instantiated and destroyed.
-TEST_F(Dhcpv4SrvTest, basic) {
-
-    // Check that the base class can be instantiated
-    boost::scoped_ptr<Dhcpv4Srv> srv;
-    ASSERT_NO_THROW(srv.reset(new Dhcpv4Srv(DHCP4_SERVER_PORT + 10000, "type=memfile",
-                                            false, false)));
-    srv.reset();
-    // We have to close open sockets because further in this test we will
-    // call the Dhcpv4Srv constructor again. This constructor will try to
-    // set the appropriate packet filter class for IfaceMgr. This requires
-    // that all sockets are closed.
-    IfaceMgr::instance().closeSockets();
-
-    // Check that the derived class can be instantiated
-    boost::scoped_ptr<NakedDhcpv4Srv> naked_srv;
-    ASSERT_NO_THROW(
-        naked_srv.reset(new NakedDhcpv4Srv(DHCP4_SERVER_PORT + 10000)));
-    EXPECT_TRUE(naked_srv->getServerID());
-    // Close sockets again for the next test.
-    IfaceMgr::instance().closeSockets();
-
-    ASSERT_NO_THROW(naked_srv.reset(new NakedDhcpv4Srv(0)));
-    EXPECT_TRUE(naked_srv->getServerID());
-}
-
-// This test verifies that exception is not thrown when an error occurs during
-// opening sockets. This test forces an error by adding a fictious interface
-// to the IfaceMgr. An attempt to open socket on this interface must always
-// fail. The DHCPv4 installs the error handler function to prevent exceptions
-// being thrown from the openSockets4 function.
-// @todo The server tests for socket should be extended but currently the
-// ability to unit test the sockets code is somewhat limited.
-TEST_F(Dhcpv4SrvTest, openActiveSockets) {
-    ASSERT_NO_THROW(CfgMgr::instance().activateAllIfaces());
-
-    Iface iface("bogusiface", 255);
-    iface.flag_loopback_ = false;
-    iface.flag_up_ = true;
-    iface.flag_running_ = true;
-    iface.inactive4_ = false;
-    iface.addAddress(IOAddress("192.0.0.0"));
-    IfaceMgr::instance().addInterface(iface);
-    ASSERT_NO_THROW(Dhcpv4Srv::openActiveSockets(DHCP4_SERVER_PORT, false));
-}
-
 // This test verifies that the destination address of the response
 // message is set to giaddr, when giaddr is set to non-zero address
 // in the received message.
-TEST_F(Dhcpv4SrvTest, adjustRemoteAddressRelay) {
-    boost::scoped_ptr<NakedDhcpv4Srv> srv(new NakedDhcpv4Srv(0));
-
+TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRelay) {
     // Create the instance of the incoming packet.
     boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
-    // Set the giaddr to non-zero address as if it was relayed.
+    // Set the giaddr to non-zero address and hops to non-zero value
+    // as if it was relayed.
     req->setGiaddr(IOAddress("192.0.2.1"));
+    req->setHops(2);
     // Set ciaddr to zero. This simulates the client which applies
     // for the new lease.
     req->setCiaddr(IOAddress("0.0.0.0"));
     // Clear broadcast flag.
     req->setFlags(0x0000);
 
+    // Set local address, port and interface.
+    req->setLocalAddr(IOAddress("192.0.3.1"));
+    req->setLocalPort(1001);
+    req->setIface("eth0");
+    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.
@@ -132,12 +87,24 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressRelay) {
     resp->setYiaddr(IOAddress("192.0.2.100"));
     // Clear the remote address.
     resp->setRemoteAddr(IOAddress("0.0.0.0"));
+    // Set hops value for the response.
+    resp->setHops(req->getHops());
 
     // This function never throws.
-    ASSERT_NO_THROW(srv->adjustRemoteAddr(req, resp));
+    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
 
     // Now the destination address should be relay's address.
     EXPECT_EQ("192.0.2.1", resp->getRemoteAddr().toText());
+    // The query has been relayed, so the response must be sent to the port 67.
+    EXPECT_EQ(DHCP4_SERVER_PORT, resp->getRemotePort());
+    // Local address should be copied from the query message.
+    EXPECT_EQ("192.0.3.1", resp->getLocalAddr().toText());
+    // The local port is always DHCPv4 server port 67.
+    EXPECT_EQ(DHCP4_SERVER_PORT, resp->getLocalPort());
+    // We will send response over the same interface which was used to receive
+    // query.
+    EXPECT_EQ("eth0", resp->getIface());
+    EXPECT_EQ(1, resp->getIndex());
 
     // Let's do another test and set other fields: ciaddr and
     // flags. By doing it, we want to make sure that the relay
@@ -150,7 +117,7 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressRelay) {
     // Clear remote address.
     resp->setRemoteAddr(IOAddress("0.0.0.0"));
 
-    ASSERT_NO_THROW(srv->adjustRemoteAddr(req, resp));
+    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
 
     // Response should be sent back to the relay address.
     EXPECT_EQ("192.0.2.50", resp->getRemoteAddr().toText());
@@ -160,9 +127,7 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressRelay) {
 // is set to ciaddr when giaddr is set to zero and the ciaddr is set to
 // non-zero address in the received message. This is the case when the
 // client is in Renew or Rebind state.
-TEST_F(Dhcpv4SrvTest, adjustRemoteAddressRenewRebind) {
-    boost::scoped_ptr<NakedDhcpv4Srv> srv(new NakedDhcpv4Srv(0));
-
+TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRenew) {
     // Create instance of the incoming packet.
     boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
 
@@ -179,6 +144,15 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressRenewRebind) {
     // whether to unicast the response to the acquired address or
     // broadcast it.
     req->setFlags(Pkt4::FLAG_BROADCAST_MASK);
+    // This is a direct message, so the hops should be cleared.
+    req->setHops(0);
+    // Set local unicast address as if we are renewing a lease.
+    req->setLocalAddr(IOAddress("192.0.3.1"));
+    // Request is received on the DHCPv4 server port.
+    req->setLocalPort(DHCP4_SERVER_PORT);
+    // Set the interface. The response should be sent over the same interface.
+    req->setIface("eth0");
+    req->setIndex(1);
 
     // Create a response.
     boost::shared_ptr<Pkt4> resp(new Pkt4(DHCPOFFER, 1234));
@@ -189,11 +163,25 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressRenewRebind) {
     resp->setYiaddr(IOAddress("192.0.2.13"));
     // Clear the remote address.
     resp->setRemoteAddr(IOAddress("0.0.0.0"));
+    // Copy hops value from the query.
+    resp->setHops(req->getHops());
 
-    ASSERT_NO_THROW(srv->adjustRemoteAddr(req, resp));
+    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
 
     // Check that server responds to ciaddr
     EXPECT_EQ("192.0.2.15", resp->getRemoteAddr().toText());
+    // The query was non-relayed, so the response should be sent to a DHCPv4
+    // client port 68.
+    EXPECT_EQ(DHCP4_CLIENT_PORT, resp->getRemotePort());
+    // The response should be sent from the unicast address on which the
+    // query has been received.
+    EXPECT_EQ("192.0.3.1", resp->getLocalAddr().toText());
+    // The response should be sent from the DHCPv4 server port.
+    EXPECT_EQ(DHCP4_SERVER_PORT, resp->getLocalPort());
+    // The interface data should match the data in the query.
+    EXPECT_EQ("eth0", resp->getIface());
+    EXPECT_EQ(1, resp->getIndex());
+
 }
 
 // This test verifies that the destination address of the response message
@@ -203,9 +191,7 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressRenewRebind) {
 // of the response should be set to yiaddr if server supports direct responses
 // to the client which doesn't have an address yet or broadcast if the server
 // doesn't support direct responses.
-TEST_F(Dhcpv4SrvTest, adjustRemoteAddressSelect) {
-    boost::scoped_ptr<NakedDhcpv4Srv> srv(new NakedDhcpv4Srv(0));
-
+TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataSelect) {
     // Create instance of the incoming packet.
     boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
 
@@ -217,13 +203,31 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressSelect) {
     // Let's clear the broadcast flag.
     req->setFlags(0);
 
+    // This is a non-relayed message, so let's clear hops count.
+    req->setHops(0);
+    // The query is sent to the broadcast address in the Select state.
+    req->setLocalAddr(IOAddress("255.255.255.255"));
+    // The query has been received on the DHCPv4 server port 67.
+    req->setLocalPort(DHCP4_SERVER_PORT);
+    // Set the interface. The response should be sent via the same interface.
+    req->setIface("eth0");
+    req->setIndex(1);
+
     // Create a response.
     boost::shared_ptr<Pkt4> resp(new Pkt4(DHCPOFFER, 1234));
     // 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"));
+    // Copy hops count.
+    resp->setHops(req->getHops());
+
+    // We want to test the case, when the server (packet filter) doesn't support
+    // ddirect responses to the client which doesn't have an address yet. In
+    // case, the server should send its response to the broadcast address.
+    // We can control whether the current packet filter returns that its support
+    // direct responses or not.
+    current_pkt_filter_->direct_resp_supported_ = false;
 
     // When running unit tests, the IfaceMgr is using the default Packet
     // Filtering class, PktFilterInet. This class does not support direct
@@ -231,24 +235,36 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressSelect) {
     // 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(srv->adjustRemoteAddr(req, resp));
+    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
 
     // Check that the response is sent to broadcast address as the
     // server doesn't have capability to respond directly.
     EXPECT_EQ("255.255.255.255", resp->getRemoteAddr().toText());
 
+    // Although the query has been sent to the broadcast address, the
+    // server should select a unicast address on the particular interface
+    // as a source address for the response.
+    EXPECT_EQ("192.0.3.1", resp->getLocalAddr().toText());
+
+    // The response should be sent from the DHCPv4 server port.
+    EXPECT_EQ(DHCP4_SERVER_PORT, resp->getLocalPort());
+
+    // The response should be sent via the same interface through which
+    // query has been received.
+    EXPECT_EQ("eth0", resp->getIface());
+    EXPECT_EQ(1, resp->getIndex());
+
     // We also want to test the case when the server has capability to
     // respond directly to the client which is not configured. Server
     // makes decision whether it responds directly or broadcast its
-    // response based on the capability reported by IfaceMgr. In order
-    // to set this capability we have to provide a dummy Packet Filter
-    // class which would report the support for direct responses.
-    // This class is called PktFilterTest.
-    IfaceMgr::instance().setPacketFilter(PktFilterPtr(new PktFilterTest()));
+    // response based on the capability reported by IfaceMgr. We can
+    // control whether the current packet filter returns that it supports
+    // direct responses or not.
+    current_pkt_filter_->direct_resp_supported_ = true;
 
     // Now we expect that the server will send its response to the
     // address assigned for the client.
-    ASSERT_NO_THROW(srv->adjustRemoteAddr(req, resp));
+    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
 
     EXPECT_EQ("192.0.2.13", resp->getRemoteAddr().toText());
 }
@@ -258,9 +274,7 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressSelect) {
 // query. Client sets this flag to indicate that it can't receive direct
 // responses from the server when it doesn't have its interface configured.
 // Server must respect broadcast flag.
-TEST_F(Dhcpv4SrvTest, adjustRemoteAddressBroadcast) {
-    boost::scoped_ptr<NakedDhcpv4Srv> srv(new NakedDhcpv4Srv(0));
-
+TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataBroadcast) {
     // Create instance of the incoming packet.
     boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
 
@@ -268,6 +282,13 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressBroadcast) {
     req->setGiaddr(IOAddress("0.0.0.0"));
     // Clear client address as it hasn't got any address configured yet.
     req->setCiaddr(IOAddress("0.0.0.0"));
+    // The query is sent to the broadcast address in the Select state.
+    req->setLocalAddr(IOAddress("255.255.255.255"));
+    // The query has been received on the DHCPv4 server port 67.
+    req->setLocalPort(DHCP4_SERVER_PORT);
+    // Set the interface. The response should be sent via the same interface.
+    req->setIface("eth0");
+    req->setIndex(1);
 
     // Let's set the broadcast flag.
     req->setFlags(Pkt4::FLAG_BROADCAST_MASK);
@@ -280,25 +301,130 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressBroadcast) {
     // Clear the remote address.
     resp->setRemoteAddr(IOAddress("0.0.0.0"));
 
-    // When running unit tests, the IfaceMgr is using the default Packet
-    // Filtering class, PktFilterInet. This class does not support direct
-    // responses to the clients without address assigned. If giaddr and
-    // ciaddr are zero and client has just got the new lease, the assigned
-    // address is carried in yiaddr. In order to send this address to the
-    // client, server must send the response to the broadcast address when
-    // direct response is not supported. This conflicts with the purpose
-    // of this test which is supposed to verify that responses are sent
-    // to broadcast address only, when broadcast flag is set. Therefore,
-    // in order to simulate that direct responses are supported we have
-    // to replace the default packet filtering class with a dummy class
-    // which reports direct response capability.
-    IfaceMgr::instance().setPacketFilter(PktFilterPtr(new PktFilterTest()));
-
-    ASSERT_NO_THROW(srv->adjustRemoteAddr(req, resp));
+    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
 
     // Server must repond to broadcast address when client desired that
     // by setting the broadcast flag in its request.
     EXPECT_EQ("255.255.255.255", resp->getRemoteAddr().toText());
+
+    // Although the query has been sent to the broadcast address, the
+    // server should select a unicast address on the particular interface
+    // as a source address for the response.
+    EXPECT_EQ("192.0.3.1", resp->getLocalAddr().toText());
+
+    // The response should be sent from the DHCPv4 server port.
+    EXPECT_EQ(DHCP4_SERVER_PORT, resp->getLocalPort());
+
+    // The response should be sent via the same interface through which
+    // query has been received.
+    EXPECT_EQ("eth0", resp->getIface());
+    EXPECT_EQ(1, resp->getIndex());
+
+}
+
+// This test verifies that exception is thrown of the invalid combination
+// of giaddr and hops is specified in a client's message.
+TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataInvalid) {
+    boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
+
+    // The hops and giaddr values are used to determine if the client's
+    // message has been relayed or sent directly. The allowed combinations
+    // are (giaddr = 0 and hops = 0) or (giaddr != 0 and hops != 0). Any
+    // other combination is invalid and the adjustIfaceData should throw
+    // an exception. We will test that exception is indeed thrown.
+    req->setGiaddr(IOAddress("0.0.0.0"));
+    req->setHops(1);
+
+    // Clear client address as it hasn't got any address configured yet.
+    req->setCiaddr(IOAddress("0.0.0.0"));
+    // The query is sent to the broadcast address in the Select state.
+    req->setLocalAddr(IOAddress("255.255.255.255"));
+    // The query has been received on the DHCPv4 server port 67.
+    req->setLocalPort(DHCP4_SERVER_PORT);
+    // Set the interface. The response should be sent via the same interface.
+    req->setIface("eth0");
+    req->setIndex(1);
+
+    // Create a response.
+    boost::shared_ptr<Pkt4> resp(new Pkt4(DHCPOFFER, 1234));
+    // 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);
+}
+
+// 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));
+    // 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));
+
+    // Make sure that the option has been added.
+    OptionPtr opt = response->getOption(DHO_DHCP_SERVER_IDENTIFIER);
+    ASSERT_TRUE(opt);
+    Option4AddrLstPtr opt_server_id =
+        boost::dynamic_pointer_cast<Option4AddrLst>(opt);
+    ASSERT_TRUE(opt_server_id);
+
+    // The option is represented as a list of IPv4 addresses but with
+    // only one address added.
+    Option4AddrLst::AddressContainer addrs = opt_server_id->getAddresses();
+    ASSERT_EQ(1, addrs.size());
+    // This address should match the local address of the packet.
+    EXPECT_EQ("192.0.3.1", addrs[0].toText());
+}
+
+// Sanity check. Verifies that both Dhcpv4Srv and its derived
+// class NakedDhcpv4Srv can be instantiated and destroyed.
+TEST_F(Dhcpv4SrvTest, basic) {
+
+    // Check that the base class can be instantiated
+    boost::scoped_ptr<Dhcpv4Srv> srv;
+    ASSERT_NO_THROW(srv.reset(new Dhcpv4Srv(DHCP4_SERVER_PORT + 10000, "type=memfile",
+                                            false, false)));
+    srv.reset();
+    // We have to close open sockets because further in this test we will
+    // call the Dhcpv4Srv constructor again. This constructor will try to
+    // set the appropriate packet filter class for IfaceMgr. This requires
+    // that all sockets are closed.
+    IfaceMgr::instance().closeSockets();
+
+    // Check that the derived class can be instantiated
+    boost::scoped_ptr<NakedDhcpv4Srv> naked_srv;
+    ASSERT_NO_THROW(
+        naked_srv.reset(new NakedDhcpv4Srv(DHCP4_SERVER_PORT + 10000)));
+    // Close sockets again for the next test.
+    IfaceMgr::instance().closeSockets();
+
+    ASSERT_NO_THROW(naked_srv.reset(new NakedDhcpv4Srv(0)));
+}
+
+// This test verifies that exception is not thrown when an error occurs during
+// opening sockets. This test forces an error by adding a fictious interface
+// to the IfaceMgr. An attempt to open socket on this interface must always
+// fail. The DHCPv4 installs the error handler function to prevent exceptions
+// being thrown from the openSockets4 function.
+// @todo The server tests for socket should be extended but currently the
+// ability to unit test the sockets code is somewhat limited.
+TEST_F(Dhcpv4SrvTest, openActiveSockets) {
+    ASSERT_NO_THROW(CfgMgr::instance().activateAllIfaces());
+
+    Iface iface("bogusiface", 255);
+    iface.flag_loopback_ = false;
+    iface.flag_up_ = true;
+    iface.flag_running_ = true;
+    iface.inactive4_ = false;
+    iface.addAddress(IOAddress("192.0.0.0"));
+    IfaceMgr::instance().addInterface(iface);
+    ASSERT_NO_THROW(Dhcpv4Srv::openActiveSockets(DHCP4_SERVER_PORT, false));
 }
 
 // Verifies that DISCOVER message can be processed correctly,
@@ -309,7 +435,7 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressBroadcast) {
 // are other tests that verify correctness of the allocation
 // engine. See DiscoverBasic, DiscoverHint, DiscoverNoClientId
 // and DiscoverInvalidHint.
-TEST_F(Dhcpv4SrvTest, processDiscover) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, processDiscover) {
     testDiscoverRequest(DHCPDISCOVER);
 }
 
@@ -321,11 +447,11 @@ TEST_F(Dhcpv4SrvTest, processDiscover) {
 // are other tests that verify correctness of the allocation
 // engine. See DiscoverBasic, DiscoverHint, DiscoverNoClientId
 // and DiscoverInvalidHint.
-TEST_F(Dhcpv4SrvTest, processRequest) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, processRequest) {
     testDiscoverRequest(DHCPREQUEST);
 }
 
-TEST_F(Dhcpv4SrvTest, processRelease) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, processRelease) {
     NakedDhcpv4Srv srv;
     Pkt4Ptr pkt(new Pkt4(DHCPRELEASE, 1234));
 
@@ -333,7 +459,7 @@ TEST_F(Dhcpv4SrvTest, processRelease) {
     EXPECT_NO_THROW(srv.processRelease(pkt));
 }
 
-TEST_F(Dhcpv4SrvTest, processDecline) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, processDecline) {
     NakedDhcpv4Srv srv;
     Pkt4Ptr pkt(new Pkt4(DHCPDECLINE, 1234));
 
@@ -341,7 +467,7 @@ TEST_F(Dhcpv4SrvTest, processDecline) {
     EXPECT_NO_THROW(srv.processDecline(pkt));
 }
 
-TEST_F(Dhcpv4SrvTest, processInform) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, processInform) {
     NakedDhcpv4Srv srv;
     Pkt4Ptr pkt(new Pkt4(DHCPINFORM, 1234));
 
@@ -398,7 +524,7 @@ TEST_F(Dhcpv4SrvTest, serverReceivedPacketName) {
 // - copy of client-id
 // - server-id
 // - offered address
-TEST_F(Dhcpv4SrvTest, DiscoverBasic) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverBasic) {
     boost::scoped_ptr<NakedDhcpv4Srv> srv;
     ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
 
@@ -406,6 +532,7 @@ TEST_F(Dhcpv4SrvTest, DiscoverBasic) {
     dis->setRemoteAddr(IOAddress("192.0.2.1"));
     OptionPtr clientid = generateClientId();
     dis->addOption(clientid);
+    dis->setIface("eth0");
 
     // Pass it to the server and get an offer
     Pkt4Ptr offer = srv->processDiscover(dis);
@@ -435,7 +562,7 @@ TEST_F(Dhcpv4SrvTest, DiscoverBasic) {
 // - copy of client-id
 // - server-id
 // - offered address
-TEST_F(Dhcpv4SrvTest, DiscoverHint) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverHint) {
     boost::scoped_ptr<NakedDhcpv4Srv> srv;
     ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
     IOAddress hint("192.0.2.107");
@@ -445,6 +572,7 @@ TEST_F(Dhcpv4SrvTest, DiscoverHint) {
     OptionPtr clientid = generateClientId();
     dis->addOption(clientid);
     dis->setYiaddr(hint);
+    dis->setIface("eth0");
 
     // Pass it to the server and get an offer
     Pkt4Ptr offer = srv->processDiscover(dis);
@@ -475,7 +603,7 @@ TEST_F(Dhcpv4SrvTest, DiscoverHint) {
 // - copy of client-id
 // - server-id
 // - offered address
-TEST_F(Dhcpv4SrvTest, DiscoverNoClientId) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverNoClientId) {
     boost::scoped_ptr<NakedDhcpv4Srv> srv;
     ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
     IOAddress hint("192.0.2.107");
@@ -484,6 +612,7 @@ TEST_F(Dhcpv4SrvTest, DiscoverNoClientId) {
     dis->setRemoteAddr(IOAddress("192.0.2.1"));
     dis->setYiaddr(hint);
     dis->setHWAddr(generateHWAddr(6));
+    dis->setIface("eth0");
 
     // Pass it to the server and get an offer
     Pkt4Ptr offer = srv->processDiscover(dis);
@@ -513,7 +642,7 @@ TEST_F(Dhcpv4SrvTest, DiscoverNoClientId) {
 // - copy of client-id
 // - server-id
 // - offered address (!= hint)
-TEST_F(Dhcpv4SrvTest, DiscoverInvalidHint) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverInvalidHint) {
     boost::scoped_ptr<NakedDhcpv4Srv> srv;
     ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
     IOAddress hint("10.1.2.3");
@@ -523,6 +652,7 @@ TEST_F(Dhcpv4SrvTest, DiscoverInvalidHint) {
     OptionPtr clientid = generateClientId();
     dis->addOption(clientid);
     dis->setYiaddr(hint);
+    dis->setIface("eth0");
 
     // Pass it to the server and get an offer
     Pkt4Ptr offer = srv->processDiscover(dis);
@@ -551,7 +681,7 @@ TEST_F(Dhcpv4SrvTest, DiscoverInvalidHint) {
 // and this is a correct behavior. It is REQUEST that will fail for the third
 // client. OFFER is basically saying "if you send me a request, you will
 // probably get an address like this" (there are no guarantees).
-TEST_F(Dhcpv4SrvTest, ManyDiscovers) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, ManyDiscovers) {
     boost::scoped_ptr<NakedDhcpv4Srv> srv;
     ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
 
@@ -563,6 +693,11 @@ TEST_F(Dhcpv4SrvTest, ManyDiscovers) {
     dis2->setRemoteAddr(IOAddress("192.0.2.2"));
     dis3->setRemoteAddr(IOAddress("192.0.2.3"));
 
+    // Assign interfaces
+    dis1->setIface("eth0");
+    dis2->setIface("eth0");
+    dis3->setIface("eth0");
+
     // Different client-id sizes
     OptionPtr clientid1 = generateClientId(4); // length 4
     OptionPtr clientid2 = generateClientId(5); // length 5
@@ -611,13 +746,14 @@ TEST_F(Dhcpv4SrvTest, ManyDiscovers) {
 // Checks whether echoing back client-id is controllable, i.e.
 // whether the server obeys echo-client-id and sends (or not)
 // client-id
-TEST_F(Dhcpv4SrvTest, discoverEchoClientId) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, discoverEchoClientId) {
     NakedDhcpv4Srv srv(0);
 
     Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
     dis->setRemoteAddr(IOAddress("192.0.2.1"));
     OptionPtr clientid = generateClientId();
     dis->addOption(clientid);
+    dis->setIface("eth0");
 
     // Pass it to the server and get an offer
     Pkt4Ptr offer = srv.processDiscover(dis);
@@ -649,7 +785,7 @@ TEST_F(Dhcpv4SrvTest, discoverEchoClientId) {
 // - assigned address
 //
 // Test verifies that the lease is actually in the database.
-TEST_F(Dhcpv4SrvTest, RequestBasic) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, RequestBasic) {
     boost::scoped_ptr<NakedDhcpv4Srv> srv;
     ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
 
@@ -659,6 +795,7 @@ TEST_F(Dhcpv4SrvTest, RequestBasic) {
     OptionPtr clientid = generateClientId();
     req->addOption(clientid);
     req->setYiaddr(hint);
+    req->setIface("eth0");
 
     // Pass it to the server and get an advertise
     Pkt4Ptr ack = srv->processRequest(req);
@@ -694,7 +831,7 @@ TEST_F(Dhcpv4SrvTest, RequestBasic) {
 // - copy of client-id
 // - server-id
 // - assigned address (different for each client)
-TEST_F(Dhcpv4SrvTest, ManyRequests) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, ManyRequests) {
 
     boost::scoped_ptr<NakedDhcpv4Srv> srv;
     ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
@@ -712,6 +849,11 @@ TEST_F(Dhcpv4SrvTest, ManyRequests) {
     req2->setRemoteAddr(relay);
     req3->setRemoteAddr(relay);
 
+    // Assign interfaces
+    req1->setIface("eth0");
+    req2->setIface("eth0");
+    req3->setIface("eth0");
+
     req1->setYiaddr(req_addr1);
     req2->setYiaddr(req_addr2);
     req3->setYiaddr(req_addr3);
@@ -777,13 +919,14 @@ TEST_F(Dhcpv4SrvTest, ManyRequests) {
 }
 
 // Checks whether echoing back client-id is controllable
-TEST_F(Dhcpv4SrvTest, requestEchoClientId) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, requestEchoClientId) {
     NakedDhcpv4Srv srv(0);
 
     Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
     dis->setRemoteAddr(IOAddress("192.0.2.1"));
     OptionPtr clientid = generateClientId();
     dis->addOption(clientid);
+    dis->setIface("eth0");
 
     // Pass it to the server and get ACK
     Pkt4Ptr ack = srv.processRequest(dis);
@@ -810,7 +953,7 @@ TEST_F(Dhcpv4SrvTest, requestEchoClientId) {
 // - returned REPLY message has server-id
 // - returned REPLY message has IA that includes IAADDR
 // - lease is actually renewed in LeaseMgr
-TEST_F(Dhcpv4SrvTest, RenewBasic) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, RenewBasic) {
     boost::scoped_ptr<NakedDhcpv4Srv> srv;
     ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
 
@@ -850,6 +993,7 @@ TEST_F(Dhcpv4SrvTest, RenewBasic) {
     req->setRemoteAddr(IOAddress(addr));
     req->setYiaddr(addr);
     req->setCiaddr(addr); // client's address
+    req->setIface("eth0");
 
     req->addOption(clientid);
     req->addOption(srv->getServerID());
@@ -898,30 +1042,32 @@ TEST_F(Dhcpv4SrvTest, sanityCheck) {
     pkt->setHWAddr(generateHWAddr(6));
 
     // Server-id is optional for information-request, so
-    EXPECT_NO_THROW(srv->sanityCheck(pkt, Dhcpv4Srv::OPTIONAL));
+    EXPECT_NO_THROW(NakedDhcpv4Srv::sanityCheck(pkt, Dhcpv4Srv::OPTIONAL));
 
     // Empty packet, no server-id
-    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv4Srv::MANDATORY), RFCViolation);
+    EXPECT_THROW(NakedDhcpv4Srv::sanityCheck(pkt, Dhcpv4Srv::MANDATORY),
+                 RFCViolation);
 
     pkt->addOption(srv->getServerID());
 
     // Server-id is mandatory and present = no exception
-    EXPECT_NO_THROW(srv->sanityCheck(pkt, Dhcpv4Srv::MANDATORY));
+    EXPECT_NO_THROW(NakedDhcpv4Srv::sanityCheck(pkt, Dhcpv4Srv::MANDATORY));
 
     // Server-id is forbidden, but present => exception
-    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv4Srv::FORBIDDEN),
+    EXPECT_THROW(NakedDhcpv4Srv::sanityCheck(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(srv->sanityCheck(pkt, Dhcpv4Srv::MANDATORY), RFCViolation);
+    EXPECT_THROW(NakedDhcpv4Srv::sanityCheck(pkt, Dhcpv4Srv::MANDATORY),
+                 RFCViolation);
 }
 
 // This test verifies that incoming (positive) RELEASE can be handled properly.
 // As there is no REPLY in DHCPv4, the only thing to verify here is that
 // the lease is indeed removed from the database.
-TEST_F(Dhcpv4SrvTest, ReleaseBasic) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, ReleaseBasic) {
     boost::scoped_ptr<NakedDhcpv4Srv> srv;
     ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
 
@@ -958,6 +1104,7 @@ TEST_F(Dhcpv4SrvTest, ReleaseBasic) {
     rel->addOption(clientid);
     rel->addOption(srv->getServerID());
     rel->setHWAddr(hw);
+    rel->setIface("eth0");
 
     // Pass it to the server and hope for a REPLY
     // Note: this is no response to RELEASE in DHCPv4
@@ -992,7 +1139,7 @@ TEST_F(Dhcpv4SrvTest, ReleaseBasic) {
 // 1. there is no such lease at all
 // 2. there is such a lease, but it is assigned to a different IAID
 // 3. there is such a lease, but it belongs to a different client
-TEST_F(Dhcpv4SrvTest, ReleaseReject) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, ReleaseReject) {
     boost::scoped_ptr<NakedDhcpv4Srv> srv;
     ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
 
@@ -1021,6 +1168,7 @@ TEST_F(Dhcpv4SrvTest, ReleaseReject) {
     rel->addOption(clientid);
     rel->addOption(srv->getServerID());
     rel->setHWAddr(bogus_hw);
+    rel->setIface("eth0");
 
     // Case 1: No lease known to server
     SCOPED_TRACE("CASE 1: Lease is not known to the server");
@@ -1079,38 +1227,8 @@ TEST_F(Dhcpv4SrvTest, ReleaseReject) {
     EXPECT_FALSE(l);
 }
 
-// This test verifies if the server-id disk operations (read, write) are
-// working properly.
-TEST_F(Dhcpv4SrvTest, ServerID) {
-    NakedDhcpv4Srv srv(0);
-
-    string srvid_text = "192.0.2.100";
-    IOAddress srvid(srvid_text);
-
-    fstream file1(SRVID_FILE, ios::out | ios::trunc);
-    file1 << srvid_text;
-    file1.close();
-
-    // Test reading from a file
-    EXPECT_TRUE(srv.loadServerID(SRVID_FILE));
-    ASSERT_TRUE(srv.getServerID());
-    EXPECT_EQ(srvid_text, srv.srvidToString(srv.getServerID()));
-
-    // Now test writing to a file
-    EXPECT_EQ(0, unlink(SRVID_FILE));
-    EXPECT_NO_THROW(srv.writeServerID(SRVID_FILE));
-
-    fstream file2(SRVID_FILE, ios::in);
-    ASSERT_TRUE(file2.good());
-    string text;
-    file2 >> text;
-    file2.close();
-
-    EXPECT_EQ(srvid_text, text);
-}
-
 // Checks if received relay agent info option is echoed back to the client
-TEST_F(Dhcpv4SrvTest, relayAgentInfoEcho) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, relayAgentInfoEcho) {
 
     NakedDhcpv4Srv srv(0);
 
@@ -1153,7 +1271,7 @@ TEST_F(Dhcpv4SrvTest, relayAgentInfoEcho) {
 
 // Checks if vendor options are parsed correctly and requested vendor options
 // are echoed back.
-TEST_F(Dhcpv4SrvTest, vendorOptionsDocsis) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, vendorOptionsDocsis) {
 
     NakedDhcpv4Srv srv(0);
 
@@ -1173,7 +1291,7 @@ TEST_F(Dhcpv4SrvTest, vendorOptionsDocsis) {
         "    \"rebind-timer\": 2000, "
         "    \"renew-timer\": 1000, "
         "    \"valid-lifetime\": 4000,"
-        "    \"interface\": \"" + valid_iface_ + "\" "
+        "    \"interface\": \"eth0\" "
         " } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -1347,7 +1465,7 @@ TEST_F(Dhcpv4SrvTest, unpackOptions) {
 
 // Checks whether the server uses default (0.0.0.0) siaddr value, unless
 // explicitly specified
-TEST_F(Dhcpv4SrvTest, siaddrDefault) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, siaddrDefault) {
     boost::scoped_ptr<NakedDhcpv4Srv> srv;
     ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
     IOAddress hint("192.0.2.107");
@@ -1357,6 +1475,7 @@ TEST_F(Dhcpv4SrvTest, siaddrDefault) {
     OptionPtr clientid = generateClientId();
     dis->addOption(clientid);
     dis->setYiaddr(hint);
+    dis->setIface("eth0");
 
     // Pass it to the server and get an offer
     Pkt4Ptr offer = srv->processDiscover(dis);
@@ -1370,13 +1489,14 @@ TEST_F(Dhcpv4SrvTest, siaddrDefault) {
 }
 
 // Checks whether the server uses specified siaddr value
-TEST_F(Dhcpv4SrvTest, siaddr) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, siaddr) {
     boost::scoped_ptr<NakedDhcpv4Srv> srv;
     ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
     subnet_->setSiaddr(IOAddress("192.0.2.123"));
 
     Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
     dis->setRemoteAddr(IOAddress("192.0.2.1"));
+    dis->setIface("eth0");
     OptionPtr clientid = generateClientId();
     dis->addOption(clientid);
 
@@ -1395,7 +1515,7 @@ TEST_F(Dhcpv4SrvTest, siaddr) {
 // specific value and returned in server messages. There's also similar test for
 // checking parser only configuration, see Dhcp4ParserTest.nextServerOverride in
 // config_parser_unittest.cc.
-TEST_F(Dhcpv4SrvTest, nextServerOverride) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, nextServerOverride) {
 
     NakedDhcpv4Srv srv(0);
 
@@ -1422,6 +1542,7 @@ TEST_F(Dhcpv4SrvTest, nextServerOverride) {
 
     Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
     dis->setRemoteAddr(IOAddress("192.0.2.1"));
+    dis->setIface("eth0");
     OptionPtr clientid = generateClientId();
     dis->addOption(clientid);
 
@@ -1437,7 +1558,7 @@ TEST_F(Dhcpv4SrvTest, nextServerOverride) {
 // when there is no specific value defined in subnet and returned to the client
 // properly. There's also similar test for checking parser only configuration,
 // see Dhcp4ParserTest.nextServerGlobal in config_parser_unittest.cc.
-TEST_F(Dhcpv4SrvTest, nextServerGlobal) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, nextServerGlobal) {
 
     NakedDhcpv4Srv srv(0);
 
@@ -1463,6 +1584,7 @@ TEST_F(Dhcpv4SrvTest, nextServerGlobal) {
 
     Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
     dis->setRemoteAddr(IOAddress("192.0.2.1"));
+    dis->setIface("eth0");
     OptionPtr clientid = generateClientId();
     dis->addOption(clientid);
 
@@ -1499,7 +1621,7 @@ const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur "
 /// can't modify non-static members (for obvious reasons), so many
 /// fields are declared static. It is still better to keep them as
 /// one class rather than unrelated collection of global objects.
-class HooksDhcpv4SrvTest : public Dhcpv4SrvTest {
+class HooksDhcpv4SrvTest : public Dhcpv4SrvFakeIfaceTest {
 
 public:
 
@@ -1986,7 +2108,7 @@ TEST_F(HooksDhcpv4SrvTest, Buffer4ReceiveSimple) {
 
 // Checks if callouts installed on buffer4_receive is able to change
 // the values and the parameters are indeed used by the server.
-TEST_F(HooksDhcpv4SrvTest, buffer4RreceiveValueChange) {
+TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveValueChange) {
 
     // Install callback that modifies MAC addr of incoming packet
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2407,7 +2529,7 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectSimple) {
         "\"subnet4\": [ { "
         "    \"pool\": [ \"192.0.2.0/25\" ],"
         "    \"subnet\": \"192.0.2.0/24\", "
-        "    \"interface\": \"" + valid_iface_ + "\" "
+        "    \"interface\": \"eth0\" "
         " }, {"
         "    \"pool\": [ \"192.0.3.0/25\" ],"
         "    \"subnet\": \"192.0.3.0/24\" "
@@ -2426,7 +2548,7 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectSimple) {
     // Prepare discover packet. Server should select first subnet for it
     Pkt4Ptr sol = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
     sol->setRemoteAddr(IOAddress("192.0.2.1"));
-    sol->setIface(valid_iface_);
+    sol->setIface("eth0");
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
 
@@ -2473,7 +2595,7 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectChange) {
         "\"subnet4\": [ { "
         "    \"pool\": [ \"192.0.2.0/25\" ],"
         "    \"subnet\": \"192.0.2.0/24\", "
-        "    \"interface\": \"" + valid_iface_ + "\" "
+        "    \"interface\": \"eth0\" "
         " }, {"
         "    \"pool\": [ \"192.0.3.0/25\" ],"
         "    \"subnet\": \"192.0.3.0/24\" "
@@ -2492,7 +2614,7 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectChange) {
     // Prepare discover packet. Server should select first subnet for it
     Pkt4Ptr sol = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
     sol->setRemoteAddr(IOAddress("192.0.2.1"));
-    sol->setIface(valid_iface_);
+    sol->setIface("eth0");
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
 
@@ -2554,6 +2676,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4RenewSimple) {
     req->setRemoteAddr(IOAddress(addr));
     req->setYiaddr(addr);
     req->setCiaddr(addr); // client's address
+    req->setIface("eth0");
 
     req->addOption(clientid);
     req->addOption(srv_->getServerID());
@@ -2645,6 +2768,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4RenewSkip) {
     req->setRemoteAddr(IOAddress(addr));
     req->setYiaddr(addr);
     req->setCiaddr(addr); // client's address
+    req->setIface("eth0");
 
     req->addOption(clientid);
     req->addOption(srv_->getServerID());
@@ -2816,7 +2940,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSkip) {
 }
 
 // Checks if server is able to handle a relayed traffic from DOCSIS3.0 modems
-TEST_F(Dhcpv4SrvTest, docsisVendorOptionsParse) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, docsisVendorOptionsParse) {
 
     // Let's get a traffic capture from DOCSIS3.0 modem
     Pkt4Ptr dis = captureRelayedDiscover();
@@ -2840,7 +2964,7 @@ TEST_F(Dhcpv4SrvTest, docsisVendorOptionsParse) {
 }
 
 // Checks if server is able to parse incoming docsis option and extract suboption 1 (docsis ORO)
-TEST_F(Dhcpv4SrvTest, docsisVendorORO) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, docsisVendorORO) {
 
     // Let's get a traffic capture from DOCSIS3.0 modem
     Pkt4Ptr dis = captureRelayedDiscover();
@@ -2862,7 +2986,7 @@ TEST_F(Dhcpv4SrvTest, docsisVendorORO) {
 
 // This test checks if Option Request Option (ORO) in docsis (vendor-id=4491)
 // vendor options is parsed correctly and the requested options are actually assigned.
-TEST_F(Dhcpv4SrvTest, vendorOptionsORO) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, vendorOptionsORO) {
 
     NakedDhcpv4Srv srv(0);
 
@@ -2883,7 +3007,7 @@ TEST_F(Dhcpv4SrvTest, vendorOptionsORO) {
         "    \"rebind-timer\": 2000, "
         "    \"renew-timer\": 1000, "
         "    \"valid-lifetime\": 4000,"
-        "    \"interface\": \"" + valid_iface_ + "\" "
+        "    \"interface\": \"eth0\" "
         " } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -2895,10 +3019,14 @@ TEST_F(Dhcpv4SrvTest, vendorOptionsORO) {
     ASSERT_EQ(0, rcode_);
 
     boost::shared_ptr<Pkt4> dis(new Pkt4(DHCPDISCOVER, 1234));
-    // Set the giaddr to non-zero address as if it was relayed.
+    // Set the giaddr and hops to non-zero address as if it was relayed.
     dis->setGiaddr(IOAddress("192.0.2.1"));
+    dis->setHops(1);
+
     OptionPtr clientid = generateClientId();
     dis->addOption(clientid);
+    // Set interface. It is required by the server to generate server id.
+    dis->setIface("eth0");
 
     // Pass it to the server and get an advertise
     Pkt4Ptr offer = srv.processDiscover(dis);
@@ -2946,7 +3074,7 @@ TEST_F(Dhcpv4SrvTest, vendorOptionsORO) {
 
 // Test checks whether it is possible to use option definitions defined in
 // src/lib/dhcp/docsis3_option_defs.h.
-TEST_F(Dhcpv4SrvTest, vendorOptionsDocsisDefinitions) {
+TEST_F(Dhcpv4SrvFakeIfaceTest, vendorOptionsDocsisDefinitions) {
     ConstElementPtr x;
     string config_prefix = "{ \"interfaces\": [ \"all\" ],"
         "\"rebind-timer\": 2000, "

+ 91 - 45
src/bin/dhcp4/tests/dhcp4_test_utils.cc

@@ -55,16 +55,6 @@ Dhcpv4SrvTest::Dhcpv4SrvTest()
 
     // 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();
 }
 
 Dhcpv4SrvTest::~Dhcpv4SrvTest() {
@@ -325,9 +315,6 @@ Lease4Ptr Dhcpv4SrvTest::checkLease(const Pkt4Ptr& rsp,
     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 Dhcpv4SrvTest::checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid) {
     // Check that server included its server-id
     OptionPtr opt = rsp->getOption(DHO_DHCP_SERVER_IDENTIFIER);
@@ -396,8 +383,88 @@ Dhcpv4SrvTest::createPacketFromBuffer(const Pkt4Ptr& src_pkt,
     return (::testing::AssertionSuccess());
 }
 
+void Dhcpv4SrvTest::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();
+    }
+
+}
+
+Dhcpv4SrvFakeIfaceTest::Dhcpv4SrvFakeIfaceTest()
+: Dhcpv4SrvTest(), current_pkt_filter_() {
+    // Remove current interface configuration. Instead we want to add
+    // a couple of fake interfaces.
+    IfaceMgr& ifacemgr = IfaceMgr::instance();
+    ifacemgr.closeSockets();
+    ifacemgr.clearIfaces();
+
+    // Add fake interfaces.
+    ifacemgr.addInterface(createIface("lo", 0, "127.0.0.1"));
+    ifacemgr.addInterface(createIface("eth0", 1, "192.0.3.1"));
+    ifacemgr.addInterface(createIface("eth1", 2, "10.0.0.1"));
+
+    // In order to use fake interfaces we have to supply the custom
+    // packet filtering class, which can mimic opening sockets on
+    // fake interafaces.
+    current_pkt_filter_.reset(new PktFilterTest());
+    ifacemgr.setPacketFilter(current_pkt_filter_);
+    ifacemgr.openSockets4();
+}
+
+void
+Dhcpv4SrvFakeIfaceTest::TearDown() {
+    // The base class function restores the original packet filtering class.
+    Dhcpv4SrvTest::TearDown();
+    // The base class however, doesn't re-detect real interfaces.
+    try {
+        IfaceMgr::instance().clearIfaces();
+        IfaceMgr::instance().detectIfaces();
 
-void Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
+    } catch (const Exception& ex) {
+        FAIL() << "Failed to restore interface configuration after using"
+            " fake interfaces";
+    }
+}
+
+Iface
+Dhcpv4SrvFakeIfaceTest::createIface(const std::string& name, const int ifindex,
+                                    const std::string& addr) {
+    Iface iface(name, ifindex);
+    iface.addAddress(IOAddress(addr));
+    if (name == "lo") {
+        iface.flag_loopback_ = true;
+    }
+    iface.flag_up_ = true;
+    iface.flag_running_ = true;
+    iface.inactive4_ = false;
+    return (iface);
+}
+
+void
+Dhcpv4SrvFakeIfaceTest::testDiscoverRequest(const uint8_t msg_type) {
     // Create an instance of the tested class.
     boost::scoped_ptr<NakedDhcpv4Srv> srv(new NakedDhcpv4Srv(0));
 
@@ -432,8 +499,9 @@ void Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
     req->setLocalHWAddr(1, 6, mac);
     // Set target IP address.
     req->setRemoteAddr(IOAddress("192.0.2.55"));
-    // Set relay address.
+    // Set relay address and hops.
     req->setGiaddr(IOAddress("192.0.2.10"));
+    req->setHops(1);
 
     // We are going to test that certain options are returned
     // in the response message when requested using 'Parameter
@@ -446,6 +514,8 @@ void Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
     // which was parsed from its wire format.
     Pkt4Ptr received;
     ASSERT_TRUE(createPacketFromBuffer(req, received));
+    // Set interface. It is required for the server to generate server id.
+    received->setIface("eth0");
     if (msg_type == DHCPDISCOVER) {
         ASSERT_NO_THROW(
             rsp = srv->processDiscover(received);
@@ -479,6 +549,9 @@ void Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
     ASSERT_TRUE(createPacketFromBuffer(req, received));
     ASSERT_TRUE(received->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
 
+    // Set interface. It is required for the server to generate server id.
+    received->setIface("eth0");
+
     if (msg_type == DHCPDISCOVER) {
         ASSERT_NO_THROW(rsp = srv->processDiscover(received));
 
@@ -512,6 +585,9 @@ void Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
     ASSERT_TRUE(createPacketFromBuffer(req, received));
     ASSERT_TRUE(received->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
 
+    // Set interface. It is required for the server to generate server id.
+    received->setIface("eth0");
+
     if (msg_type == DHCPDISCOVER) {
         ASSERT_NO_THROW(rsp = srv->processDiscover(received));
         // Should return non-NULL packet.
@@ -532,36 +608,6 @@ void Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
     EXPECT_TRUE(noBasicOptions(rsp));
 }
 
-/// @brief This function cleans up after the test.
-void Dhcpv4SrvTest::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();
-    }
-}
-
 }; // end of isc::dhcp::test namespace
 }; // end of isc::dhcp namespace
 }; // end of isc namespace

+ 89 - 21
src/bin/dhcp4/tests/dhcp4_test_utils.h

@@ -21,6 +21,7 @@
 
 #include <gtest/gtest.h>
 #include <dhcp/iface_mgr.h>
+#include <dhcp/option4_addrlst.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt_filter.h>
 #include <dhcp/pkt_filter_inet.h>
@@ -31,6 +32,8 @@
 #include <config/ccsession.h>
 #include <list>
 
+#include <boost/shared_ptr.hpp>
+
 namespace isc {
 namespace dhcp {
 namespace test {
@@ -45,11 +48,18 @@ namespace test {
 class PktFilterTest : public PktFilter {
 public:
 
+    /// @brief Constructor.
+    ///
+    /// Sets the 'direct response' capability to true.
+    PktFilterTest()
+        : direct_resp_supported_(true) {
+    }
+
     /// @brief Reports 'direct response' capability.
     ///
     /// @return always true.
     virtual bool isDirectResponseSupported() const {
-        return (true);
+        return (direct_resp_supported_);
     }
 
     /// Does nothing.
@@ -69,8 +79,14 @@ public:
         return (0);
     }
 
+    /// @brief Holds a boolean value which indicates whether direct response
+    /// capability is supported (true) or not (false).
+    bool direct_resp_supported_;
+
 };
 
+typedef boost::shared_ptr<PktFilterTest> PktFilterTestPtr;
+
 class Dhcpv4SrvTest : public ::testing::Test {
 public:
 
@@ -237,24 +253,12 @@ public:
     createPacketFromBuffer(const Pkt4Ptr& src_pkt,
                            Pkt4Ptr& dst_pkt);
 
+
     /// @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
-    ///
-    /// This test verifies that the Parameter Request List option is handled
-    /// correctly, i.e. it checks that certain options are present in the
-    /// server's response when they are requested and that they are not present
-    /// when they are not requested or NAK occurs.
-    ///
-    /// @todo We need an additional test for PRL option using real traffic
-    /// capture.
-    ///
-    /// @param msg_type DHCPDISCOVER or DHCPREQUEST
-    void testDiscoverRequest(const uint8_t msg_type);
-
     /// @brief This function cleans up after the test.
     virtual void TearDown();
 
@@ -271,8 +275,62 @@ public:
 
     isc::data::ConstElementPtr comment_;
 
-    // Name of a valid network interface
-    std::string valid_iface_;
+};
+
+/// @brief Test fixture class to be used for tests which require fake
+/// interfaces.
+///
+/// The DHCPv4 server must always append the server identifier to its response.
+/// The server identifier is typically an IP address assigned to the interface
+/// on which the query has been received. The DHCPv4 server uses IfaceMgr to
+/// check this address. In order to test this functionality, a set of interfaces
+/// must be known to the test. This test fixture class creates a set of well
+/// known (fake) interfaces which can be assigned to the test DHCPv4 messages
+/// so as the response (including server identifier) can be validated.
+/// The real interfaces are removed from the IfaceMgr in the constructor and
+/// they are re-assigned in the destructor.
+class Dhcpv4SrvFakeIfaceTest : public Dhcpv4SrvTest {
+public:
+    /// @brief Constructor.
+    ///
+    /// Creates a set of fake interfaces:
+    /// - lo, index: 0, address: 127.0.0.1
+    /// - eth0, index: 1, address: 192.0.3.1
+    /// - eth1, index: 2, address: 10.0.0.1
+    ///
+    /// These interfaces replace the real interfaces detected by the IfaceMgr.
+    Dhcpv4SrvFakeIfaceTest();
+
+    /// @brief Restores the original interface configuration.
+    virtual void TearDown();
+
+    /// @brief Creates an instance of the interface.
+    ///
+    /// @param name Name of the interface.
+    /// @param ifindex Index of the interface.
+    /// @param addr IP address assigned to the interface, represented as string.
+    ///
+    /// @return Iface Instance of the interface.
+    static Iface createIface(const std::string& name, const int ifindex,
+                             const std::string& addr);
+
+    /// @brief Tests if Discover or Request message is processed correctly
+    ///
+    /// This test verifies that the Parameter Request List option is handled
+    /// correctly, i.e. it checks that certain options are present in the
+    /// server's response when they are requested and that they are not present
+    /// when they are not requested or NAK occurs.
+    ///
+    /// @todo We need an additional test for PRL option using real traffic
+    /// capture.
+    ///
+    /// @param msg_type DHCPDISCOVER or DHCPREQUEST
+    void testDiscoverRequest(const uint8_t msg_type);
+
+    /// @brief Holds a pointer to the packet filter object currently used
+    /// by the IfaceMgr.
+    PktFilterTestPtr current_pkt_filter_;
+
 };
 
 /// @brief "Naked" DHCPv4 server, exposes internal fields
@@ -306,6 +364,15 @@ public:
     /// that sockets should not be opened.
     NakedDhcpv4Srv(uint16_t port = 0)
         : Dhcpv4Srv(port, "type=memfile", false, false) {
+        // Create fixed server id.
+        server_id_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
+                                            asiolink::IOAddress("192.0.3.1")));
+    }
+
+    /// @brief Returns fixed server identifier assigned to the naked server
+    /// instance.
+    OptionPtr getServerID() const {
+        return (server_id_);
     }
 
     /// @brief fakes packet reception
@@ -341,12 +408,16 @@ public:
     ///
     /// See fake_received_ field for description
     void fakeReceive(const Pkt4Ptr& pkt) {
+        pkt->setIface("eth0");
         fake_received_.push_back(pkt);
     }
 
     virtual ~NakedDhcpv4Srv() {
     }
 
+    /// @brief Dummy server identifier option used by various tests.
+    OptionPtr server_id_;
+
     /// @brief packets we pretend to receive
     ///
     /// Instead of setting up sockets on interfaces that change between OSes, it
@@ -357,7 +428,8 @@ public:
 
     std::list<Pkt4Ptr> fake_sent_;
 
-    using Dhcpv4Srv::adjustRemoteAddr;
+    using Dhcpv4Srv::adjustIfaceData;
+    using Dhcpv4Srv::appendServerID;
     using Dhcpv4Srv::processDiscover;
     using Dhcpv4Srv::processRequest;
     using Dhcpv4Srv::processRelease;
@@ -366,10 +438,6 @@ public:
     using Dhcpv4Srv::processClientName;
     using Dhcpv4Srv::computeDhcid;
     using Dhcpv4Srv::createNameChangeRequests;
-    using Dhcpv4Srv::getServerID;
-    using Dhcpv4Srv::loadServerID;
-    using Dhcpv4Srv::generateServerID;
-    using Dhcpv4Srv::writeServerID;
     using Dhcpv4Srv::sanityCheck;
     using Dhcpv4Srv::srvidToString;
     using Dhcpv4Srv::unpackOptions;

+ 15 - 3
src/bin/dhcp4/tests/fqdn_unittest.cc

@@ -30,9 +30,9 @@ using namespace isc::dhcp_ddns;
 
 namespace {
 
-class NameDhcpv4SrvTest : public Dhcpv4SrvTest {
+class NameDhcpv4SrvTest : public Dhcpv4SrvFakeIfaceTest {
 public:
-    NameDhcpv4SrvTest() : Dhcpv4SrvTest() {
+    NameDhcpv4SrvTest() : Dhcpv4SrvFakeIfaceTest() {
         srv_ = new NakedDhcpv4Srv(0);
     }
     virtual ~NameDhcpv4SrvTest() {
@@ -110,6 +110,7 @@ public:
                                 const bool include_clientid = true) {
         Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(msg_type, 1234));
         pkt->setRemoteAddr(IOAddress("192.0.2.3"));
+        pkt->setIface("eth0");
         // For DISCOVER we don't include server id, because client broadcasts
         // the message to all servers.
         if (msg_type != DHCPDISCOVER) {
@@ -580,7 +581,6 @@ TEST_F(NameDhcpv4SrvTest, processDiscover) {
 
     Pkt4Ptr reply;
     ASSERT_NO_THROW(reply = srv_->processDiscover(req));
-
     checkResponse(reply, DHCPOFFER, 1234);
 
     EXPECT_TRUE(srv_->name_change_reqs_.empty());
@@ -615,6 +615,9 @@ TEST_F(NameDhcpv4SrvTest, processRequestFqdnEmptyDomainName) {
 // to it when Hostname option carries the top level domain-name.
 TEST_F(NameDhcpv4SrvTest, processRequestEmptyHostname) {
     Pkt4Ptr req = generatePktWithHostname(DHCPREQUEST, ".");
+    // Set interface for the incoming packet. The server requires it to
+    // generate client id.
+    req->setIface("eth0");
 
     Pkt4Ptr reply;
     ASSERT_NO_THROW(reply = srv_->processRequest(req));
@@ -691,6 +694,11 @@ TEST_F(NameDhcpv4SrvTest, processTwoRequestsFqdn) {
 TEST_F(NameDhcpv4SrvTest, processTwoRequestsHostname) {
     Pkt4Ptr req1 = generatePktWithHostname(DHCPREQUEST, "myhost.example.com.");
 
+    // Set interface for the incoming packet. The server requires it to
+    // generate client id.
+    req1->setIface("eth0");
+
+
     Pkt4Ptr reply;
     ASSERT_NO_THROW(reply = srv_->processRequest(req1));
 
@@ -709,6 +717,10 @@ TEST_F(NameDhcpv4SrvTest, processTwoRequestsHostname) {
     // another one to add new entry with updated domain-name.
     Pkt4Ptr req2 = generatePktWithHostname(DHCPREQUEST, "otherhost");
 
+    // Set interface for the incoming packet. The server requires it to
+    // generate client id.
+    req2->setIface("eth0");
+
     ASSERT_NO_THROW(reply = srv_->processRequest(req2));
 
     checkResponse(reply, DHCPACK, 1234);

+ 13 - 7
src/lib/dhcp/iface_mgr.cc

@@ -325,6 +325,8 @@ void IfaceMgr::stubDetectIfaces() {
     addInterface(iface);
 }
 
+
+
 bool
 IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast,
                        IfaceMgrErrorMsgCallback error_handler) {
@@ -585,6 +587,11 @@ IfaceMgr::getIface(const std::string& ifname) {
     return (NULL); // not found
 }
 
+void
+IfaceMgr::clearIfaces() {
+    ifaces_.clear();
+}
+
 int IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr,
                          const uint16_t port, const bool receive_bcast,
                          const bool send_bcast) {
@@ -794,7 +801,7 @@ IfaceMgr::send(const Pkt4Ptr& pkt) {
     }
 
     // Assuming that packet filter is not NULL, because its modifier checks it.
-    return (packet_filter_->send(*iface, getSocket(*pkt), pkt));
+    return (packet_filter_->send(*iface, getSocket(*pkt).sockfd_, pkt));
 }
 
 
@@ -994,8 +1001,7 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
 uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6& pkt) {
     Iface* iface = getIface(pkt.getIface());
     if (iface == NULL) {
-        isc_throw(BadValue, "Tried to find socket for non-existent interface "
-                  << pkt.getIface());
+        isc_throw(BadValue, "Tried to find socket for non-existent interface");
     }
 
 
@@ -1048,18 +1054,18 @@ uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6& pkt) {
               << " does not have any suitable IPv6 sockets open.");
 }
 
-uint16_t IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
+SocketInfo
+IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
     Iface* iface = getIface(pkt.getIface());
     if (iface == NULL) {
-        isc_throw(BadValue, "Tried to find socket for non-existent interface "
-                  << pkt.getIface());
+        isc_throw(BadValue, "Tried to find socket for non-existent interface");
     }
 
     const Iface::SocketCollection& socket_collection = iface->getSockets();
     Iface::SocketCollection::const_iterator s;
     for (s = socket_collection.begin(); s != socket_collection.end(); ++s) {
         if (s->family_ == AF_INET) {
-            return (s->sockfd_);
+            return (*s);
         }
         /// TODO: Add more checks here later. If remote address is
         /// not link-local, we can't use link local bound socket

+ 20 - 14
src/lib/dhcp/iface_mgr.h

@@ -444,8 +444,7 @@ public:
     /// @return interface with requested name (or NULL if no such
     ///         interface is present)
     ///
-    Iface*
-    getIface(const std::string& ifname);
+    Iface* getIface(const std::string& ifname);
 
     /// @brief Returns container with all interfaces.
     ///
@@ -454,7 +453,22 @@ public:
     /// main() function completes, you should not worry much about this.
     ///
     /// @return container with all interfaces.
-    const IfaceCollection& getIfaces() { return ifaces_; }
+    const IfaceCollection& getIfaces() { return (ifaces_); }
+
+    /// @brief Removes detected interfaces.
+    ///
+    /// This method removes all detected interfaces. This method should be
+    /// used by unit tests to supply a custom set of interfaces. For example:
+    /// a unit test may create a pool of fake interfaces and use the custom
+    /// @c PktFilter class to mimic socket operation on these interfaces.
+    void clearIfaces();
+
+    /// @brief Detects network interfaces.
+    ///
+    /// This method will eventually detect available interfaces. For now
+    /// it offers stub implementation. First interface name and link-local
+    /// IPv6 address is read from interfaces.txt file.
+    void detectIfaces();
 
     /// @brief Return most suitable socket for transmitting specified IPv6 packet.
     ///
@@ -469,7 +483,7 @@ public:
     /// @return a socket descriptor
     uint16_t getSocket(const isc::dhcp::Pkt6& pkt);
 
-    /// @brief Return most suitable socket for transmitting specified IPv6 packet.
+    /// @brief Return most suitable socket for transmitting specified IPv4 packet.
     ///
     /// This method takes Pkt4 (see overloaded implementation that takes
     /// Pkt6) and chooses appropriate socket to send it. This method
@@ -479,8 +493,8 @@ public:
     ///
     /// @param pkt a packet to be transmitted
     ///
-    /// @return a socket descriptor
-    uint16_t getSocket(const isc::dhcp::Pkt4& pkt);
+    /// @return A structure describing a socket.
+    SocketInfo getSocket(const isc::dhcp::Pkt4& pkt);
 
     /// Debugging method that prints out all available interfaces.
     ///
@@ -870,14 +884,6 @@ protected:
     int openSocket6(Iface& iface, const isc::asiolink::IOAddress& addr,
                     uint16_t port, const bool join_multicast);
 
-    /// @brief Detects network interfaces.
-    ///
-    /// This method will eventually detect available interfaces. For now
-    /// it offers stub implementation. First interface name and link-local
-    /// IPv6 address is read from interfaces.txt file.
-    void
-    detectIfaces();
-
     /// @brief Stub implementation of network interface detection.
     ///
     /// This implementations reads a single line from interfaces.txt file

+ 20 - 0
src/lib/dhcp/pkt4.cc

@@ -463,6 +463,26 @@ Pkt4::updateTimestamp() {
     timestamp_ = boost::posix_time::microsec_clock::universal_time();
 }
 
+bool
+Pkt4::isRelayed() const {
+    static const IOAddress zero_addr("0.0.0.0");
+    // For non-relayed message both Giaddr and Hops are zero.
+    if (getGiaddr() == zero_addr && getHops() == 0) {
+        return (false);
+
+    // For relayed message, both Giaddr and Hops are non-zero.
+    } else if (getGiaddr() != zero_addr && getHops() > 0) {
+        return (true);
+    }
+    // In any other case, the packet is considered malformed.
+    isc_throw(isc::BadValue, "invalid combination of giaddr = "
+              << getGiaddr().toText() << " and hops = "
+              << static_cast<int>(getHops()) << ". Valid values"
+              " are: (giaddr = 0 and hops = 0) or (giaddr != 0 and"
+              "hops != 0)");
+
+}
+
 } // end of namespace isc::dhcp
 
 } // end of namespace isc

+ 18 - 0
src/lib/dhcp/pkt4.h

@@ -484,6 +484,24 @@ public:
     /// @return remote port
     uint16_t getRemotePort() const { return (remote_port_); }
 
+    /// @brief Checks if a DHCPv4 message has been relayed.
+    ///
+    /// This function returns a boolean value which indicates whether a DHCPv4
+    /// message has been relayed (if true is returned) or not (if false).
+    ///
+    /// This function uses a combination of Giaddr and Hops. It is expected that
+    /// if Giaddr is not 0, the Hops is greater than 0. In this case the message
+    /// is considered relayed. If Giaddr is 0, the Hops value must also be 0. In
+    /// this case the message is considered non-relayed. For any other
+    /// combination of Giaddr and Hops, an exception is thrown to indicate that
+    /// the message is malformed.
+    ///
+    /// @return Boolean value which indicates whether the message is relayed
+    /// (true) or non-relayed (false).
+    /// @throw isc::BadValue if invalid combination of Giaddr and Hops values is
+    /// found.
+    bool isRelayed() const;
+
     /// @brief Set callback function to be used to parse options.
     ///
     /// @param callback An instance of the callback function or NULL to

+ 0 - 20
src/lib/dhcp/pkt_filter_lpf.cc

@@ -258,26 +258,6 @@ PktFilterLPF::send(const Iface& iface, uint16_t sockfd, const Pkt4Ptr& pkt) {
     // are valid because they are checked by the function called.
     writeEthernetHeader(pkt, buf);
 
-    // This object represents broadcast address. We will compare the
-    // local packet address with it a few lines below. Having static
-    // variable guarantees that this object is created only once, not
-    // every time this function is called.
-    static const isc::asiolink::IOAddress bcast_addr("255.255.255.255");
-
-    // It is likely that the local address in pkt object is set to
-    // broadcast address. This is the case if server received the
-    // client's packet on broadcast address. Therefore, we need to
-    // correct it here and assign the actual source address.
-    if (pkt->getLocalAddr() == bcast_addr) {
-        const Iface::SocketCollection& sockets = iface.getSockets();
-        for (Iface::SocketCollection::const_iterator it = sockets.begin();
-             it != sockets.end(); ++it) {
-            if (sockfd == it->sockfd_) {
-                pkt->setLocalAddr(it->addr_);
-            }
-        }
-    }
-
     // IP and UDP header
     writeIpUdpHeader(pkt, buf);
 

+ 21 - 2
src/lib/dhcp/tests/iface_mgr_unittest.cc

@@ -577,6 +577,25 @@ TEST_F(IfaceMgrTest, getIface) {
     EXPECT_EQ(static_cast<void*>(NULL), ifacemgr->getIface("wifi15") );
 }
 
+TEST_F(IfaceMgrTest, clearIfaces) {
+    NakedIfaceMgr ifacemgr;
+    // Create a set of fake interfaces. At the same time, remove the actual
+    // interfaces that have been detected by the IfaceMgr.
+    ifacemgr.createIfaces();
+
+    ASSERT_GT(ifacemgr.countIfaces(), 0);
+
+    boost::shared_ptr<TestPktFilter> custom_packet_filter(new TestPktFilter());
+    ASSERT_TRUE(custom_packet_filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(custom_packet_filter));
+
+    ASSERT_NO_THROW(ifacemgr.openSockets4());
+
+    ifacemgr.clearIfaces();
+
+    EXPECT_EQ(0, ifacemgr.countIfaces());
+}
+
 TEST_F(IfaceMgrTest, receiveTimeout6) {
     using namespace boost::posix_time;
     std::cout << "Testing DHCPv6 packet reception timeouts."
@@ -1328,7 +1347,7 @@ TEST_F(IfaceMgrTest, socket4) {
     pkt.setIface(LOOPBACK);
 
     // Expect that we get the socket that we just opened.
-    EXPECT_EQ(socket1, ifacemgr->getSocket(pkt));
+    EXPECT_EQ(socket1, ifacemgr->getSocket(pkt).sockfd_);
 
     close(socket1);
 }
@@ -1963,7 +1982,7 @@ TEST_F(IfaceMgrTest, socketInfo) {
 
     // Socket info is set, packet has well defined interface. It should work.
     pkt4.setIface(LOOPBACK);
-    EXPECT_EQ(7, ifacemgr->getSocket(pkt4));
+    EXPECT_EQ(7, ifacemgr->getSocket(pkt4).sockfd_);
 
     EXPECT_NO_THROW(
         ifacemgr->getIface(LOOPBACK)->delSocket(7);

+ 23 - 0
src/lib/dhcp/tests/pkt4_unittest.cc

@@ -798,4 +798,27 @@ TEST_F(Pkt4Test, hwaddrSrcRemote) {
                            remote_addr->hwaddr_.begin()));
 }
 
+// This test verifies that the check for a message being relayed is correct.
+// It also checks that the exception is thrown if the combination of hops and
+// giaddr is invalid.
+TEST_F(Pkt4Test, isRelayed) {
+    Pkt4 pkt(DHCPDISCOVER, 1234);
+    // By default, the hops and giaddr should be 0.
+    ASSERT_EQ("0.0.0.0", pkt.getGiaddr().toText());
+    ASSERT_EQ(0, pkt.getHops());
+    // For hops = 0 and giaddr = 0, the message is non-relayed.
+    EXPECT_FALSE(pkt.isRelayed());
+    // Set giaddr but leave hops = 0. This should result in exception.
+    pkt.setGiaddr(IOAddress("10.0.0.1"));
+    EXPECT_THROW(pkt.isRelayed(), isc::BadValue);
+    // Set hops. Now both hops and giaddr is set. The message is relayed.
+    pkt.setHops(10);
+    EXPECT_TRUE(pkt.isRelayed());
+    // Set giaddr to 0. For hops being set to non-zero value the function
+    // should throw an exception.
+    pkt.setGiaddr(IOAddress("0.0.0.0"));
+    EXPECT_THROW(pkt.isRelayed(), isc::BadValue);
+
+}
+
 } // end of anonymous namespace