dhcp4_client.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  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. #ifndef DHCP4_CLIENT_H
  15. #define DHCP4_CLIENT_H
  16. #include <asiolink/io_address.h>
  17. #include <dhcp/hwaddr.h>
  18. #include <dhcp/pkt4.h>
  19. #include <dhcp4/tests/dhcp4_test_utils.h>
  20. #include <util/optional_value.h>
  21. #include <boost/noncopyable.hpp>
  22. #include <boost/shared_ptr.hpp>
  23. #include <set>
  24. namespace isc {
  25. namespace dhcp {
  26. namespace test {
  27. /// @brief General error emitted by the DHCP4 test client.
  28. class Dhcp4ClientError : public isc::Exception {
  29. public:
  30. Dhcp4ClientError(const char* file, size_t line, const char* what) :
  31. isc::Exception(file, line, what) { };
  32. };
  33. /// @brief DHCPv4 client used for unit testing.
  34. ///
  35. /// This class implements a DHCPv4 "client" which interoperates with the
  36. /// @c NakedDhcpv4Srv class. It calls @c NakedDhcpv4Srv::fakeReceive to
  37. /// deliver client messages to the server for processing. The server places
  38. /// the response in the @c NakedDhcpv4Srv::fake_sent_ container. The client
  39. /// pops messages from this container which simulates reception of the
  40. /// response from the server.
  41. ///
  42. /// The client maintains the leases it acquired from the server.
  43. ///
  44. /// The client exposes a set of functions which simulate different exchange
  45. /// types between the client and the server. It also provides the access to
  46. /// the objects encapsulating responses from the server so as it is possible
  47. /// to verify from the unit test that the server's response is correct.
  48. class Dhcp4Client : public boost::noncopyable {
  49. public:
  50. /// @brief States of the DHCP client.
  51. enum State {
  52. SELECTING,
  53. INIT_REBOOT,
  54. RENEWING,
  55. REBINDING
  56. };
  57. /// @brief Holds the DHCPv4 messages taking part in transaction between
  58. /// the client and the server.
  59. struct Context {
  60. /// @brief Holds the last sent message from the client to the server.
  61. Pkt4Ptr query_;
  62. /// @brief Holds the last sent message by the server to the client.
  63. Pkt4Ptr response_;
  64. };
  65. /// @brief Holds the configuration of the client received from the
  66. /// DHCP server.
  67. struct Configuration {
  68. /// @brief Holds IP addresses received in the Routers option.
  69. Option4AddrLst::AddressContainer routers_;
  70. /// @brief Holds IP addresses received in the DNS Servers option.
  71. Option4AddrLst::AddressContainer dns_servers_;
  72. /// @brief Holds IP addresses received in the Log Servers option.
  73. Option4AddrLst::AddressContainer log_servers_;
  74. /// @brief Holds IP addresses received in the Quotes Servers option.
  75. Option4AddrLst::AddressContainer quotes_servers_;
  76. /// @brief Holds a lease obtained by the client.
  77. Lease4 lease_;
  78. /// @brief Holds server id of the server which responded to the client's
  79. /// request.
  80. asiolink::IOAddress serverid_;
  81. /// @brief Constructor.
  82. Configuration();
  83. /// @brief Sets configuration values to defaults.
  84. void reset();
  85. };
  86. /// @brief Creates a new client.
  87. ///
  88. /// @param Initial client's state.
  89. Dhcp4Client(const State& state = SELECTING);
  90. /// @brief Creates a new client that communicates with a specified server.
  91. ///
  92. /// @param srv An instance of the DHCPv4 server to be used.
  93. /// @param state Initial client's state.
  94. Dhcp4Client(boost::shared_ptr<NakedDhcpv4Srv> srv,
  95. const State& state = SELECTING);
  96. /// @brief Creates a lease for the client using the specified address
  97. /// and valid lifetime.
  98. ///
  99. /// This method creates the lease using the specified address and
  100. /// valid lease lifetime. The client will use this lease in any
  101. /// future communication with the DHCP server. One of the use cases
  102. /// for this method is to pre-configure the client with the explicitly
  103. /// given address before it sends the DHCPINFORM to the DHCP server.
  104. /// The client will inject the leased address into the ciaddr field
  105. /// of the DHCPINFORM message.
  106. ///
  107. /// @param addr Lease address.
  108. /// @param valid_lft Valid lifetime.
  109. void createLease(const asiolink::IOAddress& addr, const uint32_t valid_lft);
  110. /// @brief Sends DHCPDISCOVER message to the server and receives response.
  111. ///
  112. /// The message being sent to the server includes Parameter Request List
  113. /// option if any options to be requested have been specified using the
  114. /// @c requestOptions or @c requestOption methods.
  115. ///
  116. /// The configuration returned by the server in the DHCPOFFER message is
  117. /// NOT stored in the client configuration: @c config_.
  118. ///
  119. /// @param requested_addr A pointer to the IP Address to be sent in the
  120. /// Requested IP Address option or NULL if the option should not be
  121. /// included.
  122. void doDiscover(const boost::shared_ptr<asiolink::IOAddress>&
  123. requested_addr = boost::shared_ptr<asiolink::IOAddress>());
  124. /// @brief Perform 4-way exchange with a server.
  125. ///
  126. /// This method calls @c doDiscover and @c doRequest to perform the 4-way
  127. /// exchange with the server.
  128. ///
  129. /// @param requested_addr A pointer to the address to be requested using the
  130. /// Requested IP Address option.
  131. void doDORA(const boost::shared_ptr<asiolink::IOAddress>&
  132. requested_addr = boost::shared_ptr<asiolink::IOAddress>());
  133. /// @brief Sends DHCPINFORM message to the server and receives response.
  134. ///
  135. /// This function simulates sending the DHCPINFORM message to the server
  136. /// and receiving server's response (if any). The server's response and the
  137. /// message sent to the server is stored in the context structure and can
  138. /// be accessed using @c getContext function.
  139. ///
  140. /// The configuration returned by the server is stored in the
  141. /// @c config_ public member and can be accessed directly.
  142. ///
  143. /// @param set_ciaddr Indicates if the ciaddr should be set for an
  144. /// outgoing message and defaults to true. Note, that the RFC2131 mandates
  145. /// setting the ciaddr for DHCPINFORM but the server may still want to
  146. /// respond if the ciaddr is not set.
  147. ///
  148. /// @throw This function doesn't thrown exceptions on its own, but it calls
  149. /// functions that are not exception safe, so it may emit an exception if
  150. /// an error occurs.
  151. void doInform(const bool set_ciaddr = true);
  152. /// @brief Sends DHCPRELEASE Message to the server.
  153. ///
  154. /// This method simulates sending the DHCPRELEASE message to the server.
  155. /// The released lease is removed from the client's configuration.
  156. void doRelease();
  157. /// @brief Sends DHCPREQUEST Message to the server and receives a response.
  158. ///
  159. /// This method simulates sending the DHCPREQUEST message to the server and
  160. /// receiving a response. The DHCPREQUEST message can be used by the client
  161. /// being in various states:
  162. /// - SELECTING - client is trying to obtain a new lease and it has selected
  163. /// the server using the DHCPDISCOVER.
  164. /// - INIT-REBOOT - client cached an address it was previously using and is
  165. /// now trying to verify if this address is still valid.
  166. /// - RENEW - client's renewal timer has passed and the client is trying to
  167. /// extend the lifetime of the lease.
  168. /// - REBIND - client's rebind timer has passed and the client is trying to
  169. /// extend the lifetime of the lease from any server.
  170. ///
  171. /// Depending on the state that the client is in, different combinations of
  172. /// - ciaddr
  173. /// - Requested IP Address option
  174. /// - server identifier
  175. /// are used (as per RFC2131, section 4.3.2). Therefore, the unit tests
  176. /// must set the appropriate state of the client prior to calling this
  177. /// method using the @c setState function.
  178. ///
  179. /// When the server returns the DHCPACK the configuration carried in the
  180. /// DHCPACK message is applied and can be obtained from the @c config_.
  181. void doRequest();
  182. /// @brief Generates a hardware address used by the client.
  183. ///
  184. /// It assigns random values to the bytes of the hardware address.
  185. ///
  186. /// @param htype hardware address type. Currently the only type
  187. /// supported is Ethernet hardware address.
  188. ///
  189. /// @return Pointer to the generated hardware address.
  190. HWAddrPtr generateHWAddr(const uint8_t htype = HTYPE_ETHER) const;
  191. /// @brief Returns HW address used by the client.
  192. HWAddrPtr getHWAddress() const {
  193. return (hwaddr_);
  194. }
  195. /// @brief Returns current context.
  196. const Context& getContext() const {
  197. return (context_);
  198. }
  199. /// @brief Returns the server that the client is communicating with.
  200. boost::shared_ptr<NakedDhcpv4Srv> getServer() const {
  201. return (srv_);
  202. }
  203. /// @brief Creates the client id from the client id in the textual format.
  204. ///
  205. /// The generated client id will be added to the client's messages to the
  206. /// server.
  207. ///
  208. /// @param clientid Client id in the textual format. Use the empty client id
  209. /// value to not include the client id.
  210. void includeClientId(const std::string& clientid);
  211. /// @brief Creates an instance of the Client FQDN option to be included
  212. /// in the client's message.
  213. ///
  214. /// @param flags Flags.
  215. /// @param fqdn_name Name in the textual format.
  216. /// @param fqdn_type Type of the name (fully qualified or partial).
  217. void includeFQDN(const uint8_t flags, const std::string& fqdn_name,
  218. Option4ClientFqdn::DomainNameType fqdn_type);
  219. /// @brief Creates an instance of the Hostname option to be included
  220. /// in the client's message.
  221. ///
  222. /// @param name Name to be stored in the option.
  223. void includeHostname(const std::string& name);
  224. /// @brief Modifies the client's HW address (adds one to it).
  225. ///
  226. /// The HW address should be modified to test negative scenarios when the
  227. /// client acquires a lease and tries to renew it with a different HW
  228. /// address. The server should detect the HW address mismatch and react
  229. /// accordingly.
  230. ///
  231. /// The HW address modification affects the value returned by the
  232. /// @c Dhcp4Client::getHWAddress.
  233. void modifyHWAddr();
  234. /// @brief Specify an option to be requested by a client.
  235. ///
  236. /// This function adds option code to the collection of option
  237. /// codes to be requested by a client.
  238. ///
  239. /// @param option Option code to be requested. The value of 0 is
  240. /// ignored and the function is no-op.
  241. void requestOption(const uint8_t option);
  242. /// @brief Specifies options to be requested by the client.
  243. ///
  244. /// This function configures the client to request options having
  245. /// specified codes using Parameter Request List option. The default
  246. /// value of 0 specify that the option is not requested.
  247. ///
  248. /// If there are options specified to be requested before the function
  249. /// is called, the new option codes override previously specified ones.
  250. /// In order to clear the list of requested options call
  251. /// @c requestOptions(0).
  252. ///
  253. /// @param option1 First option to be requested.
  254. /// @param option2 Second option to be requested (optional).
  255. /// @param option3 Third option to be requested (optional).
  256. void requestOptions(const uint8_t option1,
  257. const uint8_t option2 = 0,
  258. const uint8_t option3 = 0);
  259. /// @brief Sets destination address for the messages being sent by the
  260. /// client.
  261. ///
  262. /// By default, the client uses broadcast address 255.255.255.255 to
  263. /// communicate with the server. In certain cases it may be desired
  264. /// that different address is used. This function sets the new address
  265. /// for all future exchanges with the server.
  266. ///
  267. /// @param dest_addr New destination address.
  268. void setDestAddress(const asiolink::IOAddress& dest_addr) {
  269. dest_addr_ = dest_addr;
  270. }
  271. /// @brief Sets the explicit hardware address for the client.
  272. ///
  273. /// @param hwaddr_str String representation of the HW address. Use an
  274. /// empty string to set the NULL hardware address.
  275. void setHWAddress(const std::string& hwaddr_str);
  276. /// @brief Sets the interface over which the messages should be sent.
  277. ///
  278. /// @param iface_name Name of the interface over which the messages should
  279. /// be sent.
  280. void setIfaceName(const std::string& iface_name) {
  281. iface_name_ = iface_name;
  282. }
  283. /// @brief Sets client state.
  284. ///
  285. /// Depending on the current state the client's behavior is different
  286. /// when sending Request messages as per RFC2131, section 4.3.2.
  287. ///
  288. /// @param state New client's state.
  289. void setState(const State& state) {
  290. state_ = state;
  291. }
  292. /// @brief Simulate sending messages through a relay.
  293. ///
  294. /// @param use Parameter which 'true' value indicates that client should
  295. /// simulate sending messages via relay.
  296. /// @param relay_addr Relay address
  297. /// @param sf_relay_addr Server facing relay address.
  298. void useRelay(const bool use = true,
  299. const asiolink::IOAddress& relay_addr =
  300. asiolink::IOAddress("192.0.2.2"),
  301. const asiolink::IOAddress& sf_relay_addr =
  302. asiolink::IOAddress("10.0.0.2")) {
  303. use_relay_ = use;
  304. relay_addr_ = relay_addr;
  305. server_facing_relay_addr_ = sf_relay_addr;
  306. }
  307. /// @brief Current client's configuration obtained from the server.
  308. Configuration config_;
  309. /// @brief Specific ciaddr to be used in client's messages.
  310. ///
  311. /// If this value is "unspecified" the default values will be used
  312. /// by the client. If this value is specified, it will override ciaddr
  313. /// in the client's messages.
  314. isc::util::OptionalValue<asiolink::IOAddress> ciaddr_;
  315. private:
  316. /// @brief Creates and adds Requested IP Address option to the client's
  317. /// query.
  318. ///
  319. /// @param addr Address to be added in the Requested IP Address option.
  320. void addRequestedAddress(const asiolink::IOAddress& addr);
  321. /// @brief Stores configuration received from the server.
  322. ///
  323. /// This methods stores the configuration obtained from the DHCP server
  324. /// in the @c Configuration structure. This configuration includes:
  325. /// - obtained lease
  326. /// - server id of the server that provided the configuration
  327. /// - lease
  328. /// - selected options (used by unit tests):
  329. /// - DNS Servers
  330. /// - Routers
  331. /// - Log Servers
  332. /// - Quotes Servers
  333. void applyConfiguration();
  334. /// @brief Creates client's side DHCP message.
  335. ///
  336. /// @param msg_type Type of the message to be created.
  337. /// @return An instance of the message created.
  338. Pkt4Ptr createMsg(const uint8_t msg_type);
  339. /// @brief Includes the Client Identifier option in the client's message.
  340. ///
  341. /// This function creates an instance of the Client Identifier option
  342. /// if the client identifier has been specified and includes this
  343. /// option in the client's message to the server.
  344. void appendClientId();
  345. /// @brief Includes FQDN or Hostname option in the client's message.
  346. ///
  347. /// This method checks if @c fqdn_ or @c hostname_ is specified and
  348. /// includes it in the client's message. If both are specified, the
  349. /// @c fqdn_ will be used.
  350. void appendName();
  351. /// @brief Include PRL Option in the query message.
  352. ///
  353. /// This function creates the instance of the PRL (Parameter Request List)
  354. /// option and adds option codes from the @c requested_options_ to it.
  355. /// It later adds the PRL option to the @c context_.query_ message
  356. /// if it is non-NULL.
  357. void appendPRL();
  358. /// @brief Simulates reception of the message from the server.
  359. ///
  360. /// @return Received message.
  361. Pkt4Ptr receiveOneMsg();
  362. /// @brief Simulates sending a message to the server.
  363. ///
  364. /// This function instantly triggers processing of the message by the
  365. /// server. The server's response can be gathered by invoking the
  366. /// @c receiveOneMsg function.
  367. ///
  368. /// @param msg Message to be sent.
  369. void sendMsg(const Pkt4Ptr& msg);
  370. /// @brief Current context (sent and received message).
  371. Context context_;
  372. /// @biref Current transaction id (altered on each send).
  373. uint32_t curr_transid_;
  374. /// @brief Currently used destination address.
  375. asiolink::IOAddress dest_addr_;
  376. /// @brief FQDN requested by the client.
  377. Option4ClientFqdnPtr fqdn_;
  378. /// @brief Hostname requested by the client.
  379. OptionStringPtr hostname_;
  380. /// @brief Current hardware address of the client.
  381. HWAddrPtr hwaddr_;
  382. /// @brief Current client identifier.
  383. ClientIdPtr clientid_;
  384. /// @brief Interface to be used to send the messages.
  385. std::string iface_name_;
  386. /// @brief Relay address to use.
  387. asiolink::IOAddress relay_addr_;
  388. /// @brief Collection of options codes to be requested by the client.
  389. std::set<uint8_t> requested_options_;
  390. /// @brief Address of the relay interface connected to the server.
  391. asiolink::IOAddress server_facing_relay_addr_;
  392. /// @brief Pointer to the server that the client is communicating with.
  393. boost::shared_ptr<NakedDhcpv4Srv> srv_;
  394. /// @brief Current state of the client.
  395. State state_;
  396. /// @brief Enable relaying messages to the server.
  397. bool use_relay_;
  398. };
  399. } // end of namespace isc::dhcp::test
  400. } // end of namespace isc::dhcp
  401. } // end of namespace isc
  402. #endif // DHCP4_CLIENT_H