From: Marcin Siodelski Date: Thu, 12 Dec 2013 18:00:57 +0000 (+0100) Subject: [3251] Make IfaceMgr use PktFilterInet6 object to send/recv DHCP messages. X-Git-Tag: bind10-1.2.0beta1-release~162^2~9 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0ae10a533c27a489ec4bb434ca54c40e2f92f9dc;p=thirdparty%2Fkea.git [3251] Make IfaceMgr use PktFilterInet6 object to send/recv DHCP messages. --- diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 66738ceeef..d491530112 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -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& 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& 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"); @@ -456,8 +488,8 @@ bool IfaceMgr::openSockets6(const uint16_t port) { // 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))) { + if ( !PktFilter6::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 @@ -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(); @@ -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); - 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); @@ -804,114 +770,16 @@ int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, return (info.sockfd_); } -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(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 +787,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 +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! - // 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 +985,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(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(&to_addr))); - pkt->setRemoteAddr(IOAddress::fromBytes(AF_INET6, - reinterpret_cast(&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 0302e24fa6..3c7a296b71 100644 --- a/src/lib/dhcp/iface_mgr.h +++ b/src/lib/dhcp/iface_mgr.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -746,22 +747,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, @@ -896,23 +920,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. /// @@ -948,6 +955,14 @@ 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 @@ -957,6 +972,8 @@ private: /// Packet Filter is the one used for unit testing, which doesn't /// open sockets but rather mimics their behavior (mock object). PktFilterPtr packet_filter_; + + PktFilter6Ptr packet_filter6_; }; }; // namespace isc::dhcp diff --git a/src/lib/dhcp/pkt_filter6.h b/src/lib/dhcp/pkt_filter6.h index 6e81f6ba26..97f48bca21 100644 --- a/src/lib/dhcp/pkt_filter6.h +++ b/src/lib/dhcp/pkt_filter6.h @@ -96,7 +96,7 @@ public: /// @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 /// interface as a single interface may have multiple IPv6 addresses. /// @@ -108,8 +108,6 @@ public: virtual int send(const Iface& iface, uint16_t sockfd, const Pkt6Ptr& pkt) = 0; -protected: - /// @brief Joins IPv6 multicast group on a socket. /// /// Socket must be created and bound to an address. Note that this diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index 59421d2b7b..2eacd03f57 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -1073,6 +1074,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 iface_mgr(new NakedIfaceMgr()); + ASSERT_TRUE(iface_mgr); + + // Try to set NULL packet filter object and make sure it is rejected. + boost::shared_ptr 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 // This Linux specific test checks whether it is possible to use diff --git a/src/lib/dhcp/tests/pkt_filter6_test_utils.cc b/src/lib/dhcp/tests/pkt_filter6_test_utils.cc index 5382a09ce8..25cd702044 100644 --- a/src/lib/dhcp/tests/pkt_filter6_test_utils.cc +++ b/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()); } +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 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 index f1d1c6bacc..1387a1bde2 100644 --- a/src/lib/dhcp/tests/pkt_filter6_test_utils.h +++ b/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 namespace }; // end of isc namespace diff --git a/src/lib/dhcp/tests/pkt_filter_test_utils.h b/src/lib/dhcp/tests/pkt_filter_test_utils.h index 1b93b06c38..b2320cfc4a 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.