dhcp6_client.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  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/dhcp6.h>
  15. #include <dhcp/option_custom.h>
  16. #include <dhcp/option6_ia.h>
  17. #include <dhcp/option6_iaaddr.h>
  18. #include <dhcp/option_int_array.h>
  19. #include <dhcp/pkt6.h>
  20. #include <dhcpsrv/lease.h>
  21. #include <dhcp6/tests/dhcp6_client.h>
  22. #include <util/buffer.h>
  23. #include <boost/pointer_cast.hpp>
  24. #include <cstdlib>
  25. #include <time.h>
  26. using namespace isc::test;
  27. namespace isc {
  28. namespace dhcp {
  29. namespace test {
  30. Dhcp6Client::Dhcp6Client() :
  31. relay_link_addr_("3000:1::1"),
  32. curr_transid_(0),
  33. dest_addr_(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
  34. duid_(generateDUID(DUID::DUID_LLT)),
  35. link_local_("fe80::3a60:77ff:fed5:cdef"),
  36. srv_(boost::shared_ptr<NakedDhcpv6Srv>(new NakedDhcpv6Srv(0))),
  37. use_na_(false),
  38. use_pd_(false),
  39. use_relay_(false),
  40. use_oro_(false),
  41. use_client_id_(true),
  42. prefix_hint_() {
  43. }
  44. Dhcp6Client::Dhcp6Client(boost::shared_ptr<NakedDhcpv6Srv>& srv) :
  45. relay_link_addr_("3000:1::1"),
  46. curr_transid_(0),
  47. dest_addr_(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
  48. duid_(generateDUID(DUID::DUID_LLT)),
  49. link_local_("fe80::3a60:77ff:fed5:cdef"),
  50. srv_(srv),
  51. use_na_(false),
  52. use_pd_(false),
  53. use_relay_(false),
  54. use_oro_(false),
  55. use_client_id_(true),
  56. prefix_hint_() {
  57. }
  58. void
  59. Dhcp6Client::applyRcvdConfiguration(const Pkt6Ptr& reply) {
  60. typedef OptionCollection Opts;
  61. // Get all options in the reply message and pick IA_NA, IA_PD and
  62. // Status code.
  63. Opts opts = reply->options_;
  64. // Let's try to get a MAC
  65. HWAddrPtr hwaddr = reply->getMAC(HWAddr::HWADDR_SOURCE_ANY);
  66. // Set the global status code to default: success and not received.
  67. config_.resetGlobalStatusCode();
  68. for (Opts::const_iterator opt = opts.begin(); opt != opts.end(); ++opt) {
  69. Option6IAPtr ia = boost::dynamic_pointer_cast<Option6IA>(opt->second);
  70. if (!ia) {
  71. // This is not IA, so let's just store it.
  72. config_.options_.insert(*opt);
  73. continue;
  74. }
  75. const Opts& ia_opts = ia->getOptions();
  76. for (Opts::const_iterator iter_ia_opt = ia_opts.begin();
  77. iter_ia_opt != ia_opts.end(); ++iter_ia_opt) {
  78. OptionPtr ia_opt = iter_ia_opt->second;
  79. LeaseInfo lease_info;
  80. switch (ia_opt->getType()) {
  81. case D6O_IAADDR:
  82. {
  83. Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
  84. Option6IAAddr>(ia_opt);
  85. if (!iaaddr) {
  86. // There is no address. This IA option may simply
  87. // contain a status code, so let's just reset the
  88. // lease and keep IAID around.
  89. lease_info.lease_ = Lease6();
  90. lease_info.lease_.type_ = Lease::TYPE_NA;
  91. lease_info.lease_.iaid_ = ia->getIAID();
  92. break;
  93. }
  94. lease_info.lease_ = Lease6(Lease::TYPE_NA,
  95. iaaddr->getAddress(),
  96. duid_, ia->getIAID(),
  97. iaaddr->getPreferred(),
  98. iaaddr->getValid(),
  99. ia->getT1(), ia->getT2(), 0,
  100. hwaddr);
  101. lease_info.lease_.cltt_ = time(NULL);
  102. }
  103. break;
  104. case D6O_IAPREFIX:
  105. {
  106. Option6IAPrefixPtr iaprefix = boost::dynamic_pointer_cast<
  107. Option6IAPrefix>(ia_opt);
  108. if (!iaprefix) {
  109. // There is no prefix. This IA option may simply
  110. // contain a status code, so let's just reset the
  111. // lease and keep IAID around.
  112. lease_info.lease_ = Lease6();
  113. lease_info.lease_.type_ = Lease::TYPE_PD;
  114. lease_info.lease_.iaid_ = ia->getIAID();
  115. break;
  116. }
  117. lease_info.lease_ = Lease6(Lease::TYPE_PD,
  118. iaprefix->getAddress(), duid_,
  119. ia->getIAID(),
  120. iaprefix->getPreferred(),
  121. iaprefix->getValid(),
  122. ia->getT1(), ia->getT2(), 0,
  123. hwaddr,
  124. iaprefix->getLength());
  125. lease_info.lease_.cltt_ = time(NULL);
  126. }
  127. break;
  128. case D6O_STATUS_CODE:
  129. {
  130. // Check if the server has sent status code. If no status
  131. // code, assume the status code to be 0.
  132. OptionCustomPtr status_code = boost::dynamic_pointer_cast<
  133. OptionCustom>(ia->getOption(D6O_STATUS_CODE));
  134. lease_info.status_code_ =
  135. status_code ? status_code->readInteger<uint16_t>(0) : 0;
  136. }
  137. break;
  138. default:
  139. ; // no-op
  140. }
  141. applyLease(lease_info);
  142. }
  143. }
  144. // Get the global status code.
  145. OptionCustomPtr status_code = boost::dynamic_pointer_cast<
  146. OptionCustom>(reply->getOption(D6O_STATUS_CODE));
  147. // If status code has been sent, we override the default status code:
  148. // Success and record that we have received the status code.
  149. if (status_code) {
  150. config_.received_status_code_ = true;
  151. config_.status_code_ = status_code->readInteger<uint16_t>(0);
  152. }
  153. }
  154. void
  155. Dhcp6Client::applyLease(const LeaseInfo& lease_info) {
  156. // Go over existing leases and try to match the one that we have.
  157. for (int i = 0; i < config_.leases_.size(); ++i) {
  158. Lease6 existing_lease = config_.leases_[i].lease_;
  159. // If IAID is matching and there is an actual address assigned
  160. // replace the current lease. The default address is :: if the
  161. // server hasn't sent the IA option. In this case, there is no
  162. // lease assignment so we keep what we have.
  163. if ((existing_lease.iaid_ == lease_info.lease_.iaid_)
  164. && (existing_lease.type_ == lease_info.lease_.type_)
  165. && (lease_info.lease_.addr_ != asiolink::IOAddress("::"))
  166. && (existing_lease.addr_ == lease_info.lease_.addr_)) {
  167. config_.leases_[i] = lease_info;
  168. return;
  169. } else if (lease_info.lease_.addr_ == asiolink::IOAddress("::")) {
  170. config_.leases_[i].status_code_ = lease_info.status_code_;
  171. return;
  172. }
  173. }
  174. // It is a new lease. Add it.
  175. config_.leases_.push_back(lease_info);
  176. }
  177. void
  178. Dhcp6Client::copyIAs(const Pkt6Ptr& source, const Pkt6Ptr& dest) {
  179. typedef OptionCollection Opts;
  180. // Copy IA_NAs.
  181. Opts opts = source->getOptions(D6O_IA_NA);
  182. for (Opts::const_iterator opt = opts.begin(); opt != opts.end(); ++opt) {
  183. dest->addOption(opt->second);
  184. }
  185. // Copy IA_PDs.
  186. opts = source->getOptions(D6O_IA_PD);
  187. for (Opts::const_iterator opt = opts.begin(); opt != opts.end(); ++opt) {
  188. dest->addOption(opt->second);
  189. }
  190. }
  191. void
  192. Dhcp6Client::copyIAsFromLeases(const Pkt6Ptr& dest) const {
  193. // Go over leases and create IA_NA and IA_PD options from them.
  194. // Create one IA per lease.
  195. std::set<uint32_t> iaids = getIAIDs();
  196. for (std::set<uint32_t>::const_iterator iaid = iaids.begin();
  197. iaid != iaids.end(); ++iaid) {
  198. std::vector<Lease6> leases = getLeasesByIAID(*iaid);
  199. Option6IAPtr opt(new Option6IA(leases[0].type_ == Lease::TYPE_NA ?
  200. D6O_IA_NA : D6O_IA_PD, *iaid));
  201. opt->setT1(leases[0].t1_);
  202. opt->setT2(leases[0].t2_);
  203. for (std::vector<Lease6>::const_iterator lease = leases.begin();
  204. lease != leases.end(); ++lease) {
  205. if ((lease->preferred_lft_ != 0) && (lease->valid_lft_ != 0)) {
  206. if (lease->type_ == Lease::TYPE_NA) {
  207. opt->addOption(Option6IAAddrPtr(new Option6IAAddr(
  208. D6O_IAADDR,
  209. lease->addr_,
  210. lease->preferred_lft_,
  211. lease->valid_lft_)));
  212. } else if (lease->type_ == Lease::TYPE_PD) {
  213. opt->addOption(Option6IAAddrPtr(new Option6IAPrefix(
  214. D6O_IAPREFIX,
  215. lease->addr_,
  216. lease->prefixlen_,
  217. lease->preferred_lft_,
  218. lease->valid_lft_)));
  219. }
  220. }
  221. }
  222. dest->addOption(opt);
  223. }
  224. }
  225. void
  226. Dhcp6Client::createLease(const Lease6& lease) {
  227. LeaseInfo info;
  228. info.lease_ = lease;
  229. applyLease(info);
  230. }
  231. Pkt6Ptr
  232. Dhcp6Client::createMsg(const uint8_t msg_type) {
  233. Pkt6Ptr msg(new Pkt6(msg_type, curr_transid_++));
  234. if (use_client_id_) {
  235. msg->addOption(getClientId());
  236. }
  237. if (use_oro_) {
  238. OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6, D6O_ORO));
  239. oro->setValues(oro_);
  240. msg->addOption(oro);
  241. };
  242. return (msg);
  243. }
  244. void
  245. Dhcp6Client::doSARR() {
  246. doSolicit();
  247. // Don't send the Request if there was no Advertise.
  248. if (context_.response_) {
  249. doRequest();
  250. }
  251. }
  252. void
  253. Dhcp6Client::doSolicit() {
  254. context_.query_ = createMsg(DHCPV6_SOLICIT);
  255. if (use_na_) {
  256. context_.query_->addOption(Option6IAPtr(new Option6IA(D6O_IA_NA,
  257. 1234)));
  258. }
  259. if (use_pd_) {
  260. Option6IAPtr ia(new Option6IA(D6O_IA_PD, 5678));
  261. if (prefix_hint_) {
  262. ia->addOption(prefix_hint_);
  263. }
  264. context_.query_->addOption(ia);
  265. }
  266. sendMsg(context_.query_);
  267. context_.response_ = receiveOneMsg();
  268. /// @todo Sanity check here
  269. }
  270. void
  271. Dhcp6Client::doRequest() {
  272. Pkt6Ptr query = createMsg(DHCPV6_REQUEST);
  273. query->addOption(context_.response_->getOption(D6O_SERVERID));
  274. copyIAs(context_.response_, query);
  275. context_.query_ = query;
  276. sendMsg(context_.query_);
  277. context_.response_ = receiveOneMsg();
  278. /// @todo sanity check here.
  279. // Apply new configuration only if the server has responded.
  280. if (context_.response_) {
  281. applyRcvdConfiguration(context_.response_);
  282. }
  283. }
  284. void
  285. Dhcp6Client::doInfRequest() {
  286. context_.query_ = createMsg(DHCPV6_INFORMATION_REQUEST);
  287. // IA_NA, IA_TA and IA_PD options are not allowed in INF-REQUEST,
  288. // but hey! Let's test it.
  289. if (use_na_) {
  290. // Insert IA_NA option with iaid=1234.
  291. context_.query_->addOption(Option6IAPtr(new Option6IA(D6O_IA_NA,
  292. 1234)));
  293. }
  294. // IA-PD is also not allowed. So it may be useful in testing, too.
  295. if (use_pd_) {
  296. // Insert IA_PD option with iaid=5678
  297. Option6IAPtr ia(new Option6IA(D6O_IA_PD, 5678));
  298. if (prefix_hint_) {
  299. ia->addOption(prefix_hint_);
  300. }
  301. context_.query_->addOption(ia);
  302. }
  303. sendMsg(context_.query_);
  304. context_.response_ = receiveOneMsg();
  305. }
  306. void
  307. Dhcp6Client::doRebind() {
  308. Pkt6Ptr query = createMsg(DHCPV6_REBIND);
  309. copyIAsFromLeases(query);
  310. context_.query_ = query;
  311. sendMsg(context_.query_);
  312. context_.response_ = receiveOneMsg();
  313. // Apply configuration only if the server has responded.
  314. if (context_.response_) {
  315. applyRcvdConfiguration(context_.response_);
  316. }
  317. }
  318. void
  319. Dhcp6Client::doConfirm() {
  320. context_.query_ = createMsg(DHCPV6_CONFIRM);
  321. copyIAsFromLeases(context_.query_);
  322. sendMsg(context_.query_);
  323. context_.response_ = receiveOneMsg();
  324. // Set the global status code to default: success and not received.
  325. config_.resetGlobalStatusCode();
  326. if (context_.response_) {
  327. applyRcvdConfiguration(context_.response_);
  328. }
  329. }
  330. void
  331. Dhcp6Client::fastFwdTime(const uint32_t secs) {
  332. // Iterate over all leases and move their cltt backwards.
  333. for (int i = 0; i < config_.leases_.size(); ++i) {
  334. config_.leases_[i].lease_.cltt_ -= secs;
  335. }
  336. }
  337. DuidPtr
  338. Dhcp6Client::generateDUID(DUID::DUIDType duid_type) const {
  339. std::vector<uint8_t> duid;
  340. /// @todo remove this check once other DUID types are implemented.
  341. if (duid_type != DUID::DUID_LLT) {
  342. isc_throw(NotImplemented, "currently the Dhcp6Client only supports"
  343. " generation of DUID LLT");
  344. }
  345. duid.push_back(static_cast<uint8_t>(duid_type));
  346. for (int i = 0; i < 4; ++i) {
  347. duid.push_back(static_cast<uint8_t>(rand() % 255));
  348. }
  349. for (int i = 0; i < 6; ++i) {
  350. duid.push_back(static_cast<uint8_t>(i));
  351. }
  352. return (DuidPtr(new DUID(duid)));
  353. }
  354. OptionPtr
  355. Dhcp6Client::getClientId() const {
  356. OptionPtr opt_client_id(new Option(Option::V6,
  357. D6O_CLIENTID,
  358. duid_->getDuid().begin(),
  359. duid_->getDuid().end()));
  360. return (opt_client_id);
  361. }
  362. std::set<uint32_t>
  363. Dhcp6Client::getIAIDs() const {
  364. std::set<uint32_t> iaids;
  365. for (std::vector<LeaseInfo>::const_iterator lease_info =
  366. config_.leases_.begin(); lease_info != config_.leases_.end();
  367. ++lease_info) {
  368. iaids.insert(lease_info->lease_.iaid_);
  369. }
  370. return (iaids);
  371. }
  372. std::vector<Lease6>
  373. Dhcp6Client::getLeasesByIAID(const uint32_t iaid) const {
  374. std::vector<Lease6> leases;
  375. for (std::vector<LeaseInfo>::const_iterator lease_info =
  376. config_.leases_.begin(); lease_info != config_.leases_.end();
  377. ++lease_info) {
  378. if (lease_info->lease_.iaid_ == iaid) {
  379. leases.push_back(lease_info->lease_);
  380. }
  381. }
  382. return (leases);
  383. }
  384. void
  385. Dhcp6Client::modifyDUID() {
  386. if (!duid_) {
  387. duid_ = generateDUID(DUID::DUID_LLT);
  388. return;
  389. }
  390. std::vector<uint8_t> new_duid = duid_->getDuid();
  391. // Modify the DUID by adding 1 to its last byte.
  392. ++new_duid[new_duid.size() - 1];
  393. duid_.reset(new DUID(new_duid));
  394. }
  395. Pkt6Ptr
  396. Dhcp6Client::receiveOneMsg() {
  397. // Return empty pointer if server hasn't responded.
  398. if (srv_->fake_sent_.empty()) {
  399. return (Pkt6Ptr());
  400. }
  401. Pkt6Ptr msg = srv_->fake_sent_.front();
  402. srv_->fake_sent_.pop_front();
  403. return (msg);
  404. }
  405. void
  406. Dhcp6Client::sendMsg(const Pkt6Ptr& msg) {
  407. srv_->shutdown_ = false;
  408. // The client is configured to send through the relay. We achieve that
  409. // adding the relay structure.
  410. if (use_relay_ || !relay_info_.empty()) {
  411. if (relay_info_.empty()) {
  412. // Let's craft the relay info by hand
  413. Pkt6::RelayInfo relay;
  414. relay.linkaddr_ = relay_link_addr_;
  415. relay.peeraddr_ = asiolink::IOAddress("fe80::1");
  416. relay.msg_type_ = DHCPV6_RELAY_FORW;
  417. relay.hop_count_ = 1;
  418. msg->relay_info_.push_back(relay);
  419. } else {
  420. // The test provided relay_info_, let's use that.
  421. msg->relay_info_ = relay_info_;
  422. }
  423. }
  424. // Repack the message to simulate wire-data parsing.
  425. msg->pack();
  426. Pkt6Ptr msg_copy(new Pkt6(static_cast<const uint8_t*>
  427. (msg->getBuffer().getData()),
  428. msg->getBuffer().getLength()));
  429. msg_copy->setRemoteAddr(link_local_);
  430. msg_copy->setLocalAddr(dest_addr_);
  431. msg_copy->setIface("eth0");
  432. srv_->fakeReceive(msg_copy);
  433. srv_->run();
  434. }
  435. void
  436. Dhcp6Client::useHint(const uint32_t pref_lft, const uint32_t valid_lft,
  437. const uint8_t len, const std::string& prefix) {
  438. prefix_hint_.reset(new Option6IAPrefix(D6O_IAPREFIX,
  439. asiolink::IOAddress(prefix),
  440. len, pref_lft, valid_lft));
  441. }
  442. } // end of namespace isc::dhcp::test
  443. } // end of namespace isc::dhcp
  444. } // end of namespace isc