dhcp4_client.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  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 DHCPDECLINE Message to the server.
  158. ///
  159. /// This method simulates sending the DHCPDECLINE message to the server.
  160. /// The released lease is removed from the client's configuration.
  161. void doDecline();
  162. /// @brief Sends DHCPREQUEST Message to the server and receives a response.
  163. ///
  164. /// This method simulates sending the DHCPREQUEST message to the server and
  165. /// receiving a response. The DHCPREQUEST message can be used by the client
  166. /// being in various states:
  167. /// - SELECTING - client is trying to obtain a new lease and it has selected
  168. /// the server using the DHCPDISCOVER.
  169. /// - INIT-REBOOT - client cached an address it was previously using and is
  170. /// now trying to verify if this address is still valid.
  171. /// - RENEW - client's renewal timer has passed and the client is trying to
  172. /// extend the lifetime of the lease.
  173. /// - REBIND - client's rebind timer has passed and the client is trying to
  174. /// extend the lifetime of the lease from any server.
  175. ///
  176. /// Depending on the state that the client is in, different combinations of
  177. /// - ciaddr
  178. /// - Requested IP Address option
  179. /// - server identifier
  180. /// are used (as per RFC2131, section 4.3.2). Therefore, the unit tests
  181. /// must set the appropriate state of the client prior to calling this
  182. /// method using the @c setState function.
  183. ///
  184. /// When the server returns the DHCPACK the configuration carried in the
  185. /// DHCPACK message is applied and can be obtained from the @c config_.
  186. void doRequest();
  187. /// @brief Generates a hardware address used by the client.
  188. ///
  189. /// It assigns random values to the bytes of the hardware address.
  190. ///
  191. /// @param htype hardware address type. Currently the only type
  192. /// supported is Ethernet hardware address.
  193. ///
  194. /// @return Pointer to the generated hardware address.
  195. HWAddrPtr generateHWAddr(const uint8_t htype = HTYPE_ETHER) const;
  196. /// @brief Returns HW address used by the client.
  197. HWAddrPtr getHWAddress() const {
  198. return (hwaddr_);
  199. }
  200. /// @brief Returns current context.
  201. const Context& getContext() const {
  202. return (context_);
  203. }
  204. /// @brief Returns the server that the client is communicating with.
  205. boost::shared_ptr<NakedDhcpv4Srv> getServer() const {
  206. return (srv_);
  207. }
  208. /// @brief Creates the client id from the client id in the textual format.
  209. ///
  210. /// The generated client id will be added to the client's messages to the
  211. /// server.
  212. ///
  213. /// @param clientid Client id in the textual format. Use the empty client id
  214. /// value to not include the client id.
  215. void includeClientId(const std::string& clientid);
  216. /// @brief Creates an instance of the Client FQDN option to be included
  217. /// in the client's message.
  218. ///
  219. /// @param flags Flags.
  220. /// @param fqdn_name Name in the textual format.
  221. /// @param fqdn_type Type of the name (fully qualified or partial).
  222. void includeFQDN(const uint8_t flags, const std::string& fqdn_name,
  223. Option4ClientFqdn::DomainNameType fqdn_type);
  224. /// @brief Creates an instance of the Hostname option to be included
  225. /// in the client's message.
  226. ///
  227. /// @param name Name to be stored in the option.
  228. void includeHostname(const std::string& name);
  229. /// @brief Modifies the client's HW address (adds one to it).
  230. ///
  231. /// The HW address should be modified to test negative scenarios when the
  232. /// client acquires a lease and tries to renew it with a different HW
  233. /// address. The server should detect the HW address mismatch and react
  234. /// accordingly.
  235. ///
  236. /// The HW address modification affects the value returned by the
  237. /// @c Dhcp4Client::getHWAddress.
  238. void modifyHWAddr();
  239. /// @brief Specify an option to be requested by a client.
  240. ///
  241. /// This function adds option code to the collection of option
  242. /// codes to be requested by a client.
  243. ///
  244. /// @param option Option code to be requested. The value of 0 is
  245. /// ignored and the function is no-op.
  246. void requestOption(const uint8_t option);
  247. /// @brief Specifies options to be requested by the client.
  248. ///
  249. /// This function configures the client to request options having
  250. /// specified codes using Parameter Request List option. The default
  251. /// value of 0 specify that the option is not requested.
  252. ///
  253. /// If there are options specified to be requested before the function
  254. /// is called, the new option codes override previously specified ones.
  255. /// In order to clear the list of requested options call
  256. /// @c requestOptions(0).
  257. ///
  258. /// @param option1 First option to be requested.
  259. /// @param option2 Second option to be requested (optional).
  260. /// @param option3 Third option to be requested (optional).
  261. void requestOptions(const uint8_t option1,
  262. const uint8_t option2 = 0,
  263. const uint8_t option3 = 0);
  264. /// @brief Sets destination address for the messages being sent by the
  265. /// client.
  266. ///
  267. /// By default, the client uses broadcast address 255.255.255.255 to
  268. /// communicate with the server. In certain cases it may be desired
  269. /// that different address is used. This function sets the new address
  270. /// for all future exchanges with the server.
  271. ///
  272. /// @param dest_addr New destination address.
  273. void setDestAddress(const asiolink::IOAddress& dest_addr) {
  274. dest_addr_ = dest_addr;
  275. }
  276. /// @brief Sets the explicit hardware address for the client.
  277. ///
  278. /// @param hwaddr_str String representation of the HW address. Use an
  279. /// empty string to set the NULL hardware address.
  280. void setHWAddress(const std::string& hwaddr_str);
  281. /// @brief Sets the interface over which the messages should be sent.
  282. ///
  283. /// @param iface_name Name of the interface over which the messages should
  284. /// be sent.
  285. void setIfaceName(const std::string& iface_name) {
  286. iface_name_ = iface_name;
  287. }
  288. /// @brief Sets client state.
  289. ///
  290. /// Depending on the current state the client's behavior is different
  291. /// when sending Request messages as per RFC2131, section 4.3.2.
  292. ///
  293. /// @param state New client's state.
  294. void setState(const State& state) {
  295. state_ = state;
  296. }
  297. /// @brief Simulate sending messages through a relay.
  298. ///
  299. /// @param use Parameter which 'true' value indicates that client should
  300. /// simulate sending messages via relay.
  301. /// @param relay_addr Relay address
  302. /// @param sf_relay_addr Server facing relay address.
  303. void useRelay(const bool use = true,
  304. const asiolink::IOAddress& relay_addr =
  305. asiolink::IOAddress("192.0.2.2"),
  306. const asiolink::IOAddress& sf_relay_addr =
  307. asiolink::IOAddress("10.0.0.2")) {
  308. use_relay_ = use;
  309. relay_addr_ = relay_addr;
  310. server_facing_relay_addr_ = sf_relay_addr;
  311. }
  312. /// @brief Current client's configuration obtained from the server.
  313. Configuration config_;
  314. /// @brief Specific ciaddr to be used in client's messages.
  315. ///
  316. /// If this value is "unspecified" the default values will be used
  317. /// by the client. If this value is specified, it will override ciaddr
  318. /// in the client's messages.
  319. isc::util::OptionalValue<asiolink::IOAddress> ciaddr_;
  320. /// @brief Adds extra option (an option the client will always send)
  321. ///
  322. /// @param opt additional option to be sent
  323. void addExtraOption(const OptionPtr& opt);
  324. private:
  325. /// @brief Appends extra options, previously added with addExtraOption()
  326. ///
  327. /// @brief Copies options from extra_options_ into outgoing message
  328. void appendExtraOptions();
  329. /// @brief Creates and adds Requested IP Address option to the client's
  330. /// query.
  331. ///
  332. /// @param addr Address to be added in the Requested IP Address option.
  333. void addRequestedAddress(const asiolink::IOAddress& addr);
  334. /// @brief Stores configuration received from the server.
  335. ///
  336. /// This methods stores the configuration obtained from the DHCP server
  337. /// in the @c Configuration structure. This configuration includes:
  338. /// - obtained lease
  339. /// - server id of the server that provided the configuration
  340. /// - lease
  341. /// - selected options (used by unit tests):
  342. /// - DNS Servers
  343. /// - Routers
  344. /// - Log Servers
  345. /// - Quotes Servers
  346. void applyConfiguration();
  347. /// @brief Creates client's side DHCP message.
  348. ///
  349. /// @param msg_type Type of the message to be created.
  350. /// @return An instance of the message created.
  351. Pkt4Ptr createMsg(const uint8_t msg_type);
  352. /// @brief Includes the Client Identifier option in the client's message.
  353. ///
  354. /// This function creates an instance of the Client Identifier option
  355. /// if the client identifier has been specified and includes this
  356. /// option in the client's message to the server.
  357. void appendClientId();
  358. /// @brief Includes the Server Identifier option in the client's message.
  359. ///
  360. /// This function creates an instance of the Server Identifier option.
  361. /// It uses whatever information is stored in config_.serverid_.
  362. void appendServerId();
  363. /// @brief Includes FQDN or Hostname option in the client's message.
  364. ///
  365. /// This method checks if @c fqdn_ or @c hostname_ is specified and
  366. /// includes it in the client's message. If both are specified, the
  367. /// @c fqdn_ will be used.
  368. void appendName();
  369. /// @brief Include PRL Option in the query message.
  370. ///
  371. /// This function creates the instance of the PRL (Parameter Request List)
  372. /// option and adds option codes from the @c requested_options_ to it.
  373. /// It later adds the PRL option to the @c context_.query_ message
  374. /// if it is non-NULL.
  375. void appendPRL();
  376. /// @brief Simulates reception of the message from the server.
  377. ///
  378. /// @return Received message.
  379. Pkt4Ptr receiveOneMsg();
  380. /// @brief Simulates sending a message to the server.
  381. ///
  382. /// This function instantly triggers processing of the message by the
  383. /// server. The server's response can be gathered by invoking the
  384. /// @c receiveOneMsg function.
  385. ///
  386. /// @param msg Message to be sent.
  387. void sendMsg(const Pkt4Ptr& msg);
  388. /// @brief Current context (sent and received message).
  389. Context context_;
  390. /// @biref Current transaction id (altered on each send).
  391. uint32_t curr_transid_;
  392. /// @brief Currently used destination address.
  393. asiolink::IOAddress dest_addr_;
  394. /// @brief FQDN requested by the client.
  395. Option4ClientFqdnPtr fqdn_;
  396. /// @brief Hostname requested by the client.
  397. OptionStringPtr hostname_;
  398. /// @brief Current hardware address of the client.
  399. HWAddrPtr hwaddr_;
  400. /// @brief Current client identifier.
  401. ClientIdPtr clientid_;
  402. /// @brief Interface to be used to send the messages.
  403. std::string iface_name_;
  404. /// @brief Relay address to use.
  405. asiolink::IOAddress relay_addr_;
  406. /// @brief Collection of options codes to be requested by the client.
  407. std::set<uint8_t> requested_options_;
  408. /// @brief Address of the relay interface connected to the server.
  409. asiolink::IOAddress server_facing_relay_addr_;
  410. /// @brief Pointer to the server that the client is communicating with.
  411. boost::shared_ptr<NakedDhcpv4Srv> srv_;
  412. /// @brief Current state of the client.
  413. State state_;
  414. /// @brief Enable relaying messages to the server.
  415. bool use_relay_;
  416. /// @brief Extra options the client will send.
  417. OptionCollection extra_options_;
  418. };
  419. } // end of namespace isc::dhcp::test
  420. } // end of namespace isc::dhcp
  421. } // end of namespace isc
  422. #endif // DHCP4_CLIENT_H