Browse Source

[3251] Make IfaceMgr use PktFilterInet6 object to send/recv DHCP messages.

Marcin Siodelski 11 years ago
parent
commit
0ae10a533c

+ 62 - 290
src/lib/dhcp/iface_mgr.cc

@@ -23,6 +23,7 @@
 #include <dhcp/dhcp6.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/pkt_filter_inet.h>
 #include <dhcp/pkt_filter_inet.h>
+#include <dhcp/pkt_filter_inet6.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 #include <util/io/pktinfo_utilities.h>
 #include <util/io/pktinfo_utilities.h>
 
 
@@ -169,7 +170,8 @@ IfaceMgr::IfaceMgr()
     :control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
     :control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
      control_buf_(new char[control_buf_len_]),
      control_buf_(new char[control_buf_len_]),
      session_socket_(INVALID_SOCKET), session_callback_(NULL),
      session_socket_(INVALID_SOCKET), session_callback_(NULL),
-     packet_filter_(new PktFilterInet())
+     packet_filter_(new PktFilterInet()),
+     packet_filter6_(new PktFilterInet6())
 {
 {
 
 
     try {
     try {
@@ -225,10 +227,11 @@ IfaceMgr::isDirectResponseSupported() const {
 }
 }
 
 
 void
 void
-IfaceMgr::setPacketFilter(const boost::shared_ptr<PktFilter>& packet_filter) {
+IfaceMgr::setPacketFilter(const PktFilterPtr& packet_filter) {
     // Do not allow NULL pointer.
     // Do not allow NULL pointer.
     if (!packet_filter) {
     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
     // Different packet filters use different socket types. It does not make
     // sense to allow the change of packet filter when there are IPv4 sockets
     // 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.
     // 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
     // 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.
     // 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();
     for (IfaceCollection::const_iterator iface = ifaces_.begin();
          iface != ifaces_.end(); ++iface) {
          iface != ifaces_.end(); ++iface) {
         const Iface::SocketCollection& sockets = iface->getSockets();
         const Iface::SocketCollection& sockets = iface->getSockets();
         for (Iface::SocketCollection::const_iterator sock = sockets.begin();
         for (Iface::SocketCollection::const_iterator sock = sockets.begin();
              sock != sockets.end(); ++sock) {
              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() {
 void IfaceMgr::stubDetectIfaces() {
     string ifaceName;
     string ifaceName;
     const string v4addr("127.0.0.1"), v6addr("::1");
     const string v4addr("127.0.0.1"), v6addr("::1");
@@ -456,8 +488,8 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
             // Binding socket to unicast address and then joining multicast group
             // Binding socket to unicast address and then joining multicast group
             // works well on Mac OS (and possibly other BSDs), but does not work
             // works well on Mac OS (and possibly other BSDs), but does not work
             // on Linux.
             // on Linux.
-            if ( !joinMulticast(sock, iface->getName(),
-                                string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS))) {
+            if ( !PktFilter6::joinMulticast(sock, iface->getName(),
+                                            string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS))) {
                 close(sock);
                 close(sock);
                 isc_throw(SocketConfigError, "Failed to join "
                 isc_throw(SocketConfigError, "Failed to join "
                           << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
                           << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
@@ -501,7 +533,6 @@ IfaceMgr::handleSocketConfigError(const std::string& errmsg,
     }
     }
 }
 }
 
 
-
 void
 void
 IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
 IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
     for (IfaceCollection::const_iterator iface=ifaces_.begin();
     for (IfaceCollection::const_iterator iface=ifaces_.begin();
@@ -717,86 +748,21 @@ 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) {
+    // Assuming that packet filter is not NULL, because its modifier checks it.
+    SocketInfo info = packet_filter6_->openSocket(iface, addr, port);
     iface.addSocket(info);
     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 uint16_t port, const bool receive_bcast,
                           const bool send_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,
     SocketInfo info = packet_filter_->openSocket(iface, addr, port,
                                                  receive_bcast, send_bcast);
                                                  receive_bcast, send_bcast);
     iface.addSocket(info);
     iface.addSocket(info);
@@ -805,113 +771,15 @@ int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr,
 }
 }
 
 
 bool
 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) {
 IfaceMgr::send(const Pkt6Ptr& pkt) {
-    int result;
-
     Iface* iface = getIface(pkt->getIface());
     Iface* iface = getIface(pkt->getIface());
     if (!iface) {
     if (!iface) {
-        isc_throw(BadValue, "Unable to send Pkt6. Invalid interface ("
+        isc_throw(BadValue, "Unable to send DHCPv6 message. Invalid interface ("
                   << pkt->getIface() << ") specified.");
                   << 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
 bool
@@ -919,12 +787,11 @@ IfaceMgr::send(const Pkt4Ptr& pkt) {
 
 
     Iface* iface = getIface(pkt->getIface());
     Iface* iface = getIface(pkt->getIface());
     if (!iface) {
     if (!iface) {
-        isc_throw(BadValue, "Unable to send Pkt4. Invalid interface ("
+        isc_throw(BadValue, "Unable to send DHCPv4 message. Invalid interface ("
                   << pkt->getIface() << ") specified.");
                   << 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));
     return (packet_filter_->send(*iface, getSocket(*pkt), pkt));
 }
 }
 
 
@@ -1023,8 +890,7 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
     }
     }
 
 
     // Now we have a socket, let's get some data from it!
     // 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));
     return (packet_filter_->receive(*iface, *candidate));
 }
 }
 
 
@@ -1119,102 +985,8 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
     if (!candidate) {
     if (!candidate) {
         isc_throw(SocketReadError, "received data over unknown socket");
         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) {
 uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6& pkt) {

+ 44 - 27
src/lib/dhcp/iface_mgr.h

@@ -21,6 +21,7 @@
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt_filter.h>
 #include <dhcp/pkt_filter.h>
+#include <dhcp/pkt_filter6.h>
 
 
 #include <boost/function.hpp>
 #include <boost/function.hpp>
 #include <boost/noncopyable.hpp>
 #include <boost/noncopyable.hpp>
@@ -746,22 +747,45 @@ public:
         session_callback_ = callback;
         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 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);
     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.
     /// @brief Set Packet Filter object to handle send/receive packets.
     ///
     ///
     /// This function sets Packet Filter object to be used by IfaceMgr,
     /// This function sets Packet Filter object to be used by IfaceMgr,
@@ -896,23 +920,6 @@ protected:
     SessionCallback session_callback_;
     SessionCallback session_callback_;
 private:
 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
     /// @brief Identifies local network address to be used to
     /// connect to remote address.
     /// connect to remote address.
     ///
     ///
@@ -948,6 +955,14 @@ private:
     void handleSocketConfigError(const std::string& errmsg,
     void handleSocketConfigError(const std::string& errmsg,
                                  IfaceMgrErrorMsgCallback handler);
                                  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
     /// Holds instance of a class derived from PktFilter, used by the
     /// IfaceMgr to open sockets and send/receive packets through these
     /// IfaceMgr to open sockets and send/receive packets through these
     /// sockets. It is possible to supply custom object using
     /// sockets. It is possible to supply custom object using
@@ -957,6 +972,8 @@ private:
     /// Packet Filter is the one used for unit testing, which doesn't
     /// Packet Filter is the one used for unit testing, which doesn't
     /// open sockets but rather mimics their behavior (mock object).
     /// open sockets but rather mimics their behavior (mock object).
     PktFilterPtr packet_filter_;
     PktFilterPtr packet_filter_;
+
+    PktFilter6Ptr packet_filter6_;
 };
 };
 
 
 }; // namespace isc::dhcp
 }; // namespace isc::dhcp

+ 1 - 3
src/lib/dhcp/pkt_filter6.h

@@ -96,7 +96,7 @@ public:
 
 
     /// @brief Send DHCPv6 message through a specified interface and socket.
     /// @brief Send DHCPv6 message through a specified interface and socket.
     ///
     ///
-    /// Thie function sends a DHCPv6 message through a specified interface and
+    /// This function sends a DHCPv6 message through a specified interface and
     /// socket. In general, there may be multiple sockets open on a single
     /// socket. In general, there may be multiple sockets open on a single
     /// interface as a single interface may have multiple IPv6 addresses.
     /// interface as a single interface may have multiple IPv6 addresses.
     ///
     ///
@@ -108,8 +108,6 @@ public:
     virtual int send(const Iface& iface, uint16_t sockfd,
     virtual int send(const Iface& iface, uint16_t sockfd,
                      const Pkt6Ptr& pkt) = 0;
                      const Pkt6Ptr& pkt) = 0;
 
 
-protected:
-
     /// @brief Joins IPv6 multicast group on a socket.
     /// @brief Joins IPv6 multicast group on a socket.
     ///
     ///
     /// Socket must be created and bound to an address. Note that this
     /// Socket must be created and bound to an address. Note that this

+ 44 - 0
src/lib/dhcp/tests/iface_mgr_unittest.cc

@@ -19,6 +19,7 @@
 #include <dhcp/iface_mgr.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt_filter.h>
 #include <dhcp/pkt_filter.h>
+#include <dhcp/tests/pkt_filter6_test_utils.h>
 
 
 #include <boost/bind.hpp>
 #include <boost/bind.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
@@ -1073,6 +1074,49 @@ TEST_F(IfaceMgrTest, setPacketFilter) {
     EXPECT_NO_THROW(iface_mgr->setPacketFilter(custom_packet_filter));
     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<test::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 test::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
 #if defined OS_LINUX
 
 
 // This Linux specific test checks whether it is possible to use
 // This Linux specific test checks whether it is possible to use

+ 22 - 0
src/lib/dhcp/tests/pkt_filter6_test_utils.cc

@@ -176,6 +176,28 @@ PktFilter6Test::testRcvdMessage(const Pkt6Ptr& rcvd_msg) const {
     EXPECT_EQ(test_message_->getTransid(), rcvd_msg->getTransid());
     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) {
+    ++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::test namespace
 } // end of isc::dhcp namespace
 } // end of isc::dhcp namespace
 } // end of isc namespace
 } // end of isc namespace

+ 53 - 0
src/lib/dhcp/tests/pkt_filter6_test_utils.h

@@ -91,6 +91,59 @@ public:
 
 
 };
 };
 
 
+/// @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.
+    ///
+    /// @return A structure describing a primary and fallback socket.
+    virtual SocketInfo openSocket(const Iface& iface,
+                                  const isc::asiolink::IOAddress& addr,
+                                  const uint16_t port);
+
+    /// @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::test namespace
 }; // end of isc::dhcp namespace
 }; // end of isc::dhcp namespace
 }; // end of isc namespace
 }; // end of isc namespace

+ 1 - 1
src/lib/dhcp/tests/pkt_filter_test_utils.h

@@ -93,7 +93,7 @@ public:
 
 
 /// @brief A stub implementation of the PktFilter class.
 /// @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
 /// class. It is used by unit tests, which test protected methods of the
 /// @c isc::dhcp::test::PktFilter class. The implemented abstract methods are
 /// @c isc::dhcp::test::PktFilter class. The implemented abstract methods are
 /// no-op.
 /// no-op.