dhcp4_test_utils.cc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. // Copyright (C) 2013-2017 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. #include <config.h>
  7. #include <asiolink/io_address.h>
  8. #include <cc/data.h>
  9. #include <cc/command_interpreter.h>
  10. #include <dhcp4/json_config_parser.h>
  11. #include <dhcp4/tests/dhcp4_test_utils.h>
  12. #include <dhcp/libdhcp++.h>
  13. #include <dhcp/option4_addrlst.h>
  14. #include <dhcp/option_int.h>
  15. #include <dhcp/option_int_array.h>
  16. #include <dhcp/option_custom.h>
  17. #include <dhcp/iface_mgr.h>
  18. #include <dhcp/tests/iface_mgr_test_config.h>
  19. #include <dhcp/tests/pkt_captures.h>
  20. #include <dhcpsrv/cfg_db_access.h>
  21. #include <dhcpsrv/cfgmgr.h>
  22. #include <dhcpsrv/lease.h>
  23. #include <dhcpsrv/lease_mgr.h>
  24. #include <dhcpsrv/lease_mgr_factory.h>
  25. #include <log/logger_support.h>
  26. #include <stats/stats_mgr.h>
  27. using namespace std;
  28. using namespace isc::asiolink;
  29. using namespace isc::data;
  30. namespace isc {
  31. namespace dhcp {
  32. namespace test {
  33. BaseServerTest::BaseServerTest()
  34. : original_datadir_(CfgMgr::instance().getDataDir()) {
  35. CfgMgr::instance().setDataDir(TEST_DATA_BUILDDIR);
  36. }
  37. BaseServerTest::~BaseServerTest() {
  38. // Remove default lease file.
  39. std::ostringstream s2;
  40. s2 << CfgMgr::instance().getDataDir() << "/" << "kea-leases4.csv";
  41. static_cast<void>(::remove(s2.str().c_str()));
  42. // Revert to original data directory.
  43. CfgMgr::instance().setDataDir(original_datadir_);
  44. // Revert to unit test logging, in case the test reconfigured it.
  45. isc::log::initLogger();
  46. }
  47. Dhcpv4SrvTest::Dhcpv4SrvTest()
  48. :rcode_(-1), srv_(0) {
  49. // Wipe any existing statistics
  50. isc::stats::StatsMgr::instance().removeAll();
  51. subnet_ = Subnet4Ptr(new Subnet4(IOAddress("192.0.2.0"), 24, 1000,
  52. 2000, 3000));
  53. pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"), IOAddress("192.0.2.110")));
  54. subnet_->addPool(pool_);
  55. // Add Router option.
  56. Option4AddrLstPtr opt_routers(new Option4AddrLst(DHO_ROUTERS));
  57. opt_routers->setAddress(IOAddress("192.0.2.2"));
  58. subnet_->getCfgOption()->add(opt_routers, false, DHCP4_OPTION_SPACE);
  59. CfgMgr::instance().clear();
  60. CfgMgr::instance().setFamily(AF_INET);
  61. CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
  62. CfgMgr::instance().commit();
  63. LibDHCP::clearRuntimeOptionDefs();
  64. // Let's wipe all existing statistics.
  65. isc::stats::StatsMgr::instance().removeAll();
  66. }
  67. Dhcpv4SrvTest::~Dhcpv4SrvTest() {
  68. // Make sure that we revert to default value
  69. CfgMgr::instance().clear();
  70. LibDHCP::clearRuntimeOptionDefs();
  71. // Let's wipe all existing statistics.
  72. isc::stats::StatsMgr::instance().removeAll();
  73. }
  74. void Dhcpv4SrvTest::addPrlOption(Pkt4Ptr& pkt) {
  75. OptionUint8ArrayPtr option_prl =
  76. OptionUint8ArrayPtr(new OptionUint8Array(Option::V4,
  77. DHO_DHCP_PARAMETER_REQUEST_LIST));
  78. // Let's request options that have been configured for the subnet.
  79. option_prl->addValue(DHO_DOMAIN_NAME_SERVERS);
  80. option_prl->addValue(DHO_DOMAIN_NAME);
  81. option_prl->addValue(DHO_LOG_SERVERS);
  82. option_prl->addValue(DHO_COOKIE_SERVERS);
  83. // Let's also request the option that hasn't been configured. In such
  84. // case server should ignore request for this particular option.
  85. option_prl->addValue(DHO_LPR_SERVERS);
  86. // And add 'Parameter Request List' option into the DISCOVER packet.
  87. pkt->addOption(option_prl);
  88. }
  89. void Dhcpv4SrvTest::configureRequestedOptions() {
  90. // dns-servers
  91. Option4AddrLstPtr
  92. option_dns_servers(new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS));
  93. option_dns_servers->addAddress(IOAddress("192.0.2.1"));
  94. option_dns_servers->addAddress(IOAddress("192.0.2.100"));
  95. ASSERT_NO_THROW(subnet_->getCfgOption()->add(option_dns_servers, false, DHCP4_OPTION_SPACE));
  96. // domain-name
  97. OptionDefinition def("domain-name", DHO_DOMAIN_NAME, OPT_FQDN_TYPE);
  98. OptionCustomPtr option_domain_name(new OptionCustom(def, Option::V4));
  99. option_domain_name->writeFqdn("example.com");
  100. subnet_->getCfgOption()->add(option_domain_name, false, DHCP4_OPTION_SPACE);
  101. // log-servers
  102. Option4AddrLstPtr option_log_servers(new Option4AddrLst(DHO_LOG_SERVERS));
  103. option_log_servers->addAddress(IOAddress("192.0.2.2"));
  104. option_log_servers->addAddress(IOAddress("192.0.2.10"));
  105. ASSERT_NO_THROW(subnet_->getCfgOption()->add(option_log_servers, false, DHCP4_OPTION_SPACE));
  106. // cookie-servers
  107. Option4AddrLstPtr option_cookie_servers(new Option4AddrLst(DHO_COOKIE_SERVERS));
  108. option_cookie_servers->addAddress(IOAddress("192.0.2.1"));
  109. ASSERT_NO_THROW(subnet_->getCfgOption()->add(option_cookie_servers, false, DHCP4_OPTION_SPACE));
  110. }
  111. void Dhcpv4SrvTest::messageCheck(const Pkt4Ptr& q, const Pkt4Ptr& a) {
  112. ASSERT_TRUE(q);
  113. ASSERT_TRUE(a);
  114. EXPECT_EQ(q->getHops(), a->getHops());
  115. EXPECT_EQ(q->getIface(), a->getIface());
  116. EXPECT_EQ(q->getIndex(), a->getIndex());
  117. EXPECT_EQ(q->getGiaddr(), a->getGiaddr());
  118. // When processing an incoming packet the remote address
  119. // is copied as a src address, and the source address is
  120. // copied as a remote address to the response.
  121. EXPECT_TRUE(q->getLocalHWAddr() == a->getLocalHWAddr());
  122. EXPECT_TRUE(q->getRemoteHWAddr() == a->getRemoteHWAddr());
  123. // Check that the server identifier is present in the response.
  124. // Presence (or absence) of other options is checked elsewhere.
  125. EXPECT_TRUE(a->getOption(DHO_DHCP_SERVER_IDENTIFIER));
  126. // Check that something is offered
  127. EXPECT_NE("0.0.0.0", a->getYiaddr().toText());
  128. }
  129. ::testing::AssertionResult
  130. Dhcpv4SrvTest::basicOptionsPresent(const Pkt4Ptr& pkt) {
  131. std::ostringstream errmsg;
  132. errmsg << "option missing in the response";
  133. if (!pkt->getOption(DHO_DOMAIN_NAME)) {
  134. return (::testing::AssertionFailure(::testing::Message()
  135. << "domain-name " << errmsg.str()));
  136. } else if (!pkt->getOption(DHO_DOMAIN_NAME_SERVERS)) {
  137. return (::testing::AssertionFailure(::testing::Message()
  138. << "dns-servers " << errmsg.str()));
  139. } else if (!pkt->getOption(DHO_SUBNET_MASK)) {
  140. return (::testing::AssertionFailure(::testing::Message()
  141. << "subnet-mask " << errmsg.str()));
  142. } else if (!pkt->getOption(DHO_ROUTERS)) {
  143. return (::testing::AssertionFailure(::testing::Message() << "routers "
  144. << errmsg.str()));
  145. } else if (!pkt->getOption(DHO_DHCP_LEASE_TIME)) {
  146. return (::testing::AssertionFailure(::testing::Message() <<
  147. "dhcp-lease-time " << errmsg.str()));
  148. }
  149. return (::testing::AssertionSuccess());
  150. }
  151. ::testing::AssertionResult
  152. Dhcpv4SrvTest::noBasicOptions(const Pkt4Ptr& pkt) {
  153. std::ostringstream errmsg;
  154. errmsg << "option present in the response";
  155. if (pkt->getOption(DHO_DOMAIN_NAME)) {
  156. return (::testing::AssertionFailure(::testing::Message()
  157. << "domain-name " << errmsg.str()));
  158. } else if (pkt->getOption(DHO_DOMAIN_NAME_SERVERS)) {
  159. return (::testing::AssertionFailure(::testing::Message()
  160. << "dns-servers " << errmsg.str()));
  161. } else if (pkt->getOption(DHO_SUBNET_MASK)) {
  162. return (::testing::AssertionFailure(::testing::Message()
  163. << "subnet-mask " << errmsg.str()));
  164. } else if (pkt->getOption(DHO_ROUTERS)) {
  165. return (::testing::AssertionFailure(::testing::Message() << "routers "
  166. << errmsg.str()));
  167. } else if (pkt->getOption(DHO_DHCP_LEASE_TIME)) {
  168. return (::testing::AssertionFailure(::testing::Message()
  169. << "dhcp-lease-time " << errmsg.str()));
  170. }
  171. return (::testing::AssertionSuccess());
  172. }
  173. ::testing::AssertionResult
  174. Dhcpv4SrvTest::requestedOptionsPresent(const Pkt4Ptr& pkt) {
  175. std::ostringstream errmsg;
  176. errmsg << "option missing in the response";
  177. if (!pkt->getOption(DHO_LOG_SERVERS)) {
  178. return (::testing::AssertionFailure(::testing::Message()
  179. << "log-servers " << errmsg.str()));
  180. } else if (!pkt->getOption(DHO_COOKIE_SERVERS)) {
  181. return (::testing::AssertionFailure(::testing::Message()
  182. << "cookie-servers " << errmsg.str()));
  183. }
  184. return (::testing::AssertionSuccess());
  185. }
  186. ::testing::AssertionResult
  187. Dhcpv4SrvTest::noRequestedOptions(const Pkt4Ptr& pkt) {
  188. std::ostringstream errmsg;
  189. errmsg << "option present in the response";
  190. if (pkt->getOption(DHO_LOG_SERVERS)) {
  191. return (::testing::AssertionFailure(::testing::Message()
  192. << "log-servers " << errmsg.str()));
  193. } else if (pkt->getOption(DHO_COOKIE_SERVERS)) {
  194. return (::testing::AssertionFailure(::testing::Message()
  195. << "cookie-servers " << errmsg.str()));
  196. }
  197. return (::testing::AssertionSuccess());
  198. }
  199. OptionPtr Dhcpv4SrvTest::generateClientId(size_t size /*= 4*/) {
  200. OptionBuffer clnt_id(size);
  201. for (size_t i = 0; i < size; i++) {
  202. clnt_id[i] = 100 + i;
  203. }
  204. client_id_ = ClientIdPtr(new ClientId(clnt_id));
  205. return (OptionPtr(new Option(Option::V4, DHO_DHCP_CLIENT_IDENTIFIER,
  206. clnt_id.begin(),
  207. clnt_id.begin() + size)));
  208. }
  209. HWAddrPtr Dhcpv4SrvTest::generateHWAddr(size_t size /*= 6*/) {
  210. const uint8_t hw_type = 123; // Just a fake number (typically 6=HTYPE_ETHER, see dhcp4.h)
  211. OptionBuffer mac(size);
  212. for (size_t i = 0; i < size; ++i) {
  213. mac[i] = 50 + i;
  214. }
  215. return (HWAddrPtr(new HWAddr(mac, hw_type)));
  216. }
  217. void Dhcpv4SrvTest::checkAddressParams(const Pkt4Ptr& rsp,
  218. const Subnet4Ptr subnet,
  219. bool t1_present,
  220. bool t2_present) {
  221. // Technically inPool implies inRange, but let's be on the safe
  222. // side and check both.
  223. EXPECT_TRUE(subnet->inRange(rsp->getYiaddr()));
  224. EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, rsp->getYiaddr()));
  225. // Check lease time
  226. OptionUint32Ptr opt = boost::dynamic_pointer_cast<
  227. OptionUint32>(rsp->getOption(DHO_DHCP_LEASE_TIME));
  228. if (!opt) {
  229. ADD_FAILURE() << "Lease time option missing in response or the"
  230. " option has unexpected type";
  231. } else {
  232. EXPECT_EQ(opt->getValue(), subnet->getValid());
  233. }
  234. // Check T1 timer
  235. opt = boost::dynamic_pointer_cast<
  236. OptionUint32>(rsp->getOption(DHO_DHCP_RENEWAL_TIME));
  237. if (t1_present) {
  238. ASSERT_TRUE(opt) << "Required T1 option missing or it has"
  239. " an unexpected type";
  240. EXPECT_EQ(opt->getValue(), subnet->getT1());
  241. } else {
  242. EXPECT_FALSE(opt);
  243. }
  244. // Check T2 timer
  245. opt = boost::dynamic_pointer_cast<
  246. OptionUint32>(rsp->getOption(DHO_DHCP_REBINDING_TIME));
  247. if (t2_present) {
  248. ASSERT_TRUE(opt) << "Required T2 option missing or it has"
  249. " an unexpected type";
  250. EXPECT_EQ(opt->getValue(), subnet->getT2());
  251. } else {
  252. EXPECT_FALSE(opt);
  253. }
  254. }
  255. void Dhcpv4SrvTest::checkResponse(const Pkt4Ptr& rsp, int expected_message_type,
  256. uint32_t expected_transid) {
  257. ASSERT_TRUE(rsp);
  258. EXPECT_EQ(expected_message_type,
  259. static_cast<int>(rsp->getType()));
  260. EXPECT_EQ(expected_transid, rsp->getTransid());
  261. }
  262. Lease4Ptr Dhcpv4SrvTest::checkLease(const Pkt4Ptr& rsp,
  263. const OptionPtr& client_id,
  264. const HWAddrPtr&,
  265. const IOAddress& expected_addr) {
  266. ClientIdPtr id;
  267. if (client_id) {
  268. OptionBuffer data = client_id->getData();
  269. id.reset(new ClientId(data));
  270. }
  271. Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(expected_addr);
  272. if (!lease) {
  273. cout << "Lease for " << expected_addr
  274. << " not found in the database backend.";
  275. return (Lease4Ptr());
  276. }
  277. EXPECT_EQ(rsp->getYiaddr(), expected_addr);
  278. EXPECT_EQ(expected_addr, lease->addr_);
  279. if (client_id) {
  280. EXPECT_TRUE(*lease->client_id_ == *id);
  281. }
  282. EXPECT_EQ(subnet_->getID(), lease->subnet_id_);
  283. return (lease);
  284. }
  285. void Dhcpv4SrvTest::checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid) {
  286. // Check that server included its server-id
  287. OptionPtr opt = rsp->getOption(DHO_DHCP_SERVER_IDENTIFIER);
  288. ASSERT_TRUE(opt);
  289. EXPECT_EQ(opt->getType(), expected_srvid->getType() );
  290. EXPECT_EQ(opt->len(), expected_srvid->len() );
  291. EXPECT_TRUE(opt->getData() == expected_srvid->getData());
  292. }
  293. void Dhcpv4SrvTest::checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid) {
  294. bool include_clientid =
  295. CfgMgr::instance().getCurrentCfg()->getEchoClientId();
  296. // check that server included our own client-id
  297. OptionPtr opt = rsp->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
  298. if (include_clientid) {
  299. // Normal mode: echo back (see RFC6842)
  300. ASSERT_TRUE(opt);
  301. EXPECT_EQ(expected_clientid->getType(), opt->getType());
  302. EXPECT_EQ(expected_clientid->len(), opt->len());
  303. EXPECT_TRUE(expected_clientid->getData() == opt->getData());
  304. } else {
  305. // Backward compatibility mode for pre-RFC6842 devices
  306. ASSERT_FALSE(opt);
  307. }
  308. }
  309. ::testing::AssertionResult
  310. Dhcpv4SrvTest::createPacketFromBuffer(const Pkt4Ptr& src_pkt,
  311. Pkt4Ptr& dst_pkt) {
  312. // Create on-wire format of the packet. If pack() has been called
  313. // on this instance of the packet already, the next call to pack()
  314. // should remove all contents of the output buffer.
  315. try {
  316. src_pkt->pack();
  317. } catch (const Exception& ex) {
  318. return (::testing::AssertionFailure(::testing::Message()
  319. << "Failed to parse source packet: "
  320. << ex.what()));
  321. }
  322. // Get the output buffer from the source packet.
  323. const util::OutputBuffer& buf = src_pkt->getBuffer();
  324. // Create a copy of the packet using the output buffer from the source
  325. // packet.
  326. try {
  327. dst_pkt.reset(new Pkt4(static_cast<const uint8_t*>(buf.getData()),
  328. buf.getLength()));
  329. } catch (const Exception& ex) {
  330. return (::testing::AssertionFailure(::testing::Message()
  331. << "Failed to create a"
  332. " destination packet from"
  333. " the buffer: "
  334. << ex.what()));
  335. }
  336. try {
  337. // Parse the new packet and return to the caller.
  338. dst_pkt->unpack();
  339. } catch (const Exception& ex) {
  340. return (::testing::AssertionFailure(::testing::Message()
  341. << "Failed to parse a"
  342. << " destination packet: "
  343. << ex.what()));
  344. }
  345. return (::testing::AssertionSuccess());
  346. }
  347. void
  348. // cppcheck-suppress unusedFunction
  349. Dhcpv4SrvTest::TearDown() {
  350. CfgMgr::instance().clear();
  351. // Close all open sockets.
  352. IfaceMgr::instance().closeSockets();
  353. // Some unit tests override the default packet filtering class, used
  354. // by the IfaceMgr. The dummy class, called PktFilterTest, reports the
  355. // capability to directly respond to the clients without IP address
  356. // assigned. This capability is not supported by the default packet
  357. // filtering class: PktFilterInet. Therefore setting the dummy class
  358. // allows to test scenarios, when server responds to the broadcast address
  359. // on client's request, despite having support for direct response.
  360. // The following call restores the use of original packet filtering class
  361. // after the test.
  362. try {
  363. IfaceMgr::instance().setPacketFilter(PktFilterPtr(new PktFilterInet()));
  364. } catch (const Exception& ex) {
  365. FAIL() << "Failed to restore the default (PktFilterInet) packet filtering"
  366. << " class after the test. Exception has been caught: "
  367. << ex.what();
  368. }
  369. }
  370. void
  371. Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
  372. IfaceMgrTestConfig test_config(true);
  373. IfaceMgr::instance().openSockets4();
  374. // Create an instance of the tested class.
  375. boost::scoped_ptr<NakedDhcpv4Srv> srv(new NakedDhcpv4Srv(0));
  376. // Initialize the source HW address.
  377. vector<uint8_t> mac(6);
  378. for (uint8_t i = 0; i < 6; ++i) {
  379. mac[i] = i * 10;
  380. }
  381. // Initialized the destination HW address.
  382. vector<uint8_t> dst_mac(6);
  383. for (uint8_t i = 0; i < 6; ++i) {
  384. dst_mac[i] = i * 20;
  385. }
  386. // Create a DHCP message. It will be used to simulate the
  387. // incoming message.
  388. boost::shared_ptr<Pkt4> req(new Pkt4(msg_type, 1234));
  389. // Create a response message. It will hold a response packet.
  390. // Initially, set it to NULL.
  391. boost::shared_ptr<Pkt4> rsp;
  392. // Set the name of the interface on which packet is received.
  393. req->setIface("eth0");
  394. // Set the interface index. It is just a dummy value and will
  395. // not be interpreted.
  396. req->setIndex(17);
  397. // Set the target HW address. This value is normally used to
  398. // construct the data link layer header.
  399. req->setRemoteHWAddr(1, 6, dst_mac);
  400. // Set the HW address. This value is set on DHCP level (in chaddr).
  401. req->setHWAddr(1, 6, mac);
  402. // Set local HW address. It is used to construct the data link layer
  403. // header.
  404. req->setLocalHWAddr(1, 6, mac);
  405. // Set target IP address.
  406. req->setRemoteAddr(IOAddress("192.0.2.55"));
  407. // Set relay address and hops.
  408. req->setGiaddr(IOAddress("192.0.2.10"));
  409. req->setHops(1);
  410. // We are going to test that certain options are returned
  411. // in the response message when requested using 'Parameter
  412. // Request List' option. Let's configure those options that
  413. // are returned when requested.
  414. configureRequestedOptions();
  415. // Create a copy of the original packet by parsing its wire format.
  416. // This simulates the real life scenario when we process the packet
  417. // which was parsed from its wire format.
  418. Pkt4Ptr received;
  419. ASSERT_TRUE(createPacketFromBuffer(req, received));
  420. // Set interface. It is required for the server to generate server id.
  421. received->setIface("eth0");
  422. if (msg_type == DHCPDISCOVER) {
  423. ASSERT_NO_THROW(
  424. rsp = srv->processDiscover(received);
  425. );
  426. // Should return OFFER
  427. ASSERT_TRUE(rsp);
  428. EXPECT_EQ(DHCPOFFER, rsp->getType());
  429. } else {
  430. ASSERT_NO_THROW(rsp = srv->processRequest(received));
  431. // Should return ACK
  432. ASSERT_TRUE(rsp);
  433. EXPECT_EQ(DHCPACK, rsp->getType());
  434. }
  435. messageCheck(received, rsp);
  436. // Basic options should be present when we got the lease.
  437. EXPECT_TRUE(basicOptionsPresent(rsp));
  438. // We did not request any options so these should not be present
  439. // in the RSP.
  440. EXPECT_TRUE(noRequestedOptions(rsp));
  441. // Repeat the test but request some options.
  442. // Add 'Parameter Request List' option.
  443. addPrlOption(req);
  444. ASSERT_TRUE(createPacketFromBuffer(req, received));
  445. ASSERT_TRUE(received->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
  446. // Set interface. It is required for the server to generate server id.
  447. received->setIface("eth0");
  448. if (msg_type == DHCPDISCOVER) {
  449. ASSERT_NO_THROW(rsp = srv->processDiscover(received));
  450. // Should return non-NULL packet.
  451. ASSERT_TRUE(rsp);
  452. EXPECT_EQ(DHCPOFFER, rsp->getType());
  453. } else {
  454. ASSERT_NO_THROW(rsp = srv->processRequest(received));
  455. // Should return non-NULL packet.
  456. ASSERT_TRUE(rsp);
  457. EXPECT_EQ(DHCPACK, rsp->getType());
  458. }
  459. // Check that the requested options are returned.
  460. EXPECT_TRUE(basicOptionsPresent(rsp));
  461. EXPECT_TRUE(requestedOptionsPresent(rsp));
  462. // The following part of the test will test that the NAK is sent when
  463. // there is no address pool configured. In the same time, we expect
  464. // that the requested options are not included in NAK message, but that
  465. // they are only included when yiaddr is set to non-zero value.
  466. ASSERT_NO_THROW(subnet_->delPools(Lease::TYPE_V4));
  467. // There has been a lease allocated for the particular client. So,
  468. // even though we deleted the subnet, the client would get the
  469. // existing lease (not a NAK). Therefore, we have to change the chaddr
  470. // in the packet so as the existing lease is not returned.
  471. req->setHWAddr(1, 6, std::vector<uint8_t>(2, 6));
  472. ASSERT_TRUE(createPacketFromBuffer(req, received));
  473. ASSERT_TRUE(received->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
  474. // Set interface. It is required for the server to generate server id.
  475. received->setIface("eth0");
  476. if (msg_type == DHCPDISCOVER) {
  477. ASSERT_NO_THROW(rsp = srv->processDiscover(received));
  478. // Should return NULL packet.
  479. ASSERT_FALSE(rsp);
  480. } else {
  481. ASSERT_NO_THROW(rsp = srv->processRequest(received));
  482. // Should return non-NULL packet.
  483. ASSERT_TRUE(rsp);
  484. // We should get the NAK packet with yiaddr set to 0.
  485. EXPECT_EQ(DHCPNAK, rsp->getType());
  486. ASSERT_EQ("0.0.0.0", rsp->getYiaddr().toText());
  487. // Make sure that none of the requested options is returned in NAK.
  488. // Also options such as Routers or Subnet Mask should not be there,
  489. // because lease hasn't been acquired.
  490. EXPECT_TRUE(noRequestedOptions(rsp));
  491. EXPECT_TRUE(noBasicOptions(rsp));
  492. }
  493. }
  494. void
  495. Dhcpv4SrvTest::configure(const std::string& config, const bool commit) {
  496. configure(config, srv_, commit);
  497. }
  498. void
  499. Dhcpv4SrvTest::configure(const std::string& config, NakedDhcpv4Srv& srv,
  500. const bool commit) {
  501. ConstElementPtr json;
  502. ASSERT_NO_THROW(json = parseJSON(config));
  503. ConstElementPtr status;
  504. // Disable the re-detect flag
  505. disableIfacesReDetect(json);
  506. // Configure the server and make sure the config is accepted
  507. EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
  508. ASSERT_TRUE(status);
  509. int rcode;
  510. ConstElementPtr comment = config::parseAnswer(rcode, status);
  511. ASSERT_EQ(0, rcode);
  512. // Use specified lease database backend.
  513. ASSERT_NO_THROW( {
  514. CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
  515. cfg_db->setAppendedParameters("universe=4");
  516. cfg_db->createManagers();
  517. } );
  518. if (commit) {
  519. CfgMgr::instance().commit();
  520. }
  521. }
  522. Dhcpv4Exchange
  523. Dhcpv4SrvTest::createExchange(const Pkt4Ptr& query) {
  524. return (Dhcpv4Exchange(srv_.alloc_engine_, query, srv_.selectSubnet(query)));
  525. }
  526. void
  527. Dhcpv4SrvTest::pretendReceivingPkt(NakedDhcpv4Srv& srv, const std::string& config,
  528. uint8_t pkt_type, const std::string& stat_name) {
  529. IfaceMgrTestConfig test_config(true);
  530. IfaceMgr::instance().openSockets4();
  531. // Apply the configuration we just received.
  532. configure(config);
  533. // Let's just use one of the actual captured packets that we have.
  534. Pkt4Ptr pkt = PktCaptures::captureRelayedDiscover();
  535. // We just need to tweak it a it, to pretend that it's type is as desired.
  536. // Note that when receiving a packet, its on-wire form is stored in data_
  537. // field. Most methods (including setType()) operates on option objects
  538. // (objects stored in options_ after unpack() is called). Finally, outgoing
  539. // packets are stored in out_buffer_. So we need to go through the full
  540. // unpack/tweak/pack cycle and do repack, i.e. move the output buffer back
  541. // to incoming buffer.
  542. pkt->unpack();
  543. pkt->setType(pkt_type); // Set message type.
  544. pkt->pack();
  545. pkt->data_.resize(pkt->getBuffer().getLength());
  546. // Copy out_buffer_ to data_ to pretend that it's what was just received.
  547. memcpy(&pkt->data_[0], pkt->getBuffer().getData(), pkt->getBuffer().getLength());
  548. // Simulate that we have received that traffic
  549. srv.fakeReceive(pkt);
  550. srv.run();
  551. using namespace isc::stats;
  552. StatsMgr& mgr = StatsMgr::instance();
  553. ObservationPtr pkt4_rcvd = mgr.getObservation("pkt4-received");
  554. ObservationPtr tested_stat = mgr.getObservation(stat_name);
  555. // All expected statistics must be present.
  556. ASSERT_TRUE(pkt4_rcvd);
  557. ASSERT_TRUE(tested_stat);
  558. // They also must have expected values.
  559. EXPECT_EQ(1, pkt4_rcvd->getInteger().first);
  560. EXPECT_EQ(1, tested_stat->getInteger().first);
  561. }
  562. }; // end of isc::dhcp::test namespace
  563. }; // end of isc::dhcp namespace
  564. }; // end of isc namespace