dhcp6_client.cc 19 KB

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