d2_unittest.cc 14 KB

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