BIND 10 master, updated. 21d2f7ec425f8461b545687104cd76a42da61b2e [master] Merge branch 'trac3251'

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Jan 8 09:03:22 UTC 2014


The branch, master has been updated
       via  21d2f7ec425f8461b545687104cd76a42da61b2e (commit)
       via  e1c59f67be33e21c00032d64b6edb0db3882eb7a (commit)
       via  4063c0a8aa70cd0503fbf9d19a8b7f65ecd59064 (commit)
       via  cef44a4094cac6272f1a85c485b23fd047107fe0 (commit)
       via  9139b19b6220b270e2442731f258ba85c7877526 (commit)
       via  33003d2af6c0410719a68b7525284735df1ed797 (commit)
       via  26ec9c66db6d73c45d59ce731d05d91c75674890 (commit)
       via  b173ea75b749984a94830c81ad6834f42387f8ba (commit)
       via  77059f8a6b32612fab2c862b5dee11c728430327 (commit)
       via  4276b45afad091f2022b690effd2711d6a587c79 (commit)
       via  0ae10a533c27a489ec4bb434ca54c40e2f92f9dc (commit)
       via  f2f292a65d52ee73700658543d1587dc4a86a20f (commit)
       via  c2222d11fbfc90e09036434c836b419edf6e3675 (commit)
      from  be25b97a4ca9f145303e1028bfb59cff19ff39fc (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 21d2f7ec425f8461b545687104cd76a42da61b2e
Merge: be25b97 e1c59f6
Author: Marcin Siodelski <marcin at isc.org>
Date:   Wed Jan 8 09:14:21 2014 +0100

    [master] Merge branch 'trac3251'

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

Summary of changes:
 doc/devel/mainpage.dox                             |    1 +
 src/lib/dhcp/Makefile.am                           |    2 +
 src/lib/dhcp/iface_mgr.cc                          |  404 ++++-------------
 src/lib/dhcp/iface_mgr.h                           |  105 +++--
 src/lib/dhcp/libdhcp++.dox                         |   45 ++
 .../d2_unittests.cc => lib/dhcp/pkt_filter6.cc}    |   35 +-
 src/lib/dhcp/pkt_filter6.h                         |  150 ++++++
 src/lib/dhcp/pkt_filter_inet6.cc                   |  286 ++++++++++++
 src/lib/dhcp/pkt_filter_inet6.h                    |   98 ++++
 src/lib/dhcp/pkt_filter_lpf.h                      |    9 +-
 src/lib/dhcp/tests/Makefile.am                     |    2 +
 src/lib/dhcp/tests/iface_mgr_unittest.cc           |  476 +++++++++++++++++++-
 ...ter_test_utils.cc => pkt_filter6_test_utils.cc} |  143 +++---
 ...ilter_test_utils.h => pkt_filter6_test_utils.h} |  102 ++---
 ...et_unittest.cc => pkt_filter_inet6_unittest.cc} |   71 ++-
 src/lib/dhcp/tests/pkt_filter_test_utils.h         |    2 +-
 16 files changed, 1383 insertions(+), 548 deletions(-)
 copy src/{bin/d2/tests/d2_unittests.cc => lib/dhcp/pkt_filter6.cc} (51%)
 create mode 100644 src/lib/dhcp/pkt_filter6.h
 create mode 100644 src/lib/dhcp/pkt_filter_inet6.cc
 create mode 100644 src/lib/dhcp/pkt_filter_inet6.h
 copy src/lib/dhcp/tests/{pkt_filter_test_utils.cc => pkt_filter6_test_utils.cc} (50%)
 copy src/lib/dhcp/tests/{pkt_filter_test_utils.h => pkt_filter6_test_utils.h} (56%)
 copy src/lib/dhcp/tests/{pkt_filter_inet_unittest.cc => pkt_filter_inet6_unittest.cc} (66%)

-----------------------------------------------------------------------
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
index 64e2782..da5240b 100644
--- a/doc/devel/mainpage.dox
+++ b/doc/devel/mainpage.dox
@@ -73,6 +73,7 @@
  *   - @subpage libdhcpRelay
  *   - @subpage libdhcpIfaceMgr
  *   - @subpage libdhcpPktFilter
+ *   - @subpage libdhcpPktFilter6
  * - @subpage libdhcpsrv
  *   - @subpage leasemgr
  *   - @subpage cfgmgr
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index eb9309a..1748927 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -42,7 +42,9 @@ 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 pkt_filter.cc
+libb10_dhcp___la_SOURCES += pkt_filter6.h pkt_filter6.cc
 libb10_dhcp___la_SOURCES += pkt_filter_inet.cc pkt_filter_inet.h
+libb10_dhcp___la_SOURCES += pkt_filter_inet6.cc pkt_filter_inet6.h
 
 if OS_LINUX
 libb10_dhcp___la_SOURCES += pkt_filter_lpf.cc pkt_filter_lpf.h
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index 66738ce..94c7ab9 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -23,6 +23,7 @@
 #include <dhcp/dhcp6.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/pkt_filter_inet.h>
+#include <dhcp/pkt_filter_inet6.h>
 #include <exceptions/exceptions.h>
 #include <util/io/pktinfo_utilities.h>
 
@@ -169,7 +170,8 @@ IfaceMgr::IfaceMgr()
     :control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
      control_buf_(new char[control_buf_len_]),
      session_socket_(INVALID_SOCKET), session_callback_(NULL),
-     packet_filter_(new PktFilterInet())
+     packet_filter_(new PktFilterInet()),
+     packet_filter6_(new PktFilterInet6())
 {
 
     try {
@@ -225,10 +227,11 @@ IfaceMgr::isDirectResponseSupported() const {
 }
 
 void
-IfaceMgr::setPacketFilter(const boost::shared_ptr<PktFilter>& packet_filter) {
+IfaceMgr::setPacketFilter(const PktFilterPtr& packet_filter) {
     // Do not allow NULL pointer.
     if (!packet_filter) {
-        isc_throw(InvalidPacketFilter, "NULL packet filter object specified");
+        isc_throw(InvalidPacketFilter, "NULL packet filter object specified for"
+                  " DHCPv4");
     }
     // Different packet filters use different socket types. It does not make
     // sense to allow the change of packet filter when there are IPv4 sockets
@@ -236,25 +239,54 @@ IfaceMgr::setPacketFilter(const boost::shared_ptr<PktFilter>& packet_filter) {
     // 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.
+    if (hasOpenSocket(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::setPacketFilter(const PktFilter6Ptr& packet_filter) {
+    if (!packet_filter) {
+        isc_throw(InvalidPacketFilter, "NULL packet filter object specified for"
+                  " DHCPv6");
+    }
+
+    if (hasOpenSocket(AF_INET6)) {
+        // 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 IPv6 sockets - need"
+                  << " to close them first");
+    }
+
+    packet_filter6_ = packet_filter;
+}
+
+bool
+IfaceMgr::hasOpenSocket(const uint16_t family) const {
+    // Iterate over all interfaces and search for open sockets.
     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");
+            // Check if the socket matches specified family.
+            if (sock->family_ == family) {
+                // There is at least one socket open, so return.
+                return (true);
             }
         }
     }
-    // Everything is fine, so replace packet filter.
-    packet_filter_ = packet_filter;
+    // There are no open sockets found for the specified family.
+    return (false);
 }
 
-
 void IfaceMgr::stubDetectIfaces() {
     string ifaceName;
     const string v4addr("127.0.0.1"), v6addr("::1");
@@ -445,41 +477,41 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
                 continue;
             }
 
-            sock = openSocket(iface->getName(), *addr, port);
+            // Open socket and join multicast group only if the interface
+            // is multicast-capable.
+            // @todo The DHCPv6 requires multicast so we may want to think
+            // whether we want to open the socket on a multicast-incapable
+            // interface or not. For now, we prefer to be liberal and allow
+            // it for some odd use cases which may utilize non-multicast
+            // interfaces. Perhaps a warning should be emitted if the
+            // interface is not a multicast one.
+            sock = openSocket(iface->getName(), *addr, port,
+                              iface->flag_multicast_);
             if (sock < 0) {
                 const char* errstr = strerror(errno);
-                isc_throw(SocketConfigError, "failed to open link-local socket on "
-                          << addr->toText() << " on interface "
+                isc_throw(SocketConfigError, "failed to open link-local"
+                          " socket on " << addr->toText() << " on interface "
                           << iface->getName() << ", reason: " << errstr);
             }
 
-            // Binding socket to unicast address and then joining multicast group
-            // works well on Mac OS (and possibly other BSDs), but does not work
-            // on Linux.
-            if ( !joinMulticast(sock, iface->getName(),
-                                string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS))) {
-                close(sock);
-                isc_throw(SocketConfigError, "Failed to join "
-                          << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
-                          << " multicast group.");
-            }
-
             count++;
 
             /// @todo: Remove this ifdef once we start supporting BSD systems.
 #if defined(OS_LINUX)
             // To receive multicast traffic, Linux requires binding socket to
             // a multicast group. That in turn doesn't work on NetBSD.
-
-            int sock2 = openSocket(iface->getName(),
-                                   IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
-                                   port);
-            if (sock2 < 0) {
-                const char* errstr = strerror(errno);
-                isc_throw(SocketConfigError, "Failed to open multicast socket on "
-                          << " interface " << iface->getFullName() << ", reason:"
-                          << errstr);
-                iface->delSocket(sock); // delete previously opened socket
+            if (iface->flag_multicast_) {
+                int sock2 =
+                    openSocket(iface->getName(),
+                               IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
+                               port);
+                if (sock2 < 0) {
+                    const char* errstr = strerror(errno);
+                    isc_throw(SocketConfigError, "Failed to open multicast"
+                              " socket on interface " << iface->getFullName()
+                              << ", reason:" << errstr);
+                    iface->delSocket(sock); // delete previously opened socket
+                }
             }
 #endif
         }
@@ -501,7 +533,6 @@ IfaceMgr::handleSocketConfigError(const std::string& errmsg,
     }
 }
 
-
 void
 IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
     for (IfaceCollection::const_iterator iface=ifaces_.begin();
@@ -565,7 +596,7 @@ int IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr,
         return openSocket4(*iface, addr, port, receive_bcast, send_bcast);
 
     } else if (addr.isV6()) {
-        return openSocket6(*iface, addr, port);
+        return openSocket6(*iface, addr, port, receive_bcast);
 
     } else {
         isc_throw(BadValue, "Failed to detect family of address: "
@@ -594,7 +625,7 @@ int IfaceMgr::openSocketFromIface(const std::string& ifname,
             if (addr_it->getFamily() == family) {
                 // We have interface and address so let's open socket.
                 // This may cause isc::Unexpected exception.
-                return (openSocket(iface->getName(), *addr_it, port));
+                return (openSocket(iface->getName(), *addr_it, port, false));
             }
             ++addr_it;
         }
@@ -638,7 +669,7 @@ int IfaceMgr::openSocketFromAddress(const IOAddress& addr,
             if (*addr_it == addr) {
                 // Open socket using local interface, address and port.
                 // This may cause isc::Unexpected exception.
-                return (openSocket(iface->getName(), *addr_it, port));
+                return (openSocket(iface->getName(), *addr_it, port, false));
             }
         }
     }
@@ -717,86 +748,23 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
 }
 
 
-int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
-
-    struct sockaddr_in6 addr6;
-    memset(&addr6, 0, sizeof(addr6));
-    addr6.sin6_family = AF_INET6;
-    addr6.sin6_port = htons(port);
-    if (addr.toText() != "::1") {
-        addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str());
-    }
-
-    memcpy(&addr6.sin6_addr, &addr.toBytes()[0], sizeof(addr6.sin6_addr));
-#ifdef HAVE_SA_LEN
-    addr6.sin6_len = sizeof(addr6);
-#endif
-
-    // TODO: use sockcreator once it becomes available
-
-    // make a socket
-    int sock = socket(AF_INET6, SOCK_DGRAM, 0);
-    if (sock < 0) {
-        isc_throw(SocketConfigError, "Failed to create UDP6 socket.");
-    }
-
-    // Set the REUSEADDR option so that we don't fail to start if
-    // we're being restarted.
-    int flag = 1;
-    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
-                   (char *)&flag, sizeof(flag)) < 0) {
-        close(sock);
-        isc_throw(SocketConfigError, "Can't set SO_REUSEADDR option on dhcpv6 socket.");
-    }
-
-    if (bind(sock, (struct sockaddr *)&addr6, sizeof(addr6)) < 0) {
-        close(sock);
-        isc_throw(SocketConfigError, "Failed to bind socket " << sock << " to " << addr.toText()
-                  << "/port=" << port);
-    }
-#ifdef IPV6_RECVPKTINFO
-    // RFC3542 - a new way
-    if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
-                   &flag, sizeof(flag)) != 0) {
-        close(sock);
-        isc_throw(SocketConfigError, "setsockopt: IPV6_RECVPKTINFO failed.");
-    }
-#else
-    // RFC2292 - an old way
-    if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
-                   &flag, sizeof(flag)) != 0) {
-        close(sock);
-        isc_throw(SocketConfigError, "setsockopt: IPV6_PKTINFO: failed.");
-    }
-#endif
-
-    // multicast stuff
-    if (addr.getAddress().to_v6().is_multicast()) {
-        // both mcast (ALL_DHCP_RELAY_AGENTS_AND_SERVERS and ALL_DHCP_SERVERS)
-        // are link and site-scoped, so there is no sense to join those groups
-        // with global addresses.
-
-        if ( !joinMulticast( sock, iface.getName(),
-                         string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
-            close(sock);
-            isc_throw(SocketConfigError, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
-                      << " multicast group.");
-        }
-    }
-
-    SocketInfo info(addr, port, sock);
+int
+IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port,
+                      const bool join_multicast) {
+    // Assuming that packet filter is not NULL, because its modifier checks it.
+    SocketInfo info = packet_filter6_->openSocket(iface, addr, port,
+                                                  join_multicast);
     iface.addSocket(info);
 
-    return (sock);
+    return (info.sockfd_);
 }
 
