BIND 10 master, updated. c7a229f15089670d2bfde6e9f0530c30ce6f8cf8 [master] Merge branch 'trac3231'

BIND 10 source code commits bind10-changes at lists.isc.org
Fri Jan 10 18:47:47 UTC 2014


The branch, master has been updated
       via  c7a229f15089670d2bfde6e9f0530c30ce6f8cf8 (commit)
       via  861a9b6fbd5ecdb277345abbac98b8f8893e5db4 (commit)
       via  4d83e8f254cb653da6c102b73637b09e1764fe24 (commit)
       via  756857b003804fe4a869fd52cf837e4e70bf5f0b (commit)
       via  c994af9f1877b0a62b56cb34ac3d7e0528b2fa07 (commit)
       via  102f42af2ca75b47457bf57eec1d67166be17542 (commit)
       via  e6d538fd1e2e2a0669038277c6565a2b06a4956c (commit)
       via  2be3eca2864804c37f5eeb7c1fe9d6e562cf20e1 (commit)
       via  fa95cdf5ca22b4bcce2054a3d627fd4038075f3f (commit)
       via  c56b6109a0d9a63be9d1664feec644d383ba91d5 (commit)
       via  a1beae5fd28331a41e5947213b353fcfb97b2243 (commit)
      from  8e2ea0064bf2c96487a1aed89498c7c6b2410f1e (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit c7a229f15089670d2bfde6e9f0530c30ce6f8cf8
Merge: 8e2ea00 861a9b6
Author: Marcin Siodelski <marcin at isc.org>
Date:   Fri Jan 10 19:47:09 2014 +0100

    [master] Merge branch 'trac3231'
    
    Conflicts:
    	src/lib/dhcp/iface_mgr.cc

-----------------------------------------------------------------------

Summary of changes:
 doc/guide/bind10-guide.xml                |   19 +-
 src/bin/dhcp4/dhcp4_messages.mes          |   20 --
 src/bin/dhcp4/dhcp4_srv.cc                |  231 +++++----------
 src/bin/dhcp4/dhcp4_srv.h                 |  105 ++++---
 src/bin/dhcp4/tests/dhcp4_srv_unittest.cc |  450 ++++++++++++++++++-----------
 src/bin/dhcp4/tests/dhcp4_test_utils.cc   |  136 ++++++---
 src/bin/dhcp4/tests/dhcp4_test_utils.h    |  110 +++++--
 src/bin/dhcp4/tests/fqdn_unittest.cc      |   18 +-
 src/lib/dhcp/iface_mgr.cc                 |   20 +-
 src/lib/dhcp/iface_mgr.h                  |   34 ++-
 src/lib/dhcp/pkt4.cc                      |   20 ++
 src/lib/dhcp/pkt4.h                       |   18 ++
 src/lib/dhcp/pkt_filter_lpf.cc            |   20 --
 src/lib/dhcp/tests/iface_mgr_unittest.cc  |   23 +-
 src/lib/dhcp/tests/pkt4_unittest.cc       |   23 ++
 15 files changed, 749 insertions(+), 498 deletions(-)

-----------------------------------------------------------------------
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index d915e27..c0197c8 100644
--- a/doc/guide/bind10-guide.xml
+++ b/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>
 
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
index 9be599e..f561695 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/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.
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index 5e5572b..39dde03 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/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);
 }
 
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index 5b3ba3b..324ba4f 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/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_;
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
index 3787149..5142c70 100644
--- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+++ b/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, "
diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.cc b/src/bin/dhcp4/tests/dhcp4_test_utils.cc
index 8590927..8f96c1e 100644
--- a/src/bin/dhcp4/tests/dhcp4_test_utils.cc
+++ b/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
diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h
index 26ed87d..ebb6300 100644
--- a/src/bin/dhcp4/tests/dhcp4_test_utils.h
+++ b/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;
diff --git a/src/bin/dhcp4/tests/fqdn_unittest.cc b/src/bin/dhcp4/tests/fqdn_unittest.cc
index 20fe5d5..d3bf9ae 100644
--- a/src/bin/dhcp4/tests/fqdn_unittest.cc
+++ b/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);
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index 94c7ab9..f192482 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/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
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
index 0ac845d..568fc4d 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/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
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index 8a5b8ab..e9c2093 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/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
diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
index c8015cd..3d9e0ab 100644
--- a/src/lib/dhcp/pkt4.h
+++ b/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
diff --git a/src/lib/dhcp/pkt_filter_lpf.cc b/src/lib/dhcp/pkt_filter_lpf.cc
index 426d58c..06f1e19 100644
--- a/src/lib/dhcp/pkt_filter_lpf.cc
+++ b/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);
 
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index 472210b..2c2256f 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/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);
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index 72ffff7..b1bb852 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/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



More information about the bind10-changes mailing list