From: Thomas Markwalder Date: Sat, 1 Dec 2018 18:59:05 +0000 (-0500) Subject: [packet-queue-queue-by-list] PacketQueue now queues lists of packets X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1ecda82030673a2bb6f0fa8ab17fbc0aaeee75f9;p=thirdparty%2Fkea.git [packet-queue-queue-by-list] PacketQueue now queues lists of packets src/lib/dhcp/Makefile.am Added packt_queue_ring.h src/lib/dhcp/iface_mgr.* IfaceMgr::receiveDHCP4Packets() IfaceMgr::receiveDHCP6Packets() - modified to queue a list of packets IfaceMgr::receiveDHCP6Packet() - modified to return packet rather than queue it src/lib/dhcp/packet_queue.h PacketQueue<> - replaced enqueuePacket() with enqueuePackets() and is now pure virtual - dequeuePacket() is now pure virtual - Removed pop, push, peek methods Moved All PackeQueueRing<> code into new header src/lib/dhcp/packet_queue_ring.h New header which contains PacketQueueRing<> and derivations --- diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am index eb1efb5ac2..a5c4ef863c 100644 --- a/src/lib/dhcp/Makefile.am +++ b/src/lib/dhcp/Makefile.am @@ -43,10 +43,11 @@ libkea_dhcp___la_SOURCES += option_space_container.h libkea_dhcp___la_SOURCES += option_string.cc option_string.h libkea_dhcp___la_SOURCES += option_vendor.cc option_vendor.h libkea_dhcp___la_SOURCES += option_vendor_class.cc option_vendor_class.h -libkea_dhcp___la_SOURCES += packet_queue.h -libkea_dhcp___la_SOURCES += packet_queue_mgr.h -libkea_dhcp___la_SOURCES += packet_queue_mgr4.cc packet_queue_mgr4.h -libkea_dhcp___la_SOURCES += packet_queue_mgr6.cc packet_queue_mgr6.h +libkea_dhcp___la_SOURCES += packet_queue.h +libkea_dhcp___la_SOURCES += packet_queue_ring.h +libkea_dhcp___la_SOURCES += packet_queue_mgr.h +libkea_dhcp___la_SOURCES += packet_queue_mgr4.cc packet_queue_mgr4.h +libkea_dhcp___la_SOURCES += packet_queue_mgr6.cc packet_queue_mgr6.h libkea_dhcp___la_SOURCES += pkt.cc pkt.h libkea_dhcp___la_SOURCES += pkt4.cc pkt4.h libkea_dhcp___la_SOURCES += pkt4o6.cc pkt4o6.h @@ -55,7 +56,7 @@ libkea_dhcp___la_SOURCES += pkt_filter.h pkt_filter.cc libkea_dhcp___la_SOURCES += pkt_filter6.h pkt_filter6.cc libkea_dhcp___la_SOURCES += pkt_filter_inet.cc pkt_filter_inet.h libkea_dhcp___la_SOURCES += pkt_filter_inet6.cc pkt_filter_inet6.h -libkea_dhcp___la_SOURCES += socket_info.h +libkea_dhcp___la_SOURCES += socket_info.h # Utilize Linux Packet Filtering on Linux. if OS_LINUX @@ -126,6 +127,7 @@ libkea_dhcp___include_HEADERS = \ option_vendor.h \ option_vendor_class.h \ packet_queue.h \ + packet_queue_ring.h \ packet_queue_mgr.h \ packet_queue_mgr4.h \ packet_queue_mgr6.h \ diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index d2e32d2233..86fc6aa462 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -1456,18 +1456,25 @@ IfaceMgr::receiveDHCP4Packets() { continue; } - // Let's find out which interface/socket has data. + // Iterate over interface sockets and read a packet from each ready socket + Pkt4PtrSocketInfoList pkt_list; + Pkt4Ptr pkt; BOOST_FOREACH(iface, ifaces_) { BOOST_FOREACH(SocketInfo s, iface->getSockets()) { if (FD_ISSET(s.sockfd_, &sockets)) { - receiveDHCP4Packet(*iface, s); - // Can take time so check one more time the watch socket. - if (dhcp_receiver_->shouldTerminate()) { - return; + pkt = receiveDHCP4Packet(*iface, s); + if (pkt) { + pkt_list.push_back(Pkt4PtrSocketInfoPair(pkt, s)); } } } } + + // If we read any packets push them onto the queue + if (pkt_list.size()) { + getPacketQueue4()->enqueuePackets(pkt_list); + dhcp_receiver_->markReady(WatchedThread::READY); + } } } @@ -1530,38 +1537,45 @@ IfaceMgr::receiveDHCP6Packets() { continue; } - // Let's find out which interface/socket has data. + // Iterate over interface sockets and read a packet from each ready socket + Pkt6PtrSocketInfoList pkt_list; + Pkt6Ptr pkt; BOOST_FOREACH(iface, ifaces_) { BOOST_FOREACH(SocketInfo s, iface->getSockets()) { if (FD_ISSET(s.sockfd_, &sockets)) { - receiveDHCP6Packet(s); - // Can take time so check one more time the watch socket. - if (dhcp_receiver_->shouldTerminate()) { - return; + pkt = receiveDHCP6Packet(s); + if (pkt) { + pkt_list.push_back(Pkt6PtrSocketInfoPair(pkt, s)); } } } } + + // If we read any packets push them onto the queue + if (pkt_list.size()) { + getPacketQueue6()->enqueuePackets(pkt_list); + dhcp_receiver_->markReady(WatchedThread::READY); + } } } -void +Pkt4Ptr IfaceMgr::receiveDHCP4Packet(Iface& iface, const SocketInfo& socket_info) { int len; + Pkt4Ptr pkt; int result = ioctl(socket_info.sockfd_, FIONREAD, &len); if (result < 0) { // Signal the error to receive4. dhcp_receiver_->setError(strerror(errno)); - return; + return (pkt); } + if (len == 0) { // Nothing to read. - return; + return (pkt); } - Pkt4Ptr pkt; - try { pkt = packet_filter_->receive(iface, socket_info); } catch (const std::exception& ex) { @@ -1570,29 +1584,25 @@ IfaceMgr::receiveDHCP4Packet(Iface& iface, const SocketInfo& socket_info) { dhcp_receiver_->setError("packet filter receive() failed"); } - if (pkt) { - getPacketQueue4()->enqueuePacket(pkt, socket_info); - dhcp_receiver_->markReady(WatchedThread::READY); - } + return(pkt); } -void +Pkt6Ptr IfaceMgr::receiveDHCP6Packet(const SocketInfo& socket_info) { + Pkt6Ptr pkt; int len; int result = ioctl(socket_info.sockfd_, FIONREAD, &len); if (result < 0) { // Signal the error to receive6. dhcp_receiver_->setError(strerror(errno)); - return; + return (pkt); } if (len == 0) { // Nothing to read. - return; + return (pkt); } - Pkt6Ptr pkt; - try { pkt = packet_filter6_->receive(socket_info); } catch (const std::exception& ex) { @@ -1601,10 +1611,7 @@ IfaceMgr::receiveDHCP6Packet(const SocketInfo& socket_info) { dhcp_receiver_->setError("packet filter receive() failed"); } - if (pkt) { - getPacketQueue6()->enqueuePacket(pkt, socket_info); - dhcp_receiver_->markReady(WatchedThread::READY); - } + return(pkt); } uint16_t diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h index 43fc908815..afde3e42c3 100644 --- a/src/lib/dhcp/iface_mgr.h +++ b/src/lib/dhcp/iface_mgr.h @@ -1310,21 +1310,25 @@ private: /// watch socket, and exits if it is marked ready. This is method /// is used as the worker function in the thread created by @c /// startDHCP4Receiver(). It currently uses select() to monitor - /// socket readiness. If the select errors out (other than EINTR), - /// it marks the "error" watch socket as ready. + /// socket readiness. When any interface socket is ready, it + /// iterates over all of the interface sockets reading one packet + /// per ready socket into a list. It then passes that list to + /// queue, and sets the "ready" watch socket. If the select + /// errors out (other than EINTR), it marks the "error" watch socket + /// as ready. void receiveDHCP4Packets(); /// @brief Receives a single DHCPv4 packet from an interface socket /// /// Called by @c receiveDHPC4Packets when a socket fd is flagged as /// ready. It uses the DHCPv4 packet filter to receive a single packet - /// from the given interface socket, adds it to the packet queue, and - /// marks the "receive" watch socket ready. If an error occurs during - /// the read, the "error" watch socket is marked ready. + /// from the given interface socket and returns it. If an error occurs + /// during the read, the "error" watch socket is marked ready. /// /// @param iface interface /// @param socket_info structure holding socket information - void receiveDHCP4Packet(Iface& iface, const SocketInfo& socket_info); + /// @return A pointer to the packet read, or an empty pointer + Pkt4Ptr receiveDHCP4Packet(Iface& iface, const SocketInfo& socket_info); /// @brief DHCPv6 receiver method. /// @@ -1333,20 +1337,24 @@ private: /// watch socket, and exits if it is marked ready. This is method /// is used as the worker function in the thread created by @c /// startDHCP6Receiver(). It currently uses select() to monitor - /// socket readiness. If the select errors out (other than EINTR), - /// it marks the "error" watch socket as ready. + /// socket readiness. When any interface socket is ready, it + /// iterates over all of the interface sockets reading one packet + /// per ready socket into a list. It then passes that list to + /// queue, and sets the "ready" watch socket. If the select + /// errors out (other than EINTR), it marks the "error" watch socket + /// as ready. void receiveDHCP6Packets(); /// @brief Receives a single DHCPv6 packet from an interface socket /// /// Called by @c receiveDHPC6Packets when a socket fd is flagged as /// ready. It uses the DHCPv6 packet filter to receive a single packet - /// from the given interface socket, adds it to the packet queue, and - /// marks the "receive" watch socket ready. If an error occurs during - /// the read, the "error" watch socket is marked ready. + /// from the given interface socket and returns it. If an error occurs + /// during the read, the "error" watch socket is marked ready. /// /// @param socket_info structure holding socket information - void receiveDHCP6Packet(const SocketInfo& socket_info); + /// @return A pointer to the packet read, or an empty pointer + Pkt6Ptr receiveDHCP6Packet(const SocketInfo& socket_info); /// Holds instance of a class derived from PktFilter, used by the /// IfaceMgr to open sockets and send/receive packets through these diff --git a/src/lib/dhcp/packet_queue.h b/src/lib/dhcp/packet_queue.h index 004bd36dff..76a5ab3c40 100644 --- a/src/lib/dhcp/packet_queue.h +++ b/src/lib/dhcp/packet_queue.h @@ -11,10 +11,7 @@ #include #include #include -#include -#include -#include #include namespace isc { @@ -60,116 +57,32 @@ public: /// Virtual destructor virtual ~PacketQueue(){}; + /// @brief Pairs a packet pointer with its source socket. + typedef std::pair PacketPtrTypeSocketInfoPair; + /// @brief Defines a list of packets and their sockets + typedef std::vector PacketPtrTypeSocketInfoList; + /// @brief Adds a packet to the queue /// - /// Calls @c shouldDropPacket to determine if the packet should be queued - /// or dropped. If it should be queued it is added to the end of the - /// queue specified by the "to" parameter. - /// - /// @param packet packet to enqueue - /// @param source socket the packet came from - /// @param to end of the queue from which to remove packet(s). - /// Defaults to BACK. + /// Adds the list of packets to the queue. The list contains pairs of + /// packets and their originating socket info structs. Derivations determine + /// which packets to queue and how to queue them. /// - void enqueuePacket(PacketTypePtr packet, const SocketInfo& source, - const QueueEnd& to=QueueEnd::BACK) { - if (!shouldDropPacket(packet, source)) { - pushPacket(packet, to); - } - } + /// @param packets list of packets to queue + virtual void enqueuePackets(const PacketPtrTypeSocketInfoList& /*packets*/) = 0; /// @brief Dequeues the next packet from the queue /// - /// Calls @eatPackets to discard packets as needed, and then - /// dequeues the next packet (if any) and returns it. Packets - /// are dequeued from the end of the queue specified by the "from" - /// parameter. - /// - /// @param from end of the queue from which to remove packet(s). - /// Defaults to FRONT. + /// Dequeues the next packet (if any) and returns it. Derivations determine + /// how packets are dequeued. /// /// @return A pointer to dequeued packet, or an empty pointer /// if the queue is empty. - PacketTypePtr dequeuePacket(const QueueEnd& from=QueueEnd::FRONT) { - eatPackets(from); - return(popPacket(from)); - } - - /// @brief Determines if a packet should be discarded. - /// - /// This function is called at the beginning of @c enqueuePacket and - /// provides an opportunity to examine the packet and its source - /// and decide whether it should be dropped or added to the queue. - /// Derivations are expected to provide implementations based on - /// their own requirements. Bear in mind that the packet has NOT - /// been unpacked at this point. The default implementation simply - /// returns false. - /// - /// @param packet the packet under consideration - /// @param source the socket the packet came from - /// - /// @return true if the packet should be dropped, false if it should be - /// kept. - virtual bool shouldDropPacket(PacketTypePtr /* packet */, - const SocketInfo& /* source */) { - return (false); - } - - /// @brief Discards packets from one end of the queue. - /// - /// This function is called at the beginning of @c dequeuePacket and - /// provides an opportunity to examine and discard packets from - /// the queue prior to dequeuing the next packet to be - /// processed. Derivations are expected to provide implementations - /// based on their own requirements. The default implemenation is to - /// to simply return without skipping any packets. - /// - /// @param from end of the queue from which packets should discarded - /// This is passed in from @c dequeuePackets. - /// - /// @param from specifies the end of the queue from which packets - /// should be discarded. - /// - /// @return The number of packets discarded. - virtual int eatPackets(const QueueEnd& /* from */) { - return (0); - } - - /// @brief Pushes a packet onto the queue - /// - /// Adds a packet onto the end of queue specified. - /// - /// @param packet packet to add to the queue - /// @param to specifies the end of the queue to which the packet - /// should be added. - virtual void pushPacket(PacketTypePtr& packet, const QueueEnd& to=QueueEnd::BACK) = 0; - - /// @brief Pops a packet from the queue - /// - /// Removes a packet from the end of the queue specified and returns it. - /// - /// @param from specifies the end of the queue from which the packet - /// should be taken. - /// - /// @return A pointer to dequeued packet, or an empty pointer - /// if the queue is empty. - virtual PacketTypePtr popPacket(const QueueEnd& from=QueueEnd::FRONT) = 0; - - /// @brief Gets the packet currently at one end of the queue - /// - /// Returns a pointer the packet at the specified end of the - /// queue without dequeuing it. - /// - /// @param from specifies which end of the queue to examine. - /// - /// @return A pointer to packet, or an empty pointer if the - /// queue is empty. - virtual const PacketTypePtr peek(const QueueEnd& from=QueueEnd::FRONT) const = 0; + virtual PacketTypePtr dequeuePacket() = 0; /// @brief return True if the queue is empty. virtual bool empty() const = 0; - /// @todo size may not apply either... what if there are two internal buffers? /// @brief Returns the current number of packets in the buffer. virtual size_t getSize() const = 0; @@ -205,7 +118,7 @@ public: } /// @return Fetches the logical name of the type of this queue. - std::string getQueueType() { + std::string getQueueType() const { return (queue_type_); }; @@ -215,196 +128,24 @@ private: }; + /// @brief Defines pointer to the DHCPv4 queue interface used at the application level. /// This is the type understood by IfaceMgr and the type that should be returned by /// DHCPv4 packet queue factories. typedef boost::shared_ptr> PacketQueue4Ptr; +/// @brief Defines DHCPv4 packet pointer/socket info pair +typedef std::pair Pkt4PtrSocketInfoPair; +/// @brief Defines DHCPv4 packet pointer/socket info pair list +typedef std::vector Pkt4PtrSocketInfoList; /// @brief Defines pointer to the DHCPv6 queue interface used at the application level. /// This is the type understood by IfaceMgr and the type that should be returned by /// DHCPv6 packet queue factories. typedef boost::shared_ptr> PacketQueue6Ptr; - - -/// @brief Provides an abstract ring-buffer implementation of the PacketQueue interface. -template -class PacketQueueRing : public PacketQueue { -public: - /// @brief Minimum queue capacity permitted. Below five is pretty much - /// nonsensical. - static const size_t MIN_RING_CAPACITY = 5; - - /// @brief Constructor - /// - /// @param queue_type logical name of the queue implementation - /// @param capacity maximum number of packets the queue can hold - PacketQueueRing(const std::string& queue_type, size_t capacity) - : PacketQueue(queue_type) { - queue_.set_capacity(capacity); - } - - /// @brief virtual Destructor - virtual ~PacketQueueRing(){}; - - /// @brief Pushes a packet onto the queue - /// - /// Adds a packet onto the end of queue specified. Note that this - /// function is protected by a Mutex. - /// - /// @param packet packet to add to the queue - /// @param to specifies the end of the queue to which the packet - /// should be added. - virtual void pushPacket(PacketTypePtr& packet, const QueueEnd& to=QueueEnd::BACK) { - isc::util::thread::Mutex::Locker lock(mutex_); - if (to == QueueEnd::BACK) { - queue_.push_back(packet); - } else { - queue_.push_front(packet); - } - } - - /// @brief Pops a packet from the queue - /// - /// Removes a packet from the end of the queue specified and returns it. Note - /// that this function is protected by a Mutex. - /// - /// @param from specifies the end of the queue from which the packet - /// should be taken. - /// - /// @return A pointer to dequeued packet, or an empty pointer - /// if the queue is empty. - virtual PacketTypePtr popPacket(const QueueEnd& from = QueueEnd::FRONT) { - isc::util::thread::Mutex::Locker lock(mutex_); - PacketTypePtr packet; - if (queue_.empty()) { - return (packet); - } - - if (from == QueueEnd::FRONT) { - packet = queue_.front(); - queue_.pop_front(); - } else { - packet = queue_.back(); - queue_.pop_back(); - } - - return (packet); - } - - - /// @brief Gets the packet currently at one end of the queue - /// - /// Returns a pointer the packet at the specified end of the - /// queue without dequeuing it. - /// - /// @param from specifies which end of the queue to examine. - /// - /// @return A pointer to packet, or an empty pointer if the - /// queue is empty. - virtual const PacketTypePtr peek(const QueueEnd& from=QueueEnd::FRONT) const { - PacketTypePtr packet; - if (!queue_.empty()) { - packet = (from == QueueEnd::FRONT ? queue_.front() : queue_.back()); - } - - return (packet); - } - - /// @brief Returns True if the queue is empty. - virtual bool empty() const { - return(queue_.empty()); - } - - /// @brief Returns the maximum number of packets allowed in the buffer. - virtual size_t getCapacity() const { - return (queue_.capacity()); - } - - /// @brief Sets the maximum number of packets allowed in the buffer. - /// - /// @todo - do we want to change size on the fly? This might need - /// to be private, called only by constructor - /// - /// @throw BadValue if capacity is too low. - virtual void setCapacity(size_t capacity) { - if (capacity < MIN_RING_CAPACITY) { - isc_throw(BadValue, "Queue capacity of " << capacity - << " is invalid. It must be at least " - << MIN_RING_CAPACITY); - } - - /// @todo should probably throw if it's zero - queue_.set_capacity(capacity); - } - - /// @brief Returns the current number of packets in the buffer. - virtual size_t getSize() const { - return (queue_.size()); - } - - /// @brief Discards all packets currently in the buffer. - virtual void clear() { - queue_.clear(); - } - - /// @brief Fetches pertinent information - virtual data::ElementPtr getInfo() const { - data::ElementPtr info = PacketQueue::getInfo(); - info->set("capacity", data::Element::create(static_cast(getCapacity()))); - info->set("size", data::Element::create(static_cast(getSize()))); - return(info); - } - -private: - - /// @brief Packet queue - boost::circular_buffer queue_; - - /// @brief Mutex for protecting queue accesses. - isc::util::thread::Mutex mutex_; -}; - - -/// @brief DHCPv4 packet queue buffer implementation -/// -/// This implementation does not (currently) add any drop -/// or packet skip logic, it operates as a verbatim ring -/// queue for DHCPv4 packets. -/// -class PacketQueueRing4 : public PacketQueueRing { -public: - /// @brief Constructor - /// - /// @param queue_type logical name of the queue implementation - /// @param capacity maximum number of packets the queue can hold - PacketQueueRing4(const std::string& queue_type, size_t capacity) - : PacketQueueRing(queue_type, capacity) { - }; - - /// @brief virtual Destructor - virtual ~PacketQueueRing4(){} -}; - -/// @brief DHCPv6 packet queue buffer implementation -/// -/// This implementation does not (currently) add any drop -/// or packet skip logic, it operates as a verbatim ring -/// queue for DHCPv6 packets. -/// -class PacketQueueRing6 : public PacketQueueRing { -public: - /// @brief Constructor - /// - /// @param queue_type logical name of the queue implementation - /// @param capacity maximum number of packets the queue can hold - PacketQueueRing6(const std::string& queue_type, size_t capacity) - : PacketQueueRing(queue_type, capacity) { - }; - - /// @brief virtual Destructor - virtual ~PacketQueueRing6(){} -}; - +/// @brief Defines DHCPv6 packet pointer/socket info pair +typedef std::pair Pkt6PtrSocketInfoPair; +/// @brief Defines DHCPv6 packet pointer/socket info pair list +typedef std::vector Pkt6PtrSocketInfoList; }; // namespace isc::dhcp }; // namespace isc diff --git a/src/lib/dhcp/packet_queue_mgr4.cc b/src/lib/dhcp/packet_queue_mgr4.cc index b7ba4ffeb0..1918a6dfbf 100644 --- a/src/lib/dhcp/packet_queue_mgr4.cc +++ b/src/lib/dhcp/packet_queue_mgr4.cc @@ -5,6 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include +#include #include #include diff --git a/src/lib/dhcp/packet_queue_mgr6.cc b/src/lib/dhcp/packet_queue_mgr6.cc index bcb83ec527..2f08fdf6e4 100644 --- a/src/lib/dhcp/packet_queue_mgr6.cc +++ b/src/lib/dhcp/packet_queue_mgr6.cc @@ -5,6 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include +#include #include #include diff --git a/src/lib/dhcp/packet_queue_ring.h b/src/lib/dhcp/packet_queue_ring.h new file mode 100644 index 0000000000..f1d93ce941 --- /dev/null +++ b/src/lib/dhcp/packet_queue_ring.h @@ -0,0 +1,269 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef PACKET_QUEUE_RING_H +#define PACKET_QUEUE_RING_H + +#include +#include + +#include +#include +#include + +namespace isc { + +namespace dhcp { + +/// @brief Provides an abstract ring-buffer implementation of the PacketQueue interface. +template +class PacketQueueRing : public PacketQueue { +public: + /// @brief Minimum queue capacity permitted. Below five is pretty much + /// nonsensical. + static const size_t MIN_RING_CAPACITY = 5; + + /// @brief Constructor + /// + /// @param queue_type logical name of the queue implementation + /// @param capacity maximum number of packets the queue can hold + PacketQueueRing(const std::string& queue_type, size_t capacity) + : PacketQueue(queue_type) { + queue_.set_capacity(capacity); + } + + /// @brief virtual Destructor + virtual ~PacketQueueRing(){}; + + /// @brief Adds a packet to the queue + /// + /// Iterates over the packet list, passing the packet and its socket info + /// into @c shouldDropPacket to determine whether or not the packet should + /// added to the queue. Note this method locks the queue's mutex upon entry. + /// + /// @param packets list of packets to queue + virtual void enqueuePackets(const typename + PacketQueue::PacketPtrTypeSocketInfoList& packets) { + isc::util::thread::Mutex::Locker lock(mutex_); + for (auto pspair : packets) { + if (!shouldDropPacket(pspair.first, pspair.second)) { + pushPacket(pspair.first); + } + } + } + + /// @brief Dequeues the next packet from the queue + /// + /// Dequeues the next packet (if any) and returns it. Note this method + /// locks the queue'sm mutex upon entry. + /// + /// @return A pointer to dequeued packet, or an empty pointer + /// if the queue is empty. + virtual PacketTypePtr dequeuePacket() { + isc::util::thread::Mutex::Locker lock(mutex_); + eatPackets(QueueEnd::FRONT); + return(popPacket()); + } + + /// @brief Determines if a packet should be discarded. + /// + /// This function is called in @c enqueuePackets for each packet + /// in its packet list. It provides an opportunity to examine the + /// packet and its source and decide whether it should be dropped + /// or added to the queue. Derivations are expected to provide + /// implementations based on their own requirements. Bear in mind + /// that the packet has NOT been unpacked at this point. The default + /// implementation simply returns false (i.e. keep the packet). + /// + /// @param packet the packet under consideration + /// @param source the socket the packet came from + /// + /// @return true if the packet should be dropped, false if it should be + /// kept. + virtual bool shouldDropPacket(PacketTypePtr /* packet */, + const SocketInfo& /* source */) { + return (false); + } + + /// @brief Discards packets from one end of the queue. + /// + /// This function is called at the beginning of @c dequeuePacket and + /// provides an opportunity to examine and discard packets from + /// the queue prior to dequeuing the next packet to be + /// processed. Derivations are expected to provide implementations + /// based on their own requirements. The default implemenation is to + /// to simply return without skipping any packets. + /// + /// @param from end of the queue from which packets should discarded + /// This is passed in from @c dequeuePackets. + /// + /// @param from specifies the end of the queue from which packets + /// should be discarded. + /// + /// @return The number of packets discarded. + virtual int eatPackets(const QueueEnd& /* from */) { + return (0); + } + + /// @brief Pushes a packet onto the queue + /// + /// Adds a packet onto the end of queue specified. + /// + /// @param packet packet to add to the queue + /// @param to specifies the end of the queue to which the packet + /// should be added. + virtual void pushPacket(PacketTypePtr& packet, const QueueEnd& to=QueueEnd::BACK) { + if (to == QueueEnd::BACK) { + queue_.push_back(packet); + } else { + queue_.push_front(packet); + } + } + + /// @brief Pops a packet from the queue + /// + /// Removes a packet from the end of the queue specified and returns it. + /// + /// @param from specifies the end of the queue from which the packet + /// should be taken. + /// + /// @return A pointer to dequeued packet, or an empty pointer + /// if the queue is empty. + virtual PacketTypePtr popPacket(const QueueEnd& from = QueueEnd::FRONT) { + PacketTypePtr packet; + if (queue_.empty()) { + return (packet); + } + + if (from == QueueEnd::FRONT) { + packet = queue_.front(); + queue_.pop_front(); + } else { + packet = queue_.back(); + queue_.pop_back(); + } + + return (packet); + } + + + /// @brief Gets the packet currently at one end of the queue + /// + /// Returns a pointer the packet at the specified end of the + /// queue without dequeuing it. + /// + /// @param from specifies which end of the queue to examine. + /// + /// @return A pointer to packet, or an empty pointer if the + /// queue is empty. + virtual const PacketTypePtr peek(const QueueEnd& from=QueueEnd::FRONT) const { + PacketTypePtr packet; + if (!queue_.empty()) { + packet = (from == QueueEnd::FRONT ? queue_.front() : queue_.back()); + } + + return (packet); + } + + /// @brief Returns True if the queue is empty. + virtual bool empty() const { + return(queue_.empty()); + } + + /// @brief Returns the maximum number of packets allowed in the buffer. + virtual size_t getCapacity() const { + return (queue_.capacity()); + } + + /// @brief Sets the maximum number of packets allowed in the buffer. + /// + /// @todo - do we want to change size on the fly? This might need + /// to be private, called only by constructor + /// + /// @throw BadValue if capacity is too low. + virtual void setCapacity(size_t capacity) { + if (capacity < MIN_RING_CAPACITY) { + isc_throw(BadValue, "Queue capacity of " << capacity + << " is invalid. It must be at least " + << MIN_RING_CAPACITY); + } + + /// @todo should probably throw if it's zero + queue_.set_capacity(capacity); + } + + /// @brief Returns the current number of packets in the buffer. + virtual size_t getSize() const { + return (queue_.size()); + } + + /// @brief Discards all packets currently in the buffer. + virtual void clear() { + queue_.clear(); + } + + /// @brief Fetches pertinent information + virtual data::ElementPtr getInfo() const { + data::ElementPtr info = PacketQueue::getInfo(); + info->set("capacity", data::Element::create(static_cast(getCapacity()))); + info->set("size", data::Element::create(static_cast(getSize()))); + return(info); + } + +private: + + /// @brief Packet queue + boost::circular_buffer queue_; + + /// @brief Mutex for protecting queue accesses. + isc::util::thread::Mutex mutex_; +}; + + +/// @brief DHCPv4 packet queue buffer implementation +/// +/// This implementation does not (currently) add any drop +/// or packet skip logic, it operates as a verbatim ring +/// queue for DHCPv4 packets. +/// +class PacketQueueRing4 : public PacketQueueRing { +public: + /// @brief Constructor + /// + /// @param queue_type logical name of the queue implementation + /// @param capacity maximum number of packets the queue can hold + PacketQueueRing4(const std::string& queue_type, size_t capacity) + : PacketQueueRing(queue_type, capacity) { + }; + + /// @brief virtual Destructor + virtual ~PacketQueueRing4(){} +}; + +/// @brief DHCPv6 packet queue buffer implementation +/// +/// This implementation does not (currently) add any drop +/// or packet skip logic, it operates as a verbatim ring +/// queue for DHCPv6 packets. +/// +class PacketQueueRing6 : public PacketQueueRing { +public: + /// @brief Constructor + /// + /// @param queue_type logical name of the queue implementation + /// @param capacity maximum number of packets the queue can hold + PacketQueueRing6(const std::string& queue_type, size_t capacity) + : PacketQueueRing(queue_type, capacity) { + }; + + /// @brief virtual Destructor + virtual ~PacketQueueRing6(){} +}; + +}; // namespace isc::dhcp +}; // namespace isc + +#endif // PACKET_QUEUE_RING_H diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index 6478e17349..455dfc1a63 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -609,6 +609,7 @@ public: // OK, Send the PACKET! EXPECT_NO_THROW(ifacemgr->send(sendPkt)); + (ifacemgr->send(sendPkt)); // Now let's try and receive it. boost::shared_ptr rcvPkt; @@ -1294,16 +1295,26 @@ TEST_F(IfaceMgrTest, sendReceive4) { // Given an empty pointer, queueing should be disabled. // This should do direct reception. - sendReceive4Test(queue_control, false); + { + SCOPED_TRACE("no queue control"); + sendReceive4Test(queue_control, false); + } - // Now let's populate queue control. - queue_control = makeQueueConfig(PacketQueueMgr4::DEFAULT_QUEUE_TYPE4, 500, false); - // With queueing disabled, we should use direct reception. - sendReceive4Test(queue_control, false); + { + // Now let's populate queue control. + SCOPED_TRACE("queue control disabled"); + queue_control = makeQueueConfig(PacketQueueMgr4::DEFAULT_QUEUE_TYPE4, 500, false); + // With queueing disabled, we should use direct reception. + sendReceive4Test(queue_control, false); + } - // Queuing enabled, indirection reception should work. - queue_control = makeQueueConfig(PacketQueueMgr4::DEFAULT_QUEUE_TYPE4, 500, true); - sendReceive4Test(queue_control, true); + { + // Now let's populate queue control. + SCOPED_TRACE("queue control enabled"); + // Queuing enabled, indirection reception should work. + queue_control = makeQueueConfig(PacketQueueMgr4::DEFAULT_QUEUE_TYPE4, 500, true); + sendReceive4Test(queue_control, true); + } } // Verifies that it is possible to set custom packet filter object diff --git a/src/lib/dhcp/tests/packet_queue4_unittest.cc b/src/lib/dhcp/tests/packet_queue4_unittest.cc index b0666b073a..de23db7f1a 100644 --- a/src/lib/dhcp/tests/packet_queue4_unittest.cc +++ b/src/lib/dhcp/tests/packet_queue4_unittest.cc @@ -6,7 +6,7 @@ #include -#include +#include #include #include @@ -77,141 +77,141 @@ public: return (eaten); } + void queueOnePacket(Pkt4Ptr pkt, SocketInfo& sock_info) { + Pkt4PtrSocketInfoList pkt_list; + pkt_list.push_back(Pkt4PtrSocketInfoPair(pkt, sock_info)); + ASSERT_NO_THROW(enqueuePackets(pkt_list)); + } + bool drop_enabled_; int eat_count_; }; // Verifies use of the generic PacketQueue interface to // construct a queue implementation. -TEST(TestQueue4, interfaceBasics) { +TEST(PacketQueueRing4, interfaceBasics) { // Verify we can create a queue - PacketQueue4Ptr q4(new TestQueue4(100)); - ASSERT_TRUE(q4); + PacketQueue4Ptr q(new PacketQueueRing4("kea-ring4",100)); + ASSERT_TRUE(q); // It should be empty. - EXPECT_TRUE(q4->empty()); + EXPECT_TRUE(q->empty()); // Type should match. - EXPECT_EQ("kea-ring4", q4->getQueueType()); + EXPECT_EQ("kea-ring4", q->getQueueType()); // Fetch the queue info and verify it has all the expected values. - checkInfo(q4, "{ \"capacity\": 100, \"queue-type\": \"kea-ring4\", \"size\": 0 }"); + checkInfo(q, "{ \"capacity\": 100, \"queue-type\": \"kea-ring4\", \"size\": 0 }"); } -// Verifies the basic mechanics of the adding and -// removing packets to and from the ring buffer. -TEST(TestQueue4, ringTest) { - PacketQueue4Ptr q4(new TestQueue4(3)); +// Verifies the higher level functions of queueing and dequeueing +// from the ring buffer. +TEST(PacketQueueRing4, enqueueDequeueTest) { + PacketQueue4Ptr q(new PacketQueueRing4("kea-ring4", 3)); // Fetch the queue info and verify it has all the expected values. - checkInfo(q4, "{ \"capacity\": 3, \"queue-type\": \"kea-ring4\", \"size\": 0 }"); + checkInfo(q, "{ \"capacity\": 3, \"queue-type\": \"kea-ring4\", \"size\": 0 }"); // Enqueue five packets. The first two should be pushed off. SocketInfo sock1(isc::asiolink::IOAddress("127.0.0.1"), 777, 10); + + Pkt4PtrSocketInfoList pkt_list; for (int i = 1; i < 6; ++i) { Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 1000+i)); - ASSERT_NO_THROW(q4->enqueuePacket(pkt, sock1)); - checkIntStat(q4, "size", (i > 3 ? 3 : i)); + pkt_list.push_back(Pkt4PtrSocketInfoPair(pkt, sock1)); } + // Add the packets to the queue. + ASSERT_NO_THROW(q->enqueuePackets(pkt_list)); + // Fetch the queue info and verify it has all the expected values. - checkInfo(q4, "{ \"capacity\": 3, \"queue-type\": \"kea-ring4\", \"size\": 3 }"); + checkInfo(q, "{ \"capacity\": 3, \"queue-type\": \"kea-ring4\", \"size\": 3 }"); + + // We should have transids 1003,1004,1005 + Pkt4Ptr pkt; + for (int i = 3; i < 6; ++i) { + ASSERT_NO_THROW(pkt = q->dequeuePacket()); + ASSERT_TRUE(pkt); + EXPECT_EQ(1000 + i, pkt->getTransid()); + } + + // Queue should be empty. + ASSERT_TRUE(q->empty()); + + // Dequeuing should fail safely, with an empty return. + ASSERT_NO_THROW(pkt = q->dequeuePacket()); + ASSERT_FALSE(pkt); + + // Enqueue three more packets. + pkt_list.clear(); + for (int i = 0; i < 3; ++i) { + Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 1000+i)); + pkt_list.push_back(Pkt4PtrSocketInfoPair(pkt, sock1)); + } + + ASSERT_NO_THROW(q->enqueuePackets(pkt_list)); + checkIntStat(q, "size", 3); + + // Let's flush the buffer and then verify it is empty. + q->clear(); + EXPECT_TRUE(q->empty()); + checkIntStat(q, "size", 0); +} + +// Verifies peeking, pushing, and popping which +// are unique to PacketQueueRing<> derivations. +TEST(PacketQueueRing4, peekPushPopTest) { + PacketQueueRing4 q("kea-ring4", 3); + + // Push five packets onto the end. The first two should get pushed off. + for (int i = 1; i < 6; ++i) { + Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 1000+i)); + ASSERT_NO_THROW(q.pushPacket(pkt)); + } + + // We should have three. + ASSERT_EQ(3, q.getSize()); // We should have transids 1005,1004,1003 (back to front) // Peek front should be transid 1003. Pkt4Ptr pkt; - ASSERT_NO_THROW(pkt = q4->peek(QueueEnd::FRONT)); + ASSERT_NO_THROW(pkt = q.peek(QueueEnd::FRONT)); ASSERT_TRUE(pkt); EXPECT_EQ(1003, pkt->getTransid()); // Peek back should be transid 1005. - ASSERT_NO_THROW(pkt = q4->peek(QueueEnd::BACK)); + ASSERT_NO_THROW(pkt = q.peek(QueueEnd::BACK)); ASSERT_TRUE(pkt); EXPECT_EQ(1005, pkt->getTransid()); // Pop front should return transid 1003. - ASSERT_NO_THROW(pkt = q4->popPacket(QueueEnd::FRONT)); + ASSERT_NO_THROW(pkt = q.popPacket(QueueEnd::FRONT)); ASSERT_TRUE(pkt); EXPECT_EQ(1003, pkt->getTransid()); // Pop back should return transid 1005. - ASSERT_NO_THROW(pkt = q4->popPacket(QueueEnd::BACK)); + ASSERT_NO_THROW(pkt = q.popPacket(QueueEnd::BACK)); ASSERT_TRUE(pkt); EXPECT_EQ(1005, pkt->getTransid()); // Peek front should be transid 1004. - ASSERT_NO_THROW(pkt = q4->peek(QueueEnd::FRONT)); + ASSERT_NO_THROW(pkt = q.peek(QueueEnd::FRONT)); ASSERT_TRUE(pkt); EXPECT_EQ(1004, pkt->getTransid()); // Peek back should be transid 1004. - ASSERT_NO_THROW(pkt = q4->peek(QueueEnd::BACK)); + ASSERT_NO_THROW(pkt = q.peek(QueueEnd::BACK)); ASSERT_TRUE(pkt); EXPECT_EQ(1004, pkt->getTransid()); // Pop front should return transid 1004. - ASSERT_NO_THROW(pkt = q4->popPacket(QueueEnd::FRONT)); + ASSERT_NO_THROW(pkt = q.popPacket(QueueEnd::FRONT)); ASSERT_TRUE(pkt); EXPECT_EQ(1004, pkt->getTransid()); // Pop front should return an empty pointer. - ASSERT_NO_THROW(pkt = q4->popPacket(QueueEnd::BACK)); - ASSERT_FALSE(pkt); - - // Enqueue three packets. - for (int i = 1; i < 3; ++i) { - Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 1000+i)); - ASSERT_NO_THROW(q4->enqueuePacket(pkt, sock1)); - checkIntStat(q4, "size", i); - } - - // Let's flush the buffer and then verify it is empty. - q4->clear(); - EXPECT_TRUE(q4->empty()); - checkIntStat(q4, "size", 0); -} - -// Verifies the higher level functions of queueing and -// dequeueing with drop and skip logic disabled. -TEST(TestQueue4, enqueueDequeueTest) { - PacketQueue4Ptr q4(new TestQueue4(100)); - EXPECT_TRUE(q4->empty()); - - SocketInfo sock1(isc::asiolink::IOAddress("127.0.0.1"), 777, 10); - - // Enqueue the first packet. - Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 1002)); - ASSERT_NO_THROW(q4->enqueuePacket(pkt, sock1)); - checkIntStat(q4, "size", 1); - - // Enqueue a packet onto the front. - pkt.reset(new Pkt4(DHCPDISCOVER, 1003)); - ASSERT_NO_THROW(q4->enqueuePacket(pkt, sock1, QueueEnd::FRONT)); - checkIntStat(q4, "size", 2); - - // Enqueue a packet onto the back. - pkt.reset(new Pkt4(DHCPDISCOVER, 1001)); - ASSERT_NO_THROW(q4->enqueuePacket(pkt, sock1, QueueEnd::BACK)); - checkIntStat(q4, "size", 3); - - // By default we dequeue from the front. We should get transid 1003. - ASSERT_NO_THROW(pkt = q4->dequeuePacket()); - ASSERT_TRUE(pkt); - EXPECT_EQ(1003, pkt->getTransid()); - - // Dequeue from the back, we should get transid 1001. - ASSERT_NO_THROW(pkt = q4->dequeuePacket(QueueEnd::BACK)); - ASSERT_TRUE(pkt); - EXPECT_EQ(1001, pkt->getTransid()); - - // Dequeue from the front, we should get transid 1002. - ASSERT_NO_THROW(pkt = q4->dequeuePacket(QueueEnd::FRONT)); - ASSERT_TRUE(pkt); - EXPECT_EQ(1002, pkt->getTransid()); - - // Queue should be empty. - ASSERT_NO_THROW(pkt = q4->dequeuePacket()); + ASSERT_NO_THROW(pkt = q.popPacket(QueueEnd::BACK)); ASSERT_FALSE(pkt); } @@ -219,58 +219,60 @@ TEST(TestQueue4, enqueueDequeueTest) { // This accesses it's queue instance as a TestQueue4, rather than // a PacketQueue4Ptr, to provide access to TestQueue4 specifics. TEST(TestQueue4, shouldDropPacketTest) { - TestQueue4 q4(100); - EXPECT_TRUE(q4.empty()); - ASSERT_FALSE(q4.drop_enabled_); - ASSERT_EQ(0, q4.eat_count_); + TestQueue4 q(100); + EXPECT_TRUE(q.empty()); + ASSERT_FALSE(q.drop_enabled_); + ASSERT_EQ(0, q.eat_count_); - SocketInfo sockEven(isc::asiolink::IOAddress("127.0.0.1"), 888, 10); - SocketInfo sockOdd(isc::asiolink::IOAddress("127.0.0.1"), 777, 11); + SocketInfo sock_even(isc::asiolink::IOAddress("127.0.0.1"), 888, 10); + SocketInfo sock_odd(isc::asiolink::IOAddress("127.0.0.1"), 777, 11); // Drop is not enabled. // We should be able to enqueue a packet with even numbered values. + Pkt4PtrSocketInfoList pkt_list; + Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 1002)); - ASSERT_NO_THROW(q4.enqueuePacket(pkt, sockEven)); - EXPECT_EQ(1, q4.getSize()); + q.queueOnePacket(pkt, sock_even); + ASSERT_EQ(1, q.getSize()); // We should be able to enqueue a packet with odd numbered values. pkt.reset(new Pkt4(DHCPDISCOVER, 1003)); - ASSERT_NO_THROW(q4.enqueuePacket(pkt, sockOdd)); - EXPECT_EQ(2, q4.getSize()); + q.queueOnePacket(pkt, sock_odd); + ASSERT_EQ(2, q.getSize()); // Enable drop logic. - q4.drop_enabled_ = true; + q.drop_enabled_ = true; // We should not be able to add one with an even-numbered transid. pkt.reset(new Pkt4(DHCPDISCOVER, 1004)); - ASSERT_NO_THROW(q4.enqueuePacket(pkt, sockOdd)); - EXPECT_EQ(2, q4.getSize()); + q.queueOnePacket(pkt, sock_odd); + ASSERT_EQ(2, q.getSize()); // We should not be able to add one with from even-numbered port. pkt.reset(new Pkt4(DHCPDISCOVER, 1005)); - ASSERT_NO_THROW(q4.enqueuePacket(pkt, sockEven)); - EXPECT_EQ(2, q4.getSize()); + q.queueOnePacket(pkt, sock_even); + EXPECT_EQ(2, q.getSize()); // We should be able to add one with an odd-numbered values. pkt.reset(new Pkt4(DHCPDISCOVER, 1007)); - ASSERT_NO_THROW(q4.enqueuePacket(pkt, sockOdd)); - EXPECT_EQ(3, q4.getSize()); + q.queueOnePacket(pkt, sock_odd); + EXPECT_EQ(3, q.getSize()); // Dequeue them and make sure they are as expected: 1002,1003, and 1007. - ASSERT_NO_THROW(pkt = q4.dequeuePacket()); + ASSERT_NO_THROW(pkt = q.dequeuePacket()); ASSERT_TRUE(pkt); EXPECT_EQ(1002, pkt->getTransid()); - ASSERT_NO_THROW(pkt = q4.dequeuePacket()); + ASSERT_NO_THROW(pkt = q.dequeuePacket()); ASSERT_TRUE(pkt); EXPECT_EQ(1003, pkt->getTransid()); - ASSERT_NO_THROW(pkt = q4.dequeuePacket()); + ASSERT_NO_THROW(pkt = q.dequeuePacket()); ASSERT_TRUE(pkt); EXPECT_EQ(1007, pkt->getTransid()); // Queue should be empty. - ASSERT_NO_THROW(pkt = q4.dequeuePacket()); + ASSERT_NO_THROW(pkt = q.dequeuePacket()); ASSERT_FALSE(pkt); } @@ -278,10 +280,10 @@ TEST(TestQueue4, shouldDropPacketTest) { // This accesses it's queue instance as a TestQueue4, rather than // a PacketQueue4Ptr, to provide access to TestQueue4 specifics. TEST(TestQueue4, eatPacketsTest) { - TestQueue4 q4(100); - EXPECT_TRUE(q4.empty()); - ASSERT_FALSE(q4.drop_enabled_); - ASSERT_EQ(0, q4.eat_count_); + TestQueue4 q(100); + EXPECT_TRUE(q.empty()); + ASSERT_FALSE(q.drop_enabled_); + ASSERT_EQ(0, q.eat_count_); SocketInfo sock(isc::asiolink::IOAddress("127.0.0.1"), 888, 10); @@ -289,25 +291,17 @@ TEST(TestQueue4, eatPacketsTest) { // Let's add five packets. for (int i = 1; i < 6; ++i) { pkt.reset(new Pkt4(DHCPDISCOVER, 1000 + i)); - ASSERT_NO_THROW(q4.enqueuePacket(pkt, sock)); - EXPECT_EQ(i, q4.getSize()); + q.queueOnePacket(pkt, sock); + ASSERT_EQ(i, q.getSize()); } - // Setting eat count to two and dequeuing (from the front, by default), - // should discard 1001 and 1002, resulting in a dequeue of 1003. - q4.eat_count_ = 2; - ASSERT_NO_THROW(pkt = q4.dequeuePacket()); + // Setting eat count to two and dequeuing should discard 1001 + // and 1002, resulting in a dequeue of 1003. + q.eat_count_ = 2; + ASSERT_NO_THROW(pkt = q.dequeuePacket()); ASSERT_TRUE(pkt); EXPECT_EQ(1003, pkt->getTransid()); - EXPECT_EQ(2, q4.getSize()); - - // Setting eat count to one and dequeing from the back, should discard - // 1005 and dequeue 104. - q4.eat_count_ = 1; - ASSERT_NO_THROW(pkt = q4.dequeuePacket(QueueEnd::BACK)); - ASSERT_TRUE(pkt); - EXPECT_EQ(1004, pkt->getTransid()); - EXPECT_EQ(0, q4.getSize()); + EXPECT_EQ(2, q.getSize()); } } // end of anonymous namespace diff --git a/src/lib/dhcp/tests/packet_queue6_unittest.cc b/src/lib/dhcp/tests/packet_queue6_unittest.cc index 526c1814af..f5bfe400b4 100644 --- a/src/lib/dhcp/tests/packet_queue6_unittest.cc +++ b/src/lib/dhcp/tests/packet_queue6_unittest.cc @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include @@ -78,141 +78,141 @@ public: return (eaten); } + void queueOnePacket(Pkt6Ptr pkt, SocketInfo& sock_info) { + Pkt6PtrSocketInfoList pkt_list; + pkt_list.push_back(Pkt6PtrSocketInfoPair(pkt, sock_info)); + ASSERT_NO_THROW(enqueuePackets(pkt_list)); + } + bool drop_enabled_; int eat_count_; }; // Verifies use of the generic PacketQueue interface to // construct a queue implementation. -TEST(TestQueue6, interfaceBasics) { +TEST(PacketQueueRing6, interfaceBasics) { // Verify we can create a queue - PacketQueue6Ptr q6(new TestQueue6(100)); - ASSERT_TRUE(q6); + PacketQueue6Ptr q(new PacketQueueRing6("kea-ring6",100)); + ASSERT_TRUE(q); // It should be empty. - EXPECT_TRUE(q6->empty()); + EXPECT_TRUE(q->empty()); // Type should match. - EXPECT_EQ("kea-ring6", q6->getQueueType()); + EXPECT_EQ("kea-ring6", q->getQueueType()); // Fetch the queue info and verify it has all the expected values. - checkInfo(q6, "{ \"capacity\": 100, \"queue-type\": \"kea-ring6\", \"size\": 0 }"); + checkInfo(q, "{ \"capacity\": 100, \"queue-type\": \"kea-ring6\", \"size\": 0 }"); } -// Verifies the basic mechanics of the adding and -// removing packets to and from the ring buffer. -TEST(TestQueue6, ringTest) { - PacketQueue6Ptr q6(new TestQueue6(3)); +// Verifies the higher level functions of queueing and dequeueing +// from the ring buffer. +TEST(PacketQueueRing6, enqueueDequeueTest) { + PacketQueue6Ptr q(new PacketQueueRing6("kea-ring6", 3)); // Fetch the queue info and verify it has all the expected values. - checkInfo(q6, "{ \"capacity\": 3, \"queue-type\": \"kea-ring6\", \"size\": 0 }"); + checkInfo(q, "{ \"capacity\": 3, \"queue-type\": \"kea-ring6\", \"size\": 0 }"); // Enqueue five packets. The first two should be pushed off. SocketInfo sock1(isc::asiolink::IOAddress("127.0.0.1"), 777, 10); + + Pkt6PtrSocketInfoList pkt_list; for (int i = 1; i < 6; ++i) { Pkt6Ptr pkt(new Pkt6(DHCPV6_SOLICIT, 1000+i)); - ASSERT_NO_THROW(q6->enqueuePacket(pkt, sock1)); - checkIntStat(q6, "size", (i > 3 ? 3 : i)); + pkt_list.push_back(Pkt6PtrSocketInfoPair(pkt, sock1)); } + // Add the packets to the queue. + ASSERT_NO_THROW(q->enqueuePackets(pkt_list)); + // Fetch the queue info and verify it has all the expected values. - checkInfo(q6, "{ \"capacity\": 3, \"queue-type\": \"kea-ring6\", \"size\": 3 }"); + checkInfo(q, "{ \"capacity\": 3, \"queue-type\": \"kea-ring6\", \"size\": 3 }"); + + // We should have transids 1003,1004,1005 + Pkt6Ptr pkt; + for (int i = 3; i < 6; ++i) { + ASSERT_NO_THROW(pkt = q->dequeuePacket()); + ASSERT_TRUE(pkt); + EXPECT_EQ(1000 + i, pkt->getTransid()); + } + + // Queue should be empty. + ASSERT_TRUE(q->empty()); + + // Dequeuing should fail safely, with an empty return. + ASSERT_NO_THROW(pkt = q->dequeuePacket()); + ASSERT_FALSE(pkt); + + // Enqueue three more packets. + pkt_list.clear(); + for (int i = 0; i < 3; ++i) { + Pkt6Ptr pkt(new Pkt6(DHCPV6_SOLICIT, 1000+i)); + pkt_list.push_back(Pkt6PtrSocketInfoPair(pkt, sock1)); + } + + ASSERT_NO_THROW(q->enqueuePackets(pkt_list)); + checkIntStat(q, "size", 3); + + // Let's flush the buffer and then verify it is empty. + q->clear(); + EXPECT_TRUE(q->empty()); + checkIntStat(q, "size", 0); +} + +// Verifies peeking, pushing, and popping which +// are unique to PacketQueueRing<> derivations. +TEST(PacketQueueRing6, peekPushPopTest) { + PacketQueueRing6 q("kea-ring6", 3); + + // Push five packets onto the end. The first two should get pushed off. + for (int i = 1; i < 6; ++i) { + Pkt6Ptr pkt(new Pkt6(DHCPV6_SOLICIT, 1000+i)); + ASSERT_NO_THROW(q.pushPacket(pkt)); + } + + // We should have three. + ASSERT_EQ(3, q.getSize()); // We should have transids 1005,1004,1003 (back to front) // Peek front should be transid 1003. Pkt6Ptr pkt; - ASSERT_NO_THROW(pkt = q6->peek(QueueEnd::FRONT)); + ASSERT_NO_THROW(pkt = q.peek(QueueEnd::FRONT)); ASSERT_TRUE(pkt); EXPECT_EQ(1003, pkt->getTransid()); // Peek back should be transid 1005. - ASSERT_NO_THROW(pkt = q6->peek(QueueEnd::BACK)); + ASSERT_NO_THROW(pkt = q.peek(QueueEnd::BACK)); ASSERT_TRUE(pkt); EXPECT_EQ(1005, pkt->getTransid()); // Pop front should return transid 1003. - ASSERT_NO_THROW(pkt = q6->popPacket(QueueEnd::FRONT)); + ASSERT_NO_THROW(pkt = q.popPacket(QueueEnd::FRONT)); ASSERT_TRUE(pkt); EXPECT_EQ(1003, pkt->getTransid()); // Pop back should return transid 1005. - ASSERT_NO_THROW(pkt = q6->popPacket(QueueEnd::BACK)); + ASSERT_NO_THROW(pkt = q.popPacket(QueueEnd::BACK)); ASSERT_TRUE(pkt); EXPECT_EQ(1005, pkt->getTransid()); // Peek front should be transid 1004. - ASSERT_NO_THROW(pkt = q6->peek(QueueEnd::FRONT)); + ASSERT_NO_THROW(pkt = q.peek(QueueEnd::FRONT)); ASSERT_TRUE(pkt); EXPECT_EQ(1004, pkt->getTransid()); // Peek back should be transid 1004. - ASSERT_NO_THROW(pkt = q6->peek(QueueEnd::BACK)); + ASSERT_NO_THROW(pkt = q.peek(QueueEnd::BACK)); ASSERT_TRUE(pkt); EXPECT_EQ(1004, pkt->getTransid()); // Pop front should return transid 1004. - ASSERT_NO_THROW(pkt = q6->popPacket(QueueEnd::FRONT)); + ASSERT_NO_THROW(pkt = q.popPacket(QueueEnd::FRONT)); ASSERT_TRUE(pkt); EXPECT_EQ(1004, pkt->getTransid()); // Pop front should return an empty pointer. - ASSERT_NO_THROW(pkt = q6->popPacket(QueueEnd::BACK)); - ASSERT_FALSE(pkt); - - // Enqueue three packets. - for (int i = 1; i < 3; ++i) { - Pkt6Ptr pkt(new Pkt6(DHCPV6_SOLICIT, 1000+i)); - ASSERT_NO_THROW(q6->enqueuePacket(pkt, sock1)); - checkIntStat(q6, "size", i); - } - - // Let's flush the buffer and then verify it is empty. - q6->clear(); - EXPECT_TRUE(q6->empty()); - checkIntStat(q6, "size", 0); -} - -// Verifies the higher level functions of queueing and -// dequeueing with drop and skip logic disabled. -TEST(TestQueue6, enqueueDequeueTest) { - PacketQueue6Ptr q6(new TestQueue6(100)); - EXPECT_TRUE(q6->empty()); - - SocketInfo sock1(isc::asiolink::IOAddress("127.0.0.1"), 777, 10); - - // Enqueue the first packet. - Pkt6Ptr pkt(new Pkt6(DHCPV6_SOLICIT, 1002)); - ASSERT_NO_THROW(q6->enqueuePacket(pkt, sock1)); - checkIntStat(q6, "size", 1); - - // Enqueue a packet onto the front. - pkt.reset(new Pkt6(DHCPV6_SOLICIT, 1003)); - ASSERT_NO_THROW(q6->enqueuePacket(pkt, sock1, QueueEnd::FRONT)); - checkIntStat(q6, "size", 2); - - // Enqueue a packet onto the back. - pkt.reset(new Pkt6(DHCPV6_SOLICIT, 1001)); - ASSERT_NO_THROW(q6->enqueuePacket(pkt, sock1, QueueEnd::BACK)); - checkIntStat(q6, "size", 3); - - // By default we dequeue from the front. We should get transid 1003. - ASSERT_NO_THROW(pkt = q6->dequeuePacket()); - ASSERT_TRUE(pkt); - EXPECT_EQ(1003, pkt->getTransid()); - - // Dequeue from the back, we should get transid 1001. - ASSERT_NO_THROW(pkt = q6->dequeuePacket(QueueEnd::BACK)); - ASSERT_TRUE(pkt); - EXPECT_EQ(1001, pkt->getTransid()); - - // Dequeue from the front, we should get transid 1002. - ASSERT_NO_THROW(pkt = q6->dequeuePacket(QueueEnd::FRONT)); - ASSERT_TRUE(pkt); - EXPECT_EQ(1002, pkt->getTransid()); - - // Queue should be empty. - ASSERT_NO_THROW(pkt = q6->dequeuePacket()); + ASSERT_NO_THROW(pkt = q.popPacket(QueueEnd::BACK)); ASSERT_FALSE(pkt); } @@ -220,58 +220,60 @@ TEST(TestQueue6, enqueueDequeueTest) { // This accesses it's queue instance as a TestQueue6, rather than // a PacketQueue6Ptr, to provide access to TestQueue6 specifics. TEST(TestQueue6, shouldDropPacketTest) { - TestQueue6 q6(100); - EXPECT_TRUE(q6.empty()); - ASSERT_FALSE(q6.drop_enabled_); - ASSERT_EQ(0, q6.eat_count_); + TestQueue6 q(100); + EXPECT_TRUE(q.empty()); + ASSERT_FALSE(q.drop_enabled_); + ASSERT_EQ(0, q.eat_count_); - SocketInfo sockEven(isc::asiolink::IOAddress("127.0.0.1"), 888, 10); - SocketInfo sockOdd(isc::asiolink::IOAddress("127.0.0.1"), 777, 11); + SocketInfo sock_even(isc::asiolink::IOAddress("127.0.0.1"), 888, 10); + SocketInfo sock_odd(isc::asiolink::IOAddress("127.0.0.1"), 777, 11); // Drop is not enabled. // We should be able to enqueue a packet with even numbered values. + Pkt6PtrSocketInfoList pkt_list; + Pkt6Ptr pkt(new Pkt6(DHCPV6_SOLICIT, 1002)); - ASSERT_NO_THROW(q6.enqueuePacket(pkt, sockEven)); - EXPECT_EQ(1, q6.getSize()); + q.queueOnePacket(pkt, sock_even); + ASSERT_EQ(1, q.getSize()); // We should be able to enqueue a packet with odd numbered values. pkt.reset(new Pkt6(DHCPV6_SOLICIT, 1003)); - ASSERT_NO_THROW(q6.enqueuePacket(pkt, sockOdd)); - EXPECT_EQ(2, q6.getSize()); + q.queueOnePacket(pkt, sock_odd); + ASSERT_EQ(2, q.getSize()); // Enable drop logic. - q6.drop_enabled_ = true; + q.drop_enabled_ = true; // We should not be able to add one with an even-numbered transid. pkt.reset(new Pkt6(DHCPV6_SOLICIT, 1004)); - ASSERT_NO_THROW(q6.enqueuePacket(pkt, sockOdd)); - EXPECT_EQ(2, q6.getSize()); + q.queueOnePacket(pkt, sock_odd); + ASSERT_EQ(2, q.getSize()); // We should not be able to add one with from even-numbered port. pkt.reset(new Pkt6(DHCPV6_SOLICIT, 1005)); - ASSERT_NO_THROW(q6.enqueuePacket(pkt, sockEven)); - EXPECT_EQ(2, q6.getSize()); + q.queueOnePacket(pkt, sock_even); + EXPECT_EQ(2, q.getSize()); // We should be able to add one with an odd-numbered values. pkt.reset(new Pkt6(DHCPV6_SOLICIT, 1007)); - ASSERT_NO_THROW(q6.enqueuePacket(pkt, sockOdd)); - EXPECT_EQ(3, q6.getSize()); + q.queueOnePacket(pkt, sock_odd); + EXPECT_EQ(3, q.getSize()); // Dequeue them and make sure they are as expected: 1002,1003, and 1007. - ASSERT_NO_THROW(pkt = q6.dequeuePacket()); + ASSERT_NO_THROW(pkt = q.dequeuePacket()); ASSERT_TRUE(pkt); EXPECT_EQ(1002, pkt->getTransid()); - ASSERT_NO_THROW(pkt = q6.dequeuePacket()); + ASSERT_NO_THROW(pkt = q.dequeuePacket()); ASSERT_TRUE(pkt); EXPECT_EQ(1003, pkt->getTransid()); - ASSERT_NO_THROW(pkt = q6.dequeuePacket()); + ASSERT_NO_THROW(pkt = q.dequeuePacket()); ASSERT_TRUE(pkt); EXPECT_EQ(1007, pkt->getTransid()); // Queue should be empty. - ASSERT_NO_THROW(pkt = q6.dequeuePacket()); + ASSERT_NO_THROW(pkt = q.dequeuePacket()); ASSERT_FALSE(pkt); } @@ -279,10 +281,10 @@ TEST(TestQueue6, shouldDropPacketTest) { // This accesses it's queue instance as a TestQueue6, rather than // a PacketQueue6Ptr, to provide access to TestQueue6 specifics. TEST(TestQueue6, eatPacketsTest) { - TestQueue6 q6(100); - EXPECT_TRUE(q6.empty()); - ASSERT_FALSE(q6.drop_enabled_); - ASSERT_EQ(0, q6.eat_count_); + TestQueue6 q(100); + EXPECT_TRUE(q.empty()); + ASSERT_FALSE(q.drop_enabled_); + ASSERT_EQ(0, q.eat_count_); SocketInfo sock(isc::asiolink::IOAddress("127.0.0.1"), 888, 10); @@ -290,25 +292,17 @@ TEST(TestQueue6, eatPacketsTest) { // Let's add five packets. for (int i = 1; i < 6; ++i) { pkt.reset(new Pkt6(DHCPV6_SOLICIT, 1000 + i)); - ASSERT_NO_THROW(q6.enqueuePacket(pkt, sock)); - EXPECT_EQ(i, q6.getSize()); + q.queueOnePacket(pkt, sock); + ASSERT_EQ(i, q.getSize()); } - // Setting eat count to two and dequeuing (from the front, by default), - // should discard 1001 and 1002, resulting in a dequeue of 1003. - q6.eat_count_ = 2; - ASSERT_NO_THROW(pkt = q6.dequeuePacket()); + // Setting eat count to two and dequeuing should discard 1001 + // and 1002, resulting in a dequeue of 1003. + q.eat_count_ = 2; + ASSERT_NO_THROW(pkt = q.dequeuePacket()); ASSERT_TRUE(pkt); EXPECT_EQ(1003, pkt->getTransid()); - EXPECT_EQ(2, q6.getSize()); - - // Setting eat count to one and dequeing from the back, should discard - // 1005 and dequeue 104. - q6.eat_count_ = 1; - ASSERT_NO_THROW(pkt = q6.dequeuePacket(QueueEnd::BACK)); - ASSERT_TRUE(pkt); - EXPECT_EQ(1004, pkt->getTransid()); - EXPECT_EQ(0, q6.getSize()); + EXPECT_EQ(2, q.getSize()); } } // end of anonymous namespace diff --git a/src/lib/dhcp/tests/packet_queue_mgr4_unittest.cc b/src/lib/dhcp/tests/packet_queue_mgr4_unittest.cc index d746d68465..ed4b16572e 100644 --- a/src/lib/dhcp/tests/packet_queue_mgr4_unittest.cc +++ b/src/lib/dhcp/tests/packet_queue_mgr4_unittest.cc @@ -6,6 +6,7 @@ #include +#include #include #include diff --git a/src/lib/dhcp/tests/packet_queue_mgr6_unittest.cc b/src/lib/dhcp/tests/packet_queue_mgr6_unittest.cc index 38e7643766..6003e03363 100644 --- a/src/lib/dhcp/tests/packet_queue_mgr6_unittest.cc +++ b/src/lib/dhcp/tests/packet_queue_mgr6_unittest.cc @@ -6,6 +6,7 @@ #include +#include #include #include