dhcp4_srv_unittest.cc 98.7 KB
Newer Older
1
// Copyright (C) 2011-2017 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// 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/.
6 7 8 9

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

10
#include <asiolink/io_address.h>
11
#include <cc/command_interpreter.h>
12
#include <config/command_mgr.h>
13
#include <dhcp4/tests/dhcp4_test_utils.h>
14
#include <dhcp4/tests/dhcp4_client.h>
15
#include <dhcp/tests/pkt_captures.h>
16
#include <dhcp/dhcp4.h>
17
#include <dhcp/iface_mgr.h>
18
#include <dhcp/libdhcp++.h>
19
#include <dhcp/option.h>
20
#include <dhcp/option_int.h>
21
#include <dhcp/option4_addrlst.h>
22
#include <dhcp/option_custom.h>
23
#include <dhcp/option_int_array.h>
24
#include <dhcp/option_vendor.h>
25 26
#include <dhcp/pkt_filter.h>
#include <dhcp/pkt_filter_inet.h>
27
#include <dhcp/docsis3_option_defs.h>
28
#include <dhcp/tests/iface_mgr_test_config.h>
29
#include <dhcp4/dhcp4_srv.h>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
30
#include <dhcp4/dhcp4_log.h>
31
#include <dhcp4/json_config_parser.h>
32 33 34 35
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/utils.h>
36
#include <dhcpsrv/host_mgr.h>
37
#include <gtest/gtest.h>
38
#include <stats/stats_mgr.h>
39 40
#include <boost/scoped_ptr.hpp>

41 42 43
#include <iostream>

#include <arpa/inet.h>
44 45 46 47

using namespace std;
using namespace isc;
using namespace isc::dhcp;
48
using namespace isc::data;
49
using namespace isc::asiolink;
50
using namespace isc::config;
51
using namespace isc::dhcp::test;
52 53 54

namespace {

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
const char* CONFIGS[] = {
    // Configuration 0:
    // - 1 subnet: 10.254.226.0/25
    // - used for recorded traffic (see PktCaptures::captureRelayedDiscover)
    "{ \"interfaces-config\": {"
        "    \"interfaces\": [ \"*\" ]"
        "},"
        "\"rebind-timer\": 2000, "
        "\"renew-timer\": 1000, "
        "\"subnet4\": [ { "
        "    \"pools\": [ { \"pool\": \"10.254.226.0/25\" } ],"
        "    \"subnet\": \"10.254.226.0/24\", "
        "    \"rebind-timer\": 2000, "
        "    \"renew-timer\": 1000, "
        "    \"valid-lifetime\": 4000,"
        "    \"interface\": \"eth0\" "
        " } ],"
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
    "\"valid-lifetime\": 4000 }",

    // Configuration 1:
    // - 1 subnet: 192.0.2.0/24
    // - MySQL Host Data Source configured
    "{ \"interfaces-config\": {"
        "    \"interfaces\": [ \"*\" ]"
        "},"
        "\"hosts-database\": {"
        "    \"type\": \"mysql\","
        "    \"name\": \"keatest\","
        "    \"user\": \"keatest\","
        "    \"password\": \"keatest\""
        "},"
        "\"rebind-timer\": 2000, "
        "\"renew-timer\": 1000, "
        "\"subnet4\": [ { "
        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
        "    \"subnet\": \"192.0.2.0/24\", "
        "    \"rebind-timer\": 2000, "
        "    \"renew-timer\": 1000, "
        "    \"valid-lifetime\": 4000,"
        "    \"interface\": \"eth0\" "
        " } ],"
    "\"valid-lifetime\": 4000 }"
97 98
};

99 100 101
// This test verifies that the destination address of the response
// message is set to giaddr, when giaddr is set to non-zero address
// in the received message.
102 103 104 105
TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelay) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

106
    // Create the instance of the incoming packet.
107
    boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
108 109
    // Set the giaddr to non-zero address and hops to non-zero value
    // as if it was relayed.
110
    req->setGiaddr(IOAddress("192.0.1.1"));
111
    req->setHops(2);
112 113
    // Set ciaddr to zero. This simulates the client which applies
    // for the new lease.
114
    req->setCiaddr(IOAddress("0.0.0.0"));
115 116 117
    // Clear broadcast flag.
    req->setFlags(0x0000);

118
    // Set local address, port and interface.
119
    req->setLocalAddr(IOAddress("192.0.2.5"));
120
    req->setLocalPort(1001);
121
    req->setIface("eth1");
122 123
    req->setIndex(1);

124
    // Create the exchange using the req.
125
    Dhcpv4Exchange ex = createExchange(req);
126 127

    Pkt4Ptr resp = ex.getResponse();
128
    resp->setYiaddr(IOAddress("192.0.1.100"));
129 130
    // Clear the remote address.
    resp->setRemoteAddr(IOAddress("0.0.0.0"));
131 132
    // Set hops value for the response.
    resp->setHops(req->getHops());
133

134
    // This function never throws.
135
    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
136

137
    // Now the destination address should be relay's address.
138
    EXPECT_EQ("192.0.1.1", resp->getRemoteAddr().toText());
139 140
    // The query has been relayed, so the response must be sent to the port 67.
    EXPECT_EQ(DHCP4_SERVER_PORT, resp->getRemotePort());
141
    // Local address should be the address assigned to interface eth1.
142
    EXPECT_EQ("192.0.2.5", resp->getLocalAddr().toText());
