dhcp4_client.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. // Copyright (C) 2014 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/dhcp4.h>
  15. #include <dhcp/option.h>
  16. #include <dhcp/option_int_array.h>
  17. #include <dhcpsrv/lease.h>
  18. #include <dhcp4/tests/dhcp4_client.h>
  19. #include <util/range_utilities.h>
  20. #include <boost/pointer_cast.hpp>
  21. #include <cstdlib>
  22. using namespace isc::asiolink;
  23. namespace isc {
  24. namespace dhcp {
  25. namespace test {
  26. Dhcp4Client::Configuration::Configuration()
  27. : routers_(), dns_servers_(), log_servers_(), quotes_servers_(),
  28. serverid_("0.0.0.0") {
  29. reset();
  30. }
  31. void
  32. Dhcp4Client::Configuration::reset() {
  33. routers_.clear();
  34. dns_servers_.clear();
  35. log_servers_.clear();
  36. quotes_servers_.clear();
  37. serverid_ = asiolink::IOAddress("0.0.0.0");
  38. lease_ = Lease4();
  39. }
  40. Dhcp4Client::Dhcp4Client(const Dhcp4Client::State& state) :
  41. config_(),
  42. ciaddr_(IOAddress("0.0.0.0")),
  43. curr_transid_(0),
  44. dest_addr_("255.255.255.255"),
  45. hwaddr_(generateHWAddr()),
  46. relay_addr_("192.0.2.2"),
  47. requested_options_(),
  48. server_facing_relay_addr_("10.0.0.2"),
  49. srv_(boost::shared_ptr<NakedDhcpv4Srv>(new NakedDhcpv4Srv(0))),
  50. state_(state),
  51. use_relay_(false) {
  52. }
  53. Dhcp4Client::Dhcp4Client(boost::shared_ptr<NakedDhcpv4Srv> srv,
  54. const Dhcp4Client::State& state) :
  55. config_(),
  56. ciaddr_(IOAddress("0.0.0.0")),
  57. curr_transid_(0),
  58. dest_addr_("255.255.255.255"),
  59. hwaddr_(generateHWAddr()),
  60. relay_addr_("192.0.2.2"),
  61. requested_options_(),
  62. server_facing_relay_addr_("10.0.0.2"),
  63. srv_(srv),
  64. state_(state),
  65. use_relay_(false) {
  66. }
  67. void
  68. Dhcp4Client::addRequestedAddress(const asiolink::IOAddress& addr) {
  69. if (context_.query_) {
  70. Option4AddrLstPtr opt(new Option4AddrLst(DHO_DHCP_REQUESTED_ADDRESS,
  71. addr));
  72. context_.query_->addOption(opt);
  73. }
  74. }
  75. void
  76. Dhcp4Client::applyConfiguration() {
  77. Pkt4Ptr resp = context_.response_;
  78. if (!resp) {
  79. return;
  80. }
  81. config_.reset();
  82. // Routers
  83. Option4AddrLstPtr opt_routers = boost::dynamic_pointer_cast<
  84. Option4AddrLst>(resp->getOption(DHO_ROUTERS));
  85. if (opt_routers) {
  86. config_.routers_ = opt_routers->getAddresses();
  87. }
  88. // DNS Servers
  89. Option4AddrLstPtr opt_dns_servers = boost::dynamic_pointer_cast<
  90. Option4AddrLst>(resp->getOption(DHO_DOMAIN_NAME_SERVERS));
  91. if (opt_dns_servers) {
  92. config_.dns_servers_ = opt_dns_servers->getAddresses();
  93. }
  94. // Log Servers
  95. Option4AddrLstPtr opt_log_servers = boost::dynamic_pointer_cast<
  96. Option4AddrLst>(resp->getOption(DHO_LOG_SERVERS));
  97. if (opt_log_servers) {
  98. config_.log_servers_ = opt_routers->getAddresses();
  99. }
  100. // Quotes Servers
  101. Option4AddrLstPtr opt_quotes_servers = boost::dynamic_pointer_cast<
  102. Option4AddrLst>(resp->getOption(DHO_COOKIE_SERVERS));
  103. if (opt_quotes_servers) {
  104. config_.quotes_servers_ = opt_dns_servers->getAddresses();
  105. }
  106. // Server Identifier
  107. OptionCustomPtr opt_serverid = boost::dynamic_pointer_cast<
  108. OptionCustom>(resp->getOption(DHO_DHCP_SERVER_IDENTIFIER));
  109. if (opt_serverid) {
  110. config_.serverid_ = opt_serverid->readAddress();
  111. }
  112. /// @todo Set the valid lifetime, t1, t2 etc.
  113. config_.lease_ = Lease4(IOAddress(context_.response_->getYiaddr()),
  114. context_.response_->getHWAddr(),
  115. 0, 0, 0, 0, 0, time(NULL), 0, false, false,
  116. "");
  117. }
  118. void
  119. Dhcp4Client::createLease(const asiolink::IOAddress& addr,
  120. const uint32_t valid_lft) {
  121. Lease4 lease(addr, hwaddr_, 0, 0, valid_lft, valid_lft / 2, valid_lft,
  122. time(NULL), false, false, "");
  123. config_.lease_ = lease;
  124. }
  125. Pkt4Ptr
  126. Dhcp4Client::createMsg(const uint8_t msg_type) {
  127. Pkt4Ptr msg(new Pkt4(msg_type, curr_transid_++));
  128. msg->setHWAddr(hwaddr_);
  129. return (msg);
  130. }
  131. void
  132. Dhcp4Client::doDiscover(const boost::shared_ptr<IOAddress>& requested_addr) {
  133. context_.query_ = createMsg(DHCPDISCOVER);
  134. // Request options if any.
  135. includePRL();
  136. if (requested_addr) {
  137. addRequestedAddress(*requested_addr);
  138. }
  139. // Override the default ciaddr if specified by a test.
  140. if (ciaddr_.isSpecified()) {
  141. context_.query_->setCiaddr(ciaddr_.get());
  142. }
  143. // Send the message to the server.
  144. sendMsg(context_.query_);
  145. // Expect response.
  146. context_.response_ = receiveOneMsg();
  147. }
  148. void
  149. Dhcp4Client::doDORA(const boost::shared_ptr<IOAddress>& requested_addr) {
  150. doDiscover(requested_addr);
  151. if (context_.response_ && (context_.response_->getType() == DHCPOFFER)) {
  152. doRequest();
  153. }
  154. }
  155. void
  156. Dhcp4Client::doInform(const bool set_ciaddr) {
  157. context_.query_ = createMsg(DHCPINFORM);
  158. // Request options if any.
  159. includePRL();
  160. // The client sending a DHCPINFORM message has an IP address obtained
  161. // by some other means, e.g. static configuration. The lease which we
  162. // are using here is most likely set by the createLease method.
  163. if (set_ciaddr) {
  164. context_.query_->setCiaddr(config_.lease_.addr_);
  165. }
  166. context_.query_->setLocalAddr(config_.lease_.addr_);
  167. // Send the message to the server.
  168. sendMsg(context_.query_);
  169. // Expect response. If there is no response, return.
  170. context_.response_ = receiveOneMsg();
  171. if (!context_.response_) {
  172. return;
  173. }
  174. // If DHCPACK has been returned by the server, use the returned
  175. // configuration.
  176. if (context_.response_->getType() == DHCPACK) {
  177. applyConfiguration();
  178. }
  179. }
  180. void
  181. Dhcp4Client::doRequest() {
  182. context_.query_ = createMsg(DHCPREQUEST);
  183. // Override the default ciaddr if specified by a test.
  184. if (ciaddr_.isSpecified()) {
  185. context_.query_->setCiaddr(ciaddr_.get());
  186. } else if ((state_ == SELECTING) || (state_ == INIT_REBOOT)) {
  187. context_.query_->setCiaddr(IOAddress("0.0.0.0"));
  188. } else {
  189. context_.query_->setCiaddr(IOAddress(config_.lease_.addr_));
  190. }
  191. // Requested IP address.
  192. if (state_ == SELECTING) {
  193. if (context_.response_ &&
  194. (context_.response_->getType() == DHCPOFFER) &&
  195. (context_.response_->getYiaddr() != IOAddress("0.0.0.0"))) {
  196. addRequestedAddress(context_.response_->getYiaddr());
  197. } else {
  198. isc_throw(Dhcp4ClientError, "error sending the DHCPREQUEST because"
  199. " the received DHCPOFFER message was invalid");
  200. }
  201. } else if (state_ == INIT_REBOOT) {
  202. addRequestedAddress(config_.lease_.addr_);
  203. }
  204. // Server identifier.
  205. if (state_ == SELECTING) {
  206. if (context_.response_) {
  207. OptionPtr opt_serverid =
  208. context_.response_->getOption(DHO_DHCP_SERVER_IDENTIFIER);
  209. if (!opt_serverid) {
  210. isc_throw(Dhcp4ClientError, "missing server identifier in the"
  211. " server's response");
  212. }
  213. context_.query_->addOption(opt_serverid);
  214. }
  215. }
  216. // Request options if any.
  217. includePRL();
  218. // Send the message to the server.
  219. sendMsg(context_.query_);
  220. // Expect response.
  221. context_.response_ = receiveOneMsg();
  222. // If the server has responded, store the configuration received.
  223. if (context_.response_) {
  224. applyConfiguration();
  225. }
  226. }
  227. void
  228. Dhcp4Client::includePRL() {
  229. if (!context_.query_) {
  230. isc_throw(Dhcp4ClientError, "pointer to the query must not be NULL"
  231. " when adding option codes to the PRL option");
  232. } else if (!requested_options_.empty()) {
  233. // Include Parameter Request List if at least one option code
  234. // has been specified to be requested.
  235. OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
  236. DHO_DHCP_PARAMETER_REQUEST_LIST));
  237. for (std::set<uint8_t>::const_iterator opt = requested_options_.begin();
  238. opt != requested_options_.end(); ++opt) {
  239. prl->addValue(*opt);
  240. }
  241. context_.query_->addOption(prl);
  242. }
  243. }
  244. HWAddrPtr
  245. Dhcp4Client::generateHWAddr(const uint8_t htype) const {
  246. if (htype != HTYPE_ETHER) {
  247. isc_throw(isc::NotImplemented,
  248. "The hardware address type " << static_cast<int>(htype)
  249. << " is currently not supported");
  250. }
  251. std::vector<uint8_t> hwaddr(HWAddr::ETHERNET_HWADDR_LEN);
  252. // Generate ethernet hardware address by assigning random byte values.
  253. isc::util::fillRandom(hwaddr.begin(), hwaddr.end());
  254. return (HWAddrPtr(new HWAddr(hwaddr, htype)));
  255. }
  256. void
  257. Dhcp4Client::modifyHWAddr() {
  258. if (!hwaddr_) {
  259. hwaddr_ = generateHWAddr();
  260. return;
  261. }
  262. // Modify the HW address by adding 1 to its last byte.
  263. ++hwaddr_->hwaddr_[hwaddr_->hwaddr_.size() - 1];
  264. }
  265. void
  266. Dhcp4Client::requestOption(const uint8_t option) {
  267. if (option != 0) {
  268. requested_options_.insert(option);
  269. }
  270. }
  271. void
  272. Dhcp4Client::requestOptions(const uint8_t option1, const uint8_t option2,
  273. const uint8_t option3) {
  274. requested_options_.clear();
  275. requestOption(option1);
  276. requestOption(option2);
  277. requestOption(option3);
  278. }
  279. Pkt4Ptr
  280. Dhcp4Client::receiveOneMsg() {
  281. // Return empty pointer if server hasn't responded.
  282. if (srv_->fake_sent_.empty()) {
  283. return (Pkt4Ptr());
  284. }
  285. Pkt4Ptr msg = srv_->fake_sent_.front();
  286. srv_->fake_sent_.pop_front();
  287. // Copy the original message to simulate reception over the wire.
  288. msg->pack();
  289. Pkt4Ptr msg_copy(new Pkt4(static_cast<const uint8_t*>
  290. (msg->getBuffer().getData()),
  291. msg->getBuffer().getLength()));
  292. msg_copy->setRemoteAddr(msg->getLocalAddr());
  293. msg_copy->setLocalAddr(msg->getRemoteAddr());
  294. msg_copy->setIface(msg->getIface());
  295. msg_copy->unpack();
  296. return (msg_copy);
  297. }
  298. void
  299. Dhcp4Client::sendMsg(const Pkt4Ptr& msg) {
  300. srv_->shutdown_ = false;
  301. if (use_relay_) {
  302. msg->setHops(1);
  303. msg->setGiaddr(relay_addr_);
  304. msg->setLocalAddr(server_facing_relay_addr_);
  305. }
  306. // Repack the message to simulate wire-data parsing.
  307. msg->pack();
  308. Pkt4Ptr msg_copy(new Pkt4(static_cast<const uint8_t*>
  309. (msg->getBuffer().getData()),
  310. msg->getBuffer().getLength()));
  311. msg_copy->setRemoteAddr(msg->getLocalAddr());
  312. msg_copy->setLocalAddr(dest_addr_);
  313. msg_copy->setIface("eth0");
  314. srv_->fakeReceive(msg_copy);
  315. srv_->run();
  316. }
  317. void
  318. Dhcp4Client::setHWAddress(const std::string& hwaddr_str) {
  319. hwaddr_.reset(new HWAddr(HWAddr::fromText(hwaddr_str)));
  320. }
  321. } // end of namespace isc::dhcp::test
  322. } // end of namespace isc::dhcp
  323. } // end of namespace isc