fqdn_unittest.cc 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  1. // Copyright (C) 2013-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 <config.h>
  15. #include <asiolink/io_address.h>
  16. #include <dhcp_ddns/ncr_msg.h>
  17. #include <dhcp/dhcp6.h>
  18. #include <dhcp/option.h>
  19. #include <dhcp/option_custom.h>
  20. #include <dhcp/option6_client_fqdn.h>
  21. #include <dhcp/option6_ia.h>
  22. #include <dhcp/option6_iaaddr.h>
  23. #include <dhcp/option_int_array.h>
  24. #include <dhcpsrv/lease.h>
  25. #include <dhcp6/tests/dhcp6_test_utils.h>
  26. #include <boost/pointer_cast.hpp>
  27. #include <gtest/gtest.h>
  28. using namespace isc;
  29. using namespace isc::test;
  30. using namespace isc::asiolink;
  31. using namespace isc::dhcp;
  32. using namespace isc::dhcp_ddns;
  33. using namespace isc::util;
  34. using namespace isc::hooks;
  35. using namespace std;
  36. namespace {
  37. /// @brief A test fixture class for testing DHCPv6 Client FQDN Option handling.
  38. class FqdnDhcpv6SrvTest : public Dhcpv6SrvTest {
  39. public:
  40. // Reference to D2ClientMgr singleton
  41. D2ClientMgr& d2_mgr_;
  42. // Bit Constants for turning on and off DDNS configuration options.
  43. static const uint16_t ALWAYS_INCLUDE_FQDN = 1;
  44. static const uint16_t OVERRIDE_NO_UPDATE = 2;
  45. static const uint16_t OVERRIDE_CLIENT_UPDATE = 4;
  46. static const uint16_t REPLACE_CLIENT_NAME = 8;
  47. /// @brief Constructor
  48. FqdnDhcpv6SrvTest()
  49. : Dhcpv6SrvTest(), d2_mgr_(CfgMgr::instance().getD2ClientMgr()) {
  50. // generateClientId assigns DUID to duid_.
  51. generateClientId();
  52. lease_.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
  53. duid_, 1234, 501, 502, 503,
  54. 504, 1, 0));
  55. // Config DDNS to be enabled, all controls off
  56. enableD2();
  57. }
  58. /// @brief Destructor
  59. virtual ~FqdnDhcpv6SrvTest() {
  60. // Default constructor creates a config with DHCP-DDNS updates
  61. // disabled.
  62. D2ClientConfigPtr cfg(new D2ClientConfig());
  63. CfgMgr::instance().setD2ClientConfig(cfg);
  64. }
  65. /// @brief Sets the server's DDNS configuration to ddns updates disabled.
  66. void disableD2() {
  67. // Default constructor creates a config with DHCP-DDNS updates
  68. // disabled.
  69. D2ClientConfigPtr cfg(new D2ClientConfig());
  70. CfgMgr::instance().setD2ClientConfig(cfg);
  71. }
  72. /// @brief Enables DHCP-DDNS updates with the given options enabled.
  73. ///
  74. /// Replaces the current D2ClientConfiguration with a configuration
  75. /// which as updates enabled and the control options set based upon
  76. /// the bit mask of options.
  77. ///
  78. /// @param mask Bit mask of configuration options that should be enabled.
  79. void enableD2(const uint16_t mask = 0) {
  80. D2ClientConfigPtr cfg;
  81. ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
  82. isc::asiolink::IOAddress("127.0.0.1"), 53001,
  83. dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
  84. (mask & ALWAYS_INCLUDE_FQDN),
  85. (mask & OVERRIDE_NO_UPDATE),
  86. (mask & OVERRIDE_CLIENT_UPDATE),
  87. (mask & REPLACE_CLIENT_NAME),
  88. "myhost", "example.com")));
  89. ASSERT_NO_THROW(CfgMgr::instance().setD2ClientConfig(cfg));
  90. }
  91. /// @brief Construct the DHCPv6 Client FQDN option using flags and
  92. /// domain-name.
  93. ///
  94. /// @param flags Flags to be set for the created option.
  95. /// @param fqdn_name A name which should be stored in the option.
  96. /// @param fqdn_type A type of the name carried by the option: partial
  97. /// or fully qualified.
  98. ///
  99. /// @return A pointer to the created option.
  100. Option6ClientFqdnPtr
  101. createClientFqdn(const uint8_t flags,
  102. const std::string& fqdn_name,
  103. const Option6ClientFqdn::DomainNameType fqdn_type) {
  104. return (Option6ClientFqdnPtr(new Option6ClientFqdn(flags,
  105. fqdn_name,
  106. fqdn_type)));
  107. }
  108. /// @brief Create a message with or without DHCPv6 Client FQDN Option.
  109. ///
  110. /// @param msg_type A type of the DHCPv6 message to be created.
  111. /// @param fqdn_flags Flags to be carried in the FQDN option.
  112. /// @param fqdn_domain_name A name to be carried in the FQDN option.
  113. /// @param fqdn_type A type of the name carried by the option: partial
  114. /// or fully qualified.
  115. /// @param include_oro A boolean value which indicates whether the ORO
  116. /// option should be added to the message (if true).
  117. /// @param srvid server id to be stored in the message.
  118. ///
  119. /// @return An object representing the created message.
  120. Pkt6Ptr generateMessage(uint8_t msg_type,
  121. const uint8_t fqdn_flags,
  122. const std::string& fqdn_domain_name,
  123. const Option6ClientFqdn::DomainNameType
  124. fqdn_type,
  125. const bool include_oro,
  126. OptionPtr srvid = OptionPtr()) {
  127. Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
  128. pkt->setRemoteAddr(IOAddress("fe80::abcd"));
  129. Option6IAPtr ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
  130. if (msg_type != DHCPV6_REPLY) {
  131. IOAddress hint("2001:db8:1:1::dead:beef");
  132. OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
  133. ia->addOption(hint_opt);
  134. pkt->addOption(ia);
  135. }
  136. OptionPtr clientid = generateClientId();
  137. pkt->addOption(clientid);
  138. if (srvid && (msg_type != DHCPV6_SOLICIT)) {
  139. pkt->addOption(srvid);
  140. }
  141. pkt->addOption(createClientFqdn(fqdn_flags, fqdn_domain_name,
  142. fqdn_type));
  143. if (include_oro) {
  144. OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6,
  145. D6O_ORO));
  146. oro->addValue(D6O_CLIENT_FQDN);
  147. pkt->addOption(oro);
  148. }
  149. return (pkt);
  150. }
  151. /// @brief Creates instance of the DHCPv6 message with client id and
  152. /// server id.
  153. ///
  154. /// @param msg_type A type of the message to be created.
  155. /// @param srv An object representing the DHCPv6 server, which
  156. /// is used to generate the client identifier.
  157. ///
  158. /// @return An object representing the created message.
  159. Pkt6Ptr generateMessageWithIds(const uint8_t msg_type,
  160. NakedDhcpv6Srv& srv) {
  161. Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
  162. // Generate client-id.
  163. OptionPtr opt_clientid = generateClientId();
  164. pkt->addOption(opt_clientid);
  165. if (msg_type != DHCPV6_SOLICIT) {
  166. // Generate server-id.
  167. pkt->addOption(srv.getServerID());
  168. }
  169. return (pkt);
  170. }
  171. /// @brief Returns an instance of the option carrying FQDN.
  172. ///
  173. /// @param pkt A message holding FQDN option to be returned.
  174. ///
  175. /// @return An object representing DHCPv6 Client FQDN option.
  176. Option6ClientFqdnPtr getClientFqdnOption(const Pkt6Ptr& pkt) {
  177. return (boost::dynamic_pointer_cast<Option6ClientFqdn>
  178. (pkt->getOption(D6O_CLIENT_FQDN)));
  179. }
  180. /// @brief Adds IA option to the message.
  181. ///
  182. /// Addded option holds an address.
  183. ///
  184. /// @param iaid IAID
  185. /// @param pkt A DHCPv6 message to which the IA option should be added.
  186. void addIA(const uint32_t iaid, const IOAddress& addr, Pkt6Ptr& pkt) {
  187. Option6IAPtr opt_ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
  188. Option6IAAddrPtr opt_iaaddr(new Option6IAAddr(D6O_IAADDR, addr,
  189. 300, 500));
  190. opt_ia->addOption(opt_iaaddr);
  191. pkt->addOption(opt_ia);
  192. }
  193. /// @brief Adds IA option to the message.
  194. ///
  195. /// Added option holds status code.
  196. ///
  197. /// @param iaid IAID
  198. /// @param status_code Status code
  199. /// @param pkt A DHCPv6 message to which the option should be added.
  200. void addIA(const uint32_t iaid, const uint16_t status_code, Pkt6Ptr& pkt) {
  201. Option6IAPtr opt_ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
  202. addStatusCode(status_code, "", opt_ia);
  203. pkt->addOption(opt_ia);
  204. }
  205. /// @brief Creates status code with the specified code and message.
  206. ///
  207. /// @param code A status code.
  208. /// @param msg A string representation of the message to be added to the
  209. /// Status Code option.
  210. ///
  211. /// @return An object representing the Status Code option.
  212. OptionCustomPtr createStatusCode(const uint16_t code,
  213. const std::string& msg) {
  214. OptionDefinition def("status-code", D6O_STATUS_CODE, "record");
  215. def.addRecordField("uint16");
  216. def.addRecordField("string");
  217. OptionCustomPtr opt_status(new OptionCustom(def, Option::V6));
  218. opt_status->writeInteger(code);
  219. if (!msg.empty()) {
  220. opt_status->writeString(msg, 1);
  221. }
  222. return (opt_status);
  223. }
  224. /// @brief Adds Status Code option to the IA.
  225. ///
  226. /// @param code A status code value.
  227. /// @param msg A string representation of the message to be added to the
  228. /// Status Code option.
  229. void addStatusCode(const uint16_t code, const std::string& msg,
  230. Option6IAPtr& opt_ia) {
  231. opt_ia->addOption(createStatusCode(code, msg));
  232. }
  233. /// @brief Verifies if the DHCPv6 server processes DHCPv6 Client FQDN option
  234. /// as expected.
  235. ///
  236. /// This function simulates generation of the client's message holding FQDN.
  237. /// It then calls the server's @c Dhcpv6Srv::processClientFqdn option to
  238. /// generate server's response to the FQDN. This function returns the FQDN
  239. /// which should be appended to the server's response to the client.
  240. /// This function verifies that the FQDN option returned is correct.
  241. ///
  242. /// @param msg_type A type of the client's message.
  243. /// @param in_flags A value of flags field to be set for the FQDN carried
  244. /// in the client's message.
  245. /// @param in_domain_name A domain name to be carried in the client's FQDN
  246. /// option.
  247. /// @param in_domain_type A type of the domain name to be carried in the
  248. /// client's FQDM option (partial or fully qualified).
  249. /// @param exp_flags A value of flags expected in the FQDN sent by a server.
  250. /// @param exp_domain_name A domain name expected in the FQDN sent by a
  251. /// server.
  252. void testFqdn(const uint16_t msg_type,
  253. const uint8_t in_flags,
  254. const std::string& in_domain_name,
  255. const Option6ClientFqdn::DomainNameType in_domain_type,
  256. const uint8_t exp_flags,
  257. const std::string& exp_domain_name) {
  258. NakedDhcpv6Srv srv(0);
  259. Pkt6Ptr question = generateMessage(msg_type,
  260. in_flags,
  261. in_domain_name,
  262. in_domain_type,
  263. true);
  264. ASSERT_TRUE(getClientFqdnOption(question));
  265. Pkt6Ptr answer(new Pkt6(msg_type == DHCPV6_SOLICIT ? DHCPV6_ADVERTISE :
  266. DHCPV6_REPLY, question->getTransid()));
  267. ASSERT_NO_THROW(srv.processClientFqdn(question, answer));
  268. Option6ClientFqdnPtr answ_fqdn = boost::dynamic_pointer_cast<
  269. Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
  270. ASSERT_TRUE(answ_fqdn);
  271. const bool flag_n = (exp_flags & Option6ClientFqdn::FLAG_N) != 0;
  272. const bool flag_s = (exp_flags & Option6ClientFqdn::FLAG_S) != 0;
  273. const bool flag_o = (exp_flags & Option6ClientFqdn::FLAG_O) != 0;
  274. EXPECT_EQ(flag_n, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_N));
  275. EXPECT_EQ(flag_s, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_S));
  276. EXPECT_EQ(flag_o, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_O));
  277. EXPECT_EQ(exp_domain_name, answ_fqdn->getDomainName());
  278. // If server is configured to generate full FQDN for a client, and/or
  279. // client sent empty FQDN the expected result of the processing by
  280. // processClientFqdn is an empty, partial FQDN. This is an indication
  281. // for the code which performs lease allocation that the FQDN has to
  282. // be generated from the lease address.
  283. if (exp_domain_name.empty()) {
  284. EXPECT_EQ(Option6ClientFqdn::PARTIAL,
  285. answ_fqdn->getDomainNameType());
  286. } else {
  287. EXPECT_EQ(Option6ClientFqdn::FULL, answ_fqdn->getDomainNameType());
  288. }
  289. }
  290. /// @brief Tests that the client's message holding an FQDN is processed
  291. /// and that lease is acquired.
  292. ///
  293. /// @param msg_type A type of the client's message.
  294. /// @param hostname A domain name in the client's FQDN.
  295. /// @param srv A server object, used to process the message.
  296. /// @param include_oro A boolean value which indicates whether the ORO
  297. /// option should be included in the client's message (if true) or not
  298. /// (if false). In the former case, the function will expect that server
  299. /// responds with the FQDN option. In the latter case, the function expects
  300. /// that the server doesn't respond with the FQDN.
  301. void testProcessMessage(const uint8_t msg_type,
  302. const std::string& hostname,
  303. const std::string& exp_hostname,
  304. NakedDhcpv6Srv& srv,
  305. const bool include_oro = true) {
  306. // Create a message of a specified type, add server id and
  307. // FQDN option.
  308. OptionPtr srvid = srv.getServerID();
  309. // Set the appropriate FQDN type. It must be partial if hostname is
  310. // empty.
  311. Option6ClientFqdn::DomainNameType fqdn_type = (hostname.empty() ?
  312. Option6ClientFqdn::PARTIAL : Option6ClientFqdn::FULL);
  313. Pkt6Ptr req = generateMessage(msg_type, Option6ClientFqdn::FLAG_S,
  314. hostname, fqdn_type, include_oro, srvid);
  315. // For different client's message types we have to invoke different
  316. // functions to generate response.
  317. Pkt6Ptr reply;
  318. if (msg_type == DHCPV6_SOLICIT) {
  319. ASSERT_NO_THROW(reply = srv.processSolicit(req));
  320. } else if (msg_type == DHCPV6_REQUEST) {
  321. ASSERT_NO_THROW(reply = srv.processRequest(req));
  322. } else if (msg_type == DHCPV6_RENEW) {
  323. ASSERT_NO_THROW(reply = srv.processRequest(req));
  324. } else if (msg_type == DHCPV6_RELEASE) {
  325. // For Release no lease will be acquired so we have to leave
  326. // function here.
  327. ASSERT_NO_THROW(reply = srv.processRelease(req));
  328. return;
  329. } else {
  330. // We are not interested in testing other message types.
  331. return;
  332. }
  333. // For Solicit, we will get different message type obviously.
  334. if (msg_type == DHCPV6_SOLICIT) {
  335. checkResponse(reply, DHCPV6_ADVERTISE, 1234);
  336. } else {
  337. checkResponse(reply, DHCPV6_REPLY, 1234);
  338. }
  339. // Check verify that IA_NA is correct.
  340. Option6IAAddrPtr addr =
  341. checkIA_NA(reply, 234, subnet_->getT1(), subnet_->getT2());
  342. ASSERT_TRUE(addr);
  343. // Check that we have got the address we requested.
  344. checkIAAddr(addr, IOAddress("2001:db8:1:1::dead:beef"),
  345. Lease::TYPE_NA);
  346. if (msg_type != DHCPV6_SOLICIT) {
  347. // Check that the lease exists.
  348. Lease6Ptr lease =
  349. checkLease(duid_, reply->getOption(D6O_IA_NA), addr);
  350. ASSERT_TRUE(lease);
  351. EXPECT_EQ(exp_hostname, lease->hostname_);
  352. }
  353. // The Client FQDN option should be always present in the server's
  354. // response, regardless if requested using ORO or not.
  355. Option6ClientFqdnPtr fqdn;
  356. ASSERT_TRUE(fqdn = boost::dynamic_pointer_cast<
  357. Option6ClientFqdn>(reply->getOption(D6O_CLIENT_FQDN)));
  358. EXPECT_EQ(exp_hostname, fqdn->getDomainName());
  359. }
  360. /// @brief Verify that NameChangeRequest holds valid values.
  361. ///
  362. /// This function picks first NameChangeRequest from the internal server's
  363. /// queue and checks that it holds valid parameters. The NameChangeRequest
  364. /// is removed from the queue.
  365. ///
  366. /// @param srv A server object holding a queue of NameChangeRequests.
  367. /// @param type An expected type of the NameChangeRequest (Add or Remove).
  368. /// @param reverse An expected setting of the reverse update flag.
  369. /// @param forward An expected setting of the forward udpate flag.
  370. /// @param addr A string representation of the IPv6 address held in the
  371. /// NameChangeRequest.
  372. /// @param dhcid An expected DHCID value.
  373. /// @param expires A timestamp when the lease associated with the
  374. /// NameChangeRequest expires.
  375. /// @param len A valid lifetime of the lease associated with the
  376. /// NameChangeRequest.
  377. void verifyNameChangeRequest(NakedDhcpv6Srv& srv,
  378. const isc::dhcp_ddns::NameChangeType type,
  379. const bool reverse, const bool forward,
  380. const std::string& addr,
  381. const std::string& dhcid,
  382. const uint16_t expires,
  383. const uint16_t len) {
  384. NameChangeRequest ncr = srv.name_change_reqs_.front();
  385. EXPECT_EQ(type, ncr.getChangeType());
  386. EXPECT_EQ(forward, ncr.isForwardChange());
  387. EXPECT_EQ(reverse, ncr.isReverseChange());
  388. EXPECT_EQ(addr, ncr.getIpAddress());
  389. EXPECT_EQ(dhcid, ncr.getDhcid().toStr());
  390. EXPECT_EQ(expires, ncr.getLeaseExpiresOn());
  391. EXPECT_EQ(len, ncr.getLeaseLength());
  392. EXPECT_EQ(isc::dhcp_ddns::ST_NEW, ncr.getStatus());
  393. srv.name_change_reqs_.pop();
  394. }
  395. // Holds a lease used by a test.
  396. Lease6Ptr lease_;
  397. };
  398. // A set of tests verifying server's behaviour when it receives the DHCPv6
  399. // Client Fqdn Option.
  400. // @todo: Extend these tests once appropriate configuration parameters are
  401. // implemented (ticket #3034).
  402. // Test server's response when client requests that server performs AAAA update.
  403. TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdate) {
  404. testFqdn(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_S,
  405. "myhost.example.com",
  406. Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_S,
  407. "myhost.example.com.");
  408. }
  409. // Test server's response when client provides partial domain-name and requests
  410. // that server performs AAAA update.
  411. TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdatePartialName) {
  412. testFqdn(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_S, "myhost",
  413. Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
  414. "myhost.example.com.");
  415. }
  416. // Test server's response when client provides empty domain-name and requests
  417. // that server performs AAAA update.
  418. TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdateNoName) {
  419. testFqdn(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_S, "",
  420. Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S, "");
  421. }
  422. // Test server's response when client requests no DNS update.
  423. TEST_F(FqdnDhcpv6SrvTest, noUpdate) {
  424. testFqdn(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_N,
  425. "myhost.example.com",
  426. Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_N,
  427. "myhost.example.com.");
  428. }
  429. // Test server's response when client requests that server delegates the AAAA
  430. // update to the client and this delegation is not allowed.
  431. TEST_F(FqdnDhcpv6SrvTest, clientAAAAUpdateNotAllowed) {
  432. enableD2(OVERRIDE_CLIENT_UPDATE);
  433. testFqdn(DHCPV6_SOLICIT, 0, "myhost.example.com.",
  434. Option6ClientFqdn::FULL,
  435. Option6ClientFqdn::FLAG_S | Option6ClientFqdn::FLAG_O,
  436. "myhost.example.com.");
  437. }
  438. // Test that exception is thrown if supplied NULL answer packet when
  439. // creating NameChangeRequests.
  440. TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAnswer) {
  441. NakedDhcpv6Srv srv(0);
  442. Pkt6Ptr answer;
  443. EXPECT_THROW(srv.createNameChangeRequests(answer),
  444. isc::Unexpected);
  445. }
  446. // Test that exception is thrown if supplied answer from the server
  447. // contains no DUID when creating NameChangeRequests.
  448. TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoDUID) {
  449. NakedDhcpv6Srv srv(0);
  450. Pkt6Ptr answer = Pkt6Ptr(new Pkt6(DHCPV6_REPLY, 1234));
  451. Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
  452. "myhost.example.com",
  453. Option6ClientFqdn::FULL);
  454. answer->addOption(fqdn);
  455. EXPECT_THROW(srv.createNameChangeRequests(answer), isc::Unexpected);
  456. }
  457. // Test no NameChangeRequests if Client FQDN is not added to the server's
  458. // response.
  459. TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoFQDN) {
  460. NakedDhcpv6Srv srv(0);
  461. // Create Reply message with Client Id and Server id.
  462. Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
  463. ASSERT_NO_THROW(srv.createNameChangeRequests(answer));
  464. // There should be no new NameChangeRequests.
  465. EXPECT_TRUE(srv.name_change_reqs_.empty());
  466. }
  467. // Test that NameChangeRequests are not generated if an answer message
  468. // contains no addresses.
  469. TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAddr) {
  470. NakedDhcpv6Srv srv(0);
  471. // Create Reply message with Client Id and Server id.
  472. Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
  473. // Add Client FQDN option.
  474. Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
  475. "myhost.example.com",
  476. Option6ClientFqdn::FULL);
  477. answer->addOption(fqdn);
  478. ASSERT_NO_THROW(srv.createNameChangeRequests(answer));
  479. // We didn't add any IAs, so there should be no NameChangeRequests in th
  480. // queue.
  481. ASSERT_TRUE(srv.name_change_reqs_.empty());
  482. }
  483. // Test that exactly one NameChangeRequest is created as a result of processing
  484. // the answer message which holds 3 IAs and when FQDN is specified.
  485. TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequests) {
  486. NakedDhcpv6Srv srv(0);
  487. // Create Reply message with Client Id and Server id.
  488. Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
  489. // Create three IAs, each having different address.
  490. addIA(1234, IOAddress("2001:db8:1::1"), answer);
  491. addIA(2345, IOAddress("2001:db8:1::2"), answer);
  492. addIA(3456, IOAddress("2001:db8:1::3"), answer);
  493. // Use domain name in upper case. It should be converted to lower-case
  494. // before DHCID is calculated. So, we should get the same result as if
  495. // we typed domain name in lower-case.
  496. Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
  497. "MYHOST.EXAMPLE.COM",
  498. Option6ClientFqdn::FULL);
  499. answer->addOption(fqdn);
  500. // Create NameChangeRequest for the first allocated address.
  501. ASSERT_NO_THROW(srv.createNameChangeRequests(answer));
  502. ASSERT_EQ(1, srv.name_change_reqs_.size());
  503. // Verify that NameChangeRequest is correct.
  504. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  505. "2001:db8:1::1",
  506. "000201415AA33D1187D148275136FA30300478"
  507. "FAAAA3EBD29826B5C907B2C9268A6F52",
  508. 0, 500);
  509. }
  510. // Test creation of the NameChangeRequest to remove both forward and reverse
  511. // mapping for the given lease.
  512. TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestFwdRev) {
  513. NakedDhcpv6Srv srv(0);
  514. lease_->fqdn_fwd_ = true;
  515. lease_->fqdn_rev_ = true;
  516. // Part of the domain name is in upper case, to test that it gets converted
  517. // to lower case before DHCID is computed. So, we should get the same DHCID
  518. // as if we typed domain-name in lower case.
  519. lease_->hostname_ = "MYHOST.example.com.";
  520. ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
  521. ASSERT_EQ(1, srv.name_change_reqs_.size());
  522. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
  523. "2001:db8:1::1",
  524. "000201415AA33D1187D148275136FA30300478"
  525. "FAAAA3EBD29826B5C907B2C9268A6F52",
  526. 0, 502);
  527. }
  528. // Test creation of the NameChangeRequest to remove reverse mapping for the
  529. // given lease.
  530. TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestRev) {
  531. NakedDhcpv6Srv srv(0);
  532. lease_->fqdn_fwd_ = false;
  533. lease_->fqdn_rev_ = true;
  534. lease_->hostname_ = "myhost.example.com.";
  535. ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
  536. ASSERT_EQ(1, srv.name_change_reqs_.size());
  537. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, false,
  538. "2001:db8:1::1",
  539. "000201415AA33D1187D148275136FA30300478"
  540. "FAAAA3EBD29826B5C907B2C9268A6F52",
  541. 0, 502);
  542. }
  543. // Test that NameChangeRequest to remove DNS records is not generated when
  544. // neither forward nor reverse DNS update has been performed for a lease.
  545. TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoUpdate) {
  546. NakedDhcpv6Srv srv(0);
  547. lease_->fqdn_fwd_ = false;
  548. lease_->fqdn_rev_ = false;
  549. ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
  550. EXPECT_TRUE(srv.name_change_reqs_.empty());
  551. }
  552. // Test that NameChangeRequest is not generated if the hostname hasn't been
  553. // specified for a lease for which forward and reverse mapping has been set.
  554. TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoHostname) {
  555. NakedDhcpv6Srv srv(0);
  556. lease_->fqdn_fwd_ = true;
  557. lease_->fqdn_rev_ = true;
  558. lease_->hostname_ = "";
  559. ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
  560. EXPECT_TRUE(srv.name_change_reqs_.empty());
  561. }
  562. // Test that NameChangeRequest is not generated if the invalid hostname has
  563. // been specified for a lease for which forward and reverse mapping has been
  564. // set.
  565. TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestWrongHostname) {
  566. NakedDhcpv6Srv srv(0);
  567. lease_->fqdn_fwd_ = true;
  568. lease_->fqdn_rev_ = true;
  569. lease_->hostname_ = "myhost..example.com.";
  570. ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
  571. EXPECT_TRUE(srv.name_change_reqs_.empty());
  572. }
  573. // Test that Advertise message generated in a response to the Solicit will
  574. // not result in generation if the NameChangeRequests.
  575. TEST_F(FqdnDhcpv6SrvTest, processSolicit) {
  576. NakedDhcpv6Srv srv(0);
  577. // Create a Solicit message with FQDN option and generate server's
  578. // response using processSolicit function.
  579. testProcessMessage(DHCPV6_SOLICIT, "myhost.example.com",
  580. "myhost.example.com.", srv);
  581. EXPECT_TRUE(srv.name_change_reqs_.empty());
  582. }
  583. // Test that client may send two requests, each carrying FQDN option with
  584. // a different domain-name. Server should use existing lease for the second
  585. // request but modify the DNS entries for the lease according to the contents
  586. // of the FQDN sent in the second request.
  587. TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
  588. NakedDhcpv6Srv srv(0);
  589. // Create a Request message with FQDN option and generate server's
  590. // response using processRequest function. This will result in the
  591. // creation of a new lease and the appropriate NameChangeRequest
  592. // to add both reverse and forward mapping to DNS.
  593. testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
  594. "myhost.example.com.", srv);
  595. ASSERT_EQ(1, srv.name_change_reqs_.size());
  596. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  597. "2001:db8:1:1::dead:beef",
  598. "000201415AA33D1187D148275136FA30300478"
  599. "FAAAA3EBD29826B5C907B2C9268A6F52",
  600. 0, 4000);
  601. // Client may send another request message with a new domain-name. In this
  602. // case the same lease will be returned. The existing DNS entry needs to
  603. // be replaced with a new one. Server should determine that the different
  604. // FQDN has been already added to the DNS. As a result, the old DNS
  605. // entries should be removed and the entries for the new domain-name
  606. // should be added. Therefore, we expect two NameChangeRequests. One to
  607. // remove the existing entries, one to add new entries.
  608. testProcessMessage(DHCPV6_REQUEST, "otherhost.example.com",
  609. "otherhost.example.com.", srv);
  610. ASSERT_EQ(2, srv.name_change_reqs_.size());
  611. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
  612. "2001:db8:1:1::dead:beef",
  613. "000201415AA33D1187D148275136FA30300478"
  614. "FAAAA3EBD29826B5C907B2C9268A6F52",
  615. 0, 4000);
  616. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  617. "2001:db8:1:1::dead:beef",
  618. "000201D422AA463306223D269B6CB7AFE7AAD265FC"
  619. "EA97F93623019B2E0D14E5323D5A",
  620. 0, 4000);
  621. }
  622. // Test that NameChangeRequest is not generated when Solicit message is sent.
  623. // The Solicit is here sent after a lease has been allocated for a client.
  624. // The Solicit conveys a different hostname which would trigger updates to
  625. // DNS if the Request was sent instead of Soicit. The code should differentiate
  626. // behavior depending whether Solicit or Request is sent.
  627. TEST_F(FqdnDhcpv6SrvTest, processRequestSolicit) {
  628. NakedDhcpv6Srv srv(0);
  629. // Create a Request message with FQDN option and generate server's
  630. // response using processRequest function. This will result in the
  631. // creation of a new lease and the appropriate NameChangeRequest
  632. // to add both reverse and forward mapping to DNS.
  633. testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
  634. "myhost.example.com.", srv);
  635. ASSERT_EQ(1, srv.name_change_reqs_.size());
  636. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  637. "2001:db8:1:1::dead:beef",
  638. "000201415AA33D1187D148275136FA30300478"
  639. "FAAAA3EBD29826B5C907B2C9268A6F52",
  640. 0, 4000);
  641. // When the returning client sends Solicit the code should never generate
  642. // NameChangeRequest and preserve existing DNS entries for the client.
  643. // The NameChangeRequest should only be generated when a client sends
  644. // Request or Renew.
  645. testProcessMessage(DHCPV6_SOLICIT, "otherhost.example.com",
  646. "otherhost.example.com.", srv);
  647. ASSERT_TRUE(srv.name_change_reqs_.empty());
  648. }
  649. // Test that client may send Request followed by the Renew, both holding
  650. // FQDN options, but each option holding different domain-name. The Renew
  651. // should result in generation of the two NameChangeRequests, one to remove
  652. // DNS entry added previously when Request was processed, another one to
  653. // add a new entry for the FQDN held in the Renew.
  654. TEST_F(FqdnDhcpv6SrvTest, processRequestRenew) {
  655. NakedDhcpv6Srv srv(0);
  656. // Create a Request message with FQDN option and generate server's
  657. // response using processRequest function. This will result in the
  658. // creation of a new lease and the appropriate NameChangeRequest
  659. // to add both reverse and forward mapping to DNS.
  660. testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
  661. "myhost.example.com.", srv);
  662. ASSERT_EQ(1, srv.name_change_reqs_.size());
  663. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  664. "2001:db8:1:1::dead:beef",
  665. "000201415AA33D1187D148275136FA30300478"
  666. "FAAAA3EBD29826B5C907B2C9268A6F52",
  667. 0, 4000);
  668. // Client may send Renew message with a new domain-name. In this
  669. // case the same lease will be returned. The existing DNS entry needs to
  670. // be replaced with a new one. Server should determine that the different
  671. // FQDN has been already added to the DNS. As a result, the old DNS
  672. // entries should be removed and the entries for the new domain-name
  673. // should be added. Therefore, we expect two NameChangeRequests. One to
  674. // remove the existing entries, one to add new entries.
  675. testProcessMessage(DHCPV6_RENEW, "otherhost.example.com",
  676. "otherhost.example.com.", srv);
  677. ASSERT_EQ(2, srv.name_change_reqs_.size());
  678. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
  679. "2001:db8:1:1::dead:beef",
  680. "000201415AA33D1187D148275136FA30300478"
  681. "FAAAA3EBD29826B5C907B2C9268A6F52",
  682. 0, 4000);
  683. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  684. "2001:db8:1:1::dead:beef",
  685. "000201D422AA463306223D269B6CB7AFE7AAD265FC"
  686. "EA97F93623019B2E0D14E5323D5A",
  687. 0, 4000);
  688. }
  689. TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
  690. NakedDhcpv6Srv srv(0);
  691. // Create a Request message with FQDN option and generate server's
  692. // response using processRequest function. This will result in the
  693. // creation of a new lease and the appropriate NameChangeRequest
  694. // to add both reverse and forward mapping to DNS.
  695. testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
  696. "myhost.example.com.", srv);
  697. ASSERT_EQ(1, srv.name_change_reqs_.size());
  698. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  699. "2001:db8:1:1::dead:beef",
  700. "000201415AA33D1187D148275136FA30300478"
  701. "FAAAA3EBD29826B5C907B2C9268A6F52",
  702. 0, 4000);
  703. // Client may send Release message. In this case the lease should be
  704. // removed and all existing DNS entries for this lease should be
  705. // also removed. Therefore, we expect that single NameChangeRequest to
  706. // remove DNS entries is generated.
  707. testProcessMessage(DHCPV6_RELEASE, "otherhost.example.com",
  708. "otherhost.example.com.", srv);
  709. ASSERT_EQ(1, srv.name_change_reqs_.size());
  710. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
  711. "2001:db8:1:1::dead:beef",
  712. "000201415AA33D1187D148275136FA30300478"
  713. "FAAAA3EBD29826B5C907B2C9268A6F52",
  714. 0, 4000);
  715. }
  716. // Checks that the server include DHCPv6 Client FQDN option in its
  717. // response even when client doesn't request this option using ORO.
  718. TEST_F(FqdnDhcpv6SrvTest, processRequestWithoutFqdn) {
  719. NakedDhcpv6Srv srv(0);
  720. // The last parameter disables use of the ORO to request FQDN option
  721. // In this case, we expect that the FQDN option will not be included
  722. // in the server's response. The testProcessMessage will check that.
  723. testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
  724. "myhost.example.com.", srv, false);
  725. ASSERT_EQ(1, srv.name_change_reqs_.size());
  726. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  727. "2001:db8:1:1::dead:beef",
  728. "000201415AA33D1187D148275136FA30300478"
  729. "FAAAA3EBD29826B5C907B2C9268A6F52",
  730. 0, 4000);
  731. }
  732. // Checks that FQDN is generated from an ip address, when client sends an empty
  733. // FQDN.
  734. TEST_F(FqdnDhcpv6SrvTest, processRequestEmptyFqdn) {
  735. NakedDhcpv6Srv srv(0);
  736. testProcessMessage(DHCPV6_REQUEST, "",
  737. "host-2001-db8-1-1--dead-beef.example.com.",
  738. srv, false);
  739. ASSERT_EQ(1, srv.name_change_reqs_.size());
  740. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  741. "2001:db8:1:1::dead:beef",
  742. "0002018D6874B105A5C92DBBD6E4F6C80A93161"
  743. "BC03996F0CD0EB75800DEF997C29961",
  744. 0, 4000);
  745. }
  746. // Checks that when the server reuses expired lease, the NameChangeRequest
  747. // is generated to remove the DNS mapping for the expired lease and second
  748. // NameChangeRequest to add a DNS mapping for a new lease.
  749. TEST_F(FqdnDhcpv6SrvTest, processRequestReuseExpiredLease) {
  750. // This address will be used throughout the test.
  751. IOAddress addr("2001:db8:1:1::dead:beef");
  752. // We are going to configure a subnet with a pool that consists of
  753. // exactly one address. This address will be handed out to the
  754. // client, will get expired and then be reused.
  755. CfgMgr::instance().deleteSubnets6();
  756. subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1:1::"), 56, 1, 2,
  757. 3, 4));
  758. pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr));
  759. subnet_->addPool(pool_);
  760. CfgMgr::instance().addSubnet6(subnet_);
  761. // Allocate a lease.
  762. NakedDhcpv6Srv srv(0);
  763. testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
  764. "myhost.example.com.", srv);
  765. // Test that the appropriate NameChangeRequest has been generated.
  766. ASSERT_EQ(1, srv.name_change_reqs_.size());
  767. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  768. "2001:db8:1:1::dead:beef",
  769. "000201415AA33D1187D148275136FA30300478"
  770. "FAAAA3EBD29826B5C907B2C9268A6F52",
  771. 0, 4);
  772. // Get the lease acquired and modify it. In particular, expire it.
  773. Lease6Ptr lease =
  774. LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
  775. ASSERT_TRUE(lease);
  776. // One of the following: IAID, DUID or subnet identifier has to be changed
  777. // because otherwise the allocation engine will treat the lease as
  778. // being renewed by the same client. If we at least change subnet identifier
  779. // the lease will be treated as expired lease to be reused.
  780. ++lease->subnet_id_;
  781. // Move the cllt back in time and make sure that the lease got expired.
  782. lease->cltt_ = time(NULL) - 10;
  783. lease->valid_lft_ = 5;
  784. ASSERT_TRUE(lease->expired());
  785. // Change the hostname so as the name change request for removing existing
  786. // DNS mapping is generated.
  787. lease->hostname_ = "otherhost.example.com.";
  788. // Update the lease in the lease database.
  789. LeaseMgrFactory::instance().updateLease6(lease);
  790. // Simulate another lease acquisition. Since, our pool consists of
  791. // exactly one address and this address is used by the lease in the
  792. // lease database, it is guaranteed that the allocation engine will
  793. // reuse this lease.
  794. testProcessMessage(DHCPV6_REQUEST, "myhost.example.com.",
  795. "myhost.example.com.", srv);
  796. ASSERT_EQ(2, srv.name_change_reqs_.size());
  797. // The first name change request generated, should remove a DNS
  798. // mapping for an expired lease.
  799. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
  800. "2001:db8:1:1::dead:beef",
  801. "000201D422AA463306223D269B6CB7AFE7AAD2"
  802. "65FCEA97F93623019B2E0D14E5323D5A",
  803. 0, 5);
  804. // The second name change request should add a DNS mapping for
  805. // a new lease.
  806. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  807. "2001:db8:1:1::dead:beef",
  808. "000201415AA33D1187D148275136FA30300478"
  809. "FAAAA3EBD29826B5C907B2C9268A6F52",
  810. 0, 4);
  811. }
  812. } // end of anonymous namespace