dhcp4_test_utils.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. // Copyright (C) 2013 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. /// @file dhcp4_test_utils.h
  15. ///
  16. /// @brief This file contains utility classes used for DHCPv4 server testing
  17. #ifndef DHCP4_TEST_UTILS_H
  18. #define DHCP4_TEST_UTILS_H
  19. #include <gtest/gtest.h>
  20. #include <dhcp/iface_mgr.h>
  21. #include <dhcp/option4_addrlst.h>
  22. #include <dhcp/pkt4.h>
  23. #include <dhcp/pkt_filter.h>
  24. #include <dhcp/pkt_filter_inet.h>
  25. #include <dhcpsrv/subnet.h>
  26. #include <dhcpsrv/lease.h>
  27. #include <dhcp4/dhcp4_srv.h>
  28. #include <asiolink/io_address.h>
  29. #include <config/ccsession.h>
  30. #include <list>
  31. #include <boost/shared_ptr.hpp>
  32. namespace isc {
  33. namespace dhcp {
  34. namespace test {
  35. /// @brief Dummy Packet Filtering class.
  36. ///
  37. /// This class reports capability to respond directly to the client which
  38. /// doesn't have address configured yet.
  39. ///
  40. /// All packet and socket handling functions do nothing because they are not
  41. /// used in unit tests.
  42. class PktFilterTest : public PktFilter {
  43. public:
  44. /// @brief Constructor.
  45. ///
  46. /// Sets the 'direct response' capability to true.
  47. PktFilterTest()
  48. : direct_resp_supported_(true) {
  49. }
  50. /// @brief Reports 'direct response' capability.
  51. ///
  52. /// @return always true.
  53. virtual bool isDirectResponseSupported() const {
  54. return (direct_resp_supported_);
  55. }
  56. /// Does nothing.
  57. virtual SocketInfo openSocket(const Iface&,
  58. const isc::asiolink::IOAddress& addr,
  59. const uint16_t port, const bool, const bool) {
  60. return (SocketInfo(addr, port, 0));
  61. }
  62. /// Does nothing.
  63. virtual Pkt4Ptr receive(const Iface&, const SocketInfo&) {
  64. return Pkt4Ptr();
  65. }
  66. /// Does nothing.
  67. virtual int send(const Iface&, uint16_t, const Pkt4Ptr&) {
  68. return (0);
  69. }
  70. /// @brief Holds a boolean value which indicates whether direct response
  71. /// capability is supported (true) or not (false).
  72. bool direct_resp_supported_;
  73. };
  74. typedef boost::shared_ptr<PktFilterTest> PktFilterTestPtr;
  75. class Dhcpv4SrvTest : public ::testing::Test {
  76. public:
  77. /// @brief Constructor
  78. ///
  79. /// Initializes common objects used in many tests.
  80. /// Also sets up initial configuration in CfgMgr.
  81. Dhcpv4SrvTest();
  82. /// @brief destructor
  83. virtual ~Dhcpv4SrvTest();
  84. /// @brief Add 'Parameter Request List' option to the packet.
  85. ///
  86. /// This function adds PRL option comprising the following option codes:
  87. /// - 5 - Name Server
  88. /// - 15 - Domain Name
  89. /// - 7 - Log Server
  90. /// - 8 - Quotes Server
  91. /// - 9 - LPR Server
  92. ///
  93. /// @param pkt packet to add PRL option to.
  94. void addPrlOption(Pkt4Ptr& pkt);
  95. /// @brief Configures options being requested in the PRL option.
  96. ///
  97. /// The lpr-servers option is NOT configured here although it is
  98. /// added to the 'Parameter Request List' option in the
  99. /// \ref addPrlOption. When requested option is not configured
  100. /// the server should not return it in its response. The goal
  101. /// of not configuring the requested option is to verify that
  102. /// the server will not return it.
  103. void configureRequestedOptions();
  104. /// @brief checks that the response matches request
  105. /// @param q query (client's message)
  106. /// @param a answer (server's message)
  107. void messageCheck(const Pkt4Ptr& q, const Pkt4Ptr& a);
  108. /// @brief Check that certain basic (always added when lease is acquired)
  109. /// are present in a message.
  110. ///
  111. /// @param pkt A message to be checked.
  112. /// @return Assertion result which indicates whether test passed or failed.
  113. ::testing::AssertionResult basicOptionsPresent(const Pkt4Ptr& pkt);
  114. /// @brief Check that certain basic (always added when lease is acquired)
  115. /// are not present.
  116. ///
  117. /// @param pkt A packet to be checked.
  118. /// @return Assertion result which indicates whether test passed or failed.
  119. ::testing::AssertionResult noBasicOptions(const Pkt4Ptr& pkt);
  120. /// @brief Check that certain requested options are present in the message.
  121. ///
  122. /// @param pkt A message to be checked.
  123. /// @return Assertion result which indicates whether test passed or failed.
  124. ::testing::AssertionResult requestedOptionsPresent(const Pkt4Ptr& pkt);
  125. /// @brief Check that certain options (requested with PRL option)
  126. /// are not present.
  127. ///
  128. /// @param pkt A packet to be checked.
  129. /// @return Assertion result which indicates whether test passed or failed.
  130. ::testing::AssertionResult noRequestedOptions(const Pkt4Ptr& pkt);
  131. /// @brief generates client-id option
  132. ///
  133. /// Generate client-id option of specified length
  134. /// Ids with different lengths are sufficent to generate
  135. /// unique ids. If more fine grained control is required,
  136. /// tests generate client-ids on their own.
  137. /// Sets client_id_ field.
  138. /// @param size size of the client-id to be generated
  139. OptionPtr generateClientId(size_t size = 4);
  140. /// @brief generate hardware address
  141. ///
  142. /// @param size size of the generated MAC address
  143. /// @param pointer to Hardware Address object
  144. HWAddrPtr generateHWAddr(size_t size = 6);
  145. /// Check that address was returned from proper range, that its lease
  146. /// lifetime is correct, that T1 and T2 are returned properly
  147. /// @param rsp response to be checked
  148. /// @param subnet subnet that should be used to verify assigned address
  149. /// and options
  150. /// @param t1_mandatory is T1 mandatory?
  151. /// @param t2_mandatory is T2 mandatory?
  152. void checkAddressParams(const Pkt4Ptr& rsp, const SubnetPtr subnet,
  153. bool t1_mandatory = false,
  154. bool t2_mandatory = false);
  155. /// @brief Basic checks for generated response (message type and trans-id).
  156. ///
  157. /// @param rsp response packet to be validated
  158. /// @param expected_message_type expected message type
  159. /// @param expected_transid expected transaction-id
  160. void checkResponse(const Pkt4Ptr& rsp, uint8_t expected_message_type,
  161. uint32_t expected_transid);
  162. /// @brief Checks if the lease sent to client is present in the database
  163. ///
  164. /// @param rsp response packet to be validated
  165. /// @param client_id expected client-identifier (or NULL)
  166. /// @param HWAddr expected hardware address (not used now)
  167. /// @param expected_addr expected address
  168. Lease4Ptr checkLease(const Pkt4Ptr& rsp, const OptionPtr& client_id,
  169. const HWAddrPtr&,
  170. const isc::asiolink::IOAddress& expected_addr);
  171. /// @brief Checks if server response (OFFER, ACK, NAK) includes proper server-id
  172. /// @param rsp response packet to be validated
  173. /// @param expected_srvid expected value of server-id
  174. void checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid);
  175. /// @brief Checks if server response (OFFER, ACK, NAK) includes proper client-id
  176. ///
  177. /// This method follows values reported by CfgMgr in echoClientId() method.
  178. /// Depending on its configuration, the client-id is either mandatory or
  179. /// forbidden to appear in the response.
  180. ///
  181. /// @param rsp response packet to be validated
  182. /// @param expected_clientid expected value of client-id
  183. void checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid);
  184. /// @brief sets default fields in a captured packet
  185. ///
  186. /// Sets UDP ports, addresses and interface.
  187. ///
  188. /// @param pkt packet to have default fields set
  189. void captureSetDefaultFields(const Pkt4Ptr& pkt);
  190. /// @brief returns captured DISCOVER that went through a relay
  191. ///
  192. /// See method code for a detailed explanation.
  193. ///
  194. /// @return relayed DISCOVER
  195. Pkt4Ptr captureRelayedDiscover();
  196. /// @brief Create packet from output buffer of another packet.
  197. ///
  198. /// This function creates a packet using an output buffer from another
  199. /// packet. This imitates reception of a packet from the wire. The
  200. /// unpack function is then called to parse packet contents and to
  201. /// create a collection of the options carried by this packet.
  202. ///
  203. /// This function is useful for unit tests which verify that the received
  204. /// packet is parsed correctly. In those cases it is usually inappropriate
  205. /// to create an instance of the packet, add options, set packet
  206. /// fields and use such packet as an input to processDiscover or
  207. /// processRequest function. This is because, such a packet has certain
  208. /// options already initialized and there is no way to verify that they
  209. /// have been initialized when packet instance was created or wire buffer
  210. /// processing. By creating a packet from the buffer we guarantee that the
  211. /// new packet is entirely initialized during wire data parsing.
  212. ///
  213. /// @param src_pkt A source packet, to be copied.
  214. /// @param [out] dst_pkt A destination packet.
  215. ///
  216. /// @return assertion result indicating if a function completed with
  217. /// success or failure.
  218. static ::testing::AssertionResult
  219. createPacketFromBuffer(const Pkt4Ptr& src_pkt,
  220. Pkt4Ptr& dst_pkt);
  221. /// @brief generates a DHCPv4 packet based on provided hex string
  222. ///
  223. /// @return created packet
  224. Pkt4Ptr packetFromCapture(const std::string& hex_string);
  225. /// @brief This function cleans up after the test.
  226. virtual void TearDown();
  227. /// @brief A subnet used in most tests
  228. Subnet4Ptr subnet_;
  229. /// @brief A pool used in most tests
  230. Pool4Ptr pool_;
  231. /// @brief A client-id used in most tests
  232. ClientIdPtr client_id_;
  233. int rcode_;
  234. isc::data::ConstElementPtr comment_;
  235. };
  236. /// @brief Test fixture class to be used for tests which require fake
  237. /// interfaces.
  238. ///
  239. /// The DHCPv4 server must always append the server identifier to its response.
  240. /// The server identifier is typically an IP address assigned to the interface
  241. /// on which the query has been received. The DHCPv4 server uses IfaceMgr to
  242. /// check this address. In order to test this functionality, a set of interfaces
  243. /// must be known to the test. This test fixture class creates a set of well
  244. /// known (fake) interfaces which can be assigned to the test DHCPv4 messages
  245. /// so as the response (including server identifier) can be validated.
  246. /// The real interfaces are removed from the IfaceMgr in the constructor and
  247. /// they are re-assigned in the destructor.
  248. class Dhcpv4SrvFakeIfaceTest : public Dhcpv4SrvTest {
  249. public:
  250. /// @brief Constructor.
  251. ///
  252. /// Creates a set of fake interfaces:
  253. /// - lo, index: 0, address: 127.0.0.1
  254. /// - eth0, index: 1, address: 192.0.3.1
  255. /// - eth1, index: 2, address: 10.0.0.1
  256. ///
  257. /// These interfaces replace the real interfaces detected by the IfaceMgr.
  258. Dhcpv4SrvFakeIfaceTest();
  259. /// @brief Restores the original interface configuration.
  260. virtual void TearDown();
  261. /// @brief Creates an instance of the interface.
  262. ///
  263. /// @param name Name of the interface.
  264. /// @param ifindex Index of the interface.
  265. /// @param addr IP address assigned to the interface, represented as string.
  266. ///
  267. /// @return Iface Instance of the interface.
  268. static Iface createIface(const std::string& name, const int ifindex,
  269. const std::string& addr);
  270. /// @brief Tests if Discover or Request message is processed correctly
  271. ///
  272. /// This test verifies that the Parameter Request List option is handled
  273. /// correctly, i.e. it checks that certain options are present in the
  274. /// server's response when they are requested and that they are not present
  275. /// when they are not requested or NAK occurs.
  276. ///
  277. /// @todo We need an additional test for PRL option using real traffic
  278. /// capture.
  279. ///
  280. /// @param msg_type DHCPDISCOVER or DHCPREQUEST
  281. void testDiscoverRequest(const uint8_t msg_type);
  282. /// @brief Holds a pointer to the packet filter object currently used
  283. /// by the IfaceMgr.
  284. PktFilterTestPtr current_pkt_filter_;
  285. };
  286. /// @brief "Naked" DHCPv4 server, exposes internal fields
  287. class NakedDhcpv4Srv: public Dhcpv4Srv {
  288. public:
  289. /// @brief Constructor.
  290. ///
  291. /// This constructor disables default modes of operation used by the
  292. /// Dhcpv4Srv class:
  293. /// - Send/receive broadcast messages through sockets on interfaces
  294. /// which support broadcast traffic.
  295. /// - Direct DHCPv4 traffic - communication with clients which do not
  296. /// have IP address assigned yet.
  297. ///
  298. /// Enabling these modes requires root privilges so they must be
  299. /// disabled for unit testing.
  300. ///
  301. /// Note, that disabling broadcast options on sockets does not impact
  302. /// the operation of these tests because they use local loopback
  303. /// interface which doesn't have broadcast capability anyway. It rather
  304. /// prevents setting broadcast options on other (broadcast capable)
  305. /// sockets which are opened on other interfaces in Dhcpv4Srv constructor.
  306. ///
  307. /// The Direct DHCPv4 Traffic capability can be disabled here because
  308. /// it is tested with PktFilterLPFTest unittest. The tests which belong
  309. /// to PktFilterLPFTest can be enabled on demand when root privileges can
  310. /// be guaranteed.
  311. ///
  312. /// @param port port number to listen on; the default value 0 indicates
  313. /// that sockets should not be opened.
  314. NakedDhcpv4Srv(uint16_t port = 0)
  315. : Dhcpv4Srv(port, "type=memfile", false, false) {
  316. // Create fixed server id.
  317. server_id_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
  318. asiolink::IOAddress("192.0.3.1")));
  319. }
  320. /// @brief Returns fixed server identifier assigned to the naked server
  321. /// instance.
  322. OptionPtr getServerID() const {
  323. return (server_id_);
  324. }
  325. /// @brief fakes packet reception
  326. /// @param timeout ignored
  327. ///
  328. /// The method receives all packets queued in receive queue, one after
  329. /// another. Once the queue is empty, it initiates the shutdown procedure.
  330. ///
  331. /// See fake_received_ field for description
  332. virtual Pkt4Ptr receivePacket(int /*timeout*/) {
  333. // If there is anything prepared as fake incoming traffic, use it
  334. if (!fake_received_.empty()) {
  335. Pkt4Ptr pkt = fake_received_.front();
  336. fake_received_.pop_front();
  337. return (pkt);
  338. }
  339. // If not, just trigger shutdown and return immediately
  340. shutdown();
  341. return (Pkt4Ptr());
  342. }
  343. /// @brief fake packet sending
  344. ///
  345. /// Pretend to send a packet, but instead just store it in fake_send_ list
  346. /// where test can later inspect server's response.
  347. virtual void sendPacket(const Pkt4Ptr& pkt) {
  348. fake_sent_.push_back(pkt);
  349. }
  350. /// @brief adds a packet to fake receive queue
  351. ///
  352. /// See fake_received_ field for description
  353. void fakeReceive(const Pkt4Ptr& pkt) {
  354. pkt->setIface("eth0");
  355. fake_received_.push_back(pkt);
  356. }
  357. virtual ~NakedDhcpv4Srv() {
  358. }
  359. /// @brief Dummy server identifier option used by various tests.
  360. OptionPtr server_id_;
  361. /// @brief packets we pretend to receive
  362. ///
  363. /// Instead of setting up sockets on interfaces that change between OSes, it
  364. /// is much easier to fake packet reception. This is a list of packets that
  365. /// we pretend to have received. You can schedule new packets to be received
  366. /// using fakeReceive() and NakedDhcpv4Srv::receivePacket() methods.
  367. std::list<Pkt4Ptr> fake_received_;
  368. std::list<Pkt4Ptr> fake_sent_;
  369. using Dhcpv4Srv::adjustIfaceData;
  370. using Dhcpv4Srv::appendServerID;
  371. using Dhcpv4Srv::processDiscover;
  372. using Dhcpv4Srv::processRequest;
  373. using Dhcpv4Srv::processRelease;
  374. using Dhcpv4Srv::processDecline;
  375. using Dhcpv4Srv::processInform;
  376. using Dhcpv4Srv::processClientName;
  377. using Dhcpv4Srv::computeDhcid;
  378. using Dhcpv4Srv::createNameChangeRequests;
  379. using Dhcpv4Srv::sanityCheck;
  380. using Dhcpv4Srv::srvidToString;
  381. using Dhcpv4Srv::unpackOptions;
  382. using Dhcpv4Srv::name_change_reqs_;
  383. };
  384. }; // end of isc::dhcp::test namespace
  385. }; // end of isc::dhcp namespace
  386. }; // end of isc namespace
  387. #endif // DHCP4_TEST_UTILS_H