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
// Copyright (C) 2019-2021 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/.

/// @file This file contains tests which verify flexible option.

#include <config.h>
#include <bootp_log.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <dhcp/pkt4.h>
#include <hooks/callout_manager.h>
#include <hooks/hooks.h>

#include <gtest/gtest.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <sstream><--- 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::bootp;
using namespace isc::dhcp;
using namespace isc::hooks;
using namespace isc::util;

extern "C" {
extern int buffer4_receive(CalloutHandle& handle);
extern int pkt4_send(CalloutHandle& handle);
}

namespace {

/// @brief Test fixture for exercising bootp library callout.
/// It fetches the CalloutManager and prepares stub packets that can be used in
/// tests.
class BootpTest : public ::testing::Test {
public:

    /// @brief Constructor.
    BootpTest() : co_manager_(new CalloutManager(1)) {
    }

    /// @brief Destructor.
    virtual ~BootpTest() {
    }

    /// @brief Fetches the callout manager instance.
    boost::shared_ptr<CalloutManager>getCalloutManager() {
        return (co_manager_);
    }

    /// @brief Tests buffer4_receive callout.
    ///
    /// @param pkt The packet to submit.
    /// @param processed True if the packet must be processed, false otherwise.
    void buffer4ReceiveCalloutCall(Pkt4Ptr& pkt, bool processed) {
        // Get callout handle.
        CalloutHandle handle(getCalloutManager());

        // Get type.
        uint8_t type = pkt->getType();

        // Get data so it becomes possible to reset it to unpacked state.
        ASSERT_NO_THROW(pkt->pack());
        const OutputBuffer& buffer = pkt->getBuffer();
        pkt.reset(new Pkt4(reinterpret_cast<const uint8_t*>(buffer.getData()),
                           buffer.getLength()));

        // Set query.
        handle.setArgument("query4", pkt);

        // Execute buffer4_receive callout.
        int ret;
        ASSERT_NO_THROW(ret = buffer4_receive(handle));
        EXPECT_EQ(0, ret);

        // Verify status (SKIP means unpacked).
        EXPECT_EQ(CalloutHandle::NEXT_STEP_SKIP, handle.getStatus());

        // Verify processing.
        if (processed) {
            EXPECT_TRUE(pkt->inClass("BOOTP"));
            EXPECT_EQ(DHCPREQUEST, pkt->getType());
        } else {
            EXPECT_FALSE(pkt->inClass("BOOTP"));
            EXPECT_EQ(type, pkt->getType());
        }
    }

    /// @brief Tests pkt4_send callout.
    ///
    /// @param pkt The packet to submit.
    /// @param bootp True if the query is in the BOOTP class.
    /// @param processed True if the packet must be processed, false otherwise.
    void pkt4SendCalloutCall(Pkt4Ptr& pkt, bool bootp, bool processed) {
        // Get callout handle.
        CalloutHandle handle(getCalloutManager());

        // Set query.
        Pkt4Ptr query(new Pkt4(DHCPREQUEST, 12345));
        if (bootp) {
            query->addClass("BOOTP");
        }
        handle.setArgument("query4", query);

        // Set response.
        handle.setArgument("response4", pkt);

        // Execute buffer4_receive callout.
        int ret;
        ASSERT_NO_THROW(ret = pkt4_send(handle));
        EXPECT_EQ(0, ret);

        // Verify processing.
        if (processed) {
            EXPECT_EQ(CalloutHandle::NEXT_STEP_SKIP, handle.getStatus());
            EXPECT_FALSE(pkt->getOption(DHO_DHCP_MESSAGE_TYPE));
            EXPECT_LE(300, pkt->getBuffer().getLength());
        } else {
            EXPECT_EQ(CalloutHandle::NEXT_STEP_CONTINUE, handle.getStatus());
            EXPECT_TRUE(pkt->getOption(DHO_DHCP_MESSAGE_TYPE));
            // This works because we did not add options to the response.
            EXPECT_GT(300, pkt->getBuffer().getLength());
        }
    }

    /// @brief Callout manager accessed by this CalloutHandle.
    boost::shared_ptr<CalloutManager> co_manager_;
};

// Verifies that DHCPDISCOVER goes through unmodified.
TEST_F(BootpTest, dhcpDiscover) {<--- syntax error
    Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 12345));
    buffer4ReceiveCalloutCall(pkt, false);
}

// Verifies that DHCPREQUEST goes through unmodified.
TEST_F(BootpTest, dhcpRequest) {
    Pkt4Ptr pkt(new Pkt4(DHCPREQUEST, 12345));
    buffer4ReceiveCalloutCall(pkt, false);
}

// Verifies that DHCPOFFER goes through unmodified.
TEST_F(BootpTest, dhcpOffer) {
    Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 12345));
    buffer4ReceiveCalloutCall(pkt, false);
}

// Verifies that BOOTREPLY goes through unmodified.
TEST_F(BootpTest, bootReply) {
    // The constructor does not allow to directly create a BOOTREPLY packet.
    Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 12345));
    pkt->setType(DHCP_NOTYPE);
    pkt->delOption(DHO_DHCP_MESSAGE_TYPE);
    ASSERT_EQ(BOOTREPLY, pkt->getOp());
    buffer4ReceiveCalloutCall(pkt, false);
}

// Verifies that BOOTREQUEST is recognized and processed.
TEST_F(BootpTest, bootRequest) {
    // The constructor does not allow to directly create a BOOTREQUEST packet.
    Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 12345));
    pkt->setType(DHCP_NOTYPE);
    pkt->delOption(DHO_DHCP_MESSAGE_TYPE);
    ASSERT_EQ(BOOTREQUEST, pkt->getOp());
    buffer4ReceiveCalloutCall(pkt, true);
}

// Verifies that pure DHCPACK goes through unmodified.
TEST_F(BootpTest, dhcpAck) {
    Pkt4Ptr pkt(new Pkt4(DHCPACK, 12345));
    pkt4SendCalloutCall(pkt, false, false);
}

// Verifies that ACK is processed when responding to BOOTP packet.  There's no
// such thing as BOOTP ACK. This test checks that when the server responding to
// BOOTPREQUEST (modified by the hook to look like a DHCPREQUEST), it will
// send back an ACK. The hooks is supposed to strip down the DHCP specific stuff,
// so the response looks like a BOOTPREPLY.
TEST_F(BootpTest, bootpAck) {
    Pkt4Ptr pkt(new Pkt4(DHCPACK, 12345));
    pkt4SendCalloutCall(pkt, true, true);
}

// Verifies that pure DHCPNAK goes through unmodified.
TEST_F(BootpTest, dhcpNak) {
    Pkt4Ptr pkt(new Pkt4(DHCPNAK, 12345));
    pkt4SendCalloutCall(pkt, false, false);
}

// Verifies that BOOTP NAK is processed.
// This particular scenario doesn't make much sense. Since there's not way to
// convey back to the BOOTP client that its request has been declined (and
// IIRC the BOOTP client never sends its address), so there are no cases of
// the server responding with NAK), this scenario seems mostly theoretical.
// But it's good to check that the code is robust.
TEST_F(BootpTest, bootpNak) {
    Pkt4Ptr pkt(new Pkt4(DHCPNAK, 12345));
    pkt4SendCalloutCall(pkt, true, true);
}

} // end of anonymous namespace