BIND 10 master, updated. c2d40e3d425f1e51647be6a717c4a97d7ca3c29c [master] Merge branch 'trac2902'

BIND 10 source code commits bind10-changes at lists.isc.org
Thu May 23 09:40:58 UTC 2013


The branch, master has been updated
       via  c2d40e3d425f1e51647be6a717c4a97d7ca3c29c (commit)
       via  454afa9877926214539d135f239035706bbec035 (commit)
       via  32931752aee5c1c61bf6758a3ad34a77eec04064 (commit)
       via  9a49846cb4456b17526c30799df03c9a06c8a318 (commit)
       via  18665064e7946e537931caccde60c180e9796127 (commit)
       via  74c286503e39fe3f70a52a0f8b7b7c206508a494 (commit)
       via  e7a483b3f53323d03b64f49385d173ceb595c39c (commit)
       via  5162bd7739ccfab83a0e81e44ba5e46314eae0cf (commit)
       via  9c2f41d0ce4250623b36966eb04698d55eac7509 (commit)
       via  97e41248dcb4ec3285d1e5f6d0f7d922952c63a6 (commit)
       via  8f8bfc92e622717d75684b8bd36181b525a22309 (commit)
       via  6e966c8f89b249a3f1c1a87201f24db656a06720 (commit)
       via  882b1c68c77a1e3e8261fd7a8ff9d654313963b2 (commit)
       via  4cef89808213a50c9da3aa469d6253ff939f254b (commit)
       via  8f4d5d803a3fad8019f8b4b638731d533c6dae16 (commit)
       via  4f47c644ff2ba7b3d062e51d4b7eef4f87417301 (commit)
       via  3c9911b4fde96eeec42bdf951ac6beb003eb0184 (commit)
       via  eecd53050bae78469caad312532cbf404244f1e2 (commit)
       via  cb324f11a8b5ac623b9c4ed491d7ec01551531cf (commit)
       via  d20b1f2ce6030846d6de80f6addf2c5ce53d564f (commit)
       via  b9be6a9538f9d11946c5291e7063aaa0db990261 (commit)
       via  39aa6489f86c5aa33e07da05c0f0be4ca2d9d9ae (commit)
       via  85d28a85b04c5c6a6905c6e018dc9a899afc4494 (commit)
       via  80f01bc608cbd779c76a6e5d5ca8dade8b37dbdd (commit)
       via  1ca6a3ac00cdf22e6fea1157bc887dd44e1f87a7 (commit)
       via  8b0cb0ded8035fcfb9cdd483ab3936db6690b97e (commit)
       via  fa9feb90e8588508daabccf397cc3fd4751a0e70 (commit)
       via  ae0620edd4c9cb477066e5f29f1d983b79554f4a (commit)
       via  757a5b14ef9101d99c47e7155a1b19b526d09389 (commit)
       via  c6302d0c4226e13753428df87e7d7864e86ed778 (commit)
       via  b91e4297ed4ba93adcb9af382f00c301876bf8de (commit)
       via  f5b9768aba32bb7673458eeeb14f0f56734427bc (commit)
       via  decee38f00f3efac436fa899bf6160fff787e318 (commit)
       via  35b62ac905d85ea7216a2d61b1cfb62a890bdea6 (commit)
       via  d8595ab6bd85996c00019da0b086fdb40374e1f9 (commit)
       via  edd0b0623481ea70b4af23a0cbd2bb4ef797d10f (commit)
       via  45e29ee0eeeced40dd047ec9da32da674124c55e (commit)
      from  9346c7a0f4d9dce50b673e66b5cc992f9b32d18b (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 c2d40e3d425f1e51647be6a717c4a97d7ca3c29c
Merge: 9346c7a 454afa9
Author: Marcin Siodelski <marcin at isc.org>
Date:   Thu May 23 11:38:16 2013 +0200

    [master] Merge branch 'trac2902'
    
    Conflicts:
    	src/lib/dhcp/Makefile.am

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

Summary of changes:
 src/bin/dhcp4/dhcp4_srv.cc                     |  118 +++++--
 src/bin/dhcp4/dhcp4_srv.h                      |   32 +-
 src/bin/dhcp4/tests/dhcp4_srv_unittest.cc      |  419 +++++++++++++++++++-----
 src/lib/dhcp/Makefile.am                       |    5 +
 src/lib/dhcp/hwaddr.h                          |    6 +-
 src/lib/dhcp/iface_mgr.cc                      |   87 ++++-
 src/lib/dhcp/iface_mgr.h                       |  154 ++++++---
 src/lib/dhcp/iface_mgr_bsd.cc                  |   14 +-
 src/lib/dhcp/iface_mgr_linux.cc                |   17 +-
 src/lib/dhcp/iface_mgr_sun.cc                  |   13 +-
 src/lib/dhcp/pkt4.cc                           |   50 ++-
 src/lib/dhcp/pkt4.h                            |  100 +++++-
 src/lib/dhcp/pkt_filter.h                      |   29 +-
 src/lib/dhcp/pkt_filter_inet.cc                |   22 +-
 src/lib/dhcp/pkt_filter_inet.h                 |   16 +-
 src/lib/dhcp/pkt_filter_lpf.cc                 |  243 +++++++++++++-
 src/lib/dhcp/pkt_filter_lpf.h                  |   15 +-
 src/lib/dhcp/protocol_util.cc                  |  243 ++++++++++++++
 src/lib/dhcp/protocol_util.h                   |  153 +++++++++
 src/lib/dhcp/tests/Makefile.am                 |    7 +
 src/lib/dhcp/tests/iface_mgr_unittest.cc       |  183 +++++++++--
 src/lib/dhcp/tests/pkt4_unittest.cc            |   45 +++
 src/lib/dhcp/tests/pkt_filter_inet_unittest.cc |  269 +++++++++++++++
 src/lib/dhcp/tests/pkt_filter_lpf_unittest.cc  |  293 +++++++++++++++++
 src/lib/dhcp/tests/protocol_util_unittest.cc   |  390 ++++++++++++++++++++++
 25 files changed, 2669 insertions(+), 254 deletions(-)
 create mode 100644 src/lib/dhcp/protocol_util.cc
 create mode 100644 src/lib/dhcp/protocol_util.h
 create mode 100644 src/lib/dhcp/tests/pkt_filter_inet_unittest.cc
 create mode 100644 src/lib/dhcp/tests/pkt_filter_lpf_unittest.cc
 create mode 100644 src/lib/dhcp/tests/protocol_util_unittest.cc

-----------------------------------------------------------------------
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index 6f119ed..01e7da7 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -57,12 +57,18 @@ 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) {
+Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
+                     const bool direct_response_desired) {
     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
-        IfaceMgr::instance();
+        // 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);
 
         if (port) {
             // open sockets only if port is non-zero. Port 0 is used
@@ -199,9 +205,9 @@ Dhcpv4Srv::run() {
             }
 
             if (rsp) {
-                if (rsp->getRemoteAddr().toText() == "0.0.0.0") {
-                    rsp->setRemoteAddr(query->getRemoteAddr());
-                }
+
+                adjustRemoteAddr(query, rsp);
+
                 if (!rsp->getHops()) {
                     rsp->setRemotePort(DHCP4_CLIENT_PORT);
                 } else {
@@ -352,19 +358,27 @@ Dhcpv4Srv::copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer) {
     // relay address
     answer->setGiaddr(question->getGiaddr());
 
-    if (question->getGiaddr().toText() != "0.0.0.0") {
-        // relayed traffic
-        answer->setRemoteAddr(question->getGiaddr());
-    } else {
-        // direct traffic
-        answer->setRemoteAddr(question->getRemoteAddr());
-    }
-
     // Let's copy client-id to response. See RFC6842.
     OptionPtr client_id = question->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
     if (client_id) {
         answer->addOption(client_id);
     }
+
+    // If src/dest HW addresses are used by the packet filtering class
+    // we need to copy them as well. There is a need to check that the
+    // address being set is not-NULL because an attempt to set the NULL
+    // HW would result in exception. If these values are not set, the
+    // the default HW addresses (zeroed) should be generated by the
+    // packet filtering class when creating Ethernet header for
+    // outgoing packet.
+    HWAddrPtr src_hw_addr = question->getLocalHWAddr();
+    if (src_hw_addr) {
+        answer->setLocalHWAddr(src_hw_addr);
+    }
+    HWAddrPtr dst_hw_addr = question->getRemoteHWAddr();
+    if (dst_hw_addr) {
+        answer->setRemoteHWAddr(dst_hw_addr);
+    }
 }
 
 void
@@ -517,28 +531,6 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
 
         answer->setYiaddr(lease->addr_);
 
-        // If remote address is not set, we are dealing with a directly
-        // connected client requesting new lease. We can send response to
-        // the address assigned in the lease, but first we have to make sure
-        // that IfaceMgr supports responding directly to the client when
-        // client doesn't have address assigned to its interface yet.
-        if (answer->getRemoteAddr().toText() == "0.0.0.0") {
-            if (IfaceMgr::instance().isDirectResponseSupported()) {
-                answer->setRemoteAddr(lease->addr_);
-            } else {
-                // Since IfaceMgr does not support direct responses to
-                // clients not having IP addresses, we have to send response
-                // to broadcast. We don't check whether the use_bcast flag
-                // was set in the constructor, because this flag is only used
-                // by unit tests to prevent opening broadcast sockets, as
-                // it requires root privileges. If this function is invoked by
-                // unit tests, we expect that it sets broadcast address if
-                // direct response is not supported, so as a test can verify
-                // function's behavior, regardless of the use_bcast flag's value.
-                answer->setRemoteAddr(IOAddress("255.255.255.255"));
-            }
-        }
-
         // IP Address Lease time (type 51)
         opt = OptionPtr(new Option(Option::V4, DHO_DHCP_LEASE_TIME));
         opt->setUint32(lease->valid_lft_);
@@ -573,6 +565,60 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
     }
 }
 
+void
+Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg) {
+    // 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
+    // be created only once.
+    static const IOAddress zero_addr("0.0.0.0");
+    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 giaddr is 0 but client set ciaddr, server should unicast the
+    // response to ciaddr.
+    } else if (question->getCiaddr() != zero_addr) {
+        msg->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);
+
+    // If yiaddr is set it means that we have created a lease for a client.
+    } else if (msg->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
+        // which doesn't have an address assigned. The other case when response
+        // must be broadcasted is when our server does not support responding
+        // 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);
+
+        // 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());
+
+        }
+
+    // In most cases, we should have the remote address found already. If we
+    // 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());
+
+    }
+}
+
+
 OptionPtr
 Dhcpv4Srv::getNetmaskOption(const Subnet4Ptr& subnet) {
     uint32_t netmask = getNetmask4(subnet->get().second);
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index bc8851e..f98233c 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -45,7 +45,7 @@ namespace dhcp {
 /// Dhcpv4Srv and other classes, see \ref dhcpv4Session.
 class Dhcpv4Srv : public boost::noncopyable {
 
-    public:
+public:
 
     /// @brief defines if certain option may, must or must not appear
     typedef enum {
@@ -61,15 +61,22 @@ class Dhcpv4Srv : public boost::noncopyable {
     /// network interaction. Will instantiate lease manager, and load
     /// old or create new DUID. It is possible to specify alternate
     /// port on which DHCPv4 server will listen on. That is mostly useful
-    /// for testing purposes.
+    /// for testing purposes. The Last two arguments of the constructor
+    /// should be left at default values for normal server operation.
+    /// They should be set to 'false' when creating an instance of this
+    /// class for unit testing because features they enable require
+    /// root privileges.
     ///
     /// @param port specifies port number to listen on
     /// @param dbconfig Lease manager configuration string.  The default
     ///        of the "memfile" manager is used for testing.
     /// @param use_bcast configure sockets to support broadcast messages.
+    /// @param direct_response_desired specifies if it is desired to
+    /// use direct V4 traffic.
     Dhcpv4Srv(uint16_t port = DHCP4_SERVER_PORT,
               const char* dbconfig = "type=memfile",
-              const bool use_bcast = true);
+              const bool use_bcast = true,
+              const bool direct_response_desired = true);
 
     /// @brief Destructor. Used during DHCPv4 service shutdown.
     ~Dhcpv4Srv();
@@ -218,6 +225,23 @@ protected:
     /// @param msg_type specifies message type
     void appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type);
 
+    /// @brief Sets remote addresses for outgoing packet.
+    ///
+    /// This method sets the local and remote addresses on outgoing packet.
+    /// The addresses being set depend on the following conditions:
+    /// - has incoming packet been relayed,
+    /// - is direct response to a client without address supported,
+    /// - type of the outgoing packet,
+    /// - broadcast flag set in the incoming packet.
+    ///
+    /// @warning This method does not check whether provided packet pointers
+    /// 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
     ///
     /// @return server-id option
@@ -272,7 +296,7 @@ protected:
     /// initiate server shutdown procedure.
     volatile bool shutdown_;
 
-    private:
+private:
 
     /// @brief Constructs netmask option based on subnet4
     /// @param subnet subnet for which the netmask will be calculated
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
index cabd8b7..c39c56a 100644
--- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -22,6 +22,8 @@
 #include <dhcp/option4_addrlst.h>
 #include <dhcp/option_custom.h>
 #include <dhcp/option_int_array.h>
+#include <dhcp/pkt_filter.h>
+#include <dhcp/pkt_filter_inet.h>
 #include <dhcp4/dhcp4_srv.h>
 #include <dhcp4/dhcp4_log.h>
 #include <dhcpsrv/cfgmgr.h>
@@ -50,14 +52,34 @@ public:
 
     /// @brief Constructor.
     ///
-    /// It disables configuration of broadcast options on
-    /// sockets that are opened by the Dhcpv4Srv constructor.
-    /// Setting broadcast options requires root privileges
-    /// which is not the case when running unit tests.
+    /// This constructor disables default modes of operation used by the
+    /// Dhcpv4Srv class:
+    /// - Send/receive broadcast messages through sockets on interfaces
+    /// which support broadcast traffic.
+    /// - Direct DHCPv4 traffic - communication with clients which do not
+    /// have IP address assigned yet.
+    ///
+    /// Enabling these modes requires root privilges so they must be
+    /// disabled for unit testing.
+    ///
+    /// Note, that disabling broadcast options on sockets does not impact
+    /// the operation of these tests because they use local loopback
+    /// interface which doesn't have broadcast capability anyway. It rather
+    /// prevents setting broadcast options on other (broadcast capable)
+    /// sockets which are opened on other interfaces in Dhcpv4Srv constructor.
+    ///
+    /// The Direct DHCPv4 Traffic capability can be disabled here because
+    /// it is tested with PktFilterLPFTest unittest. The tests which belong
+    /// to PktFilterLPFTest can be enabled on demand when root privileges can
+    /// be guaranteed.
+    ///
+    /// @param port port number to listen on; the default value 0 indicates
+    /// that sockets should not be opened.
     NakedDhcpv4Srv(uint16_t port = 0)
-        : Dhcpv4Srv(port, "type=memfile", false) {
+        : Dhcpv4Srv(port, "type=memfile", false, false) {
     }
 
+    using Dhcpv4Srv::adjustRemoteAddr;
     using Dhcpv4Srv::processDiscover;
     using Dhcpv4Srv::processRequest;
     using Dhcpv4Srv::processRelease;
@@ -73,6 +95,41 @@ public:
 
 static const char* SRVID_FILE = "server-id-test.txt";
 
+/// @brief Dummy Packet Filtering class.
+///
+/// This class reports capability to respond directly to the
+/// client which doesn't have address configured yet.
+///
+/// All packet and socket handling functions do nothing because
+/// they are not used in unit tests.
+class PktFilterTest : public PktFilter {
+public:
+
+    /// @brief Reports 'direct response' capability.
+    ///
+    /// @return always true.
+    virtual bool isDirectResponseSupported() const {
+        return (true);
+    }
+
+    /// Does nothing.
+    virtual int openSocket(const Iface&, const IOAddress&, const uint16_t,
+                           const bool, const bool) {
+        return (0);
+    }
+
+    /// Does nothing.
+    virtual Pkt4Ptr receive(const Iface&, const SocketInfo&) {
+        return Pkt4Ptr();
+    }
+
+    /// Does nothing.
+    virtual int send(const Iface&, uint16_t, const Pkt4Ptr&) {
+        return (0);
+    }
+
+};
+
 class Dhcpv4SrvTest : public ::testing::Test {
 public:
 
@@ -171,6 +228,11 @@ public:
         EXPECT_EQ(q->getIface(),  a->getIface());
         EXPECT_EQ(q->getIndex(),  a->getIndex());
         EXPECT_EQ(q->getGiaddr(), a->getGiaddr());
+        // When processing an incoming packet the remote address
+        // is copied as a src address, and the source address is
+        // copied as a remote address to the response.
+        EXPECT_TRUE(q->getLocalHWAddr() == a->getLocalHWAddr());
+        EXPECT_TRUE(q->getRemoteHWAddr() == a->getRemoteHWAddr());
 
         // Check that bare minimum of required options are there.
         // We don't check options requested by a client. Those
@@ -360,26 +422,43 @@ public:
     /// @brief Tests if Discover or Request message is processed correctly
     ///
     /// @param msg_type DHCPDISCOVER or DHCPREQUEST
-    /// @param client_addr client address
-    /// @param relay_addr relay address
-    void testDiscoverRequest(const uint8_t msg_type,
-                             const IOAddress& client_addr,
-                             const IOAddress& relay_addr) {
-
+    void testDiscoverRequest(const uint8_t msg_type) {
+        // Create an instance of the tested class.
         boost::scoped_ptr<NakedDhcpv4Srv> srv(new NakedDhcpv4Srv(0));
+
+        // Initialize the source HW address.
         vector<uint8_t> mac(6);
-        for (int i = 0; i < 6; i++) {
-            mac[i] = i*10;
+        for (int i = 0; i < 6; ++i) {
+            mac[i] = i * 10;
         }
-
+        // Initialized the destination HW address.
+        vector<uint8_t> dst_mac(6);
+        for (int i = 0; i < 6; ++i) {
+            dst_mac[i] = i * 20;
+        }
+        // Create a DHCP message. It will be used to simulate the
+        // incoming message.
         boost::shared_ptr<Pkt4> req(new Pkt4(msg_type, 1234));
+        // Create a response message. It will hold a reponse packet.
+        // Initially, set it to NULL.
         boost::shared_ptr<Pkt4> rsp;
-
+        // Set the name of the interface on which packet is received.
         req->setIface("eth0");
+        // Set the interface index. It is just a dummy value and will
+        // not be interpreted.
         req->setIndex(17);
+        // Set the target HW address. This value is normally used to
+        // construct the data link layer header.
+        req->setRemoteHWAddr(1, 6, dst_mac);
+        // Set the HW address. This value is set on DHCP level (in chaddr).
         req->setHWAddr(1, 6, mac);
-        req->setRemoteAddr(IOAddress(client_addr));
-        req->setGiaddr(relay_addr);
+        // Set local HW address. It is used to construct the data link layer
+        // header.
+        req->setLocalHWAddr(1, 6, mac);
+        // Set target IP address.
+        req->setRemoteAddr(IOAddress("192.0.2.55"));
+        // Set relay address.
+        req->setGiaddr(IOAddress("192.0.2.10"));
 
         // We are going to test that certain options are returned
         // in the response message when requested using 'Parameter
@@ -407,33 +486,6 @@ public:
 
         }
 
-        if (relay_addr.toText() != "0.0.0.0") {
-            // This is relayed message. It should be sent brsp to relay address.
-            EXPECT_EQ(req->getGiaddr().toText(),
-                      rsp->getRemoteAddr().toText());
-
-        } else if (client_addr.toText() != "0.0.0.0") {
-            // This is a message from a client having an IP address.
-            EXPECT_EQ(req->getRemoteAddr().toText(),
-                      rsp->getRemoteAddr().toText());
-
-        } else {
-            // This is a message from a client having no IP address yet.
-            // If IfaceMgr supports direct traffic the response should
-            // be sent to the new address assigned to the client.
-            if (IfaceMgr::instance().isDirectResponseSupported()) {
-                EXPECT_EQ(rsp->getYiaddr(),
-                          rsp->getRemoteAddr().toText());
-
-            // If direct response to the client having no IP address is
-            // not supported, response should go to broadcast.
-            } else {
-                EXPECT_EQ("255.255.255.255", rsp->getRemoteAddr().toText());
-
-            }
-
-        }
-
         messageCheck(req, rsp);
 
         // We did not request any options so these should not be present
@@ -471,12 +523,35 @@ public:
 
     }
 
-    ~Dhcpv4SrvTest() {
+    /// @brief This function cleans up after the test.
+    virtual void TearDown() {
+
         CfgMgr::instance().deleteSubnets4();
 
         // Let's clean up if there is such a file.
         unlink(SRVID_FILE);
-    };
+
+        // Close all open sockets.
+        IfaceMgr::instance().closeSockets();
+
+        // Some unit tests override the default packet filtering class, used
+        // by the IfaceMgr. The dummy class, called PktFilterTest, reports the
+        // capability to directly respond to the clients without IP address
+        // assigned. This capability is not supported by the default packet
+        // filtering class: PktFilterInet. Therefore setting the dummy class
+        // allows to test scenarios, when server responds to the broadcast address
+        // on client's request, despite having support for direct response.
+        // The following call restores the use of original packet filtering class
+        // after the test.
+        try {
+            IfaceMgr::instance().setPacketFilter(PktFilterPtr(new PktFilterInet()));
+
+        } catch (const Exception& ex) {
+            FAIL() << "Failed to restore the default (PktFilterInet) packet filtering"
+                   << " class after the test. Exception has been caught: "
+                   << ex.what();
+        }
+    }
 
     /// @brief A subnet used in most tests
     Subnet4Ptr subnet_;
@@ -494,20 +569,220 @@ 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)));
+    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)));
+        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());
 }
 
