inform_unittest.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  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 <asiolink/io_address.h>
  16. #include <cc/data.h>
  17. #include <dhcp/dhcp4.h>
  18. #include <dhcp/tests/iface_mgr_test_config.h>
  19. #include <dhcp4/tests/dhcp4_test_utils.h>
  20. #include <dhcp4/tests/dhcp4_client.h>
  21. #include <stats/stats_mgr.h>
  22. using namespace isc;
  23. using namespace isc::asiolink;
  24. using namespace isc::data;
  25. using namespace isc::dhcp;
  26. using namespace isc::dhcp::test;
  27. namespace {
  28. /// @brief Set of JSON configurations used throughout the Inform tests.
  29. ///
  30. /// - Configuration 0:
  31. /// - Used for testing direct traffic
  32. /// - 1 subnet: 10.0.0.0/24
  33. /// - 1 pool: 10.0.0.10-10.0.0.100
  34. /// - Router option present: 10.0.0.200 and 10.0.0.201
  35. /// - Domain Name Server option present: 10.0.0.202, 10.0.0.203.
  36. /// - Log Servers option present: 192.0.2.200 and 192.0.2.201
  37. /// - Quotes Servers option present: 192.0.2.202, 192.0.2.203.
  38. ///
  39. /// - Configuration 1:
  40. /// - Use for testing relayed messages
  41. /// - 1 subnet: 192.0.2.0/24
  42. /// - Router option present: 192.0.2.200 and 192.0.2.201
  43. /// - Domain Name Server option present: 192.0.2.202, 192.0.2.203.
  44. /// - Log Servers option present: 192.0.2.200 and 192.0.2.201
  45. /// - Quotes Servers option present: 192.0.2.202, 192.0.2.203.
  46. const char* INFORM_CONFIGS[] = {
  47. // Configuration 0
  48. "{ \"interfaces-config\": {"
  49. " \"interfaces\": [ \"*\" ]"
  50. "},"
  51. "\"valid-lifetime\": 600,"
  52. "\"subnet4\": [ { "
  53. " \"subnet\": \"10.0.0.0/24\", "
  54. " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
  55. " \"option-data\": [ {"
  56. " \"name\": \"routers\","
  57. " \"code\": 3,"
  58. " \"data\": \"10.0.0.200,10.0.0.201\","
  59. " \"csv-format\": true,"
  60. " \"space\": \"dhcp4\""
  61. " },"
  62. " {"
  63. " \"name\": \"domain-name-servers\","
  64. " \"code\": 6,"
  65. " \"data\": \"10.0.0.202,10.0.0.203\","
  66. " \"csv-format\": true,"
  67. " \"space\": \"dhcp4\""
  68. " },"
  69. " {"
  70. " \"name\": \"log-servers\","
  71. " \"code\": 7,"
  72. " \"data\": \"10.0.0.200,10.0.0.201\","
  73. " \"csv-format\": true,"
  74. " \"space\": \"dhcp4\""
  75. " },"
  76. " {"
  77. " \"name\": \"cookie-servers\","
  78. " \"code\": 8,"
  79. " \"data\": \"10.0.0.202,10.0.0.203\","
  80. " \"csv-format\": true,"
  81. " \"space\": \"dhcp4\""
  82. " } ]"
  83. " } ]"
  84. "}",
  85. // Configuration 1
  86. "{ \"interfaces-config\": {"
  87. " \"interfaces\": [ \"*\" ]"
  88. "},"
  89. "\"valid-lifetime\": 600,"
  90. "\"subnet4\": [ { "
  91. " \"subnet\": \"192.0.2.0/24\", "
  92. " \"option-data\": [ {"
  93. " \"name\": \"routers\","
  94. " \"code\": 3,"
  95. " \"data\": \"192.0.2.200,192.0.2.201\","
  96. " \"csv-format\": true,"
  97. " \"space\": \"dhcp4\""
  98. " },"
  99. " {"
  100. " \"name\": \"domain-name-servers\","
  101. " \"code\": 6,"
  102. " \"data\": \"192.0.2.202,192.0.2.203\","
  103. " \"csv-format\": true,"
  104. " \"space\": \"dhcp4\""
  105. " },"
  106. " {"
  107. " \"name\": \"log-servers\","
  108. " \"code\": 7,"
  109. " \"data\": \"10.0.0.200,10.0.0.201\","
  110. " \"csv-format\": true,"
  111. " \"space\": \"dhcp4\""
  112. " },"
  113. " {"
  114. " \"name\": \"cookie-servers\","
  115. " \"code\": 8,"
  116. " \"data\": \"10.0.0.202,10.0.0.203\","
  117. " \"csv-format\": true,"
  118. " \"space\": \"dhcp4\""
  119. " } ]"
  120. " } ]"
  121. "}"
  122. };
  123. /// @brief Test fixture class for testing DHCPINFORM.
  124. class InformTest : public Dhcpv4SrvTest {
  125. public:
  126. /// @brief Constructor.
  127. ///
  128. /// Sets up fake interfaces.
  129. InformTest()
  130. : Dhcpv4SrvTest(),
  131. iface_mgr_test_config_(true) {
  132. IfaceMgr::instance().openSockets4();
  133. // Let's wipe all existing statistics.
  134. isc::stats::StatsMgr::instance().removeAll();
  135. }
  136. /// @brief Desctructor.
  137. ///
  138. /// Cleans up statistics after the test.
  139. ~InformTest() {
  140. // Let's wipe all existing statistics.
  141. isc::stats::StatsMgr::instance().removeAll();
  142. }
  143. /// @brief Interface Manager's fake configuration control.
  144. IfaceMgrTestConfig iface_mgr_test_config_;
  145. };
  146. // Test that directly connected client's DHCPINFORM message is processed and
  147. // DHCPACK message is sent back.
  148. TEST_F(InformTest, directClientBroadcast) {
  149. Dhcp4Client client;
  150. // Configure DHCP server.
  151. configure(INFORM_CONFIGS[0], *client.getServer());
  152. // Request some configuration when DHCPINFORM is sent.
  153. client.requestOptions(DHO_LOG_SERVERS, DHO_COOKIE_SERVERS);
  154. // Preconfigure the client with the IP address.
  155. client.createLease(IOAddress("10.0.0.56"), 600);
  156. // Send DHCPINFORM message to the server.
  157. ASSERT_NO_THROW(client.doInform());
  158. // Make sure that the server responded.
  159. ASSERT_TRUE(client.getContext().response_);
  160. Pkt4Ptr resp = client.getContext().response_;
  161. // Make sure that the server has responded with DHCPACK.
  162. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  163. // Response should have been unicast to the ciaddr.
  164. EXPECT_EQ(IOAddress("10.0.0.56"), resp->getLocalAddr());
  165. // Response must not be relayed.
  166. EXPECT_FALSE(resp->isRelayed());
  167. // Make sure that the server id is present.
  168. EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
  169. // Make sure that the Routers option has been received.
  170. ASSERT_EQ(2, client.config_.routers_.size());
  171. EXPECT_EQ("10.0.0.200", client.config_.routers_[0].toText());
  172. EXPECT_EQ("10.0.0.201", client.config_.routers_[1].toText());
  173. // Make sure that the DNS Servers option has been received.
  174. ASSERT_EQ(2, client.config_.dns_servers_.size());
  175. EXPECT_EQ("10.0.0.202", client.config_.dns_servers_[0].toText());
  176. EXPECT_EQ("10.0.0.203", client.config_.dns_servers_[1].toText());
  177. // Make sure that the Log Servers option has been received.
  178. ASSERT_EQ(2, client.config_.quotes_servers_.size());
  179. EXPECT_EQ("10.0.0.200", client.config_.routers_[0].toText());
  180. EXPECT_EQ("10.0.0.201", client.config_.routers_[1].toText());
  181. // Make sure that the Quotes Servers option has been received.
  182. ASSERT_EQ(2, client.config_.log_servers_.size());
  183. EXPECT_EQ("10.0.0.202", client.config_.dns_servers_[0].toText());
  184. EXPECT_EQ("10.0.0.203", client.config_.dns_servers_[1].toText());
  185. // Check that we can send another DHCPINFORM message using
  186. // different ciaddr and we will get the configuration.
  187. client.createLease(IOAddress("10.0.0.12"), 600);
  188. // This time do not request Quotes Servers option and it should not
  189. // be returned.
  190. client.requestOptions(DHO_LOG_SERVERS);
  191. // Send DHCPINFORM.
  192. ASSERT_NO_THROW(client.doInform());
  193. // Make sure that the server responded.
  194. resp = client.getContext().response_;
  195. ASSERT_TRUE(resp);
  196. // Make sure that the server has responded with DHCPACK.
  197. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  198. // Response should have been unicast to the ciaddr.
  199. EXPECT_EQ(IOAddress("10.0.0.12"), resp->getLocalAddr());
  200. // Response must not be relayed.
  201. EXPECT_FALSE(resp->isRelayed());
  202. // Make sure that the server id is present.
  203. EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
  204. // Make sure that the Routers option has been received.
  205. ASSERT_EQ(2, client.config_.routers_.size());
  206. EXPECT_EQ("10.0.0.200", client.config_.routers_[0].toText());
  207. EXPECT_EQ("10.0.0.201", client.config_.routers_[1].toText());
  208. // Make sure that the DNS Servers option has been received.
  209. ASSERT_EQ(2, client.config_.dns_servers_.size());
  210. EXPECT_EQ("10.0.0.202", client.config_.dns_servers_[0].toText());
  211. EXPECT_EQ("10.0.0.203", client.config_.dns_servers_[1].toText());
  212. // Make sure that the Quotes Servers option hasn't been received.
  213. ASSERT_TRUE(client.config_.quotes_servers_.empty());
  214. }
  215. // This test checks that the server drops DHCPINFORM message when the
  216. // source address and ciaddr is 0.
  217. TEST_F(InformTest, directClientBroadcastNoAddress) {
  218. Dhcp4Client client;
  219. // Configure DHCP server.
  220. configure(INFORM_CONFIGS[0], *client.getServer());
  221. // Request some configuration when DHCPINFORM is sent.
  222. client.requestOptions(DHO_LOG_SERVERS, DHO_COOKIE_SERVERS);
  223. // Send DHCPINFORM message to the server.
  224. ASSERT_NO_THROW(client.doInform());
  225. // Make sure that the server dropped the message.
  226. ASSERT_FALSE(client.getContext().response_);
  227. }
  228. // Test that client's DHCPINFORM message sent to a unicast address
  229. // is received and processed by the server and that the DHCPACK is
  230. // is sent.
  231. TEST_F(InformTest, directClientUnicast) {
  232. Dhcp4Client client;
  233. // Configure DHCP server.
  234. configure(INFORM_CONFIGS[0], *client.getServer());
  235. // Preconfigure the client with the IP address.
  236. client.createLease(IOAddress("10.0.0.56"), 600);
  237. // Set remote address to unicast.
  238. client.setDestAddress(IOAddress("10.0.0.1"));
  239. // Send DHCPINFORM message to the server.
  240. ASSERT_NO_THROW(client.doInform());
  241. // Make sure that the server responded.
  242. ASSERT_TRUE(client.getContext().response_);
  243. Pkt4Ptr resp = client.getContext().response_;
  244. // Make sure that the server has responded with DHCPACK.
  245. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  246. // Response should have been unicast to the ciaddr.
  247. EXPECT_EQ(IOAddress("10.0.0.56"), resp->getLocalAddr());
  248. // Response must not be relayed.
  249. EXPECT_FALSE(resp->isRelayed());
  250. // Make sure that the server id is present.
  251. EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
  252. // Make sure that the Routers option has been received.
  253. ASSERT_EQ(2, client.config_.routers_.size());
  254. EXPECT_EQ("10.0.0.200", client.config_.routers_[0].toText());
  255. EXPECT_EQ("10.0.0.201", client.config_.routers_[1].toText());
  256. // Make sure that the DNS Servers option has been received.
  257. ASSERT_EQ(2, client.config_.dns_servers_.size());
  258. EXPECT_EQ("10.0.0.202", client.config_.dns_servers_[0].toText());
  259. EXPECT_EQ("10.0.0.203", client.config_.dns_servers_[1].toText());
  260. }
  261. // This test checks that the server responds to the source address of the
  262. // packet received from the directly connected client if the client didn't
  263. // set the ciaddr.
  264. TEST_F(InformTest, directClientNoCiaddr) {
  265. Dhcp4Client client;
  266. // Configure DHCP server.
  267. configure(INFORM_CONFIGS[0], *client.getServer());
  268. // Preconfigure the client with the IP address.
  269. client.createLease(IOAddress("10.0.0.56"), 600);
  270. // Send DHCPINFORM message (with ciaddr not set) to the server.
  271. ASSERT_NO_THROW(client.doInform(false));
  272. // Make sure that the server responded.
  273. ASSERT_TRUE(client.getContext().response_);
  274. Pkt4Ptr resp = client.getContext().response_;
  275. // Make sure that the server has responded with DHCPACK.
  276. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  277. // Response should have been unicast to the ciaddr.
  278. EXPECT_EQ(IOAddress("10.0.0.56"), resp->getLocalAddr());
  279. // Response must not be relayed.
  280. EXPECT_FALSE(resp->isRelayed());
  281. // Make sure that the server id is present.
  282. EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
  283. // Make sure that the Routers option has been received.
  284. ASSERT_EQ(2, client.config_.routers_.size());
  285. EXPECT_EQ("10.0.0.200", client.config_.routers_[0].toText());
  286. EXPECT_EQ("10.0.0.201", client.config_.routers_[1].toText());
  287. // Make sure that the DNS Servers option has been received.
  288. ASSERT_EQ(2, client.config_.dns_servers_.size());
  289. EXPECT_EQ("10.0.0.202", client.config_.dns_servers_[0].toText());
  290. EXPECT_EQ("10.0.0.203", client.config_.dns_servers_[1].toText());
  291. }
  292. // This test checks that the server receiving DHCPINFORM via relay, unicasts the
  293. // DHCPACK to the client (ciaddr).
  294. TEST_F(InformTest, relayedClient) {
  295. Dhcp4Client client;
  296. // Configure DHCP server.
  297. configure(INFORM_CONFIGS[1], *client.getServer());
  298. // Message is relayed.
  299. client.useRelay();
  300. // Request some configuration when DHCPINFORM is sent.
  301. client.requestOptions(DHO_LOG_SERVERS, DHO_COOKIE_SERVERS);
  302. // Preconfigure the client with the IP address.
  303. client.createLease(IOAddress("192.0.2.56"), 600);
  304. // Send DHCPINFORM message to the server.
  305. ASSERT_NO_THROW(client.doInform());
  306. // Make sure that the server responded.
  307. ASSERT_TRUE(client.getContext().response_);
  308. Pkt4Ptr resp = client.getContext().response_;
  309. // Make sure that the server has responded with DHCPACK.
  310. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  311. // Response should have been unicast to the ciaddr.
  312. EXPECT_EQ(IOAddress("192.0.2.56"), resp->getLocalAddr());
  313. // Response is unicast to the client, so it must not be relayed.
  314. EXPECT_FALSE(resp->isRelayed());
  315. // Make sure that the server id is present.
  316. EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
  317. // Make sure that the Routers option has been received.
  318. ASSERT_EQ(2, client.config_.routers_.size());
  319. EXPECT_EQ("192.0.2.200", client.config_.routers_[0].toText());
  320. EXPECT_EQ("192.0.2.201", client.config_.routers_[1].toText());
  321. // Make sure that the DNS Servers option has been received.
  322. ASSERT_EQ(2, client.config_.dns_servers_.size());
  323. EXPECT_EQ("192.0.2.202", client.config_.dns_servers_[0].toText());
  324. EXPECT_EQ("192.0.2.203", client.config_.dns_servers_[1].toText());
  325. // Make sure that the Log Servers option has been received.
  326. ASSERT_EQ(2, client.config_.quotes_servers_.size());
  327. EXPECT_EQ("192.0.2.200", client.config_.routers_[0].toText());
  328. EXPECT_EQ("192.0.2.201", client.config_.routers_[1].toText());
  329. // Make sure that the Quotes Servers option has been received.
  330. ASSERT_EQ(2, client.config_.log_servers_.size());
  331. EXPECT_EQ("192.0.2.202", client.config_.dns_servers_[0].toText());
  332. EXPECT_EQ("192.0.2.203", client.config_.dns_servers_[1].toText());
  333. }
  334. // This test checks that the server can respond to the DHCPINFORM message
  335. // received via relay when the ciaddr is not set.
  336. TEST_F(InformTest, relayedClientNoCiaddr) {
  337. Dhcp4Client client;
  338. // Configure DHCP server.
  339. configure(INFORM_CONFIGS[1], *client.getServer());
  340. // Message is relayed.
  341. client.useRelay();
  342. // Send DHCPINFORM message to the server.
  343. ASSERT_NO_THROW(client.doInform());
  344. // Make sure that the server responded.
  345. ASSERT_TRUE(client.getContext().response_);
  346. Pkt4Ptr resp = client.getContext().response_;
  347. // Make sure that the server has responded with DHCPACK.
  348. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  349. // Response should go through a relay as there is no ciaddr.
  350. EXPECT_EQ(IOAddress("192.0.2.2"), resp->getLocalAddr());
  351. EXPECT_EQ(IOAddress("192.0.2.2"), resp->getGiaddr());
  352. EXPECT_EQ(1, resp->getHops());
  353. // In the case when the client didn't set the ciaddr and the message
  354. // was received via relay the server sets the Broadcast flag to help
  355. // the relay forwarding the message (without yiaddr) to the client.
  356. EXPECT_EQ(BOOTP_BROADCAST, resp->getFlags() & BOOTP_BROADCAST);
  357. // Make sure that the server id is present.
  358. EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
  359. // Make sure that the Routers option has been received.
  360. ASSERT_EQ(2, client.config_.routers_.size());
  361. EXPECT_EQ("192.0.2.200", client.config_.routers_[0].toText());
  362. EXPECT_EQ("192.0.2.201", client.config_.routers_[1].toText());
  363. // Make sure that the DNS Servers option has been received.
  364. ASSERT_EQ(2, client.config_.dns_servers_.size());
  365. EXPECT_EQ("192.0.2.202", client.config_.dns_servers_[0].toText());
  366. EXPECT_EQ("192.0.2.203", client.config_.dns_servers_[1].toText());
  367. }
  368. /// This test verifies that after a client completes its INFORM exchange,
  369. /// appropriate statistics are updated.
  370. TEST_F(InformTest, statisticsInform) {
  371. Dhcp4Client client;
  372. // Configure DHCP server.
  373. configure(INFORM_CONFIGS[0], *client.getServer());
  374. // Request some configuration when DHCPINFORM is sent.
  375. client.requestOptions(DHO_LOG_SERVERS, DHO_COOKIE_SERVERS);
  376. // Preconfigure the client with the IP address.
  377. client.createLease(IOAddress("10.0.0.56"), 600);
  378. // Send DHCPINFORM message to the server.
  379. ASSERT_NO_THROW(client.doInform());
  380. // Make sure that the server responded.
  381. ASSERT_TRUE(client.getContext().response_);
  382. Pkt4Ptr resp = client.getContext().response_;
  383. // Make sure that the server has responded with DHCPACK.
  384. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  385. // Ok, let's check the statistics.
  386. using namespace isc::stats;
  387. StatsMgr& mgr = StatsMgr::instance();
  388. ObservationPtr pkt4_received = mgr.getObservation("pkt4-received");
  389. ObservationPtr pkt4_inform_received = mgr.getObservation("pkt4-inform-received");
  390. ObservationPtr pkt4_ack_sent = mgr.getObservation("pkt4-ack-sent");
  391. ObservationPtr pkt4_sent = mgr.getObservation("pkt4-sent");
  392. // All expected statistics must be present.
  393. ASSERT_TRUE(pkt4_received);
  394. ASSERT_TRUE(pkt4_inform_received);
  395. ASSERT_TRUE(pkt4_ack_sent);
  396. ASSERT_TRUE(pkt4_sent);
  397. // And they must have expected values.
  398. EXPECT_EQ(1, pkt4_received->getInteger().first);
  399. EXPECT_EQ(1, pkt4_inform_received->getInteger().first);
  400. EXPECT_EQ(1, pkt4_ack_sent->getInteger().first);
  401. EXPECT_EQ(1, pkt4_sent->getInteger().first);
  402. // Let the client send iform 4 times, which should make the server
  403. // to send 4 acks.
  404. ASSERT_NO_THROW(client.doInform());
  405. ASSERT_NO_THROW(client.doInform());
  406. ASSERT_NO_THROW(client.doInform());
  407. ASSERT_NO_THROW(client.doInform());
  408. // Let's see if the stats are properly updated.
  409. EXPECT_EQ(5, pkt4_received->getInteger().first);
  410. EXPECT_EQ(5, pkt4_inform_received->getInteger().first);
  411. EXPECT_EQ(5, pkt4_ack_sent->getInteger().first);
  412. EXPECT_EQ(5, pkt4_sent->getInteger().first);
  413. }
  414. } // end of anonymous namespace