143 144 145 146
    // The local port is always DHCPv4 server port 67.
    EXPECT_EQ(DHCP4_SERVER_PORT, resp->getLocalPort());
    // We will send response over the same interface which was used to receive
    // query.
147
    EXPECT_EQ("eth1", resp->getIface());
148
    EXPECT_EQ(1, resp->getIndex());
149 150 151 152

    // Let's do another test and set other fields: ciaddr and
    // flags. By doing it, we want to make sure that the relay
    // address will take precedence.
153 154
    req->setGiaddr(IOAddress("192.0.1.50"));
    req->setCiaddr(IOAddress("192.0.1.11"));
155
    req->setFlags(Pkt4::FLAG_BROADCAST_MASK);
156

157
    resp->setYiaddr(IOAddress("192.0.1.100"));
158 159 160
    // Clear remote address.
    resp->setRemoteAddr(IOAddress("0.0.0.0"));

161
    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
162 163

    // Response should be sent back to the relay address.
164
    EXPECT_EQ("192.0.1.50", resp->getRemoteAddr().toText());
165 166
}

167 168 169 170
// This test verifies that the destination address of the response message
// is set to ciaddr when giaddr is set to zero and the ciaddr is set to
// non-zero address in the received message. This is the case when the
// client is in Renew or Rebind state.
171 172 173 174
TEST_F(Dhcpv4SrvTest, adjustIfaceDataRenew) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

175 176 177 178 179 180 181 182
    // Create instance of the incoming packet.
    boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));

    // Clear giaddr to simulate direct packet.
    req->setGiaddr(IOAddress("0.0.0.0"));
    // Set ciaddr to non-zero address. The response should be sent to this
    // address as the client is in renewing or rebinding state (it is fully
    // configured).
183
    req->setCiaddr(IOAddress("192.0.1.15"));
184 185 186 187 188 189
    // Let's configure broadcast flag. It should be ignored because
    // we are responding directly to the client having an address
    // and trying to extend his lease. Broadcast flag is only used
    // when new lease is acquired and server must make a decision
    // whether to unicast the response to the acquired address or
    // broadcast it.
190
    req->setFlags(Pkt4::FLAG_BROADCAST_MASK);
191 192 193
    // This is a direct message, so the hops should be cleared.
    req->setHops(0);
    // Set local unicast address as if we are renewing a lease.
194
    req->setLocalAddr(IOAddress("192.0.2.1"));
195 196 197
    // Request is received on the DHCPv4 server port.
    req->setLocalPort(DHCP4_SERVER_PORT);
    // Set the interface. The response should be sent over the same interface.
198
    req->setIface("eth1");
199
    req->setIndex(1);
200

201
    // Create the exchange using the req.
202
    Dhcpv4Exchange ex = createExchange(req);
203 204
    Pkt4Ptr resp = ex.getResponse();

205 206 207 208
    // Let's extend the lease for the client in such a way that
    // it will actually get different address. The response
    // should not be sent to this address but rather to ciaddr
    // as client still have ciaddr configured.
209
    resp->setYiaddr(IOAddress("192.0.1.13"));
210 211
    // Clear the remote address.
    resp->setRemoteAddr(IOAddress("0.0.0.0"));
212 213
    // Copy hops value from the query.
    resp->setHops(req->getHops());
214

215
    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
216 217

    // Check that server responds to ciaddr
218
    EXPECT_EQ("192.0.1.15", resp->getRemoteAddr().toText());
219 220 221 222 223
    // The query was non-relayed, so the response should be sent to a DHCPv4
    // client port 68.
    EXPECT_EQ(DHCP4_CLIENT_PORT, resp->getRemotePort());
    // The response should be sent from the unicast address on which the
    // query has been received.
224
    EXPECT_EQ("192.0.2.1", resp->getLocalAddr().toText());
225 226 227
    // The response should be sent from the DHCPv4 server port.
    EXPECT_EQ(DHCP4_SERVER_PORT, resp->getLocalPort());
    // The interface data should match the data in the query.
228
    EXPECT_EQ("eth1", resp->getIface());
229 230
    EXPECT_EQ(1, resp->getIndex());

231 232
}

233 234 235 236 237 238 239
// This test verifies that the destination address of the response message
// is set correctly when giaddr and ciaddr is zeroed in the received message
// and the new lease is acquired. The lease address is carried in the
// response message in the yiaddr field. In this case destination address
// of the response should be set to yiaddr if server supports direct responses
// to the client which doesn't have an address yet or broadcast if the server
// doesn't support direct responses.
240 241 242 243
TEST_F(Dhcpv4SrvTest, adjustIfaceDataSelect) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

244 245 246 247 248 249 250 251 252 253 254
    // Create instance of the incoming packet.
    boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));

    // Clear giaddr to simulate direct packet.
    req->setGiaddr(IOAddress("0.0.0.0"));
    // Clear client address as it hasn't got any address configured yet.
    req->setCiaddr(IOAddress("0.0.0.0"));

    // Let's clear the broadcast flag.
    req->setFlags(0);

255 256 257 258 259 260 261
    // This is a non-relayed message, so let's clear hops count.
    req->setHops(0);
    // The query is sent to the broadcast address in the Select state.
    req->setLocalAddr(IOAddress("255.255.255.255"));
    // The query has been received on the DHCPv4 server port 67.
    req->setLocalPort(DHCP4_SERVER_PORT);
    // Set the interface. The response should be sent via the same interface.
262
    req->setIface("eth1");
263 264
    req->setIndex(1);

265
    // Create the exchange using the req.
