dhcp4_srv_unittest.cc 8.4 KB
Newer Older
1
// Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#include <config.h>
#include <sstream>

18
#include <asiolink/io_address.h>
19 20
#include <dhcp/dhcp4.h>
#include <dhcp/option.h>
21 22 23 24 25 26 27 28
#include <dhcp4/dhcp4_srv.h>

#include <gtest/gtest.h>

#include <fstream>
#include <iostream>

#include <arpa/inet.h>
29 30 31 32

using namespace std;
using namespace isc;
using namespace isc::dhcp;
33
using namespace isc::asiolink;
34 35

namespace {
36
const char* const INTERFACE_FILE = "interfaces.txt";
37 38

class NakedDhcpv4Srv: public Dhcpv4Srv {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
39
    // "naked" DHCPv4 server, exposes internal fields
40
public:
Tomek Mrugalski's avatar
Tomek Mrugalski committed
41
    NakedDhcpv4Srv():Dhcpv4Srv(DHCP4_SERVER_PORT + 10000) { }
42

43
    boost::shared_ptr<Pkt4> processDiscover(boost::shared_ptr<Pkt4>& discover) {
44 45
        return Dhcpv4Srv::processDiscover(discover);
    }
46
    boost::shared_ptr<Pkt4> processRequest(boost::shared_ptr<Pkt4>& request) {
47 48
        return Dhcpv4Srv::processRequest(request);
    }
49 50 51 52 53 54 55 56 57
    void processRelease(boost::shared_ptr<Pkt4>& release) {
        return Dhcpv4Srv::processRelease(release);
    }
    void processDecline(boost::shared_ptr<Pkt4>& decline) {
        Dhcpv4Srv::processDecline(decline);
    }
    boost::shared_ptr<Pkt4> processInform(boost::shared_ptr<Pkt4>& inform) {
        return Dhcpv4Srv::processInform(inform);
    }
58 59 60 61 62
};

class Dhcpv4SrvTest : public ::testing::Test {
public:
    Dhcpv4SrvTest() {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
63 64 65 66 67 68
        unlink(INTERFACE_FILE);
        fstream fakeifaces(INTERFACE_FILE, ios::out | ios::trunc);
        if (if_nametoindex("lo") > 0) {
            fakeifaces << "lo 127.0.0.1";
        } else if (if_nametoindex("lo0") > 0) {
            fakeifaces << "lo0 127.0.0.1";
69
        }
70
        fakeifaces.close();
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
    void MessageCheck(const boost::shared_ptr<Pkt4>& q,
                      const boost::shared_ptr<Pkt4>& a) {
        ASSERT_TRUE(q);
        ASSERT_TRUE(a);

        EXPECT_EQ(q->getHops(),   a->getHops());
        EXPECT_EQ(q->getIface(),  a->getIface());
        EXPECT_EQ(q->getIndex(),  a->getIndex());
        EXPECT_EQ(q->getGiaddr(), a->getGiaddr());

        // check that bare minimum of required options are there
        EXPECT_TRUE(a->getOption(DHO_SUBNET_MASK));
        EXPECT_TRUE(a->getOption(DHO_ROUTERS));
        EXPECT_TRUE(a->getOption(DHO_DHCP_SERVER_IDENTIFIER));
        EXPECT_TRUE(a->getOption(DHO_DHCP_LEASE_TIME));
        EXPECT_TRUE(a->getOption(DHO_SUBNET_MASK));
        EXPECT_TRUE(a->getOption(DHO_ROUTERS));
        EXPECT_TRUE(a->getOption(DHO_DOMAIN_NAME));
        EXPECT_TRUE(a->getOption(DHO_DOMAIN_NAME_SERVERS));

        // check that something is offered
        EXPECT_TRUE(a->getYiaddr().toText() != "0.0.0.0");
    }

97
    ~Dhcpv4SrvTest() {
98
        unlink(INTERFACE_FILE);
99
    };
100 101 102
};

TEST_F(Dhcpv4SrvTest, basic) {
103 104
    // nothing to test. DHCPv4_srv instance is created
    // in test fixture. It is destroyed in destructor
105

106
    Dhcpv4Srv* srv = NULL;
107
    ASSERT_NO_THROW({
Tomek Mrugalski's avatar
Tomek Mrugalski committed
108
        srv = new Dhcpv4Srv(DHCP4_SERVER_PORT + 10000);
109
    });
110

Tomek Mrugalski's avatar
Tomek Mrugalski committed
111
    delete srv;
112 113 114 115
}

TEST_F(Dhcpv4SrvTest, processDiscover) {
    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
Tomek Mrugalski's avatar
Tomek Mrugalski committed
116
    vector<uint8_t> mac(6);
117 118 119
    for (int i = 0; i < 6; i++) {
        mac[i] = 255 - i;
    }
120 121

    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDISCOVER, 1234));
122 123 124 125 126 127 128 129 130 131 132
    boost::shared_ptr<Pkt4> offer;

    pkt->setIface("eth0");
    pkt->setIndex(17);
    pkt->setHWAddr(1, 6, mac);
    pkt->setRemoteAddr(IOAddress("192.0.2.56"));
    pkt->setGiaddr(IOAddress("192.0.2.67"));

