packet_queue_ring.h 8.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// 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 {

21 22
/// @brief Provides a ring-buffer implementation of the PacketQueue interface.
///
23
/// @tparam PacketTypePtr Type of packet the queue contains.
24
/// This expected to be either isc::dhcp::Pkt4Ptr or isc::dhcp::Pkt6Ptr
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
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
    ///
    /// 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
    virtual void enqueuePacket(PacketTypePtr packet, const SocketInfo& source) {
        if (!shouldDropPacket(packet, source)) {
            pushPacket(packet);
        }
    }

    /// @brief Dequeues the next packet from the queue
    ///
    /// Dequeues the next packet (if any) and returns it.
    ///
    /// @return A pointer to dequeued packet, or an empty pointer
    /// if the queue is empty.
    virtual PacketTypePtr dequeuePacket() {
        eatPackets(QueueEnd::FRONT);
        return(popPacket());
    }

    /// @brief Determines if a packet should be discarded.
    ///
    /// This function is called in @c enqueuePackets for each packet
72
    /// in its packet list. It provides an opportunity to examine the
73
    /// packet and its source and decide whether it should be dropped
74 75 76
    /// 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
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
    /// implementation simply returns false (i.e. keep the packet).
    ///
    /// @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.
    ///
    /// @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.  It locks the queue's Mutex upon entry.
    ///
    /// @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(){}
};

}; // namespace isc::dhcp
}; // namespace isc

#endif // PACKET_QUEUE_RING_H