-int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr,
+int
+IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr,
                           const uint16_t port, const bool receive_bcast,
                           const bool send_bcast) {
 
-    // Skip checking if the packet_filter_ is non-NULL because this check
-    // has been already done when packet filter object was set.
-
+    // Assuming that packet filter is not NULL, because its modifier checks it.
     SocketInfo info = packet_filter_->openSocket(iface, addr, port,
                                                  receive_bcast, send_bcast);
     iface.addSocket(info);
@@ -805,113 +773,15 @@ int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr,
 }
 
 bool
-IfaceMgr::joinMulticast(int sock, const std::string& ifname,
-const std::string & mcast) {
-
-    struct ipv6_mreq mreq;
-
-    if (inet_pton(AF_INET6, mcast.c_str(),
-                  &mreq.ipv6mr_multiaddr) <= 0) {
-        return (false);
-    }
-
-    mreq.ipv6mr_interface = if_nametoindex(ifname.c_str());
-    if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
-                   &mreq, sizeof(mreq)) < 0) {
-        return (false);
-    }
-
-    return (true);
-}
-
-bool
 IfaceMgr::send(const Pkt6Ptr& pkt) {
-    int result;
-
     Iface* iface = getIface(pkt->getIface());
     if (!iface) {
-        isc_throw(BadValue, "Unable to send Pkt6. Invalid interface ("
+        isc_throw(BadValue, "Unable to send DHCPv6 message. Invalid interface ("
                   << pkt->getIface() << ") specified.");
     }
 
-    memset(&control_buf_[0], 0, control_buf_len_);
-
-
-    // Set the target address we're sending to.
-    sockaddr_in6 to;
-    memset(&to, 0, sizeof(to));
-    to.sin6_family = AF_INET6;
-    to.sin6_port = htons(pkt->getRemotePort());
-    memcpy(&to.sin6_addr,
-           &pkt->getRemoteAddr().toBytes()[0],
-           16);
-    to.sin6_scope_id = pkt->getIndex();
-
-    // Initialize our message header structure.
-    struct msghdr m;
-    memset(&m, 0, sizeof(m));
-    m.msg_name = &to;
-    m.msg_namelen = sizeof(to);
-
-    // Set the data buffer we're sending. (Using this wacky
-    // "scatter-gather" stuff... we only have a single chunk
-    // of data to send, so we declare a single vector entry.)
-
-    // As v structure is a C-style is used for both sending and
-    // receiving data, it is shared between sending and receiving
-    // (sendmsg and recvmsg). It is also defined in system headers,
-    // so we have no control over its definition. To set iov_base
-    // (defined as void*) we must use const cast from void *.
-    // Otherwise C++ compiler would complain that we are trying
-    // to assign const void* to void*.
-    struct iovec v;
-    memset(&v, 0, sizeof(v));
-    v.iov_base = const_cast<void *>(pkt->getBuffer().getData());
-    v.iov_len = pkt->getBuffer().getLength();
-    m.msg_iov = &v;
-    m.msg_iovlen = 1;
-
-    // Setting the interface is a bit more involved.
-    //
-    // We have to create a "control message", and set that to
-    // define the IPv6 packet information. We could set the
-    // source address if we wanted, but we can safely let the
-    // kernel decide what that should be.
-    m.msg_control = &control_buf_[0];
-    m.msg_controllen = control_buf_len_;
-    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&m);
-
-    // FIXME: Code below assumes that cmsg is not NULL, but
-    // CMSG_FIRSTHDR() is coded to return NULL as a possibility.  The
-    // following assertion should never fail, but if it did and you came
-    // here, fix the code. :)
-    assert(cmsg != NULL);
-
-    cmsg->cmsg_level = IPPROTO_IPV6;
-    cmsg->cmsg_type = IPV6_PKTINFO;
-    cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
-    struct in6_pktinfo *pktinfo = convertPktInfo6(CMSG_DATA(cmsg));
-    memset(pktinfo, 0, sizeof(struct in6_pktinfo));
-    pktinfo->ipi6_ifindex = pkt->getIndex();
-    // According to RFC3542, section 20.2, the msg_controllen field
-    // may be set using CMSG_SPACE (which includes padding) or
-    // using CMSG_LEN. Both forms appear to work fine on Linux, FreeBSD,
-    // NetBSD, but OpenBSD appears to have a bug, discussed here:
-    // http://www.archivum.info/mailing.openbsd.bugs/2009-02/00017/
-    // kernel-6080-msg_controllen-of-IPV6_PKTINFO.html
-    // which causes sendmsg to return EINVAL if the CMSG_LEN is
-    // used to set the msg_controllen value.
-    m.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
-
-    pkt->updateTimestamp();
-
-    result = sendmsg(getSocket(*pkt), &m, 0);
-    if (result < 0) {
-        isc_throw(SocketWriteError, "pkt6 send failed: sendmsg() returned"
-                  " with an error: " << strerror(errno));
-    }
-
-    return (result);
+    // Assuming that packet filter is not NULL, because its modifier checks it.
+    return (packet_filter6_->send(*iface, getSocket(*pkt), pkt));
 }
 
 bool
@@ -919,12 +789,11 @@ IfaceMgr::send(const Pkt4Ptr& pkt) {
 
     Iface* iface = getIface(pkt->getIface());
     if (!iface) {
-        isc_throw(BadValue, "Unable to send Pkt4. Invalid interface ("
+        isc_throw(BadValue, "Unable to send DHCPv4 message. Invalid interface ("
                   << pkt->getIface() << ") specified.");
     }
 
-    // Skip checking if packet filter is non-NULL because it has been
-    // already checked when packet filter was set.
+    // Assuming that packet filter is not NULL, because its modifier checks it.
     return (packet_filter_->send(*iface, getSocket(*pkt), pkt));
 }
 
@@ -1023,8 +892,7 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
     }
 
     // Now we have a socket, let's get some data from it!
-    // Skip checking if packet filter is non-NULL because it has been
-    // already checked when packet filter was set.
+    // Assuming that packet filter is not NULL, because its modifier checks it.
     return (packet_filter_->receive(*iface, *candidate));
 }
 
@@ -1119,102 +987,8 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
     if (!candidate) {
         isc_throw(SocketReadError, "received data over unknown socket");
     }
-
-    // Now we have a socket, let's get some data from it!
-    uint8_t buf[RCVBUFSIZE];
-    memset(&control_buf_[0], 0, control_buf_len_);
-    struct sockaddr_in6 from;
-    memset(&from, 0, sizeof(from));
-
-    // Initialize our message header structure.
-    struct msghdr m;
-    memset(&m, 0, sizeof(m));
-
-    // Point so we can get the from address.
-    m.msg_name = &from;
-    m.msg_namelen = sizeof(from);
-
-    // Set the data buffer we're receiving. (Using this wacky
-    // "scatter-gather" stuff... but we that doesn't really make
-    // sense for us, so we use a single vector entry.)
-    struct iovec v;
-    memset(&v, 0, sizeof(v));
-    v.iov_base = static_cast<void*>(buf);
-    v.iov_len = RCVBUFSIZE;
-    m.msg_iov = &v;
-    m.msg_iovlen = 1;
-
-    // Getting the interface is a bit more involved.
-    //
-    // We set up some space for a "control message". We have
-    // previously asked the kernel to give us packet
-    // information (when we initialized the interface), so we
-    // should get the destination address from that.
-    m.msg_control = &control_buf_[0];
-    m.msg_controllen = control_buf_len_;
-
-    result = recvmsg(candidate->sockfd_, &m, 0);
-
-    struct in6_addr to_addr;
-    memset(&to_addr, 0, sizeof(to_addr));
-
-    int ifindex = -1;
-    if (result >= 0) {
-        struct in6_pktinfo* pktinfo = NULL;
-
-
-        // If we did read successfully, then we need to loop
-        // through the control messages we received and
-        // find the one with our destination address.
-        //
-        // We also keep a flag to see if we found it. If we
-        // didn't, then we consider this to be an error.
-        bool found_pktinfo = false;
-        struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
-        while (cmsg != NULL) {
-            if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
-                (cmsg->cmsg_type == IPV6_PKTINFO)) {
-                pktinfo = convertPktInfo6(CMSG_DATA(cmsg));
-                to_addr = pktinfo->ipi6_addr;
-                ifindex = pktinfo->ipi6_ifindex;
-                found_pktinfo = true;
-                break;
-            }
-            cmsg = CMSG_NXTHDR(&m, cmsg);
-        }
-        if (!found_pktinfo) {
-            isc_throw(SocketReadError, "unable to find pktinfo");
-        }
-    } else {
-        isc_throw(SocketReadError, "failed to receive data");
-    }
-
-    // Let's create a packet.
-    Pkt6Ptr pkt;
-    try {
-        pkt = Pkt6Ptr(new Pkt6(buf, result));
-    } catch (const std::exception& ex) {
-        isc_throw(SocketReadError, "failed to create new packet");
-    }
-
-    pkt->updateTimestamp();
-
-    pkt->setLocalAddr(IOAddress::fromBytes(AF_INET6,
-                      reinterpret_cast<const uint8_t*>(&to_addr)));
-    pkt->setRemoteAddr(IOAddress::fromBytes(AF_INET6,
-                       reinterpret_cast<const uint8_t*>(&from.sin6_addr)));
-    pkt->setRemotePort(ntohs(from.sin6_port));
-    pkt->setIndex(ifindex);
-
-    Iface* received = getIface(pkt->getIndex());
-    if (received) {
-        pkt->setIface(received->getName());
-    } else {
-        isc_throw(SocketReadError, "received packet over unknown interface"
-                  << "(ifindex=" << pkt->getIndex() << ")");
-    }
-
-    return (pkt);
+    // Assuming that packet filter is not NULL, because its modifier checks it.
+    return (packet_filter6_->receive(*candidate));
 }
 
 uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6& pkt) {
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
index e02b9b1..0ac845d 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -21,6 +21,7 @@
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt_filter.h>
+#include <dhcp/pkt_filter6.h>
 
 #include <boost/function.hpp>
 #include <boost/noncopyable.hpp>
@@ -554,8 +555,8 @@ public:
     /// @param ifname name of the interface
     /// @param addr address to be bound.
     /// @param port UDP port.
-    /// @param receive_bcast configure IPv4 socket to receive broadcast messages.
-    /// This parameter is ignored for IPv6 sockets.
+    /// @param receive_bcast configure IPv4 socket to receive broadcast
+    /// messages or IPv6 socket to join multicast group.
     /// @param send_bcast configure IPv4 socket to send broadcast messages.
     /// This parameter is ignored for IPv6 sockets.
     ///
@@ -577,11 +578,13 @@ public:
     /// Instead, the method searches through the addresses on the specified
     /// interface and selects one that matches the address family.
     ///
+    /// @note This method does not join the socket to the multicast group.
+    ///
     /// @param ifname name of the interface
     /// @param port UDP port
     /// @param family address family (AF_INET or AF_INET6)
-    /// @return socket descriptor, if socket creation, binding and multicast
-    /// group join were all successful.
+    /// @return socket descriptor, if socket creation and binding was
+    /// successful.
     /// @throw isc::Unexpected if failed to create and bind socket.
     /// @throw isc::BadValue if there is no address on specified interface
     /// that belongs to given family.
@@ -594,10 +597,12 @@ public:
     /// This methods differs from \ref openSocket in that it does not require
     /// the specification of the interface to which the socket will be bound.
     ///
+    /// @note This method does not join the socket to the multicast group.
+    ///
     /// @param addr address to be bound
     /// @param port UDP port
-    /// @return socket descriptor, if socket creation, binding and multicast
-    /// group join were all successful.
+    /// @return socket descriptor, if socket creation and binding was
+    /// successful.
     /// @throw isc::Unexpected if failed to create and bind socket
     /// @throw isc::BadValue if specified address is not available on
     /// any interface
@@ -611,10 +616,12 @@ public:
     /// identified, \ref openSocket is called to open a socket and bind it to
     /// the interface, address and port.
     ///
+    /// @note This method does not join the socket to a multicast group.
+    ///
     /// @param remote_addr remote address to connect to
     /// @param port UDP port
-    /// @return socket descriptor, if socket creation, binding and multicast
-    /// group join were all successful.
+    /// @return socket descriptor, if socket creation and binding was
+    /// successful.
     /// @throw isc::Unexpected if failed to create and bind socket
     int openSocketFromRemoteAddress(const isc::asiolink::IOAddress& remote_addr,
                                     const uint16_t port);
@@ -749,22 +756,45 @@ public:
         session_callback_ = callback;
     }
 
