dhcp_parsers.cc 59 KB

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