direct_client_unittest.cc 15 KB

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