-    /// @brief Set Packet Filter object to handle send/receive packets.
+    /// @brief Set packet filter object to handle sending and receiving DHCPv4
+    /// messages.
     ///
-    /// 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. 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.
+    /// Packet filter objects provide means for the @c IfaceMgr to open sockets
+    /// for IPv4 packets reception and sending. This function sets custom packet
+    /// filter (represented by a class derived from PktFilter) to be used by
+    /// @c 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.
+    /// @param packet_filter A pointer to the new packet filter object to be
+    /// used by @c IfaceMgr.
     ///
     /// @throw InvalidPacketFilter if provided packet filter object is NULL.
-    /// @throw PacketFilterChangeDenied if there are open IPv4 sockets
+    /// @throw PacketFilterChangeDenied if there are open IPv4 sockets.
     void setPacketFilter(const PktFilterPtr& packet_filter);
 
+    /// @brief Set packet filter object to handle sending and receving DHCPv6
+    /// messages.
+    ///
+    /// Packet filter objects provide means for the @c IfaceMgr to open sockets
+    /// for IPv6 packets reception and sending. This function sets the new
+    /// instance of the packet filter which will be used by @c IfaceMgr to send
+    /// and receive DHCPv6 messages, until replaced by another packet filter.
+    ///
+    /// It is required that DHCPv6 messages are send and received using methods
+    /// of the same object that was used to open socket. Therefore, it is
+    /// required that all IPv6 sockets are closed prior to calling this
+    /// function. Call closeSockets(AF_INET6) to close all hanging IPv6 sockets
+    /// opened by the current packet filter object.
+    ///
+    /// @param packet_filter A pointer to the new packet filter object to be
+    /// used by @c IfaceMgr.
+    ///
+    /// @throw isc::dhcp::InvalidPacketFilter if specified object is NULL.
+    /// @throw isc::dhcp::PacketFilterChangeDenied if there are open IPv6
+    /// sockets.
+    void setPacketFilter(const PktFilter6Ptr& packet_filter);
+
     /// @brief Set Packet Filter object to handle send/receive packets.
     ///
     /// This function sets Packet Filter object to be used by IfaceMgr,
@@ -832,9 +862,13 @@ protected:
     /// @param iface reference to interface structure.
     /// @param addr an address the created socket should be bound to
     /// @param port a port that created socket should be bound to
+    /// @param join_multicast A boolean parameter which indicates whether
+    /// socket should join All_DHCP_Relay_Agents_and_servers multicast
+    /// group.
     ///
     /// @return socket descriptor
-    int openSocket6(Iface& iface, const isc::asiolink::IOAddress& addr, uint16_t port);
+    int openSocket6(Iface& iface, const isc::asiolink::IOAddress& addr,
+                    uint16_t port, const bool join_multicast);
 
     /// @brief Detects network interfaces.
     ///
@@ -899,23 +933,6 @@ protected:
     SessionCallback session_callback_;
 private:
 
-    /// @brief Joins IPv6 multicast group on a socket.
-    ///
-    /// Socket must be created and bound to an address. Note that this
-    /// address is different than the multicast address. For example DHCPv6
-    /// server should bind its socket to link-local address (fe80::1234...)
-    /// and later join ff02::1:2 multicast group.
-    ///
-    /// @param sock socket fd (socket must be bound)
-    /// @param ifname interface name (for link-scoped multicast groups)
-    /// @param mcast multicast address to join (e.g. "ff02::1:2")
-    ///
-    /// @return true if multicast join was successful
-    ///
-    bool
-    joinMulticast(int sock, const std::string& ifname,
-                  const std::string& mcast);
-
     /// @brief Identifies local network address to be used to
     /// connect to remote address.
     ///
@@ -951,15 +968,29 @@ private:
     void handleSocketConfigError(const std::string& errmsg,
                                  IfaceMgrErrorMsgCallback handler);
 
+    /// @brief Checks if there is at least one socket of the specified family
+    /// open.
+    ///
+    /// @param family A socket family.
+    ///
+    /// @return true if there is at least one socket open, false otherwise.
+    bool hasOpenSocket(const uint16_t family) const;
+
     /// Holds instance of a class derived from PktFilter, used by the
     /// IfaceMgr to open sockets and send/receive packets through these
     /// sockets. It is possible to supply custom object using
-    /// setPacketFilter class. Various Packet Filters differ mainly by using
+    /// setPacketFilter method. Various Packet Filters differ mainly by using
     /// different types of sockets, e.g. SOCK_DGRAM,  SOCK_RAW and different
     /// 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).
     PktFilterPtr packet_filter_;
+
+    /// Holds instance of a class derived from PktFilter6, used by the
+    /// IfaceMgr to manage sockets used to send and receive DHCPv6
+    /// messages. It is possible to supply a custom object using
+    /// setPacketFilter method.
+    PktFilter6Ptr packet_filter6_;
 };
 
 }; // namespace isc::dhcp
diff --git a/src/lib/dhcp/libdhcp++.dox b/src/lib/dhcp/libdhcp++.dox
index ab4d7eb..e1d7920 100644
--- a/src/lib/dhcp/libdhcp++.dox
+++ b/src/lib/dhcp/libdhcp++.dox
@@ -185,5 +185,50 @@ the regular IP/UDP socket. The isc::dhcp::PktFilterInet should be used in all
 cases when an application using the libdhcp++ doesn't require sending
 DHCP messages to a device which doesn't have an address yet.
 
+ at section libdhcpPktFilter6 Switchable Packet Filters for DHCPv6
+
+The DHCPv6 implementation doesn't suffer from the problems described in \ref
+libdhcpPktFilter. Therefore, the socket creation and methods used to send
+and receive DHCPv6 messages are common for all OSes. However, there is
+still a need to customize the operations on the sockets to reliably unit test
+the \ref isc::dhcp::IfaceMgr logic.
+
+The \ref isc::dhcp::IfaceMgr::openSockets6 function examines configuration
+of detected interfaces for their availability to listen DHCPv6 traffic. For
+all running interfaces (except local loopback) it will try to open a socket
+and bind it to the link local or global unicast address. The socket will
+not be opened on the interface which is down or for which it was explicitly
+specified that it should not be used to listen to DHCPv6 messages. There is
+a substantial amount of logic in this function that has to be unit tested for
+various interface configurations, e.g.:
+- multiple interfaces with link-local addresses only
+- multiple interfaces, some of them having global unicast addresses,
+- multiple interfaces, some of them disabled
+- no interfaces
+
+The \ref isc::dhcp::IfaceMgr::openSockets6 function attempts to open
+sockets on detected interfaces. At the same time, the number of interfaces,
+and their configuration is specific to OS where the tests are being run.
+So the test doesn't have any means to configure interfaces for the test case
+being run. Moreover, a unit test should not change the configuration of the
+system. For example, a change to the configuration of the interface which
+is used to access the machine running a test, may effectively break the
+access to this machine.
+
+In order to overcome the problem described above, the unit tests use
+fake interfaces which can be freely added, configured and removed from the
+\ref isc::dhcp::IfaceMgr. Obviously, it is not possible to open a socket
+on a fake interface, nor use it to send or receive IP packets. To mimic
+socket operations on fake interfaces it is required that the functions
+which open sockets, send messages and receive messages have to be
+customizable. This is achieved by implementation of replaceable packet
+filter objects which derive from the \ref isc::dhcp::PktFilter6 class.
+The default implementation of this class is \ref isc::dhcp::PktFilterInet6
+which creates a regular datagram IPv6/UDPv6 socket. The unit tests use a
+stub implementation isc::dhcp::test::PktFilter6Stub which contains no-op
+functions.
+
+Use \ref isc::dhcp::IfaceMgr::setPacketFilter function to set the custom packet
+filter object to be used by Interface Manager.
 
 */
