dhcp6_srv.cc 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040
  1. // Copyright (C) 2011-2013 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 <asiolink/io_address.h>
  16. #include <dhcp/dhcp6.h>
  17. #include <dhcp/duid.h>
  18. #include <dhcp/iface_mgr.h>
  19. #include <dhcp/libdhcp++.h>
  20. #include <dhcp/option6_addrlst.h>
  21. #include <dhcp/option6_ia.h>
  22. #include <dhcp/option6_iaaddr.h>
  23. #include <dhcp/option6_iaaddr.h>
  24. #include <dhcp/option_custom.h>
  25. #include <dhcp/option_int_array.h>
  26. #include <dhcp/pkt6.h>
  27. #include <dhcp6/dhcp6_log.h>
  28. #include <dhcp6/dhcp6_srv.h>
  29. #include <dhcpsrv/cfgmgr.h>
  30. #include <dhcpsrv/lease_mgr.h>
  31. #include <dhcpsrv/lease_mgr_factory.h>
  32. #include <dhcpsrv/subnet.h>
  33. #include <dhcpsrv/utils.h>
  34. #include <exceptions/exceptions.h>
  35. #include <util/io_utilities.h>
  36. #include <util/range_utilities.h>
  37. #include <util/encode/hex.h>
  38. #include <boost/foreach.hpp>
  39. #include <boost/tokenizer.hpp>
  40. #include <boost/algorithm/string/erase.hpp>
  41. #include <stdlib.h>
  42. #include <time.h>
  43. #include <iomanip>
  44. #include <fstream>
  45. using namespace isc;
  46. using namespace isc::asiolink;
  47. using namespace isc::dhcp;
  48. using namespace isc::util;
  49. using namespace std;
  50. namespace isc {
  51. namespace dhcp {
  52. Dhcpv6Srv::Dhcpv6Srv(uint16_t port, const char* dbconfig)
  53. : alloc_engine_(), serverid_(), shutdown_(true) {
  54. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
  55. // Initialize objects required for DHCP server operation.
  56. try {
  57. // Port 0 is used for testing purposes. It means that the server should
  58. // not open any sockets at all. Some tests, e.g. configuration parser,
  59. // require Dhcpv6Srv object, but they don't really need it to do
  60. // anything. This speed up and simplifies the tests.
  61. if (port > 0) {
  62. if (IfaceMgr::instance().countIfaces() == 0) {
  63. LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES);
  64. return;
  65. }
  66. IfaceMgr::instance().openSockets6(port);
  67. }
  68. string duid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_DUID_FILE);
  69. if (loadServerID(duid_file)) {
  70. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_SERVERID_LOADED)
  71. .arg(duid_file);
  72. } else {
  73. generateServerID();
  74. LOG_INFO(dhcp6_logger, DHCP6_SERVERID_GENERATED)
  75. .arg(duidToString(getServerID()))
  76. .arg(duid_file);
  77. if (!writeServerID(duid_file)) {
  78. LOG_WARN(dhcp6_logger, DHCP6_SERVERID_WRITE_FAIL)
  79. .arg(duid_file);
  80. }
  81. }
  82. // Instantiate LeaseMgr
  83. LeaseMgrFactory::create(dbconfig);
  84. LOG_INFO(dhcp6_logger, DHCP6_DB_BACKEND_STARTED)
  85. .arg(LeaseMgrFactory::instance().getType())
  86. .arg(LeaseMgrFactory::instance().getName());
  87. // Instantiate allocation engine
  88. alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
  89. } catch (const std::exception &e) {
  90. LOG_ERROR(dhcp6_logger, DHCP6_SRV_CONSTRUCT_ERROR).arg(e.what());
  91. return;
  92. }
  93. // All done, so can proceed
  94. shutdown_ = false;
  95. }
  96. Dhcpv6Srv::~Dhcpv6Srv() {
  97. IfaceMgr::instance().closeSockets();
  98. LeaseMgrFactory::destroy();
  99. }
  100. void Dhcpv6Srv::shutdown() {
  101. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_SHUTDOWN_REQUEST);
  102. shutdown_ = true;
  103. }
  104. bool Dhcpv6Srv::run() {
  105. while (!shutdown_) {
  106. /// @todo: calculate actual timeout to the next event (e.g. lease
  107. /// expiration) once we have lease database. The idea here is that
  108. /// it is possible to do everything in a single process/thread.
  109. /// For now, we are just calling select for 1000 seconds. There
  110. /// were some issues reported on some systems when calling select()
  111. /// with too large values. Unfortunately, I don't recall the details.
  112. int timeout = 1000;
  113. // client's message and server's response
  114. Pkt6Ptr query;
  115. Pkt6Ptr rsp;
  116. try {
  117. query = IfaceMgr::instance().receive6(timeout);
  118. } catch (const std::exception& e) {
  119. LOG_ERROR(dhcp6_logger, DHCP6_PACKET_RECEIVE_FAIL).arg(e.what());
  120. }
  121. if (query) {
  122. if (!query->unpack()) {
  123. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
  124. DHCP6_PACKET_PARSE_FAIL);
  125. continue;
  126. }
  127. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PACKET_RECEIVED)
  128. .arg(query->getName());
  129. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_QUERY_DATA)
  130. .arg(static_cast<int>(query->getType()))
  131. .arg(query->getBuffer().getLength())
  132. .arg(query->toText());
  133. try {
  134. switch (query->getType()) {
  135. case DHCPV6_SOLICIT:
  136. rsp = processSolicit(query);
  137. break;
  138. case DHCPV6_REQUEST:
  139. rsp = processRequest(query);
  140. break;
  141. case DHCPV6_RENEW:
  142. rsp = processRenew(query);
  143. break;
  144. case DHCPV6_REBIND:
  145. rsp = processRebind(query);
  146. break;
  147. case DHCPV6_CONFIRM:
  148. rsp = processConfirm(query);
  149. break;
  150. case DHCPV6_RELEASE:
  151. rsp = processRelease(query);
  152. break;
  153. case DHCPV6_DECLINE:
  154. rsp = processDecline(query);
  155. break;
  156. case DHCPV6_INFORMATION_REQUEST:
  157. rsp = processInfRequest(query);
  158. break;
  159. default:
  160. // Only action is to output a message if debug is enabled,
  161. // and that will be covered by the debug statement before
  162. // the "switch" statement.
  163. ;
  164. }
  165. } catch (const RFCViolation& e) {
  166. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_REQUIRED_OPTIONS_CHECK_FAIL)
  167. .arg(query->getName())
  168. .arg(query->getRemoteAddr())
  169. .arg(e.what());
  170. }
  171. if (rsp) {
  172. rsp->setRemoteAddr(query->getRemoteAddr());
  173. rsp->setLocalAddr(query->getLocalAddr());
  174. rsp->setRemotePort(DHCP6_CLIENT_PORT);
  175. rsp->setLocalPort(DHCP6_SERVER_PORT);
  176. rsp->setIndex(query->getIndex());
  177. rsp->setIface(query->getIface());
  178. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA,
  179. DHCP6_RESPONSE_DATA)
  180. .arg(rsp->getType()).arg(rsp->toText());
  181. if (rsp->pack()) {
  182. try {
  183. IfaceMgr::instance().send(rsp);
  184. } catch (const std::exception& e) {
  185. LOG_ERROR(dhcp6_logger, DHCP6_PACKET_SEND_FAIL).arg(e.what());
  186. }
  187. } else {
  188. LOG_ERROR(dhcp6_logger, DHCP6_PACK_FAIL);
  189. }
  190. }
  191. }
  192. }
  193. return (true);
  194. }
  195. bool Dhcpv6Srv::loadServerID(const std::string& file_name) {
  196. // load content of the file into a string
  197. fstream f(file_name.c_str(), ios::in);
  198. if (!f.is_open()) {
  199. return (false);
  200. }
  201. string hex_string;
  202. f >> hex_string;
  203. f.close();
  204. // remove any spaces
  205. boost::algorithm::erase_all(hex_string, " ");
  206. // now remove :
  207. /// @todo: We should check first if the format is sane.
  208. /// Otherwise 1:2:3:4 will be converted to 0x12, 0x34
  209. boost::algorithm::erase_all(hex_string, ":");
  210. std::vector<uint8_t> bin;
  211. // Decode the hex string and store it in bin (which happens
  212. // to be OptionBuffer format)
  213. isc::util::encode::decodeHex(hex_string, bin);
  214. // Now create server-id option
  215. serverid_.reset(new Option(Option::V6, D6O_SERVERID, bin));
  216. return (true);
  217. }
  218. std::string Dhcpv6Srv::duidToString(const OptionPtr& opt) {
  219. stringstream tmp;
  220. OptionBuffer data = opt->getData();
  221. bool colon = false;
  222. for (OptionBufferConstIter it = data.begin(); it != data.end(); ++it) {
  223. if (colon) {
  224. tmp << ":";
  225. }
  226. tmp << hex << setw(2) << setfill('0') << static_cast<uint16_t>(*it);
  227. if (!colon) {
  228. colon = true;
  229. }
  230. }
  231. return tmp.str();
  232. }
  233. bool Dhcpv6Srv::writeServerID(const std::string& file_name) {
  234. fstream f(file_name.c_str(), ios::out | ios::trunc);
  235. if (!f.good()) {
  236. return (false);
  237. }
  238. f << duidToString(getServerID());
  239. f.close();
  240. }
  241. void Dhcpv6Srv::generateServerID() {
  242. /// @todo: This code implements support for DUID-LLT (the recommended one).
  243. /// We should eventually add support for other DUID types: DUID-LL, DUID-EN
  244. /// and DUID-UUID
  245. const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
  246. // Let's find suitable interface.
  247. for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
  248. iface != ifaces.end(); ++iface) {
  249. // All the following checks could be merged into one multi-condition
  250. // statement, but let's keep them separated as perhaps one day
  251. // we will grow knobs to selectively turn them on or off. Also,
  252. // this code is used only *once* during first start on a new machine
  253. // and then server-id is stored. (or at least it will be once
  254. // DUID storage is implemente
  255. // I wish there was a this_is_a_real_physical_interface flag...
  256. // MAC address should be at least 6 bytes. Although there is no such
  257. // requirement in any RFC, all decent physical interfaces (Ethernet,
  258. // WiFi, Infiniband, etc.) have 6 bytes long MAC address. We want to
  259. // base our DUID on real hardware address, rather than virtual
  260. // interface that pretends that underlying IP address is its MAC.
  261. if (iface->getMacLen() < MIN_MAC_LEN) {
  262. continue;
  263. }
  264. // Let's don't use loopback.
  265. if (iface->flag_loopback_) {
  266. continue;
  267. }
  268. // Let's skip downed interfaces. It is better to use working ones.
  269. if (!iface->flag_up_) {
  270. continue;
  271. }
  272. // Some interfaces (like lo on Linux) report 6-bytes long
  273. // MAC adress 00:00:00:00:00:00. Let's not use such weird interfaces
  274. // to generate DUID.
  275. if (isRangeZero(iface->getMac(), iface->getMac() + iface->getMacLen())) {
  276. continue;
  277. }
  278. // Ok, we have useful MAC. Let's generate DUID-LLT based on
  279. // it. See RFC3315, Section 9.2 for details.
  280. // DUID uses seconds since midnight of 01-01-2000, time() returns
  281. // seconds since 01-01-1970. DUID_TIME_EPOCH substution corrects that.
  282. time_t seconds = time(NULL);
  283. seconds -= DUID_TIME_EPOCH;
  284. OptionBuffer srvid(8 + iface->getMacLen());
  285. writeUint16(DUID::DUID_LLT, &srvid[0]);
  286. writeUint16(HWTYPE_ETHERNET, &srvid[2]);
  287. writeUint32(static_cast<uint32_t>(seconds), &srvid[4]);
  288. memcpy(&srvid[0] + 8, iface->getMac(), iface->getMacLen());
  289. serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
  290. srvid.begin(), srvid.end()));
  291. return;
  292. }
  293. // If we reached here, there are no suitable interfaces found.
  294. // Either interface detection is not supported on this platform or
  295. // this is really weird box. Let's use DUID-EN instead.
  296. // See Section 9.3 of RFC3315 for details.
  297. OptionBuffer srvid(12);
  298. writeUint16(DUID::DUID_EN, &srvid[0]);
  299. writeUint32(ENTERPRISE_ID_ISC, &srvid[2]);
  300. // Length of the identifier is company specific. I hereby declare
  301. // ISC "standard" of 6 bytes long pseudo-random numbers.
  302. srandom(time(NULL));
  303. fillRandom(&srvid[6], &srvid[12]);
  304. serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
  305. srvid.begin(), srvid.end()));
  306. }
  307. void Dhcpv6Srv::copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
  308. // Add client-id.
  309. OptionPtr clientid = question->getOption(D6O_CLIENTID);
  310. if (clientid) {
  311. answer->addOption(clientid);
  312. }
  313. // TODO: Should throw if there is no client-id (except anonymous INF-REQUEST)
  314. }
  315. void Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
  316. // add server-id
  317. answer->addOption(getServerID());
  318. // Get the subnet object. It holds options to be sent to the client
  319. // that belongs to the particular subnet.
  320. Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(question->getRemoteAddr());
  321. // Warn if subnet is not supported and quit.
  322. if (!subnet) {
  323. LOG_WARN(dhcp6_logger, DHCP6_NO_SUBNET_DEF_OPT)
  324. .arg(question->getRemoteAddr().toText());
  325. return;
  326. }
  327. }
  328. void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
  329. // Get the subnet for a particular address.
  330. Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(question->getRemoteAddr());
  331. if (!subnet) {
  332. LOG_WARN(dhcp6_logger, DHCP6_NO_SUBNET_REQ_OPT)
  333. .arg(question->getRemoteAddr().toText());
  334. return;
  335. }
  336. // Client requests some options using ORO option. Try to
  337. // get this option from client's message.
  338. boost::shared_ptr<OptionIntArray<uint16_t> > option_oro =
  339. boost::dynamic_pointer_cast<OptionIntArray<uint16_t> >(question->getOption(D6O_ORO));
  340. // Option ORO not found. Don't do anything then.
  341. if (!option_oro) {
  342. return;
  343. }
  344. // Get the list of options that client requested.
  345. const std::vector<uint16_t>& requested_opts = option_oro->getValues();
  346. // Get the list of options configured for a subnet.
  347. Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp6");
  348. const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
  349. // Try to match requested options with those configured for a subnet.
  350. // If match is found, append configured option to the answer message.
  351. BOOST_FOREACH(uint16_t opt, requested_opts) {
  352. const Subnet::OptionContainerTypeRange& range = idx.equal_range(opt);
  353. BOOST_FOREACH(Subnet::OptionDescriptor desc, range) {
  354. answer->addOption(desc.option);
  355. }
  356. }
  357. }
  358. OptionPtr Dhcpv6Srv::createStatusCode(uint16_t code, const std::string& text) {
  359. // @todo This function uses OptionCustom class to manage contents
  360. // of the data fields. Since this this option is frequently used
  361. // it may be good to implement dedicated class to avoid performance
  362. // impact.
  363. // Get the definition of the option holding status code.
  364. OptionDefinitionPtr status_code_def =
  365. LibDHCP::getOptionDef(Option::V6, D6O_STATUS_CODE);
  366. // This definition is assumed to be initialized in LibDHCP.
  367. assert(status_code_def);
  368. // As there is no dedicated class to represent Status Code
  369. // the OptionCustom class should be returned here.
  370. boost::shared_ptr<OptionCustom> option_status =
  371. boost::dynamic_pointer_cast<
  372. OptionCustom>(status_code_def->optionFactory(Option::V6, D6O_STATUS_CODE));
  373. assert(option_status);
  374. // Set status code to 'code' (0 - means data field #0).
  375. option_status->writeInteger(code, 0);
  376. // Set a message (1 - means data field #1).
  377. option_status->writeString(text, 1);
  378. return (option_status);
  379. }
  380. void Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
  381. RequirementLevel serverid) {
  382. Option::OptionCollection client_ids = pkt->getOptions(D6O_CLIENTID);
  383. switch (clientid) {
  384. case MANDATORY:
  385. if (client_ids.size() != 1) {
  386. isc_throw(RFCViolation, "Exactly 1 client-id option expected in "
  387. << pkt->getName() << ", but " << client_ids.size()
  388. << " received");
  389. }
  390. break;
  391. case OPTIONAL:
  392. if (client_ids.size() > 1) {
  393. isc_throw(RFCViolation, "Too many (" << client_ids.size()
  394. << ") client-id options received in " << pkt->getName());
  395. }
  396. break;
  397. case FORBIDDEN:
  398. // doesn't make sense - client-id is always allowed
  399. break;
  400. }
  401. Option::OptionCollection server_ids = pkt->getOptions(D6O_SERVERID);
  402. switch (serverid) {
  403. case FORBIDDEN:
  404. if (!server_ids.empty()) {
  405. isc_throw(RFCViolation, "Server-id option was not expected, but "
  406. << server_ids.size() << " received in " << pkt->getName());
  407. }
  408. break;
  409. case MANDATORY:
  410. if (server_ids.size() != 1) {
  411. isc_throw(RFCViolation, "Invalid number of server-id options received ("
  412. << server_ids.size() << "), exactly 1 expected in message "
  413. << pkt->getName());
  414. }
  415. break;
  416. case OPTIONAL:
  417. if (server_ids.size() > 1) {
  418. isc_throw(RFCViolation, "Too many (" << server_ids.size()
  419. << ") server-id options received in " << pkt->getName());
  420. }
  421. }
  422. }
  423. Subnet6Ptr Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
  424. /// @todo: pass interface information only if received direct (non-relayed) message
  425. // Try to find a subnet if received packet from a directly connected client
  426. Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(question->getIface());
  427. if (subnet) {
  428. return (subnet);
  429. }
  430. // If no subnet was found, try to find it based on remote address
  431. subnet = CfgMgr::instance().getSubnet6(question->getRemoteAddr());
  432. return (subnet);
  433. }
  434. void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
  435. // We need to allocate addresses for all IA_NA options in the client's
  436. // question (i.e. SOLICIT or REQUEST) message.
  437. // @todo add support for IA_TA
  438. // @todo add support for IA_PD
  439. // We need to select a subnet the client is connected in.
  440. Subnet6Ptr subnet = selectSubnet(question);
  441. if (!subnet) {
  442. // This particular client is out of luck today. We do not have
  443. // information about the subnet he is connected to. This likely means
  444. // misconfiguration of the server (or some relays). We will continue to
  445. // process this message, but our response will be almost useless: no
  446. // addresses or prefixes, no subnet specific configuration etc. The only
  447. // thing this client can get is some global information (like DNS
  448. // servers).
  449. // perhaps this should be logged on some higher level? This is most likely
  450. // configuration bug.
  451. LOG_ERROR(dhcp6_logger, DHCP6_SUBNET_SELECTION_FAILED)
  452. .arg(question->getRemoteAddr().toText())
  453. .arg(question->getName());
  454. } else {
  455. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_SUBNET_SELECTED)
  456. .arg(subnet->toText());
  457. }
  458. // @todo: We should implement Option6Duid some day, but we can do without it
  459. // just fine for now
  460. // Let's find client's DUID. Client is supposed to include its client-id
  461. // option almost all the time (the only exception is an anonymous inf-request,
  462. // but that is mostly a theoretical case). Our allocation engine needs DUID
  463. // and will refuse to allocate anything to anonymous clients.
  464. DuidPtr duid;
  465. OptionPtr opt_duid = question->getOption(D6O_CLIENTID);
  466. if (opt_duid) {
  467. duid = DuidPtr(new DUID(opt_duid->getData()));
  468. } else {
  469. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLIENTID_MISSING);
  470. // Let's drop the message. This client is not sane.
  471. isc_throw(RFCViolation, "Mandatory client-id is missing in received message");
  472. }
  473. // Now that we have all information about the client, let's iterate over all
  474. // received options and handle IA_NA options one by one and store our
  475. // responses in answer message (ADVERTISE or REPLY).
  476. //
  477. // @todo: expand this to cover IA_PD and IA_TA once we implement support for
  478. // prefix delegation and temporary addresses.
  479. for (Option::OptionCollection::iterator opt = question->options_.begin();
  480. opt != question->options_.end(); ++opt) {
  481. switch (opt->second->getType()) {
  482. case D6O_IA_NA: {
  483. OptionPtr answer_opt = assignIA_NA(subnet, duid, question,
  484. boost::dynamic_pointer_cast<Option6IA>(opt->second));
  485. if (answer_opt) {
  486. answer->addOption(answer_opt);
  487. }
  488. break;
  489. }
  490. default:
  491. break;
  492. }
  493. }
  494. }
  495. OptionPtr Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
  496. Pkt6Ptr question, boost::shared_ptr<Option6IA> ia) {
  497. // If there is no subnet selected for handling this IA_NA, the only thing to do left is
  498. // to say that we are sorry, but the user won't get an address. As a convenience, we
  499. // use a different status text to indicate that (compare to the same status code,
  500. // but different wording below)
  501. if (!subnet) {
  502. // Create empty IA_NA option with IAID matching the request.
  503. // Note that we don't use OptionDefinition class to create this option.
  504. // This is because we prefer using a constructor of Option6IA that
  505. // initializes IAID. Otherwise we would have to use setIAID() after
  506. // creation of the option which has some performance implications.
  507. boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
  508. // Insert status code NoAddrsAvail.
  509. ia_rsp->addOption(createStatusCode(STATUS_NoAddrsAvail, "Sorry, no subnet available."));
  510. return (ia_rsp);
  511. }
  512. // Check if the client sent us a hint in his IA_NA. Clients may send an
  513. // address in their IA_NA options as a suggestion (e.g. the last address
  514. // they used before).
  515. boost::shared_ptr<Option6IAAddr> hintOpt = boost::dynamic_pointer_cast<Option6IAAddr>
  516. (ia->getOption(D6O_IAADDR));
  517. IOAddress hint("::");
  518. if (hintOpt) {
  519. hint = hintOpt->getAddress();
  520. }
  521. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_NA_REQUEST)
  522. .arg(duid?duid->toText():"(no-duid)").arg(ia->getIAID())
  523. .arg(hintOpt?hint.toText():"(no hint)");
  524. // "Fake" allocation is processing of SOLICIT message. We pretend to do an
  525. // allocation, but we do not put the lease in the database. That is ok,
  526. // because we do not guarantee that the user will get that exact lease. If
  527. // the user selects this server to do actual allocation (i.e. sends REQUEST)
  528. // it should include this hint. That will help us during the actual lease
  529. // allocation.
  530. bool fake_allocation = false;
  531. if (question->getType() == DHCPV6_SOLICIT) {
  532. /// @todo: Check if we support rapid commit
  533. fake_allocation = true;
  534. }
  535. // Use allocation engine to pick a lease for this client. Allocation engine
  536. // will try to honour the hint, but it is just a hint - some other address
  537. // may be used instead. If fake_allocation is set to false, the lease will
  538. // be inserted into the LeaseMgr as well.
  539. Lease6Ptr lease = alloc_engine_->allocateAddress6(subnet, duid, ia->getIAID(),
  540. hint, fake_allocation);
  541. // Create IA_NA that we will put in the response.
  542. // Do not use OptionDefinition to create option's instance so
  543. // as we can initialize IAID using a constructor.
  544. boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
  545. if (lease) {
  546. // We have a lease! Let's wrap its content into IA_NA option
  547. // with IAADDR suboption.
  548. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, fake_allocation?
  549. DHCP6_LEASE_ADVERT:DHCP6_LEASE_ALLOC)
  550. .arg(lease->addr_.toText())
  551. .arg(duid?duid->toText():"(no-duid)")
  552. .arg(ia->getIAID());
  553. ia_rsp->setT1(subnet->getT1());
  554. ia_rsp->setT2(subnet->getT2());
  555. boost::shared_ptr<Option6IAAddr>
  556. addr(new Option6IAAddr(D6O_IAADDR,
  557. lease->addr_,
  558. lease->preferred_lft_,
  559. lease->valid_lft_));
  560. ia_rsp->addOption(addr);
  561. // It would be possible to insert status code=0(success) as well,
  562. // but this is considered waste of bandwidth as absence of status
  563. // code is considered a success.
  564. } else {
  565. // Allocation engine did not allocate a lease. The engine logged
  566. // cause of that failure. The only thing left is to insert
  567. // status code to pass the sad news to the client.
  568. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, fake_allocation?
  569. DHCP6_LEASE_ADVERT_FAIL:DHCP6_LEASE_ALLOC_FAIL)
  570. .arg(duid?duid->toText():"(no-duid)")
  571. .arg(ia->getIAID())
  572. .arg(subnet->toText());
  573. ia_rsp->addOption(createStatusCode(STATUS_NoAddrsAvail,
  574. "Sorry, no address could be allocated."));
  575. }
  576. return (ia_rsp);
  577. }
  578. OptionPtr Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
  579. Pkt6Ptr question, boost::shared_ptr<Option6IA> ia) {
  580. Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(*duid, ia->getIAID(),
  581. subnet->getID());
  582. if (!lease) {
  583. // client renewing a lease that we don't know about.
  584. // Create empty IA_NA option with IAID matching the request.
  585. boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
  586. // Insert status code NoAddrsAvail.
  587. ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
  588. "Sorry, no known leases for this duid/iaid."));
  589. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_UNKNOWN_RENEW)
  590. .arg(duid->toText())
  591. .arg(ia->getIAID())
  592. .arg(subnet->toText());
  593. return (ia_rsp);
  594. }
  595. lease->preferred_lft_ = subnet->getPreferred();
  596. lease->valid_lft_ = subnet->getValid();
  597. lease->t1_ = subnet->getT1();
  598. lease->t2_ = subnet->getT2();
  599. lease->cltt_ = time(NULL);
  600. LeaseMgrFactory::instance().updateLease6(lease);
  601. // Create empty IA_NA option with IAID matching the request.
  602. boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
  603. ia_rsp->setT1(subnet->getT1());
  604. ia_rsp->setT2(subnet->getT2());
  605. boost::shared_ptr<Option6IAAddr> addr(new Option6IAAddr(D6O_IAADDR,
  606. lease->addr_, lease->preferred_lft_,
  607. lease->valid_lft_));
  608. ia_rsp->addOption(addr);
  609. return (ia_rsp);
  610. }
  611. void Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
  612. // We need to renew addresses for all IA_NA options in the client's
  613. // RENEW message.
  614. // @todo add support for IA_TA
  615. // @todo add support for IA_PD
  616. // We need to select a subnet the client is connected in.
  617. Subnet6Ptr subnet = selectSubnet(renew);
  618. if (!subnet) {
  619. // This particular client is out of luck today. We do not have
  620. // information about the subnet he is connected to. This likely means
  621. // misconfiguration of the server (or some relays). We will continue to
  622. // process this message, but our response will be almost useless: no
  623. // addresses or prefixes, no subnet specific configuration etc. The only
  624. // thing this client can get is some global information (like DNS
  625. // servers).
  626. // perhaps this should be logged on some higher level? This is most likely
  627. // configuration bug.
  628. LOG_ERROR(dhcp6_logger, DHCP6_SUBNET_SELECTION_FAILED);
  629. } else {
  630. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_SUBNET_SELECTED)
  631. .arg(subnet->toText());
  632. }
  633. // Let's find client's DUID. Client is supposed to include its client-id
  634. // option almost all the time (the only exception is an anonymous inf-request,
  635. // but that is mostly a theoretical case). Our allocation engine needs DUID
  636. // and will refuse to allocate anything to anonymous clients.
  637. OptionPtr opt_duid = renew->getOption(D6O_CLIENTID);
  638. if (!opt_duid) {
  639. // This should not happen. We have checked this before.
  640. reply->addOption(createStatusCode(STATUS_UnspecFail,
  641. "You did not include mandatory client-id"));
  642. return;
  643. }
  644. DuidPtr duid(new DUID(opt_duid->getData()));
  645. for (Option::OptionCollection::iterator opt = renew->options_.begin();
  646. opt != renew->options_.end(); ++opt) {
  647. switch (opt->second->getType()) {
  648. case D6O_IA_NA: {
  649. OptionPtr answer_opt = renewIA_NA(subnet, duid, renew,
  650. boost::dynamic_pointer_cast<Option6IA>(opt->second));
  651. if (answer_opt) {
  652. reply->addOption(answer_opt);
  653. }
  654. break;
  655. }
  656. default:
  657. break;
  658. }
  659. }
  660. }
  661. void Dhcpv6Srv::releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply) {
  662. // We need to release addresses for all IA_NA options in the client's
  663. // RELEASE message.
  664. // @todo Add support for IA_TA
  665. // @todo Add support for IA_PD
  666. // @todo Consider supporting more than one address in a single IA_NA.
  667. // That was envisaged by RFC3315, but it never happened. The only
  668. // software that supports that is Dibbler, but its author seriously doubts
  669. // if anyone is really using it. Clients that want more than one address
  670. // just include more instances of IA_NA options.
  671. // Let's find client's DUID. Client is supposed to include its client-id
  672. // option almost all the time (the only exception is an anonymous inf-request,
  673. // but that is mostly a theoretical case). Our allocation engine needs DUID
  674. // and will refuse to allocate anything to anonymous clients.
  675. OptionPtr opt_duid = release->getOption(D6O_CLIENTID);
  676. if (!opt_duid) {
  677. // This should not happen. We have checked this before.
  678. // see sanityCheck() called from processRelease()
  679. LOG_WARN(dhcp6_logger, DHCP6_RELEASE_MISSING_CLIENTID)
  680. .arg(release->getRemoteAddr().toText());
  681. reply->addOption(createStatusCode(STATUS_UnspecFail,
  682. "You did not include mandatory client-id"));
  683. return;
  684. }
  685. DuidPtr duid(new DUID(opt_duid->getData()));
  686. int general_status = STATUS_Success;
  687. for (Option::OptionCollection::iterator opt = release->options_.begin();
  688. opt != release->options_.end(); ++opt) {
  689. switch (opt->second->getType()) {
  690. case D6O_IA_NA: {
  691. OptionPtr answer_opt = releaseIA_NA(duid, release, general_status,
  692. boost::dynamic_pointer_cast<Option6IA>(opt->second));
  693. if (answer_opt) {
  694. reply->addOption(answer_opt);
  695. }
  696. break;
  697. }
  698. // @todo: add support for IA_PD
  699. // @todo: add support for IA_TA
  700. default:
  701. // remaining options are stateless and thus ignored in this context
  702. ;
  703. }
  704. }
  705. // To be pedantic, we should also include status code in the top-level
  706. // scope, not just in each IA_NA. See RFC3315, section 18.2.6.
  707. // This behavior will likely go away in RFC3315bis.
  708. reply->addOption(createStatusCode(general_status,
  709. "Summary status for all processed IA_NAs"));
  710. }
  711. OptionPtr Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, Pkt6Ptr question,
  712. int& general_status,
  713. boost::shared_ptr<Option6IA> ia) {
  714. // Release can be done in one of two ways:
  715. // Approach 1: extract address from client's IA_NA and see if it belongs
  716. // to this particular client.
  717. // Approach 2: find a subnet for this client, get a lease for
  718. // this subnet/duid/iaid and check if its content matches to what the
  719. // client is asking us to release.
  720. //
  721. // This method implements approach 1.
  722. // That's our response
  723. boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
  724. boost::shared_ptr<Option6IAAddr> release_addr = boost::dynamic_pointer_cast<Option6IAAddr>
  725. (ia->getOption(D6O_IAADDR));
  726. if (!release_addr) {
  727. ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
  728. "You did not include address in your RELEASE"));
  729. general_status = STATUS_NoBinding;
  730. return (ia_rsp);
  731. }
  732. Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(release_addr->getAddress());
  733. if (!lease) {
  734. // client releasing a lease that we don't know about.
  735. // Insert status code NoAddrsAvail.
  736. ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
  737. "Sorry, no known leases for this duid/iaid, can't release."));
  738. general_status = STATUS_NoBinding;
  739. LOG_INFO(dhcp6_logger, DHCP6_UNKNOWN_RELEASE)
  740. .arg(duid->toText())
  741. .arg(ia->getIAID());
  742. return (ia_rsp);
  743. }
  744. if (!lease->duid_) {
  745. // Something is gravely wrong here. We do have a lease, but it does not
  746. // have mandatory DUID information attached. Someone was messing with our
  747. // database.
  748. LOG_ERROR(dhcp6_logger, DHCP6_LEASE_WITHOUT_DUID)
  749. .arg(release_addr->getAddress().toText());
  750. general_status = STATUS_UnspecFail;
  751. ia_rsp->addOption(createStatusCode(STATUS_UnspecFail,
  752. "Database consistency check failed when trying to RELEASE"));
  753. return (ia_rsp);
  754. }
  755. if (*duid != *(lease->duid_)) {
  756. // Sorry, it's not your address. You can't release it.
  757. LOG_INFO(dhcp6_logger, DHCP6_RELEASE_FAIL_WRONG_DUID)
  758. .arg(duid->toText())
  759. .arg(release_addr->getAddress().toText())
  760. .arg(lease->duid_->toText());
  761. general_status = STATUS_NoBinding;
  762. ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
  763. "This address does not belong to you, you can't release it"));
  764. return (ia_rsp);
  765. }
  766. if (ia->getIAID() != lease->iaid_) {
  767. // This address belongs to this client, but to a different IA
  768. LOG_WARN(dhcp6_logger, DHCP6_RELEASE_FAIL_WRONG_IAID)
  769. .arg(duid->toText())
  770. .arg(release_addr->getAddress().toText())
  771. .arg(lease->iaid_)
  772. .arg(ia->getIAID());
  773. ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
  774. "This is your address, but you used wrong IAID"));
  775. general_status = STATUS_NoBinding;
  776. return (ia_rsp);
  777. }
  778. // It is not necessary to check if the address matches as we used
  779. // getLease6(addr) method that is supposed to return a proper lease.
  780. // Ok, we've passed all checks. Let's release this address.
  781. if (!LeaseMgrFactory::instance().deleteLease(lease->addr_)) {
  782. ia_rsp->addOption(createStatusCode(STATUS_UnspecFail,
  783. "Server failed to release a lease"));
  784. LOG_ERROR(dhcp6_logger, DHCP6_RELEASE_FAIL)
  785. .arg(lease->addr_.toText())
  786. .arg(duid->toText())
  787. .arg(lease->iaid_);
  788. general_status = STATUS_UnspecFail;
  789. return (ia_rsp);
  790. } else {
  791. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_RELEASE)
  792. .arg(lease->addr_.toText())
  793. .arg(duid->toText())
  794. .arg(lease->iaid_);
  795. ia_rsp->addOption(createStatusCode(STATUS_Success,
  796. "Lease released. Thank you, please come again."));
  797. return (ia_rsp);
  798. }
  799. }
  800. Pkt6Ptr Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
  801. sanityCheck(solicit, MANDATORY, FORBIDDEN);
  802. Pkt6Ptr advertise(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
  803. copyDefaultOptions(solicit, advertise);
  804. appendDefaultOptions(solicit, advertise);
  805. appendRequestedOptions(solicit, advertise);
  806. assignLeases(solicit, advertise);
  807. return (advertise);
  808. }
  809. Pkt6Ptr Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
  810. sanityCheck(request, MANDATORY, MANDATORY);
  811. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
  812. copyDefaultOptions(request, reply);
  813. appendDefaultOptions(request, reply);
  814. appendRequestedOptions(request, reply);
  815. assignLeases(request, reply);
  816. return (reply);
  817. }
  818. Pkt6Ptr Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
  819. sanityCheck(renew, MANDATORY, MANDATORY);
  820. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
  821. copyDefaultOptions(renew, reply);
  822. appendDefaultOptions(renew, reply);
  823. appendRequestedOptions(renew, reply);
  824. renewLeases(renew, reply);
  825. return reply;
  826. }
  827. Pkt6Ptr Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
  828. /// @todo: Implement this
  829. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
  830. return reply;
  831. }
  832. Pkt6Ptr Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
  833. /// @todo: Implement this
  834. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
  835. return reply;
  836. }
  837. Pkt6Ptr Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
  838. sanityCheck(release, MANDATORY, MANDATORY);
  839. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
  840. copyDefaultOptions(release, reply);
  841. appendDefaultOptions(release, reply);
  842. releaseLeases(release, reply);
  843. return reply;
  844. }
  845. Pkt6Ptr Dhcpv6Srv::processDecline(const Pkt6Ptr& decline) {
  846. /// @todo: Implement this
  847. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
  848. return reply;
  849. }
  850. Pkt6Ptr Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
  851. /// @todo: Implement this
  852. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, infRequest->getTransid()));
  853. return reply;
  854. }
  855. };
  856. };