dhcp_parsers.cc 57 KB

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