d2_unittest.cc 14 KB

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