d2_unittest.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. // Copyright (C) 2014-2015 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 <dhcp/iface_mgr.h>
  16. #include <dhcp4/json_config_parser.h>
  17. #include <dhcp4/tests/d2_unittest.h>
  18. #include <dhcpsrv/cfgmgr.h>
  19. #include <gtest/gtest.h>
  20. #include <string>
  21. using namespace isc;
  22. using namespace isc::asiolink;
  23. using namespace isc::data;
  24. namespace isc {
  25. namespace dhcp {
  26. namespace test {
  27. void
  28. D2Dhcpv4Srv::d2ClientErrorHandler(const
  29. dhcp_ddns::NameChangeSender::Result result,
  30. dhcp_ddns::NameChangeRequestPtr& ncr) {
  31. ++error_count_;
  32. // call base class error handler
  33. Dhcpv4Srv::d2ClientErrorHandler(result, ncr);
  34. }
  35. const bool Dhcp4SrvD2Test::SHOULD_PASS;
  36. const bool Dhcp4SrvD2Test::SHOULD_FAIL;
  37. Dhcp4SrvD2Test::Dhcp4SrvD2Test() : rcode_(-1) {
  38. }
  39. Dhcp4SrvD2Test::~Dhcp4SrvD2Test() {
  40. reset();
  41. }
  42. dhcp_ddns::NameChangeRequestPtr
  43. Dhcp4SrvD2Test::buildTestNcr(uint32_t dhcid_id_num) {
  44. // Build an NCR from json string.
  45. std::ostringstream stream;
  46. stream <<
  47. "{"
  48. " \"change_type\" : 0 , "
  49. " \"forward_change\" : true , "
  50. " \"reverse_change\" : false , "
  51. " \"fqdn\" : \"myhost.example.com.\" , "
  52. " \"ip_address\" : \"192.168.2.1\" , "
  53. " \"dhcid\" : \""
  54. << std::hex << std::setfill('0') << std::setw(16)
  55. << dhcid_id_num << "\" , "
  56. " \"lease_expires_on\" : \"20140121132405\" , "
  57. " \"lease_length\" : 1300 "
  58. "}";
  59. return (dhcp_ddns::NameChangeRequest::fromJSON(stream.str()));
  60. }
  61. void
  62. Dhcp4SrvD2Test::reset() {
  63. std::string config = "{ \"interfaces-config\": {"
  64. " \"interfaces\": [ \"*\" ]"
  65. "},"
  66. "\"hooks-libraries\": [ ], "
  67. "\"rebind-timer\": 2000, "
  68. "\"renew-timer\": 1000, "
  69. "\"valid-lifetime\": 4000, "
  70. "\"subnet4\": [ ], "
  71. "\"dhcp-ddns\": { \"enable-updates\" : false }, "
  72. "\"option-def\": [ ], "
  73. "\"option-data\": [ ] }";
  74. configure(config, SHOULD_PASS);
  75. }
  76. void
  77. Dhcp4SrvD2Test::configureD2(bool enable_d2, const bool exp_result,
  78. const std::string& server_ip,
  79. const size_t port,
  80. const std::string& sender_ip,
  81. const size_t sender_port,
  82. const size_t max_queue_size) {
  83. std::ostringstream config;
  84. config <<
  85. "{ \"interfaces-config\": {"
  86. " \"interfaces\": [ \"*\" ]"
  87. "},"
  88. "\"rebind-timer\": 2000, "
  89. "\"renew-timer\": 1000, "
  90. "\"subnet4\": [ { "
  91. " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
  92. " \"subnet\": \"192.0.2.0/24\" } ],"
  93. " \"dhcp-ddns\" : {"
  94. " \"enable-updates\" : " << (enable_d2 ? "true" : "false") << ", "
  95. " \"server-ip\" : \"" << server_ip << "\", "
  96. " \"server-port\" : " << port << ", "
  97. " \"sender-ip\" : \"" << sender_ip << "\", "
  98. " \"sender-port\" : " << sender_port << ", "
  99. " \"max-queue-size\" : " << max_queue_size << ", "
  100. " \"ncr-protocol\" : \"UDP\", "
  101. " \"ncr-format\" : \"JSON\", "
  102. " \"always-include-fqdn\" : true, "
  103. " \"allow-client-update\" : true, "
  104. " \"override-no-update\" : true, "
  105. " \"override-client-update\" : true, "
  106. " \"replace-client-name\" : true, "
  107. " \"generated-prefix\" : \"test.prefix\", "
  108. " \"qualifying-suffix\" : \"test.suffix.\" },"
  109. "\"valid-lifetime\": 4000 }";
  110. configure(config.str(), exp_result);
  111. }
  112. void
  113. Dhcp4SrvD2Test::configure(const std::string& config, bool exp_result) {
  114. ElementPtr json = Element::fromJSON(config);
  115. ConstElementPtr status;
  116. CfgMgr::instance().clear();
  117. // Configure the server and make sure the config is accepted
  118. EXPECT_NO_THROW(status = configureDhcp4Server(srv_, json));
  119. ASSERT_TRUE(status);
  120. int rcode;
  121. ConstElementPtr comment = config::parseAnswer(rcode, status);
  122. if (exp_result == SHOULD_PASS) {
  123. ASSERT_EQ(0, rcode);
  124. } else {
  125. ASSERT_EQ(1, rcode);
  126. }
  127. }
  128. // Tests ability to turn on and off ddns updates by submitting
  129. // by submitting the appropriate configuration to Dhcp4 server
  130. // and then invoking its startD2() method.
  131. TEST_F(Dhcp4SrvD2Test, enableDisable) {
  132. // Grab the manager and verify that be default ddns is off
  133. // and a sender was not started.
  134. dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
  135. ASSERT_FALSE(mgr.ddnsEnabled());
  136. ASSERT_FALSE(mgr.amSending());
  137. // Verify a valid config with ddns enabled configures ddns properly,
  138. // but does not start the sender.
  139. ASSERT_NO_FATAL_FAILURE(configureD2(true));
  140. ASSERT_TRUE(mgr.ddnsEnabled());
  141. ASSERT_FALSE(mgr.amSending());
  142. // Verify that calling start does not throw and starts the sender.
  143. ASSERT_NO_THROW(srv_.startD2());
  144. ASSERT_TRUE(mgr.amSending());
  145. // Verify a valid config with ddns disabled configures ddns properly.
  146. // Sender should not have been started.
  147. ASSERT_NO_FATAL_FAILURE(configureD2(false));
  148. ASSERT_FALSE(mgr.ddnsEnabled());
  149. ASSERT_FALSE(mgr.amSending());
  150. // Verify that the sender does NOT get started when ddns is disabled.
  151. srv_.startD2();
  152. ASSERT_FALSE(mgr.amSending());
  153. }
  154. // Tests Dhcp4 server's ability to correctly handle a flawed dhcp-ddns
  155. // configuration. It does so by first enabling updates by submitting a valid
  156. // configuration and then ensuring they remain on after submitting a flawed
  157. // configuration and then invoking its startD2() method.
  158. TEST_F(Dhcp4SrvD2Test, badConfig) {
  159. // Grab the manager and verify that be default ddns is off
  160. // and a sender was not started.
  161. dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
  162. ASSERT_FALSE(mgr.ddnsEnabled());
  163. // Configure it enabled and start it.
  164. ASSERT_NO_FATAL_FAILURE(configureD2(true));
  165. ASSERT_TRUE(mgr.ddnsEnabled());
  166. ASSERT_NO_THROW(srv_.startD2());
  167. ASSERT_TRUE(mgr.amSending());
  168. // Now attempt to give it an invalid configuration.
  169. // Result should indicate failure.
  170. ASSERT_NO_FATAL_FAILURE(configureD2(false, SHOULD_FAIL, "bogus_ip"));
  171. // Configure was not altered, so ddns should be enabled and still sending.
  172. ASSERT_TRUE(mgr.ddnsEnabled());
  173. ASSERT_TRUE(mgr.amSending());
  174. // Verify that calling start does not throw or stop the sender.
  175. ASSERT_NO_THROW(srv_.startD2());
  176. ASSERT_TRUE(mgr.amSending());
  177. }
  178. // Checks that submitting an identical dhcp-ddns configuration
  179. // is handled properly. Not effect should be no change in
  180. // status for ddns updating. Updates should still enabled and
  181. // in send mode. This indicates that the sender was not stopped.
  182. TEST_F(Dhcp4SrvD2Test, sameConfig) {
  183. // Grab the manager and verify that be default ddns is off
  184. // and a sender was not started.
  185. dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
  186. ASSERT_FALSE(mgr.ddnsEnabled());
  187. // Configure it enabled and start it.
  188. ASSERT_NO_FATAL_FAILURE(configureD2(true));
  189. ASSERT_TRUE(mgr.ddnsEnabled());
  190. ASSERT_NO_THROW(srv_.startD2());
  191. ASSERT_TRUE(mgr.amSending());
  192. // Now submit an identical configuration.
  193. ASSERT_NO_FATAL_FAILURE(configureD2(true));
  194. // Configuration was not altered, so ddns should still enabled and sending.
  195. ASSERT_TRUE(mgr.ddnsEnabled());
  196. ASSERT_TRUE(mgr.amSending());
  197. // Verify that calling start does not throw or stop the sender.
  198. ASSERT_NO_THROW(srv_.startD2());
  199. ASSERT_TRUE(mgr.amSending());
  200. }
  201. // Checks that submitting an different, but valid dhcp-ddns configuration
  202. // is handled properly. Updates should be enabled, however they should
  203. // not yet be running. This indicates that the sender was stopped and
  204. // replaced, but not yet started.
  205. TEST_F(Dhcp4SrvD2Test, differentConfig) {
  206. // Grab the manager and verify that be default ddns is off
  207. // and a sender was not started.
  208. dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
  209. ASSERT_FALSE(mgr.ddnsEnabled());
  210. // Configure it enabled and start it.
  211. ASSERT_NO_FATAL_FAILURE(configureD2(true));
  212. ASSERT_TRUE(mgr.ddnsEnabled());
  213. ASSERT_NO_THROW(srv_.startD2());
  214. ASSERT_TRUE(mgr.amSending());
  215. // Now enable it on a different port.
  216. ASSERT_NO_FATAL_FAILURE(configureD2(true, SHOULD_PASS, "127.0.0.1", 54001));
  217. // Configuration was altered, so ddns should still enabled but not sending.
  218. ASSERT_TRUE(mgr.ddnsEnabled());
  219. ASSERT_FALSE(mgr.amSending());
  220. // Verify that calling start starts the sender.
  221. ASSERT_NO_THROW(srv_.startD2());
  222. ASSERT_TRUE(mgr.amSending());
  223. }
  224. // Checks that given a valid, enabled configuration and placing
  225. // sender in send mode, permits NCR requests to be sent via UPD
  226. // socket. Note this test does not employ any sort of receiving
  227. // client to verify actual transmission. These types of tests
  228. // are including under dhcp_ddns and d2 unit testing.
  229. TEST_F(Dhcp4SrvD2Test, simpleUDPSend) {
  230. // Grab the manager and verify that be default ddns is off
  231. // and a sender was not started.
  232. dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
  233. ASSERT_FALSE(mgr.ddnsEnabled());
  234. // Configure it enabled and start it.
  235. ASSERT_NO_FATAL_FAILURE(configureD2(true));
  236. ASSERT_TRUE(mgr.ddnsEnabled());
  237. ASSERT_NO_THROW(srv_.startD2());
  238. ASSERT_TRUE(mgr.amSending());
  239. // Verify that we can queue up a message.
  240. dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr();
  241. ASSERT_NO_THROW(mgr.sendRequest(ncr));
  242. EXPECT_EQ(1, mgr.getQueueSize());
  243. // Calling receive should detect the ready IO on the sender's select-fd,
  244. // and invoke callback, which should complete the send.
  245. ASSERT_NO_THROW(IfaceMgr::instance().receive4(0,0));
  246. // Verify the queue is now empty.
  247. EXPECT_EQ(0, mgr.getQueueSize());
  248. }
  249. // Checks that an IO error in sending a request to D2, results in ddns updates
  250. // being suspended. This indicates that Dhcp4Srv's error handler has been
  251. // invoked as expected. Note that this unit test relies on an attempt to send
  252. // to a server address of 0.0.0.0 port 0 fails, which it does under all OSes
  253. // except Solaris 11.
  254. /// @todo Eventually we should find a way to test this under Solaris.
  255. #ifndef OS_SOLARIS
  256. TEST_F(Dhcp4SrvD2Test, forceUDPSendFailure) {
  257. #else
  258. TEST_F(Dhcp4SrvD2Test, DISABLED_forceUDPSendFailure) {
  259. #endif
  260. // Grab the manager and verify that be default ddns is off
  261. // and a sender was not started.
  262. dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
  263. ASSERT_FALSE(mgr.ddnsEnabled());
  264. // Configure it enabled and start it.
  265. // Using server address of 0.0.0.0/0 should induce failure on send.
  266. // Pass in a non-zero sender port to avoid validation error when
  267. // server-ip/port are same as sender-ip/port
  268. ASSERT_NO_FATAL_FAILURE(configureD2(true, SHOULD_PASS, "0.0.0.0", 0,
  269. "0.0.0.0", 53001));
  270. ASSERT_TRUE(mgr.ddnsEnabled());
  271. ASSERT_NO_THROW(srv_.startD2());
  272. ASSERT_TRUE(mgr.amSending());
  273. // Queue up 3 messages.
  274. for (int i = 0; i < 3; i++) {
  275. dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(i + 1);
  276. ASSERT_NO_THROW(mgr.sendRequest(ncr));
  277. }
  278. EXPECT_EQ(3, mgr.getQueueSize());
  279. // Calling receive should detect the ready IO on the sender's select-fd,
  280. // and invoke callback, which should complete the send, which should
  281. // fail.
  282. ASSERT_NO_THROW(IfaceMgr::instance().receive4(0,0));
  283. // Verify the error handler was invoked.
  284. EXPECT_EQ(1, srv_.error_count_);
  285. // Verify that updates are disabled and we are no longer sending.
  286. ASSERT_FALSE(mgr.ddnsEnabled());
  287. ASSERT_FALSE(mgr.amSending());
  288. // Verify message is still in the queue.
  289. EXPECT_EQ(3, mgr.getQueueSize());
  290. // Verify that we can't just restart it.
  291. /// @todo This may change if we add ability to resume.
  292. ASSERT_NO_THROW(srv_.startD2());
  293. ASSERT_FALSE(mgr.amSending());
  294. // Configure it enabled and start it.
  295. ASSERT_NO_FATAL_FAILURE(configureD2(true));
  296. ASSERT_TRUE(mgr.ddnsEnabled());
  297. ASSERT_NO_THROW(srv_.startD2());
  298. ASSERT_TRUE(mgr.amSending());
  299. // Verify message is still in the queue.
  300. EXPECT_EQ(3, mgr.getQueueSize());
  301. // This will finish sending the 1st message in queue
  302. // and initiate send of 2nd message.
  303. ASSERT_NO_THROW(IfaceMgr::instance().receive4(0,0));
  304. EXPECT_EQ(1, srv_.error_count_);
  305. // First message is off the queue.
  306. EXPECT_EQ(2, mgr.getQueueSize());
  307. }
  308. // Tests error handling of D2ClientMgr::sendRequest() failure
  309. // by attempting to queue maximum number of messages.
  310. TEST_F(Dhcp4SrvD2Test, queueMaxError) {
  311. // Configure it enabled and start it.
  312. dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
  313. ASSERT_NO_FATAL_FAILURE(configureD2(true));
  314. ASSERT_TRUE(mgr.ddnsEnabled());
  315. ASSERT_NO_THROW(srv_.startD2());
  316. ASSERT_TRUE(mgr.amSending());
  317. // Attempt to queue more then the maximum allowed.
  318. int max_msgs = mgr.getQueueMaxSize();
  319. for (int i = 0; i < max_msgs + 1; i++) {
  320. dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(i + 1);
  321. ASSERT_NO_THROW(mgr.sendRequest(ncr));
  322. }
  323. // Stopping sender will complete the first message so there
  324. // should be max less one.
  325. EXPECT_EQ(max_msgs - 1, mgr.getQueueSize());
  326. // Verify the error handler was invoked.
  327. EXPECT_EQ(1, srv_.error_count_);
  328. // Verify that updates are disabled and we are no longer sending.
  329. ASSERT_FALSE(mgr.ddnsEnabled());
  330. ASSERT_FALSE(mgr.amSending());
  331. }
  332. } // namespace test
  333. } // namespace dhcp
  334. } // namespace isc