dhcp6_srv.cc 39 KB

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