]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[packet-queue-queue-by-list] PacketQueue now queues lists of packets packet-queue-queue-by-list
authorThomas Markwalder <tmark@isc.org>
Sat, 1 Dec 2018 18:59:05 +0000 (13:59 -0500)
committerThomas Markwalder <tmark@isc.org>
Sat, 1 Dec 2018 18:59:05 +0000 (13:59 -0500)
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

12 files changed:
src/lib/dhcp/Makefile.am
src/lib/dhcp/iface_mgr.cc
src/lib/dhcp/iface_mgr.h
src/lib/dhcp/packet_queue.h
src/lib/dhcp/packet_queue_mgr4.cc
src/lib/dhcp/packet_queue_mgr6.cc
src/lib/dhcp/packet_queue_ring.h [new file with mode: 0644]
src/lib/dhcp/tests/iface_mgr_unittest.cc
src/lib/dhcp/tests/packet_queue4_unittest.cc
src/lib/dhcp/tests/packet_queue6_unittest.cc
src/lib/dhcp/tests/packet_queue_mgr4_unittest.cc
src/lib/dhcp/tests/packet_queue_mgr6_unittest.cc

index eb1efb5ac22f484bca83767f1845302d54e95f4b..a5c4ef863cabebe19e1e6583ece87e75eef29950 100644 (file)
@@ -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 \
index d2e32d2233fdc16e1ce7cdde1d3f818447a4f5f4..86fc6aa462ff0bb4496bb40e38cc36c3530d24d2 100644 (file)
@@ -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
index 43fc90881545ccdf63b7df7f1995cf3276e7fddf..afde3e42c3907fd25b147819cc9dbec6a16918f3 100644 (file)
@@ -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
index 004bd36dffdb015b92cfb88eee248faad3103e32..76a5ab3c407947780abb3b435ad96383acf8ac16 100644 (file)
 #include <dhcp/socket_info.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt6.h>
-#include <util/threads/sync.h>
 
-#include <boost/function.hpp>
-#include <boost/circular_buffer.hpp>
 #include <sstream>
 
 namespace isc {
@@ -60,116 +57,32 @@ public:
     /// Virtual destructor
     virtual ~PacketQueue(){};
 
+    /// @brief Pairs a packet pointer with its source socket.
+    typedef std::pair<PacketTypePtr, SocketInfo> PacketPtrTypeSocketInfoPair;
+    /// @brief Defines a list of packets and their sockets
+    typedef std::vector<PacketPtrTypeSocketInfoPair> 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<PacketQueue<Pkt4Ptr>> PacketQueue4Ptr;
+/// @brief Defines DHCPv4 packet pointer/socket info pair
+typedef std::pair<Pkt4Ptr, SocketInfo> Pkt4PtrSocketInfoPair;
+/// @brief Defines DHCPv4 packet pointer/socket info pair list
+typedef std::vector<Pkt4PtrSocketInfoPair> 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<PacketQueue<Pkt6Ptr>> PacketQueue6Ptr;
-
-
-/// @brief Provides an abstract ring-buffer implementation of the PacketQueue interface.
-template<typename PacketTypePtr>
-class PacketQueueRing : public PacketQueue<PacketTypePtr> {
-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<PacketTypePtr>(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<PacketTypePtr>::getInfo();
-       info->set("capacity", data::Element::create(static_cast<int64_t>(getCapacity())));
-       info->set("size", data::Element::create(static_cast<int64_t>(getSize())));
-       return(info);
-    }
-
-private:
-
-    /// @brief Packet queue
-    boost::circular_buffer<PacketTypePtr> 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<Pkt4Ptr> {
-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<Pkt6Ptr> {
-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<Pkt6Ptr, SocketInfo> Pkt6PtrSocketInfoPair;
+/// @brief Defines DHCPv6 packet pointer/socket info pair list
+typedef std::vector<Pkt6PtrSocketInfoPair> Pkt6PtrSocketInfoList;
 
 }; // namespace isc::dhcp
 }; // namespace isc
index b7ba4ffeb0793645677500ba0364f505421bcd1e..1918a6dfbf77a18373d6189988801deda19f7306 100644 (file)
@@ -5,6 +5,7 @@
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 #include <config.h>
+#include <dhcp/packet_queue_ring.h>
 #include <dhcp/packet_queue_mgr4.h>
 
 #include <boost/scoped_ptr.hpp>
index bcb83ec527eed8157ca751597d8f0f0f4e1206bc..2f08fdf6e4e6dbb5fac26d55773f740edab89079 100644 (file)
@@ -5,6 +5,7 @@
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 #include <config.h>
+#include <dhcp/packet_queue_ring.h>
 #include <dhcp/packet_queue_mgr6.h>
 
 #include <boost/scoped_ptr.hpp>
diff --git a/src/lib/dhcp/packet_queue_ring.h b/src/lib/dhcp/packet_queue_ring.h
new file mode 100644 (file)
index 0000000..f1d93ce
--- /dev/null
@@ -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 <dhcp/packet_queue.h>
+#include <util/threads/sync.h>
+
+#include <boost/function.hpp>
+#include <boost/circular_buffer.hpp>
+#include <sstream>
+
+namespace isc {
+
+namespace dhcp {
+
+/// @brief Provides an abstract ring-buffer implementation of the PacketQueue interface.
+template<typename PacketTypePtr>
+class PacketQueueRing : public PacketQueue<PacketTypePtr> {
+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<PacketTypePtr>(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<PacketTypePtr>::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<PacketTypePtr>::getInfo();
+       info->set("capacity", data::Element::create(static_cast<int64_t>(getCapacity())));
+       info->set("size", data::Element::create(static_cast<int64_t>(getSize())));
+       return(info);
+    }
+
+private:
+
+    /// @brief Packet queue
+    boost::circular_buffer<PacketTypePtr> 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<Pkt4Ptr> {
+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<Pkt6Ptr> {
+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
index 6478e17349c7e55c84b7e38f90600fb1fd978748..455dfc1a6391301ec87f6d2f57ceb3b471faca53 100644 (file)
@@ -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<Pkt4> 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
index b0666b073a80f9ef6b6ae483ca6893f601212f90..de23db7f1a7daabaffb125b508508a8d50d094f4 100644 (file)
@@ -6,7 +6,7 @@
 
 #include <config.h>
 
-#include <dhcp/packet_queue.h>
+#include <dhcp/packet_queue_ring.h>
 #include <dhcp/tests/packet_queue_testutils.h>
 
 #include <boost/shared_ptr.hpp>
@@ -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
index 526c1814afd50a7203be20e0b794651719c0431c..f5bfe400b48ec741e264ce34117153863e034319 100644 (file)
@@ -7,7 +7,7 @@
 #include <config.h>
 
 #include <dhcp/dhcp6.h>
-#include <dhcp/packet_queue.h>
+#include <dhcp/packet_queue_ring.h>
 #include <dhcp/tests/packet_queue_testutils.h>
 
 #include <boost/shared_ptr.hpp>
@@ -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
index d746d6846533634ec60592df260cc4d0248806de..ed4b16572e2f9ecb5f966f115d57e9667949ef6d 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <config.h>
 
+#include <dhcp/packet_queue_ring.h>
 #include <dhcp/packet_queue_mgr4.h>
 #include <dhcp/tests/packet_queue_testutils.h>
 
index 38e76437668610c8d7841467d45360497ff307b6..6003e0336322a8addafe0d8dca02612ecac74203 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <config.h>
 
+#include <dhcp/packet_queue_ring.h>
 #include <dhcp/packet_queue_mgr6.h>
 #include <dhcp/tests/packet_queue_testutils.h>