dhcp6_srv.cc 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030
  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.size() > 0) {
  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. Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(question->getRemoteAddr());
  425. return (subnet);
  426. }
  427. void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
  428. // We need to allocate addresses for all IA_NA options in the client's
  429. // question (i.e. SOLICIT or REQUEST) message.
  430. // @todo add support for IA_TA
  431. // @todo add support for IA_PD
  432. // We need to select a subnet the client is connected in.
  433. Subnet6Ptr subnet = selectSubnet(question);
  434. if (!subnet) {
  435. // This particular client is out of luck today. We do not have
  436. // information about the subnet he is connected to. This likely means
  437. // misconfiguration of the server (or some relays). We will continue to
  438. // process this message, but our response will be almost useless: no
  439. // addresses or prefixes, no subnet specific configuration etc. The only
  440. // thing this client can get is some global information (like DNS
  441. // servers).
  442. // perhaps this should be logged on some higher level? This is most likely
  443. // configuration bug.
  444. LOG_ERROR(dhcp6_logger, DHCP6_SUBNET_SELECTION_FAILED)
  445. .arg(question->getRemoteAddr().toText())
  446. .arg(question->getName());
  447. } else {
  448. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_SUBNET_SELECTED)
  449. .arg(subnet->toText());
  450. }
  451. // @todo: We should implement Option6Duid some day, but we can do without it
  452. // just fine for now
  453. // Let's find client's DUID. Client is supposed to include its client-id
  454. // option almost all the time (the only exception is an anonymous inf-request,
  455. // but that is mostly a theoretical case). Our allocation engine needs DUID
  456. // and will refuse to allocate anything to anonymous clients.
  457. DuidPtr duid;
  458. OptionPtr opt_duid = question->getOption(D6O_CLIENTID);
  459. if (opt_duid) {
  460. duid = DuidPtr(new DUID(opt_duid->getData()));
  461. } else {
  462. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLIENTID_MISSING);
  463. // Let's drop the message. This client is not sane.
  464. isc_throw(RFCViolation, "Mandatory client-id is missing in received message");
  465. }
  466. // Now that we have all information about the client, let's iterate over all
  467. // received options and handle IA_NA options one by one and store our
  468. // responses in answer message (ADVERTISE or REPLY).
  469. //
  470. // @todo: expand this to cover IA_PD and IA_TA once we implement support for
  471. // prefix delegation and temporary addresses.
  472. for (Option::OptionCollection::iterator opt = question->options_.begin();
  473. opt != question->options_.end(); ++opt) {
  474. switch (opt->second->getType()) {
  475. case D6O_IA_NA: {
  476. OptionPtr answer_opt = assignIA_NA(subnet, duid, question,
  477. boost::dynamic_pointer_cast<Option6IA>(opt->second));
  478. if (answer_opt) {
  479. answer->addOption(answer_opt);
  480. }
  481. break;
  482. }
  483. default:
  484. break;
  485. }
  486. }
  487. }
  488. OptionPtr Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
  489. Pkt6Ptr question, boost::shared_ptr<Option6IA> ia) {
  490. // If there is no subnet selected for handling this IA_NA, the only thing to do left is
  491. // to say that we are sorry, but the user won't get an address. As a convenience, we
  492. // use a different status text to indicate that (compare to the same status code,
  493. // but different wording below)
  494. if (!subnet) {
  495. // Create empty IA_NA option with IAID matching the request.
  496. // Note that we don't use OptionDefinition class to create this option.
  497. // This is because we prefer using a constructor of Option6IA that
  498. // initializes IAID. Otherwise we would have to use setIAID() after
  499. // creation of the option which has some performance implications.
  500. boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
  501. // Insert status code NoAddrsAvail.
  502. ia_rsp->addOption(createStatusCode(STATUS_NoAddrsAvail, "Sorry, no subnet available."));
  503. return (ia_rsp);
  504. }
  505. // Check if the client sent us a hint in his IA_NA. Clients may send an
  506. // address in their IA_NA options as a suggestion (e.g. the last address
  507. // they used before).
  508. boost::shared_ptr<Option6IAAddr> hintOpt = boost::dynamic_pointer_cast<Option6IAAddr>
  509. (ia->getOption(D6O_IAADDR));
  510. IOAddress hint("::");
  511. if (hintOpt) {
  512. hint = hintOpt->getAddress();
  513. }
  514. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_NA_REQUEST)
  515. .arg(duid?duid->toText():"(no-duid)").arg(ia->getIAID())
  516. .arg(hintOpt?hint.toText():"(no hint)");
  517. // "Fake" allocation is processing of SOLICIT message. We pretend to do an
  518. // allocation, but we do not put the lease in the database. That is ok,
  519. // because we do not guarantee that the user will get that exact lease. If
  520. // the user selects this server to do actual allocation (i.e. sends REQUEST)
  521. // it should include this hint. That will help us during the actual lease
  522. // allocation.
  523. bool fake_allocation = false;
  524. if (question->getType() == DHCPV6_SOLICIT) {
  525. /// @todo: Check if we support rapid commit
  526. fake_allocation = true;
  527. }
  528. // Use allocation engine to pick a lease for this client. Allocation engine
  529. // will try to honour the hint, but it is just a hint - some other address
  530. // may be used instead. If fake_allocation is set to false, the lease will
  531. // be inserted into the LeaseMgr as well.
  532. Lease6Ptr lease = alloc_engine_->allocateAddress6(subnet, duid, ia->getIAID(),
  533. hint, fake_allocation);
  534. // Create IA_NA that we will put in the response.
  535. // Do not use OptionDefinition to create option's instance so
  536. // as we can initialize IAID using a constructor.
  537. boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
  538. if (lease) {
  539. // We have a lease! Let's wrap its content into IA_NA option
  540. // with IAADDR suboption.
  541. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, fake_allocation?
  542. DHCP6_LEASE_ADVERT:DHCP6_LEASE_ALLOC)
  543. .arg(lease->addr_.toText())
  544. .arg(duid?duid->toText():"(no-duid)")
  545. .arg(ia->getIAID());
  546. ia_rsp->setT1(subnet->getT1());
  547. ia_rsp->setT2(subnet->getT2());
  548. boost::shared_ptr<Option6IAAddr>
  549. addr(new Option6IAAddr(D6O_IAADDR,
  550. lease->addr_,
  551. lease->preferred_lft_,
  552. lease->valid_lft_));
  553. ia_rsp->addOption(addr);
  554. // It would be possible to insert status code=0(success) as well,
  555. // but this is considered waste of bandwidth as absence of status
  556. // code is considered a success.
  557. } else {
  558. // Allocation engine did not allocate a lease. The engine logged
  559. // cause of that failure. The only thing left is to insert
  560. // status code to pass the sad news to the client.
  561. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, fake_allocation?
  562. DHCP6_LEASE_ADVERT_FAIL:DHCP6_LEASE_ALLOC_FAIL)
  563. .arg(duid?duid->toText():"(no-duid)")
  564. .arg(ia->getIAID())
  565. .arg(subnet->toText());
  566. ia_rsp->addOption(createStatusCode(STATUS_NoAddrsAvail,
  567. "Sorry, no address could be allocated."));
  568. }
  569. return (ia_rsp);
  570. }
  571. OptionPtr Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
  572. Pkt6Ptr question, boost::shared_ptr<Option6IA> ia) {
  573. Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(*duid, ia->getIAID(),
  574. subnet->getID());
  575. if (!lease) {
  576. // client renewing a lease that we don't know about.
  577. // Create empty IA_NA option with IAID matching the request.
  578. boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
  579. // Insert status code NoAddrsAvail.
  580. ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
  581. "Sorry, no known leases for this duid/iaid."));
  582. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_UNKNOWN_RENEW)
  583. .arg(duid->toText())
  584. .arg(ia->getIAID())
  585. .arg(subnet->toText());
  586. return (ia_rsp);
  587. }
  588. lease->preferred_lft_ = subnet->getPreferred();
  589. lease->valid_lft_ = subnet->getValid();
  590. lease->t1_ = subnet->getT1();
  591. lease->t2_ = subnet->getT2();
  592. lease->cltt_ = time(NULL);
  593. LeaseMgrFactory::instance().updateLease6(lease);
  594. // Create empty IA_NA option with IAID matching the request.
  595. boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
  596. ia_rsp->setT1(subnet->getT1());
  597. ia_rsp->setT2(subnet->getT2());
  598. boost::shared_ptr<Option6IAAddr> addr(new Option6IAAddr(D6O_IAADDR,
  599. lease->addr_, lease->preferred_lft_,
  600. lease->valid_lft_));
  601. ia_rsp->addOption(addr);
  602. return (ia_rsp);
  603. }
  604. void Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
  605. // We need to renew addresses for all IA_NA options in the client's
  606. // RENEW message.
  607. // @todo add support for IA_TA
  608. // @todo add support for IA_PD
  609. // We need to select a subnet the client is connected in.
  610. Subnet6Ptr subnet = selectSubnet(renew);
  611. if (!subnet) {
  612. // This particular client is out of luck today. We do not have
  613. // information about the subnet he is connected to. This likely means
  614. // misconfiguration of the server (or some relays). We will continue to
  615. // process this message, but our response will be almost useless: no
  616. // addresses or prefixes, no subnet specific configuration etc. The only
  617. // thing this client can get is some global information (like DNS
  618. // servers).
  619. // perhaps this should be logged on some higher level? This is most likely
  620. // configuration bug.
  621. LOG_ERROR(dhcp6_logger, DHCP6_SUBNET_SELECTION_FAILED);
  622. } else {
  623. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_SUBNET_SELECTED)
  624. .arg(subnet->toText());
  625. }
  626. // Let's find client's DUID. Client is supposed to include its client-id
  627. // option almost all the time (the only exception is an anonymous inf-request,
  628. // but that is mostly a theoretical case). Our allocation engine needs DUID
  629. // and will refuse to allocate anything to anonymous clients.
  630. OptionPtr opt_duid = renew->getOption(D6O_CLIENTID);
  631. if (!opt_duid) {
  632. // This should not happen. We have checked this before.
  633. reply->addOption(createStatusCode(STATUS_UnspecFail,
  634. "You did not include mandatory client-id"));
  635. return;
  636. }
  637. DuidPtr duid(new DUID(opt_duid->getData()));
  638. for (Option::OptionCollection::iterator opt = renew->options_.begin();
  639. opt != renew->options_.end(); ++opt) {
  640. switch (opt->second->getType()) {
  641. case D6O_IA_NA: {
  642. OptionPtr answer_opt = renewIA_NA(subnet, duid, renew,
  643. boost::dynamic_pointer_cast<Option6IA>(opt->second));
  644. if (answer_opt) {
  645. reply->addOption(answer_opt);
  646. }
  647. break;
  648. }
  649. default:
  650. break;
  651. }
  652. }
  653. }
  654. void Dhcpv6Srv::releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply) {
  655. // We need to release addresses for all IA_NA options in the client's
  656. // RELEASE message.
  657. // @todo Add support for IA_TA
  658. // @todo Add support for IA_PD
  659. // @todo Consider supporting more than one address in a single IA_NA.
  660. // That was envisaged by RFC3315, but it never happened. The only
  661. // software that supports that is Dibbler, but its author seriously doubts
  662. // if anyone is really using it. Clients that want more than one address
  663. // just include more instances of IA_NA options.
  664. // Let's find client's DUID. Client is supposed to include its client-id
  665. // option almost all the time (the only exception is an anonymous inf-request,
  666. // but that is mostly a theoretical case). Our allocation engine needs DUID
  667. // and will refuse to allocate anything to anonymous clients.
  668. OptionPtr opt_duid = release->getOption(D6O_CLIENTID);
  669. if (!opt_duid) {
  670. // This should not happen. We have checked this before.
  671. // see sanityCheck() called from processRelease()
  672. LOG_WARN(dhcp6_logger, DHCP6_RELEASE_MISSING_CLIENTID)
  673. .arg(release->getRemoteAddr().toText());
  674. reply->addOption(createStatusCode(STATUS_UnspecFail,
  675. "You did not include mandatory client-id"));
  676. return;
  677. }
  678. DuidPtr duid(new DUID(opt_duid->getData()));
  679. int general_status = STATUS_Success;
  680. for (Option::OptionCollection::iterator opt = release->options_.begin();
  681. opt != release->options_.end(); ++opt) {
  682. switch (opt->second->getType()) {
  683. case D6O_IA_NA: {
  684. OptionPtr answer_opt = releaseIA_NA(duid, release, general_status,
  685. boost::dynamic_pointer_cast<Option6IA>(opt->second));
  686. if (answer_opt) {
  687. reply->addOption(answer_opt);
  688. }
  689. break;
  690. }
  691. // @todo: add support for IA_PD
  692. // @todo: add support for IA_TA
  693. default:
  694. // remaining options are stateless and thus ignored in this context
  695. ;
  696. }
  697. }
  698. // To be pedantic, we should also include status code in the top-level
  699. // scope, not just in each IA_NA. See RFC3315, section 18.2.6.
  700. // This behavior will likely go away in RFC3315bis.
  701. reply->addOption(createStatusCode(general_status,
  702. "Summary status for all processed IA_NAs"));
  703. }
  704. OptionPtr Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, Pkt6Ptr question,
  705. int& general_status,
  706. boost::shared_ptr<Option6IA> ia) {
  707. // Release can be done in one of two ways:
  708. // Approach 1: extract address from client's IA_NA and see if it belongs
  709. // to this particular client.
  710. // Approach 2: find a subnet for this client, get a lease for
  711. // this subnet/duid/iaid and check if its content matches to what the
  712. // client is asking us to release.
  713. //
  714. // This method implements approach 1.
  715. // That's our response
  716. boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
  717. boost::shared_ptr<Option6IAAddr> release_addr = boost::dynamic_pointer_cast<Option6IAAddr>
  718. (ia->getOption(D6O_IAADDR));
  719. if (!release_addr) {
  720. ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
  721. "You did not include address in your RELEASE"));
  722. general_status = STATUS_NoBinding;
  723. return (ia_rsp);
  724. }
  725. Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(release_addr->getAddress());
  726. if (!lease) {
  727. // client releasing a lease that we don't know about.
  728. // Insert status code NoAddrsAvail.
  729. ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
  730. "Sorry, no known leases for this duid/iaid, can't release."));
  731. general_status = STATUS_NoBinding;
  732. LOG_INFO(dhcp6_logger, DHCP6_UNKNOWN_RELEASE)
  733. .arg(duid->toText())
  734. .arg(ia->getIAID());
  735. return (ia_rsp);
  736. }
  737. if (!lease->duid_) {
  738. // Something is gravely wrong here. We do have a lease, but it does not
  739. // have mandatory DUID information attached. Someone was messing with our
  740. // database.
  741. LOG_ERROR(dhcp6_logger, DHCP6_LEASE_WITHOUT_DUID)
  742. .arg(release_addr->getAddress().toText());
  743. general_status = STATUS_UnspecFail;
  744. ia_rsp->addOption(createStatusCode(STATUS_UnspecFail,
  745. "Database consistency check failed when trying to RELEASE"));
  746. return (ia_rsp);
  747. }
  748. if (*duid != *(lease->duid_)) {
  749. // Sorry, it's not your address. You can't release it.
  750. LOG_INFO(dhcp6_logger, DHCP6_RELEASE_FAIL_WRONG_DUID)
  751. .arg(duid->toText())
  752. .arg(release_addr->getAddress().toText())
  753. .arg(lease->duid_->toText());
  754. general_status = STATUS_NoBinding;
  755. ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
  756. "This address does not belong to you, you can't release it"));
  757. return (ia_rsp);
  758. }
  759. if (ia->getIAID() != lease->iaid_) {
  760. // This address belongs to this client, but to a different IA
  761. LOG_WARN(dhcp6_logger, DHCP6_RELEASE_FAIL_WRONG_IAID)
  762. .arg(duid->toText())
  763. .arg(release_addr->getAddress().toText())
  764. .arg(lease->iaid_)
  765. .arg(ia->getIAID());
  766. ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
  767. "This is your address, but you used wrong IAID"));
  768. general_status = STATUS_NoBinding;
  769. return (ia_rsp);
  770. }
  771. // It is not necessary to check if the address matches as we used
  772. // getLease6(addr) method that is supposed to return a proper lease.
  773. // Ok, we've passed all checks. Let's release this address.
  774. if (!LeaseMgrFactory::instance().deleteLease(lease->addr_)) {
  775. ia_rsp->addOption(createStatusCode(STATUS_UnspecFail,
  776. "Server failed to release a lease"));
  777. LOG_ERROR(dhcp6_logger, DHCP6_RELEASE_FAIL)
  778. .arg(lease->addr_.toText())
  779. .arg(duid->toText())
  780. .arg(lease->iaid_);
  781. general_status = STATUS_UnspecFail;
  782. return (ia_rsp);
  783. } else {
  784. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_RELEASE)
  785. .arg(lease->addr_.toText())
  786. .arg(duid->toText())
  787. .arg(lease->iaid_);
  788. ia_rsp->addOption(createStatusCode(STATUS_Success,
  789. "Lease released. Thank you, please come again."));
  790. return (ia_rsp);
  791. }
  792. }
  793. Pkt6Ptr Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
  794. sanityCheck(solicit, MANDATORY, FORBIDDEN);
  795. Pkt6Ptr advertise(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
  796. copyDefaultOptions(solicit, advertise);
  797. appendDefaultOptions(solicit, advertise);
  798. appendRequestedOptions(solicit, advertise);
  799. assignLeases(solicit, advertise);
  800. return (advertise);
  801. }
  802. Pkt6Ptr Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
  803. sanityCheck(request, MANDATORY, MANDATORY);
  804. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
  805. copyDefaultOptions(request, reply);
  806. appendDefaultOptions(request, reply);
  807. appendRequestedOptions(request, reply);
  808. assignLeases(request, reply);
  809. return (reply);
  810. }
  811. Pkt6Ptr Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
  812. sanityCheck(renew, MANDATORY, MANDATORY);
  813. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
  814. copyDefaultOptions(renew, reply);
  815. appendDefaultOptions(renew, reply);
  816. appendRequestedOptions(renew, reply);
  817. renewLeases(renew, reply);
  818. return reply;
  819. }
  820. Pkt6Ptr Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
  821. /// @todo: Implement this
  822. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
  823. return reply;
  824. }
  825. Pkt6Ptr Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
  826. /// @todo: Implement this
  827. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
  828. return reply;
  829. }
  830. Pkt6Ptr Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
  831. sanityCheck(release, MANDATORY, MANDATORY);
  832. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
  833. copyDefaultOptions(release, reply);
  834. appendDefaultOptions(release, reply);
  835. releaseLeases(release, reply);
  836. return reply;
  837. }
  838. Pkt6Ptr Dhcpv6Srv::processDecline(const Pkt6Ptr& decline) {
  839. /// @todo: Implement this
  840. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
  841. return reply;
  842. }
  843. Pkt6Ptr Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
  844. /// @todo: Implement this
  845. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, infRequest->getTransid()));
  846. return reply;
  847. }
  848. };
  849. };