    // let's make it a relayed message
    pkt->setHops(3);
    pkt->setRemotePort(DHCP4_SERVER_PORT);
133 134 135

    // should not throw
    EXPECT_NO_THROW(
136
        offer = srv->processDiscover(pkt);
137 138 139
    );

    // should return something
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
    ASSERT_TRUE(offer);

    EXPECT_EQ(DHCPOFFER, offer->getType());

    // this is relayed message. It should be sent back to relay address.
    EXPECT_EQ(pkt->getGiaddr(), offer->getRemoteAddr());

    MessageCheck(pkt, offer);

    // now repeat the test for directly sent message
    pkt->setHops(0);
    pkt->setGiaddr(IOAddress("0.0.0.0"));
    pkt->setRemotePort(DHCP4_CLIENT_PORT);

    EXPECT_NO_THROW(
        offer = srv->processDiscover(pkt);
    );

    // should return something
    ASSERT_TRUE(offer);

    EXPECT_EQ(DHCPOFFER, offer->getType());

    // this is direct message. It should be sent back to origin, not
    // to relay.
    EXPECT_EQ(pkt->getRemoteAddr(), offer->getRemoteAddr());

    MessageCheck(pkt, offer);
168 169 170 171 172 173

    delete srv;
}

TEST_F(Dhcpv4SrvTest, processRequest) {
    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
Tomek Mrugalski's avatar
Tomek Mrugalski committed
174
    vector<uint8_t> mac(6);
175 176 177 178 179 180
    for (int i = 0; i < 6; i++) {
        mac[i] = i*10;
    }

    boost::shared_ptr<Pkt4> req(new Pkt4(DHCPREQUEST, 1234));
    boost::shared_ptr<Pkt4> ack;
181

182 183 184 185 186
    req->setIface("eth0");
    req->setIndex(17);
    req->setHWAddr(1, 6, mac);
    req->setRemoteAddr(IOAddress("192.0.2.56"));
    req->setGiaddr(IOAddress("192.0.2.67"));
187 188

    // should not throw
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
    ASSERT_NO_THROW(
        ack = srv->processRequest(req);
    );

    // should return something
    ASSERT_TRUE(ack);

    EXPECT_EQ(DHCPACK, ack->getType());

    // this is relayed message. It should be sent back to relay address.
    EXPECT_EQ(req->getGiaddr(), ack->getRemoteAddr());

    MessageCheck(req, ack);

    // now repeat the test for directly sent message
    req->setHops(0);
    req->setGiaddr(IOAddress("0.0.0.0"));
    req->setRemotePort(DHCP4_CLIENT_PORT);

208
    EXPECT_NO_THROW(
209
        ack = srv->processDiscover(req);
210 211 212
    );

    // should return something
213 214 215 216 217 218 219 220 221
    ASSERT_TRUE(ack);

    EXPECT_EQ(DHCPOFFER, ack->getType());

    // this is direct message. It should be sent back to origin, not
    // to relay.
    EXPECT_EQ(ack->getRemoteAddr(), req->getRemoteAddr());

    MessageCheck(req, ack);
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

    delete srv;
}

TEST_F(Dhcpv4SrvTest, processRelease) {
    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();

    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPRELEASE, 1234));

    // should not throw
    EXPECT_NO_THROW(
        srv->processRelease(pkt);
    );

    // TODO: Implement more reasonable tests before starting
    // work on processSomething() method.

    delete srv;
}

TEST_F(Dhcpv4SrvTest, processDecline) {
    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();

    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDECLINE, 1234));

    // should not throw
    EXPECT_NO_THROW(
        srv->processDecline(pkt);
    );

    // TODO: Implement more reasonable tests before starting
    // work on processSomething() method.
    delete srv;
}

TEST_F(Dhcpv4SrvTest, processInform) {
    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();

    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPINFORM, 1234));

    // should not throw
    EXPECT_NO_THROW(
        srv->processInform(pkt);
    );

    // should return something
    EXPECT_TRUE(srv->processInform(pkt));

    // TODO: Implement more reasonable tests before starting
    // work on processSomething() method.
272

273
    delete srv;
274 275
}

276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
TEST_F(Dhcpv4SrvTest, serverReceivedPacketName) {
    // Check all possible packet types
    for (int itype = 0; itype < 256; ++itype) {
        uint8_t type = itype;

        switch (type) {
        case DHCPDECLINE:
            EXPECT_STREQ("DECLINE", Dhcpv4Srv::serverReceivedPacketName(type));
            break;

        case DHCPDISCOVER:
            EXPECT_STREQ("DISCOVER", Dhcpv4Srv::serverReceivedPacketName(type));
            break;

        case DHCPINFORM:
            EXPECT_STREQ("INFORM", Dhcpv4Srv::serverReceivedPacketName(type));
            break;

        case DHCPRELEASE:
            EXPECT_STREQ("RELEASE", Dhcpv4Srv::serverReceivedPacketName(type));
            break;

        case DHCPREQUEST:
            EXPECT_STREQ("REQUEST", Dhcpv4Srv::serverReceivedPacketName(type));
            break;

        default:
            EXPECT_STREQ("UNKNOWN", Dhcpv4Srv::serverReceivedPacketName(type));
        }
    }
}

308
} // end of anonymous namespace