dhcp4_test_utils.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. // Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <asiolink/io_address.h>
  16. #include <config/ccsession.h>
  17. #include <dhcp4/tests/dhcp4_test_utils.h>
  18. #include <dhcp/option4_addrlst.h>
  19. #include <dhcp/option_int_array.h>
  20. #include <dhcp/option_custom.h>
  21. #include <dhcp/iface_mgr.h>
  22. #include <dhcpsrv/cfgmgr.h>
  23. #include <dhcpsrv/lease.h>
  24. #include <dhcpsrv/lease_mgr.h>
  25. #include <dhcpsrv/lease_mgr_factory.h>
  26. using namespace std;
  27. using namespace isc::asiolink;
  28. namespace isc {
  29. namespace dhcp {
  30. namespace test {
  31. /// dummy server-id file location
  32. static const char* SRVID_FILE = "server-id-test.txt";
  33. Dhcpv4SrvTest::Dhcpv4SrvTest()
  34. :rcode_(-1) {
  35. subnet_ = Subnet4Ptr(new Subnet4(IOAddress("192.0.2.0"), 24, 1000,
  36. 2000, 3000));
  37. pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"), IOAddress("192.0.2.110")));
  38. subnet_->addPool(pool_);
  39. CfgMgr::instance().deleteSubnets4();
  40. CfgMgr::instance().addSubnet4(subnet_);
  41. // Add Router option.
  42. Option4AddrLstPtr opt_routers(new Option4AddrLst(DHO_ROUTERS));
  43. opt_routers->setAddress(IOAddress("192.0.2.2"));
  44. subnet_->addOption(opt_routers, false, "dhcp4");
  45. // it's ok if that fails. There should not be such a file anyway
  46. unlink(SRVID_FILE);
  47. const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
  48. // There must be some interface detected
  49. if (ifaces.empty()) {
  50. // We can't use ASSERT in constructor
  51. ADD_FAILURE() << "No interfaces detected.";
  52. }
  53. valid_iface_ = ifaces.begin()->getName();
  54. }
  55. void Dhcpv4SrvTest::addPrlOption(Pkt4Ptr& pkt) {
  56. OptionUint8ArrayPtr option_prl =
  57. OptionUint8ArrayPtr(new OptionUint8Array(Option::V4,
  58. DHO_DHCP_PARAMETER_REQUEST_LIST));
  59. // Let's request options that have been configured for the subnet.
  60. option_prl->addValue(DHO_DOMAIN_NAME_SERVERS);
  61. option_prl->addValue(DHO_DOMAIN_NAME);
  62. option_prl->addValue(DHO_LOG_SERVERS);
  63. option_prl->addValue(DHO_COOKIE_SERVERS);
  64. // Let's also request the option that hasn't been configured. In such
  65. // case server should ignore request for this particular option.
  66. option_prl->addValue(DHO_LPR_SERVERS);
  67. // And add 'Parameter Request List' option into the DISCOVER packet.
  68. pkt->addOption(option_prl);
  69. }
  70. void Dhcpv4SrvTest::configureRequestedOptions() {
  71. // dns-servers
  72. Option4AddrLstPtr
  73. option_dns_servers(new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS));
  74. option_dns_servers->addAddress(IOAddress("192.0.2.1"));
  75. option_dns_servers->addAddress(IOAddress("192.0.2.100"));
  76. ASSERT_NO_THROW(subnet_->addOption(option_dns_servers, false, "dhcp4"));
  77. // domain-name
  78. OptionDefinition def("domain-name", DHO_DOMAIN_NAME, OPT_FQDN_TYPE);
  79. OptionCustomPtr option_domain_name(new OptionCustom(def, Option::V4));
  80. option_domain_name->writeFqdn("example.com");
  81. subnet_->addOption(option_domain_name, false, "dhcp4");
  82. // log-servers
  83. Option4AddrLstPtr option_log_servers(new Option4AddrLst(DHO_LOG_SERVERS));
  84. option_log_servers->addAddress(IOAddress("192.0.2.2"));
  85. option_log_servers->addAddress(IOAddress("192.0.2.10"));
  86. ASSERT_NO_THROW(subnet_->addOption(option_log_servers, false, "dhcp4"));
  87. // cookie-servers
  88. Option4AddrLstPtr option_cookie_servers(new Option4AddrLst(DHO_COOKIE_SERVERS));
  89. option_cookie_servers->addAddress(IOAddress("192.0.2.1"));
  90. ASSERT_NO_THROW(subnet_->addOption(option_cookie_servers, false, "dhcp4"));
  91. }
  92. void Dhcpv4SrvTest::messageCheck(const Pkt4Ptr& q, const Pkt4Ptr& a) {
  93. ASSERT_TRUE(q);
  94. ASSERT_TRUE(a);
  95. EXPECT_EQ(q->getHops(), a->getHops());
  96. EXPECT_EQ(q->getIface(), a->getIface());
  97. EXPECT_EQ(q->getIndex(), a->getIndex());
  98. EXPECT_EQ(q->getGiaddr(), a->getGiaddr());
  99. // When processing an incoming packet the remote address
  100. // is copied as a src address, and the source address is
  101. // copied as a remote address to the response.
  102. EXPECT_TRUE(q->getLocalHWAddr() == a->getLocalHWAddr());
  103. EXPECT_TRUE(q->getRemoteHWAddr() == a->getRemoteHWAddr());
  104. // Check that bare minimum of required options are there.
  105. // We don't check options requested by a client. Those
  106. // are checked elsewhere.
  107. EXPECT_TRUE(a->getOption(DHO_SUBNET_MASK));
  108. EXPECT_TRUE(a->getOption(DHO_ROUTERS));
  109. EXPECT_TRUE(a->getOption(DHO_DHCP_SERVER_IDENTIFIER));
  110. EXPECT_TRUE(a->getOption(DHO_DHCP_LEASE_TIME));
  111. EXPECT_TRUE(a->getOption(DHO_SUBNET_MASK));
  112. EXPECT_TRUE(a->getOption(DHO_DOMAIN_NAME));
  113. EXPECT_TRUE(a->getOption(DHO_DOMAIN_NAME_SERVERS));
  114. // Check that something is offered
  115. EXPECT_TRUE(a->getYiaddr().toText() != "0.0.0.0");
  116. }
  117. void Dhcpv4SrvTest::optionsCheck(const Pkt4Ptr& pkt) {
  118. // Check that the requested and configured options are returned
  119. // in the ACK message.
  120. EXPECT_TRUE(pkt->getOption(DHO_DOMAIN_NAME))
  121. << "domain-name not present in the response";
  122. EXPECT_TRUE(pkt->getOption(DHO_DOMAIN_NAME_SERVERS))
  123. << "dns-servers not present in the response";
  124. EXPECT_TRUE(pkt->getOption(DHO_LOG_SERVERS))
  125. << "log-servers not present in the response";
  126. EXPECT_TRUE(pkt->getOption(DHO_COOKIE_SERVERS))
  127. << "cookie-servers not present in the response";
  128. // Check that the requested but not configured options are not
  129. // returned in the ACK message.
  130. EXPECT_FALSE(pkt->getOption(DHO_LPR_SERVERS))
  131. << "domain-name present in the response but it is"
  132. << " expected not to be present";
  133. }
  134. void Dhcpv4SrvTest::noOptionsCheck(const Pkt4Ptr& pkt) {
  135. // Check that certain options are not returned in the packet.
  136. // This is the case, when client didn't ask for them or when
  137. // NAK was returned by the server.
  138. EXPECT_FALSE(pkt->getOption(DHO_DOMAIN_NAME))
  139. << "domain-name present in the response";
  140. EXPECT_FALSE(pkt->getOption(DHO_DOMAIN_NAME_SERVERS))
  141. << "dns-servers present in the response";
  142. EXPECT_FALSE(pkt->getOption(DHO_LOG_SERVERS))
  143. << "log-servers present in the response";
  144. EXPECT_FALSE(pkt->getOption(DHO_COOKIE_SERVERS))
  145. << "cookie-servers present in the response";
  146. }
  147. OptionPtr Dhcpv4SrvTest::generateClientId(size_t size /*= 4*/) {
  148. OptionBuffer clnt_id(size);
  149. for (int i = 0; i < size; i++) {
  150. clnt_id[i] = 100 + i;
  151. }
  152. client_id_ = ClientIdPtr(new ClientId(clnt_id));
  153. return (OptionPtr(new Option(Option::V4, DHO_DHCP_CLIENT_IDENTIFIER,
  154. clnt_id.begin(),
  155. clnt_id.begin() + size)));
  156. }
  157. HWAddrPtr Dhcpv4SrvTest::generateHWAddr(size_t size /*= 6*/) {
  158. const uint8_t hw_type = 123; // Just a fake number (typically 6=HTYPE_ETHER, see dhcp4.h)
  159. OptionBuffer mac(size);
  160. for (int i = 0; i < size; ++i) {
  161. mac[i] = 50 + i;
  162. }
  163. return (HWAddrPtr(new HWAddr(mac, hw_type)));
  164. }
  165. void Dhcpv4SrvTest::checkAddressParams(const Pkt4Ptr& rsp, const SubnetPtr subnet,
  166. bool t1_mandatory /*= false*/,
  167. bool t2_mandatory /*= false*/) {
  168. // Technically inPool implies inRange, but let's be on the safe
  169. // side and check both.
  170. EXPECT_TRUE(subnet->inRange(rsp->getYiaddr()));
  171. EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, rsp->getYiaddr()));
  172. // Check lease time
  173. OptionPtr opt = rsp->getOption(DHO_DHCP_LEASE_TIME);
  174. if (!opt) {
  175. ADD_FAILURE() << "Lease time option missing in response";
  176. } else {
  177. EXPECT_EQ(opt->getUint32(), subnet->getValid());
  178. }
  179. // Check T1 timer
  180. opt = rsp->getOption(DHO_DHCP_RENEWAL_TIME);
  181. if (opt) {
  182. EXPECT_EQ(opt->getUint32(), subnet->getT1());
  183. } else {
  184. if (t1_mandatory) {
  185. ADD_FAILURE() << "Required T1 option missing";
  186. }
  187. }
  188. // Check T2 timer
  189. opt = rsp->getOption(DHO_DHCP_REBINDING_TIME);
  190. if (opt) {
  191. EXPECT_EQ(opt->getUint32(), subnet->getT2());
  192. } else {
  193. if (t2_mandatory) {
  194. ADD_FAILURE() << "Required T2 option missing";
  195. }
  196. }
  197. }
  198. void Dhcpv4SrvTest::checkResponse(const Pkt4Ptr& rsp, uint8_t expected_message_type,
  199. uint32_t expected_transid) {
  200. ASSERT_TRUE(rsp);
  201. EXPECT_EQ(expected_message_type, rsp->getType());
  202. EXPECT_EQ(expected_transid, rsp->getTransid());
  203. }
  204. Lease4Ptr Dhcpv4SrvTest::checkLease(const Pkt4Ptr& rsp,
  205. const OptionPtr& client_id,
  206. const HWAddrPtr&,
  207. const IOAddress& expected_addr) {
  208. ClientIdPtr id;
  209. if (client_id) {
  210. OptionBuffer data = client_id->getData();
  211. id.reset(new ClientId(data));
  212. }
  213. Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(expected_addr);
  214. if (!lease) {
  215. cout << "Lease for " << expected_addr.toText()
  216. << " not found in the database backend.";
  217. return (Lease4Ptr());
  218. }
  219. EXPECT_EQ(rsp->getYiaddr().toText(), expected_addr.toText());
  220. EXPECT_EQ(expected_addr.toText(), lease->addr_.toText());
  221. if (client_id) {
  222. EXPECT_TRUE(*lease->client_id_ == *id);
  223. }
  224. EXPECT_EQ(subnet_->getID(), lease->subnet_id_);
  225. return (lease);
  226. }
  227. /// @brief Checks if server response (OFFER, ACK, NAK) includes proper server-id
  228. /// @param rsp response packet to be validated
  229. /// @param expected_srvid expected value of server-id
  230. void Dhcpv4SrvTest::checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid) {
  231. // Check that server included its server-id
  232. OptionPtr opt = rsp->getOption(DHO_DHCP_SERVER_IDENTIFIER);
  233. ASSERT_TRUE(opt);
  234. EXPECT_EQ(opt->getType(), expected_srvid->getType() );
  235. EXPECT_EQ(opt->len(), expected_srvid->len() );
  236. EXPECT_TRUE(opt->getData() == expected_srvid->getData());
  237. }
  238. void Dhcpv4SrvTest::checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid) {
  239. // check that server included our own client-id
  240. OptionPtr opt = rsp->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
  241. ASSERT_TRUE(opt);
  242. EXPECT_EQ(expected_clientid->getType(), opt->getType());
  243. EXPECT_EQ(expected_clientid->len(), opt->len());
  244. EXPECT_TRUE(expected_clientid->getData() == opt->getData());
  245. }
  246. ::testing::AssertionResult
  247. Dhcpv4SrvTest::createPacketFromBuffer(const Pkt4Ptr& src_pkt,
  248. Pkt4Ptr& dst_pkt) {
  249. // Create on-wire format of the packet. If pack() has been called
  250. // on this instance of the packet already, the next call to pack()
  251. // should remove all contents of the output buffer.
  252. try {
  253. src_pkt->pack();
  254. } catch (const Exception& ex) {
  255. return (::testing::AssertionFailure() <<
  256. "Failed to parse source packet: "
  257. << ex.what());
  258. }
  259. // Get the output buffer from the source packet.
  260. const util::OutputBuffer& buf = src_pkt->getBuffer();
  261. // Create a copy of the packet using the output buffer from the source
  262. // packet.
  263. try {
  264. dst_pkt.reset(new Pkt4(static_cast<const uint8_t*>(buf.getData()),
  265. buf.getLength()));
  266. } catch (const Exception& ex) {
  267. return (::testing::AssertionFailure()
  268. << "Failed to create a destination packet from the buffer: "
  269. << ex.what());
  270. }
  271. try {
  272. // Parse the new packet and return to the caller.
  273. dst_pkt->unpack();
  274. } catch (const Exception& ex) {
  275. return (::testing::AssertionFailure()
  276. << "Failed to parse a destination packet: "
  277. << ex.what());
  278. }
  279. return (::testing::AssertionSuccess());
  280. }
  281. void Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
  282. // Create an instance of the tested class.
  283. boost::scoped_ptr<NakedDhcpv4Srv> srv(new NakedDhcpv4Srv(0));
  284. // Initialize the source HW address.
  285. vector<uint8_t> mac(6);
  286. for (int i = 0; i < 6; ++i) {
  287. mac[i] = i * 10;
  288. }
  289. // Initialized the destination HW address.
  290. vector<uint8_t> dst_mac(6);
  291. for (int i = 0; i < 6; ++i) {
  292. dst_mac[i] = i * 20;
  293. }
  294. // Create a DHCP message. It will be used to simulate the
  295. // incoming message.
  296. boost::shared_ptr<Pkt4> req(new Pkt4(msg_type, 1234));
  297. // Create a response message. It will hold a reponse packet.
  298. // Initially, set it to NULL.
  299. boost::shared_ptr<Pkt4> rsp;
  300. // Set the name of the interface on which packet is received.
  301. req->setIface("eth0");
  302. // Set the interface index. It is just a dummy value and will
  303. // not be interpreted.
  304. req->setIndex(17);
  305. // Set the target HW address. This value is normally used to
  306. // construct the data link layer header.
  307. req->setRemoteHWAddr(1, 6, dst_mac);
  308. // Set the HW address. This value is set on DHCP level (in chaddr).
  309. req->setHWAddr(1, 6, mac);
  310. // Set local HW address. It is used to construct the data link layer
  311. // header.
  312. req->setLocalHWAddr(1, 6, mac);
  313. // Set target IP address.
  314. req->setRemoteAddr(IOAddress("192.0.2.55"));
  315. // Set relay address.
  316. req->setGiaddr(IOAddress("192.0.2.10"));
  317. // We are going to test that certain options are returned
  318. // in the response message when requested using 'Parameter
  319. // Request List' option. Let's configure those options that
  320. // are returned when requested.
  321. configureRequestedOptions();
  322. // Create a copy of the original packet by parsing its wire format.
  323. // This simulates the real life scenario when we process the packet
  324. // which was parsed from its wire format.
  325. Pkt4Ptr received;
  326. ASSERT_TRUE(createPacketFromBuffer(req, received));
  327. if (msg_type == DHCPDISCOVER) {
  328. ASSERT_NO_THROW(
  329. rsp = srv->processDiscover(received);
  330. );
  331. // Should return OFFER
  332. ASSERT_TRUE(rsp);
  333. EXPECT_EQ(DHCPOFFER, rsp->getType());
  334. } else {
  335. ASSERT_NO_THROW(rsp = srv->processRequest(received));
  336. // Should return ACK
  337. ASSERT_TRUE(rsp);
  338. EXPECT_EQ(DHCPACK, rsp->getType());
  339. }
  340. messageCheck(received, rsp);
  341. // We did not request any options so these should not be present
  342. // in the RSP.
  343. EXPECT_FALSE(rsp->getOption(DHO_LOG_SERVERS));
  344. EXPECT_FALSE(rsp->getOption(DHO_COOKIE_SERVERS));
  345. EXPECT_FALSE(rsp->getOption(DHO_LPR_SERVERS));
  346. // Repeat the test but request some options.
  347. // Add 'Parameter Request List' option.
  348. addPrlOption(req);
  349. ASSERT_TRUE(createPacketFromBuffer(req, received));
  350. ASSERT_TRUE(received->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
  351. if (msg_type == DHCPDISCOVER) {
  352. ASSERT_NO_THROW(rsp = srv->processDiscover(received));
  353. // Should return non-NULL packet.
  354. ASSERT_TRUE(rsp);
  355. EXPECT_EQ(DHCPOFFER, rsp->getType());
  356. } else {
  357. ASSERT_NO_THROW(rsp = srv->processRequest(received));
  358. // Should return non-NULL packet.
  359. ASSERT_TRUE(rsp);
  360. EXPECT_EQ(DHCPACK, rsp->getType());
  361. }
  362. // Check that the requested options are returned.
  363. optionsCheck(rsp);
  364. // The following part of the test will test that the NAK is sent when
  365. // there is no address pool configured. In the same time, we expect
  366. // that the requested options are not included in NAK message, but that
  367. // they are only included when yiaddr is set to non-zero value.
  368. ASSERT_NO_THROW(subnet_->delPools(Lease::TYPE_V4));
  369. // There has been a lease allocated for the particular client. So,
  370. // even though we deleted the subnet, the client would get the
  371. // existing lease (not a NAK). Therefore, we have to change the chaddr
  372. // in the packet so as the existing lease is not returned.
  373. req->setHWAddr(1, 6, std::vector<uint8_t>(2, 6));
  374. ASSERT_TRUE(createPacketFromBuffer(req, received));
  375. ASSERT_TRUE(received->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
  376. if (msg_type == DHCPDISCOVER) {
  377. ASSERT_NO_THROW(rsp = srv->processDiscover(received));
  378. // Should return non-NULL packet.
  379. ASSERT_TRUE(rsp);
  380. } else {
  381. ASSERT_NO_THROW(rsp = srv->processRequest(received));
  382. // Should return non-NULL packet.
  383. ASSERT_TRUE(rsp);
  384. }
  385. // We should get the NAK packet with yiaddr set to 0.
  386. EXPECT_EQ(DHCPNAK, rsp->getType());
  387. ASSERT_EQ("0.0.0.0", rsp->getYiaddr().toText());
  388. // Make sure that none of the requested options is returned in NAK.
  389. noOptionsCheck(rsp);
  390. }
  391. /// @brief This function cleans up after the test.
  392. void Dhcpv4SrvTest::TearDown() {
  393. CfgMgr::instance().deleteSubnets4();
  394. // Let's clean up if there is such a file.
  395. unlink(SRVID_FILE);
  396. // Close all open sockets.
  397. IfaceMgr::instance().closeSockets();
  398. // Some unit tests override the default packet filtering class, used
  399. // by the IfaceMgr. The dummy class, called PktFilterTest, reports the
  400. // capability to directly respond to the clients without IP address
  401. // assigned. This capability is not supported by the default packet
  402. // filtering class: PktFilterInet. Therefore setting the dummy class
  403. // allows to test scenarios, when server responds to the broadcast address
  404. // on client's request, despite having support for direct response.
  405. // The following call restores the use of original packet filtering class
  406. // after the test.
  407. try {
  408. IfaceMgr::instance().setPacketFilter(PktFilterPtr(new PktFilterInet()));
  409. } catch (const Exception& ex) {
  410. FAIL() << "Failed to restore the default (PktFilterInet) packet filtering"
  411. << " class after the test. Exception has been caught: "
  412. << ex.what();
  413. }
  414. }
  415. }; // end of isc::dhcp::test namespace
  416. }; // end of isc::dhcp namespace
  417. }; // end of isc namespace