266
    Dhcpv4Exchange ex = createExchange(req);
267
    Pkt4Ptr resp = ex.getResponse();
268
    // Assign some new address for this client.
269
    resp->setYiaddr(IOAddress("192.0.1.13"));
270 271
    // Clear the remote address.
    resp->setRemoteAddr(IOAddress("0.0.0.0"));
272 273 274 275 276 277 278 279
    // Copy hops count.
    resp->setHops(req->getHops());

    // We want to test the case, when the server (packet filter) doesn't support
    // ddirect responses to the client which doesn't have an address yet. In
    // case, the server should send its response to the broadcast address.
    // We can control whether the current packet filter returns that its support
    // direct responses or not.
280
    test_config.setDirectResponse(false);
281 282 283 284 285 286 287

    // When running unit tests, the IfaceMgr is using the default Packet
    // Filtering class, PktFilterInet. This class does not support direct
    // responses to clients without address assigned. When giaddr and ciaddr
    // are zero and client has just got new lease, the assigned address is
    // carried in yiaddr. In order to send this address to the client,
    // server must broadcast its response.
288
    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
289 290 291 292 293

    // Check that the response is sent to broadcast address as the
    // server doesn't have capability to respond directly.
    EXPECT_EQ("255.255.255.255", resp->getRemoteAddr().toText());

294 295 296
    // Although the query has been sent to the broadcast address, the
    // server should select a unicast address on the particular interface
    // as a source address for the response.
297
    EXPECT_EQ("192.0.2.3", resp->getLocalAddr().toText());
298 299 300 301 302 303

    // The response should be sent from the DHCPv4 server port.
    EXPECT_EQ(DHCP4_SERVER_PORT, resp->getLocalPort());

    // The response should be sent via the same interface through which
    // query has been received.
304
    EXPECT_EQ("eth1", resp->getIface());
305 306
    EXPECT_EQ(1, resp->getIndex());

307 308 309
    // We also want to test the case when the server has capability to
    // respond directly to the client which is not configured. Server
    // makes decision whether it responds directly or broadcast its
310 311 312
    // response based on the capability reported by IfaceMgr. We can
    // control whether the current packet filter returns that it supports
    // direct responses or not.
313
    test_config.setDirectResponse(true);
314 315 316

    // Now we expect that the server will send its response to the
    // address assigned for the client.
317
    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
318

319
    EXPECT_EQ("192.0.1.13", resp->getRemoteAddr().toText());
320 321
}

322 323 324 325 326
// This test verifies that the destination address of the response message
// is set to broadcast address when client set broadcast flag in its
// query. Client sets this flag to indicate that it can't receive direct
// responses from the server when it doesn't have its interface configured.
// Server must respect broadcast flag.
327 328 329 330
TEST_F(Dhcpv4SrvTest, adjustIfaceDataBroadcast) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

331 332 333 334 335 336 337
    // Create instance of the incoming packet.
    boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));

    // Clear giaddr to simulate direct packet.
    req->setGiaddr(IOAddress("0.0.0.0"));
    // Clear client address as it hasn't got any address configured yet.
    req->setCiaddr(IOAddress("0.0.0.0"));
338 339 340 341 342
    // The query is sent to the broadcast address in the Select state.
    req->setLocalAddr(IOAddress("255.255.255.255"));
    // The query has been received on the DHCPv4 server port 67.
    req->setLocalPort(DHCP4_SERVER_PORT);
    // Set the interface. The response should be sent via the same interface.
343
    req->setIface("eth1");
344
    req->setIndex(1);
345 346

    // Let's set the broadcast flag.
347
    req->setFlags(Pkt4::FLAG_BROADCAST_MASK);
348

349
    // Create the exchange using the req.
350
    Dhcpv4Exchange ex = createExchange(req);
351 352
    Pkt4Ptr resp = ex.getResponse();

353
    // Assign some new address for this client.
354
    resp->setYiaddr(IOAddress("192.0.1.13"));
355 356 357 358

    // Clear the remote address.
    resp->setRemoteAddr(IOAddress("0.0.0.0"));

359
    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
360

Francis Dupont's avatar
Francis Dupont committed
361
    // Server must respond to broadcast address when client desired that
362 363
    // by setting the broadcast flag in its request.
    EXPECT_EQ("255.255.255.255", resp->getRemoteAddr().toText());
364 365 366 367

    // Although the query has been sent to the broadcast address, the
    // server should select a unicast address on the particular interface
    // as a source address for the response.
368
    EXPECT_EQ("192.0.2.3", resp->getLocalAddr().toText());
369 370 371 372 373 374

    // The response should be sent from the DHCPv4 server port.
    EXPECT_EQ(DHCP4_SERVER_PORT, resp->getLocalPort());

    // The response should be sent via the same interface through which
    // query has been received.
375
    EXPECT_EQ("eth1", resp->getIface());
376 377 378 379
    EXPECT_EQ(1, resp->getIndex());

}

380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
// This test verifies that the mandatory to copy fields and options
// are really copied into the response.
TEST_F(Dhcpv4SrvTest, initResponse) {
    Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 1234));

    // Set fields which must be copied
    query->setIface("foo");
    query->setIndex(111);
    query->setHops(5);
    const HWAddr& hw = HWAddr::fromText("11:22:33:44:55:66:77:88", 10);
    HWAddrPtr hw_addr(new HWAddr(hw));
    query->setHWAddr(hw_addr);
    query->setGiaddr(IOAddress("10.10.10.10"));
    const HWAddr& src_hw = HWAddr::fromText("e4:ce:8f:12:34:56");
    HWAddrPtr src_hw_addr(new HWAddr(src_hw));
    query->setLocalHWAddr(src_hw_addr);
    const HWAddr& dst_hw = HWAddr::fromText("e8:ab:cd:78:9a:bc");
    HWAddrPtr dst_hw_addr(new HWAddr(dst_hw));
    query->setRemoteHWAddr(dst_hw_addr);
