direct_client_unittest.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. // Copyright (C) 2014-2015 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 <dhcp/pkt4.h>
  9. #include <dhcp/classify.h>
  10. #include <dhcp/tests/iface_mgr_test_config.h>
  11. #include <dhcpsrv/cfgmgr.h>
  12. #include <dhcpsrv/cfg_subnets4.h>
  13. #include <dhcpsrv/lease_mgr_factory.h>
  14. #include <dhcpsrv/subnet.h>
  15. #include <dhcp4/json_config_parser.h>
  16. #include <dhcp4/tests/dhcp4_client.h>
  17. #include <dhcp4/tests/dhcp4_test_utils.h>
  18. #include <gtest/gtest.h>
  19. #include <string>
  20. using namespace isc;
  21. using namespace isc::asiolink;
  22. using namespace isc::data;
  23. using namespace isc::dhcp;
  24. using namespace isc::dhcp::test;
  25. namespace {
  26. /// @brief Test fixture class for testing message processing from directly
  27. /// connected clients.
  28. ///
  29. /// This class provides mechanisms for testing processing of DHCPv4 messages
  30. /// from directly connected clients.
  31. class DirectClientTest : public Dhcpv4SrvTest {
  32. public:
  33. /// @brief Constructor.
  34. ///
  35. /// Initializes DHCPv4 server object used by various tests.
  36. DirectClientTest();
  37. /// @brief Configures the server with one subnet.
  38. ///
  39. /// This creates new configuration for the DHCPv4 with one subnet having
  40. /// a specified prefix.
  41. ///
  42. /// The subnet parameters (such as options, timers etc.) are arbitrarily
  43. /// selected. The subnet and pool mask is always /24. The real configuration
  44. /// would exclude .0 (network address) and .255 (broadcast address), but we
  45. /// ignore that fact for the sake of test simplicity.
  46. ///
  47. /// @param prefix Prefix for a subnet.
  48. void configureSubnet(const std::string& prefix);
  49. /// @brief Configures the server with two subnets.
  50. ///
  51. /// This function configures DHCPv4 server with two different subnets.
  52. /// The subnet parameters (such as options, timers etc.) are arbitrarily
  53. /// selected. The subnet and pool mask is /24. The real configuration
  54. /// would exclude .0 (network address) and .255 (broadcast address), but we
  55. /// ignore that fact for the sake of test simplicity.
  56. ///
  57. /// @param prefix1 Prefix of the first subnet to be configured.
  58. /// @param prefix2 Prefix of the second subnet to be configured.
  59. void configureTwoSubnets(const std::string& prefix1,
  60. const std::string& prefix2);
  61. /// @brief Creates simple message from a client.
  62. ///
  63. /// This function creates a DHCPv4 message having a specified type
  64. /// (e.g. Discover, Request) and sets some properties of this
  65. /// message: client identifier, address and interface. The copy of
  66. /// this message is then created by parsing wire data of the original
  67. /// message. This simulates the case when the message is received and
  68. /// parsed by the server.
  69. ///
  70. /// @param msg_type Type of the message to be created.
  71. /// @param iface Name of the interface on which the message has been
  72. /// "received" by the server.
  73. ///
  74. /// @return Generated message.
  75. Pkt4Ptr createClientMessage(const uint16_t msg_type,
  76. const std::string& iface);
  77. /// @brief Creates simple message from a client.
  78. ///
  79. /// This function configures a client's message by adding client identifier,
  80. /// setting interface and addresses. The copy of this message is then
  81. /// created by parsing wire data of the original message. This simulates the
  82. /// case when the message is received and parsed by the server.
  83. ///
  84. /// @param msg Caller supplied message to be configured. This object must
  85. /// not be NULL.
  86. /// @param iface Name of the interface on which the message has been
  87. /// "received" by the server.
  88. ///
  89. /// @return Configured and parsed message.
  90. Pkt4Ptr createClientMessage(const Pkt4Ptr &msg, const std::string& iface);
  91. /// @brief classes the client belongs to
  92. ///
  93. /// This is empty in most cases, but it is needed as a parameter for all
  94. /// getSubnet4() calls.
  95. ClientClasses classify_;
  96. };
  97. DirectClientTest::DirectClientTest() : Dhcpv4SrvTest() {
  98. }
  99. void
  100. DirectClientTest::configureSubnet(const std::string& prefix) {
  101. std::ostringstream config;
  102. config << "{ \"interfaces-config\": {"
  103. " \"interfaces\": [ \"*\" ]"
  104. "},"
  105. "\"rebind-timer\": 2000, "
  106. "\"renew-timer\": 1000, "
  107. "\"option-data\": [ ],"
  108. "\"subnet4\": [ { "
  109. " \"pools\": [ { \"pool\": \"" << prefix << "/24\" } ],"
  110. " \"subnet\": \"" << prefix << "/24\", "
  111. " \"rebind-timer\": 2000, "
  112. " \"renew-timer\": 1000, "
  113. " \"valid-lifetime\": 4000"
  114. "} ],"
  115. "\"valid-lifetime\": 4000 }";
  116. configure(config.str());
  117. }
  118. void
  119. DirectClientTest::configureTwoSubnets(const std::string& prefix1,
  120. const std::string& prefix2) {
  121. std::ostringstream config;
  122. config << "{ \"interfaces-config\": {"
  123. " \"interfaces\": [ \"*\" ]"
  124. "},"
  125. "\"rebind-timer\": 2000, "
  126. "\"renew-timer\": 1000, "
  127. "\"option-data\": [ ],"
  128. "\"subnet4\": [ { "
  129. " \"pools\": [ { \"pool\": \"" << prefix1 << "/24\" } ],"
  130. " \"subnet\": \"" << prefix1 << "/24\", "
  131. " \"rebind-timer\": 2000, "
  132. " \"renew-timer\": 1000, "
  133. " \"valid-lifetime\": 4000"
  134. " },"
  135. "{ "
  136. " \"pools\": [ { \"pool\": \"" << prefix2 << "/24\" } ],"
  137. " \"subnet\": \"" << prefix2 << "/24\", "
  138. " \"rebind-timer\": 2000, "
  139. " \"renew-timer\": 1000, "
  140. " \"valid-lifetime\": 4000"
  141. "} ],"
  142. "\"valid-lifetime\": 4000 }";
  143. configure(config.str());
  144. }
  145. Pkt4Ptr
  146. DirectClientTest:: createClientMessage(const uint16_t msg_type,
  147. const std::string& iface) {
  148. // Create a source packet.
  149. Pkt4Ptr msg = Pkt4Ptr(new Pkt4(msg_type, 1234));
  150. return (createClientMessage(msg, iface));
  151. }
  152. Pkt4Ptr
  153. DirectClientTest::createClientMessage(const Pkt4Ptr& msg,
  154. const std::string& iface) {
  155. msg->setRemoteAddr(IOAddress("255.255.255.255"));
  156. msg->addOption(generateClientId());
  157. msg->setIface(iface);
  158. // Create copy of this packet by parsing its wire data. Make sure that the
  159. // local and remote address are set like it was a message sent from the
  160. // directly connected client.
  161. Pkt4Ptr received;
  162. createPacketFromBuffer(msg, received);
  163. received->setIface(iface);
  164. received->setLocalAddr(IOAddress("255.255.255.255"));
  165. received->setRemoteAddr(IOAddress("0.0.0.0"));
  166. return (received);
  167. }
  168. // This test checks that the message from directly connected client
  169. // is processed and that client is offered IPv4 address from the subnet which
  170. // is suitable for the local interface on which the client's message is
  171. // received. This test uses two subnets, with two active interfaces which IP
  172. // addresses belong to these subnets. The address offered to the client
  173. // which message has been sent over eth0 should belong to a different
  174. // subnet than the address offered for the client sending its message
  175. // via eth1.
  176. TEST_F(DirectClientTest, twoSubnets) {
  177. // Configure IfaceMgr with fake interfaces lo, eth0 and eth1.
  178. IfaceMgrTestConfig iface_config(true);
  179. // After creating interfaces we have to open sockets as it is required
  180. // by the message processing code.
  181. ASSERT_NO_THROW(IfaceMgr::instance().openSockets4());
  182. // Add two subnets: address on eth0 belongs to the second subnet,
  183. // address on eth1 belongs to the first subnet.
  184. ASSERT_NO_FATAL_FAILURE(configureTwoSubnets("192.0.2.0", "10.0.0.0"));
  185. // Create Discover and simulate reception of this message through eth0.
  186. Pkt4Ptr dis = createClientMessage(DHCPDISCOVER, "eth0");
  187. srv_.fakeReceive(dis);
  188. // Create Request and simulate reception of this message through eth1.
  189. Pkt4Ptr req = createClientMessage(DHCPREQUEST, "eth1");
  190. srv_.fakeReceive(req);
  191. // Process clients' messages.
  192. srv_.run();
  193. // Check that the server did send responses.
  194. ASSERT_EQ(2, srv_.fake_sent_.size());
  195. // Make sure that we received a response.
  196. Pkt4Ptr response = srv_.fake_sent_.front();
  197. ASSERT_TRUE(response);
  198. srv_.fake_sent_.pop_front();
  199. // Client should get an Offer (not a NAK).
  200. ASSERT_EQ(DHCPOFFER, response->getType());
  201. // Check that the offered address belongs to the suitable subnet.
  202. Subnet4Ptr subnet = CfgMgr::instance().getCurrentCfg()->
  203. getCfgSubnets4()->selectSubnet(response->getYiaddr());
  204. ASSERT_TRUE(subnet);
  205. EXPECT_EQ("10.0.0.0", subnet->get().first.toText());
  206. // A client that sent Request over the other interface should get Ack.
  207. response = srv_.fake_sent_.front();
  208. ASSERT_TRUE(response);
  209. // Client should get an Ack (not a NAK).
  210. ASSERT_EQ(DHCPACK, response->getType());
  211. // Check that the offered address belongs to the suitable subnet.
  212. subnet = CfgMgr::instance().getCurrentCfg()->
  213. getCfgSubnets4()->selectSubnet(response->getYiaddr());
  214. ASSERT_TRUE(subnet);
  215. EXPECT_EQ("192.0.2.0", subnet->get().first.toText());
  216. }
  217. // This test checks that server selects a subnet when receives a message
  218. // through an interface for which the subnet has been configured. This
  219. // interface has IPv4 address assigned which belongs to this subnet.
  220. // This test also verifies that when the message is received through
  221. // the interface for which there is no suitable subnet, the message
  222. // is discarded.
  223. TEST_F(DirectClientTest, oneSubnet) {
  224. // Configure IfaceMgr with fake interfaces lo, eth0 and eth1.
  225. IfaceMgrTestConfig iface_config(true);
  226. // After creating interfaces we have to open sockets as it is required
  227. // by the message processing code.
  228. ASSERT_NO_THROW(IfaceMgr::instance().openSockets4());
  229. // Add a subnet which will be selected when a message from directly
  230. // connected client is received through interface eth0.
  231. ASSERT_NO_FATAL_FAILURE(configureSubnet("10.0.0.0"));
  232. // Create Discover and simulate reception of this message through eth0.
  233. Pkt4Ptr dis = createClientMessage(DHCPDISCOVER, "eth0");
  234. srv_.fakeReceive(dis);
  235. // Create Request and simulate reception of this message through eth1.
  236. Pkt4Ptr req = createClientMessage(DHCPDISCOVER, "eth1");
  237. srv_.fakeReceive(req);
  238. // Process clients' messages.
  239. srv_.run();
  240. // Check that the server sent one response for the message received
  241. // through eth0. The other client's message should be discarded.
  242. ASSERT_EQ(1, srv_.fake_sent_.size());
  243. // Check the response. The first Discover was sent via eth0 for which
  244. // the subnet has been configured.
  245. Pkt4Ptr response = srv_.fake_sent_.front();
  246. ASSERT_TRUE(response);
  247. srv_.fake_sent_.pop_front();
  248. // Since Discover has been received through the interface for which
  249. // the subnet has been configured, the server should respond with
  250. // an Offer message.
  251. ASSERT_EQ(DHCPOFFER, response->getType());
  252. // Check that the offered address belongs to the suitable subnet.
  253. Subnet4Ptr subnet = CfgMgr::instance().getCurrentCfg()->
  254. getCfgSubnets4()->selectSubnet(response->getYiaddr());
  255. ASSERT_TRUE(subnet);
  256. EXPECT_EQ("10.0.0.0", subnet->get().first.toText());
  257. }
  258. // This test verifies that the server uses ciaddr to select a subnet for a
  259. // client which renews its lease.
  260. TEST_F(DirectClientTest, renew) {
  261. // Configure IfaceMgr with fake interfaces lo, eth0 and eth1.
  262. IfaceMgrTestConfig iface_config(true);
  263. // After creating interfaces we have to open sockets as it is required
  264. // by the message processing code.
  265. ASSERT_NO_THROW(IfaceMgr::instance().openSockets4());
  266. // Add a subnet.
  267. ASSERT_NO_FATAL_FAILURE(configureSubnet("10.0.0.0"));
  268. // Create the DHCPv4 client.
  269. Dhcp4Client client;
  270. client.useRelay(false);
  271. // Obtain the lease using the 4-way exchange.
  272. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<IOAddress>(new IOAddress("10.0.0.10"))));
  273. ASSERT_EQ("10.0.0.10", client.config_.lease_.addr_.toText());
  274. // Put the client into the renewing state.
  275. client.setState(Dhcp4Client::RENEWING);
  276. // Renew, and make sure we have obtained the same address.
  277. ASSERT_NO_THROW(client.doRequest());
  278. ASSERT_TRUE(client.getContext().response_);
  279. EXPECT_EQ(DHCPACK, static_cast<int>(client.getContext().response_->getType()));
  280. EXPECT_EQ("10.0.0.10", client.config_.lease_.addr_.toText());
  281. }
  282. // This test verifies that when a client in the Rebinding state broadcasts
  283. // a Request message through an interface for which a subnet is configured,
  284. // the server responds to this Request. It also verifies that when such a
  285. // Request is sent through the interface for which there is no subnet configured
  286. // the client's message is discarded.
  287. TEST_F(DirectClientTest, rebind) {
  288. // Configure IfaceMgr with fake interfaces lo, eth0 and eth1.
  289. IfaceMgrTestConfig iface_config(true);
  290. // After creating interfaces we have to open sockets as it is required
  291. // by the message processing code.
  292. ASSERT_NO_THROW(IfaceMgr::instance().openSockets4());
  293. // Add a subnet.
  294. ASSERT_NO_FATAL_FAILURE(configureSubnet("10.0.0.0"));
  295. // Create the DHCPv4 client.
  296. Dhcp4Client client;
  297. client.useRelay(false);
  298. // Obtain the lease using the 4-way exchange.
  299. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<IOAddress>(new IOAddress("10.0.0.10"))));
  300. ASSERT_EQ("10.0.0.10", client.config_.lease_.addr_.toText());
  301. // Put the client into the rebinding state.
  302. client.setState(Dhcp4Client::REBINDING);
  303. // Broadcast Request through an interface for which there is no subnet
  304. // configured. This message should be discarded by the server.
  305. client.setIfaceName("eth1");
  306. ASSERT_NO_THROW(client.doRequest());
  307. EXPECT_FALSE(client.getContext().response_);
  308. // Send Rebind over the correct interface, and make sure we have obtained
  309. // the same address.
  310. client.setIfaceName("eth0");
  311. ASSERT_NO_THROW(client.doRequest());
  312. ASSERT_TRUE(client.getContext().response_);
  313. EXPECT_EQ(DHCPACK, static_cast<int>(client.getContext().response_->getType()));
  314. EXPECT_EQ("10.0.0.10", client.config_.lease_.addr_.toText());
  315. }
  316. }