-// Verifies that DISCOVER received via relay can be processed correctly,
+// 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));
+
+    // 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.
+    req->setGiaddr(IOAddress("192.0.2.1"));
+    // 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);
+
+    // Create a response packet. Assume that the new lease have
+    // been created and new address allocated. This address is
+    // stored in yiaddr field.
+    boost::shared_ptr<Pkt4> resp(new Pkt4(DHCPOFFER, 1234));
+    resp->setYiaddr(IOAddress("192.0.2.100"));
+    // Clear the remote address.
+    resp->setRemoteAddr(IOAddress("0.0.0.0"));
+
+    // This function never throws.
+    ASSERT_NO_THROW(srv->adjustRemoteAddr(req, resp));
+
+    // Now the destination address should be relay's address.
+    EXPECT_EQ("192.0.2.1", resp->getRemoteAddr().toText());
+
+    // Let's do another test and set other fields: ciaddr and
+    // flags. By doing it, we want to make sure that the relay
+    // address will take precedence.
+    req->setGiaddr(IOAddress("192.0.2.50"));
+    req->setCiaddr(IOAddress("192.0.2.11"));
+    req->setFlags(Pkt4::FLAG_BROADCAST_MASK);
+
+    resp->setYiaddr(IOAddress("192.0.2.100"));
+    // Clear remote address.
+    resp->setRemoteAddr(IOAddress("0.0.0.0"));
+
+    ASSERT_NO_THROW(srv->adjustRemoteAddr(req, resp));
+
+    // Response should be sent back to the relay address.
+    EXPECT_EQ("192.0.2.50", resp->getRemoteAddr().toText());
+}
+
+// This test verifies that the destination address of the response message
+// 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));
+
+    // Create instance of the incoming packet.
+    boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
+
+    // Clear giaddr to simulate direct packet.
+    req->setGiaddr(IOAddress("0.0.0.0"));
+    // Set ciaddr to non-zero address. The response should be sent to this
+    // address as the client is in renewing or rebinding state (it is fully
+    // configured).
+    req->setCiaddr(IOAddress("192.0.2.15"));
+    // Let's configure broadcast flag. It should be ignored because
+    // we are responding directly to the client having an address
+    // and trying to extend his lease. Broadcast flag is only used
+    // when new lease is acquired and server must make a decision
+    // whether to unicast the response to the acquired address or
+    // broadcast it.
+    req->setFlags(Pkt4::FLAG_BROADCAST_MASK);
+
+    // Create a response.
+    boost::shared_ptr<Pkt4> resp(new Pkt4(DHCPOFFER, 1234));
+    // Let's extend the lease for the client in such a way that
+    // it will actually get different address. The response
+    // should not be sent to this address but rather to ciaddr
+    // as client still have ciaddr configured.
+    resp->setYiaddr(IOAddress("192.0.2.13"));
+    // Clear the remote address.
+    resp->setRemoteAddr(IOAddress("0.0.0.0"));
+
+    ASSERT_NO_THROW(srv->adjustRemoteAddr(req, resp));
+
+    // Check that server responds to ciaddr
+    EXPECT_EQ("192.0.2.15", resp->getRemoteAddr().toText());
+}
+
+// This test verifies that the destination address of the response message
+// is set correctly when giaddr and ciaddr is zeroed in the received message
+// and the new lease is acquired. The lease address is carried in the
+// response message in the yiaddr field. In this case destination address
+// 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));
+
+    // Create instance of the incoming packet.
+    boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
+
+    // Clear giaddr to simulate direct packet.
+    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"));
+
+    // Let's clear the broadcast flag.
+    req->setFlags(0);
+
+    // 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"));
+
+    // When running unit tests, the IfaceMgr is using the default Packet
+    // Filtering class, PktFilterInet. This class does not support direct
+    // responses to clients without address assigned. When giaddr and ciaddr
+    // 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));
+
+    // 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());
+
+    // 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()));
+
+    // Now we expect that the server will send its response to the
+    // address assigned for the client.
+    ASSERT_NO_THROW(srv->adjustRemoteAddr(req, resp));
+
+    EXPECT_EQ("192.0.2.13", resp->getRemoteAddr().toText());
+}
+
+// This test verifies that the destination address of the response message
+// is set to broadcast address when client set broadcast flag in its
+// 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));
+
+    // Create instance of the incoming packet.
+    boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
+
+    // Clear giaddr to simulate direct packet.
+    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"));
+
+    // Let's set the broadcast flag.
+    req->setFlags(Pkt4::FLAG_BROADCAST_MASK);
+
+    // 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"));
+
+    // 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));
+
+    // 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());
+}
+
+// Verifies that DISCOVER message can be processed correctly,
 // that the OFFER message generated in response is valid and
 // contains necessary options.
 //
