dhcp4_srv_unittest.cc 138 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
    "\"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\" "
        " } ],"
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
    "\"valid-lifetime\": 4000 }",

    // Configuration 2:
    // - 1 subnet, 2 global options (one forced with always-send)
    "{"
    "    \"interfaces-config\": {"
    "    \"interfaces\": [ \"*\" ] }, "
    "    \"rebind-timer\": 2000, "
    "    \"renew-timer\": 1000, "
    "    \"valid-lifetime\": 4000, "
    "    \"subnet4\": [ {"
    "        \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ], "
    "        \"subnet\": \"192.0.2.0/24\""
    "    } ], "
    "    \"option-data\": ["
    "        {"
    "            \"name\": \"default-ip-ttl\", "
    "            \"data\": \"FF\", "
    "            \"csv-format\": false"
    "        }, "
    "        {"
    "            \"name\": \"ip-forwarding\", "
    "            \"data\": \"false\", "
    "            \"always-send\": true"
    "        }"
    "    ]"
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
    "}",

    // Configuration 3:
    // - one subnet, with one pool
    // - user-contexts defined in both subnet and pool
    "{"
        "    \"subnet4\": [ { "
        "    \"pools\": [ { \"pool\": \"10.254.226.0/25\","
        "                   \"user-context\": { \"value\": 42 } } ],"
        "    \"subnet\": \"10.254.226.0/24\", "
        "    \"user-context\": {"
        "        \"secure\": false"
        "    }"
        " } ],"
    "\"valid-lifetime\": 4000 }",
};
138

139 140 141
// 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.
142 143 144 145
TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelay) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

146
    // Create the instance of the incoming packet.
147
    boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
148 149
    // Set the giaddr to non-zero address and hops to non-zero value
    // as if it was relayed.
150
    req->setGiaddr(IOAddress("192.0.1.1"));
151
    req->setHops(2);
152 153
    // Set ciaddr to zero. This simulates the client which applies
    // for the new lease.
154
    req->setCiaddr(IOAddress("0.0.0.0"));
155 156 157
    // Clear broadcast flag.
    req->setFlags(0x0000);

158
    // Set local address, port and interface.
159
    req->setLocalAddr(IOAddress("192.0.2.5"));
160
    req->setLocalPort(1001);
161
    req->setIface("eth1");
162 163
    req->setIndex(1);

164
    // Create the exchange using the req.
165
    Dhcpv4Exchange ex = createExchange(req);
166 167

    Pkt4Ptr resp = ex.getResponse();
168
    resp->setYiaddr(IOAddress("192.0.1.100"));
169 170
    // Clear the remote address.
    resp->setRemoteAddr(IOAddress("0.0.0.0"));
171 172
    // Set hops value for the response.
    resp->setHops(req->getHops());
173

174
    // This function never throws.
175
    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
176

177
    // Now the destination address should be relay's address.
178
    EXPECT_EQ("192.0.1.1", resp->getRemoteAddr().toText());
179 180
    // The query has been relayed, so the response must be sent to the port 67.
    EXPECT_EQ(DHCP4_SERVER_PORT, resp->getRemotePort());
181
    // Local address should be the address assigned to interface eth1.
182
    EXPECT_EQ("192.0.2.5", resp->getLocalAddr().toText());
183 184 185 186
    // 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.
187
    EXPECT_EQ("eth1", resp->getIface());
188
    EXPECT_EQ(1, resp->getIndex());
189 190 191 192

    // 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.
193 194
    req->setGiaddr(IOAddress("192.0.1.50"));
    req->setCiaddr(IOAddress("192.0.1.11"));
195
    req->setFlags(Pkt4::FLAG_BROADCAST_MASK);
196

197
    resp->setYiaddr(IOAddress("192.0.1.100"));
198 199 200
    // Clear remote address.
    resp->setRemoteAddr(IOAddress("0.0.0.0"));

201
    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
202 203

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

207 208 209 210
// 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.
211 212 213 214
TEST_F(Dhcpv4SrvTest, adjustIfaceDataRenew) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

215 216 217 218 219 220 221 222
    // 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).
223
    req->setCiaddr(IOAddress("192.0.1.15"));
224 225 226 227 228 229
    // 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.
230
    req->setFlags(Pkt4::FLAG_BROADCAST_MASK);
231 232 233
    // 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.
234
    req->setLocalAddr(IOAddress("192.0.2.1"));
235 236 237
    // 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.
238
    req->setIface("eth1");
239
    req->setIndex(1);
240

241
    // Create the exchange using the req.
242
    Dhcpv4Exchange ex = createExchange(req);
243 244
    Pkt4Ptr resp = ex.getResponse();

245 246 247 248
    // 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.
249
    resp->setYiaddr(IOAddress("192.0.1.13"));
