dhcp_parsers.cc 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466
  1. // Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <dhcp/iface_mgr.h>
  15. #include <dhcp/libdhcp++.h>
  16. #include <dhcpsrv/cfgmgr.h>
  17. #include <dhcpsrv/dhcp_parsers.h>
  18. #include <hooks/hooks_manager.h>
  19. #include <util/encode/hex.h>
  20. #include <util/strutil.h>
  21. #include <boost/algorithm/string.hpp>
  22. #include <boost/foreach.hpp>
  23. #include <boost/lexical_cast.hpp>
  24. #include <map>
  25. #include <string>
  26. #include <vector>
  27. using namespace std;
  28. using namespace isc::data;
  29. using namespace isc::hooks;
  30. namespace isc {
  31. namespace dhcp {
  32. namespace {
  33. const char* ALL_IFACES_KEYWORD = "*";
  34. }
  35. // *********************** ParserContext *************************
  36. ParserContext::ParserContext(Option::Universe universe):
  37. boolean_values_(new BooleanStorage()),
  38. uint32_values_(new Uint32Storage()),
  39. string_values_(new StringStorage()),
  40. options_(new OptionStorage()),
  41. option_defs_(new OptionDefStorage()),
  42. hooks_libraries_(),
  43. universe_(universe)
  44. {
  45. }
  46. ParserContext::ParserContext(const ParserContext& rhs):
  47. boolean_values_(),
  48. uint32_values_(),
  49. string_values_(),
  50. options_(),
  51. option_defs_(),
  52. hooks_libraries_(),
  53. universe_(rhs.universe_)
  54. {
  55. copyContext(rhs);
  56. }
  57. ParserContext&
  58. // The cppcheck version 1.56 doesn't recognize that copyContext
  59. // copies all context fields.
  60. // cppcheck-suppress operatorEqVarError
  61. ParserContext::operator=(const ParserContext& rhs) {
  62. if (this != &rhs) {
  63. copyContext(rhs);
  64. }
  65. return (*this);
  66. }
  67. void
  68. ParserContext::copyContext(const ParserContext& ctx) {
  69. copyContextPointer(ctx.boolean_values_, boolean_values_);
  70. copyContextPointer(ctx.uint32_values_, uint32_values_);
  71. copyContextPointer(ctx.string_values_, string_values_);
  72. copyContextPointer(ctx.options_, options_);
  73. copyContextPointer(ctx.option_defs_, option_defs_);
  74. copyContextPointer(ctx.hooks_libraries_, hooks_libraries_);
  75. // Copy universe.
  76. universe_ = ctx.universe_;
  77. }
  78. template<typename T>
  79. void
  80. ParserContext::copyContextPointer(const boost::shared_ptr<T>& source_ptr,
  81. boost::shared_ptr<T>& dest_ptr) {
  82. if (source_ptr) {
  83. dest_ptr.reset(new T(*source_ptr));
  84. } else {
  85. dest_ptr.reset();
  86. }
  87. }
  88. // **************************** DebugParser *************************
  89. DebugParser::DebugParser(const std::string& param_name)
  90. :param_name_(param_name) {
  91. }
  92. void
  93. DebugParser::build(ConstElementPtr new_config) {
  94. value_ = new_config;
  95. std::cout << "Build for token: [" << param_name_ << "] = ["
  96. << value_->str() << "]" << std::endl;
  97. }
  98. void
  99. DebugParser::commit() {
  100. // Debug message. The whole DebugParser class is used only for parser
  101. // debugging, and is not used in production code. It is very convenient
  102. // to keep it around. Please do not turn this cout into logger calls.
  103. std::cout << "Commit for token: [" << param_name_ << "] = ["
  104. << value_->str() << "]" << std::endl;
  105. }
  106. // **************************** BooleanParser *************************
  107. template<> void ValueParser<bool>::build(isc::data::ConstElementPtr value) {
  108. // Invoke common code for all specializations of build().
  109. buildCommon(value);
  110. // The Config Manager checks if user specified a
  111. // valid value for a boolean parameter: True or False.
  112. // We should have a boolean Element, use value directly
  113. try {
  114. value_ = value->boolValue();
  115. } catch (const isc::data::TypeError &) {
  116. isc_throw(BadValue, " Wrong value type for " << param_name_
  117. << " : build called with a non-boolean element "
  118. << "(" << value->getPosition() << ").");
  119. }
  120. }
  121. // **************************** Uin32Parser *************************
  122. template<> void ValueParser<uint32_t>::build(ConstElementPtr value) {
  123. // Invoke common code for all specializations of build().
  124. buildCommon(value);
  125. int64_t check;
  126. string x = value->str();
  127. try {
  128. check = boost::lexical_cast<int64_t>(x);
  129. } catch (const boost::bad_lexical_cast &) {
  130. isc_throw(BadValue, "Failed to parse value " << value->str()
  131. << " as unsigned 32-bit integer "
  132. "(" << value->getPosition() << ").");
  133. }
  134. if (check > std::numeric_limits<uint32_t>::max()) {
  135. isc_throw(BadValue, "Value " << value->str() << " is too large"
  136. " for unsigned 32-bit integer "
  137. "(" << value->getPosition() << ").");
  138. }
  139. if (check < 0) {
  140. isc_throw(BadValue, "Value " << value->str() << " is negative."
  141. << " Only 0 or larger are allowed for unsigned 32-bit integer "
  142. "(" << value->getPosition() << ").");
  143. }
  144. // value is small enough to fit
  145. value_ = static_cast<uint32_t>(check);
  146. }
  147. // **************************** StringParser *************************
  148. template <> void ValueParser<std::string>::build(ConstElementPtr value) {
  149. // Invoke common code for all specializations of build().
  150. buildCommon(value);
  151. value_ = value->str();
  152. boost::erase_all(value_, "\"");
  153. }
  154. // ******************** InterfaceListConfigParser *************************
  155. InterfaceListConfigParser::
  156. InterfaceListConfigParser(const std::string& param_name)
  157. : activate_all_(false),
  158. param_name_(param_name) {
  159. if (param_name_ != "interfaces") {
  160. isc_throw(BadValue, "Internal error. Interface configuration "
  161. "parser called for the wrong parameter: " << param_name);
  162. }
  163. }
  164. void
  165. InterfaceListConfigParser::build(ConstElementPtr value) {
  166. // First, we iterate over all specified entries and add it to the
  167. // local container so as we can do some basic validation, e.g. eliminate
  168. // duplicates.
  169. BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
  170. std::string iface_name = iface->stringValue();
  171. if (iface_name != ALL_IFACES_KEYWORD) {
  172. // Let's eliminate duplicates. We could possibly allow duplicates,
  173. // but if someone specified duplicated interface name it is likely
  174. // that he mistyped the configuration. Failing here should draw his
  175. // attention.
  176. if (isIfaceAdded(iface_name)) {
  177. isc_throw(isc::dhcp::DhcpConfigError, "duplicate interface"
  178. << " name '" << iface_name << "' specified in '"
  179. << param_name_ << "' configuration parameter "
  180. "(" << value->getPosition() << ")");
  181. }
  182. // @todo check that this interface exists in the system!
  183. // The IfaceMgr exposes mechanisms to check this.
  184. // Add the interface name if ok.
  185. interfaces_.push_back(iface_name);
  186. } else {
  187. activate_all_ = true;
  188. }
  189. }
  190. }
  191. void
  192. InterfaceListConfigParser::commit() {
  193. CfgMgr& cfg_mgr = CfgMgr::instance();
  194. // Remove active interfaces and clear a flag which marks all interfaces
  195. // active
  196. cfg_mgr.deleteActiveIfaces();
  197. if (activate_all_) {
  198. // Activate all interfaces. There is not need to add their names
  199. // explicitly.
  200. cfg_mgr.activateAllIfaces();
  201. } else {
  202. // Explicitly add names of the interfaces which server should listen on.
  203. BOOST_FOREACH(std::string iface, interfaces_) {
  204. cfg_mgr.addActiveIface(iface);
  205. }
  206. }
  207. }
  208. bool
  209. InterfaceListConfigParser::isIfaceAdded(const std::string& iface) const {
  210. for (IfaceListStorage::const_iterator it = interfaces_.begin();
  211. it != interfaces_.end(); ++it) {
  212. if (iface == *it) {
  213. return (true);
  214. }
  215. }
  216. return (false);
  217. }
  218. // ******************** HooksLibrariesParser *************************
  219. HooksLibrariesParser::HooksLibrariesParser(const std::string& param_name)
  220. : libraries_(), changed_(false)
  221. {
  222. // Sanity check on the name.
  223. if (param_name != "hooks-libraries") {
  224. isc_throw(BadValue, "Internal error. Hooks libraries "
  225. "parser called for the wrong parameter: " << param_name);
  226. }
  227. }
  228. void
  229. HooksLibrariesParser::build(ConstElementPtr value) {
  230. // Initialize.
  231. libraries_.clear();
  232. changed_ = false;
  233. // Extract the list of libraries.
  234. BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
  235. string libname = iface->str();
  236. boost::erase_all(libname, "\"");
  237. libraries_.push_back(libname);
  238. }
  239. // Check if the list of libraries has changed. If not, nothing is done
  240. // - the command "DhcpN libreload" is required to reload the same
  241. // libraries (this prevents needless reloads when anything else in the
  242. // configuration is changed).
  243. vector<string> current_libraries = HooksManager::getLibraryNames();
  244. if (current_libraries == libraries_) {
  245. return;
  246. }
  247. // Library list has changed, validate each of the libraries specified.
  248. vector<string> error_libs = HooksManager::validateLibraries(libraries_);
  249. if (!error_libs.empty()) {
  250. // Construct the list of libraries in error for the message.
  251. string error_list = error_libs[0];
  252. for (int i = 1; i < error_libs.size(); ++i) {
  253. error_list += (string(", ") + error_libs[i]);
  254. }
  255. isc_throw(DhcpConfigError, "hooks libraries failed to validate - "
  256. "library or libraries in error are: " << error_list
  257. << " (" << value->getPosition() << ")");
  258. }
  259. // The library list has changed and the libraries are valid, so flag for
  260. // update when commit() is called.
  261. changed_ = true;
  262. }
  263. void
  264. HooksLibrariesParser::commit() {
  265. /// Commits the list of libraries to the configuration manager storage if
  266. /// the list of libraries has changed.
  267. if (changed_) {
  268. // TODO Delete any stored CalloutHandles before reloading the
  269. // libraries
  270. HooksManager::loadLibraries(libraries_);
  271. }
  272. }
  273. // Method for testing
  274. void
  275. HooksLibrariesParser::getLibraries(std::vector<std::string>& libraries,
  276. bool& changed) {
  277. libraries = libraries_;
  278. changed = changed_;
  279. }
  280. // **************************** OptionDataParser *************************
  281. OptionDataParser::OptionDataParser(const std::string&, OptionStoragePtr options,
  282. ParserContextPtr global_context)
  283. : boolean_values_(new BooleanStorage()),
  284. string_values_(new StringStorage()), uint32_values_(new Uint32Storage()),
  285. options_(options), option_descriptor_(false),
  286. global_context_(global_context) {
  287. if (!options_) {
  288. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  289. << "options storage may not be NULL");
  290. }
  291. if (!global_context_) {
  292. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  293. << "context may may not be NULL");
  294. }
  295. }
  296. void
  297. OptionDataParser::build(ConstElementPtr option_data_entries) {
  298. BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
  299. ParserPtr parser;
  300. if (param.first == "name" || param.first == "data" ||
  301. param.first == "space") {
  302. StringParserPtr name_parser(new StringParser(param.first,
  303. string_values_));
  304. parser = name_parser;
  305. } else if (param.first == "code") {
  306. Uint32ParserPtr code_parser(new Uint32Parser(param.first,
  307. uint32_values_));
  308. parser = code_parser;
  309. } else if (param.first == "csv-format") {
  310. BooleanParserPtr value_parser(new BooleanParser(param.first,
  311. boolean_values_));
  312. parser = value_parser;
  313. } else {
  314. isc_throw(DhcpConfigError,
  315. "option-data parameter not supported: " << param.first
  316. << " (" << param.second->getPosition() << ")");
  317. }
  318. parser->build(param.second);
  319. // Before we can create an option we need to get the data from
  320. // the child parsers. The only way to do it is to invoke commit
  321. // on them so as they store the values in appropriate storages
  322. // that this class provided to them. Note that this will not
  323. // modify values stored in the global storages so the configuration
  324. // will remain consistent even parsing fails somewhere further on.
  325. parser->commit();
  326. }
  327. // Try to create the option instance.
  328. createOption(option_data_entries);
  329. }
  330. void
  331. OptionDataParser::commit() {
  332. if (!option_descriptor_.option) {
  333. // Before we can commit the new option should be configured. If it is
  334. // not than somebody must have called commit() before build().
  335. isc_throw(isc::InvalidOperation,
  336. "parser logic error: no option has been configured and"
  337. " thus there is nothing to commit. Has build() been called?");
  338. }
  339. uint16_t opt_type = option_descriptor_.option->getType();
  340. Subnet::OptionContainerPtr options = options_->getItems(option_space_);
  341. // The getItems() should never return NULL pointer. If there are no
  342. // options configured for the particular option space a pointer
  343. // to an empty container should be returned.
  344. assert(options);
  345. Subnet::OptionContainerTypeIndex& idx = options->get<1>();
  346. // Try to find options with the particular option code in the main
  347. // storage. If found, remove these options because they will be
  348. // replaced with new one.
  349. Subnet::OptionContainerTypeRange range = idx.equal_range(opt_type);
  350. if (std::distance(range.first, range.second) > 0) {
  351. idx.erase(range.first, range.second);
  352. }
  353. // Append new option to the main storage.
  354. options_->addItem(option_descriptor_, option_space_);
  355. }
  356. void
  357. OptionDataParser::createOption(ConstElementPtr option_data) {
  358. // Check if mandatory parameters are specified.
  359. uint32_t code;
  360. std::string name;
  361. std::string data;
  362. try {
  363. code = uint32_values_->getParam("code");
  364. name = string_values_->getParam("name");
  365. data = string_values_->getParam("data");
  366. } catch (const std::exception& ex) {
  367. isc_throw(DhcpConfigError,
  368. ex.what() << "(" << option_data->getPosition() << ")");
  369. }
  370. // Check parameters having default values.
  371. std::string space = string_values_->getOptionalParam("space",
  372. global_context_->universe_ == Option::V4 ? "dhcp4" : "dhcp6");
  373. bool csv_format = boolean_values_->getOptionalParam("csv-format", false);
  374. // Option code is held in the uint32_t storage but is supposed to
  375. // be uint16_t value. We need to check that value in the configuration
  376. // does not exceed range of uint8_t for DHCPv4, uint16_t for DHCPv6 and
  377. // is not zero.
  378. if (code == 0) {
  379. isc_throw(DhcpConfigError, "option code must not be zero "
  380. "(" << uint32_values_->getPosition("code") << ")");
  381. } else if (global_context_->universe_ == Option::V4 &&
  382. code > std::numeric_limits<uint8_t>::max()) {
  383. isc_throw(DhcpConfigError, "invalid option code '" << code
  384. << "', it must not exceed '"
  385. << static_cast<int>(std::numeric_limits<uint8_t>::max())
  386. << "' (" << uint32_values_->getPosition("code") << ")");
  387. } else if (global_context_->universe_ == Option::V6 &&
  388. code > std::numeric_limits<uint16_t>::max()) {
  389. isc_throw(DhcpConfigError, "invalid option code '" << code
  390. << "', it must not exceed '"
  391. << std::numeric_limits<uint16_t>::max()
  392. << "' (" << uint32_values_->getPosition("code") << ")");
  393. }
  394. // Check that the option name is non-empty and does not contain spaces
  395. if (name.empty()) {
  396. isc_throw(DhcpConfigError, "name of the option with code '"
  397. << code << "' is empty ("
  398. << string_values_->getPosition("name") << ")");
  399. } else if (name.find(" ") != std::string::npos) {
  400. isc_throw(DhcpConfigError, "invalid option name '" << name
  401. << "', space character is not allowed ("
  402. << string_values_->getPosition("name") << ")");
  403. }
  404. if (!OptionSpace::validateName(space)) {
  405. isc_throw(DhcpConfigError, "invalid option space name '"
  406. << space << "' specified for option '"
  407. << name << "', code '" << code
  408. << "' (" << string_values_->getPosition("space") << ")");
  409. }
  410. // Find the Option Definition for the option by its option code.
  411. // findOptionDefinition will throw if not found, no need to test.
  412. // Find the definition for the option by its code. This function
  413. // may throw so we catch exceptions to log the culprit line of the
  414. // configuration.
  415. OptionDefinitionPtr def;
  416. try {
  417. def = findServerSpaceOptionDefinition(space, code);
  418. } catch (const std::exception& ex) {
  419. isc_throw(DhcpConfigError, ex.what()
  420. << " (" << string_values_->getPosition("space") << ")");
  421. }
  422. if (!def) {
  423. // If we are not dealing with a standard option then we
  424. // need to search for its definition among user-configured
  425. // options. They are expected to be in the global storage
  426. // already.
  427. OptionDefContainerPtr defs =
  428. global_context_->option_defs_->getItems(space);
  429. // The getItems() should never return the NULL pointer. If there are
  430. // no option definitions for the particular option space a pointer
  431. // to an empty container should be returned.
  432. assert(defs);
  433. const OptionDefContainerTypeIndex& idx = defs->get<1>();
  434. OptionDefContainerTypeRange range = idx.equal_range(code);
  435. if (std::distance(range.first, range.second) > 0) {
  436. def = *range.first;
  437. }
  438. // It's ok if we don't have option format if the option is
  439. // specified as hex
  440. if (!def && csv_format) {
  441. isc_throw(DhcpConfigError, "definition for the option '"
  442. << space << "." << name
  443. << "' having code '" << code
  444. << "' does not exist ("
  445. << string_values_->getPosition("name") << ")");
  446. }
  447. }
  448. // Transform string of hexadecimal digits into binary format.
  449. std::vector<uint8_t> binary;
  450. std::vector<std::string> data_tokens;
  451. if (csv_format) {
  452. // If the option data is specified as a string of comma
  453. // separated values then we need to split this string into
  454. // individual values - each value will be used to initialize
  455. // one data field of an option.
  456. data_tokens = isc::util::str::tokens(data, ",");
  457. } else {
  458. // Otherwise, the option data is specified as a string of
  459. // hexadecimal digits that we have to turn into binary format.
  460. try {
  461. // The decodeHex function expects that the string contains an
  462. // even number of digits. If we don't meet this requirement,
  463. // we have to insert a leading 0.
  464. if (!data.empty() && data.length() % 2) {
  465. data = data.insert(0, "0");
  466. }
  467. util::encode::decodeHex(data, binary);
  468. } catch (...) {
  469. isc_throw(DhcpConfigError, "option data is not a valid"
  470. << " string of hexadecimal digits: " << data
  471. << " (" << string_values_->getPosition("data") << ")");
  472. }
  473. }
  474. OptionPtr option;
  475. if (!def) {
  476. if (csv_format) {
  477. isc_throw(DhcpConfigError, "the CSV option data format can be"
  478. " used to specify values for an option that has a"
  479. " definition. The option with code " << code
  480. << " does not have a definition ("
  481. << boolean_values_->getPosition("csv-format") << ")");
  482. }
  483. // @todo We have a limited set of option definitions initalized at
  484. // the moment. In the future we want to initialize option definitions
  485. // for all options. Consequently an error will be issued if an option
  486. // definition does not exist for a particular option code. For now it is
  487. // ok to create generic option if definition does not exist.
  488. OptionPtr option(new Option(global_context_->universe_,
  489. static_cast<uint16_t>(code), binary));
  490. // The created option is stored in option_descriptor_ class member
  491. // until the commit stage when it is inserted into the main storage.
  492. // If an option with the same code exists in main storage already the
  493. // old option is replaced.
  494. option_descriptor_.option = option;
  495. option_descriptor_.persistent = false;
  496. } else {
  497. // Option name should match the definition. The option name
  498. // may seem to be redundant but in the future we may want
  499. // to reference options and definitions using their names
  500. // and/or option codes so keeping the option name in the
  501. // definition of option value makes sense.
  502. if (def->getName() != name) {
  503. isc_throw(DhcpConfigError, "specified option name '"
  504. << name << "' does not match the "
  505. << "option definition: '" << space
  506. << "." << def->getName() << "' ("
  507. << string_values_->getPosition("name") << ")");
  508. }
  509. // Option definition has been found so let's use it to create
  510. // an instance of our option.
  511. try {
  512. OptionPtr option = csv_format ?
  513. def->optionFactory(global_context_->universe_,
  514. code, data_tokens) :
  515. def->optionFactory(global_context_->universe_,
  516. code, binary);
  517. Subnet::OptionDescriptor desc(option, false);
  518. option_descriptor_.option = option;
  519. option_descriptor_.persistent = false;
  520. } catch (const isc::Exception& ex) {
  521. isc_throw(DhcpConfigError, "option data does not match"
  522. << " option definition (space: " << space
  523. << ", code: " << code << "): "
  524. << ex.what() << " ("
  525. << string_values_->getPosition("data") << ")");
  526. }
  527. }
  528. // All went good, so we can set the option space name.
  529. option_space_ = space;
  530. }
  531. // **************************** OptionDataListParser *************************
  532. OptionDataListParser::OptionDataListParser(const std::string&,
  533. OptionStoragePtr options, ParserContextPtr global_context,
  534. OptionDataParserFactory* optionDataParserFactory)
  535. : options_(options), local_options_(new OptionStorage()),
  536. global_context_(global_context),
  537. optionDataParserFactory_(optionDataParserFactory) {
  538. if (!options_) {
  539. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  540. << "options storage may not be NULL");
  541. }
  542. if (!options_) {
  543. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  544. << "context may not be NULL");
  545. }
  546. if (!optionDataParserFactory_) {
  547. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  548. << "option data parser factory may not be NULL");
  549. }
  550. }
  551. void
  552. OptionDataListParser::build(ConstElementPtr option_data_list) {
  553. BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
  554. boost::shared_ptr<OptionDataParser>
  555. parser((*optionDataParserFactory_)("option-data",
  556. local_options_, global_context_));
  557. // options_ member will hold instances of all options thus
  558. // each OptionDataParser takes it as a storage.
  559. // Build the instance of a single option.
  560. parser->build(option_value);
  561. // Store a parser as it will be used to commit.
  562. parsers_.push_back(parser);
  563. }
  564. }
  565. void
  566. OptionDataListParser::commit() {
  567. BOOST_FOREACH(ParserPtr parser, parsers_) {
  568. parser->commit();
  569. }
  570. // Parsing was successful and we have all configured
  571. // options in local storage. We can now replace old values
  572. // with new values.
  573. std::swap(*local_options_, *options_);
  574. }
  575. // ******************************** OptionDefParser ****************************
  576. OptionDefParser::OptionDefParser(const std::string&,
  577. OptionDefStoragePtr storage,
  578. ParserContextPtr global_context)
  579. : storage_(storage),
  580. boolean_values_(new BooleanStorage()),
  581. string_values_(new StringStorage()),
  582. uint32_values_(new Uint32Storage()),
  583. global_context_(global_context) {
  584. if (!storage_) {
  585. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  586. << "options storage may not be NULL");
  587. }
  588. }
  589. void
  590. OptionDefParser::build(ConstElementPtr option_def) {
  591. // Parse the elements that make up the option definition.
  592. BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
  593. std::string entry(param.first);
  594. ParserPtr parser;
  595. if (entry == "name" || entry == "type" || entry == "record-types"
  596. || entry == "space" || entry == "encapsulate") {
  597. StringParserPtr str_parser(new StringParser(entry,
  598. string_values_));
  599. parser = str_parser;
  600. } else if (entry == "code") {
  601. Uint32ParserPtr code_parser(new Uint32Parser(entry,
  602. uint32_values_));
  603. parser = code_parser;
  604. } else if (entry == "array") {
  605. BooleanParserPtr array_parser(new BooleanParser(entry,
  606. boolean_values_));
  607. parser = array_parser;
  608. } else {
  609. isc_throw(DhcpConfigError, "invalid parameter '" << entry
  610. << "' (" << param.second->getPosition() << ")");
  611. }
  612. parser->build(param.second);
  613. parser->commit();
  614. }
  615. // Create an instance of option definition.
  616. createOptionDef(option_def);
  617. // Get all items we collected so far for the particular option space.
  618. OptionDefContainerPtr defs = storage_->getItems(option_space_name_);
  619. // Check if there are any items with option code the same as the
  620. // one specified for the definition we are now creating.
  621. const OptionDefContainerTypeIndex& idx = defs->get<1>();
  622. const OptionDefContainerTypeRange& range =
  623. idx.equal_range(option_definition_->getCode());
  624. // If there are any items with this option code already we need
  625. // to issue an error because we don't allow duplicates for
  626. // option definitions within an option space.
  627. if (std::distance(range.first, range.second) > 0) {
  628. isc_throw(DhcpConfigError, "duplicated option definition for"
  629. << " code '" << option_definition_->getCode() << "' ("
  630. << option_def->getPosition() << ")");
  631. }
  632. }
  633. void
  634. OptionDefParser::commit() {
  635. if (storage_ && option_definition_ &&
  636. OptionSpace::validateName(option_space_name_)) {
  637. storage_->addItem(option_definition_, option_space_name_);
  638. }
  639. }
  640. void
  641. OptionDefParser::createOptionDef(ConstElementPtr option_def_element) {
  642. // Check if mandatory parameters have been specified.
  643. std::string name = string_values_->getParam("name");
  644. uint32_t code = uint32_values_->getParam("code");
  645. std::string type = string_values_->getParam("type");
  646. try {
  647. name = string_values_->getParam("name");
  648. code = uint32_values_->getParam("code");
  649. type = string_values_->getParam("type");
  650. } catch (const std::exception& ex) {
  651. isc_throw(DhcpConfigError, ex.what() << " ("
  652. << option_def_element->getPosition() << ")");
  653. }
  654. bool array_type = boolean_values_->getOptionalParam("array", false);
  655. std::string record_types =
  656. string_values_->getOptionalParam("record-types", "");
  657. std::string space = string_values_->getOptionalParam("space",
  658. global_context_->universe_ == Option::V4 ? "dhcp4" : "dhcp6");
  659. std::string encapsulates =
  660. string_values_->getOptionalParam("encapsulate", "");
  661. if (!OptionSpace::validateName(space)) {
  662. isc_throw(DhcpConfigError, "invalid option space name '"
  663. << space << "' ("
  664. << string_values_->getPosition("space") << ")");
  665. }
  666. // Create option definition.
  667. OptionDefinitionPtr def;
  668. // We need to check if user has set encapsulated option space
  669. // name. If so, different constructor will be used.
  670. if (!encapsulates.empty()) {
  671. // Arrays can't be used together with sub-options.
  672. if (array_type) {
  673. isc_throw(DhcpConfigError, "option '" << space << "."
  674. << "name" << "', comprising an array of data"
  675. << " fields may not encapsulate any option space ("
  676. << option_def_element->getPosition() << ")");
  677. } else if (encapsulates == space) {
  678. isc_throw(DhcpConfigError, "option must not encapsulate"
  679. << " an option space it belongs to: '"
  680. << space << "." << name << "' is set to"
  681. << " encapsulate '" << space << "' ("
  682. << option_def_element->getPosition() << ")");
  683. } else {
  684. def.reset(new OptionDefinition(name, code, type,
  685. encapsulates.c_str()));
  686. }
  687. } else {
  688. def.reset(new OptionDefinition(name, code, type, array_type));
  689. }
  690. // Split the list of record types into tokens.
  691. std::vector<std::string> record_tokens =
  692. isc::util::str::tokens(record_types, ",");
  693. // Iterate over each token and add a record type into
  694. // option definition.
  695. BOOST_FOREACH(std::string record_type, record_tokens) {
  696. try {
  697. boost::trim(record_type);
  698. if (!record_type.empty()) {
  699. def->addRecordField(record_type);
  700. }
  701. } catch (const Exception& ex) {
  702. isc_throw(DhcpConfigError, "invalid record type values"
  703. << " specified for the option definition: "
  704. << ex.what() << " ("
  705. << string_values_->getPosition("record-types") << ")");
  706. }
  707. }
  708. // Validate the definition.
  709. try {
  710. def->validate();
  711. } catch (const std::exception& ex) {
  712. isc_throw(DhcpConfigError, ex.what()
  713. << " (" << option_def_element->getPosition() << ")");
  714. }
  715. // Option definition has been created successfully.
  716. option_space_name_ = space;
  717. option_definition_ = def;
  718. }
  719. // ******************************** OptionDefListParser ************************
  720. OptionDefListParser::OptionDefListParser(const std::string&,
  721. ParserContextPtr global_context)
  722. : storage_(global_context->option_defs_),
  723. global_context_(global_context) {
  724. if (!storage_) {
  725. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  726. << "storage may not be NULL");
  727. }
  728. }
  729. void
  730. OptionDefListParser::build(ConstElementPtr option_def_list) {
  731. // Clear existing items in the storage.
  732. // We are going to replace all of them.
  733. storage_->clearItems();
  734. if (!option_def_list) {
  735. isc_throw(DhcpConfigError, "parser error: a pointer to a list of"
  736. << " option definitions is NULL ("
  737. << option_def_list->getPosition() << ")");
  738. }
  739. BOOST_FOREACH(ConstElementPtr option_def, option_def_list->listValue()) {
  740. boost::shared_ptr<OptionDefParser>
  741. parser(new OptionDefParser("single-option-def", storage_,
  742. global_context_));
  743. parser->build(option_def);
  744. parser->commit();
  745. }
  746. }
  747. void
  748. OptionDefListParser::commit() {
  749. CfgMgr& cfg_mgr = CfgMgr::instance();
  750. cfg_mgr.deleteOptionDefs();
  751. // We need to move option definitions from the temporary
  752. // storage to the storage.
  753. std::list<std::string> space_names =
  754. storage_->getOptionSpaceNames();
  755. BOOST_FOREACH(std::string space_name, space_names) {
  756. BOOST_FOREACH(OptionDefinitionPtr def,
  757. *(storage_->getItems(space_name))) {
  758. // All option definitions should be initialized to non-NULL
  759. // values. The validation is expected to be made by the
  760. // OptionDefParser when creating an option definition.
  761. assert(def);
  762. cfg_mgr.addOptionDef(def, space_name);
  763. }
  764. }
  765. }
  766. //****************************** RelayInfoParser ********************************
  767. RelayInfoParser::RelayInfoParser(const std::string&,
  768. const isc::dhcp::Subnet::RelayInfoPtr& relay_info,
  769. const Option::Universe& family)
  770. :storage_(relay_info), local_(isc::asiolink::IOAddress(
  771. family == Option::V4 ? "0.0.0.0" : "::")),
  772. string_values_(new StringStorage()), family_(family) {
  773. if (!relay_info) {
  774. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  775. << "relay-info storage may not be NULL");
  776. }
  777. };
  778. void
  779. RelayInfoParser::build(ConstElementPtr relay_info) {
  780. BOOST_FOREACH(ConfigPair param, relay_info->mapValue()) {
  781. ParserPtr parser(createConfigParser(param.first));
  782. parser->build(param.second);
  783. parser->commit();
  784. }
  785. // Get the IP address
  786. boost::scoped_ptr<asiolink::IOAddress> ip;
  787. try {
  788. ip.reset(new asiolink::IOAddress(string_values_->getParam("ip-address")));
  789. } catch (...) {
  790. isc_throw(DhcpConfigError, "Failed to parse ip-address "
  791. "value: " << string_values_->getParam("ip-address")
  792. << " (" << string_values_->getPosition("ip-address") << ")");
  793. }
  794. if ( (ip->isV4() && family_ != Option::V4) ||
  795. (ip->isV6() && family_ != Option::V6) ) {
  796. isc_throw(DhcpConfigError, "ip-address field " << ip->toText()
  797. << "does not have IP address of expected family type: "
  798. << (family_ == Option::V4 ? "IPv4" : "IPv6")
  799. << " (" << string_values_->getPosition("ip-address") << ")");
  800. }
  801. local_.addr_ = *ip;
  802. }
  803. isc::dhcp::ParserPtr
  804. RelayInfoParser::createConfigParser(const std::string& parameter) {
  805. DhcpConfigParser* parser = NULL;
  806. if (parameter.compare("ip-address") == 0) {
  807. parser = new StringParser(parameter, string_values_);
  808. } else {
  809. isc_throw(NotImplemented,
  810. "parser error: RelayInfoParser parameter not supported: "
  811. << parameter);
  812. }
  813. return (isc::dhcp::ParserPtr(parser));
  814. }
  815. void
  816. RelayInfoParser::commit() {
  817. *storage_ = local_;
  818. }
  819. //****************************** PoolParser ********************************
  820. PoolParser::PoolParser(const std::string&, PoolStoragePtr pools)
  821. :pools_(pools) {
  822. if (!pools_) {
  823. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  824. << "storage may not be NULL");
  825. }
  826. }
  827. void
  828. PoolParser::build(ConstElementPtr pools_list) {
  829. BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) {
  830. // That should be a single pool representation. It should contain
  831. // text is form prefix/len or first - last. Note that spaces
  832. // are allowed
  833. string txt = text_pool->stringValue();
  834. // first let's remove any whitespaces
  835. boost::erase_all(txt, " "); // space
  836. boost::erase_all(txt, "\t"); // tabulation
  837. // Is this prefix/len notation?
  838. size_t pos = txt.find("/");
  839. if (pos != string::npos) {
  840. isc::asiolink::IOAddress addr("::");
  841. uint8_t len = 0;
  842. try {
  843. addr = isc::asiolink::IOAddress(txt.substr(0, pos));
  844. // start with the first character after /
  845. string prefix_len = txt.substr(pos + 1);
  846. // It is lexical cast to int and then downcast to uint8_t.
  847. // Direct cast to uint8_t (which is really an unsigned char)
  848. // will result in interpreting the first digit as output
  849. // value and throwing exception if length is written on two
  850. // digits (because there are extra characters left over).
  851. // No checks for values over 128. Range correctness will
  852. // be checked in Pool4 constructor.
  853. len = boost::lexical_cast<int>(prefix_len);
  854. } catch (...) {
  855. isc_throw(DhcpConfigError, "Failed to parse pool "
  856. "definition: " << text_pool->stringValue()
  857. << " (" << text_pool->getPosition() << ")");
  858. }
  859. PoolPtr pool(poolMaker(addr, len));
  860. local_pools_.push_back(pool);
  861. continue;
  862. }
  863. // Is this min-max notation?
  864. pos = txt.find("-");
  865. if (pos != string::npos) {
  866. // using min-max notation
  867. isc::asiolink::IOAddress min(txt.substr(0,pos));
  868. isc::asiolink::IOAddress max(txt.substr(pos + 1));
  869. PoolPtr pool(poolMaker(min, max));
  870. local_pools_.push_back(pool);
  871. continue;
  872. }
  873. isc_throw(DhcpConfigError, "Failed to parse pool definition:"
  874. << text_pool->stringValue() <<
  875. ". Does not contain - (for min-max) nor / (prefix/len) ("
  876. << text_pool->getPosition() << ")");
  877. }
  878. }
  879. void
  880. PoolParser::commit() {
  881. if (pools_) {
  882. // local_pools_ holds the values produced by the build function.
  883. // At this point parsing should have completed successfuly so
  884. // we can append new data to the supplied storage.
  885. pools_->insert(pools_->end(), local_pools_.begin(), local_pools_.end());
  886. }
  887. }
  888. //****************************** SubnetConfigParser *************************
  889. SubnetConfigParser::SubnetConfigParser(const std::string&,
  890. ParserContextPtr global_context,
  891. const isc::asiolink::IOAddress& default_addr)
  892. : uint32_values_(new Uint32Storage()), string_values_(new StringStorage()),
  893. pools_(new PoolStorage()), options_(new OptionStorage()),
  894. global_context_(global_context),
  895. relay_info_(new isc::dhcp::Subnet::RelayInfo(default_addr)) {
  896. // The first parameter should always be "subnet", but we don't check
  897. // against that here in case some wants to reuse this parser somewhere.
  898. if (!global_context_) {
  899. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  900. << "context storage may not be NULL");
  901. }
  902. }
  903. void
  904. SubnetConfigParser::build(ConstElementPtr subnet) {
  905. BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
  906. ParserPtr parser;
  907. // When unsupported parameter is specified, the function called
  908. // below will thrown an exception. We have to catch this exception
  909. // to append the line number where the parameter is.
  910. try {
  911. parser.reset(createSubnetConfigParser(param.first));
  912. } catch (const std::exception& ex) {
  913. isc_throw(DhcpConfigError, ex.what() << " ("
  914. << param.second->getPosition() << ")");
  915. }
  916. parser->build(param.second);
  917. parsers_.push_back(parser);
  918. }
  919. // In order to create new subnet we need to get the data out
  920. // of the child parsers first. The only way to do it is to
  921. // invoke commit on them because it will make them write
  922. // parsed data into storages we have supplied.
  923. // Note that triggering commits on child parsers does not
  924. // affect global data because we supplied pointers to storages
  925. // local to this object. Thus, even if this method fails
  926. // later on, the configuration remains consistent.
  927. BOOST_FOREACH(ParserPtr parser, parsers_) {
  928. parser->commit();
  929. }
  930. // Create a subnet.
  931. try {
  932. createSubnet();
  933. } catch (const std::exception& ex) {
  934. isc_throw(DhcpConfigError,
  935. "subnet configuration failed (" << subnet->getPosition()
  936. << "): " << ex.what());
  937. }
  938. }
  939. void
  940. SubnetConfigParser::appendSubOptions(const std::string& option_space,
  941. OptionPtr& option) {
  942. // Only non-NULL options are stored in option container.
  943. // If this option pointer is NULL this is a serious error.
  944. assert(option);
  945. OptionDefinitionPtr def;
  946. if (isServerStdOption(option_space, option->getType())) {
  947. def = getServerStdOptionDefinition(option->getType());
  948. // Definitions for some of the standard options hasn't been
  949. // implemented so it is ok to leave here.
  950. if (!def) {
  951. return;
  952. }
  953. } else {
  954. const OptionDefContainerPtr defs =
  955. global_context_->option_defs_->getItems(option_space);
  956. const OptionDefContainerTypeIndex& idx = defs->get<1>();
  957. const OptionDefContainerTypeRange& range =
  958. idx.equal_range(option->getType());
  959. // There is no definition so we have to leave.
  960. if (std::distance(range.first, range.second) == 0) {
  961. return;
  962. }
  963. def = *range.first;
  964. // If the definition exists, it must be non-NULL.
  965. // Otherwise it is a programming error.
  966. assert(def);
  967. }
  968. // We need to get option definition for the particular option space
  969. // and code. This definition holds the information whether our
  970. // option encapsulates any option space.
  971. // Get the encapsulated option space name.
  972. std::string encapsulated_space = def->getEncapsulatedSpace();
  973. // If option space name is empty it means that our option does not
  974. // encapsulate any option space (does not include sub-options).
  975. if (!encapsulated_space.empty()) {
  976. // Get the sub-options that belong to the encapsulated
  977. // option space.
  978. const Subnet::OptionContainerPtr sub_opts =
  979. global_context_->options_->getItems(encapsulated_space);
  980. // Append sub-options to the option.
  981. BOOST_FOREACH(Subnet::OptionDescriptor desc, *sub_opts) {
  982. if (desc.option) {
  983. option->addOption(desc.option);
  984. }
  985. }
  986. }
  987. }
  988. void
  989. SubnetConfigParser::createSubnet() {
  990. std::string subnet_txt;
  991. try {
  992. subnet_txt = string_values_->getParam("subnet");
  993. } catch (const DhcpConfigError &) {
  994. // rethrow with precise error
  995. isc_throw(DhcpConfigError,
  996. "Mandatory subnet definition in subnet missing ("
  997. << string_values_->getPosition("subnet") << ")");
  998. }
  999. // Remove any spaces or tabs.
  1000. boost::erase_all(subnet_txt, " ");
  1001. boost::erase_all(subnet_txt, "\t");
  1002. // The subnet format is prefix/len. We are going to extract
  1003. // the prefix portion of a subnet string to create IOAddress
  1004. // object from it. IOAddress will be passed to the Subnet's
  1005. // constructor later on. In order to extract the prefix we
  1006. // need to get all characters preceding "/".
  1007. size_t pos = subnet_txt.find("/");
  1008. if (pos == string::npos) {
  1009. isc_throw(DhcpConfigError,
  1010. "Invalid subnet syntax (prefix/len expected):" << subnet_txt
  1011. << " (" << string_values_->getPosition("subnet") << ")");
  1012. }
  1013. // Try to create the address object. It also validates that
  1014. // the address syntax is ok.
  1015. isc::asiolink::IOAddress addr(subnet_txt.substr(0, pos));
  1016. uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
  1017. // Call the subclass's method to instantiate the subnet
  1018. initSubnet(addr, len);
  1019. // Add pools to it.
  1020. for (PoolStorage::iterator it = pools_->begin(); it != pools_->end();
  1021. ++it) {
  1022. subnet_->addPool(*it);
  1023. }
  1024. // Configure interface, if defined
  1025. // Get interface name. If it is defined, then the subnet is available
  1026. // directly over specified network interface.
  1027. std::string iface;
  1028. try {
  1029. iface = string_values_->getParam("interface");
  1030. } catch (const DhcpConfigError &) {
  1031. // iface not mandatory so swallow the exception
  1032. }
  1033. if (!iface.empty()) {
  1034. if (!IfaceMgr::instance().getIface(iface)) {
  1035. isc_throw(DhcpConfigError, "Specified interface name " << iface
  1036. << " for subnet " << subnet_->toText()
  1037. << " is not present" << " in the system ("
  1038. << string_values_->getPosition("interface") << ")");
  1039. }
  1040. subnet_->setIface(iface);
  1041. }
  1042. // We are going to move configured options to the Subnet object.
  1043. // Configured options reside in the container where options
  1044. // are grouped by space names. Thus we need to get all space names
  1045. // and iterate over all options that belong to them.
  1046. std::list<std::string> space_names = options_->getOptionSpaceNames();
  1047. BOOST_FOREACH(std::string option_space, space_names) {
  1048. // Get all options within a particular option space.
  1049. BOOST_FOREACH(Subnet::OptionDescriptor desc,
  1050. *options_->getItems(option_space)) {
  1051. // The pointer should be non-NULL. The validation is expected
  1052. // to be performed by the OptionDataParser before adding an
  1053. // option descriptor to the container.
  1054. assert(desc.option);
  1055. // We want to check whether an option with the particular
  1056. // option code has been already added. If so, we want
  1057. // to issue a warning.
  1058. Subnet::OptionDescriptor existing_desc =
  1059. subnet_->getOptionDescriptor("option_space",
  1060. desc.option->getType());
  1061. if (existing_desc.option) {
  1062. duplicate_option_warning(desc.option->getType(), addr);
  1063. }
  1064. // Add sub-options (if any).
  1065. appendSubOptions(option_space, desc.option);
  1066. // Check if the option space defines a vendor-option
  1067. uint32_t vendor_id = optionSpaceToVendorId(option_space);
  1068. if (vendor_id) {
  1069. // This is a vendor option
  1070. subnet_->addVendorOption(desc.option, false, vendor_id);
  1071. } else {
  1072. // This is a normal option
  1073. subnet_->addOption(desc.option, false, option_space);
  1074. }
  1075. }
  1076. }
  1077. // Check all global options and add them to the subnet object if
  1078. // they have been configured in the global scope. If they have been
  1079. // configured in the subnet scope we don't add global option because
  1080. // the one configured in the subnet scope always takes precedence.
  1081. space_names = global_context_->options_->getOptionSpaceNames();
  1082. BOOST_FOREACH(std::string option_space, space_names) {
  1083. // Get all global options for the particular option space.
  1084. BOOST_FOREACH(Subnet::OptionDescriptor desc,
  1085. *(global_context_->options_->getItems(option_space))) {
  1086. // The pointer should be non-NULL. The validation is expected
  1087. // to be performed by the OptionDataParser before adding an
  1088. // option descriptor to the container.
  1089. assert(desc.option);
  1090. // Check if the particular option has been already added.
  1091. // This would mean that it has been configured in the
  1092. // subnet scope. Since option values configured in the
  1093. // subnet scope take precedence over globally configured
  1094. // values we don't add option from the global storage
  1095. // if there is one already.
  1096. Subnet::OptionDescriptor existing_desc =
  1097. subnet_->getOptionDescriptor(option_space,
  1098. desc.option->getType());
  1099. if (!existing_desc.option) {
  1100. // Add sub-options (if any).
  1101. appendSubOptions(option_space, desc.option);
  1102. uint32_t vendor_id = optionSpaceToVendorId(option_space);
  1103. if (vendor_id) {
  1104. // This is a vendor option
  1105. subnet_->addVendorOption(desc.option, false, vendor_id);
  1106. } else {
  1107. // This is a normal option
  1108. subnet_->addOption(desc.option, false, option_space);
  1109. }
  1110. }
  1111. }
  1112. }
  1113. }
  1114. uint32_t
  1115. SubnetConfigParser::optionSpaceToVendorId(const std::string& option_space) {
  1116. if (option_space.size() < 8) {
  1117. // 8 is a minimal length of "vendor-X" format
  1118. return (0);
  1119. }
  1120. if (option_space.substr(0,7) != "vendor-") {
  1121. return (0);
  1122. }
  1123. // text after "vendor-", supposedly numbers only
  1124. string x = option_space.substr(7);
  1125. int64_t check;
  1126. try {
  1127. check = boost::lexical_cast<int64_t>(x);
  1128. } catch (const boost::bad_lexical_cast &) {
  1129. /// @todo: Should we throw here?
  1130. // isc_throw(BadValue, "Failed to parse vendor-X value (" << x
  1131. // << ") as unsigned 32-bit integer.");
  1132. return (0);
  1133. }
  1134. if (check > std::numeric_limits<uint32_t>::max()) {
  1135. /// @todo: Should we throw here?
  1136. //isc_throw(BadValue, "Value " << x << "is too large"
  1137. // << " for unsigned 32-bit integer.");
  1138. return (0);
  1139. }
  1140. if (check < 0) {
  1141. /// @todo: Should we throw here?
  1142. // isc_throw(BadValue, "Value " << x << "is negative."
  1143. // << " Only 0 or larger are allowed for unsigned 32-bit integer.");
  1144. return (0);
  1145. }
  1146. // value is small enough to fit
  1147. return (static_cast<uint32_t>(check));
  1148. }
  1149. isc::dhcp::Triplet<uint32_t>
  1150. SubnetConfigParser::getParam(const std::string& name) {
  1151. uint32_t value = 0;
  1152. try {
  1153. // look for local value
  1154. value = uint32_values_->getParam(name);
  1155. } catch (const DhcpConfigError &) {
  1156. try {
  1157. // no local, use global value
  1158. value = global_context_->uint32_values_->getParam(name);
  1159. } catch (const DhcpConfigError &) {
  1160. isc_throw(DhcpConfigError, "Mandatory parameter " << name
  1161. << " missing (no global default and no subnet-"
  1162. << "specific value)");
  1163. }
  1164. }
  1165. return (Triplet<uint32_t>(value));
  1166. }
  1167. //**************************** D2ClientConfigParser **********************
  1168. D2ClientConfigParser::D2ClientConfigParser(const std::string& entry_name)
  1169. : entry_name_(entry_name), boolean_values_(new BooleanStorage()),
  1170. uint32_values_(new Uint32Storage()), string_values_(new StringStorage()),
  1171. local_client_config_() {
  1172. }
  1173. D2ClientConfigParser::~D2ClientConfigParser() {
  1174. }
  1175. void
  1176. D2ClientConfigParser::build(isc::data::ConstElementPtr client_config) {
  1177. BOOST_FOREACH(ConfigPair param, client_config->mapValue()) {
  1178. ParserPtr parser;
  1179. try {
  1180. parser = createConfigParser(param.first);
  1181. } catch (std::exception& ex) {
  1182. // Catch exception in case the configuration contains the
  1183. // unsupported parameter. In this case, we will need to
  1184. // append the position of this element.
  1185. isc_throw(DhcpConfigError, ex.what() << " ("
  1186. << param.second->getPosition() << ")");
  1187. }
  1188. parser->build(param.second);
  1189. parser->commit();
  1190. }
  1191. bool enable_updates = boolean_values_->getParam("enable-updates");
  1192. if (!enable_updates && (client_config->mapValue().size() == 1)) {
  1193. // If enable-updates is the only parameter and it is false then
  1194. // we're done. This allows for an abbreviated configuration entry
  1195. // that only contains that flag. Use the default D2ClientConfig
  1196. // constructor to a create a disabled instance.
  1197. local_client_config_.reset(new D2ClientConfig());
  1198. return;
  1199. }
  1200. // Get all parameters that are needed to create the D2ClientConfig.
  1201. asiolink::IOAddress server_ip(string_values_->
  1202. getOptionalParam("server-ip",
  1203. D2ClientConfig::
  1204. DFT_SERVER_IP));
  1205. uint32_t server_port = uint32_values_->getOptionalParam("server-port",
  1206. D2ClientConfig::
  1207. DFT_SERVER_PORT);
  1208. dhcp_ddns::NameChangeProtocol ncr_protocol
  1209. = dhcp_ddns::stringToNcrProtocol(string_values_->
  1210. getOptionalParam("ncr-protocol",
  1211. D2ClientConfig::
  1212. DFT_NCR_PROTOCOL));
  1213. dhcp_ddns::NameChangeFormat ncr_format
  1214. = dhcp_ddns::stringToNcrFormat(string_values_->
  1215. getOptionalParam("ncr-format",
  1216. D2ClientConfig::
  1217. DFT_NCR_FORMAT));
  1218. std::string generated_prefix = string_values_->
  1219. getOptionalParam("generated-prefix",
  1220. D2ClientConfig::
  1221. DFT_GENERATED_PREFIX);
  1222. std::string qualifying_suffix = string_values_->
  1223. getOptionalParam("qualifying-suffix",
  1224. D2ClientConfig::
  1225. DFT_QUALIFYING_SUFFIX);
  1226. bool always_include_fqdn = boolean_values_->
  1227. getOptionalParam("always-include-fqdn",
  1228. D2ClientConfig::
  1229. DFT_ALWAYS_INCLUDE_FQDN);
  1230. bool override_no_update = boolean_values_->
  1231. getOptionalParam("override-no-update",
  1232. D2ClientConfig::
  1233. DFT_OVERRIDE_NO_UPDATE);
  1234. bool override_client_update = boolean_values_->
  1235. getOptionalParam("override-client-update",
  1236. D2ClientConfig::
  1237. DFT_OVERRIDE_CLIENT_UPDATE);
  1238. bool replace_client_name = boolean_values_->
  1239. getOptionalParam("replace-client-name",
  1240. D2ClientConfig::
  1241. DFT_REPLACE_CLIENT_NAME);
  1242. // Attempt to create the new client config.
  1243. local_client_config_.reset(new D2ClientConfig(enable_updates, server_ip,
  1244. server_port, ncr_protocol,
  1245. ncr_format,
  1246. always_include_fqdn,
  1247. override_no_update,
  1248. override_client_update,
  1249. replace_client_name,
  1250. generated_prefix,
  1251. qualifying_suffix));
  1252. }
  1253. isc::dhcp::ParserPtr
  1254. D2ClientConfigParser::createConfigParser(const std::string& config_id) {
  1255. DhcpConfigParser* parser = NULL;
  1256. if (config_id.compare("server-port") == 0) {
  1257. parser = new Uint32Parser(config_id, uint32_values_);
  1258. } else if ((config_id.compare("server-ip") == 0) ||
  1259. (config_id.compare("ncr-protocol") == 0) ||
  1260. (config_id.compare("ncr-format") == 0) ||
  1261. (config_id.compare("generated-prefix") == 0) ||
  1262. (config_id.compare("qualifying-suffix") == 0)) {
  1263. parser = new StringParser(config_id, string_values_);
  1264. } else if ((config_id.compare("enable-updates") == 0) ||
  1265. (config_id.compare("always-include-fqdn") == 0) ||
  1266. (config_id.compare("allow-client-update") == 0) ||
  1267. (config_id.compare("override-no-update") == 0) ||
  1268. (config_id.compare("override-client-update") == 0) ||
  1269. (config_id.compare("replace-client-name") == 0)) {
  1270. parser = new BooleanParser(config_id, boolean_values_);
  1271. } else {
  1272. isc_throw(NotImplemented,
  1273. "parser error: D2ClientConfig parameter not supported: "
  1274. << config_id);
  1275. }
  1276. return (isc::dhcp::ParserPtr(parser));
  1277. }
  1278. void
  1279. D2ClientConfigParser::commit() {
  1280. // @todo if local_client_config_ is empty then shutdown the listener...
  1281. // @todo Should this also attempt to start a listener?
  1282. // In keeping with Interface, Subnet, and Hooks parsers, then this
  1283. // should initialize the listener. Failure to init it, should cause
  1284. // rollback. This gets sticky, because who owns the listener instance?
  1285. // Does CfgMgr maintain it or does the server class? If the latter
  1286. // how do we get that value here?
  1287. // I'm thinkikng D2ClientConfig could contain the listener instance
  1288. CfgMgr::instance().setD2ClientConfig(local_client_config_);
  1289. }
  1290. }; // namespace dhcp
  1291. }; // namespace isc