1 // Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <asiolink/io_address.h>
10 #include <cc/data.h>
11 #include <cc/command_interpreter.h>
12 #include <dhcp4/json_config_parser.h>
13 #include <dhcp4/tests/dhcp4_test_utils.h>
14 #include <dhcp/libdhcp++.h>
15 #include <dhcp/option4_addrlst.h>
16 #include <dhcp/option_int.h>
17 #include <dhcp/option_int_array.h>
18 #include <dhcp/option_custom.h>
19 #include <dhcp/iface_mgr.h>
20 #include <dhcp/tests/iface_mgr_test_config.h>
21 #include <dhcp/tests/pkt_captures.h>
22 #include <dhcpsrv/cfg_db_access.h>
23 #include <dhcpsrv/cfg_multi_threading.h>
24 #include <dhcpsrv/cfgmgr.h>
25 #include <dhcpsrv/lease.h>
26 #include <dhcpsrv/lease_mgr.h>
27 #include <dhcpsrv/lease_mgr_factory.h>
28 #include <log/logger_support.h>
29 #include <stats/stats_mgr.h>
30 #include <sstream>
31 
32 using namespace std;
33 using namespace isc::asiolink;
34 using namespace isc::data;
35 using namespace isc::util;
36 
37 namespace isc {
38 namespace dhcp {
39 namespace test {
40 
BaseServerTest()41 BaseServerTest::BaseServerTest()
42     : original_datadir_(CfgMgr::instance().getDataDir()) {
43     CfgMgr::instance().setDataDir(TEST_DATA_BUILDDIR);
44 }
45 
~BaseServerTest()46 BaseServerTest::~BaseServerTest() {
47     // Remove default lease file.
48     std::ostringstream s2;
49     s2 << CfgMgr::instance().getDataDir() << "/kea-leases4.csv";
50     static_cast<void>(::remove(s2.str().c_str()));
51 
52     // Revert to original data directory.
53     CfgMgr::instance().setDataDir(original_datadir_);
54 
55     // Revert to unit test logging, in case the test reconfigured it.
56     isc::log::initLogger();
57 }
58 
Dhcpv4SrvTest()59 Dhcpv4SrvTest::Dhcpv4SrvTest()
60     : rcode_(-1), srv_(0), multi_threading_(false) {
61 
62     // Wipe any existing statistics
63     isc::stats::StatsMgr::instance().removeAll();
64 
65     subnet_ = Subnet4Ptr(new Subnet4(IOAddress("192.0.2.0"), 24, 1000,
66                                      2000, 3000));
67     pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"), IOAddress("192.0.2.110")));
68     subnet_->addPool(pool_);
69 
70     // Add Router option.
71     Option4AddrLstPtr opt_routers(new Option4AddrLst(DHO_ROUTERS));
72     opt_routers->setAddress(IOAddress("192.0.2.2"));
73     subnet_->getCfgOption()->add(opt_routers, false, DHCP4_OPTION_SPACE);
74 
75     CfgMgr::instance().clear();
76     CfgMgr::instance().setFamily(AF_INET);
77     CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
78     CfgMgr::instance().commit();
79 
80     LibDHCP::clearRuntimeOptionDefs();
81 
82     // Let's wipe all existing statistics.
83     isc::stats::StatsMgr::instance().removeAll();
84 
85     // Reset the thread pool.
86     MultiThreadingMgr::instance().apply(false, 0, 0);
87 }
88 
~Dhcpv4SrvTest()89 Dhcpv4SrvTest::~Dhcpv4SrvTest() {
90 
91     // Make sure that we revert to default value
92     CfgMgr::instance().clear();
93 
94     LibDHCP::clearRuntimeOptionDefs();
95 
96     // Let's wipe all existing statistics.
97     isc::stats::StatsMgr::instance().removeAll();
98 
99     // Reset the thread pool.
100     MultiThreadingMgr::instance().apply(false, 0, 0);
101 }
102 
addPrlOption(Pkt4Ptr & pkt)103 void Dhcpv4SrvTest::addPrlOption(Pkt4Ptr& pkt) {
104 
105     OptionUint8ArrayPtr option_prl =
106         OptionUint8ArrayPtr(new OptionUint8Array(Option::V4,
107                                                  DHO_DHCP_PARAMETER_REQUEST_LIST));
108 
109     // Let's request options that have been configured for the subnet.
110     option_prl->addValue(DHO_DOMAIN_NAME_SERVERS);
111     option_prl->addValue(DHO_DOMAIN_NAME);
112     option_prl->addValue(DHO_LOG_SERVERS);
113     option_prl->addValue(DHO_COOKIE_SERVERS);
114     // Let's also request the option that hasn't been configured. In such
115     // case server should ignore request for this particular option.
116     option_prl->addValue(DHO_LPR_SERVERS);
117     // And add 'Parameter Request List' option into the DISCOVER packet.
118     pkt->addOption(option_prl);
119 }
120 
121 void
configureRequestedOptions()122 Dhcpv4SrvTest::configureRequestedOptions() {
123     // dns-servers
124     Option4AddrLstPtr
125         option_dns_servers(new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS));
126     option_dns_servers->addAddress(IOAddress("192.0.2.1"));
127     option_dns_servers->addAddress(IOAddress("192.0.2.100"));
128     ASSERT_NO_THROW(subnet_->getCfgOption()->add(option_dns_servers, false, DHCP4_OPTION_SPACE));
129 
130     // domain-name
131     OptionDefinition def("domain-name", DHO_DOMAIN_NAME, DHCP4_OPTION_SPACE,
132                          OPT_FQDN_TYPE);
133     OptionCustomPtr option_domain_name(new OptionCustom(def, Option::V4));
134     option_domain_name->writeFqdn("example.com");
135     subnet_->getCfgOption()->add(option_domain_name, false, DHCP4_OPTION_SPACE);
136 
137     // log-servers
138     Option4AddrLstPtr option_log_servers(new Option4AddrLst(DHO_LOG_SERVERS));
139     option_log_servers->addAddress(IOAddress("192.0.2.2"));
140     option_log_servers->addAddress(IOAddress("192.0.2.10"));
141     ASSERT_NO_THROW(subnet_->getCfgOption()->add(option_log_servers, false, DHCP4_OPTION_SPACE));
142 
143     // cookie-servers
144     Option4AddrLstPtr option_cookie_servers(new Option4AddrLst(DHO_COOKIE_SERVERS));
145     option_cookie_servers->addAddress(IOAddress("192.0.2.1"));
146     ASSERT_NO_THROW(subnet_->getCfgOption()->add(option_cookie_servers, false, DHCP4_OPTION_SPACE));
147 }
148 
149 void
configureServerIdentifier()150 Dhcpv4SrvTest::configureServerIdentifier() {
151     CfgMgr& cfg_mgr = CfgMgr::instance();
152     CfgSubnets4Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4();
153 
154     // Build and add subnet2.
155     Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.0"), 24, 1200, 2400, 3600, 2));
156     Pool4Ptr pool(new Pool4(IOAddress("192.0.2.100"), IOAddress("192.0.2.200")));
157     // Add server identifier to the pool.
158     OptionCustomPtr server_id = makeServerIdOption(IOAddress("192.0.2.254"));
159     CfgOptionPtr cfg_option = pool->getCfgOption();
160     cfg_option->add(server_id, false, DHCP4_OPTION_SPACE);
161     subnet2->addPool(pool);
162 
163     // Add a second pool.
164     pool.reset(new Pool4(IOAddress("192.0.2.201"), IOAddress("192.0.2.220")));
165     subnet2->addPool(pool);
166 
167     subnets->add(subnet2);
168 
169     // Build and add subnet3.
170     Triplet<uint32_t> unspec;
171     Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.3.0"), 24, unspec, unspec, 3600, 3));
172     pool.reset(new Pool4(IOAddress("192.0.3.100"), IOAddress("192.0.3.200")));
173     subnet3->addPool(pool);
174     subnet3->setT1Percent(0.5);
175     subnet3->setT2Percent(0.75);
176     subnet3->setCalculateTeeTimes(true);
177 
178     // Add server identifier.
179     server_id = makeServerIdOption(IOAddress("192.0.3.254"));
180     cfg_option = subnet3->getCfgOption();
181     cfg_option->add(server_id, false, DHCP4_OPTION_SPACE);
182 
183     subnets->add(subnet3);
184 
185     // Build and add subnet4.
186     Subnet4Ptr subnet4(new Subnet4(IOAddress("192.0.4.0"), 24, unspec, unspec, 3600, 4));
187     pool.reset(new Pool4(IOAddress("192.0.4.100"), IOAddress("192.0.4.200")));
188     subnet4->addPool(pool);
189     subnet4->setCalculateTeeTimes(false);
190 
191     subnets->add(subnet4);
192 
193     // Build and add subnet5.
194     Subnet4Ptr subnet5(new Subnet4(IOAddress("192.0.5.0"), 24, unspec, unspec, 3600, 5));
195     pool.reset(new Pool4(IOAddress("192.0.5.100"), IOAddress("192.0.5.200")));
196     subnet5->addPool(pool);
197     subnet5->setCalculateTeeTimes(false);
198 
199     subnets->add(subnet5);
200 
201     CfgOptionPtr options(new CfgOption());
202     OptionDescriptor desc(false);
203     desc.option_ = makeServerIdOption(IOAddress("192.0.5.254"));
204     options->add(desc, DHCP4_OPTION_SPACE);
205     CfgMgr::instance().getStagingCfg()->getClientClassDictionary()->addClass("foo", ExpressionPtr(), "", true, false, options);
206     subnet5->requireClientClass("foo");
207 
208     // Build and add subnet6.
209     Subnet4Ptr subnet6(new Subnet4(IOAddress("192.0.6.0"), 24, unspec, unspec, 3600, 6));
210     pool.reset(new Pool4(IOAddress("192.0.6.100"), IOAddress("192.0.6.200")));
211     subnet6->addPool(pool);
212     subnet6->setCalculateTeeTimes(false);
213 
214     subnets->add(subnet6);
215 
216     options.reset(new CfgOption());
217     OptionDescriptor desc_other(false);
218     desc_other.option_ = makeFqdnListOption();
219     options->add(desc_other, DHCP4_OPTION_SPACE);
220     CfgMgr::instance().getStagingCfg()->getClientClassDictionary()->addClass("bar", ExpressionPtr(), "", true, false, options);
221     subnet6->requireClientClass("bar");
222 
223     // Build and add subnet7.
224     Subnet4Ptr subnet7(new Subnet4(IOAddress("192.0.7.0"), 24, unspec, unspec, 3600, 7));
225     pool.reset(new Pool4(IOAddress("192.0.7.100"), IOAddress("192.0.7.200")));
226     subnet7->addPool(pool);
227     subnet7->setCalculateTeeTimes(false);
228 
229     subnets->add(subnet7);
230 
231     options.reset();
232     CfgMgr::instance().getStagingCfg()->getClientClassDictionary()->addClass("xyz", ExpressionPtr(), "", true, false, options);
233     subnet7->requireClientClass("xyz");
234 
235     // Build and add a shared-network.
236     CfgSharedNetworks4Ptr networks = cfg_mgr.getStagingCfg()->getCfgSharedNetworks4();
237     SharedNetwork4Ptr network1(new SharedNetwork4("one"));
238     network1->add(subnet4);
239 
240     // Add server identifier.
241     server_id = makeServerIdOption(IOAddress("192.0.4.254"));
242     cfg_option = network1->getCfgOption();
243     cfg_option->add(server_id, false, DHCP4_OPTION_SPACE);
244 
245     networks->add(network1);
246 
247     // Add a global server identifier.
248     cfg_option = cfg_mgr.getStagingCfg()->getCfgOption();
249     server_id = makeServerIdOption(IOAddress("10.0.0.254"));
250     cfg_option->add(server_id, false, DHCP4_OPTION_SPACE);
251 
252     // Commit the config.
253     cfg_mgr.commit();
254 }
255 
256 void
messageCheck(const Pkt4Ptr & q,const Pkt4Ptr & a)257 Dhcpv4SrvTest::messageCheck(const Pkt4Ptr& q, const Pkt4Ptr& a) {
258     ASSERT_TRUE(q);
259     ASSERT_TRUE(a);
260 
261     EXPECT_EQ(q->getHops(),   a->getHops());
262     EXPECT_EQ(q->getIface(),  a->getIface());
263     EXPECT_EQ(q->getIndex(),  a->getIndex());
264     EXPECT_EQ(q->getGiaddr(), a->getGiaddr());
265     // When processing an incoming packet the remote address
266     // is copied as a src address, and the source address is
267     // copied as a remote address to the response.
268     EXPECT_TRUE(q->getLocalHWAddr() == a->getLocalHWAddr());
269     EXPECT_TRUE(q->getRemoteHWAddr() == a->getRemoteHWAddr());
270 
271     // Check that the server identifier is present in the response.
272     // Presence (or absence) of other options is checked elsewhere.
273     EXPECT_TRUE(a->getOption(DHO_DHCP_SERVER_IDENTIFIER));
274 
275     // Check that something is offered
276     EXPECT_NE("0.0.0.0", a->getYiaddr().toText());
277 }
278 
279 ::testing::AssertionResult
basicOptionsPresent(const Pkt4Ptr & pkt)280 Dhcpv4SrvTest::basicOptionsPresent(const Pkt4Ptr& pkt) {
281     std::ostringstream errmsg;
282     errmsg << "option missing in the response";
283     if (!pkt->getOption(DHO_DOMAIN_NAME)) {
284         return (::testing::AssertionFailure(::testing::Message()
285                                             << "domain-name " << errmsg.str()));
286 
287     } else if (!pkt->getOption(DHO_DOMAIN_NAME_SERVERS)) {
288         return (::testing::AssertionFailure(::testing::Message()
289                                             << "dns-servers " << errmsg.str()));
290 
291     } else if (!pkt->getOption(DHO_SUBNET_MASK)) {
292         return (::testing::AssertionFailure(::testing::Message()
293                                             << "subnet-mask " << errmsg.str()));
294 
295     } else if (!pkt->getOption(DHO_ROUTERS)) {
296         return (::testing::AssertionFailure(::testing::Message() << "routers "
297                                             << errmsg.str()));
298 
299     } else if (!pkt->getOption(DHO_DHCP_LEASE_TIME)) {
300         return (::testing::AssertionFailure(::testing::Message() <<
301                                             "dhcp-lease-time " << errmsg.str()));
302 
303     }
304     return (::testing::AssertionSuccess());
305 
306 }
307 
308 ::testing::AssertionResult
noBasicOptions(const Pkt4Ptr & pkt)309 Dhcpv4SrvTest::noBasicOptions(const Pkt4Ptr& pkt) {
310     std::ostringstream errmsg;
311     errmsg << "option present in the response";
312     if (pkt->getOption(DHO_DOMAIN_NAME)) {
313         return (::testing::AssertionFailure(::testing::Message()
314                                             << "domain-name " << errmsg.str()));
315 
316     } else if (pkt->getOption(DHO_DOMAIN_NAME_SERVERS)) {
317         return (::testing::AssertionFailure(::testing::Message()
318                                             << "dns-servers " << errmsg.str()));
319 
320     } else if (pkt->getOption(DHO_SUBNET_MASK)) {
321         return (::testing::AssertionFailure(::testing::Message()
322                                             << "subnet-mask " << errmsg.str()));
323 
324     } else if (pkt->getOption(DHO_ROUTERS)) {
325         return (::testing::AssertionFailure(::testing::Message() << "routers "
326                                             << errmsg.str()));
327 
328     } else if (pkt->getOption(DHO_DHCP_LEASE_TIME)) {
329         return (::testing::AssertionFailure(::testing::Message()
330                                             << "dhcp-lease-time " << errmsg.str()));
331 
332     }
333     return (::testing::AssertionSuccess());
334 }
335 
336 ::testing::AssertionResult
requestedOptionsPresent(const Pkt4Ptr & pkt)337 Dhcpv4SrvTest::requestedOptionsPresent(const Pkt4Ptr& pkt) {
338     std::ostringstream errmsg;
339     errmsg << "option missing in the response";
340     if (!pkt->getOption(DHO_LOG_SERVERS)) {
341         return (::testing::AssertionFailure(::testing::Message()
342                                             << "log-servers " << errmsg.str()));
343 
344     } else if (!pkt->getOption(DHO_COOKIE_SERVERS)) {
345         return (::testing::AssertionFailure(::testing::Message()
346                                             << "cookie-servers " << errmsg.str()));
347 
348     }
349     return (::testing::AssertionSuccess());
350 }
351 
352 ::testing::AssertionResult
noRequestedOptions(const Pkt4Ptr & pkt)353 Dhcpv4SrvTest::noRequestedOptions(const Pkt4Ptr& pkt) {
354     std::ostringstream errmsg;
355     errmsg << "option present in the response";
356     if (pkt->getOption(DHO_LOG_SERVERS)) {
357         return (::testing::AssertionFailure(::testing::Message()
358                                             << "log-servers " << errmsg.str()));
359 
360     } else if (pkt->getOption(DHO_COOKIE_SERVERS)) {
361         return (::testing::AssertionFailure(::testing::Message()
362                                             << "cookie-servers " << errmsg.str()));
363 
364     }
365     return (::testing::AssertionSuccess());
366 }
367 
368 OptionPtr
generateClientId(size_t size)369 Dhcpv4SrvTest::generateClientId(size_t size /*= 4*/) {
370 
371     OptionBuffer clnt_id(size);
372     for (size_t i = 0; i < size; i++) {
373         clnt_id[i] = 100 + i;
374     }
375 
376     client_id_ = ClientIdPtr(new ClientId(clnt_id));
377 
378     return (OptionPtr(new Option(Option::V4, DHO_DHCP_CLIENT_IDENTIFIER,
379                                  clnt_id.begin(),
380                                  clnt_id.begin() + size)));
381 }
382 
383 HWAddrPtr
generateHWAddr(size_t size)384 Dhcpv4SrvTest::generateHWAddr(size_t size /*= 6*/) {
385     const uint8_t hw_type = 123; // Just a fake number (typically 6=HTYPE_ETHER, see dhcp4.h)
386     OptionBuffer mac(size);
387     for (size_t i = 0; i < size; ++i) {
388         mac[i] = 50 + i;
389     }
390     return (HWAddrPtr(new HWAddr(mac, hw_type)));
391 }
392 
393 OptionCustomPtr
makeServerIdOption(const IOAddress & address)394 Dhcpv4SrvTest::makeServerIdOption(const IOAddress& address) {
395     OptionDefinitionPtr option_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE,
396                                                            DHO_DHCP_SERVER_IDENTIFIER);
397     OptionCustomPtr server_id(new OptionCustom(*option_def, Option::V4));
398     server_id->writeAddress(address);
399     return (server_id);
400 }
401 
402 OptionPtr
makeFqdnListOption()403 Dhcpv4SrvTest::makeFqdnListOption() {
404     OptionDefinitionPtr def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE,
405                                                     DHO_DOMAIN_SEARCH);
406 
407     // Prepare buffer holding an array of FQDNs.
408     const uint8_t fqdn[] = {
409         8, 109, 121, 100, 111, 109, 97, 105, 110, // "mydomain"
410         7, 101, 120, 97, 109, 112, 108, 101,      // "example"
411         3, 99, 111, 109,                          // "com"
412         0
413     };
414 
415     // Initialize a vector with the FQDN data.
416     std::vector<uint8_t> fqdn_buf(fqdn, fqdn + sizeof(fqdn));
417 
418     OptionPtr option = def->optionFactory(Option::V4, DHO_DOMAIN_SEARCH,
419                                           fqdn_buf.begin(), fqdn_buf.end());
420 
421     return (option);
422 }
423 
424 void
checkAddressParams(const Pkt4Ptr & rsp,const Subnet4Ptr subnet,bool t1_present,bool t2_present,uint32_t expected_valid)425 Dhcpv4SrvTest::checkAddressParams(const Pkt4Ptr& rsp,
426                                   const Subnet4Ptr subnet,
427                                   bool t1_present,
428                                   bool t2_present,
429                                   uint32_t expected_valid) {
430 
431     // Technically inPool implies inRange, but let's be on the safe
432     // side and check both.
433     EXPECT_TRUE(subnet->inRange(rsp->getYiaddr()));
434     EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, rsp->getYiaddr()));
435 
436     // Check lease time
437     OptionUint32Ptr opt = boost::dynamic_pointer_cast<
438         OptionUint32>(rsp->getOption(DHO_DHCP_LEASE_TIME));
439     if (!opt) {
440         ADD_FAILURE() << "Lease time option missing in response or the"
441             " option has unexpected type";
442     } else if (subnet->getValid().getMin() != subnet->getValid().getMax()) {
443         EXPECT_GE(opt->getValue(), subnet->getValid().getMin());
444         EXPECT_LE(opt->getValue(), subnet->getValid().getMax());
445     } else {
446         EXPECT_EQ(opt->getValue(), subnet->getValid());
447     }
448 
449     // Check expected value when wanted.
450     if (opt && expected_valid) {
451       EXPECT_EQ(opt->getValue(), expected_valid);
452     }
453 
454     // Check T1 timer
455     opt = boost::dynamic_pointer_cast<
456         OptionUint32>(rsp->getOption(DHO_DHCP_RENEWAL_TIME));
457     if (t1_present) {
458         ASSERT_TRUE(opt) << "Required T1 option missing or it has"
459             " an unexpected type";
460         EXPECT_EQ(opt->getValue(), subnet->getT1());
461     } else {
462         EXPECT_FALSE(opt);
463     }
464 
465     // Check T2 timer
466     opt = boost::dynamic_pointer_cast<
467         OptionUint32>(rsp->getOption(DHO_DHCP_REBINDING_TIME));
468     if (t2_present) {
469         ASSERT_TRUE(opt) << "Required T2 option missing or it has"
470             " an unexpected type";
471         EXPECT_EQ(opt->getValue(), subnet->getT2());
472     } else {
473         EXPECT_FALSE(opt);
474     }
475 }
476 
477 void
checkResponse(const Pkt4Ptr & rsp,int expected_message_type,uint32_t expected_transid)478 Dhcpv4SrvTest::checkResponse(const Pkt4Ptr& rsp, int expected_message_type,
479                              uint32_t expected_transid) {
480     ASSERT_TRUE(rsp);
481     EXPECT_EQ(expected_message_type,
482               static_cast<int>(rsp->getType()));
483     EXPECT_EQ(expected_transid, rsp->getTransid());
484 }
485 
486 Lease4Ptr
checkLease(const Pkt4Ptr & rsp,const OptionPtr & client_id,const HWAddrPtr &,const IOAddress & expected_addr)487 Dhcpv4SrvTest::checkLease(const Pkt4Ptr& rsp, const OptionPtr& client_id,
488                           const HWAddrPtr&, const IOAddress& expected_addr) {
489 
490     ClientIdPtr id;
491     if (client_id) {
492         OptionBuffer data = client_id->getData();
493         id.reset(new ClientId(data));
494     }
495 
496     Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(expected_addr);
497     if (!lease) {
498         cout << "Lease for " << expected_addr
499              << " not found in the database backend.";
500         return (Lease4Ptr());
501     }
502 
503     EXPECT_EQ(rsp->getYiaddr(), expected_addr);
504 
505     EXPECT_EQ(expected_addr, lease->addr_);
506     if (client_id) {
507         EXPECT_TRUE(*lease->client_id_ == *id);
508     }
509     EXPECT_EQ(subnet_->getID(), lease->subnet_id_);
510 
511     return (lease);
512 }
513 
514 void
checkServerId(const Pkt4Ptr & rsp,const OptionPtr & expected_srvid)515 Dhcpv4SrvTest::checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid) {
516     // Check that server included its server-id
517     OptionPtr opt = rsp->getOption(DHO_DHCP_SERVER_IDENTIFIER);
518     ASSERT_TRUE(opt);
519     EXPECT_EQ(opt->getType(), expected_srvid->getType() );
520     EXPECT_EQ(opt->len(), expected_srvid->len() );
521     EXPECT_TRUE(opt->getData() == expected_srvid->getData());
522 }
523 
524 void
checkClientId(const Pkt4Ptr & rsp,const OptionPtr & expected_clientid)525 Dhcpv4SrvTest::checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid) {
526 
527     bool include_clientid =
528         CfgMgr::instance().getCurrentCfg()->getEchoClientId();
529 
530     // check that server included our own client-id
531     OptionPtr opt = rsp->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
532     if (include_clientid) {
533         // Normal mode: echo back (see RFC6842)
534         ASSERT_TRUE(opt);
535         EXPECT_EQ(expected_clientid->getType(), opt->getType());
536         EXPECT_EQ(expected_clientid->len(), opt->len());
537         EXPECT_TRUE(expected_clientid->getData() == opt->getData());
538     } else {
539         // Backward compatibility mode for pre-RFC6842 devices
540         ASSERT_FALSE(opt);
541     }
542 }
543 
544 void
checkServerIdOption(const Pkt4Ptr & packet,const IOAddress & expected_address)545 Dhcpv4SrvTest::checkServerIdOption(const Pkt4Ptr& packet, const IOAddress& expected_address) {
546     OptionPtr opt = packet->getOption(DHO_DHCP_SERVER_IDENTIFIER);
547     ASSERT_TRUE(opt) << "no server-id option";
548 
549     OptionCustomPtr server_id_opt = boost::dynamic_pointer_cast<OptionCustom>(opt);
550     ASSERT_TRUE(server_id_opt) << "server-id option is not an instance of OptionCustom";
551 
552     EXPECT_EQ(expected_address, server_id_opt->readAddress());
553 }
554 
555 ::testing::AssertionResult
createPacketFromBuffer(const Pkt4Ptr & src_pkt,Pkt4Ptr & dst_pkt)556 Dhcpv4SrvTest::createPacketFromBuffer(const Pkt4Ptr& src_pkt,
557                                       Pkt4Ptr& dst_pkt) {
558     // Create on-wire format of the packet. If pack() has been called
559     // on this instance of the packet already, the next call to pack()
560     // should remove all contents of the output buffer.
561     try {
562         src_pkt->pack();
563     } catch (const Exception& ex) {
564         return (::testing::AssertionFailure(::testing::Message()
565                                             << "Failed to parse source packet: "
566                                             << ex.what()));
567     }
568     // Get the output buffer from the source packet.
569     const util::OutputBuffer& buf = src_pkt->getBuffer();
570     // Create a copy of the packet using the output buffer from the source
571     // packet.
572     try {
573         dst_pkt.reset(new Pkt4(static_cast<const uint8_t*>(buf.getData()),
574                                buf.getLength()));
575     } catch (const Exception& ex) {
576         return (::testing::AssertionFailure(::testing::Message()
577                                             << "Failed to create a"
578                                             " destination packet from"
579                                             " the buffer: "
580                                             << ex.what()));
581     }
582 
583     try {
584         // Parse the new packet and return to the caller.
585         dst_pkt->unpack();
586     } catch (const Exception& ex) {
587         return (::testing::AssertionFailure(::testing::Message()
588                                             << "Failed to parse a"
589                                             << " destination packet: "
590                                             << ex.what()));
591     }
592 
593     return (::testing::AssertionSuccess());
594 }
595 
596 void
597 // cppcheck-suppress unusedFunction
TearDown()598 Dhcpv4SrvTest::TearDown() {
599 
600     CfgMgr::instance().clear();
601 
602     // Close all open sockets.
603     IfaceMgr::instance().closeSockets();
604 
605     // Some unit tests override the default packet filtering class, used
606     // by the IfaceMgr. The dummy class, called PktFilterTest, reports the
607     // capability to directly respond to the clients without IP address
608     // assigned. This capability is not supported by the default packet
609     // filtering class: PktFilterInet. Therefore setting the dummy class
610     // allows to test scenarios, when server responds to the broadcast address
611     // on client's request, despite having support for direct response.
612     // The following call restores the use of original packet filtering class
613     // after the test.
614     try {
615         IfaceMgr::instance().setPacketFilter(PktFilterPtr(new PktFilterInet()));
616 
617     } catch (const Exception& ex) {
618         FAIL() << "Failed to restore the default (PktFilterInet) packet filtering"
619                << " class after the test. Exception has been caught: "
620                << ex.what();
621     }
622 }
623 
624 void
testDiscoverRequest(const uint8_t msg_type)625 Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
626     IfaceMgrTestConfig test_config(true);
627     IfaceMgr::instance().openSockets4();
628 
629     // Create an instance of the tested class.
630     boost::scoped_ptr<NakedDhcpv4Srv> srv(new NakedDhcpv4Srv(0));
631 
632     // Initialize the source HW address.
633     vector<uint8_t> mac(6);
634     for (uint8_t i = 0; i < 6; ++i) {
635         mac[i] = i * 10;
636     }
637     // Initialized the destination HW address.
638     vector<uint8_t> dst_mac(6);
639     for (uint8_t i = 0; i < 6; ++i) {
640         dst_mac[i] = i * 20;
641     }
642     // Create a DHCP message. It will be used to simulate the
643     // incoming message.
644     boost::shared_ptr<Pkt4> req(new Pkt4(msg_type, 1234));
645     // Create a response message. It will hold a response packet.
646     // Initially, set it to NULL.
647     boost::shared_ptr<Pkt4> rsp;
648     // Set the name of the interface on which packet is received.
649     req->setIface("eth0");
650     // Set the interface index.
651     req->setIndex(ETH0_INDEX);
652     // Set the target HW address. This value is normally used to
653     // construct the data link layer header.
654     req->setRemoteHWAddr(1, 6, dst_mac);
655     // Set the HW address. This value is set on DHCP level (in chaddr).
656     req->setHWAddr(1, 6, mac);
657     // Set local HW address. It is used to construct the data link layer
658     // header.
659     req->setLocalHWAddr(1, 6, mac);
660     // Set target IP address.
661     req->setRemoteAddr(IOAddress("192.0.2.55"));
662     // Set relay address and hops.
663     req->setGiaddr(IOAddress("192.0.2.10"));
664     req->setHops(1);
665 
666     // We are going to test that certain options are returned
667     // in the response message when requested using 'Parameter
668     // Request List' option. Let's configure those options that
669     // are returned when requested.
670     configureRequestedOptions();
671 
672     // Create a copy of the original packet by parsing its wire format.
673     // This simulates the real life scenario when we process the packet
674     // which was parsed from its wire format.
675     Pkt4Ptr received;
676     ASSERT_TRUE(createPacketFromBuffer(req, received));
677     // Set interface. It is required for the server to generate server id.
678     received->setIface("eth0");
679     received->setIndex(ETH0_INDEX);
680     if (msg_type == DHCPDISCOVER) {
681         ASSERT_NO_THROW(rsp = srv->processDiscover(received));
682 
683         // Should return OFFER
684         ASSERT_TRUE(rsp);
685         EXPECT_EQ(DHCPOFFER, rsp->getType());
686 
687     } else {
688         ASSERT_NO_THROW(rsp = srv->processRequest(received));
689 
690         // Should return ACK
691         ASSERT_TRUE(rsp);
692         EXPECT_EQ(DHCPACK, rsp->getType());
693 
694     }
695 
696     messageCheck(received, rsp);
697 
698     // Basic options should be present when we got the lease.
699     EXPECT_TRUE(basicOptionsPresent(rsp));
700     // We did not request any options so these should not be present
701     // in the RSP.
702     EXPECT_TRUE(noRequestedOptions(rsp));
703 
704     // Repeat the test but request some options.
705     // Add 'Parameter Request List' option.
706     addPrlOption(req);
707 
708     ASSERT_TRUE(createPacketFromBuffer(req, received));
709     ASSERT_TRUE(received->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
710 
711     // Set interface. It is required for the server to generate server id.
712     received->setIface("eth0");
713     received->setIndex(ETH0_INDEX);
714 
715     if (msg_type == DHCPDISCOVER) {
716         ASSERT_NO_THROW(rsp = srv->processDiscover(received));
717 
718         // Should return non-NULL packet.
719         ASSERT_TRUE(rsp);
720         EXPECT_EQ(DHCPOFFER, rsp->getType());
721 
722     } else {
723         ASSERT_NO_THROW(rsp = srv->processRequest(received));
724 
725         // Should return non-NULL packet.
726         ASSERT_TRUE(rsp);
727         EXPECT_EQ(DHCPACK, rsp->getType());
728     }
729 
730     // Check that the requested options are returned.
731     EXPECT_TRUE(basicOptionsPresent(rsp));
732     EXPECT_TRUE(requestedOptionsPresent(rsp));
733 
734     // The following part of the test will test that the NAK is sent when
735     // there is no address pool configured. In the same time, we expect
736     // that the requested options are not included in NAK message, but that
737     // they are only included when yiaddr is set to non-zero value.
738     ASSERT_NO_THROW(subnet_->delPools(Lease::TYPE_V4));
739 
740     // There has been a lease allocated for the particular client. So,
741     // even though we deleted the subnet, the client would get the
742     // existing lease (not a NAK). Therefore, we have to change the chaddr
743     // in the packet so as the existing lease is not returned.
744     req->setHWAddr(1, 6, std::vector<uint8_t>(2, 6));
745     ASSERT_TRUE(createPacketFromBuffer(req, received));
746     ASSERT_TRUE(received->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
747 
748     // Set interface. It is required for the server to generate server id.
749     received->setIface("eth0");
750     received->setIndex(ETH0_INDEX);
751 
752     if (msg_type == DHCPDISCOVER) {
753         ASSERT_NO_THROW(rsp = srv->processDiscover(received));
754         // Should return NULL packet.
755         ASSERT_FALSE(rsp);
756 
757     } else {
758         ASSERT_NO_THROW(rsp = srv->processRequest(received));
759         // Should return non-NULL packet.
760         ASSERT_TRUE(rsp);
761         // We should get the NAK packet with yiaddr set to 0.
762         EXPECT_EQ(DHCPNAK, rsp->getType());
763         ASSERT_EQ("0.0.0.0", rsp->getYiaddr().toText());
764 
765         // Make sure that none of the requested options is returned in NAK.
766         // Also options such as Routers or Subnet Mask should not be there,
767         // because lease hasn't been acquired.
768         EXPECT_TRUE(noRequestedOptions(rsp));
769         EXPECT_TRUE(noBasicOptions(rsp));
770     }
771 }
772 
773 void
buildCfgOptionTest(IOAddress expected_server_id,Pkt4Ptr & query,IOAddress requested,IOAddress server_id)774 Dhcpv4SrvTest::buildCfgOptionTest(IOAddress expected_server_id,
775                                   Pkt4Ptr& query,
776                                   IOAddress requested,
777                                   IOAddress server_id) {
778     OptionDefinitionPtr req_addr_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE,
779                                                              DHO_DHCP_REQUESTED_ADDRESS);
780     ASSERT_TRUE(req_addr_def);
781 
782     OptionDefinitionPtr sbnsel_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE,
783                                                            DHO_SUBNET_SELECTION);
784     ASSERT_TRUE(sbnsel_def);
785 
786     OptionCustomPtr req_addr(new OptionCustom(*req_addr_def, Option::V4));
787     req_addr->writeAddress(requested);
788 
789     OptionCustomPtr sbnsel(new OptionCustom(*sbnsel_def, Option::V4));
790     sbnsel->writeAddress(requested);
791 
792     query->addOption(req_addr);
793     query->addOption(sbnsel);
794     query->addOption(makeServerIdOption(server_id));
795 
796     Pkt4Ptr response;
797     ASSERT_NO_THROW(response = srv_.processRequest(query));
798 
799     checkServerIdOption(response, expected_server_id);
800 
801     ASSERT_NO_THROW(query->delOption(DHO_DHCP_REQUESTED_ADDRESS));
802     ASSERT_NO_THROW(query->delOption(DHO_SUBNET_SELECTION));
803     ASSERT_NO_THROW(query->delOption(DHO_DHCP_SERVER_IDENTIFIER));
804 }
805 
806 void
configure(const std::string & config,const bool commit,const bool open_sockets)807 Dhcpv4SrvTest::configure(const std::string& config,
808                          const bool commit,
809                          const bool open_sockets) {
810     configure(config, srv_, commit, open_sockets);
811 }
812 
813 void
configure(const std::string & config,NakedDhcpv4Srv & srv,const bool commit,const bool open_sockets)814 Dhcpv4SrvTest::configure(const std::string& config,
815                          NakedDhcpv4Srv& srv,
816                          const bool commit,
817                          const bool open_sockets) {
818     setenv("KEA_LFC_EXECUTABLE", KEA_LFC_EXECUTABLE, 1);
819     MultiThreadingCriticalSection cs;
820     ConstElementPtr json;
821     try {
822         json = parseJSON(config);
823     } catch (const std::exception& ex){
824         // Fatal failure on parsing error
825         FAIL() << "parsing failure:"
826                 << "config:" << config << std::endl
827                 << "error: " << ex.what();
828     }
829 
830     ConstElementPtr status;
831 
832     // Disable the re-detect flag
833     disableIfacesReDetect(json);
834 
835     // Set up multi-threading
836     configureMultiThreading(multi_threading_, json);
837 
838     // Configure the server and make sure the config is accepted
839     EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
840     ASSERT_TRUE(status);
841     int rcode;
842     ConstElementPtr comment = config::parseAnswer(rcode, status);
843     ASSERT_EQ(0, rcode) << comment->stringValue();
844 
845     // Use specified lease database backend.
846     ASSERT_NO_THROW( {
847         CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
848         cfg_db->setAppendedParameters("universe=4");
849         cfg_db->createManagers();
850     } );
851 
852     try {
853         CfgMultiThreading::apply(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading());
854     } catch (const std::exception& ex) {
855         ADD_FAILURE() << "Error applying multi threading settings: "
856             << ex.what();
857     }
858 
859     if (commit) {
860         CfgMgr::instance().commit();
861     }
862 
863     // Opening sockets.
864     if (open_sockets) {
865         IfaceMgr::instance().openSockets4();
866     }
867 }
868 
869 std::pair<int, std::string>
configureWithStatus(const std::string & config,NakedDhcpv4Srv & srv,const bool commit,const int exp_rcode)870 Dhcpv4SrvTest::configureWithStatus(const std::string& config, NakedDhcpv4Srv& srv,
871                                    const bool commit, const int exp_rcode) {
872     ConstElementPtr json;
873     try {
874         json = parseJSON(config);
875     } catch (const std::exception& ex){
876         // Fatal failure on parsing error
877 
878         std::stringstream tmp;
879         tmp << "parsing failure:"
880             << "config:" << config << std::endl
881             << "error: " << ex.what();
882         ADD_FAILURE() << tmp.str();
883         return (std::make_pair(-1, tmp.str()));
884     }
885 
886     ConstElementPtr status;
887 
888     // Disable the re-detect flag
889     disableIfacesReDetect(json);
890 
891     // Configure the server and make sure the config is accepted
892     EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
893     EXPECT_TRUE(status);
894     if (!status) {
895         return (make_pair(-1, "configureDhcp4Server didn't return anything"));
896     }
897 
898     int rcode;
899     ConstElementPtr comment = config::parseAnswer(rcode, status);
900     EXPECT_EQ(exp_rcode, rcode) << comment->stringValue();
901 
902     // Use specified lease database backend.
903     if (rcode == 0) {
904         EXPECT_NO_THROW( {
905             CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
906             cfg_db->setAppendedParameters("universe=4");
907             cfg_db->createManagers();
908             } );
909         if (commit) {
910             CfgMgr::instance().commit();
911         }
912     }
913 
914     return (std::make_pair(rcode, comment->stringValue()));
915 }
916 
917 Dhcpv4Exchange
createExchange(const Pkt4Ptr & query)918 Dhcpv4SrvTest::createExchange(const Pkt4Ptr& query) {
919     bool drop = false;
920     Subnet4Ptr subnet = srv_.selectSubnet(query, drop);
921     EXPECT_FALSE(drop);
922     Dhcpv4Exchange ex(srv_.alloc_engine_, query, subnet, drop);
923     EXPECT_FALSE(drop);
924     return (ex);
925 }
926 
927 void
pretendReceivingPkt(NakedDhcpv4Srv & srv,const std::string & config,uint8_t pkt_type,const std::string & stat_name)928 Dhcpv4SrvTest::pretendReceivingPkt(NakedDhcpv4Srv& srv, const std::string& config,
929                                    uint8_t pkt_type, const std::string& stat_name) {
930 
931     IfaceMgrTestConfig test_config(true);
932     IfaceMgr::instance().openSockets4();
933 
934     // Apply the configuration we just received.
935     configure(config);
936 
937     // Let's just use one of the actual captured packets that we have.
938     Pkt4Ptr pkt = PktCaptures::captureRelayedDiscover();
939 
940     // We just need to tweak it a it, to pretend that its type is as desired.
941     // Note that when receiving a packet, its on-wire form is stored in data_
942     // field. Most methods (including setType()) operates on option objects
943     // (objects stored in options_ after unpack() is called). Finally, outgoing
944     // packets are stored in out_buffer_. So we need to go through the full
945     // unpack/tweak/pack cycle and do repack, i.e. move the output buffer back
946     // to incoming buffer.
947     pkt->unpack();
948     pkt->setType(pkt_type); // Set message type.
949     pkt->pack();
950     pkt->data_.resize(pkt->getBuffer().getLength());
951     // Copy out_buffer_ to data_ to pretend that it's what was just received.
952     memcpy(&pkt->data_[0], pkt->getBuffer().getData(), pkt->getBuffer().getLength());
953 
954     // Simulate that we have received that traffic
955     srv.fakeReceive(pkt);
956     srv.run();
957 
958     using namespace isc::stats;
959     StatsMgr& mgr = StatsMgr::instance();
960     ObservationPtr pkt4_rcvd = mgr.getObservation("pkt4-received");
961     ObservationPtr tested_stat = mgr.getObservation(stat_name);
962 
963     // All expected statistics must be present.
964     ASSERT_TRUE(pkt4_rcvd);
965     ASSERT_TRUE(tested_stat);
966 
967     // They also must have expected values.
968     EXPECT_EQ(1, pkt4_rcvd->getInteger().first);
969     EXPECT_EQ(1, tested_stat->getInteger().first);
970 }
971 
972 } // end of isc::dhcp::test namespace
973 } // end of isc::dhcp namespace
974 } // end of isc namespace
975