250 251
    // Clear the remote address.
    resp->setRemoteAddr(IOAddress("0.0.0.0"));
252 253
    // Copy hops value from the query.
    resp->setHops(req->getHops());
254

255
    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
256 257

    // Check that server responds to ciaddr
258
    EXPECT_EQ("192.0.1.15", resp->getRemoteAddr().toText());
259 260 261 262 263
    // 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.
264
    EXPECT_EQ("192.0.2.1", resp->getLocalAddr().toText());
265 266 267
    // 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.
268
    EXPECT_EQ("eth1", resp->getIface());
269 270
    EXPECT_EQ(1, resp->getIndex());

271 272
}

273 274 275 276 277 278 279
// 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.
280 281 282 283
TEST_F(Dhcpv4SrvTest, adjustIfaceDataSelect) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

284 285 286 287 288 289 290 291 292 293 294
    // 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);

295 296 297 298 299 300 301
    // 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.
302
    req->setIface("eth1");
303 304
    req->setIndex(1);

305
    // Create the exchange using the req.
306
    Dhcpv4Exchange ex = createExchange(req);
307
    Pkt4Ptr resp = ex.getResponse();
308
    // Assign some new address for this client.
309
    resp->setYiaddr(IOAddress("192.0.1.13"));
310 311
    // Clear the remote address.
    resp->setRemoteAddr(IOAddress("0.0.0.0"));
312 313 314 315 316 317 318 319
    // 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.
320
    test_config.setDirectResponse(false);
321 322 323 324 325 326 327

    // 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.
328
    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
329 330 331 332 333

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

334 335 336
    // 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.
337
    EXPECT_EQ("192.0.2.3", resp->getLocalAddr().toText());
338 339 340 341 342 343

    // 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.
344
    EXPECT_EQ("eth1", resp->getIface());
345 346
    EXPECT_EQ(1, resp->getIndex());

347 348 349
    // 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
350 351 352
    // response based on the capability reported by IfaceMgr. We can
    // control whether the current packet filter returns that it supports
    // direct responses or not.
353
    test_config.setDirectResponse(true);
354 355 356

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

359
    EXPECT_EQ("192.0.1.13", resp->getRemoteAddr().toText());
360 361
}

362 363 364 365 366
// 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.
367 368 369 370
TEST_F(Dhcpv4SrvTest, adjustIfaceDataBroadcast) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

371 372 373 374 375 376 377
    // 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"));
378 379 380 381 382
    // 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.
383
    req->setIface("eth1");
384
    req->setIndex(1);
385 386

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

389
    // Create the exchange using the req.
390
    Dhcpv4Exchange ex = createExchange(req);
391 392
    Pkt4Ptr resp = ex.getResponse();

393
    // Assign some new address for this client.
394
    resp->setYiaddr(IOAddress("192.0.1.13"));
395 396 397 398

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

399
    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
400

Francis Dupont's avatar
Francis Dupont committed
401
    // Server must respond to broadcast address when client desired that
402 403
    // by setting the broadcast flag in its request.
    EXPECT_EQ("255.255.255.255", resp->getRemoteAddr().toText());
404 405 406 407

    // 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.
408
    EXPECT_EQ("192.0.2.3", resp->getLocalAddr().toText());
409 410 411 412 413 414

    // 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.
415
    EXPECT_EQ("eth1", resp->getIface());
416 417 418 419
    EXPECT_EQ(1, resp->getIndex());

}

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
// 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);
439
    query->setFlags(BOOTP_BROADCAST);
440 441 442 443 444

    // Add options which must be copied
    // client-id echo is optional
    // rai echo is done in relayAgentInfoEcho
    // Do subnet selection option
445
    OptionDefinitionPtr sbnsel_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE,
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
                                                           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());
464
    EXPECT_TRUE(hw == *response->getHWAddr());
465
    EXPECT_EQ(IOAddress("10.10.10.10"), response->getGiaddr());
466 467
    EXPECT_TRUE(src_hw == *response->getLocalHWAddr());
    EXPECT_TRUE(dst_hw == *response->getRemoteHWAddr());
468
    EXPECT_TRUE(BOOTP_BROADCAST == response->getFlags());
469 470 471 472 473

    // Check options (i.e., subnet selection option)
    OptionPtr resp_sbnsel = response->getOption(DHO_SUBNET_SELECTION);
    ASSERT_TRUE(resp_sbnsel);
    OptionCustomPtr resp_custom =
474
        boost::dynamic_pointer_cast<OptionCustom>(resp_sbnsel);
475 476 477 478 479 480
    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);
}

481 482 483
// 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) {
484
    Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 1234));
485
    Dhcpv4Exchange ex = createExchange(query);
486 487
    Pkt4Ptr response = ex.getResponse();