399
    query->setFlags(BOOTP_BROADCAST);
400 401 402 403 404

    // Add options which must be copied
    // client-id echo is optional
    // rai echo is done in relayAgentInfoEcho
    // Do subnet selection option
405
    OptionDefinitionPtr sbnsel_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE,
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
                                                           DHO_SUBNET_SELECTION);
    ASSERT_TRUE(sbnsel_def);
    OptionCustomPtr sbnsel(new OptionCustom(*sbnsel_def, Option::V4));
    ASSERT_TRUE(sbnsel);
    sbnsel->writeAddress(IOAddress("192.0.2.3"));
    query->addOption(sbnsel);

    // Create exchange and get Response
    Dhcpv4Exchange ex = createExchange(query);
    Pkt4Ptr response = ex.getResponse();
    ASSERT_TRUE(response);

    // Check fields
    EXPECT_EQ("foo", response->getIface());
    EXPECT_EQ(111, response->getIndex());
    EXPECT_TRUE(response->getSiaddr().isV4Zero());
    EXPECT_TRUE(response->getCiaddr().isV4Zero());
    EXPECT_EQ(5, response->getHops());
424
    EXPECT_TRUE(hw == *response->getHWAddr());
425
    EXPECT_EQ(IOAddress("10.10.10.10"), response->getGiaddr());
426 427
    EXPECT_TRUE(src_hw == *response->getLocalHWAddr());
    EXPECT_TRUE(dst_hw == *response->getRemoteHWAddr());
428
    EXPECT_TRUE(BOOTP_BROADCAST == response->getFlags());
429 430 431 432 433

    // Check options (i.e., subnet selection option)
    OptionPtr resp_sbnsel = response->getOption(DHO_SUBNET_SELECTION);
    ASSERT_TRUE(resp_sbnsel);
    OptionCustomPtr resp_custom =
434
        boost::dynamic_pointer_cast<OptionCustom>(resp_sbnsel);
435 436 437 438 439 440
    ASSERT_TRUE(resp_custom);
    IOAddress subnet_addr("0.0.0.0");
    ASSERT_NO_THROW(subnet_addr = resp_custom->readAddress());
    EXPECT_EQ(IOAddress("192.0.2.3"), subnet_addr);
}

441 442 443
// This test verifies that the server identifier option is appended to
// a specified DHCPv4 message and the server identifier is correct.
TEST_F(Dhcpv4SrvTest, appendServerID) {
444
    Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 1234));
445
    Dhcpv4Exchange ex = createExchange(query);
446 447
    Pkt4Ptr response = ex.getResponse();

448 449 450 451 452
    // Set a local address. It is required by the function under test
    // to create the Server Identifier option.
    response->setLocalAddr(IOAddress("192.0.3.1"));

    // Append the Server Identifier.
453
    ASSERT_NO_THROW(NakedDhcpv4Srv::appendServerID(ex));
454 455 456 457 458 459 460 461 462 463 464 465 466 467

    // Make sure that the option has been added.
    OptionPtr opt = response->getOption(DHO_DHCP_SERVER_IDENTIFIER);
    ASSERT_TRUE(opt);
    Option4AddrLstPtr opt_server_id =
        boost::dynamic_pointer_cast<Option4AddrLst>(opt);
    ASSERT_TRUE(opt_server_id);

    // The option is represented as a list of IPv4 addresses but with
    // only one address added.
    Option4AddrLst::AddressContainer addrs = opt_server_id->getAddresses();
    ASSERT_EQ(1, addrs.size());
    // This address should match the local address of the packet.
    EXPECT_EQ("192.0.3.1", addrs[0].toText());
468 469
}

470 471 472 473 474 475
// Sanity check. Verifies that both Dhcpv4Srv and its derived
// class NakedDhcpv4Srv can be instantiated and destroyed.
TEST_F(Dhcpv4SrvTest, basic) {

    // Check that the base class can be instantiated
    boost::scoped_ptr<Dhcpv4Srv> srv;
476 477
    ASSERT_NO_THROW(srv.reset(new Dhcpv4Srv(DHCP4_SERVER_PORT + 10000, false,
                                            false)));
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
    srv.reset();
    // We have to close open sockets because further in this test we will
    // call the Dhcpv4Srv constructor again. This constructor will try to
    // set the appropriate packet filter class for IfaceMgr. This requires
    // that all sockets are closed.
    IfaceMgr::instance().closeSockets();

    // Check that the derived class can be instantiated
    boost::scoped_ptr<NakedDhcpv4Srv> naked_srv;
    ASSERT_NO_THROW(
        naked_srv.reset(new NakedDhcpv4Srv(DHCP4_SERVER_PORT + 10000)));
    // Close sockets again for the next test.
    IfaceMgr::instance().closeSockets();

    ASSERT_NO_THROW(naked_srv.reset(new NakedDhcpv4Srv(0)));
}

