dhcp_parsers.cc 52 KB

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