488 489 490 491 492
    // 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.
493
    ASSERT_NO_THROW(NakedDhcpv4Srv::appendServerID(ex));
494 495 496 497 498 499 500 501 502 503 504 505 506 507

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

510 511 512 513 514 515
// 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;
516 517
    ASSERT_NO_THROW(srv.reset(new Dhcpv4Srv(DHCP4_SERVER_PORT + 10000, false,
                                            false)));
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
    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)));
}

535
// Verifies that DISCOVER message can be processed correctly,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
536 537 538 539 540 541 542
// 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.
543
TEST_F(Dhcpv4SrvTest, processDiscover) {
544
    testDiscoverRequest(DHCPDISCOVER);
545
}
546

547
// Verifies that REQUEST message can be processed correctly,
548
// that the OFFER message generated in response is valid and
Tomek Mrugalski's avatar
Tomek Mrugalski committed
549 550 551 552
// contains necessary options.
//
// Note: this test focuses on the packet correctness. There
// are other tests that verify correctness of the allocation
553 554
// engine. See DiscoverBasic, DiscoverHint, DiscoverNoClientId
// and DiscoverInvalidHint.
555
TEST_F(Dhcpv4SrvTest, processRequest) {
556
    testDiscoverRequest(DHCPREQUEST);
557 558
}

559
TEST_F(Dhcpv4SrvTest, processRelease) {
560
    NakedDhcpv4Srv srv;
561
    Pkt4Ptr pkt(new Pkt4(DHCPRELEASE, 1234));
562

563
    // Should not throw
564
    EXPECT_NO_THROW(srv.processRelease(pkt));
565 566
}

567 568 569 570 571 572 573 574 575 576 577
// 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
578 579 580 581
TEST_F(Dhcpv4SrvTest, DiscoverBasic) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

582
    boost::scoped_ptr<NakedDhcpv4Srv> srv;
583
    ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
584 585 586 587 588

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

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

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

597
    // Check that address was returned from proper range, that its lease
598
    // lifetime is correct, that T1 and T2 are returned properly
599
    checkAddressParams(offer, subnet_, true, true);
600

601
    // Check identifiers
602 603 604 605
    checkServerId(offer, srv->getServerID());
    checkClientId(offer, clientid);
}

606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
// 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_);
628 629 630
    CfgMgr::instance().clear();
    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
    CfgMgr::instance().commit();
631 632 633 634 635 636 637 638 639 640 641 642 643 644

    // 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);
}
645 646 647 648 649 650 651 652 653 654 655 656 657

// 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)
658 659 660 661
TEST_F(Dhcpv4SrvTest, DiscoverInvalidHint) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

662
    boost::scoped_ptr<NakedDhcpv4Srv> srv;
663
    ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
664 665 666 667 668 669 670
    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);
671
    dis->setIface("eth1");
672

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

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

679
    // Check that address was returned from proper range, that its lease
680
    // lifetime is correct, that T1 and T2 are returned properly
681
    checkAddressParams(offer, subnet_, true, true);
682

683
    EXPECT_NE(offer->getYiaddr(), hint);
684

685
    // Check identifiers
686 687 688 689 690 691 692 693
    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
694 695
// 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
696
// 2 addresses, the third client would get the same offer as the first one
697
// and this is a correct behavior. It is REQUEST that will fail for the third
Tomek Mrugalski's avatar
Tomek Mrugalski committed
698
// client. OFFER is basically saying "if you send me a request, you will
699
// probably get an address like this" (there are no guarantees).
700 701 702 703
TEST_F(Dhcpv4SrvTest, ManyDiscovers) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

704
    boost::scoped_ptr<NakedDhcpv4Srv> srv;
705
    ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
706 707 708 709 710 711 712 713 714

    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"));

715
    // Assign interfaces
716 717 718
    dis1->setIface("eth1");
    dis2->setIface("eth1");
    dis3->setIface("eth1");
719

720
    // Different client-id sizes
721 722 723 724 725 726 727 728
    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);

729 730 731 732
    // Pass it to the server and get an offer
    Pkt4Ptr offer1 = srv->processDiscover(dis1);
    Pkt4Ptr offer2 = srv->processDiscover(dis2);
    Pkt4Ptr offer3 = srv->processDiscover(dis3);
733

734 735 736 737
    // Check if we get response at all
    checkResponse(offer1, DHCPOFFER, 1234);
    checkResponse(offer2, DHCPOFFER, 2345);
    checkResponse(offer3, DHCPOFFER, 3456);
738

739 740 741
    IOAddress addr1 = offer1->getYiaddr();
    IOAddress addr2 = offer2->getYiaddr();
    IOAddress addr3 = offer3->getYiaddr();
742 743

    // Check that the assigned address is indeed from the configured pool