495
// Verifies that DISCOVER message can be processed correctly,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
496 497 498 499 500 501 502
// that the OFFER message generated in response is valid and
// contains necessary options.
//
// Note: this test focuses on the packet correctness. There
// are other tests that verify correctness of the allocation
// engine. See DiscoverBasic, DiscoverHint, DiscoverNoClientId
// and DiscoverInvalidHint.
503
TEST_F(Dhcpv4SrvTest, processDiscover) {
504
    testDiscoverRequest(DHCPDISCOVER);
505
}
506

507
// Verifies that REQUEST message can be processed correctly,
508
// that the OFFER message generated in response is valid and
Tomek Mrugalski's avatar
Tomek Mrugalski committed
509 510 511 512
// contains necessary options.
//
// Note: this test focuses on the packet correctness. There
// are other tests that verify correctness of the allocation
513 514
// engine. See DiscoverBasic, DiscoverHint, DiscoverNoClientId
// and DiscoverInvalidHint.
515
TEST_F(Dhcpv4SrvTest, processRequest) {
516
    testDiscoverRequest(DHCPREQUEST);
517 518
}

519
TEST_F(Dhcpv4SrvTest, processRelease) {
520
    NakedDhcpv4Srv srv;
521
    Pkt4Ptr pkt(new Pkt4(DHCPRELEASE, 1234));
522

523
    // Should not throw
524
    EXPECT_NO_THROW(srv.processRelease(pkt));
525 526
}

527 528 529 530 531 532 533 534 535 536 537
// This test verifies that incoming DISCOVER can be handled properly, that an
// OFFER is generated, that the response has an address and that address
// really belongs to the configured pool.
//
// constructed very simple DISCOVER message with:
// - client-id option
//
// expected returned OFFER message:
// - copy of client-id
// - server-id
// - offered address
538 539 540 541
TEST_F(Dhcpv4SrvTest, DiscoverBasic) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

542
    boost::scoped_ptr<NakedDhcpv4Srv> srv;
543
    ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
544 545 546 547 548

    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
    dis->setRemoteAddr(IOAddress("192.0.2.1"));
    OptionPtr clientid = generateClientId();
    dis->addOption(clientid);
549
    dis->setIface("eth1");
550

551
    // Pass it to the server and get an offer
552 553
    Pkt4Ptr offer = srv->processDiscover(dis);

554
    // Check if we get response at all
555 556
    checkResponse(offer, DHCPOFFER, 1234);

557
    // Check that address was returned from proper range, that its lease
558
    // lifetime is correct, that T1 and T2 are returned properly
559
    checkAddressParams(offer, subnet_, true, true);
560

561
    // Check identifiers
562 563 564 565
    checkServerId(offer, srv->getServerID());
    checkClientId(offer, clientid);
}

566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
// Check that option 58 and 59 are not included if they are not specified.
TEST_F(Dhcpv4SrvTest, DiscoverNoTimers) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

    boost::scoped_ptr<NakedDhcpv4Srv> srv;
    ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));

    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
    dis->setRemoteAddr(IOAddress("192.0.2.1"));
    OptionPtr clientid = generateClientId();
    dis->addOption(clientid);
    dis->setIface("eth1");

    // Recreate a subnet but set T1 and T2 to "unspecified".
    subnet_.reset(new Subnet4(IOAddress("192.0.2.0"), 24,
                              Triplet<uint32_t>(),
                              Triplet<uint32_t>(),
                              3000));
    pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"),
                               IOAddress("192.0.2.110")));
    subnet_->addPool(pool_);
588 589 590
    CfgMgr::instance().clear();
    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
    CfgMgr::instance().commit();
591 592 593 594 595 596 597 598 599 600 601 602 603 604

    // Pass it to the server and get an offer
    Pkt4Ptr offer = srv->processDiscover(dis);

    // Check if we get response at all
    checkResponse(offer, DHCPOFFER, 1234);

    // T1 and T2 timers must not be present.
    checkAddressParams(offer, subnet_, false, false);

    // Check identifiers
    checkServerId(offer, srv->getServerID());
    checkClientId(offer, clientid);
}
605 606 607 608 609 610 611 612 613 614 615 616 617

// This test verifies that incoming DISCOVER can be handled properly, that an
// OFFER is generated, that the response has an address and that address
// really belongs to the configured pool.
//
// constructed very simple DISCOVER message with:
// - client-id option
// - address set to specific value as hint, but that hint is invalid
//
// expected returned OFFER message:
// - copy of client-id
// - server-id
// - offered address (!= hint)
618 619 620 621
TEST_F(Dhcpv4SrvTest, DiscoverInvalidHint) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

622
    boost::scoped_ptr<NakedDhcpv4Srv> srv;
623
    ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
624 625 626 627 628 629 630
    IOAddress hint("10.1.2.3");

    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
    dis->setRemoteAddr(IOAddress("192.0.2.107"));
    OptionPtr clientid = generateClientId();
    dis->addOption(clientid);
    dis->setYiaddr(hint);
631
    dis->setIface("eth1");
632

633
    // Pass it to the server and get an offer
634 635
    Pkt4Ptr offer = srv->processDiscover(dis);

636
    // Check if we get response at all
637 638
    checkResponse(offer, DHCPOFFER, 1234);

639
    // Check that address was returned from proper range, that its lease
640
    // lifetime is correct, that T1 and T2 are returned properly
641
    checkAddressParams(offer, subnet_, true, true);
642

643
    EXPECT_NE(offer->getYiaddr(), hint);
644

645
    // Check identifiers
646 647 648 649 650 651 652 653
    checkServerId(offer, srv->getServerID());
    checkClientId(offer, clientid);
}

/// @todo: Add a test that client sends hint that is in pool, but currently
/// being used by a different client.

