dhcp_parsers.cc 52 KB

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