744 745 746
    checkAddressParams(offer1, subnet_, true, true);
    checkAddressParams(offer2, subnet_, true, true);
    checkAddressParams(offer3, subnet_, true, true);
747

748
    // Check server-ids
749 750 751 752 753 754
    checkServerId(offer1, srv->getServerID());
    checkServerId(offer2, srv->getServerID());
    checkServerId(offer3, srv->getServerID());
    checkClientId(offer1, clientid1);
    checkClientId(offer2, clientid2);
    checkClientId(offer3, clientid3);
755 756

    // Finally check that the addresses offered are different
757 758 759 760 761 762
    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;
763 764
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
765 766 767
// Checks whether echoing back client-id is controllable, i.e.
// whether the server obeys echo-client-id and sends (or not)
// client-id
768 769 770 771
TEST_F(Dhcpv4SrvTest, discoverEchoClientId) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

772 773 774 775 776 777
    NakedDhcpv4Srv srv(0);

    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
    dis->setRemoteAddr(IOAddress("192.0.2.1"));
    OptionPtr clientid = generateClientId();
    dis->addOption(clientid);
778
    dis->setIface("eth1");
779 780 781 782 783 784 785 786

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

787 788 789 790 791 792 793
    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();
794

795 796 797 798 799 800 801
    offer = srv.processDiscover(dis);

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

802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823
// 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_);
824 825 826
    CfgMgr::instance().clear();
    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
    CfgMgr::instance().commit();
827 828 829 830 831 832 833 834 835 836 837 838 839 840 841

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

842
// Checks whether echoing back client-id is controllable
843 844 845 846
TEST_F(Dhcpv4SrvTest, requestEchoClientId) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

847 848 849 850 851 852
    NakedDhcpv4Srv srv(0);

    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
    dis->setRemoteAddr(IOAddress("192.0.2.1"));
    OptionPtr clientid = generateClientId();
    dis->addOption(clientid);
853
    dis->setIface("eth1");
854 855 856 857 858 859 860 861

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

862 863 864 865 866 867 868 869
    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();

870
    ack = srv.processRequest(dis);
871 872

    // Check if we get response at all
873
    checkResponse(ack, DHCPACK, 1234);
874 875 876
    checkClientId(ack, clientid);
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
877 878

// This test verifies that incoming (positive) REQUEST/Renewing can be handled properly, that a
879 880 881 882 883 884 885 886
// 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
887 888 889 890
TEST_F(Dhcpv4SrvTest, RenewBasic) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

891
    boost::scoped_ptr<NakedDhcpv4Srv> srv;
892
    ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
893 894

    const IOAddress addr("192.0.2.106");
895 896 897 898
    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;
899

900
    // Generate client-id also sets client_id_ member
901 902 903
    OptionPtr clientid = generateClientId();

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

    // let's create a lease and put it in the LeaseMgr
907 908 909
    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,
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929
                              &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
930
    req->setCiaddr(addr); // client's address
931
    req->setIface("eth0");
932
    req->setHWAddr(hwaddr2);
933 934 935 936 937 938 939 940 941

    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);
942
    EXPECT_EQ(addr, ack->getYiaddr());
943

944
    // Check that address was returned from proper range, that its lease
945
    // lifetime is correct, that T1 and T2 are returned properly
946
    checkAddressParams(ack, subnet_, true, true);
947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963

    // 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));
964
    // Equality or difference by 1 between cltt and expected is ok.
965 966 967 968 969
    EXPECT_GE(1, abs(cltt - expected));

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

970 971 972 973 974 975 976
// 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.
977 978 979 980
TEST_F(Dhcpv4SrvTest, acceptServerId) {
    IfaceMgrTestConfig test_config(true);
    IfaceMgr::instance().openSockets4();

981 982 983 984 985 986 987
    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));

988 989 990 991
    // Create definition of the server identifier option.
    OptionDefinition def("server-identifier", DHO_DHCP_SERVER_IDENTIFIER,
                         "ipv4-address", false);

992 993 994
    // 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
995 996 997
    // any interfaces.
    OptionCustomPtr other_serverid(new OptionCustom(def, Option::V6));
    other_serverid->writeAddress(IOAddress("10.1.2.3"));
998 999 1000 1001 1002 1003 1004 1005
    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.
1006
    OptionCustomPtr eth0_serverid(new OptionCustom(def, Option::V6));
1007
    eth0_serverid->writeAddress(IOAddress("192.0.2.3"));
1008 1009 1010 1011 1012 1013 1014 1015
    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.
1016 1017
    OptionCustomPtr eth1_serverid(new OptionCustom(def, Option::V6));
    eth1_serverid->writeAddress(IOAddress("10.0.0.1"));
1018 1019 1020