// This test checks that the server is offering different addresses to different
Tomek Mrugalski's avatar
Tomek Mrugalski committed
654 655
// clients in OFFERs. Please note that OFFER is not a guarantee that such
// an address will be assigned. Had the pool was very small and contained only
656
// 2 addresses, the third client would get the same offer as the first one
657
// and this is a correct behavior. It is REQUEST that will fail for the third
Tomek Mrugalski's avatar
Tomek Mrugalski committed
658
// client. OFFER is basically saying "if you send me a request, you will
659
// probably get an address like this" (there are no guarantees).
660 661 662 663
TEST_F(Dhcpv4SrvTest, ManyDiscovers) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

664
    boost::scoped_ptr<NakedDhcpv4Srv> srv;
665
    ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
666 667 668 669 670 671 672 673 674

    Pkt4Ptr dis1 = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
    Pkt4Ptr dis2 = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 2345));
    Pkt4Ptr dis3 = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 3456));

    dis1->setRemoteAddr(IOAddress("192.0.2.1"));
    dis2->setRemoteAddr(IOAddress("192.0.2.2"));
    dis3->setRemoteAddr(IOAddress("192.0.2.3"));

675
    // Assign interfaces
676 677 678
    dis1->setIface("eth1");
    dis2->setIface("eth1");
    dis3->setIface("eth1");
679

680
    // Different client-id sizes
681 682 683 684 685 686 687 688
    OptionPtr clientid1 = generateClientId(4); // length 4
    OptionPtr clientid2 = generateClientId(5); // length 5
    OptionPtr clientid3 = generateClientId(6); // length 6

    dis1->addOption(clientid1);
    dis2->addOption(clientid2);
    dis3->addOption(clientid3);

689 690 691 692
    // Pass it to the server and get an offer
    Pkt4Ptr offer1 = srv->processDiscover(dis1);
    Pkt4Ptr offer2 = srv->processDiscover(dis2);
    Pkt4Ptr offer3 = srv->processDiscover(dis3);
693

694 695 696 697
    // Check if we get response at all
    checkResponse(offer1, DHCPOFFER, 1234);
    checkResponse(offer2, DHCPOFFER, 2345);
    checkResponse(offer3, DHCPOFFER, 3456);
698

699 700 701
    IOAddress addr1 = offer1->getYiaddr();
    IOAddress addr2 = offer2->getYiaddr();
    IOAddress addr3 = offer3->getYiaddr();
702 703

    // Check that the assigned address is indeed from the configured pool
704 705 706
    checkAddressParams(offer1, subnet_, true, true);
    checkAddressParams(offer2, subnet_, true, true);
    checkAddressParams(offer3, subnet_, true, true);
707

708
    // Check server-ids
709 710 711 712 713 714
    checkServerId(offer1, srv->getServerID());
    checkServerId(offer2, srv->getServerID());
    checkServerId(offer3, srv->getServerID());
    checkClientId(offer1, clientid1);
    checkClientId(offer2, clientid2);
    checkClientId(offer3, clientid3);
715 716

    // Finally check that the addresses offered are different
717 718 719 720 721 722
    EXPECT_NE(addr1, addr2);
    EXPECT_NE(addr2, addr3);
    EXPECT_NE(addr3, addr1);
    cout << "Offered address to client1=" << addr1 << endl;
    cout << "Offered address to client2=" << addr2 << endl;
    cout << "Offered address to client3=" << addr3 << endl;
723 724
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
725 726 727
// Checks whether echoing back client-id is controllable, i.e.
// whether the server obeys echo-client-id and sends (or not)
// client-id
728 729 730 731
TEST_F(Dhcpv4SrvTest, discoverEchoClientId) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

732 733 734 735 736 737
    NakedDhcpv4Srv srv(0);

    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
    dis->setRemoteAddr(IOAddress("192.0.2.1"));
    OptionPtr clientid = generateClientId();
    dis->addOption(clientid);
738
    dis->setIface("eth1");
739 740 741 742 743 744 745 746

    // Pass it to the server and get an offer
    Pkt4Ptr offer = srv.processDiscover(dis);

    // Check if we get response at all
    checkResponse(offer, DHCPOFFER, 1234);
    checkClientId(offer, clientid);

747 748 749 750 751 752 753 754
    ConstSrvConfigPtr cfg = CfgMgr::instance().getCurrentCfg();
    const Subnet4Collection* subnets = cfg->getCfgSubnets4()->getAll();
    ASSERT_EQ(1, subnets->size());
    CfgMgr::instance().clear();
    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnets->at(0));
    CfgMgr::instance().getStagingCfg()->setEchoClientId(false);
    CfgMgr::instance().commit();
    
755 756 757 758 759 760 761
    offer = srv.processDiscover(dis);

    // Check if we get response at all
    checkResponse(offer, DHCPOFFER, 1234);
    checkClientId(offer, clientid);
}

762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
// Check that option 58 and 59 are not included if they are not specified.
TEST_F(Dhcpv4SrvTest, RequestNoTimers) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

    boost::scoped_ptr<NakedDhcpv4Srv> srv;
    ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));

    Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
    req->setRemoteAddr(IOAddress("192.0.2.1"));
    OptionPtr clientid = generateClientId();
    req->addOption(clientid);
    req->setIface("eth1");

    // Recreate a subnet but set T1 and T2 to "unspecified".
    subnet_.reset(new Subnet4(IOAddress("192.0.2.0"), 24,
                              Triplet<uint32_t>(),
                              Triplet<uint32_t>(),
                              3000));
    pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"),
                               IOAddress("192.0.2.110")));
    subnet_->addPool(pool_);
