1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 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
 72
 73
 74
 75
 76
 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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
// Copyright (C) 2018-2024 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/.

#include <config.h>

#include <dhcp/packet_queue_ring.h>
#include <dhcp/tests/packet_queue_testutils.h>

#include <boost/shared_ptr.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <gtest/gtest.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

using namespace std;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::dhcp::test;

namespace {

/// @brief DHCPv4 queue with implements drop and eat logic
///
/// This class derives from the default DHCPv4 ring queue
/// and provides implementations for shouldDropPacket() and
/// eatPackets().  This permits a full exercising of the
/// PacketQueue interface as well as the basic v4 ring queue
/// mechanics.
class TestQueue4 : public PacketQueueRing4 {
public:
    /// @brief Constructor
    ///
    /// @param queue_size maximum number of packets the queue can hold
    TestQueue4(size_t queue_size)
        : PacketQueueRing4("kea-ring4", queue_size), drop_enabled_(false), eat_count_(0) {
    };

    /// @brief virtual Destructor
    virtual ~TestQueue4(){};

    /// @brief Determines is a packet should be dropped.
    ///
    /// If drop is enabled and either the packet transaction
    /// id or the socket source port are even numbers, drop the packet
    ///
    /// @param packet the packet under consideration
    /// @param source the socket the packet came from
    ///
    /// @return True if the packet should be dropped.
    virtual bool shouldDropPacket(Pkt4Ptr packet,
                            const SocketInfo& source) {
        if (drop_enabled_) {
            return ((packet->getTransid() % 2 == 0) ||
                    (source.port_ % 2 == 0));
        }

        return (false);
    }

    /// @brief Discards a number of packets from one end of the queue
    ///
    /// Dequeue and discard eat_count_ packets from the given end of
    /// the queue_.
    ///
    /// @param from end of the queue from which packets should discarded
    ///
    /// @return The number of packets discarded.
    virtual int eatPackets(const QueueEnd& from) {
        int eaten = 0;
        for ( ; eaten < eat_count_; ++eaten) {
            Pkt4Ptr pkt = popPacket(from);
            if (!pkt) {
                break;
            }
        }

        return (eaten);
    }

    bool drop_enabled_;
    int eat_count_;
};

// Verifies use of the generic PacketQueue interface to
// construct a queue implementation.
TEST(PacketQueueRing4, interfaceBasics) {<--- syntax error
    // Verify we can create a queue
    PacketQueue4Ptr q(new PacketQueueRing4("kea-ring4",100));
    ASSERT_TRUE(q);

    // It should be empty.
    EXPECT_TRUE(q->empty());

    // Type should match.
    EXPECT_EQ("kea-ring4", q->getQueueType());

    // Fetch the queue info and verify it has all the expected values.
    checkInfo(q, "{ \"capacity\": 100, \"queue-type\": \"kea-ring4\", \"size\": 0 }");
}

// 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(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);

    for (unsigned i = 1; i < 6; ++i) {
        Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 1000+i));
        ASSERT_NO_THROW(q->enqueuePacket(pkt, sock1));
    }


    // Fetch the queue info and verify it has all the expected values.
    checkInfo(q, "{ \"capacity\": 3, \"queue-type\": \"kea-ring4\", \"size\": 3 }");

    // We should have transids 1003,1004,1005
    Pkt4Ptr pkt;
    for (unsigned 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.
    for (unsigned i = 0; i < 3; ++i) {
        Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1000+i));
        ASSERT_NO_THROW(q->enqueuePacket(dis, sock1));
    }

    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 (unsigned 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 = q.peek(QueueEnd::FRONT));
    ASSERT_TRUE(pkt);
    EXPECT_EQ(1003, pkt->getTransid());

    // Peek back should be transid 1005.
    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 = q.popPacket(QueueEnd::FRONT));
    ASSERT_TRUE(pkt);
    EXPECT_EQ(1003, pkt->getTransid());

    // Pop back should return transid 1005.
    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 = q.peek(QueueEnd::FRONT));
    ASSERT_TRUE(pkt);
    EXPECT_EQ(1004, pkt->getTransid());

    // Peek back should be transid 1004.
    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 = q.popPacket(QueueEnd::FRONT));
    ASSERT_TRUE(pkt);
    EXPECT_EQ(1004, pkt->getTransid());

    // Pop front should return an empty pointer.
    ASSERT_NO_THROW(pkt = q.popPacket(QueueEnd::BACK));
    ASSERT_FALSE(pkt);
}

// Verifies enqueuing operations when drop logic is enabled.
// This accesses it's queue instance as a TestQueue4, rather than
// a PacketQueue4Ptr, to provide access to TestQueue4 specifics.
TEST(TestQueue4, shouldDropPacketTest) {
    TestQueue4 q(100);
    EXPECT_TRUE(q.empty());
    ASSERT_FALSE(q.drop_enabled_);
    ASSERT_EQ(0, q.eat_count_);

    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.
    Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 1002));
    ASSERT_NO_THROW(q.enqueuePacket(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(q.enqueuePacket(pkt, sock_odd));
    ASSERT_EQ(2, q.getSize());

    // Enable drop logic.
    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(q.enqueuePacket(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(q.enqueuePacket(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(q.enqueuePacket(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 = q.dequeuePacket());
    ASSERT_TRUE(pkt);
    EXPECT_EQ(1002, pkt->getTransid());

    ASSERT_NO_THROW(pkt = q.dequeuePacket());
    ASSERT_TRUE(pkt);
    EXPECT_EQ(1003, pkt->getTransid());

    ASSERT_NO_THROW(pkt = q.dequeuePacket());
    ASSERT_TRUE(pkt);
    EXPECT_EQ(1007, pkt->getTransid());

    // Queue should be empty.
    ASSERT_NO_THROW(pkt = q.dequeuePacket());
    ASSERT_FALSE(pkt);
}

// Verifies dequeuing operations when eat packets is enabled.
// This accesses it's queue instance as a TestQueue4, rather than
// a PacketQueue4Ptr, to provide access to TestQueue4 specifics.
TEST(TestQueue4, eatPacketsTest) {
    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);

    Pkt4Ptr pkt;
    // Let's add five packets.
    for (unsigned i = 1; i < 6; ++i) {
        pkt.reset(new Pkt4(DHCPDISCOVER, 1000 + i));
        ASSERT_NO_THROW(q.enqueuePacket(pkt, sock));
        ASSERT_EQ(i, q.getSize());
    }

    // 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, q.getSize());
}

} // end of anonymous namespace