@@ -515,29 +790,11 @@ TEST_F(Dhcpv4SrvTest, basic) {
 // are other tests that verify correctness of the allocation
 // engine. See DiscoverBasic, DiscoverHint, DiscoverNoClientId
 // and DiscoverInvalidHint.
-TEST_F(Dhcpv4SrvTest, processDiscoverRelay) {
-    testDiscoverRequest(DHCPDISCOVER,
-                        IOAddress("192.0.2.56"),
-                        IOAddress("192.0.2.67"));
-}
-
-// Verifies that the non-relayed DISCOVER is processed correctly when
-// client source address is specified.
-TEST_F(Dhcpv4SrvTest, processDiscoverNoRelay) {
-    testDiscoverRequest(DHCPDISCOVER,
-                        IOAddress("0.0.0.0"),
-                        IOAddress("192.0.2.67"));
+TEST_F(Dhcpv4SrvTest, processDiscover) {
+    testDiscoverRequest(DHCPDISCOVER);
 }
 
-// Verified that the non-relayed DISCOVER is processed correctly when
-// client source address is not specified.
-TEST_F(Dhcpv4SrvTest, processDiscoverNoClientAddr) {
-    testDiscoverRequest(DHCPDISCOVER,
-                        IOAddress("0.0.0.0"),
-                        IOAddress("0.0.0.0"));
-}
-
-// Verifies that REQUEST received via relay can be processed correctly,
+// Verifies that REQUEST message can be processed correctly,
 // that the OFFER message generated in response is valid and
 // contains necessary options.
 //
@@ -545,26 +802,8 @@ TEST_F(Dhcpv4SrvTest, processDiscoverNoClientAddr) {
 // are other tests that verify correctness of the allocation
 // engine. See DiscoverBasic, DiscoverHint, DiscoverNoClientId
 // and DiscoverInvalidHint.
-TEST_F(Dhcpv4SrvTest, processRequestRelay) {
-    testDiscoverRequest(DHCPREQUEST,
-                        IOAddress("192.0.2.56"),
-                        IOAddress("192.0.2.67"));
-}
-
-// Verifies that the non-relayed REQUEST is processed correctly when
-// client source address is specified.
-TEST_F(Dhcpv4SrvTest, processRequestNoRelay) {
-    testDiscoverRequest(DHCPREQUEST,
-                        IOAddress("0.0.0.0"),
-                        IOAddress("192.0.2.67"));
-}
-
-// Verified that the non-relayed REQUEST is processed correctly when
-// client source address is not specified.
-TEST_F(Dhcpv4SrvTest, processRequestNoClientAddr) {
-    testDiscoverRequest(DHCPREQUEST,
-                        IOAddress("0.0.0.0"),
-                        IOAddress("0.0.0.0"));
+TEST_F(Dhcpv4SrvTest, processRequest) {
+    testDiscoverRequest(DHCPREQUEST);
 }
 
 TEST_F(Dhcpv4SrvTest, processRelease) {
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index f9a2881..114c301 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -34,11 +34,16 @@ libb10_dhcp___la_SOURCES += option_data_types.cc option_data_types.h
 libb10_dhcp___la_SOURCES += option_definition.cc option_definition.h
 libb10_dhcp___la_SOURCES += option_space.cc option_space.h
 libb10_dhcp___la_SOURCES += option_string.cc option_string.h
+libb10_dhcp___la_SOURCES += protocol_util.cc protocol_util.h
 libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
 libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
 libb10_dhcp___la_SOURCES += pkt_filter.h
 libb10_dhcp___la_SOURCES += pkt_filter_inet.cc pkt_filter_inet.h
+
+if OS_LINUX
 libb10_dhcp___la_SOURCES += pkt_filter_lpf.cc pkt_filter_lpf.h
+endif
+
 libb10_dhcp___la_SOURCES += std_option_defs.h
 
 libb10_dhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
diff --git a/src/lib/dhcp/hwaddr.h b/src/lib/dhcp/hwaddr.h
index 13a16bf..848ad23 100644
--- a/src/lib/dhcp/hwaddr.h
+++ b/src/lib/dhcp/hwaddr.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -27,6 +27,10 @@ namespace dhcp {
 /// @brief Hardware type that represents information from DHCPv4 packet
 struct HWAddr {
 public:
+
+    /// @brief Size of an ethernet hardware address.
+    static const size_t ETHERNET_HWADDR_LEN = 6;
+
     /// @brief Maximum size of a hardware address.
     static const size_t MAX_HWADDR_LEN = 20;
 
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index 74f5fe8..1e97205 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -58,11 +58,44 @@ Iface::Iface(const std::string& name, int ifindex)
 
 void
 Iface::closeSockets() {
-    for (SocketCollection::iterator sock = sockets_.begin();
-         sock != sockets_.end(); ++sock) {
-        close(sock->sockfd_);
+    // Close IPv4 sockets.
+    closeSockets(AF_INET);
+    // Close IPv6 sockets.
+    closeSockets(AF_INET6);
+}
+
+void
+Iface::closeSockets(const uint16_t family) {
+    // Check that the correect 'family' value has been specified.
+    // The possible values are AF_INET or AF_INET6. Note that, in
+    // the current code they are used to differentiate that the
+    // socket is used to transmit IPv4 or IPv6 traffic. However,
+    // the actual family types of the sockets may be different,
+    // e.g. for LPF we are using raw sockets of AF_PACKET family.
+    //
+    // @todo Consider replacing the AF_INET and AF_INET6 with some
+    // enum which will not be confused with the actual socket type.
+    if ((family != AF_INET) && (family != AF_INET6)) {
+        isc_throw(BadValue, "Invalid socket family " << family
+                  << " specified when requested to close all sockets"
+                  << " which belong to this family");
+    }
+    // Search for the socket of the specific type.
+    SocketCollection::iterator sock = sockets_.begin();
+    while (sock != sockets_.end()) {
+        if (sock->family_ == family) {
+            // Close and delete the socket and move to the
+            // next one.
+            close(sock->sockfd_);
+            sockets_.erase(sock++);
+
+        } else {
+            // Different type of socket. Let's move
+            // to the next one.
+            ++sock;
+
+        }
     }
-    sockets_.clear();
 }
 
 std::string
@@ -150,6 +183,14 @@ void IfaceMgr::closeSockets() {
     }
 }
 
+void
+IfaceMgr::closeSockets(const uint16_t family) {
+    for (IfaceCollection::iterator iface = ifaces_.begin();
+         iface != ifaces_.end(); ++iface) {
+        iface->closeSockets(family);
+    }
+}
+
 IfaceMgr::~IfaceMgr() {
     // control_buf_ is deleted automatically (scoped_ptr)
     control_buf_len_ = 0;
@@ -157,6 +198,42 @@ IfaceMgr::~IfaceMgr() {
     closeSockets();
 }
 
+bool
+IfaceMgr::isDirectResponseSupported() const {
+    return (packet_filter_->isDirectResponseSupported());
+}
+
+void
+IfaceMgr::setPacketFilter(const boost::shared_ptr<PktFilter>& packet_filter) {
+    // Do not allow NULL pointer.
+    if (!packet_filter) {
+        isc_throw(InvalidPacketFilter, "NULL packet filter object specified");
+    }
+    // Different packet filters use different socket types. It does not make
+    // sense to allow the change of packet filter when there are IPv4 sockets
+    // open because they can't be used by the receive/send functions of the
+    // new packet filter. Below, we check that there are no open IPv4 sockets.
+    // If we find at least one, we have to fail. However, caller still has a
+    // chance to replace the packet filter if he closes sockets explicitly.
+    for (IfaceCollection::const_iterator iface = ifaces_.begin();
+         iface != ifaces_.end(); ++iface) {
+        const Iface::SocketCollection& sockets = iface->getSockets();
+        for (Iface::SocketCollection::const_iterator sock = sockets.begin();
+             sock != sockets.end(); ++sock) {
+            if (sock->family_ == AF_INET) {
+            // There is at least one socket open, so we have to fail.
+                isc_throw(PacketFilterChangeDenied,
+                          "it is not allowed to set new packet"
+                          << " filter when there are open IPv4 sockets - need"
+                          << " to close them first");
+            }
+        }
+    }
+    // Everything is fine, so replace packet filter.
+    packet_filter_ = packet_filter;
+}
+
+
 void IfaceMgr::stubDetectIfaces() {
     string ifaceName;
     const string v4addr("127.0.0.1"), v6addr("::1");
@@ -758,7 +835,7 @@ IfaceMgr::send(const Pkt4Ptr& pkt) {
 
     // Skip checking if packet filter is non-NULL because it has been
     // already checked when packet filter was set.
-    return (packet_filter_->send(getSocket(*pkt), pkt));
+    return (packet_filter_->send(*iface, getSocket(*pkt), pkt));
 }
 
 
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
index 2085b97..35b4dc0 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -39,10 +39,10 @@ public:
         isc::Exception(file, line, what) { };
 };
 
-/// @brief IfaceMgr exception thrown when invalid packet filter object specified.
-class InvalidPacketFilter : public Exception {
+/// @brief Exception thrown when it is not allowed to set new Packet Filter.
+class PacketFilterChangeDenied : public Exception {
 public:
-    InvalidPacketFilter(const char* file, size_t line, const char* what) :
+    PacketFilterChangeDenied(const char* file, size_t line, const char* what) :
         isc::Exception(file, line, what) { };
 };
 
@@ -88,7 +88,7 @@ struct SocketInfo {
 };
 
 
-/// @brief represents a single network interface
+/// @brief Represents a single network interface
 ///
 /// Iface structure represents network interface with all useful
 /// information, like name, interface index, MAC address and
@@ -96,13 +96,20 @@ struct SocketInfo {
 class Iface {
 public:
 
-    /// maximum MAC address length (Infiniband uses 20 bytes)
+    /// Maximum MAC address length (Infiniband uses 20 bytes)
     static const unsigned int MAX_MAC_LEN = 20;
 
-    /// type that defines list of addresses
+    /// Type that defines list of addresses
     typedef std::vector<isc::asiolink::IOAddress> AddressCollection;
 
-    /// type that holds a list of socket informations
+    /// @brief Type that holds a list of socket information.
+    ///
+    /// @warning The type of the container used here must guarantee
+    /// that the iterators do not invalidate when erase() is called.
+    /// This is because, the \ref closeSockets function removes
+    /// elements selectively by calling erase on the element to be
+    /// removed and further iterates through remaining elements.
+    ///
     /// @todo: Add SocketCollectionConstIter type
     typedef std::list<SocketInfo> SocketCollection;
 
@@ -117,6 +124,27 @@ public:
     /// @brief Closes all open sockets on interface.
     void closeSockets();
 
+    /// @brief Closes all IPv4 or IPv6 sockets.
+    ///
+    /// This function closes sockets of the specific 'type' and closes them.
+    /// The 'type' of the socket indicates whether it is used to send IPv4
+    /// or IPv6 packets. The allowed values of the parameter are AF_INET and
+    /// AF_INET6 for IPv4 and IPv6 packets respectively. It is important
+    /// to realize that the actual types of sockets may be different than
+    /// AF_INET for IPv4 packets. This is because, historically the IfaceMgr
+    /// always used AF_INET sockets for IPv4 traffic. This is no longer the
+    /// case when the Direct IPv4 traffic must be supported. In order to support
+    /// direct traffic, the IfaceMgr operates on raw sockets, e.g. AF_PACKET
+    /// family sockets on Linux.
+    ///
+    /// @todo Replace the AF_INET and AF_INET6 values with an enum
+    /// which will not be confused with the actual socket type.
+    ///
+    /// @param family type of the sockets to be closed (AF_INET or AF_INET6)
+    ///
+    /// @throw BadValue if family value is different than AF_INET or AF_INET6.
+    void closeSockets(const uint16_t family);
+
     /// @brief Returns full interface name as "ifname/ifindex" string.
     ///
     /// @return string with interface name
@@ -146,7 +174,7 @@ public:
 
     /// @brief Sets flag_*_ fields based on bitmask value returned by OS
     ///
-    /// Note: Implementation of this method is OS-dependent as bits have
+    /// @note Implementation of this method is OS-dependent as bits have
     /// different meaning on each OS.
     ///
     /// @param flags bitmask value returned by OS in interface detection
@@ -237,53 +265,53 @@ public:
     const SocketCollection& getSockets() const { return sockets_; }
 
 protected:
-    /// socket used to sending data
+    /// Socket used to send data.
     SocketCollection sockets_;
 
-    /// network interface name
+    /// Network interface name.
     std::string name_;
 
-    /// interface index (a value that uniquely indentifies an interface)
+    /// Interface index (a value that uniquely indentifies an interface).
     int ifindex_;
 
-    /// list of assigned addresses
+    /// List of assigned addresses.
     AddressCollection addrs_;
 
-    /// link-layer address
+    /// Link-layer address.
     uint8_t mac_[MAX_MAC_LEN];
 
-    /// length of link-layer address (usually 6)
+    /// Length of link-layer address (usually 6).
     size_t mac_len_;
 
-    /// hardware type
+    /// Hardware type.
     uint16_t hardware_type_;
 
 public:
     /// @todo: Make those fields protected once we start supporting more
     /// than just Linux
 
-    /// specifies if selected interface is loopback
+    /// Specifies if selected interface is loopback.
     bool flag_loopback_;
 
-    /// specifies if selected interface is up
+    /// Specifies if selected interface is up.
     bool flag_up_;
 
-    /// flag specifies if selected interface is running
-    /// (e.g. cable plugged in, wifi associated)
+    /// Flag specifies if selected interface is running
+    /// (e.g. cable plugged in, wifi associated).
     bool flag_running_;
 
-    /// flag specifies if selected interface is multicast capable
+    /// Flag specifies if selected interface is multicast capable.
     bool flag_multicast_;
 
-    /// flag specifies if selected interface is broadcast capable
+    /// Flag specifies if selected interface is broadcast capable.
     bool flag_broadcast_;
 
-    /// interface flags (this value is as is returned by OS,
-    /// it may mean different things on different OSes)
+    /// Interface flags (this value is as is returned by OS,
+    /// it may mean different things on different OSes).
     uint32_t flags_;
 };
 
-/// @brief handles network interfaces, transmission and reception
+/// @brief Handles network interfaces, transmission and reception.
 ///
 /// IfaceMgr is an interface manager class that detects available network
 /// interfaces, configured addresses, link-local addresses, and provides
@@ -291,7 +319,7 @@ public:
 ///
 class IfaceMgr : public boost::noncopyable {
 public:
-    /// defines callback used when commands are received over control session
+    /// Defines callback used when commands are received over control session.
     typedef void (*SessionCallback) (void);
 
     /// @brief Packet reception buffer size
@@ -307,7 +335,7 @@ public:
     //      2 maps (ifindex-indexed and name-indexed) and
     //      also hide it (make it public make tests easier for now)
 
-    /// type that holds a list of interfaces
+    /// Type that holds a list of interfaces.
     typedef std::list<Iface> IfaceCollection;
 
     /// IfaceMgr is a singleton class. This method returns reference
@@ -324,7 +352,7 @@ public:
     /// the client.
     ///
     /// @return true if direct response is supported.
-    bool isDirectResponseSupported();
+    bool isDirectResponseSupported() const;
 
     /// @brief Returns interface with specified interface index
     ///
@@ -380,11 +408,10 @@ public:
     /// @return a socket descriptor
     uint16_t getSocket(const isc::dhcp::Pkt4& pkt);
 
-    /// debugging method that prints out all available interfaces
+    /// Debugging method that prints out all available interfaces.
     ///
     /// @param out specifies stream to print list of interfaces to
-    void
-    printIfaces(std::ostream& out = std::cout);
+    void printIfaces(std::ostream& out = std::cout);
 
     /// @brief Sends an IPv6 packet.
     ///
@@ -542,10 +569,31 @@ public:
                       const bool use_bcast = true);
 
     /// @brief Closes all open sockets.
-    /// Is used in destructor, but also from Dhcpv4_srv and Dhcpv6_srv classes.
+    /// Is used in destructor, but also from Dhcpv4Srv and Dhcpv6Srv classes.
     void closeSockets();
 
-    /// @brief returns number of detected interfaces
+    /// @brief Closes all IPv4 or IPv6 sockets.
+    ///
+    /// This function closes sockets of the specific 'type' and closes them.
+    /// The 'type' of the socket indicates whether it is used to send IPv4
+    /// or IPv6 packets. The allowed values of the parameter are AF_INET and
+    /// AF_INET6 for IPv4 and IPv6 packets respectively. It is important
+    /// to realize that the actual types of sockets may be different than
+    /// AF_INET for IPv4 packets. This is because, historically the IfaceMgr
+    /// always used AF_INET sockets for IPv4 traffic. This is no longer the
+    /// case when the Direct IPv4 traffic must be supported. In order to support
+    /// direct traffic, the IfaceMgr operates on raw sockets, e.g. AF_PACKET
+    /// family sockets on Linux.
+    ///
+    /// @todo Replace the AF_INET and AF_INET6 values with an enum
+    /// which will not be confused with the actual socket type.
+    ///
+    /// @param family type of the sockets to be closed (AF_INET or AF_INET6)
+    ///
+    /// @throw BadValue if family value is different than AF_INET or AF_INET6.
+    void closeSockets(const uint16_t family);
+
+    /// @brief Returns number of detected interfaces.
     ///
     /// @return number of detected interfaces
     uint16_t countIfaces() { return ifaces_.size(); }
@@ -567,18 +615,34 @@ public:
     /// Packet Filters expose low-level functions handling sockets opening
     /// and sending/receiving packets through those sockets. This function
     /// sets custom Packet Filter (represented by a class derived from PktFilter)
-    /// to be used by IfaceMgr.
+    /// to be used by IfaceMgr. Note that there must be no IPv4 sockets open
+    /// when this function is called. Call closeSockets(AF_INET) to close
+    /// all hanging IPv4 sockets opened by the current packet filter object.
     ///
     /// @param packet_filter new packet filter to be used by IfaceMgr to send/receive
     /// packets and open sockets.
     ///
     /// @throw InvalidPacketFilter if provided packet filter object is NULL.
-    void setPacketFilter(const boost::shared_ptr<PktFilter>& packet_filter) {
-        if (!packet_filter) {
-            isc_throw(InvalidPacketFilter, "NULL packet filter object specified");
-        }
-        packet_filter_ = packet_filter;
-    }
+    /// @throw PacketFilterChangeDenied if there are open IPv4 sockets
+    void setPacketFilter(const PktFilterPtr& packet_filter);
+
+    /// @brief Set Packet Filter object to handle send/receive packets.
+    ///
+    /// This function sets Packet Filter object to be used by IfaceMgr,
+    /// appropriate for the current OS. Setting the argument to 'true'
+    /// indicates that function should set a packet filter class
+    /// which supports direct responses to clients having no address
+    /// assigned yet. Filters picked by this function will vary, depending
+    /// on the OS being used. There is no guarantee that there is an
+    /// implementation that supports this feature on a particular OS.
+    /// If there isn't, the PktFilterInet object will be set. If the
+    /// argument is set to 'false', PktFilterInet object instance will
+    /// be set as the Packet Filter regrdaless of the OS type.
+    ///
+    /// @param direct_response_desired specifies whether the Packet Filter
+    /// object being set should support direct traffic to the host
+    /// not having address assigned.
+    void setMatchingPacketFilter(const bool direct_response_desired = false);
 
     /// A value of socket descriptor representing "not specified" state.
     static const int INVALID_SOCKET = -1;
@@ -660,14 +724,14 @@ protected:
     //int recvsock_; // TODO: should be fd_set eventually, but we have only
     //int sendsock_; // 2 sockets for now. Will do for until next release
 
-    // we can't use the same socket, as receiving socket
+    // We can't use the same socket, as receiving socket
     // is bound to multicast address. And we all know what happens
     // to people who try to use multicast as source address.
 
-    /// length of the control_buf_ array
+    /// Length of the control_buf_ array
     size_t control_buf_len_;
 
-    /// control-buffer, used in transmission and reception
+    /// Control-buffer, used in transmission and reception.
     boost::scoped_array<char> control_buf_;
 
     /// @brief A wrapper for OS-specific operations before sending IPv4 packet
@@ -687,10 +751,10 @@ protected:
     /// @return true if successful, false otherwise
     bool os_receive4(struct msghdr& m, Pkt4Ptr& pkt);
 
-    /// socket descriptor of the session socket
+    /// Socket descriptor of the session socket.
     int session_socket_;
 
-    /// a callback that will be called when data arrives over session_socket_
+    /// A callback that will be called when data arrives over session_socket_.
     SessionCallback session_callback_;
 private:
 
@@ -737,7 +801,7 @@ private:
     /// families, e.g. AF_INET, AF_PACKET etc. Another possible type of
     /// Packet Filter is the one used for unit testing, which doesn't
     /// open sockets but rather mimics their behavior (mock object).
-    boost::shared_ptr<PktFilter> packet_filter_;
+    PktFilterPtr packet_filter_;
 };
 
 }; // namespace isc::dhcp
diff --git a/src/lib/dhcp/iface_mgr_bsd.cc b/src/lib/dhcp/iface_mgr_bsd.cc
index afd97bb..2293877 100644
--- a/src/lib/dhcp/iface_mgr_bsd.cc
+++ b/src/lib/dhcp/iface_mgr_bsd.cc
@@ -17,6 +17,7 @@
 #if defined(OS_BSD)
 
 #include <dhcp/iface_mgr.h>
+#include <dhcp/pkt_filter_inet.h>
 #include <exceptions/exceptions.h>
 
 using namespace std;
@@ -34,11 +35,6 @@ IfaceMgr::detectIfaces() {
     stubDetectIfaces();
 }
 
-bool
-IfaceMgr::isDirectResponseSupported() {
-    return (false);
-}
-
 void IfaceMgr::os_send4(struct msghdr& /*m*/,
                         boost::scoped_array<char>& /*control_buf*/,
                         size_t /*control_buf_len*/,
@@ -54,6 +50,14 @@ bool IfaceMgr::os_receive4(struct msghdr& /*m*/, Pkt4Ptr& /*pkt*/) {
   return (true); // pretend that we have everything set up for reception.
 }
 
+void
+IfaceMgr::setMatchingPacketFilter(const bool /* direct_response_desired */) {
+    // @todo Currently we ignore the preference to use direct traffic
+    // because it hasn't been implemented for BSD systems.
+    setPacketFilter(PktFilterPtr(new PktFilterInet()));
+}
+
+
 } // end of isc::dhcp namespace
 } // end of dhcp namespace
 
diff --git a/src/lib/dhcp/iface_mgr_linux.cc b/src/lib/dhcp/iface_mgr_linux.cc
index 71a32d8..f31c353 100644
--- a/src/lib/dhcp/iface_mgr_linux.cc
+++ b/src/lib/dhcp/iface_mgr_linux.cc
@@ -33,6 +33,8 @@
 
 #include <asiolink/io_address.h>
 #include <dhcp/iface_mgr.h>
+#include <dhcp/pkt_filter_inet.h>
+#include <dhcp/pkt_filter_lpf.h>
 #include <exceptions/exceptions.h>
 #include <util/io/sockaddr_util.h>
 
@@ -494,11 +496,6 @@ void IfaceMgr::detectIfaces() {
     nl.release_list(addr_info);
 }
 
-bool
-IfaceMgr::isDirectResponseSupported() {
-    return (false);
-}
-
 /// @brief sets flag_*_ fields.
 ///
 /// This implementation is OS-specific as bits have different meaning
@@ -515,6 +512,16 @@ void Iface::setFlags(uint32_t flags) {
     flag_broadcast_ = flags & IFF_BROADCAST;
 }
 
+void
+IfaceMgr::setMatchingPacketFilter(const bool direct_response_desired) {
+    if (direct_response_desired) {
+        setPacketFilter(PktFilterPtr(new PktFilterLPF()));
+
+    } else {
+        setPacketFilter(PktFilterPtr(new PktFilterInet()));
+
+    }
+}
 
 void IfaceMgr::os_send4(struct msghdr&, boost::scoped_array<char>&,
                         size_t, const Pkt4Ptr&) {
diff --git a/src/lib/dhcp/iface_mgr_sun.cc b/src/lib/dhcp/iface_mgr_sun.cc
index 1556b70..46c4a97 100644
--- a/src/lib/dhcp/iface_mgr_sun.cc
+++ b/src/lib/dhcp/iface_mgr_sun.cc
@@ -17,6 +17,7 @@
 #if defined(OS_SUN)
 
 #include <dhcp/iface_mgr.h>
+#include <dhcp/pkt_filter_inet.h>
 #include <exceptions/exceptions.h>
 
 using namespace std;
@@ -34,11 +35,6 @@ IfaceMgr::detectIfaces() {
     stubDetectIfaces();
 }
 
-bool
-IfaceMgr::isDirectResponseSupported() {
-    return (false);
-}
-
 void IfaceMgr::os_send4(struct msghdr& /*m*/,
                         boost::scoped_array<char>& /*control_buf*/,
                         size_t /*control_buf_len*/,
@@ -54,6 +50,13 @@ bool IfaceMgr::os_receive4(struct msghdr& /*m*/, Pkt4Ptr& /*pkt*/) {
   return (true); // pretend that we have everything set up for reception.
 }
 
+void
+IfaceMgr::setMatchingPacketFilter(const bool /* direct_response_desired */) {
+    // @todo Currently we ignore the preference to use direct traffic
+    // because it hasn't been implemented for Solaris.
+    setPacketFilter(PktFilterPtr(new PktFilterInet()));
+}
+
 } // end of isc::dhcp namespace
 } // end of dhcp namespace
 
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index 0592807..919235b 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -276,8 +276,24 @@ Pkt4::toText() {
 }
 
 void
-Pkt4::setHWAddr(uint8_t hType, uint8_t hlen,
+Pkt4::setHWAddr(uint8_t htype, uint8_t hlen,
                 const std::vector<uint8_t>& mac_addr) {
+    setHWAddrMember(htype, hlen, mac_addr, hwaddr_);
+}
+
+void
+Pkt4::setHWAddr(const HWAddrPtr& addr) {
+    if (!addr) {
+        isc_throw(BadValue, "Setting DHCPv4 chaddr field to NULL"
+                  << " is forbidden");
+    }
+    hwaddr_ = addr;
+}
+
+void
+Pkt4::setHWAddrMember(const uint8_t htype, const uint8_t hlen,
+                      const std::vector<uint8_t>& mac_addr,
+                      HWAddrPtr& hw_addr) {
     /// @todo Rewrite this once support for client-identifier option
     /// is implemented (ticket 1228?)
     if (hlen > MAX_CHADDR_LEN) {
@@ -288,15 +304,37 @@ Pkt4::setHWAddr(uint8_t hType, uint8_t hlen,
         isc_throw(OutOfRange, "Invalid HW Address specified");
     }
 
-    hwaddr_.reset(new HWAddr(mac_addr, hType));
+    hw_addr.reset(new HWAddr(mac_addr, htype));
 }
 
 void
-Pkt4::setHWAddr(const HWAddrPtr& addr) {
+Pkt4::setLocalHWAddr(const uint8_t htype, const uint8_t hlen,
+                      const std::vector<uint8_t>& mac_addr) {
+    setHWAddrMember(htype, hlen, mac_addr, local_hwaddr_);
+}
+
+void
+Pkt4::setLocalHWAddr(const HWAddrPtr& addr) {
     if (!addr) {
-        isc_throw(BadValue, "Setting hw address to NULL is forbidden");
+        isc_throw(BadValue, "Setting local HW address to NULL is"
+                  << " forbidden.");
     }
-    hwaddr_ = addr;
+    local_hwaddr_ = addr;
+}
+
+void
+Pkt4::setRemoteHWAddr(const uint8_t htype, const uint8_t hlen,
+                      const std::vector<uint8_t>& mac_addr) {
+    setHWAddrMember(htype, hlen, mac_addr, remote_hwaddr_);
+}
+
+void
+Pkt4::setRemoteHWAddr(const HWAddrPtr& addr) {
+    if (!addr) {
+        isc_throw(BadValue, "Setting remote HW address to NULL is"
+                  << " forbidden.");
+    }
+    remote_hwaddr_ = addr;
 }
 
 void
diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
index e7f33c5..d232745 100644
--- a/src/lib/dhcp/pkt4.h
+++ b/src/lib/dhcp/pkt4.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -47,6 +47,10 @@ public:
     /// specifies DHCPv4 packet header length (fixed part)
     const static size_t DHCPV4_PKT_HDR_LEN = 236;
 
+    /// Mask for the value of flags field in the DHCPv4 message
+    /// to check whether client requested broadcast response.
+    const static uint16_t FLAG_BROADCAST_MASK = 0x8000;
+
     /// Constructor, used in replying to a message.
     ///
     /// @param msg_type type of message (e.g. DHCPDISOVER=1)
@@ -267,10 +271,10 @@ public:
     ///
     /// Note: mac_addr must be a buffer of at least hlen bytes.
     ///
-    /// @param hType hardware type (will be sent in htype field)
+    /// @param htype hardware type (will be sent in htype field)
     /// @param hlen hardware length (will be sent in hlen field)
     /// @param mac_addr pointer to hardware address
-    void setHWAddr(uint8_t hType, uint8_t hlen,
+    void setHWAddr(uint8_t htype, uint8_t hlen,
                    const std::vector<uint8_t>& mac_addr);
 
     /// @brief Sets hardware address
@@ -363,6 +367,72 @@ public:
     /// @return interface index
     uint32_t getIndex() const { return (ifindex_); };
 
+    /// @brief Sets remote HW address.
+    ///
+    /// Sets the destination HW address for the outgoing packet
+    /// or source HW address for the incoming packet. When this
+    /// is an outgoing packet this address will be used to construct
+    /// the link layer header.
+    ///
+    /// @note mac_addr must be a buffer of at least hlen bytes.
+    ///
+    /// @param htype hardware type (will be sent in htype field)
+    /// @param hlen hardware length (will be sent in hlen field)
+    /// @param mac_addr pointer to hardware address
+    void setRemoteHWAddr(const uint8_t htype, const uint8_t hlen,
+                         const std::vector<uint8_t>& mac_addr);
+
+    /// @brief Sets remote HW address.
+    ///
+    /// Sets hardware address from an existing HWAddr structure.
+    /// The remote address is a destination address for outgoing
+    /// packet and source address for incoming packet. When this
+    /// is an outgoing packet, this address will be used to
+    /// construct the link layer header.
+    ///
+    /// @param addr structure representing HW address.
+    ///
+    /// @throw BadValue if addr is null
+    void setRemoteHWAddr(const HWAddrPtr& addr);
+
+    /// @brief Returns the remote HW address.
+    ///
+    /// @return remote HW address.
+    HWAddrPtr getRemoteHWAddr() const {
+        return (remote_hwaddr_);
+    }
+
+    /// @brief Sets local HW address.
+    ///
+    /// Sets the source HW address for the outgoing packet or
+    /// destination HW address for the incoming packet.
+    ///
+    /// @note mac_addr must be a buffer of at least hlen bytes.
+    ///
+    /// @param htype hardware type (will be sent in htype field)
+    /// @param hlen hardware length (will be sent in hlen field)
+    /// @param mac_addr pointer to hardware address
+    void setLocalHWAddr(const uint8_t htype, const uint8_t hlen,
+                        const std::vector<uint8_t>& mac_addr);
+
+    /// @brief Sets local HW address.
+    ///
+    /// Sets hardware address from an existing HWAddr structure.
+    /// The local address is a source address for outgoing
+    /// packet and destination address for incoming packet.
+    ///
+    /// @param addr structure representing HW address.
+    ///
+    /// @throw BadValue if addr is null
+    void setLocalHWAddr(const HWAddrPtr& addr);
+
+    /// @brief Returns local HW address.
+    ///
+    /// @return local HW addr.
+    HWAddrPtr getLocalHWAddr() const {
+        return (local_hwaddr_);
+    }
+
     /// @brief Sets remote address.
     ///
     /// @param remote specifies remote address
@@ -419,6 +489,23 @@ public:
     /// @throw isc::Unexpected if timestamp update failed
     void updateTimestamp();
 
+private:
+
+    /// @brief Generic method that validates and sets HW address.
+    ///
+    /// This is a generic method used by all modifiers of this class
+    /// which set class members representing HW address.
+    ///
+    /// @param htype hardware type.
+    /// @param hlen hardware length.
+    /// @param mac_addr pointer to actual hardware address.
+    /// @param [out] hw_addr pointer to a class member to be modified.
+    ///
+    /// @trow isc::OutOfRange if invalid HW address specified.
+    void setHWAddrMember(const uint8_t htype, const uint8_t hlen,
+                         const std::vector<uint8_t>& mac_addr,
+                         HWAddrPtr& hw_addr);
+
 protected:
 
     /// converts DHCP message type to BOOTP op type
@@ -429,6 +516,12 @@ protected:
     uint8_t
     DHCPTypeToBootpType(uint8_t dhcpType);
 
+    /// local HW address (dst if receiving packet, src if sending packet)
+    HWAddrPtr local_hwaddr_;
+
+    // remote HW address (src if receiving packet, dst if sending packet)
+    HWAddrPtr remote_hwaddr_;
+
     /// local address (dst if receiving packet, src if sending packet)
     isc::asiolink::IOAddress local_addr_;
 
@@ -533,6 +626,7 @@ protected:
 
     /// packet timestamp
     boost::posix_time::ptime timestamp_;
+
 }; // Pkt4 class
 
 typedef boost::shared_ptr<Pkt4> Pkt4Ptr;
diff --git a/src/lib/dhcp/pkt_filter.h b/src/lib/dhcp/pkt_filter.h
index 946bd14..204b25e 100644
--- a/src/lib/dhcp/pkt_filter.h
+++ b/src/lib/dhcp/pkt_filter.h
@@ -15,11 +15,21 @@
 #ifndef PKT_FILTER_H
 #define PKT_FILTER_H
 
+#include <dhcp/pkt4.h>
 #include <asiolink/io_address.h>
+#include <boost/shared_ptr.hpp>
 
 namespace isc {
 namespace dhcp {
 
+/// @brief Exception thrown when invalid packet filter object specified.
+class InvalidPacketFilter : public Exception {
+public:
+    InvalidPacketFilter(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// Forward declaration to the structure describing a socket.
 struct SocketInfo;
 
 /// Forward declaration to the class representing interface
@@ -45,6 +55,18 @@ public:
     /// @brief Virtual Destructor
     virtual ~PktFilter() { }
 
+    /// @brief Check if packet can be sent to the host without address directly.
+    ///
+    /// Checks if the Packet Filter class has capability to send a packet
+    /// directly to the client having no address assigned. This capability
+    /// is used by DHCPv4 servers which respond to the clients they assign
+    /// addresses to. Not all classes derived from PktFilter support this
+    /// because it requires injection of the destination host HW address to
+    /// the link layer header of the packet.
+    ///
+    /// @return true of the direct response is supported.
+    virtual bool isDirectResponseSupported() const = 0;
+
     /// @brief Open socket.
     ///
     /// @param iface interface descriptor
@@ -71,13 +93,18 @@ public:
 
     /// @brief Send packet over specified socket.
     ///
+    /// @param iface interface to be used to send packet
     /// @param sockfd socket descriptor
     /// @param pkt packet to be sent
     ///
     /// @return result of sending the packet. It is 0 if successful.
-    virtual int send(uint16_t sockfd, const Pkt4Ptr& pkt) = 0;
+    virtual int send(const Iface& iface, uint16_t sockfd,
+                     const Pkt4Ptr& pkt) = 0;
 };
 
+/// Pointer to a PktFilter object.
+typedef boost::shared_ptr<PktFilter> PktFilterPtr;
+
 } // namespace isc::dhcp
 } // namespace isc
 
diff --git a/src/lib/dhcp/pkt_filter_inet.cc b/src/lib/dhcp/pkt_filter_inet.cc
index a6360aa..62695e5 100644
--- a/src/lib/dhcp/pkt_filter_inet.cc
+++ b/src/lib/dhcp/pkt_filter_inet.cc
@@ -28,25 +28,12 @@ PktFilterInet::PktFilterInet()
 {
 }
 
-// iface is only used when SO_BINDTODEVICE is defined and thus
-// the code section using this variable is compiled.
-#ifdef SO_BINDTODEVICE
 int PktFilterInet::openSocket(const Iface& iface,
                               const isc::asiolink::IOAddress& addr,
                               const uint16_t port,
                               const bool receive_bcast,
                               const bool send_bcast) {
 
-#else
-int PktFilterInet::openSocket(const Iface&,
-                              const isc::asiolink::IOAddress& addr,
-                              const uint16_t port,
-                              const bool receive_bcast,
-                              const bool send_bcast) {
-
-
-#endif
-
     struct sockaddr_in addr4;
     memset(&addr4, 0, sizeof(sockaddr));
     addr4.sin_family = AF_INET;
@@ -54,7 +41,7 @@ int PktFilterInet::openSocket(const Iface&,
 
     // If we are to receive broadcast messages we have to bind
     // to "ANY" address.
-    if (receive_bcast) {
+    if (receive_bcast && iface.flag_broadcast_) {
         addr4.sin_addr.s_addr = INADDR_ANY;
     } else {
         addr4.sin_addr.s_addr = htonl(addr);
@@ -66,7 +53,7 @@ int PktFilterInet::openSocket(const Iface&,
     }
 
 #ifdef SO_BINDTODEVICE
-    if (receive_bcast) {
+    if (receive_bcast && iface.flag_broadcast_) {
         // Bind to device so as we receive traffic on a specific interface.
         if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, iface.getName().c_str(),
                        iface.getName().length() + 1) < 0) {
@@ -77,7 +64,7 @@ int PktFilterInet::openSocket(const Iface&,
     }
 #endif
 
-    if (send_bcast) {
+    if (send_bcast && iface.flag_broadcast_) {
         // Enable sending to broadcast address.
         int flag = 1;
         if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag)) < 0) {
@@ -198,7 +185,8 @@ PktFilterInet::receive(const Iface& iface, const SocketInfo& socket_info) {
 }
 
 int
-PktFilterInet::send(uint16_t sockfd, const Pkt4Ptr& pkt) {
+PktFilterInet::send(const Iface&, uint16_t sockfd,
+                    const Pkt4Ptr& pkt) {
     memset(&control_buf_[0], 0, control_buf_len_);
 
     // Set the target address we're sending to.
diff --git a/src/lib/dhcp/pkt_filter_inet.h b/src/lib/dhcp/pkt_filter_inet.h
index 4e98612..95c9224 100644
--- a/src/lib/dhcp/pkt_filter_inet.h
+++ b/src/lib/dhcp/pkt_filter_inet.h
@@ -16,6 +16,7 @@
 #define PKT_FILTER_INET_H
 
 #include <dhcp/pkt_filter.h>
+#include <boost/scoped_array.hpp>
 
 namespace isc {
 namespace dhcp {
@@ -32,6 +33,17 @@ public:
     /// Allocates control buffer.
     PktFilterInet();
 
+    /// @brief Check if packet can be sent to the host without address directly.
+    ///
+    /// This Packet Filter sends packets through AF_INET datagram sockets, so
+    /// it can't inject the HW address of the destionation host into the packet.
+    /// Therefore this class does not support direct responses.
+    ///
+    /// @return false always.
+    virtual bool isDirectResponseSupported() const {
+        return (false);
+    }
+
     /// @brief Open socket.
     ///
     /// @param iface interface descriptor
@@ -57,11 +69,13 @@ public:
 
     /// @brief Send packet over specified socket.
     ///
+    /// @param iface interface to be used to send packet
     /// @param sockfd socket descriptor
     /// @param pkt packet to be sent
     ///
     /// @return result of sending a packet. It is 0 if successful.
-    virtual int send(uint16_t sockfd, const Pkt4Ptr& pkt);
+    virtual int send(const Iface& iface, uint16_t sockfd,
+                     const Pkt4Ptr& pkt);
 
 private:
     /// Length of the control_buf_ array.
diff --git a/src/lib/dhcp/pkt_filter_lpf.cc b/src/lib/dhcp/pkt_filter_lpf.cc
index ef75426..e0964d5 100644
--- a/src/lib/dhcp/pkt_filter_lpf.cc
+++ b/src/lib/dhcp/pkt_filter_lpf.cc
@@ -13,31 +13,254 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <config.h>
+#include <dhcp/dhcp4.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt_filter_lpf.h>
+#include <dhcp/protocol_util.h>
+#include <exceptions/exceptions.h>
+#include <linux/filter.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <net/ethernet.h>
+
+namespace {
+
+using namespace isc::dhcp;
+
+/// The following structure defines a Berkely Packet Filter program to perform
+/// packet filtering. The program operates on Ethernet packets.  To help with
+/// interpretation of the program, for the types of Ethernet packets we are
+/// interested in, the header layout is:
+///
+///   6 bytes  Destination Ethernet Address
+///   6 bytes  Source Ethernet Address
+///   2 bytes  Ethernet packet type
+///
+///  20 bytes  Fixed part of IP header
+///  variable  Variable part of IP header
+///
+///   2 bytes  UDP Source port
+///   2 bytes  UDP destination port
+///   4 bytes  Rest of UDP header
+///
+/// @todo We may want to extend the filter to receive packets sent
+/// to the particular IP address assigned to the interface or
+/// broadcast address.
+struct sock_filter dhcp_sock_filter [] = {
+    // Make sure this is an IP packet: check the half-word (two bytes)
+    // at offset 12 in the packet (the Ethernet packet type).  If it
+    // is, advance to the next instruction.  If not, advance 8
+    // instructions (which takes execution to the last instruction in
+    // the sequence: "drop it").
+    BPF_STMT(BPF_LD + BPF_H + BPF_ABS, ETHERNET_PACKET_TYPE_OFFSET),
+    BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
+
+    // Make sure it's a UDP packet.  The IP protocol is at offset
+    // 9 in the IP header so, adding the Ethernet packet header size
+    // of 14 bytes gives an absolute byte offset in the packet of 23.
+    BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETHERNET_HEADER_LEN + IP_PROTO_TYPE_OFFSET),
+    BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
+
+    // Make sure this isn't a fragment by checking that the fragment
+    // offset field in the IP header is zero.  This field is the
+    // least-significant 13 bits in the bytes at offsets 6 and 7 in
+    // the IP header, so the half-word at offset 20 (6 + size of
+    // Ethernet header) is loaded and an appropriate mask applied.
+    BPF_STMT(BPF_LD + BPF_H + BPF_ABS, ETHERNET_HEADER_LEN + IP_FLAGS_OFFSET),
+    BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
+
+    // Get the IP header length.  This is achieved by the following
+    // (special) instruction that, given the offset of the start
+    // of the IP header (offset 14) loads the IP header length.
+    BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, ETHERNET_HEADER_LEN),
+
+    // Make sure it's to the right port.  The following instruction
+    // adds the previously extracted IP header length to the given
+    // offset to locate the correct byte.  The given offset of 16
+    // comprises the length of the Ethernet header (14) plus the offset
+    // of the UDP destination port (2) within the UDP header.
+    BPF_STMT(BPF_LD + BPF_H + BPF_IND, ETHERNET_HEADER_LEN + UDP_DEST_PORT),
+    // The following instruction tests against the default DHCP server port,
+    // but the action port is actually set in PktFilterLPF::openSocket().
+    // N.B. The code in that method assumes that this instruction is at
+    // offset 8 in the program.  If this is changed, openSocket() must be
+    // updated.
+    BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP4_SERVER_PORT, 0, 1),
+
+    // If we passed all the tests, ask for the whole packet.
+    BPF_STMT(BPF_RET + BPF_K, (u_int)-1),
+
+    // Otherwise, drop it.
+    BPF_STMT(BPF_RET + BPF_K, 0),
+};
+
+}
+
+using namespace isc::util;
 
 namespace isc {
 namespace dhcp {
 
 int
-PktFilterLPF::openSocket(const Iface&, const isc::asiolink::IOAddress&,
-                         const uint16_t, const bool,
+PktFilterLPF::openSocket(const Iface& iface, const isc::asiolink::IOAddress&,
+                         const uint16_t port, const bool,
                          const bool) {
-    isc_throw(isc::NotImplemented,
-              "Linux Packet Filtering is not implemented yet");
+
+    int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+    if (sock < 0) {
+        isc_throw(SocketConfigError, "Failed to create raw LPF socket");
+    }
+
+    // Create socket filter program. This program will only allow incoming UDP
+    // traffic which arrives on the specific (DHCP) port). It will also filter
+    // out all fragmented packets.
+    struct sock_fprog filter_program;
+    memset(&filter_program, 0, sizeof(filter_program));
+
+    filter_program.filter = dhcp_sock_filter;
+    filter_program.len = sizeof(dhcp_sock_filter) / sizeof(struct sock_filter);
+    // Override the default port value.
+    dhcp_sock_filter[8].k = port;
+    // Apply the filter.
+    if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter_program,
+                   sizeof(filter_program)) < 0) {
+        close(sock);
+        isc_throw(SocketConfigError, "Failed to install packet filtering program"
+                  << " on the socket " << sock);
+    }
+
+    struct sockaddr_ll sa;
+    memset(&sa, 0, sizeof(sockaddr_ll));
+    sa.sll_family = AF_PACKET;
+    sa.sll_ifindex = iface.getIndex();
+
+    // For raw sockets we construct IP headers on our own, so we don't bind
+    // socket to IP address but to the interface. We will later use the
+    // Linux Packet Filtering to filter out these packets that we are
+    // interested in.
+    if (bind(sock, reinterpret_cast<const struct sockaddr*>(&sa),
+             sizeof(sa)) < 0) {
+        close(sock);
+        isc_throw(SocketConfigError, "Failed to bind LPF socket '" << sock
+                  << "' to interface '" << iface.getName() << "'");
+    }
+
+    return (sock);
+
 }
 
 Pkt4Ptr
-PktFilterLPF::receive(const Iface&, const SocketInfo&) {
-    isc_throw(isc::NotImplemented,
-              "Linux Packet Filtering is not implemented yet");
+PktFilterLPF::receive(const Iface& iface, const SocketInfo& socket_info) {
+    uint8_t raw_buf[IfaceMgr::RCVBUFSIZE];
+    int data_len = read(socket_info.sockfd_, raw_buf, sizeof(raw_buf));
+    // If negative value is returned by read(), it indicates that an
+    // error occured. If returned value is 0, no data was read from the
+    // socket. In both cases something has gone wrong, because we expect
+    // that a chunk of data is there. We signal the lack of data by
+    // returing an empty packet.
+    if (data_len <= 0) {
+        return Pkt4Ptr();
+    }
+
+    InputBuffer buf(raw_buf, data_len);
+
+    // @todo: This is awkward way to solve the chicken and egg problem
+    // whereby we don't know the offset where DHCP data start in the
+    // received buffer when we create the packet object. In general case,
+    // the IP header has variable length. The information about its length
+    // is stored in one of its fields. Therefore, we have to decode the
+    // packet to get the offset of the DHCP data. The dummy object is
+    // created so as we can pass it to the functions which decode IP stack
+    // and find actual offset of the DHCP data.
+    // Once we find the offset we can create another Pkt4 object from
+    // the reminder of the input buffer and set the IP addresses and
+    // ports from the dummy packet. We should consider doing it
+    // in some more elegant way.
+    Pkt4Ptr dummy_pkt = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 0));
+
+    // Decode ethernet, ip and udp headers.
+    decodeEthernetHeader(buf, dummy_pkt);
+    decodeIpUdpHeader(buf, dummy_pkt);
+
+    // Read the DHCP data.
+    std::vector<uint8_t> dhcp_buf;
+    buf.readVector(dhcp_buf, buf.getLength() - buf.getPosition());
+
+    // Decode DHCP data into the Pkt4 object.
+    Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(&dhcp_buf[0], dhcp_buf.size()));
+
+    // Set the appropriate packet members using data collected from
+    // the decoded headers.
+    pkt->setIndex(iface.getIndex());
+    pkt->setIface(iface.getName());
+    pkt->setLocalAddr(dummy_pkt->getLocalAddr());
+    pkt->setRemoteAddr(dummy_pkt->getRemoteAddr());
+    pkt->setLocalPort(dummy_pkt->getLocalPort());
+    pkt->setRemotePort(dummy_pkt->getRemotePort());
+    pkt->setLocalHWAddr(dummy_pkt->getLocalHWAddr());
+    pkt->setRemoteHWAddr(dummy_pkt->getRemoteHWAddr());
+
+    return (pkt);
 }
 
 int
-PktFilterLPF::send(uint16_t, const Pkt4Ptr&) {
-    isc_throw(isc::NotImplemented,
-              "Linux Packet Filtering is not implemented yet");
+PktFilterLPF::send(const Iface& iface, uint16_t sockfd, const Pkt4Ptr& pkt) {
+
+    OutputBuffer buf(14);
+
+    HWAddrPtr hwaddr(new HWAddr(iface.getMac(), iface.getMacLen(),
+                                iface.getHWType()));
+    pkt->setLocalHWAddr(hwaddr);
+
+
+    // Ethernet frame header.
+    // Note that we don't validate whether HW addresses in '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);
+
+    // DHCPv4 message
+    buf.writeData(pkt->getBuffer().getData(), pkt->getBuffer().getLength());
+
+    sockaddr_ll sa;
+    sa.sll_family = AF_PACKET;
+    sa.sll_ifindex = iface.getIndex();
+    sa.sll_protocol = htons(ETH_P_IP);
+    sa.sll_halen = 6;
+
+    int result = sendto(sockfd, buf.getData(), buf.getLength(), 0,
+                        reinterpret_cast<const struct sockaddr*>(&sa),
+                        sizeof(sockaddr_ll));
+    if (result < 0) {
+        isc_throw(SocketWriteError, "failed to send DHCPv4 packet, errno="
+                  << errno << " (check errno.h)");
+    }
+
+    return (0);
+
 }
 
 
diff --git a/src/lib/dhcp/pkt_filter_lpf.h b/src/lib/dhcp/pkt_filter_lpf.h
index 67b190f..d36719f 100644
--- a/src/lib/dhcp/pkt_filter_lpf.h
+++ b/src/lib/dhcp/pkt_filter_lpf.h
@@ -17,6 +17,8 @@
 
 #include <dhcp/pkt_filter.h>
 
+#include <util/buffer.h>
+
 namespace isc {
 namespace dhcp {
 
@@ -30,6 +32,15 @@ namespace dhcp {
 class PktFilterLPF : public PktFilter {
 public:
 
+    /// @brief Check if packet can be sent to the host without address directly.
+    ///
+    /// This class supports direct responses to the host without address.
+    ///
+    /// @return true always.
+    virtual bool isDirectResponseSupported() const {
+        return (true);
+    }
+
     /// @brief Open socket.
     ///
     /// @param iface interface descriptor
@@ -57,12 +68,14 @@ public:
 
     /// @brief Send packet over specified socket.
     ///
+    /// @param iface interface to be used to send packet
     /// @param sockfd socket descriptor
     /// @param pkt packet to be sent
     ///
     /// @throw isc::NotImplemented always
     /// @return result of sending a packet. It is 0 if successful.
-    virtual int send(uint16_t sockfd, const Pkt4Ptr& pkt);
+    virtual int send(const Iface& iface, uint16_t sockfd,
+                     const Pkt4Ptr& pkt);
 
 };
 
diff --git a/src/lib/dhcp/protocol_util.cc b/src/lib/dhcp/protocol_util.cc
new file mode 100644
index 0000000..d93f8c4
--- /dev/null
+++ b/src/lib/dhcp/protocol_util.cc
@@ -0,0 +1,243 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <asiolink/io_address.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/protocol_util.h>
+#include <boost/static_assert.hpp>
+// in_systm.h is required on some some BSD systems
+// complaining that n_time is undefined but used
+// in ip.h.
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+using namespace isc::asiolink;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+void
+decodeEthernetHeader(InputBuffer& buf, Pkt4Ptr& pkt) {
+    // The size of the buffer to be parsed must not be lower
+    // then the size of the Ethernet frame header.
+    if (buf.getLength() - buf.getPosition() < ETHERNET_HEADER_LEN) {
+        isc_throw(InvalidPacketHeader, "size of ethernet header in received "
+                  << "packet is invalid, expected at least "
+                  << ETHERNET_HEADER_LEN << " bytes, received "
+                  << buf.getLength() - buf.getPosition() << " bytes");
+    }
+    // Packet object must not be NULL. We want to output some values
+    // to this object.
+    if (!pkt) {
+        isc_throw(BadValue, "NULL packet object provided when parsing ethernet"
+                  " frame header");
+    }
+
+    // The size of the single address is always lower then the size of
+    // the header that holds this address. Otherwise, it is a programming
+    // error that we want to detect in the compilation time.
+    BOOST_STATIC_ASSERT(ETHERNET_HEADER_LEN > HWAddr::ETHERNET_HWADDR_LEN);
+
+    // Remember initial position.
+    size_t start_pos = buf.getPosition();
+
+    // Read the destination HW address.
+    std::vector<uint8_t> dest_addr;
+    buf.readVector(dest_addr, HWAddr::ETHERNET_HWADDR_LEN);
+    pkt->setLocalHWAddr(HWTYPE_ETHERNET, HWAddr::ETHERNET_HWADDR_LEN, dest_addr);
+    // Read the source HW address.
+    std::vector<uint8_t> src_addr;
+    buf.readVector(src_addr, HWAddr::ETHERNET_HWADDR_LEN);
+    pkt->setRemoteHWAddr(HWTYPE_ETHERNET, HWAddr::ETHERNET_HWADDR_LEN, src_addr);
+    // Move the buffer read pointer to the end of the Ethernet frame header.
+    buf.setPosition(start_pos + ETHERNET_HEADER_LEN);
+}
+
+void
+decodeIpUdpHeader(InputBuffer& buf, Pkt4Ptr& pkt) {
+    // The size of the buffer must be at least equal to the minimal size of
+    // the IPv4 packet header plus UDP header length.
+    if (buf.getLength() - buf.getPosition() < MIN_IP_HEADER_LEN + UDP_HEADER_LEN) {
+        isc_throw(InvalidPacketHeader, "the total size of the IP and UDP headers in "
+                  << "received packet is invalid, expected at least "
+                  << MIN_IP_HEADER_LEN + UDP_HEADER_LEN
+                  << " bytes, received " << buf.getLength() - buf.getPosition()
+                  << " bytes");
+    }
+
+    // Packet object must not be NULL.
+    if (!pkt) {
+        isc_throw(BadValue, "NULL packet object provided when parsing IP and UDP"
+                  " packet headers");
+    }
+
+    BOOST_STATIC_ASSERT(IP_SRC_ADDR_OFFSET < MIN_IP_HEADER_LEN);
+
+    // Remember initial position of the read pointer.
+    size_t start_pos = buf.getPosition();
+
+    // Read IP header length (mask most significant bits as they indicate IP version).
+    uint8_t ip_len = buf.readUint8() & 0xF;
+    // IP length is the number of 4 byte chunks that construct IPv4 header.
+    // It must not be lower than 5 because first 20 bytes are fixed.
+    if (ip_len < 5) {
+        isc_throw(InvalidPacketHeader, "Value of the length of the IP header must not be"
+                  << " lower than 5 words. The length of the received header is "
+                  << ip_len << ".");
+    }
+
+    // Seek to the position of source IP address.
+    buf.setPosition(start_pos + IP_SRC_ADDR_OFFSET);
+    // Read source address.
+    pkt->setRemoteAddr(IOAddress(buf.readUint32()));
+    // Read destination address.
+    pkt->setLocalAddr(IOAddress(buf.readUint32()));
+
+    // Skip IP header options (if any) to start of the
+    // UDP header.
+    buf.setPosition(start_pos + ip_len * 4);
+
+    // Read source port from UDP header.
+    pkt->setRemotePort(buf.readUint16());
+    // Read destination port from UDP header.
+    pkt->setLocalPort(buf.readUint16());
+
+    // Set the pointer position to the first byte o the
+    // UDP payload (DHCP packet).
+    buf.setPosition(start_pos + ip_len * 4 + UDP_HEADER_LEN);
+}
+
+void
+writeEthernetHeader(const Pkt4Ptr& pkt, OutputBuffer& out_buf) {
+    // Set destination HW address.
+    HWAddrPtr remote_addr = pkt->getRemoteHWAddr();
+    if (remote_addr) {
+        if (remote_addr->hwaddr_.size() == HWAddr::ETHERNET_HWADDR_LEN) {
+            out_buf.writeData(&remote_addr->hwaddr_[0],
+                              HWAddr::ETHERNET_HWADDR_LEN);
+        } else {
+            isc_throw(BadValue, "invalid size of the remote HW address "
+                      << remote_addr->hwaddr_.size() << " when constructing"
+                      << " an ethernet frame header; expected size is"
+                      << " " << HWAddr::ETHERNET_HWADDR_LEN);
+        }
+    } else {
+        // HW address has not been specified. This is possible when receiving
+        // packet through a logical interface (e.g. lo). In such cases, we
+        // don't want to fail but rather provide a default HW address, which
+        // consists of zeros.
+        out_buf.writeData(&std::vector<uint8_t>(HWAddr::ETHERNET_HWADDR_LEN)[0],
+                          HWAddr::ETHERNET_HWADDR_LEN);
+    }
+
+    // Set source HW address.
+    HWAddrPtr local_addr = pkt->getLocalHWAddr();
+    if (local_addr) {
+        if (local_addr->hwaddr_.size() == HWAddr::ETHERNET_HWADDR_LEN) {
+            out_buf.writeData(&local_addr->hwaddr_[0],
+                              HWAddr::ETHERNET_HWADDR_LEN);
+        } else {
+            isc_throw(BadValue, "invalid size of the local HW address "
+                      << local_addr->hwaddr_.size() << " when constructing"
+                      << " an ethernet frame header; expected size is"
+                      << " " << HWAddr::ETHERNET_HWADDR_LEN);
+        }
+    } else {
+        // Provide default HW address.
+        out_buf.writeData(&std::vector<uint8_t>(HWAddr::ETHERNET_HWADDR_LEN)[0],
+                          HWAddr::ETHERNET_HWADDR_LEN);
+    }
+
+    // Type IP.
+    out_buf.writeUint16(ETHERNET_TYPE_IP);
+}
+
+void
+writeIpUdpHeader(const Pkt4Ptr& pkt, util::OutputBuffer& out_buf) {
+
+    out_buf.writeUint8(0x45); // IP version 4, IP header length 5
+    out_buf.writeUint8(IPTOS_LOWDELAY); // DSCP and ECN
+    out_buf.writeUint16(28 + pkt->getBuffer().getLength()); // Total length.
+    out_buf.writeUint16(0); // Identification
+    out_buf.writeUint16(0x4000); // Disable fragmentation.
+    out_buf.writeUint8(128); // TTL
+    out_buf.writeUint8(IPPROTO_UDP); // Protocol UDP.
+    out_buf.writeUint16(0); // Temporarily set checksum to 0.
+    out_buf.writeUint32(pkt->getLocalAddr()); // Source address.
+    out_buf.writeUint32(pkt->getRemoteAddr()); // Destination address.
+
+    // Calculate pseudo header checksum. It will be necessary to compute
+    // UDP checksum.
+    // Get the UDP length. This includes udp header's and data length.
+    uint32_t udp_len = 8 + pkt->getBuffer().getLength();
+    // The magic number "8" indicates the offset where the source address
+    // is stored in the buffer. This offset is counted here from the
+    // current tail of the buffer. Starting from this offset we calculate
+    // the checksum using 8 following bytes of data. This will include
+    // 4 bytes of source address and 4 bytes of destination address.
+    // The IPPROTO_UDP and udp_len are also added up to the checksum.
+    uint16_t pseudo_hdr_checksum =
+        calcChecksum(static_cast<const uint8_t*>(out_buf.getData()) + out_buf.getLength() - 8,
+                     8, IPPROTO_UDP + udp_len);
+
+    // Calculate IP header checksum.
+    uint16_t ip_checksum = ~calcChecksum(static_cast<const uint8_t*>(out_buf.getData())
+                                         + out_buf.getLength() - 20, 20);
+    // Write checksum in the IP header. The offset of the checksum is 10 bytes
+    // back from the tail of the current buffer.
+    out_buf.writeUint16At(ip_checksum, out_buf.getLength() - 10);
+
+    // Start UDP header.
+    out_buf.writeUint16(pkt->getLocalPort()); // Source port.
+    out_buf.writeUint16(pkt->getRemotePort()); // Destination port.
+    out_buf.writeUint16(udp_len); // Length of the header and data.
+
+    // Checksum is calculated from the contents of UDP header, data and pseudo ip header.
+    // The magic number "6" indicates that the UDP header starts at offset 6 from the
+    // tail of the current buffer. These 6 bytes contain source and destination port
+    // as well as the length of the header.
+    uint16_t udp_checksum =
+        ~calcChecksum(static_cast<const uint8_t*>(out_buf.getData()) + out_buf.getLength() - 6, 6,
+                      calcChecksum(static_cast<const uint8_t*>(pkt->getBuffer().getData()),
+                                   pkt->getBuffer().getLength(),
+                                   pseudo_hdr_checksum));
+    // Write UDP checksum.
+    out_buf.writeUint16(udp_checksum);
+}
+
+uint16_t
+calcChecksum(const uint8_t* buf, const uint32_t buf_size, uint32_t sum) {
+    uint32_t i;
+    for (i = 0; i < (buf_size & ~1U); i += 2) {
+        uint16_t chunk = buf[i] << 8 | buf[i + 1];
+        sum += chunk;
+        if (sum > 0xFFFF) {
+            sum -= 0xFFFF;
+        }
+    }
+    // If one byte has left, we also need to add it to the checksum.
+    if (i < buf_size) {
+        sum += buf[i] << 8;
+        if (sum > 0xFFFF) {
+            sum -= 0xFFFF;
+        }
+    }
+
+    return (sum);
+
+}
+
+}
+}
diff --git a/src/lib/dhcp/protocol_util.h b/src/lib/dhcp/protocol_util.h
new file mode 100644
index 0000000..b3f8085
--- /dev/null
+++ b/src/lib/dhcp/protocol_util.h
@@ -0,0 +1,153 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef PROTOCOL_UTIL_H
+#define PROTOCOL_UTIL_H
+
+#include <dhcp/pkt4.h>
+#include <util/buffer.h>
+
+#include <stdint.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Exception thrown when error occured during parsing packet's headers.
+///
+/// This exception is thrown when parsing link, Internet or Transport layer
+/// header has failed.
+class InvalidPacketHeader : public Exception {
+public:
+    InvalidPacketHeader(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// Size of the Ethernet frame header.
+static const size_t ETHERNET_HEADER_LEN = 14;
+/// Offset of the 2-byte word in the Ethernet packet which
+/// holds the type of the protocol it encapsulates.
+static const size_t ETHERNET_PACKET_TYPE_OFFSET = 12;
+/// This value is held in the Ethertype field of Ethernet frame
+/// and indicates that an IP packet is encapsulated with this
+/// frame. In the standard headers, there is an ETHERTYPE_IP,
+/// constant which serves the same purpose. However, it is more
+/// convenient to have our constant because we avoid
+/// inclusion of additional headers, which have different names
+/// and locations on different OSes.
+static const uint16_t ETHERNET_TYPE_IP = 0x0800;
+
+/// Minimal IPv4 header length.
+static const size_t MIN_IP_HEADER_LEN = 20;
+/// Offset in the IP header where the flags field starts.
+static const size_t IP_FLAGS_OFFSET = 6;
+/// Offset of the byte in IP header which holds the type
+/// of the protocol it encapsulates.
+static const size_t IP_PROTO_TYPE_OFFSET = 9;
+/// Offset of source address in the IPv4 header.
+static const size_t IP_SRC_ADDR_OFFSET = 12;
+
+/// UDP header length.
+static const size_t UDP_HEADER_LEN = 8;
+/// Offset within UDP header where destination port is held.
+static const size_t UDP_DEST_PORT = 2;
+
+/// @brief Decode the Ethernet header.
+///
+/// This function reads Ethernet frame header from the provided
+/// buffer at the current read position. The source HW address
+/// is read from the header and assigned as client address in
+/// the pkt object. The buffer read pointer is set to the end
+/// of the Ethernet frame header if read was successful.
+///
+/// @warning This function does not check that the provided 'pkt'
+/// pointer is valid. Caller must make sure that pointer is
+/// allocated.
+///
+/// @param buf input buffer holding header to be parsed.
+/// @param [out] pkt packet object receiving HW source address read from header.
+///
+/// @throw InvalidPacketHeader if packet header is truncated
+/// @throw BadValue if pkt object is NULL.
+void decodeEthernetHeader(util::InputBuffer& buf, Pkt4Ptr& pkt);
+
+/// @brief Decode IP and UDP header.
+///
+/// This function reads IP and UDP headers from the provided buffer
+/// at the current read position. The source and destination IP
+/// addresses and ports and read from these headers and stored in
+/// the appropriate members of the pkt object.
+///
+/// @warning This function does not check that the provided 'pkt'
+/// pointer is valid. Caller must make sure that pointer is
+/// allocated.
+///
+/// @param buf input buffer holding headers to be parsed.
+/// @param [out] pkt packet object where IP addresses and ports
+/// are stored.
+///
+/// @throw InvalidPacketHeader if packet header is truncated
+/// @throw BadValue if pkt object is NULL.
+void decodeIpUdpHeader(util::InputBuffer& buf, Pkt4Ptr& pkt);
+
+/// @brief Writes ethernet frame header into a buffer.
+///
+/// @warning This function does not check that the provided 'pkt'
+/// pointer is valid. Caller must make sure that pointer is
+/// allocated.
+///
+/// @param pkt packet object holding source and destination HW address.
+/// @param [out] out_buf buffer where a header is written.
+void writeEthernetHeader(const Pkt4Ptr& pkt,
+                         util::OutputBuffer& out_buf);
+
+/// @brief Writes both IP and UDP header into output buffer
+///
+/// This utility function assembles IP and UDP packet headers for the
+/// provided DHCPv4 message. The source and destination addreses and
+/// ports stored in the pkt object are copied as source and destination
+/// addresses and ports into IP/UDP headers.
+///
+/// @warning This function does not check that the provided 'pkt'
+/// pointer is valid. Caller must make sure that pointer is
+/// allocated.
+///
+/// @param pkt DHCPv4 packet to be sent in IP packet
+/// @param [out] out_buf buffer where an IP header is written
+void writeIpUdpHeader(const Pkt4Ptr& pkt, util::OutputBuffer& out_buf);
+
+/// @brief Calculates checksum for provided buffer
+///
+/// This function returns the sum of 16-bit values from the provided
+/// buffer. If the third parameter is specified, it indicates the
+/// initial checksum value. This parameter can be a result of
+/// calcChecksum function's invocation on different data buffer.
+/// The IP or UDP checksum value is a complement of the result returned
+/// by this function. However, this function does not compute complement
+/// of the summed values. It must be calculated outside of this function
+/// before writing the value to the packet buffer.
+///
+/// The IP header checksum calculation algorithm has been defined in
+/// <a href="https://tools.ietf.org/html/rfc791#page-14">RFC 791</a>
+///
+/// @param buf buffer for which the checksum is calculated.
+/// @param buf_size size of the buffer for which checksum is calculated.
+/// @param sum initial checksum value, other values will be added to it.
+///
+/// @return calculated checksum.
+uint16_t calcChecksum(const uint8_t* buf, const uint32_t buf_size,
+                      uint32_t sum = 0);
+
+}
+}
+#endif // PROTOCOL_UTIL_H
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index 0432ca5..0216a0b 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -44,6 +44,13 @@ libdhcp___unittests_SOURCES += option_space_unittest.cc
 libdhcp___unittests_SOURCES += option_string_unittest.cc
 libdhcp___unittests_SOURCES += pkt4_unittest.cc
 libdhcp___unittests_SOURCES += pkt6_unittest.cc
+libdhcp___unittests_SOURCES += pkt_filter_inet_unittest.cc
+
+if OS_LINUX
+libdhcp___unittests_SOURCES += pkt_filter_lpf_unittest.cc
+endif
+
+libdhcp___unittests_SOURCES += protocol_util_unittest.cc
 libdhcp___unittests_SOURCES += duid_unittest.cc
 
 libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index ede7abf..859bde6 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -74,6 +74,10 @@ public:
         : open_socket_called_(false) {
     }
 
+    virtual bool isDirectResponseSupported() const {
+        return (false);
+    }
+
     /// Pretends to open socket. Only records a call to this function.
     virtual int openSocket(const Iface&,
                            const isc::asiolink::IOAddress&,
@@ -91,7 +95,7 @@ public:
     }
 
     /// Does nothing
-    virtual int send(uint16_t, const Pkt4Ptr&) {
+    virtual int send(const Iface&, uint16_t, const Pkt4Ptr&) {
         return (0);
     }
 
@@ -103,7 +107,8 @@ public:
 class NakedIfaceMgr: public IfaceMgr {
     // "Naked" Interface Manager, exposes internal fields
 public:
-    NakedIfaceMgr() { }
+    NakedIfaceMgr() {
+    }
     IfaceCollection & getIfacesLst() { return ifaces_; }
 };
 
@@ -117,6 +122,24 @@ public:
     ~IfaceMgrTest() {
     }
 
+    // Get ther number of IPv4 or IPv6 sockets on the loopback interface
+    int getOpenSocketsCount(const Iface& iface, uint16_t family) const {
+        // Get all sockets.
+        Iface::SocketCollection sockets = iface.getSockets();
+
+        // Loop through sockets and try to find the ones which match the
+        // specified type.
+        int sockets_count = 0;
+        for (Iface::SocketCollection::const_iterator sock = sockets.begin();
+             sock != sockets.end(); ++sock) {
+            // Match found, increase the counter.
+            if (sock->family_ == family) {
+                ++sockets_count;
+            }
+        }
+        return (sockets_count);
+    }
+
 };
 
 // We need some known interface to work reliably. Loopback interface is named
@@ -208,6 +231,66 @@ TEST_F(IfaceMgrTest, basic) {
     ASSERT_TRUE(&ifacemgr != 0);
 }
 
+
+// This test verifies that sockets can be closed selectively, i.e. all
+// IPv4 sockets can be closed first and all IPv6 sockets remain open.
+TEST_F(IfaceMgrTest, closeSockets) {
+    // Will be using local loopback addresses for this test.
+    IOAddress loaddr("127.0.0.1");
+    IOAddress loaddr6("::1");
+
+    // Create instance of IfaceMgr.
+    boost::scoped_ptr<NakedIfaceMgr> iface_mgr(new NakedIfaceMgr());
+    ASSERT_TRUE(iface_mgr);
+
+    // Out constructor does not detect interfaces by itself. We need
+    // to create one and add.
+    int ifindex = if_nametoindex(LOOPBACK);
+    ASSERT_GT(ifindex, 0);
+    Iface lo_iface(LOOPBACK, ifindex);
+    iface_mgr->getIfacesLst().push_back(lo_iface);
+
+    // Create set of V4 and V6 sockets on the loopback interface.
+    // They must differ by a port they are bound to.
+    for (int i = 0; i < 6; ++i) {
+        // Every other socket will be IPv4.
+        if (i % 2) {
+            ASSERT_NO_THROW(
+                iface_mgr->openSocket(LOOPBACK, loaddr, 10000 + i)
+            );
+        } else {
+            ASSERT_NO_THROW(
+                iface_mgr->openSocket(LOOPBACK, loaddr6, 10000 + i)
+            );
+        }
+    }
+
+    // At the end we should have 3 IPv4 and 3 IPv6 sockets open.
+    Iface* iface = iface_mgr->getIface(LOOPBACK);
+    ASSERT_TRUE(iface != NULL);
+
+    int v4_sockets_count = getOpenSocketsCount(*iface, AF_INET);
+    ASSERT_EQ(3, v4_sockets_count);
+    int v6_sockets_count = getOpenSocketsCount(*iface, AF_INET6);
+    ASSERT_EQ(3, v6_sockets_count);
+
+    // Let's try to close only IPv4 sockets.
+    ASSERT_NO_THROW(iface_mgr->closeSockets(AF_INET));
+    v4_sockets_count = getOpenSocketsCount(*iface, AF_INET);
+    EXPECT_EQ(0, v4_sockets_count);
+    // The IPv6 sockets should remain open.
+    v6_sockets_count = getOpenSocketsCount(*iface, AF_INET6);
+    EXPECT_EQ(3, v6_sockets_count);
+
+    // Let's try to close IPv6 sockets.
+    ASSERT_NO_THROW(iface_mgr->closeSockets(AF_INET6));
+    v4_sockets_count = getOpenSocketsCount(*iface, AF_INET);
+    EXPECT_EQ(0, v4_sockets_count);
+    // They should have been closed now.
+    v6_sockets_count = getOpenSocketsCount(*iface, AF_INET6);
+    EXPECT_EQ(0, v6_sockets_count);
+}
+
 TEST_F(IfaceMgrTest, ifaceClass) {
     // Basic tests for Iface inner class
 
@@ -597,19 +680,11 @@ TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
     // open sockets on the same ports.
     ifacemgr->closeSockets();
 
-    // The following test is currently disabled for OSes other than
-    // Linux because interface detection is not implemented on them.
-    // @todo enable this test for all OSes once interface detection
-    // is implemented.
-#if defined(OS_LINUX)
-    // Open v4 socket to connect to broadcast address.
-    int socket3  = 0;
-    IOAddress bcastAddr("255.255.255.255");
-    EXPECT_NO_THROW(
-        socket3 = ifacemgr->openSocketFromRemoteAddress(bcastAddr, PORT2);
-    );
-    EXPECT_GT(socket3, 0);
-#endif
+    // There used to be a check here that verified the ability to open
+    // suitable socket for sending broadcast request. However,
+    // there is no guarantee for such test to work on all systems
+    // because some systems may have no broadcast capable interfaces at all.
+    // Thus, this check has been removed.
 
     // Do not call closeSockets() because it is called by IfaceMgr's
     // virtual destructor.
@@ -714,14 +789,12 @@ TEST_F(IfaceMgrTest, sendReceive4) {
 
     // let's assume that every supported OS have lo interface
     IOAddress loAddr("127.0.0.1");
-    int socket1 = 0, socket2 = 0;
+    int socket1 = 0;
     EXPECT_NO_THROW(
         socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, DHCP4_SERVER_PORT + 10000);
-        socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, DHCP4_SERVER_PORT + 10000 + 1);
     );
 
     EXPECT_GE(socket1, 0);
-    EXPECT_GE(socket2, 0);
 
     boost::shared_ptr<Pkt4> sendPkt(new Pkt4(DHCPDISCOVER, 1234) );
 
@@ -754,7 +827,7 @@ TEST_F(IfaceMgrTest, sendReceive4) {
 
     boost::shared_ptr<Pkt4> rcvPkt;
 
-    EXPECT_EQ(true, ifacemgr->send(sendPkt));
+    EXPECT_NO_THROW(ifacemgr->send(sendPkt));
 
     ASSERT_NO_THROW(rcvPkt = ifacemgr->receive4(10));
     ASSERT_TRUE(rcvPkt); // received our own packet
@@ -826,8 +899,80 @@ TEST_F(IfaceMgrTest, setPacketFilter) {
     EXPECT_TRUE(custom_packet_filter->open_socket_called_);
     // This function always returns fake socket descriptor equal to 1024.
     EXPECT_EQ(1024, socket1);
+
+    // Replacing current packet filter object while there are IPv4
+    // sockets open is not allowed!
+    EXPECT_THROW(iface_mgr->setPacketFilter(custom_packet_filter),
+                 PacketFilterChangeDenied);
+
+    // So, let's close the open IPv4 sockets and retry. Now it should succeed.
+    iface_mgr->closeSockets(AF_INET);
+    EXPECT_NO_THROW(iface_mgr->setPacketFilter(custom_packet_filter));
+}
+
+#if defined OS_LINUX
+
+// This Linux specific test checks whether it is possible to use
+// IfaceMgr to figure out which Pakcket Filter object should be
+// used when direct responses to hosts, having no address assigned
+// are desired or not desired.
+TEST_F(IfaceMgrTest, setMatchingPacketFilter) {
+
+    // Create an instance of IfaceMgr.
+    boost::scoped_ptr<NakedIfaceMgr> iface_mgr(new NakedIfaceMgr());
+    ASSERT_TRUE(iface_mgr);
+
+    // Let IfaceMgr figure out which Packet Filter to use when
+    // direct response capability is not desired. It should pick
+    // PktFilterInet.
+    EXPECT_NO_THROW(iface_mgr->setMatchingPacketFilter(false));
+    // The PktFilterInet is supposed to report lack of direct
+    // response capability.
+    EXPECT_FALSE(iface_mgr->isDirectResponseSupported());
+
+    // There is working implementation of direct responses on Linux
+    // in PktFilterLPF. It uses Linux Packet Filtering as underlying
+    // mechanism. When direct responses are desired the object of
+    // this class should be set.
+    EXPECT_NO_THROW(iface_mgr->setMatchingPacketFilter(true));
+    // This object should report that direct responses are supported.
+    EXPECT_TRUE(iface_mgr->isDirectResponseSupported());
 }
 
+#else
+
+// This non-Linux specific test checks whether it is possible to use
+// IfaceMgr to figure out which Pakcket Filter object should be
+// used when direct responses to hosts, having no address assigned
+// are desired or not desired. Since direct responses aren't supported
+// on systems other than Linux the function under test should always
+// set object of PktFilterInet type as current Packet Filter. This
+// object does not support direct responses. Once implementation is
+// added on non-Linux systems the OS specific version of the test
+// will be removed.
+TEST_F(IfaceMgrTest, setMatchingPacketFilter) {
+
+    // Create an instance of IfaceMgr.
+    boost::scoped_ptr<NakedIfaceMgr> iface_mgr(new NakedIfaceMgr());
+    ASSERT_TRUE(iface_mgr);
+
+    // Let IfaceMgr figure out which Packet Filter to use when
+    // direct response capability is not desired. It should pick
+    // PktFilterInet.
+    EXPECT_NO_THROW(iface_mgr->setMatchingPacketFilter(false));
+    // The PktFilterInet is supposed to report lack of direct
+    // response capability.
+    EXPECT_FALSE(iface_mgr->isDirectResponseSupported());
+
+    // On non-Linux systems, we are missing the direct traffic
+    // implementation. Therefore, we expect that PktFilterInet
+    // object will be set.
+    EXPECT_NO_THROW(iface_mgr->setMatchingPacketFilter(true));
+    // This object should report lack of direct response capability.
+    EXPECT_FALSE(iface_mgr->isDirectResponseSupported());
+}
+
+#endif
 
 TEST_F(IfaceMgrTest, socket4) {
 
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index e9c7dd2..a108930 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -653,4 +653,49 @@ TEST(Pkt4Test, hwaddr) {
     EXPECT_TRUE(hwaddr == pkt->getHWAddr());
 }
 
+// This test verifies that the packet remte and local HW address can
+// be set and returned.
+TEST(Pkt4Test, hwaddrSrcRemote) {
+    scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
+    const uint8_t src_hw[] = { 1, 2, 3, 4, 5, 6 };
+    const uint8_t dst_hw[] = { 7, 8, 9, 10, 11, 12 };
+    const uint8_t hw_type = 123;
+
+    HWAddrPtr dst_hwaddr(new HWAddr(dst_hw, sizeof(src_hw), hw_type));
+    HWAddrPtr src_hwaddr(new HWAddr(src_hw, sizeof(src_hw), hw_type));
+
+    // Check that we can set the local address.
+    EXPECT_NO_THROW(pkt->setLocalHWAddr(dst_hwaddr));
+    EXPECT_TRUE(dst_hwaddr == pkt->getLocalHWAddr());
+
+    // Check that we can set the remote address.
+    EXPECT_NO_THROW(pkt->setRemoteHWAddr(src_hwaddr));
+    EXPECT_TRUE(src_hwaddr == pkt->getRemoteHWAddr());
+
+    // Can't set the NULL addres.
+    EXPECT_THROW(pkt->setRemoteHWAddr(HWAddrPtr()), BadValue);
+    EXPECT_THROW(pkt->setLocalHWAddr(HWAddrPtr()), BadValue);
+
+    // Test alternative way to set local address.
+    const uint8_t dst_hw2[] = { 19, 20, 21, 22, 23, 24 };
+    std::vector<uint8_t> dst_hw_vec(dst_hw2, dst_hw2 + sizeof(dst_hw2));
+    const uint8_t hw_type2 = 234;
+    EXPECT_NO_THROW(pkt->setLocalHWAddr(hw_type2, sizeof(dst_hw2), dst_hw_vec));
+    HWAddrPtr local_addr = pkt->getLocalHWAddr();
+    ASSERT_TRUE(local_addr);
+    EXPECT_EQ(hw_type2, local_addr->htype_);
+    EXPECT_TRUE(std::equal(dst_hw_vec.begin(), dst_hw_vec.end(),
+                           local_addr->hwaddr_.begin()));
+
+    // Set remote address.
+    const uint8_t src_hw2[] = { 25, 26, 27, 28, 29, 30 };
+    std::vector<uint8_t> src_hw_vec(src_hw2, src_hw2 + sizeof(src_hw2));
+    EXPECT_NO_THROW(pkt->setRemoteHWAddr(hw_type2, sizeof(src_hw2), src_hw_vec));
+    HWAddrPtr remote_addr = pkt->getRemoteHWAddr();
+    ASSERT_TRUE(remote_addr);
+    EXPECT_EQ(hw_type2, remote_addr->htype_);
+    EXPECT_TRUE(std::equal(src_hw_vec.begin(), src_hw_vec.end(),
+                           remote_addr->hwaddr_.begin()));
+}
+
 } // end of anonymous namespace
diff --git a/src/lib/dhcp/tests/pkt_filter_inet_unittest.cc b/src/lib/dhcp/tests/pkt_filter_inet_unittest.cc
new file mode 100644
index 0000000..a80c064
--- /dev/null
+++ b/src/lib/dhcp/tests/pkt_filter_inet_unittest.cc
@@ -0,0 +1,269 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <asiolink/io_address.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt_filter_inet.h>
+
+#include <gtest/gtest.h>
+
+#include <sys/socket.h>
+
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+namespace {
+
+/// Port number used by tests.
+const uint16_t PORT = 10067;
+/// Size of the buffer holding received packets.
+const size_t RECV_BUF_SIZE = 2048;
+
+/// This class handles has simple algorithm checking
+/// presence of loopback interface and initializing
+/// its index.
+class PktFilterInetTest : public ::testing::Test {
+public:
+    PktFilterInetTest() {
+        // Initialize ifname_ and ifindex_.
+        loInit();
+    }
+
+    ~PktFilterInetTest() {
+        // Cleanup after each test. This guarantees
+        // that the socket does not hang after a test.
+        close(socket_);
+    }
+
+    /// @brief Detect loopback interface.
+    ///
+    /// @todo this function will be removed once cross-OS interface
+    /// detection is implemented
+    void loInit() {
+        if (if_nametoindex("lo") > 0) {
+            ifname_ = "lo";
+            ifindex_ = if_nametoindex("lo");
+
+        } else if (if_nametoindex("lo0") > 0) {
+            ifname_ = "lo0";
+            ifindex_ = if_nametoindex("lo0");
+
+        } else {
+            std::cout << "Failed to detect loopback interface. Neither "
+                      << "lo nor lo0 worked. Giving up." << std::endl;
+            FAIL();
+
+
+
+        }
+    }
+
+    std::string ifname_; ///< Loopback interface name
+    uint16_t ifindex_;   ///< Loopback interface index.
+    int socket_;         ///< Socket descriptor.
+
+};
+
+// This test verifies that the PktFilterInet class reports its lack
+// of capability to send packets to the host having no IP address
+// assigned.
+TEST_F(PktFilterInetTest, isDirectResponseSupported) {
+    // Create object under test.
+    PktFilterInet pkt_filter;
+    // This Packet Filter class does not support direct responses
+    // under any conditions.
+    EXPECT_FALSE(pkt_filter.isDirectResponseSupported());
+}
+
+// This test verifies that the INET datagram socket is correctly opened and
+// bound to the appropriate address and port.
+TEST_F(PktFilterInetTest, openSocket) {
+    // Create object representing loopback interface.
+    Iface iface(ifname_, ifindex_);
+    // Set loopback address.
+    IOAddress addr("127.0.0.1");
+
+    // Try to open socket.
+    PktFilterInet pkt_filter;
+    socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+    // Check that socket has been opened.
+    ASSERT_GE(socket_, 0);
+
+    // Verify that the socket belongs to AF_INET family.
+    sockaddr_in sock_address;
+    socklen_t sock_address_len = sizeof(sock_address);
+    ASSERT_EQ(0, getsockname(socket_, reinterpret_cast<sockaddr*>(&sock_address),
+                             &sock_address_len));
+    EXPECT_EQ(AF_INET, sock_address.sin_family);
+
+    // Verify that the socket is bound the appropriate address.
+    const std::string bind_addr(inet_ntoa(sock_address.sin_addr));
+    EXPECT_EQ("127.0.0.1", bind_addr);
+
+    // Verify that the socket is bound to appropriate port.
+    EXPECT_EQ(PORT, ntohs(sock_address.sin_port));
+
+    // Verify that the socket has SOCK_DGRAM type.
+    int sock_type;
+    socklen_t sock_type_len = sizeof(sock_type);
+    ASSERT_EQ(0, getsockopt(socket_, SOL_SOCKET, SO_TYPE, &sock_type, &sock_type_len));
+    EXPECT_EQ(SOCK_DGRAM, sock_type);
+}
+
+// This test verifies that the packet is correctly sent over the INET
+// datagram socket.
+TEST_F(PktFilterInetTest, send) {
+    // Let's create a DHCPv4 packet.
+    Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 0));
+    ASSERT_TRUE(pkt);
+
+    // Set required fields.
+    pkt->setLocalAddr(IOAddress("127.0.0.1"));
+    pkt->setRemoteAddr(IOAddress("127.0.0.1"));
+    pkt->setRemotePort(PORT);
+    pkt->setLocalPort(PORT + 1);
+    pkt->setIndex(ifindex_);
+    pkt->setIface(ifname_);
+    pkt->setHops(6);
+    pkt->setSecs(42);
+    pkt->setCiaddr(IOAddress("192.0.2.1"));
+    pkt->setSiaddr(IOAddress("192.0.2.2"));
+    pkt->setYiaddr(IOAddress("192.0.2.3"));
+    pkt->setGiaddr(IOAddress("192.0.2.4"));
+
+    // Create the on-wire data.
+    ASSERT_NO_THROW(pkt->pack());
+
+    // Packet will be sent over loopback interface.
+    Iface iface(ifname_, ifindex_);
+    IOAddress addr("127.0.0.1");
+
+    // Create an instance of the class which we are testing.
+    PktFilterInet pkt_filter;
+    // Open socket. We don't check that the socket has appropriate
+    // options and family set because we have checked that in the
+    // openSocket test already.
+    socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+    ASSERT_GE(socket_, 0);
+
+    // Send the packet over the socket.
+    ASSERT_NO_THROW(pkt_filter.send(iface, socket_, pkt));
+
+    // Read the data from socket.
+    fd_set readfds;
+    FD_ZERO(&readfds);
+    FD_SET(socket_, &readfds);
+    
+    struct timeval timeout;
+    timeout.tv_sec = 5;
+    timeout.tv_usec = 0;
+    int result = select(socket_ + 1, &readfds, NULL, NULL, &timeout);
+    // We should receive some data from loopback interface.
+    ASSERT_GT(result, 0);
+
+    // Get the actual data.
+    uint8_t rcv_buf[RECV_BUF_SIZE];
+    result = recv(socket_, rcv_buf, RECV_BUF_SIZE, 0);
+    ASSERT_GT(result, 0);
+
+    // Create the DHCPv4 packet from the received data.
+    Pkt4Ptr rcvd_pkt(new Pkt4(rcv_buf, result));
+    ASSERT_TRUE(rcvd_pkt);
+
+    // Parse the packet.
+    ASSERT_NO_THROW(rcvd_pkt->unpack());
+
+    // Verify that the received packet matches sent packet.
+    EXPECT_EQ(pkt->getHops(), rcvd_pkt->getHops());
+    EXPECT_EQ(pkt->getOp(),   rcvd_pkt->getOp());
+    EXPECT_EQ(pkt->getSecs(), rcvd_pkt->getSecs());
+    EXPECT_EQ(pkt->getFlags(), rcvd_pkt->getFlags());
+    EXPECT_EQ(pkt->getCiaddr(), rcvd_pkt->getCiaddr());
+    EXPECT_EQ(pkt->getSiaddr(), rcvd_pkt->getSiaddr());
+    EXPECT_EQ(pkt->getYiaddr(), rcvd_pkt->getYiaddr());
+    EXPECT_EQ(pkt->getGiaddr(), rcvd_pkt->getGiaddr());
+    EXPECT_EQ(pkt->getTransid(), rcvd_pkt->getTransid());
+    EXPECT_TRUE(pkt->getSname() == rcvd_pkt->getSname());
+    EXPECT_TRUE(pkt->getFile() == rcvd_pkt->getFile());
+    EXPECT_EQ(pkt->getHtype(), rcvd_pkt->getHtype());
+    EXPECT_EQ(pkt->getHlen(), rcvd_pkt->getHlen());
+}
+
+// This test verifies that the DHCPv4 packet is correctly received via
+// INET datagram socket and that it matches sent packet.
+TEST_F(PktFilterInetTest, receive) {
+    // Let's create a DHCPv4 packet.
+    Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 0));
+    ASSERT_TRUE(pkt);
+
+    // Set required fields.
+    pkt->setLocalAddr(IOAddress("127.0.0.1"));
+    pkt->setRemoteAddr(IOAddress("127.0.0.1"));
+    pkt->setRemotePort(PORT);
+    pkt->setLocalPort(PORT + 1);
+    pkt->setIndex(ifindex_);
+    pkt->setIface(ifname_);
+    pkt->setHops(6);
+    pkt->setSecs(42);
+    pkt->setCiaddr(IOAddress("192.0.2.1"));
+    pkt->setSiaddr(IOAddress("192.0.2.2"));
+    pkt->setYiaddr(IOAddress("192.0.2.3"));
+    pkt->setGiaddr(IOAddress("192.0.2.4"));
+
+    // Create the on-wire data.
+    ASSERT_NO_THROW(pkt->pack());
+
+    // Packet will be sent over loopback interface.
+    Iface iface(ifname_, ifindex_);
+    IOAddress addr("127.0.0.1");
+
+    // Create an instance of the class which we are testing.
+    PktFilterInet pkt_filter;
+    // Open socket. We don't check that the socket has appropriate
+    // options and family set because we have checked that in the
+    // openSocket test already.
+    socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+    ASSERT_GE(socket_, 0);
+
+    // Send the packet over the socket.
+    ASSERT_NO_THROW(pkt_filter.send(iface, socket_, pkt));
+
+    // Receive the packet.
+    SocketInfo socket_info(socket_, IOAddress("127.0.0.1"), PORT);
+    Pkt4Ptr rcvd_pkt = pkt_filter.receive(iface, socket_info);
+    // Check that the packet has been correctly received.
+    ASSERT_TRUE(rcvd_pkt);
+
+    // Parse the packet.
+    ASSERT_NO_THROW(rcvd_pkt->unpack());
+
+    // Verify that the received packet matches sent packet.
+    EXPECT_EQ(pkt->getHops(), rcvd_pkt->getHops());
+    EXPECT_EQ(pkt->getOp(),   rcvd_pkt->getOp());
+    EXPECT_EQ(pkt->getSecs(), rcvd_pkt->getSecs());
+    EXPECT_EQ(pkt->getFlags(), rcvd_pkt->getFlags());
+    EXPECT_EQ(pkt->getCiaddr(), rcvd_pkt->getCiaddr());
+    EXPECT_EQ(pkt->getSiaddr(), rcvd_pkt->getSiaddr());
+    EXPECT_EQ(pkt->getYiaddr(), rcvd_pkt->getYiaddr());
+    EXPECT_EQ(pkt->getGiaddr(), rcvd_pkt->getGiaddr());
+    EXPECT_EQ(pkt->getTransid(), rcvd_pkt->getTransid());
+    EXPECT_TRUE(pkt->getSname() == rcvd_pkt->getSname());
+    EXPECT_TRUE(pkt->getFile() == rcvd_pkt->getFile());
+    EXPECT_EQ(pkt->getHtype(), rcvd_pkt->getHtype());
+    EXPECT_EQ(pkt->getHlen(), rcvd_pkt->getHlen());
+}
+
+} // anonymous namespace
diff --git a/src/lib/dhcp/tests/pkt_filter_lpf_unittest.cc b/src/lib/dhcp/tests/pkt_filter_lpf_unittest.cc
new file mode 100644
index 0000000..c072488
--- /dev/null
+++ b/src/lib/dhcp/tests/pkt_filter_lpf_unittest.cc
@@ -0,0 +1,293 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <asiolink/io_address.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt_filter_lpf.h>
+#include <dhcp/protocol_util.h>
+#include <util/buffer.h>
+
+#include <gtest/gtest.h>
+
+#include <linux/if_packet.h>
+#include <sys/socket.h>
+
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::util;
+
+namespace {
+
+/// Port number used by tests.
+const uint16_t PORT = 10067;
+/// Size of the buffer holding received packets.
+const size_t RECV_BUF_SIZE = 2048;
+
+/// This class handles has simple algorithm checking
+/// presence of loopback interface and initializing
+/// its index.
+class PktFilterLPFTest : public ::testing::Test {
+public:
+    PktFilterLPFTest() {
+        // Initialize ifname_ and ifindex_.
+        loInit();
+    }
+
+    ~PktFilterLPFTest() {
+        // Cleanup after each test. This guarantees
+        // that the socket does not hang after a test.
+        close(socket_);
+    }
+
+    /// @brief Detect loopback interface.
+    ///
+    /// @todo this function will be removed once cross-OS interface
+    /// detection is implemented
+    void loInit() {
+        if (if_nametoindex("lo") > 0) {
+            ifname_ = "lo";
+            ifindex_ = if_nametoindex("lo");
+
+        } else if (if_nametoindex("lo0") > 0) {
+            ifname_ = "lo0";
+            ifindex_ = if_nametoindex("lo0");
+
+        } else {
+            std::cout << "Failed to detect loopback interface. Neither "
+                      << "lo nor lo0 worked. Giving up." << std::endl;
+            FAIL();
+
+
+
+        }
+    }
+
+    std::string ifname_; ///< Loopback interface name
+    uint16_t ifindex_;   ///< Loopback interface index.
+    int socket_;         ///< Socket descriptor.
+
+};
+
+// This test verifies that the PktFilterLPF class reports its capability
+// to send packets to the host having no IP address assigned.
+TEST_F(PktFilterLPFTest, isDirectResponseSupported) {
+    // Create object under test.
+    PktFilterLPF pkt_filter;
+    // Must support direct responses.
+    EXPECT_TRUE(pkt_filter.isDirectResponseSupported());
+}
+
+// All tests below require root privileges to execute successfully. If
+// they are run as non-root user they will fail due to insufficient privileges
+// to open raw network sockets. Therefore, they should remain disabled by default
+// and "DISABLED_" tags should not be removed. If one is willing to run these
+// tests please run "make check" as root and enable execution of disabled tests
+// by setting GTEST_ALSO_RUN_DISABLED_TESTS to a value other than 0. In order
+// to run tests from this particular file, set the GTEST_FILTER environmental
+// variable to "PktFilterLPFTest.*" apart from GTEST_ALSO_RUN_DISABLED_TESTS
+// setting.
+
+// This test verifies that the raw AF_PACKET family socket can
+// be opened and bound to the specific interface.
+TEST_F(PktFilterLPFTest, DISABLED_openSocket) {
+    // Create object representing loopback interface.
+    Iface iface(ifname_, ifindex_);
+    // Set loopback address.
+    IOAddress addr("127.0.0.1");
+
+    // Try to open socket.
+    PktFilterLPF pkt_filter;
+    socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+    // Check that socket has been opened.
+    ASSERT_GE(socket_, 0);
+
+    // Verify that the socket belongs to AF_PACKET family.
+    sockaddr_ll sock_address;
+    socklen_t sock_address_len = sizeof(sock_address);
+    ASSERT_EQ(0, getsockname(socket_, reinterpret_cast<sockaddr*>(&sock_address),
+                             &sock_address_len));
+    EXPECT_EQ(AF_PACKET, sock_address.sll_family);
+
+    // Verify that the socket is bound to appropriate interface.
+    EXPECT_EQ(ifindex_, sock_address.sll_ifindex);
+
+    // Verify that the socket has SOCK_RAW type.
+    int sock_type;
+    socklen_t sock_type_len = sizeof(sock_type);
+    ASSERT_EQ(0, getsockopt(socket_, SOL_SOCKET, SO_TYPE, &sock_type, &sock_type_len));
+    EXPECT_EQ(SOCK_RAW, sock_type);
+}
+
+// This test verifies correctness of sending DHCP packet through the raw
+// socket, whereby all IP stack headers are hand-crafted.
+TEST_F(PktFilterLPFTest, DISABLED_send) {
+        // Let's create a DHCPv4 packet.
+    Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 0));
+    ASSERT_TRUE(pkt);
+
+    // Set required fields.
+    // By setting the local address to broadcast we simulate the
+    // typical scenario when client's request was send to broadcast
+    // address and server by default used it as a source address
+    // in its response. The send() function should be able to detect
+    // it and correct the source address.
+    pkt->setLocalAddr(IOAddress("255.255.255.255"));
+    pkt->setRemoteAddr(IOAddress("127.0.0.1"));
+    pkt->setRemotePort(PORT);
+    pkt->setLocalPort(PORT + 1);
+    pkt->setIndex(ifindex_);
+    pkt->setIface(ifname_);
+    pkt->setHops(6);
+    pkt->setSecs(42);
+    pkt->setCiaddr(IOAddress("192.0.2.1"));
+    pkt->setSiaddr(IOAddress("192.0.2.2"));
+    pkt->setYiaddr(IOAddress("192.0.2.3"));
+    pkt->setGiaddr(IOAddress("192.0.2.4"));
+
+    // Create the on-wire data.
+    ASSERT_NO_THROW(pkt->pack());
+
+    // Packet will be sent over loopback interface.
+    Iface iface(ifname_, ifindex_);
+    IOAddress addr("127.0.0.1");
+
+    // Create an instance of the class which we are testing.
+    PktFilterLPF pkt_filter;
+    // Open socket. We don't check that the socket has appropriate
+    // options and family set because we have checked that in the
+    // openSocket test already.
+    socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+    ASSERT_GE(socket_, 0);
+
+    // Send the packet over the socket.
+    ASSERT_NO_THROW(pkt_filter.send(iface, socket_, pkt));
+
+    // Read the data from socket.
+    fd_set readfds;
+    FD_ZERO(&readfds);
+    FD_SET(socket_, &readfds);
+    
+    struct timeval timeout;
+    timeout.tv_sec = 5;
+    timeout.tv_usec = 0;
+    int result = select(socket_ + 1, &readfds, NULL, NULL, &timeout);
+    // We should receive some data from loopback interface.
+    ASSERT_GT(result, 0);
+
+    // Get the actual data.
+    uint8_t rcv_buf[RECV_BUF_SIZE];
+    result = recv(socket_, rcv_buf, RECV_BUF_SIZE, 0);
+    ASSERT_GT(result, 0);
+
+    Pkt4Ptr dummy_pkt = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 0));
+
+    InputBuffer buf(rcv_buf, result);
+
+    // Decode ethernet, ip and udp headers.
+    decodeEthernetHeader(buf, dummy_pkt);
+    decodeIpUdpHeader(buf, dummy_pkt);
+
+    // Create the DHCPv4 packet from the received data.
+    std::vector<uint8_t> dhcp_buf;
+    buf.readVector(dhcp_buf, buf.getLength() - buf.getPosition());
+    Pkt4Ptr rcvd_pkt(new Pkt4(&dhcp_buf[0], dhcp_buf.size()));
+    ASSERT_TRUE(rcvd_pkt);
+
+    // Parse the packet.
+    ASSERT_NO_THROW(rcvd_pkt->unpack());
+
+    // Verify that the received packet matches sent packet.
+    EXPECT_EQ(pkt->getHops(), rcvd_pkt->getHops());
+    EXPECT_EQ(pkt->getOp(),   rcvd_pkt->getOp());
+    EXPECT_EQ(pkt->getSecs(), rcvd_pkt->getSecs());
+    EXPECT_EQ(pkt->getFlags(), rcvd_pkt->getFlags());
+    EXPECT_EQ(pkt->getCiaddr(), rcvd_pkt->getCiaddr());
+    EXPECT_EQ(pkt->getSiaddr(), rcvd_pkt->getSiaddr());
+    EXPECT_EQ(pkt->getYiaddr(), rcvd_pkt->getYiaddr());
+    EXPECT_EQ(pkt->getGiaddr(), rcvd_pkt->getGiaddr());
+    EXPECT_EQ(pkt->getTransid(), rcvd_pkt->getTransid());
+    EXPECT_TRUE(pkt->getSname() == rcvd_pkt->getSname());
+    EXPECT_TRUE(pkt->getFile() == rcvd_pkt->getFile());
+    EXPECT_EQ(pkt->getHtype(), rcvd_pkt->getHtype());
+    EXPECT_EQ(pkt->getHlen(), rcvd_pkt->getHlen());
+}
+
+// This test verifies correctness of reception of the DHCP packet over
+// raw socket, whereby all IP stack headers are hand-crafted.
+TEST_F(PktFilterLPFTest, DISABLED_receive) {
+
+    // Let's create a DHCPv4 packet.
+    Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 0));
+    ASSERT_TRUE(pkt);
+
+    // Set required fields.
+    pkt->setLocalAddr(IOAddress("127.0.0.1"));
+    pkt->setRemoteAddr(IOAddress("127.0.0.1"));
+    pkt->setRemotePort(PORT);
+    pkt->setLocalPort(PORT + 1);
+    pkt->setIndex(ifindex_);
+    pkt->setIface(ifname_);
+    pkt->setHops(6);
+    pkt->setSecs(42);
+    pkt->setCiaddr(IOAddress("192.0.2.1"));
+    pkt->setSiaddr(IOAddress("192.0.2.2"));
+    pkt->setYiaddr(IOAddress("192.0.2.3"));
+    pkt->setGiaddr(IOAddress("192.0.2.4"));
+
+    // Create the on-wire data.
+    ASSERT_NO_THROW(pkt->pack());
+
+    // Packet will be sent over loopback interface.
+    Iface iface(ifname_, ifindex_);
+    IOAddress addr("127.0.0.1");
+
+    // Create an instance of the class which we are testing.
+    PktFilterLPF pkt_filter;
+    // Open socket. We don't check that the socket has appropriate
+    // options and family set because we have checked that in the
+    // openSocket test already.
+    socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+    ASSERT_GE(socket_, 0);
+
+    // Send the packet over the socket.
+    ASSERT_NO_THROW(pkt_filter.send(iface, socket_, pkt));
+
+    // Receive the packet.
+    SocketInfo socket_info(socket_, IOAddress("127.0.0.1"), PORT);
+    Pkt4Ptr rcvd_pkt = pkt_filter.receive(iface, socket_info);
+    // Check that the packet has been correctly received.
+    ASSERT_TRUE(rcvd_pkt);
+
+    // Parse the packet.
+    ASSERT_NO_THROW(rcvd_pkt->unpack());
+
+    // Verify that the received packet matches sent packet.
+    EXPECT_EQ(pkt->getHops(), rcvd_pkt->getHops());
+    EXPECT_EQ(pkt->getOp(),   rcvd_pkt->getOp());
+    EXPECT_EQ(pkt->getSecs(), rcvd_pkt->getSecs());
+    EXPECT_EQ(pkt->getFlags(), rcvd_pkt->getFlags());
+    EXPECT_EQ(pkt->getCiaddr(), rcvd_pkt->getCiaddr());
+    EXPECT_EQ(pkt->getSiaddr(), rcvd_pkt->getSiaddr());
+    EXPECT_EQ(pkt->getYiaddr(), rcvd_pkt->getYiaddr());
+    EXPECT_EQ(pkt->getGiaddr(), rcvd_pkt->getGiaddr());
+    EXPECT_EQ(pkt->getTransid(), rcvd_pkt->getTransid());
+    EXPECT_TRUE(pkt->getSname() == rcvd_pkt->getSname());
+    EXPECT_TRUE(pkt->getFile() == rcvd_pkt->getFile());
+    EXPECT_EQ(pkt->getHtype(), rcvd_pkt->getHtype());
+    EXPECT_EQ(pkt->getHlen(), rcvd_pkt->getHlen());
+}
+
+} // anonymous namespace
diff --git a/src/lib/dhcp/tests/protocol_util_unittest.cc b/src/lib/dhcp/tests/protocol_util_unittest.cc
new file mode 100644
index 0000000..eee98e6
--- /dev/null
+++ b/src/lib/dhcp/tests/protocol_util_unittest.cc
@@ -0,0 +1,390 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <asiolink/io_address.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/hwaddr.h>
+#include <dhcp/protocol_util.h>
+#include <util/buffer.h>
+#include <gtest/gtest.h>
+// in_systm.h is required on some some BSD systems
+// complaining that n_time is undefined but used
+// in ip.h.
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::util;
+
+namespace {
+
+    /*/// @brief OptionCustomTest test class.
+class OptionCustomTest : public ::testing::Test {
+public:
+};*/
+
+/// The purpose of this test is to verify that the IP header checksum
+/// is calculated correctly.
+TEST(ProtocolUtilTest, checksum) {
+    // IPv4 header to be used to calculate checksum.
+    const uint8_t hdr[] = {
+        0x45,                      // IP version and header length
+        0x00,                      // TOS
+        0x00, 0x3c,                // Total length of the IP packet.
+        0x1c, 0x46,                // Identification field.
+        0x40, 0x00,                // Fragmentation.
+        0x40,                      // TTL
+        0x06,                      // Protocol
+        0x00, 0x00,                // Checksum (reset to 0x00).
+        0xac, 0x10, 0x0a, 0x63,    // Source IP address.
+        0xac, 0x10, 0x0a, 0x0c     // Destination IP address.
+    };
+    // Calculate size of the header array.
+    const uint32_t hdr_size = sizeof(hdr) / sizeof(hdr[0]);
+    // Get the actual checksum.
+    uint16_t chksum = ~calcChecksum(hdr, hdr_size);
+    // The 0xb1e6 value has been calculated by other means.
+    EXPECT_EQ(0xb1e6, chksum);
+    // Tested function may also take the initial value of the sum.
+    // Let's set it to 2 and see whether it is included in the
+    // calculation.
+    chksum = ~calcChecksum(hdr, hdr_size, 2);
+    // The checkum value should change.
+    EXPECT_EQ(0xb1e4, chksum);
+}
+
+// The purpose of this test is to verify that the Ethernet frame header
+// can be decoded correctly. In particular it verifies that the source
+// HW address can be extracted from it.
+TEST(ProtocolUtilTest, decodeEthernetHeader) {
+    // Source HW address, 6 bytes.
+    const uint8_t src_hw_addr[6] = {
+        0x10, 0x11, 0x12, 0x13, 0x14, 0x15
+    };
+    // Destination HW address, 6 bytes.
+    const uint8_t dest_hw_addr[6] = {
+        0x20, 0x31, 0x42, 0x53, 0x64, 0x75
+    };
+
+    // Prepare a buffer holding Ethernet frame header and 4 bytes of
+    // dummy data.
+    OutputBuffer buf(1);
+    buf.writeData(dest_hw_addr, sizeof(dest_hw_addr));
+    buf.writeData(src_hw_addr, sizeof(src_hw_addr));
+    buf.writeUint16(ETHERNET_TYPE_IP);
+    // Append dummy data. We will later check that this data is not
+    // removed or corrupted when reading the ethernet header.
+    buf.writeUint32(0x01020304);
+
+    // Create a buffer with truncated ethernet frame header..
+    InputBuffer in_buf_truncated(buf.getData(), buf.getLength() - 6);
+    // But provide valid packet object to make sure that the function
+    // under test does not throw due to NULL pointer packet.
+    Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 0));
+    // Function should throw because header data is truncated.
+    EXPECT_THROW(decodeEthernetHeader(in_buf_truncated, pkt),
+                 InvalidPacketHeader);
+
+    // Get not truncated buffer.
+    InputBuffer in_buf(buf.getData(), buf.getLength());
+    // But provide NULL packet object instead.
+    pkt.reset();
+    // It should throw again but a different exception.
+    EXPECT_THROW(decodeEthernetHeader(in_buf, pkt),
+                 BadValue);
+    // Now provide, correct data.
+    pkt.reset(new Pkt4(DHCPDISCOVER, 0));
+    // It should not throw now.
+    ASSERT_NO_THROW(decodeEthernetHeader(in_buf, pkt));
+    // Verify that the destination HW address has been initialized...
+    HWAddrPtr checked_dest_hwaddr = pkt->getLocalHWAddr();
+    ASSERT_TRUE(checked_dest_hwaddr);
+    // and is correct.
+    EXPECT_EQ(HWTYPE_ETHERNET, checked_dest_hwaddr->htype_);
+    ASSERT_EQ(sizeof(dest_hw_addr), checked_dest_hwaddr->hwaddr_.size());
+    EXPECT_TRUE(std::equal(dest_hw_addr, dest_hw_addr + sizeof(dest_hw_addr),
+                           checked_dest_hwaddr->hwaddr_.begin()));
+
+    // Verify that the HW address of the source has been initialized.
+    HWAddrPtr checked_src_hwaddr = pkt->getRemoteHWAddr();
+    ASSERT_TRUE(checked_src_hwaddr);
+    // And that it is correct.
+    EXPECT_EQ(HWTYPE_ETHERNET, checked_src_hwaddr->htype_);
+    ASSERT_EQ(sizeof(src_hw_addr), checked_src_hwaddr->hwaddr_.size());
+    EXPECT_TRUE(std::equal(src_hw_addr, src_hw_addr + sizeof(src_hw_addr),
+                           checked_src_hwaddr->hwaddr_.begin()));
+
+    // The entire ethernet packet header should have been read. This means
+    // that the internal buffer pointer should now point to its tail.
+    ASSERT_EQ(ETHERNET_HEADER_LEN, in_buf.getPosition());
+    // And the dummy data should be still readable and correct.
+    uint32_t dummy_data = in_buf.readUint32();
+    EXPECT_EQ(0x01020304, dummy_data);
+}
+
+/// The purpose of this test is to verify that the IP and UDP header
+/// is decoded correctly and appropriate values of IP addresses and
+/// ports are assigned to a Pkt4 object.
+TEST(ProtocolUtilTest, decodeIpUdpHeader) {
+    // IPv4 header to be parsed.
+    const uint8_t hdr[] = {
+        0x45,                      // IP version and header length
+        0x00,                      // TOS
+        0x00, 0x3c,                // Total length of the IP packet.
+        0x1c, 0x46,                // Identification field.
+        0x40, 0x00,                // Fragmentation.
+        0x40,                      // TTL
+        IPPROTO_UDP,               // Protocol
+        0x00, 0x00,                // Checksum (reset to 0x00).
+        0xc0, 0x00, 0x02, 0x63,    // Source IP address.
+        0xc0, 0x00, 0x02, 0x0c,    // Destination IP address.
+        0x27, 0x54,                // Source port
+        0x27, 0x53,                // Destination port
+        0x00, 0x08,                // UDP length
+        0x00, 0x00                 // Checksum
+    };
+
+    // Write header data to the buffer.
+    OutputBuffer buf(1);
+    buf.writeData(hdr, sizeof(hdr));
+    // Append some dummy data.
+    buf.writeUint32(0x01020304);
+
+    // Create an input buffer holding truncated headers.
+    InputBuffer in_buf_truncated(buf.getData(), buf.getLength() - 10);
+    // Create non NULL Pkt4 object to make sure that the function under
+    // test does not throw due to invalid Pkt4 object.
+    Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 0));
+    // Function should throw because buffer holds truncated data.
+    EXPECT_THROW(decodeIpUdpHeader(in_buf_truncated, pkt), InvalidPacketHeader);
+
+    // Create a valid input buffer (not truncated).
+    InputBuffer in_buf(buf.getData(), buf.getLength());
+    // Set NULL Pkt4 object to verify that function under test will
+    // return exception as expected.
+    pkt.reset();
+    // And check whether it throws exception.
+    EXPECT_THROW(decodeIpUdpHeader(in_buf, pkt), BadValue);
+
+    // Now, let's provide valid arguments and make sure it doesn't throw.
+    pkt.reset(new Pkt4(DHCPDISCOVER, 0));
+    ASSERT_TRUE(pkt);
+    EXPECT_NO_THROW(decodeIpUdpHeader(in_buf, pkt));
+
+    // Verify the source address and port.
+    EXPECT_EQ("192.0.2.99", pkt->getRemoteAddr().toText());
+    EXPECT_EQ(10068, pkt->getRemotePort());
+
+    // Verify the destination address and port.
+    EXPECT_EQ("192.0.2.12", pkt->getLocalAddr().toText());
+    EXPECT_EQ(10067, pkt->getLocalPort());
+
+    // Verify that the dummy data has not been corrupted and that the
+    // internal read pointer has been moved to the tail of the UDP
+    // header.
+    ASSERT_EQ(MIN_IP_HEADER_LEN + UDP_HEADER_LEN, in_buf.getPosition());
+    EXPECT_EQ(0x01020304, in_buf.readUint32());
+}
+
+/// The purpose of this test is to verify that the ethernet
+/// header is correctly constructed from the destination and
+/// hardware addresses.
+TEST(ProtocolUtilTest, writeEthernetHeader) {
+    // Source HW address, 6 bytes.
+    const uint8_t src_hw_addr[6] = {
+        0x10, 0x11, 0x12, 0x13, 0x14, 0x15
+    };
+    // Destination HW address, 6 bytes.
+    const uint8_t dest_hw_addr[6] = {
+        0x20, 0x31, 0x42, 0x53, 0x64, 0x75
+    };
+
+    // Create output buffer.
+    OutputBuffer buf(1);
+    Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 0));
+
+    HWAddrPtr local_hw_addr(new HWAddr(src_hw_addr, 6, 1));
+    ASSERT_NO_THROW(pkt->setLocalHWAddr(local_hw_addr));
+
+    // Set invalid length (7) of the hw address.
+    HWAddrPtr remote_hw_addr(new HWAddr(&std::vector<uint8_t>(1, 7)[0], 7, 1));
+    ASSERT_NO_THROW(pkt->setRemoteHWAddr(remote_hw_addr));
+    // HW address is too long, so it should fail.
+    EXPECT_THROW(writeEthernetHeader(pkt, buf), BadValue);
+
+    // Finally, set a valid HW address.
+    remote_hw_addr.reset(new HWAddr(dest_hw_addr, 6, 1));
+    ASSERT_NO_THROW(pkt->setRemoteHWAddr(remote_hw_addr));
+
+    // Construct the ethernet header using HW addresses stored
+    // in the pkt object.
+    writeEthernetHeader(pkt, buf);
+
+    // The resulting ethernet header consists of destination
+    // and src HW address (each 6 bytes long) and two bytes
+    // of encapsulated protocol type.
+    ASSERT_EQ(14, buf.getLength());
+
+    // Verify that first 6 bytes comprise valid destination
+    // HW address. Instead of using memory comparison functions
+    // we check bytes one-by-one. In case of mismatch we will
+    // get exact values that are mismatched. If memcmp was used
+    // the error message would not indicate the values of
+    // mismatched bytes.
+    for (int i = 0; i < 6; ++i) {
+        EXPECT_EQ(dest_hw_addr[i], buf[i]);
+    }
+    // Verify that following 6 bytes comprise the valid source
+    // HW address.
+    for (int i = 0; i < 6; ++i) {
+        EXPECT_EQ(src_hw_addr[i], buf[i + 6]);
+    }
+
+    // The last two bytes comprise the encapsulated protocol type.
+    // We expect IPv4 protocol type which is specified by 0x0800.
+    EXPECT_EQ(0x08, buf[12]);
+    EXPECT_EQ(0x0, buf[13]);
+}
+
+TEST(ProtocolUtilTest, writeIpUdpHeader) {
+    // Create DHCPv4 packet. Some values held by this object are
+    // used by the utility function under test to figure out the
+    // contents of the IP and UDP headers, e.g. source and
+    // destination IP address or port number.
+    Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 0));
+    ASSERT_TRUE(pkt);
+
+    // Set local and remote address and port.
+    pkt->setLocalAddr(IOAddress("192.0.2.1"));
+    pkt->setRemoteAddr(IOAddress("192.0.2.111"));
+    pkt->setLocalPort(DHCP4_SERVER_PORT);
+    pkt->setRemotePort(DHCP4_CLIENT_PORT);
+
+    // Pack the contents of the packet.
+    ASSERT_NO_THROW(pkt->pack());
+
+    // Create output buffer. The headers will be written to it.
+    OutputBuffer buf(1);
+    // Write some dummy data to the buffer. We will check that the
+    // function under test appends to this data, not overrides it.
+    buf.writeUint16(0x0102);
+
+    // Write both IP and UDP headers.
+    writeIpUdpHeader(pkt, buf);
+
+    // The resulting size of the buffer must be 30. The 28 bytes are
+    // consumed by the IP and UDP headers. The other 2 bytes are dummy
+    // data at the beginning of the buffer.
+    ASSERT_EQ(30, buf.getLength());
+
+    // Make sure that the existing data in the buffer was not corrupted
+    // by the function under test.
+    EXPECT_EQ(0x01, buf[0]);
+    EXPECT_EQ(0x02, buf[1]);
+
+    // Copy the contents of the buffer to InputBuffer object. This object
+    // exposes convenient functions for reading.
+    InputBuffer in_buf(buf.getData(), buf.getLength());
+
+    // Check dummy data.
+    uint16_t dummy_data = in_buf.readUint16();
+    EXPECT_EQ(0x0102, dummy_data);
+
+    // The IP version and IHL are stored in the same octet (4 bits each).
+    uint8_t ver_len = in_buf.readUint8();
+    // The most significant bits define IP version.
+    uint8_t ip_ver = ver_len >> 4;
+    EXPECT_EQ(4, ip_ver);
+    // The least significant bits define header length (in 32-bits chunks).
+    uint8_t ip_len = ver_len & 0x0F;
+    EXPECT_EQ(5, ip_len);
+
+    // Get Differentiated Services Codepoint and Explicit Congestion
+    // Notification field.
+    uint8_t dscp_ecn = in_buf.readUint8();
+    EXPECT_EQ(IPTOS_LOWDELAY, dscp_ecn);
+
+    // Total length of IP packet. Includes UDP header and payload.
+    uint16_t total_len = in_buf.readUint16();
+    EXPECT_EQ(28 + pkt->getBuffer().getLength(), total_len);
+
+    // Identification field.
+    uint16_t ident = in_buf.readUint16();
+    EXPECT_EQ(0, ident);
+
+    // Fragmentation.
+    uint16_t fragment = in_buf.readUint16();
+    // Setting second most significant bit means no fragmentation.
+    EXPECT_EQ(0x4000, fragment);
+
+    // Get TTL
+    uint8_t ttl = in_buf.readUint8();
+    // Expect non-zero TTL.
+    EXPECT_GE(ttl, 1);
+
+    // Protocol type is UDP.
+    uint8_t proto = in_buf.readUint8();
+    EXPECT_EQ(IPPROTO_UDP, proto);
+
+    // Check that the checksum is correct. The reference checksum value
+    // has been calculated manually.
+    uint16_t ip_checksum = in_buf.readUint16();
+    EXPECT_EQ(0x755c, ip_checksum);
+
+    // Validate source address.
+    // Initializing it to IPv6 address guarantees that it is not initialized
+    // to the value that we expect to be read from a header since the value
+    // read from a header will be IPv4.
+    IOAddress src_addr("::1");
+    // Read src address as an array of bytes because it is easely convertible
+    // to IOAddress object.
+    uint8_t src_addr_data[4];
+    ASSERT_NO_THROW(
+        in_buf.readData(src_addr_data, 4);
+        src_addr = IOAddress::fromBytes(AF_INET, src_addr_data);
+    );
+    EXPECT_EQ(IOAddress("192.0.2.1").toText(), src_addr.toText());
+
+    // Validate destination address.
+    IOAddress dest_addr("::1");
+    uint8_t dest_addr_data[4];
+    ASSERT_NO_THROW(
+        in_buf.readData(dest_addr_data, 4);
+        dest_addr = IOAddress::fromBytes(AF_INET, dest_addr_data);
+    );
+    EXPECT_EQ(IOAddress("192.0.2.111").toText(), dest_addr.toText());
+
+    // UDP header starts here.
+
+    // Check source port.
+    uint16_t src_port = in_buf.readUint16();
+    EXPECT_EQ(pkt->getLocalPort(), src_port);
+
+    // Check destination port.
+    uint16_t dest_port = in_buf.readUint16();
+    EXPECT_EQ(pkt->getRemotePort(), dest_port);
+
+    // UDP header and data length.
+    uint16_t udp_len = in_buf.readUint16();
+    EXPECT_EQ(8 + pkt->getBuffer().getLength(), udp_len);
+
+    // Verify UDP checksum. The reference checksum has been calculated manually.
+    uint16_t udp_checksum = in_buf.readUint16();
+    EXPECT_EQ(0x8817, udp_checksum);
+}
+
+} // anonymous namespace



More information about the bind10-changes mailing list