784 785 786
    CfgMgr::instance().clear();
    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
    CfgMgr::instance().commit();
787 788 789 790 791 792 793 794 795 796 797 798 799 800 801

    // Pass it to the server and get an ACK.
    Pkt4Ptr ack = srv->processRequest(req);

    // Check if we get response at all
    checkResponse(ack, DHCPACK, 1234);

    // T1 and T2 timers must not be present.
    checkAddressParams(ack, subnet_, false, false);

    // Check identifiers
    checkServerId(ack, srv->getServerID());
    checkClientId(ack, clientid);
}

802
// Checks whether echoing back client-id is controllable
803 804 805 806
TEST_F(Dhcpv4SrvTest, requestEchoClientId) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

807 808 809 810 811 812
    NakedDhcpv4Srv srv(0);

    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
    dis->setRemoteAddr(IOAddress("192.0.2.1"));
    OptionPtr clientid = generateClientId();
    dis->addOption(clientid);
813
    dis->setIface("eth1");
814 815 816 817 818 819 820 821

    // Pass it to the server and get ACK
    Pkt4Ptr ack = srv.processRequest(dis);

    // Check if we get response at all
    checkResponse(ack, DHCPACK, 1234);
    checkClientId(ack, clientid);

822 823 824 825 826 827 828 829
    ConstSrvConfigPtr cfg = CfgMgr::instance().getCurrentCfg();
    const Subnet4Collection* subnets = cfg->getCfgSubnets4()->getAll();
    ASSERT_EQ(1, subnets->size());
    CfgMgr::instance().clear();
    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnets->at(0));
    CfgMgr::instance().getStagingCfg()->setEchoClientId(false);
    CfgMgr::instance().commit();

830
    ack = srv.processRequest(dis);
831 832

    // Check if we get response at all
833
    checkResponse(ack, DHCPACK, 1234);
834 835 836
    checkClientId(ack, clientid);
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
837 838

// This test verifies that incoming (positive) REQUEST/Renewing can be handled properly, that a
839 840 841 842 843 844 845 846
// REPLY is generated, that the response has an address and that address
// really belongs to the configured pool and that lease is actually renewed.
//
// expected:
// - returned REPLY message has copy of client-id
// - returned REPLY message has server-id
// - returned REPLY message has IA that includes IAADDR
// - lease is actually renewed in LeaseMgr
847 848 849 850
TEST_F(Dhcpv4SrvTest, RenewBasic) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

851
    boost::scoped_ptr<NakedDhcpv4Srv> srv;
852
    ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
853 854

    const IOAddress addr("192.0.2.106");
855 856 857 858
    const uint32_t temp_t1 = 50;
    const uint32_t temp_t2 = 75;
    const uint32_t temp_valid = 100;
    const time_t temp_timestamp = time(NULL) - 10;
859

860
    // Generate client-id also sets client_id_ member
861 862 863
    OptionPtr clientid = generateClientId();

    // Check that the address we are about to use is indeed in pool
864
    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
865 866

    // let's create a lease and put it in the LeaseMgr
867 868 869
    uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
    HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER));
    Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2,
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889
                              &client_id_->getDuid()[0], client_id_->getDuid().size(),
                              temp_valid, temp_t1, temp_t2, temp_timestamp,
                              subnet_->getID()));
    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));

    // Check that the lease is really in the database
    Lease4Ptr l = LeaseMgrFactory::instance().getLease4(addr);
    ASSERT_TRUE(l);

    // Check that T1, T2, preferred, valid and cltt really set.
    // Constructed lease looks as if it was assigned 10 seconds ago
    // EXPECT_EQ(l->t1_, temp_t1);
    // EXPECT_EQ(l->t2_, temp_t2);
    EXPECT_EQ(l->valid_lft_, temp_valid);
    EXPECT_EQ(l->cltt_, temp_timestamp);

    // Let's create a RENEW
    Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
    req->setRemoteAddr(IOAddress(addr));
    req->setYiaddr(addr);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
890
    req->setCiaddr(addr); // client's address
891
    req->setIface("eth0");
892
    req->setHWAddr(hwaddr2);
893 894 895 896 897 898 899 900 901

    req->addOption(clientid);
    req->addOption(srv->getServerID());

    // Pass it to the server and hope for a REPLY
    Pkt4Ptr ack = srv->processRequest(req);

    // Check if we get response at all
    checkResponse(ack, DHCPACK, 1234);
902
    EXPECT_EQ(addr, ack->getYiaddr());
903

904
    // Check that address was returned from proper range, that its lease
905
    // lifetime is correct, that T1 and T2 are returned properly
906
    checkAddressParams(ack, subnet_, true, true);
907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923

    // Check identifiers
    checkServerId(ack, srv->getServerID());
    checkClientId(ack, clientid);

    // Check that the lease is really in the database
    l = checkLease(ack, clientid, req->getHWAddr(), addr);
    ASSERT_TRUE(l);

    // Check that T1, T2, preferred, valid and cltt were really updated
    EXPECT_EQ(l->t1_, subnet_->getT1());
    EXPECT_EQ(l->t2_, subnet_->getT2());
    EXPECT_EQ(l->valid_lft_, subnet_->getValid());

    // Checking for CLTT is a bit tricky if we want to avoid off by 1 errors
    int32_t cltt = static_cast<int32_t>(l->cltt_);
    int32_t expected = static_cast<int32_t>(time(NULL));