diff --git a/src/lib/dhcp/pkt_filter6.cc b/src/lib/dhcp/pkt_filter6.cc
new file mode 100644
index 0000000..03927fb
--- /dev/null
+++ b/src/lib/dhcp/pkt_filter6.cc
@@ -0,0 +1,44 @@
+// 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 <dhcp/pkt_filter6.h>
+
+namespace isc {
+namespace dhcp {
+
+bool
+PktFilter6::joinMulticast(int sock, const std::string& ifname,
+                          const std::string & mcast) {
+
+    struct ipv6_mreq mreq;
+    memset(&mreq, 0, sizeof(ipv6_mreq));
+
+    // Convert the multicast address to a binary form.
+    if (inet_pton(AF_INET6, mcast.c_str(), &mreq.ipv6mr_multiaddr) <= 0) {
+        return (false);
+    }
+    // Set the interface being used.
+    mreq.ipv6mr_interface = if_nametoindex(ifname.c_str());
+    // Join the multicast group.
+    if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+                   &mreq, sizeof(mreq)) < 0) {
+        return (false);
+    }
+
+    return (true);
+}
+
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
diff --git a/src/lib/dhcp/pkt_filter6.h b/src/lib/dhcp/pkt_filter6.h
new file mode 100644
index 0000000..dcddf6c
--- /dev/null
+++ b/src/lib/dhcp/pkt_filter6.h
@@ -0,0 +1,150 @@
+// 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 PKT_FILTER6_H
+#define PKT_FILTER6_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/pkt6.h>
+
+namespace isc {
+namespace dhcp {
+
+/// Forward declaration to the structure describing a socket.
+struct SocketInfo;
+
+/// Forward declaration to the class representing interface
+class Iface;
+
+/// @brief Abstract packet handling class for DHCPv6.
+///
+/// This class defines methods for performing low level operations on IPv6
+/// socket:
+/// - open socket,
+/// - send DHCPv6 message through the socket,
+/// - receive DHCPv6 through the socket.
+///
+/// Methods exposed by this class are called through the @c IfaceMgr only. They
+/// are not meant to be called directly, except unit testing.
+///
+/// The @c IfaceMgr is responsible for managing the pool of sockets. In
+/// particular, @c IfaceMgr detects interfaces suitable to send/receive DHCPv6
+/// messages. When it intends to open a socket on a particular interface, it
+/// will call the PktFilter6::openSocket. If this call is successful, the
+/// structure describing a new socket is returned.
+///
+/// In order to send or receive a DHCPv6 message through this socket,
+/// the @c IfaceMgr must use PktFilter6::send or PktFilter6::receive
+/// functions of the same class that has been used to open a socket,
+/// i.e. all send/receive operations should be performed using this
+/// particular class.
+///
+/// The major motivation behind creating a separate class, to manage low level
+/// operations using sockets, is to make @c IfaceMgr unit testable. By providing
+/// a stub implementation of this class which mimics the behavior of the real
+/// socket handling class, it is possible to simulate and test various
+/// conditions. For example, the @c IfaceMgr::openSockets function will try to
+/// open sockets on all available interfaces. The test doesn't have any means
+/// to know which interfaces are present. In addition, even if the network
+/// interface detection was implemented on the test side, there is no guarantee
+/// that the particular system has sufficient number of suitable IPv6-enabled
+/// interfaces available for a particular test. Moreover, the test may need
+/// to tweak some of the interface configuration to cover certain test
+/// scenarios. The proposed solution is to not use the actual interfaces
+/// but simply create a pool of fake interfaces which configuration
+/// can be freely modified by a test. This however implies that operations
+/// on sockets must be simulated.
+///
+/// @note This class is named after @c PktFilter abstract class which exposes
+/// similar interface for DHVPv4. However, the PktFilter class is devoted to
+/// solve the problem of sending DHCPv4 messages to the hosts which don't have
+/// an IP address yet (a.k.a. direct DHCPv4 traffic). Where required, the
+/// custom implementations of @c PktFilter are provided to send and receive
+/// messages through raw sockets. In order to filter out the desired traffic
+/// Linux Packet Filtering or Berkeley Packet Filtering is used, hence the
+/// name of the class. In case of DHCPv6 regular IPv6/UDPv6 sockets are used
+/// and derived classes do not use Linux or Berkeley Packet Filtering.
+class PktFilter6 {
+public:
+
+    /// @brief Virtual Destructor.
+    virtual ~PktFilter6() { }
+
+    /// @brief Opens a socket.
+    ///
+    /// This function open an IPv6 socket on an interface and binds it to a
+    /// specified UDP port and IPv6 address.
+    ///
+    /// @param iface Interface descriptor.
+    /// @param addr Address on the interface to be used to send packets.
+    /// @param port Port number.
+    /// @param join_multicast A boolean parameter which indicates whether
+    /// socket should join All_DHCP_Relay_Agents_and_servers multicast
+    /// group.
+    ///
+    /// @return A structure describing a primary and fallback socket.
+    virtual SocketInfo openSocket(const Iface& iface,
+                                  const isc::asiolink::IOAddress& addr,
+                                  const uint16_t port,
+                                  const bool join_multicast) = 0;
+
+    /// @brief Receives DHCPv6 message on the interface.
+    ///
+    /// This function receives a single DHCPv6 message through using a socket
+    /// open on a specified interface.
+    ///
+    /// @param socket_info A structure holding socket information.
+    ///
+    /// @return A pointer to received message.
+    virtual Pkt6Ptr receive(const SocketInfo& socket_info) = 0;
+
+    /// @brief Sends DHCPv6 message through a specified interface and socket.
+    ///
+    /// This function sends a DHCPv6 message through a specified interface and
+    /// socket. In general, there may be multiple sockets open on a single
+    /// interface as a single interface may have multiple IPv6 addresses.
+    ///
+    /// @param iface Interface to be used to send packet.
+    /// @param sockfd A socket descriptor
+    /// @param pkt A packet to be sent.
+    ///
+    /// @return A result of sending the message. It is 0 if successful.
+    virtual int send(const Iface& iface, uint16_t sockfd,
+                     const Pkt6Ptr& pkt) = 0;
+
+    /// @brief Joins IPv6 multicast group on a socket.
+    ///
+    /// Socket must be created and bound to an address. Note that this
+    /// address is different than the multicast address. For example DHCPv6
+    /// server should bind its socket to link-local address (fe80::1234...)
+    /// and later join ff02::1:2 multicast group.
+    ///
+    /// @param sock A socket descriptor (socket must be bound).
+    /// @param ifname An interface name (for link-scoped multicast groups).
+    /// @param mcast A multicast address to join (e.g. "ff02::1:2").
+    ///
+    /// @return true if multicast join was successful
+    static bool joinMulticast(int sock, const std::string& ifname,
+                              const std::string & mcast);
+
+};
+
+
+/// Pointer to a PktFilter object.
+typedef boost::shared_ptr<PktFilter6> PktFilter6Ptr;
+
+} // namespace isc::dhcp
+} // namespace isc
+
+#endif // PKT_FILTER6_H
diff --git a/src/lib/dhcp/pkt_filter_inet6.cc b/src/lib/dhcp/pkt_filter_inet6.cc
new file mode 100644
index 0000000..1ec75f3
--- /dev/null
+++ b/src/lib/dhcp/pkt_filter_inet6.cc
@@ -0,0 +1,286 @@
+// 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 <dhcp/iface_mgr.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/pkt_filter_inet6.h>
+#include <util/io/pktinfo_utilities.h>
+
+#include <netinet/in.h>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+PktFilterInet6::PktFilterInet6()
+: control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
+    control_buf_(new char[control_buf_len_]) {
+}
+
+SocketInfo
+PktFilterInet6::openSocket(const Iface& iface,
+                           const isc::asiolink::IOAddress& addr,
+                           const uint16_t port,
+                           const bool join_multicast) {
+    struct sockaddr_in6 addr6;
+    memset(&addr6, 0, sizeof(addr6));
+    addr6.sin6_family = AF_INET6;
+    addr6.sin6_port = htons(port);
+    if (addr.toText() != "::1") {
+        addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str());
+    }
+
+    memcpy(&addr6.sin6_addr, &addr.toBytes()[0], sizeof(addr6.sin6_addr));
+#ifdef HAVE_SA_LEN
+    addr6.sin6_len = sizeof(addr6);
+#endif
+
+    // @todo use sockcreator once it becomes available
+
+    // make a socket
+    int sock = socket(AF_INET6, SOCK_DGRAM, 0);
+    if (sock < 0) {
+        isc_throw(SocketConfigError, "Failed to create UDP6 socket.");
+    }
+
+    // Set SO_REUSEADDR option.
+    int flag = 1;
+    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+                   (char *)&flag, sizeof(flag)) < 0) {
+        close(sock);
+        isc_throw(SocketConfigError, "Can't set SO_REUSEADDR option on dhcpv6 socket.");
+    }
+
+    if (bind(sock, (struct sockaddr *)&addr6, sizeof(addr6)) < 0) {
+        close(sock);
+        isc_throw(SocketConfigError, "Failed to bind socket " << sock << " to " << addr.toText()
+                  << "/port=" << port);
+    }
+#ifdef IPV6_RECVPKTINFO
+    // RFC3542 - a new way
+    if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+                   &flag, sizeof(flag)) != 0) {
+        close(sock);
+        isc_throw(SocketConfigError, "setsockopt: IPV6_RECVPKTINFO failed.");
+    }
+#else
+    // RFC2292 - an old way
+    if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
+                   &flag, sizeof(flag)) != 0) {
+        close(sock);
+        isc_throw(SocketConfigError, "setsockopt: IPV6_PKTINFO: failed.");
+    }
+#endif
+
+    // Join All_DHCP_Relay_Agents_and_Servers multicast group if
+    // requested.
+    if (join_multicast &&
+        !joinMulticast(sock, iface.getName(),
+                       std::string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS))) {
+        close(sock);
+        isc_throw(SocketConfigError, "Failed to join "
+                  << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
+                  << " multicast group.");
+    }
+
+    return (SocketInfo(addr, port, sock));
+}
+
+Pkt6Ptr
+PktFilterInet6::receive(const SocketInfo& socket_info) {
+    // Now we have a socket, let's get some data from it!
+    uint8_t buf[IfaceMgr::RCVBUFSIZE];
+    memset(&control_buf_[0], 0, control_buf_len_);
+    struct sockaddr_in6 from;
+    memset(&from, 0, sizeof(from));
+
+    // Initialize our message header structure.
+    struct msghdr m;
+    memset(&m, 0, sizeof(m));
+
+    // Point so we can get the from address.
+    m.msg_name = &from;
+    m.msg_namelen = sizeof(from);
+
+    // Set the data buffer we're receiving. (Using this wacky
+    // "scatter-gather" stuff... but we that doesn't really make
+    // sense for us, so we use a single vector entry.)
+    struct iovec v;
+    memset(&v, 0, sizeof(v));
+    v.iov_base = static_cast<void*>(buf);
+    v.iov_len = IfaceMgr::RCVBUFSIZE;
+    m.msg_iov = &v;
+    m.msg_iovlen = 1;
+
+    // Getting the interface is a bit more involved.
+    //
+    // We set up some space for a "control message". We have
+    // previously asked the kernel to give us packet
+    // information (when we initialized the interface), so we
+    // should get the destination address from that.
+    m.msg_control = &control_buf_[0];
+    m.msg_controllen = control_buf_len_;
+
+    int result = recvmsg(socket_info.sockfd_, &m, 0);
+
+    struct in6_addr to_addr;
+    memset(&to_addr, 0, sizeof(to_addr));
+
+    int ifindex = -1;
+    if (result >= 0) {
+        struct in6_pktinfo* pktinfo = NULL;
+
+
+        // If we did read successfully, then we need to loop
+        // through the control messages we received and
+        // find the one with our destination address.
+        //
+        // We also keep a flag to see if we found it. If we
+        // didn't, then we consider this to be an error.
+        bool found_pktinfo = false;
+        struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
+        while (cmsg != NULL) {
+            if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
+                (cmsg->cmsg_type == IPV6_PKTINFO)) {
+                pktinfo = util::io::internal::convertPktInfo6(CMSG_DATA(cmsg));
+                to_addr = pktinfo->ipi6_addr;
+                ifindex = pktinfo->ipi6_ifindex;
+                found_pktinfo = true;
+                break;
+            }
+            cmsg = CMSG_NXTHDR(&m, cmsg);
+        }
+        if (!found_pktinfo) {
+            isc_throw(SocketReadError, "unable to find pktinfo");
+        }
+    } else {
+        isc_throw(SocketReadError, "failed to receive data");
+    }
+
+    // Let's create a packet.
+    Pkt6Ptr pkt;
+    try {
+        pkt = Pkt6Ptr(new Pkt6(buf, result));
+    } catch (const std::exception& ex) {
+        isc_throw(SocketReadError, "failed to create new packet");
+    }
+
+    pkt->updateTimestamp();
+
+    pkt->setLocalAddr(IOAddress::fromBytes(AF_INET6,
+                      reinterpret_cast<const uint8_t*>(&to_addr)));
+    pkt->setRemoteAddr(IOAddress::fromBytes(AF_INET6,
+                       reinterpret_cast<const uint8_t*>(&from.sin6_addr)));
+    pkt->setRemotePort(ntohs(from.sin6_port));
+    pkt->setIndex(ifindex);
+
+    Iface* received = IfaceMgr::instance().getIface(pkt->getIndex());
+    if (received) {
+        pkt->setIface(received->getName());
+    } else {
+        isc_throw(SocketReadError, "received packet over unknown interface"
+                  << "(ifindex=" << pkt->getIndex() << ")");
+    }
+
+    return (pkt);
+
+}
+
+int
+PktFilterInet6::send(const Iface&, uint16_t sockfd, const Pkt6Ptr& pkt) {
+
+    memset(&control_buf_[0], 0, control_buf_len_);
+
+    // Set the target address we're sending to.
+    sockaddr_in6 to;
+    memset(&to, 0, sizeof(to));
+    to.sin6_family = AF_INET6;
+    to.sin6_port = htons(pkt->getRemotePort());
+    memcpy(&to.sin6_addr,
+           &pkt->getRemoteAddr().toBytes()[0],
+           16);
+    to.sin6_scope_id = pkt->getIndex();
+
+    // Initialize our message header structure.
+    struct msghdr m;
+    memset(&m, 0, sizeof(m));
+    m.msg_name = &to;
+    m.msg_namelen = sizeof(to);
+
+    // Set the data buffer we're sending. (Using this wacky
+    // "scatter-gather" stuff... we only have a single chunk
+    // of data to send, so we declare a single vector entry.)
+
+    // As v structure is a C-style is used for both sending and
+    // receiving data, it is shared between sending and receiving
+    // (sendmsg and recvmsg). It is also defined in system headers,
+    // so we have no control over its definition. To set iov_base
+    // (defined as void*) we must use const cast from void *.
+    // Otherwise C++ compiler would complain that we are trying
+    // to assign const void* to void*.
+    struct iovec v;
+    memset(&v, 0, sizeof(v));
+    v.iov_base = const_cast<void *>(pkt->getBuffer().getData());
+    v.iov_len = pkt->getBuffer().getLength();
+    m.msg_iov = &v;
+    m.msg_iovlen = 1;
+
+    // Setting the interface is a bit more involved.
+    //
+    // We have to create a "control message", and set that to
+    // define the IPv6 packet information. We could set the
+    // source address if we wanted, but we can safely let the
+    // kernel decide what that should be.
+    m.msg_control = &control_buf_[0];
+    m.msg_controllen = control_buf_len_;
+    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&m);
+
+    // FIXME: Code below assumes that cmsg is not NULL, but
+    // CMSG_FIRSTHDR() is coded to return NULL as a possibility.  The
+    // following assertion should never fail, but if it did and you came
+    // here, fix the code. :)
+    assert(cmsg != NULL);
+
+    cmsg->cmsg_level = IPPROTO_IPV6;
+    cmsg->cmsg_type = IPV6_PKTINFO;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+    struct in6_pktinfo *pktinfo =
+        util::io::internal::convertPktInfo6(CMSG_DATA(cmsg));
+    memset(pktinfo, 0, sizeof(struct in6_pktinfo));
+    pktinfo->ipi6_ifindex = pkt->getIndex();
+    // According to RFC3542, section 20.2, the msg_controllen field
+    // may be set using CMSG_SPACE (which includes padding) or
+    // using CMSG_LEN. Both forms appear to work fine on Linux, FreeBSD,
+    // NetBSD, but OpenBSD appears to have a bug, discussed here:
+    // http://www.archivum.info/mailing.openbsd.bugs/2009-02/00017/
+    // kernel-6080-msg_controllen-of-IPV6_PKTINFO.html
+    // which causes sendmsg to return EINVAL if the CMSG_LEN is
+    // used to set the msg_controllen value.
+    m.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
+
+    pkt->updateTimestamp();
+
+    int result = sendmsg(sockfd, &m, 0);
+    if  (result < 0) {
+        isc_throw(SocketWriteError, "pkt6 send failed: sendmsg() returned"
+                  " with an error: " << strerror(errno));
+    }
+
+    return (result);
+}
+
+
+}
+}
diff --git a/src/lib/dhcp/pkt_filter_inet6.h b/src/lib/dhcp/pkt_filter_inet6.h
new file mode 100644
index 0000000..c475cf5
--- /dev/null
+++ b/src/lib/dhcp/pkt_filter_inet6.h
@@ -0,0 +1,98 @@
+// 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 PKT_FILTER_INET6_H
+#define PKT_FILTER_INET6_H
+
+#include <dhcp/pkt_filter6.h>
+#include <boost/scoped_array.hpp>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief A DHCPv6 packet handling class using datagram sockets.
+///
+/// This class opens a datagram IPv6/UDPv6 socket. It also implements functions
+/// to send and receive DHCPv6 messages through this socket. It is a default
+/// class to be used by @c IfaceMgr to access IPv6 sockets.
+class PktFilterInet6 : public PktFilter6 {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Initializes a control buffer used in the message transmission.
+    PktFilterInet6();
+
+    /// @brief Opens a socket.
+    ///
+    /// This function open an IPv6 socket on an interface and binds it to a
+    /// specified UDP port and IP address.
+    ///
+    /// @param iface Interface descriptor.
+    /// @param addr Address on the interface to be used to send packets.
+    /// @param port Port number.
+    /// @param join_multicast A boolean parameter which indicates whether
+    /// socket should join All_DHCP_Relay_Agents_and_servers multicast
+    /// group.
+    ///
+    /// @return A structure describing a primary and fallback socket.
+    /// @throw isc::dhcp::SocketConfigError if error occured when opening
+    /// or configuring a socket.
+    virtual SocketInfo openSocket(const Iface& iface,
+                                  const isc::asiolink::IOAddress& addr,
+                                  const uint16_t port,
+                                  const bool join_multicast);
+
+    /// @brief Receives DHCPv6 message on the interface.
+    ///
+    /// This function receives a single DHCPv6 message through a socket
+    /// open on a specified interface. This function will block if there is
+    /// no message waiting on the specified socket. Therefore the @c IfaceMgr
+    /// must first check that there is any message on the socket (using
+    /// select function) prior to calling this function.
+    ///
+    /// @param socket_info A structure holding socket information.
+    ///
+    /// @return A pointer to received message.
+    /// @throw isc::dhcp::SocketReadError if error occurred during packet
+    /// reception.
+    virtual Pkt6Ptr receive(const SocketInfo& socket_info);
+
+    /// @brief Sends DHCPv6 message through a specified interface and socket.
+    ///
+    /// Thie function sends a DHCPv6 message through a specified interface and
+    /// socket. In general, there may be multiple sockets open on a single
+    /// interface as a single interface may have multiple IPv6 addresses.
+    ///
+    /// @param iface Interface to be used to send packet.
+    /// @param sockfd A socket descriptor
+    /// @param pkt A packet to be sent.
+    ///
+    /// @return A result of sending the message. It is 0 if successful.
+    /// @throw isc::dhcp::SocketWriteError if error occured when sending a
+    /// packet.
+    virtual int send(const Iface& iface, uint16_t sockfd,
+                     const Pkt6Ptr& pkt);
+
+private:
+    /// Length of the control_buf_ array.
+    size_t control_buf_len_;
+    /// Control buffer, used in transmission and reception.
+    boost::scoped_array<char> control_buf_;
+};
+
+} // namespace isc::dhcp
+} // namespace isc
+
+#endif // PKT_FILTER_INET6_H
diff --git a/src/lib/dhcp/pkt_filter_lpf.h b/src/lib/dhcp/pkt_filter_lpf.h
index 279c864..1815dda 100644
--- a/src/lib/dhcp/pkt_filter_lpf.h
+++ b/src/lib/dhcp/pkt_filter_lpf.h
@@ -24,11 +24,10 @@ namespace dhcp {
 
 /// @brief Packet handling class using Linux Packet Filtering
 ///
-/// This class provides methods to send and recive packet using raw sockets
-/// and Linux Packet Filtering.
-///
-/// @warning This class is not implemented yet. Therefore all functions
-/// currently throw isc::NotImplemented exception.
+/// This class provides methods to send and recive DHCPv4 messages using raw
+/// sockets and Linux Packet Filtering. It is used by @c isc::dhcp::IfaceMgr
+/// to send DHCPv4 messages to the hosts which don't have an IPv4 address
+/// assigned yet.
 class PktFilterLPF : public PktFilter {
 public:
 
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index 19678dd..6b3683d 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -50,7 +50,9 @@ libdhcp___unittests_SOURCES += pkt4_unittest.cc
 libdhcp___unittests_SOURCES += pkt6_unittest.cc
 libdhcp___unittests_SOURCES += pkt_filter_unittest.cc
 libdhcp___unittests_SOURCES += pkt_filter_inet_unittest.cc
+libdhcp___unittests_SOURCES += pkt_filter_inet6_unittest.cc
 libdhcp___unittests_SOURCES += pkt_filter_test_utils.h pkt_filter_test_utils.cc
+libdhcp___unittests_SOURCES += pkt_filter6_test_utils.h pkt_filter6_test_utils.cc
 
 if OS_LINUX
 libdhcp___unittests_SOURCES += pkt_filter_lpf_unittest.cc
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index 129b9ca..472210b 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -19,6 +19,7 @@
 #include <dhcp/iface_mgr.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt_filter.h>
+#include <dhcp/tests/pkt_filter6_test_utils.h>
 
 #include <boost/bind.hpp>
 #include <boost/scoped_ptr.hpp>
@@ -35,6 +36,7 @@ using namespace std;
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
+using namespace isc::dhcp::test;
 using boost::scoped_ptr;
 
 namespace {
@@ -156,7 +158,7 @@ public:
     /// @brief Returns the collection of existing interfaces.
     IfaceCollection& getIfacesLst() { return (ifaces_); }
 
-    /// @brief This function creates fictious interfaces with fictious
+    /// @brief This function creates fictitious interfaces with fictious
     /// addresses.
     ///
     /// These interfaces can be used in tests that don't actually try
@@ -168,11 +170,21 @@ public:
         ifaces_.clear();
 
         // local loopback
-        ifaces_.push_back(createIface("lo", 0, "127.0.0.1"));
+        Iface lo = createIface("lo", 0);
+        lo.addAddress(IOAddress("127.0.0.1"));
+        lo.addAddress(IOAddress("::1"));
+        ifaces_.push_back(lo);
         // eth0
-        ifaces_.push_back(createIface("eth0", 1, "10.0.0.1"));
+        Iface eth0 = createIface("eth0", 1);
+        eth0.addAddress(IOAddress("10.0.0.1"));
+        eth0.addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+        eth0.addAddress(IOAddress("2001:db8:1::1"));
+        ifaces_.push_back(eth0);
         // eth1
-        ifaces_.push_back(createIface("eth1", 2, "192.0.2.3"));
+        Iface eth1 = createIface("eth1", 2);
+        eth1.addAddress(IOAddress("192.0.2.3"));
+        eth1.addAddress(IOAddress("fe80::3a60:77ff:fed5:abcd"));
+        ifaces_.push_back(eth1);
     }
 
     /// @brief Create an object representing interface.
@@ -183,29 +195,59 @@ public:
     /// - up always true
     /// - running always true
     /// - inactive always to false
+    /// - multicast always to true
+    /// - broadcast always to false
     ///
     /// If one needs to modify the default flag settings, the setIfaceFlags
     /// function should be used.
     ///
     /// @param name A name of the interface to be created.
     /// @param ifindex An index of the interface to be created.
-    /// @param addr An IP address to be assigned to the interface.
     ///
     /// @return An object representing interface.
-    static Iface createIface(const std::string& name, const int ifindex,
-                             const std::string& addr) {
+    static Iface createIface(const std::string& name, const int ifindex) {
         Iface iface(name, ifindex);
-        iface.addAddress(IOAddress(addr));
         if (name == "lo") {
             iface.flag_loopback_ = true;
         }
+        iface.flag_multicast_ = true;
+        // On BSD systems, the SO_BINDTODEVICE option is not supported.
+        // Therefore the IfaceMgr will throw an exception on attempt to
+        // open sockets on more than one broadcast-capable interface at
+        // the same time. In order to prevent this error, we mark all
+        // interfaces broadcast-incapable for unit testing.
+        iface.flag_broadcast_ = false;
         iface.flag_up_ = true;
         iface.flag_running_ = true;
         iface.inactive4_ = false;
+        iface.inactive6_ = false;
         return (iface);
     }
 
-    /// @brief Modified flags on the interface.
+    /// @brief Checks if the specified interface has a socket bound to a
+    /// specified adddress.
+    ///
+    /// @param iface_name A name of the interface.
+    /// @param addr An address to be checked for binding.
+    ///
+    /// @return true if there is a socket bound to the specified address.
+    bool isBound(const std::string& iface_name, const std::string& addr) {
+        Iface* iface = getIface(iface_name);
+        if (iface == NULL) {
+            ADD_FAILURE() << "the interface " << iface_name << " doesn't exist";
+            return (false);
+        }
+        const Iface::SocketCollection& sockets = iface->getSockets();
+        for (Iface::SocketCollection::const_iterator sock = sockets.begin();
+             sock != sockets.end(); ++sock) {
+            if (sock->addr_ == IOAddress(addr)) {
+                return (true);
+            }
+        }
+        return (false);
+    }
+
+    /// @brief Modify flags on the interface.
     ///
     /// @param name A name of the interface.
     /// @param loopback A new value of the loopback flag.
@@ -214,14 +256,16 @@ public:
     /// @param inactive A new value of the inactive flag.
     void setIfaceFlags(const std::string& name, const bool loopback,
                        const bool up, const bool running,
-                       const bool inactive) {
+                       const bool inactive4,
+                       const bool inactive6) {
         for (IfaceMgr::IfaceCollection::iterator iface = ifaces_.begin();
              iface != ifaces_.end(); ++iface) {
             if (iface->getName() == name) {
                 iface->flag_loopback_ = loopback;
                 iface->flag_up_ = up;
                 iface->flag_running_ = running;
-                iface->inactive4_ = inactive;
+                iface->inactive4_ = inactive4;
+                iface->inactive6_ = inactive6;
             }
         }
     }
@@ -244,6 +288,43 @@ public:
     ~IfaceMgrTest() {
     }
 
+    /// @brief Tests the number of IPv6 sockets on interface
+    ///
+    /// This function checks the expected number of open IPv6 sockets on the
+    /// specified interface. On non-Linux systems, sockets are bound to a
+    /// link-local address and the number of unicast addresses specified.
+    /// On Linux systems, there is one more socket bound to a ff02::1:2
+    /// multicast address.
+    ///
+    /// @param iface An interface on which sockets are open.
+    /// @param unicast_num A number of unicast addresses bound.
+    /// @param link_local_num A number of link local addresses bound.
+    void checkSocketsCount6(const Iface& iface, const int unicast_num,
+                            const int link_local_num = 1) {
+        // On local-loopback interface, there should be no sockets.
+        if (iface.flag_loopback_) {
+            ASSERT_TRUE(iface.getSockets().empty())
+                << "expected empty socket set on loopback interface "
+                << iface.getName();
+            return;
+        }
+#if defined OS_LINUX
+        // On Linux, for each link-local address there may be an
+        // additional socket opened and bound to ff02::1:2. This socket
+        // is only opened if the interface is multicast-capable.
+        ASSERT_EQ(unicast_num + (iface.flag_multicast_ ? link_local_num : 0)
+                  + link_local_num, iface.getSockets().size())
+            << "invalid number of sockets on interface "
+            << iface.getName();
+#else
+        // On non-Linux, there is no additional socket.
+        ASSERT_EQ(unicast_num + link_local_num, iface.getSockets().size())
+            << "invalid number of sockets on interface "
+            << iface.getName();
+
+#endif
+    }
+
     // Get ther number of IPv4 or IPv6 sockets on the loopback interface
     int getOpenSocketsCount(const Iface& iface, uint16_t family) const {
         // Get all sockets.
@@ -1073,6 +1154,49 @@ TEST_F(IfaceMgrTest, setPacketFilter) {
     EXPECT_NO_THROW(iface_mgr->setPacketFilter(custom_packet_filter));
 }
 
+// This test checks that the default packet filter for DHCPv6 can be replaced
+// with the custom one.
+TEST_F(IfaceMgrTest, setPacketFilter6) {
+    // Create an instance of IfaceMgr.
+    boost::scoped_ptr<NakedIfaceMgr> iface_mgr(new NakedIfaceMgr());
+    ASSERT_TRUE(iface_mgr);
+
+    // Try to set NULL packet filter object and make sure it is rejected.
+    boost::shared_ptr<PktFilter6Stub> custom_packet_filter;
+    EXPECT_THROW(iface_mgr->setPacketFilter(custom_packet_filter),
+                 isc::dhcp::InvalidPacketFilter);
+
+    // Create valid object and check if it can be set.
+    custom_packet_filter.reset(new PktFilter6Stub());
+    ASSERT_TRUE(custom_packet_filter);
+    ASSERT_NO_THROW(iface_mgr->setPacketFilter(custom_packet_filter));
+
+    // Try to open socket using IfaceMgr. It should call the openSocket()
+    // function on the packet filter object we have set.
+    IOAddress loAddr("::1");
+    int socket1 = 0;
+    EXPECT_NO_THROW(
+        socket1 = iface_mgr->openSocket(LOOPBACK, loAddr,
+                                        DHCP6_SERVER_PORT + 10000);
+    );
+    // Check that openSocket function has been actually called on the packet
+    // filter object.
+    EXPECT_EQ(1, custom_packet_filter->open_socket_count_);
+    // Also check that the returned socket descriptor has an expected value.
+    EXPECT_EQ(0, socket1);
+
+    // Replacing current packet filter object, while there are sockets open,
+    // is not allowed!
+    EXPECT_THROW(iface_mgr->setPacketFilter(custom_packet_filter),
+                 PacketFilterChangeDenied);
+
+    // So, let's close the IPv6 sockets and retry. Now it should succeed.
+    iface_mgr->closeSockets(AF_INET6);
+    EXPECT_NO_THROW(iface_mgr->setPacketFilter(custom_packet_filter));
+
+}
+
+
 #if defined OS_LINUX
 
 // This Linux specific test checks whether it is possible to use
@@ -1251,7 +1375,7 @@ TEST_F(IfaceMgrTest, openSockets4IfaceDown) {
     // - is "down" (not up)
     // - is not running
     // - is active (is not inactive)
-    ifacemgr.setIfaceFlags("eth0", false, false, true, false);
+    ifacemgr.setIfaceFlags("eth0", false, false, true, false, false);
     ASSERT_FALSE(ifacemgr.getIface("eth0")->flag_up_);
     ASSERT_NO_THROW(ifacemgr.openSockets4(DHCP4_SERVER_PORT, true, NULL));
 
@@ -1282,7 +1406,7 @@ TEST_F(IfaceMgrTest, openSockets4IfaceInactive) {
     // - is up
     // - is running
     // - is inactive
-    ifacemgr.setIfaceFlags("eth1", false, true, true, true);
+    ifacemgr.setIfaceFlags("eth1", false, true, true, true, false);
     ASSERT_TRUE(ifacemgr.getIface("eth1")->inactive4_);
     ASSERT_NO_THROW(ifacemgr.openSockets4(DHCP4_SERVER_PORT, true, NULL));
 
@@ -1359,6 +1483,332 @@ TEST_F(IfaceMgrTest, openSocket4ErrorHandler) {
 
 }
 
+// This test checks that the sockets are open and bound to link local addresses
+// only, if unicast addresses are not specified.
+TEST_F(IfaceMgrTest, openSockets6LinkLocal) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+
+    boost::shared_ptr<PktFilter6Stub> filter(new PktFilter6Stub());
+    ASSERT_TRUE(filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
+
+    // Simulate opening sockets using the dummy packet filter.
+    bool success = false;
+    ASSERT_NO_THROW(success = ifacemgr.openSockets6(DHCP6_SERVER_PORT));
+    EXPECT_TRUE(success);
+
+    // Check that the number of sockets is correct on each interface.
+    checkSocketsCount6(*ifacemgr.getIface("lo"), 0);
+    checkSocketsCount6(*ifacemgr.getIface("eth0"), 0);
+    checkSocketsCount6(*ifacemgr.getIface("eth1"), 0);
+
+    // Sockets on eth0 should be bound to link-local and should not be bound
+    // to global unicast address, even though this address is configured on
+    // the eth0.
+    EXPECT_TRUE(ifacemgr.isBound("eth0", "fe80::3a60:77ff:fed5:cdef"));
+    EXPECT_FALSE(ifacemgr.isBound("eth0", "2001:db8:1::1"));
+    // Socket on eth1 should be bound to link local only.
+    EXPECT_TRUE(ifacemgr.isBound("eth1", "fe80::3a60:77ff:fed5:abcd"));
+
+    // If we are on Linux, there is one more socket bound to ff02::1:2
+#if defined OS_LINUX
+    EXPECT_TRUE(ifacemgr.isBound("eth0", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+    EXPECT_TRUE(ifacemgr.isBound("eth1", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+#endif
+}
+
+// This test checks that socket is not open on the interface which doesn't
+// have a link-local address.
+TEST_F(IfaceMgrTest, openSockets6NoLinkLocal) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+
+    boost::shared_ptr<PktFilter6Stub> filter(new PktFilter6Stub());
+    ASSERT_TRUE(filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
+
+    // Remove a link local address from eth0. If there is no link-local
+    // address, the socket should not open.
+    ASSERT_TRUE(ifacemgr.getIface("eth0")->
+                delAddress(IOAddress("fe80::3a60:77ff:fed5:cdef")));
+
+    // Simulate opening sockets using the dummy packet filter.
+    bool success = false;
+    ASSERT_NO_THROW(success = ifacemgr.openSockets6(DHCP6_SERVER_PORT));
+    EXPECT_TRUE(success);
+
+    // Check that the number of sockets is correct on each interface.
+    checkSocketsCount6(*ifacemgr.getIface("lo"), 0);
+    // The thrid parameter specifies that the number of link-local
+    // addresses for eth0 is equal to 0.
+    checkSocketsCount6(*ifacemgr.getIface("eth0"), 0, 0);
+    checkSocketsCount6(*ifacemgr.getIface("eth1"), 0, 1);
+
+    // There should be no sockets open on eth0 because it neither has
+    // link-local nor global unicast addresses.
+    EXPECT_FALSE(ifacemgr.isBound("eth0", "fe80::3a60:77ff:fed5:cdef"));
+    EXPECT_FALSE(ifacemgr.isBound("eth0", "2001:db8:1::1"));
+    // Socket on eth1 should be bound to link local only.
+    EXPECT_TRUE(ifacemgr.isBound("eth1", "fe80::3a60:77ff:fed5:abcd"));
+
+    // If we are on Linux, there is one more socket bound to ff02::1:2
+#if defined OS_LINUX
+    EXPECT_FALSE(ifacemgr.isBound("eth0", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+#endif
+
+}
+
+// This test checks that socket is open on the non-muticast-capable
+// interface. This socket simply doesn't join multicast group.
+TEST_F(IfaceMgrTest, openSockets6NotMulticast) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+
+    boost::shared_ptr<PktFilter6Stub> filter(new PktFilter6Stub());
+    ASSERT_TRUE(filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
+
+    // Make eth0 multicast-incapable.
+    ifacemgr.getIface("eth0")->flag_multicast_ = false;
+
+    // Simulate opening sockets using the dummy packet filter.
+    bool success = false;
+    ASSERT_NO_THROW(success = ifacemgr.openSockets6(DHCP6_SERVER_PORT));
+    EXPECT_TRUE(success);
+
+    // Check that the number of sockets is correct on each interface.
+    checkSocketsCount6(*ifacemgr.getIface("lo"), 0);
+    checkSocketsCount6(*ifacemgr.getIface("eth0"), 0);
+    checkSocketsCount6(*ifacemgr.getIface("eth1"), 0);
+
+    // Sockets on eth0 should be bound to link-local and should not be bound
+    // to global unicast address, even though this address is configured on
+    // the eth0.
+    EXPECT_TRUE(ifacemgr.isBound("eth0", "fe80::3a60:77ff:fed5:cdef"));
+    EXPECT_FALSE(ifacemgr.isBound("eth0", "2001:db8:1::1"));
+    // The eth0 is not a multicast-capable interface, so the socket should
+    // not be bound to the multicast address.
+    EXPECT_FALSE(ifacemgr.isBound("eth0", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+    // Socket on eth1 should be bound to link local only.
+    EXPECT_TRUE(ifacemgr.isBound("eth1", "fe80::3a60:77ff:fed5:abcd"));
+
+    // If we are on Linux, there is one more socket bound to ff02::1:2
+    // on eth1.
+#if defined OS_LINUX
+    EXPECT_TRUE(ifacemgr.isBound("eth1", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+#endif
+}
+
+// This test checks that the sockets are opened and bound to link local
+// and unicast addresses which have been explicitly specified.
+TEST_F(IfaceMgrTest, openSockets6Unicast) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+
+    boost::shared_ptr<PktFilter6Stub> filter(new PktFilter6Stub());
+    ASSERT_TRUE(filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
+
+    // Configure the eth0 to open socket on the unicast address, apart
+    // from link-local address.
+    ifacemgr.getIface("eth0")->addUnicast(IOAddress("2001:db8:1::1"));
+
+    // Simulate opening sockets using the dummy packet filter.
+    bool success = false;
+    ASSERT_NO_THROW(success = ifacemgr.openSockets6(DHCP6_SERVER_PORT));
+    EXPECT_TRUE(success);
+
+    // Check that we have correct number of sockets on each interface.
+    checkSocketsCount6(*ifacemgr.getIface("lo"), 0);
+    checkSocketsCount6(*ifacemgr.getIface("eth0"), 1); // one unicast address.
+    checkSocketsCount6(*ifacemgr.getIface("eth1"), 0);
+
+    // eth0 should have two sockets, one bound to link-local, another one
+    // bound to unicast address.
+    EXPECT_TRUE(ifacemgr.isBound("eth0", "fe80::3a60:77ff:fed5:cdef"));
+    EXPECT_TRUE(ifacemgr.isBound("eth0", "2001:db8:1::1"));
+    // eth1 should have one socket, bound to link-local address.
+    EXPECT_TRUE(ifacemgr.isBound("eth1", "fe80::3a60:77ff:fed5:abcd"));
+
+    // If we are on Linux, there is one more socket bound to ff02::1:2
+#if defined OS_LINUX
+    EXPECT_TRUE(ifacemgr.isBound("eth0", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+    EXPECT_TRUE(ifacemgr.isBound("eth1", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+#endif
+
+}
+
+// This test checks that the socket is open and bound to a global unicast
+// address if the link-local address does not exist for the particular
+// interface.
+TEST_F(IfaceMgrTest, openSockets6UnicastOnly) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+
+    boost::shared_ptr<PktFilter6Stub> filter(new PktFilter6Stub());
+    ASSERT_TRUE(filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
+
+    // Configure the eth0 to open socket on the unicast address, apart
+    // from link-local address.
+    ifacemgr.getIface("eth0")->addUnicast(IOAddress("2001:db8:1::1"));
+    // Explicitly remove the link-local address from eth0.
+    ASSERT_TRUE(ifacemgr.getIface("eth0")->
+                delAddress(IOAddress("fe80::3a60:77ff:fed5:cdef")));
+
+    // Simulate opening sockets using the dummy packet filter.
+    bool success = false;
+    ASSERT_NO_THROW(success = ifacemgr.openSockets6(DHCP6_SERVER_PORT));
+    EXPECT_TRUE(success);
+
+    // Check that we have correct number of sockets on each interface.
+    checkSocketsCount6(*ifacemgr.getIface("lo"), 0);
+    checkSocketsCount6(*ifacemgr.getIface("eth0"), 1, 0);
+    checkSocketsCount6(*ifacemgr.getIface("eth1"), 0);
+
+    // The link-local address is not present on eth0. Therefore, no socket
+    // must be bound to this address, nor to multicast address.
+    EXPECT_FALSE(ifacemgr.isBound("eth0", "fe80::3a60:77ff:fed5:cdef"));
+    EXPECT_FALSE(ifacemgr.isBound("eth0", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+    // There should be one socket bound to unicast address.
+    EXPECT_TRUE(ifacemgr.isBound("eth0", "2001:db8:1::1"));
+    // eth1 should have one socket, bound to link-local address.
+    EXPECT_TRUE(ifacemgr.isBound("eth1", "fe80::3a60:77ff:fed5:abcd"));
+
+    // If we are on Linux, there is one more socket bound to ff02::1:2
+#if defined OS_LINUX
+    EXPECT_TRUE(ifacemgr.isBound("eth1", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+#endif
+
+}
+
+// This test checks that no sockets are open for the interface which is down.
+TEST_F(IfaceMgrTest, openSockets6IfaceDown) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+
+    boost::shared_ptr<PktFilter6Stub> filter(new PktFilter6Stub());
+    ASSERT_TRUE(filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
+
+    // Configure the eth0 to open socket on the unicast address, apart
+    // from link-local address.
+    ifacemgr.getIface("eth0")->addUnicast(IOAddress("2001:db8:1::1"));
+
+    // Boolean parameters specify that eth0 is:
+    // - not a loopback
+    // - is "down" (not up)
+    // - is not running
+    // - is active for both v4 and v6
+    ifacemgr.setIfaceFlags("eth0", false, false, false, false, false);
+
+    // Simulate opening sockets using the dummy packet filter.
+    bool success = false;
+    ASSERT_NO_THROW(success = ifacemgr.openSockets6(DHCP6_SERVER_PORT));
+    EXPECT_TRUE(success);
+
+    // Check that we have correct number of sockets on each interface.
+    checkSocketsCount6(*ifacemgr.getIface("lo"), 0);
+    // There should be no sockets on eth0 because interface is down.
+    ASSERT_TRUE(ifacemgr.getIface("eth0")->getSockets().empty());
+    checkSocketsCount6(*ifacemgr.getIface("eth1"), 0);
+
+    // eth0 should have no sockets because the interface is down.
+    EXPECT_FALSE(ifacemgr.isBound("eth0", "fe80::3a60:77ff:fed5:cdef"));
+    EXPECT_FALSE(ifacemgr.isBound("eth0", "2001:db8:1::1"));
+    EXPECT_FALSE(ifacemgr.isBound("eth0", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+    // eth1 should have one socket, bound to link-local address.
+    EXPECT_TRUE(ifacemgr.isBound("eth1", "fe80::3a60:77ff:fed5:abcd"));
+
+    // If we are on Linux, there is one more socket bound to ff02::1:2
+#if defined OS_LINUX
+    EXPECT_TRUE(ifacemgr.isBound("eth1", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+#endif
+
+}
+
+// This test checks that no sockets are open for the interface which is
+// inactive.
+TEST_F(IfaceMgrTest, openSockets6IfaceInactive) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+
+    boost::shared_ptr<PktFilter6Stub> filter(new PktFilter6Stub());
+    ASSERT_TRUE(filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
+
+    // Configure the eth0 to open socket on the unicast address, apart
+    // from link-local address.
+    ifacemgr.getIface("eth0")->addUnicast(IOAddress("2001:db8:1::1"));
+
+    // Boolean parameters specify that eth1 is:
+    // - not a loopback
+    // - is up
+    // - is running
+    // - is active for v4
+    // - is inactive for v6
+    ifacemgr.setIfaceFlags("eth1", false, true, true, false, true);
+
+    // Simulate opening sockets using the dummy packet filter.
+    bool success = false;
+    ASSERT_NO_THROW(success = ifacemgr.openSockets6(DHCP6_SERVER_PORT));
+    EXPECT_TRUE(success);
+
+    // Check that we have correct number of sockets on each interface.
+    checkSocketsCount6(*ifacemgr.getIface("lo"), 0);
+    checkSocketsCount6(*ifacemgr.getIface("eth0"), 1); // one unicast address
+    // There should be no sockets on eth1 because interface is inactive
+    ASSERT_TRUE(ifacemgr.getIface("eth1")->getSockets().empty());
+
+    // eth0 should have one socket bound to a link-local address, another one
+    // bound to unicast address.
+    EXPECT_TRUE(ifacemgr.isBound("eth0", "fe80::3a60:77ff:fed5:cdef"));
+    EXPECT_TRUE(ifacemgr.isBound("eth0", "2001:db8:1::1"));
+
+    // eth1 shouldn't have a socket bound to link local address because
+    // interface is inactive.
+    EXPECT_FALSE(ifacemgr.isBound("eth1", "fe80::3a60:77ff:fed5:abcd"));
+    EXPECT_FALSE(ifacemgr.isBound("eth1", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+
+    // If we are on Linux, there is one more socket bound to ff02::1:2
+#if defined OS_LINUX
+    EXPECT_TRUE(ifacemgr.isBound("eth0", ALL_DHCP_RELAY_AGENTS_AND_SERVERS));
+#endif
+
+}
+
+// Test that the openSockets6 function does not throw if there are no interfaces
+// detected. This function is expected to return false.
+TEST_F(IfaceMgrTest, openSockets6NoIfaces) {
+    NakedIfaceMgr ifacemgr;
+    // Remove existing interfaces.
+    ifacemgr.getIfacesLst().clear();
+
+    boost::shared_ptr<PktFilter6Stub> filter(new PktFilter6Stub());
+    ASSERT_TRUE(filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
+
+    // This value indicates if at least one socket opens. There are no
+    // interfaces, so it should be set to false.
+    bool socket_open = false;
+    ASSERT_NO_THROW(socket_open = ifacemgr.openSockets6(DHCP6_SERVER_PORT));
+    EXPECT_FALSE(socket_open);
+}
 
 // Test the Iface structure itself
 TEST_F(IfaceMgrTest, iface) {
diff --git a/src/lib/dhcp/tests/pkt_filter6_test_utils.cc b/src/lib/dhcp/tests/pkt_filter6_test_utils.cc
new file mode 100644
index 0000000..b08e41c
--- /dev/null
+++ b/src/lib/dhcp/tests/pkt_filter6_test_utils.cc
@@ -0,0 +1,203 @@
+// 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/pkt6.h>
+#include <dhcp/tests/pkt_filter6_test_utils.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+PktFilter6Test::PktFilter6Test(const uint16_t port)
+    : port_(port),
+      sock_info_(isc::asiolink::IOAddress("::1"), port, -1, -1),
+      send_msg_sock_(-1) {
+    // Initialize ifname_ and ifindex_.
+    loInit();
+    // Initialize test_message_.
+    initTestMessage();
+}
+
+PktFilter6Test::~PktFilter6Test() {
+    // Cleanup after each test. This guarantees
+    // that the sockets do not hang after a test.
+    if (sock_info_.sockfd_ >= 0) {
+        close(sock_info_.sockfd_);
+    }
+    if (sock_info_.fallbackfd_ >=0) {
+        close(sock_info_.fallbackfd_);
+    }
+    if (send_msg_sock_ >= 0) {
+        close(send_msg_sock_);
+    }
+}
+
+void
+PktFilter6Test::initTestMessage() {
+    // Let's create a DHCPv6 message instance.
+    test_message_.reset(new Pkt6(DHCPV6_ADVERTISE, 123));
+
+    // Set required fields.
+    test_message_->setLocalAddr(IOAddress("::1"));
+    test_message_->setRemoteAddr(IOAddress("::1"));
+    test_message_->setRemotePort(port_);
+    test_message_->setLocalPort(port_ + 1);
+    test_message_->setIndex(ifindex_);
+    test_message_->setIface(ifname_);
+
+    try {
+        test_message_->pack();
+    } catch (const isc::Exception& ex) {
+        ADD_FAILURE() << "failed to create test message for PktFilter6Test";
+    }
+}
+
+void
+PktFilter6Test::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();
+
+    }
+}
+
+void
+PktFilter6Test::sendMessage() {
+    // DHCPv6 message will be sent over loopback interface.
+    Iface iface(ifname_, ifindex_);
+    IOAddress addr("::1");
+
+    // Initialize the source address and port.
+    struct sockaddr_in6 addr6;
+    memset(&addr6, 0, sizeof(addr6));
+    addr6.sin6_family = AF_INET6;
+    addr6.sin6_port = htons(port_);
+    memcpy(&addr6.sin6_addr, &addr.toBytes()[0], sizeof(addr6.sin6_addr));
+
+    // Open socket and bind to source address and port.
+    send_msg_sock_ = socket(AF_INET6, SOCK_DGRAM, 0);
+    ASSERT_GE(send_msg_sock_, 0);
+
+    ASSERT_GE(bind(send_msg_sock_, (struct sockaddr *)&addr6,
+                   sizeof(addr6)), 0);
+
+    // Set the destination address and port.
+    struct sockaddr_in6 dest_addr6;
+    memset(&dest_addr6, 0, sizeof(sockaddr_in6));
+    dest_addr6.sin6_family = AF_INET6;
+    dest_addr6.sin6_port = htons(port_ + 1);
+    memcpy(&dest_addr6.sin6_addr, &addr.toBytes()[0], 16);
+
+    // Initialize the message header structure, required by sendmsg.
+    struct msghdr m;
+    memset(&m, 0, sizeof(m));
+    m.msg_name = &dest_addr6;
+    m.msg_namelen = sizeof(dest_addr6);
+    // The iovec structure holds the packet data.
+    struct iovec v;
+    memset(&v, 0, sizeof(v));
+    v.iov_base = const_cast<void *>(test_message_->getBuffer().getData());
+    v.iov_len = test_message_->getBuffer().getLength();
+    // Assign the iovec to msghdr structure.
+    m.msg_iov = &v;
+    m.msg_iovlen = 1;
+    // We should be able to send the whole message. The sendmsg function should
+    // return the number of bytes sent, which is equal to the size of our
+    // message.
+    ASSERT_EQ(sendmsg(send_msg_sock_, &m, 0),
+              test_message_->getBuffer().getLength());
+    close(send_msg_sock_);
+    send_msg_sock_ = -1;
+
+}
+
+void
+PktFilter6Test::testDgramSocket(const int sock) const {
+    // Check that socket has been opened.
+    ASSERT_GE(sock, 0);
+
+    // Verify that the socket belongs to AF_INET family.
+    sockaddr_in6 sock_address;
+    socklen_t sock_address_len = sizeof(sock_address);
+    ASSERT_EQ(0, getsockname(sock,
+                             reinterpret_cast<sockaddr*>(&sock_address),
+                             &sock_address_len));
+    EXPECT_EQ(AF_INET6, sock_address.sin6_family);
+
+    // Verify that the socket is bound the appropriate address.
+    char straddr[INET6_ADDRSTRLEN];
+    inet_ntop(AF_INET6, &sock_address.sin6_addr, straddr, sizeof(straddr));
+    std::string bind_addr(straddr);
+    EXPECT_EQ("::1", bind_addr);
+
+    // Verify that the socket is bound to appropriate port.
+    EXPECT_EQ(port_, ntohs(sock_address.sin6_port));
+
+    // Verify that the socket has SOCK_DGRAM type.
+    int sock_type;
+    socklen_t sock_type_len = sizeof(sock_type);
+    ASSERT_EQ(0, getsockopt(sock, SOL_SOCKET, SO_TYPE,
+                            &sock_type, &sock_type_len));
+    EXPECT_EQ(SOCK_DGRAM, sock_type);
+}
+
+void
+PktFilter6Test::testRcvdMessage(const Pkt6Ptr& rcvd_msg) const {
+    // Currently, we don't send any payload in the message.
+    // Let's just check that the transaction id matches so as we
+    // are sure that we received the message that we expected.
+    EXPECT_EQ(test_message_->getTransid(), rcvd_msg->getTransid());
+}
+
+PktFilter6Stub::PktFilter6Stub()
+    : open_socket_count_ (0) {
+}
+
+SocketInfo
+PktFilter6Stub::openSocket(const Iface&, const isc::asiolink::IOAddress& addr,
+                           const uint16_t port, const bool) {
+    ++open_socket_count_;
+    return (SocketInfo(addr, port, 0));
+}
+
+Pkt6Ptr
+PktFilter6Stub::receive(const SocketInfo&) {
+    return Pkt6Ptr();
+}
+
+int
+PktFilter6Stub::send(const Iface&, uint16_t, const Pkt6Ptr&) {
+    return (0);
+}
+
+
+} // end of isc::dhcp::test namespace
+} // end of isc::dhcp namespace
+} // end of isc namespace
diff --git a/src/lib/dhcp/tests/pkt_filter6_test_utils.h b/src/lib/dhcp/tests/pkt_filter6_test_utils.h
new file mode 100644
index 0000000..a0fb1b8
--- /dev/null
+++ b/src/lib/dhcp/tests/pkt_filter6_test_utils.h
@@ -0,0 +1,155 @@
+// 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 PKT_FILTER6_TEST_UTILS_H
+#define PKT_FILTER6_TEST_UTILS_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/pkt_filter.h>
+#include <gtest/gtest.h>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @brief Test fixture class for testing classes derived from PktFilter6 class.
+///
+/// This class implements a simple algorithm checking presence of the loopback
+/// interface and initializing its index. It assumes that the loopback interface
+/// name is one of 'lo' or 'lo0'. If none of those interfaces is found, the
+/// constructor will report a failure.
+///
+/// @todo The interface detection algorithm should be more generic. This will
+/// be possible once the cross-OS interface detection is implemented.
+class PktFilter6Test : public ::testing::Test {
+public:
+
+    /// @brief Constructor
+    ///
+    /// This constructor initializes sock_info_ structure to a default value.
+    /// The socket descriptors should be set to a negative value to indicate
+    /// that no socket has been opened. Specific tests will reinitialize this
+    /// structure with the values of the open sockets. For non-negative socket
+    /// descriptors, the class destructor will close associated sockets.
+    PktFilter6Test(const uint16_t port);
+
+    /// @brief Destructor
+    ///
+    /// Closes open sockets (if any).
+    virtual ~PktFilter6Test();
+
+    /// @brief Initializes DHCPv6 message used by tests.
+    void initTestMessage();
+
+    /// @brief Detect loopback interface.
+    ///
+    /// @todo this function will be removed once cross-OS interface
+    /// detection is implemented
+    void loInit();
+
+    /// @brief Sends a single DHCPv6 message to the loopback address.
+    ///
+    /// This function opens a datagram socket and binds it to the local loopback
+    /// address and client port. The client's port is assumed to be port_ + 1.
+    /// The send_msg_sock_ member holds the socket descriptor so as the socket
+    /// is closed automatically in the destructor. If the function succeeds to
+    /// send a DHCPv6 message, the socket is closed so as the function can be
+    /// called again within the same test.
+    void sendMessage();
+
+    /// @brief Test that the datagram socket is opened correctly.
+    ///
+    /// This function is used by multiple tests.
+    ///
+    /// @param sock A descriptor of the open socket.
+    void testDgramSocket(const int sock) const;
+
+    /// @brief Checks if the received message matches the test_message_.
+    ///
+    /// @param rcvd_msg An instance of the message to be tested.
+    void testRcvdMessage(const Pkt6Ptr& rcvd_msg) const;
+
+    std::string ifname_;   ///< Loopback interface name.
+    uint16_t ifindex_;     ///< Loopback interface index.
+    uint16_t port_;        ///< A port number used for the test.
+    isc::dhcp::SocketInfo sock_info_; ///< A structure holding socket info.
+    int send_msg_sock_;    ///< Holds a descriptor of the socket used by
+                           ///< sendMessage function.
+    Pkt6Ptr test_message_; ///< A DHCPv6 message used by tests.
+
+};
+
+/// @brief A stub implementation of the PktFilter6 class.
+///
+/// This class implements abstract methods of the @c isc::dhcp::PktFilter class.
+/// The methods of this class mimic operations on sockets, but they neither
+/// open actual sockets, nor perform any send nor receive operations on them.
+class PktFilter6Stub : public PktFilter6 {
+public:
+
+    /// @brief Constructor
+    PktFilter6Stub();
+
+    /// @brief Simulate opening of a socket.
+    ///
+    /// This function simulates opening a socket. In reality, it doesn't open a
+    /// socket but the socket descriptor returned in the SocketInfo structure is
+    /// always set to 0. On each call to this function, the counter of
+    /// invocations is increased by one. This is useful to check if packet
+    /// filter object has been correctly installed and is used by @c IfaceMgr.
+    ///
+    /// @param iface Interface descriptor.
+    /// @param addr Address on the interface to be used to send packets.
+    /// @param port Port number.
+    /// @param join_multicast A boolean parameter which indicates whether
+    /// socket should join All_DHCP_Relay_Agents_and_servers multicast
+    /// group.
+    ///
+    /// @return A structure describing a primary and fallback socket.
+    virtual SocketInfo openSocket(const Iface& iface,
+                                  const isc::asiolink::IOAddress& addr,
+                                  const uint16_t port,
+                                  const bool join_multicast);
+
+    /// @brief Simulate reception of the DHCPv6 message.
+    ///
+    /// @param socket_info A structure holding socket information.
+    ///
+    /// @return Always a NULL object.
+    virtual Pkt6Ptr receive(const SocketInfo& socket_info);
+
+    /// @brief Simulate sending a DHCPv6 message.
+    ///
+    /// This function does nothing.
+    ///
+    /// @param iface Interface to be used to send packet.
+    /// @param sockfd A socket descriptor
+    /// @param pkt A packet to be sent.
+    ///
+    /// @note All parameters are ignored.
+    ///
+    /// @return 0.
+    virtual int send(const Iface& iface, uint16_t sockfd, const Pkt6Ptr& pkt);
+
+    /// Holds the number of invocations to PktFilter6Stub::openSocket.
+    int open_socket_count_;
+
+};
+
+}; // end of isc::dhcp::test namespace
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // PKT_FILTER6_TEST_UTILS_H
diff --git a/src/lib/dhcp/tests/pkt_filter_inet6_unittest.cc b/src/lib/dhcp/tests/pkt_filter_inet6_unittest.cc
new file mode 100644
index 0000000..e82cffe
--- /dev/null
+++ b/src/lib/dhcp/tests/pkt_filter_inet6_unittest.cc
@@ -0,0 +1,140 @@
+// 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/pkt6.h>
+#include <dhcp/pkt_filter_inet6.h>
+#include <dhcp/tests/pkt_filter6_test_utils.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+namespace {
+
+/// Port number used by tests.
+const uint16_t PORT = 10546;
+/// Size of the buffer holding received packets.
+const size_t RECV_BUF_SIZE = 2048;
+
+// Test fixture class inherits from the class common for all packet
+// filter tests.
+class PktFilterInet6Test : public isc::dhcp::test::PktFilter6Test {
+public:
+    PktFilterInet6Test() : PktFilter6Test(PORT) {
+    }
+};
+
+// This test verifies that the INET6 datagram socket is correctly opened and
+// bound to the appropriate address and port.
+TEST_F(PktFilterInet6Test, openSocket) {
+    // Create object representing loopback interface.
+    Iface iface(ifname_, ifindex_);
+    // Set loopback address.
+    IOAddress addr("::1");
+
+    // Try to open socket.
+    PktFilterInet6 pkt_filter;
+    sock_info_ = pkt_filter.openSocket(iface, addr, PORT, true);
+    // For the packet filter in use, the fallback socket shouldn't be opened.
+    // Fallback is typically opened for raw IPv4 sockets.
+    EXPECT_LT(sock_info_.fallbackfd_, 0);
+
+    // Test the primary socket.
+    testDgramSocket(sock_info_.sockfd_);
+}
+
+// This test verifies that the packet is correctly sent over the INET6
+// datagram socket.
+TEST_F(PktFilterInet6Test, send) {
+    // Packet will be sent over loopback interface.
+    Iface iface(ifname_, ifindex_);
+    IOAddress addr("::1");
+
+    // Create an instance of the class which we are testing.
+    PktFilterInet6 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.
+    sock_info_ = pkt_filter.openSocket(iface, addr, PORT, true);
+    ASSERT_GE(sock_info_.sockfd_, 0);
+
+    // Send the packet over the socket.
+    ASSERT_NO_THROW(pkt_filter.send(iface, sock_info_.sockfd_, test_message_));
+
+    // Read the data from socket.
+    fd_set readfds;
+    FD_ZERO(&readfds);
+    FD_SET(sock_info_.sockfd_, &readfds);
+
+    struct timeval timeout;
+    timeout.tv_sec = 5;
+    timeout.tv_usec = 0;
+    int result = select(sock_info_.sockfd_ + 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(sock_info_.sockfd_, rcv_buf, RECV_BUF_SIZE, 0);
+    ASSERT_GT(result, 0);
+
+    // Create the DHCPv6 packet from the received data.
+    Pkt6Ptr rcvd_pkt(new Pkt6(rcv_buf, result));
+    ASSERT_TRUE(rcvd_pkt);
+
+    // Parse the packet.
+    ASSERT_NO_THROW(rcvd_pkt->unpack());
+
+    // Check if the received message is correct.
+    testRcvdMessage(rcvd_pkt);
+
+}
+
+// This test verifies that the DHCPv6 packet is correctly received via
+// INET6 datagram socket and that it matches sent packet.
+TEST_F(PktFilterInet6Test, receive) {
+
+    // Packet will be received over loopback interface.
+    Iface iface(ifname_, ifindex_);
+    IOAddress addr("::1");
+
+    // Create an instance of the class which we are testing.
+    PktFilterInet6 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.
+    sock_info_ = pkt_filter.openSocket(iface, addr, PORT + 1, true);
+    ASSERT_GE(sock_info_.sockfd_, 0);
+
+    // Send a DHCPv6 message to the local loopback address and server's port.
+    //    ASSERT_NO_THROW(pkt_filter.send(iface, sock_info_.sockfd_, test_message_));
+    sendMessage();
+
+    // Receive the packet.
+    Pkt6Ptr rcvd_pkt = pkt_filter.receive(sock_info_);
+    // Check that the packet has been correctly received.
+    ASSERT_TRUE(rcvd_pkt);
+
+    // Parse the packet.
+    ASSERT_NO_THROW(rcvd_pkt->unpack());
+
+    // Check if the received message is correct.
+    testRcvdMessage(rcvd_pkt);
+    }
+
+} // anonymous namespace
diff --git a/src/lib/dhcp/tests/pkt_filter_test_utils.h b/src/lib/dhcp/tests/pkt_filter_test_utils.h
index 1b93b06..b2320cf 100644
--- a/src/lib/dhcp/tests/pkt_filter_test_utils.h
+++ b/src/lib/dhcp/tests/pkt_filter_test_utils.h
@@ -93,7 +93,7 @@ public:
 
 /// @brief A stub implementation of the PktFilter class.
 ///
-/// This class implements abstract methods of the @c isc::dhcp::test::PktFilter
+/// This class implements abstract methods of the @c isc::dhcp::PktFilter
 /// class. It is used by unit tests, which test protected methods of the
 /// @c isc::dhcp::test::PktFilter class. The implemented abstract methods are
 /// no-op.



More information about the bind10-changes mailing list