924
    // Equality or difference by 1 between cltt and expected is ok.
925 926 927 928 929
    EXPECT_GE(1, abs(cltt - expected));

    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
}

930 931 932 933 934 935 936
// This test verifies that the logic which matches server identifier in the
// received message with server identifiers used by a server works correctly:
// - a message with no server identifier is accepted,
// - a message with a server identifier which matches one of the server
// identifiers used by a server is accepted,
// - a message with a server identifier which doesn't match any server
// identifier used by a server, is not accepted.
937 938 939 940
TEST_F(Dhcpv4SrvTest, acceptServerId) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

941 942 943 944 945 946 947
    NakedDhcpv4Srv srv(0);

    Pkt4Ptr pkt(new Pkt4(DHCPREQUEST, 1234));
    // If no server identifier option is present, the message is always
    // accepted.
    EXPECT_TRUE(srv.acceptServerId(pkt));

948 949 950 951
    // Create definition of the server identifier option.
    OptionDefinition def("server-identifier", DHO_DHCP_SERVER_IDENTIFIER,
                         "ipv4-address", false);

952 953 954
    // Add a server identifier option which doesn't match server ids being
    // used by the server. The accepted server ids are the IPv4 addresses
    // configured on the interfaces. The 10.1.2.3 is not configured on
955 956 957
    // any interfaces.
    OptionCustomPtr other_serverid(new OptionCustom(def, Option::V6));
    other_serverid->writeAddress(IOAddress("10.1.2.3"));
958 959 960 961 962 963 964 965
    pkt->addOption(other_serverid);
    EXPECT_FALSE(srv.acceptServerId(pkt));

    // Remove the server identifier.
    ASSERT_NO_THROW(pkt->delOption(DHO_DHCP_SERVER_IDENTIFIER));

    // Add a server id being an IPv4 address configured on eth0 interface.
    // A DHCPv4 message holding this server identifier should be accepted.
966
    OptionCustomPtr eth0_serverid(new OptionCustom(def, Option::V6));
967
    eth0_serverid->writeAddress(IOAddress("192.0.2.3"));
968 969 970 971 972 973 974 975
    ASSERT_NO_THROW(pkt->addOption(eth0_serverid));
    EXPECT_TRUE(srv.acceptServerId(pkt));

    // Remove the server identifier.
    ASSERT_NO_THROW(pkt->delOption(DHO_DHCP_SERVER_IDENTIFIER));

    // Add a server id being an IPv4 address configured on eth1 interface.
    // A DHCPv4 message holding this server identifier should be accepted.
976 977
    OptionCustomPtr eth1_serverid(new OptionCustom(def, Option::V6));
    eth1_serverid->writeAddress(IOAddress("10.0.0.1"));
978 979 980 981 982
    ASSERT_NO_THROW(pkt->addOption(eth1_serverid));
    EXPECT_TRUE(srv.acceptServerId(pkt));

}

983 984 985 986 987
// @todo: Implement tests for rejecting renewals

// This test verifies if the sanityCheck() really checks options presence.
TEST_F(Dhcpv4SrvTest, sanityCheck) {
    boost::scoped_ptr<NakedDhcpv4Srv> srv;
988
    ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
989 990

    Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
991
    pkt->setHWAddr(generateHWAddr(6));
992

993
    // Server-id is optional for information-request, so
994
    EXPECT_NO_THROW(NakedDhcpv4Srv::sanityCheck(pkt, Dhcpv4Srv::OPTIONAL));
995

996
    // Empty packet, no server-id
997 998
    EXPECT_THROW(NakedDhcpv4Srv::sanityCheck(pkt, Dhcpv4Srv::MANDATORY),
                 RFCViolation);
999 1000 1001

    pkt->addOption(srv->getServerID());

1002
    // Server-id is mandatory and present = no exception
1003
    EXPECT_NO_THROW(NakedDhcpv4Srv::sanityCheck(pkt, Dhcpv4Srv::MANDATORY));
1004

1005
    // Server-id is forbidden, but present => exception
1006
    EXPECT_THROW(NakedDhcpv4Srv::sanityCheck(pkt, Dhcpv4Srv::FORBIDDEN),
1007
                 RFCViolation);
1008 1009 1010 1011

    // There's no client-id and no HWADDR. Server needs something to
    // identify the client
    pkt->setHWAddr(generateHWAddr(0));
1012 1013
    EXPECT_THROW(NakedDhcpv4Srv::sanityCheck(pkt, Dhcpv4Srv::MANDATORY),
                 RFCViolation);
1014 1015
}

1016
// Checks if received relay agent info option is echoed back to the client
1017 1018 1019
TEST_F(Dhcpv4SrvTest, relayAgentInfoEcho) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();
1020 1021 1022

    NakedDhcpv4Srv srv(0);

1023 1024 1025 1026
    // Use of the captured DHCPDISCOVER packet requires that
    // subnet 10.254.226.0/24 is in use, because this packet
    // contains the giaddr which belongs to this subnet and
    // this giaddr is used to select the subnet
1027
    configure(CONFIGS[0]);
1028

1029 1030 1031
    // Let's create a relayed DISCOVER. This particular relayed DISCOVER has
    // added option 82 (relay agent info) with 3 suboptions. The server
    // is supposed to echo it back in its response.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
1032
    Pkt4Ptr dis;
1033
    ASSERT_NO_THROW(dis = PktCaptures::captureRelayedDiscover());
Tomek Mrugalski's avatar