dhcp_parsers.cc 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333
  1. // Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <dhcp/iface_mgr.h>
  8. #include <dhcp/libdhcp++.h>
  9. #include <dhcpsrv/cfgmgr.h>
  10. #include <dhcpsrv/cfg_option.h>
  11. #include <dhcpsrv/parsers/dhcp_parsers.h>
  12. #include <dhcpsrv/cfg_mac_source.h>
  13. #include <hooks/hooks_manager.h>
  14. #include <util/encode/hex.h>
  15. #include <util/strutil.h>
  16. #include <boost/algorithm/string.hpp>
  17. #include <boost/foreach.hpp>
  18. #include <boost/lexical_cast.hpp>
  19. #include <boost/scoped_ptr.hpp>
  20. #include <map>
  21. #include <string>
  22. #include <vector>
  23. using namespace std;
  24. using namespace isc::asiolink;
  25. using namespace isc::data;
  26. using namespace isc::hooks;
  27. using namespace isc::util;
  28. namespace isc {
  29. namespace dhcp {
  30. // **************************** DebugParser *************************
  31. DebugParser::DebugParser(const std::string& param_name)
  32. :param_name_(param_name) {
  33. }
  34. void
  35. DebugParser::build(ConstElementPtr new_config) {
  36. value_ = new_config;
  37. std::cout << "Build for token: [" << param_name_ << "] = ["
  38. << value_->str() << "]" << std::endl;
  39. }
  40. void
  41. DebugParser::commit() {
  42. // Debug message. The whole DebugParser class is used only for parser
  43. // debugging, and is not used in production code. It is very convenient
  44. // to keep it around. Please do not turn this cout into logger calls.
  45. std::cout << "Commit for token: [" << param_name_ << "] = ["
  46. << value_->str() << "]" << std::endl;
  47. }
  48. // **************************** BooleanParser *************************
  49. template<> void ValueParser<bool>::build(isc::data::ConstElementPtr value) {
  50. // Invoke common code for all specializations of build().
  51. buildCommon(value);
  52. // The Config Manager checks if user specified a
  53. // valid value for a boolean parameter: true or false.
  54. // We should have a boolean Element, use value directly
  55. try {
  56. value_ = value->boolValue();
  57. } catch (const isc::data::TypeError &) {
  58. isc_throw(BadValue, " Wrong value type for " << param_name_
  59. << " : build called with a non-boolean element "
  60. << "(" << value->getPosition() << ").");
  61. }
  62. }
  63. // **************************** Uin32Parser *************************
  64. template<> void ValueParser<uint32_t>::build(ConstElementPtr value) {
  65. // Invoke common code for all specializations of build().
  66. buildCommon(value);
  67. int64_t check;
  68. string x = value->str();
  69. try {
  70. check = boost::lexical_cast<int64_t>(x);
  71. } catch (const boost::bad_lexical_cast &) {
  72. isc_throw(BadValue, "Failed to parse value " << value->str()
  73. << " as unsigned 32-bit integer "
  74. "(" << value->getPosition() << ").");
  75. }
  76. if (check > std::numeric_limits<uint32_t>::max()) {
  77. isc_throw(BadValue, "Value " << value->str() << " is too large"
  78. " for unsigned 32-bit integer "
  79. "(" << value->getPosition() << ").");
  80. }
  81. if (check < 0) {
  82. isc_throw(BadValue, "Value " << value->str() << " is negative."
  83. << " Only 0 or larger are allowed for unsigned 32-bit integer "
  84. "(" << value->getPosition() << ").");
  85. }
  86. // value is small enough to fit
  87. value_ = static_cast<uint32_t>(check);
  88. }
  89. // **************************** StringParser *************************
  90. template <> void ValueParser<std::string>::build(ConstElementPtr value) {
  91. // Invoke common code for all specializations of build().
  92. buildCommon(value);
  93. // For strings we need to use stringValue() rather than str().
  94. // str() returns fully escaped special characters, so
  95. // single backslash would be misrepresented as "\\".
  96. if (value->getType() == Element::string) {
  97. value_ = value->stringValue();
  98. } else {
  99. value_ = value->str();
  100. }
  101. boost::erase_all(value_, "\"");
  102. }
  103. // ******************** MACSourcesListConfigParser *************************
  104. void
  105. MACSourcesListConfigParser::parse(CfgMACSource& mac_sources, ConstElementPtr value) {
  106. CfgIface cfg_iface;
  107. uint32_t source = 0;
  108. size_t cnt = 0;
  109. // By default, there's only one source defined: ANY.
  110. // If user specified anything, we need to get rid of that default.
  111. mac_sources.clear();
  112. BOOST_FOREACH(ConstElementPtr source_elem, value->listValue()) {
  113. std::string source_str = source_elem->stringValue();
  114. try {
  115. source = CfgMACSource::MACSourceFromText(source_str);
  116. mac_sources.add(source);
  117. ++cnt;
  118. } catch (const InvalidParameter& ex) {
  119. isc_throw(DhcpConfigError, "The mac-sources value '" << source_str
  120. << "' was specified twice (" << value->getPosition() << ")");
  121. } catch (const std::exception& ex) {
  122. isc_throw(DhcpConfigError, "Failed to convert '"
  123. << source_str << "' to any recognized MAC source:"
  124. << ex.what() << " (" << value->getPosition() << ")");
  125. }
  126. }
  127. if (!cnt) {
  128. isc_throw(DhcpConfigError, "If specified, MAC Sources cannot be empty");
  129. }
  130. }
  131. // ******************** ControlSocketParser *************************
  132. void ControlSocketParser::parse(SrvConfig& srv_cfg, isc::data::ConstElementPtr value) {
  133. if (!value) {
  134. isc_throw(DhcpConfigError, "Logic error: specified control-socket is null");
  135. }
  136. if (value->getType() != Element::map) {
  137. isc_throw(DhcpConfigError, "Specified control-socket is expected to be a map"
  138. ", i.e. a structure defined within { }");
  139. }
  140. srv_cfg.setControlSocketInfo(value);
  141. }
  142. // ******************** HooksLibrariesParser *************************
  143. void
  144. HooksLibrariesParser::parse(ConstElementPtr value) {
  145. // Initialize.
  146. libraries_.clear();
  147. if (!value) {
  148. isc_throw(DhcpConfigError, "Tried to parse null hooks libraries");
  149. }
  150. // Let's store
  151. position_ = value->getPosition();
  152. // This is the new syntax. Iterate through it and get each map.
  153. BOOST_FOREACH(ConstElementPtr library_entry, value->listValue()) {
  154. ConstElementPtr parameters;
  155. // Is it a map?
  156. if (library_entry->getType() != Element::map) {
  157. isc_throw(DhcpConfigError, "hooks library configuration error:"
  158. " one or more entries in the hooks-libraries list is not"
  159. " a map (" << library_entry->getPosition() << ")");
  160. }
  161. // Iterate through each element in the map. We check
  162. // whether we have found a library element.
  163. bool lib_found = false;
  164. string libname = "";
  165. // Let's explicitly reset the parameters, so we won't cover old
  166. // values from the previous loop round.
  167. parameters.reset();
  168. BOOST_FOREACH(ConfigPair entry_item, library_entry->mapValue()) {
  169. if (entry_item.first == "library") {
  170. if (entry_item.second->getType() != Element::string) {
  171. isc_throw(DhcpConfigError, "hooks library configuration"
  172. " error: value of 'library' element is not a string"
  173. " giving the path to a hooks library (" <<
  174. entry_item.second->getPosition() << ")");
  175. }
  176. // Get the name of the library and add it to the list after
  177. // removing quotes.
  178. libname = (entry_item.second)->stringValue();
  179. // Remove leading/trailing quotes and any leading/trailing
  180. // spaces.
  181. boost::erase_all(libname, "\"");
  182. libname = isc::util::str::trim(libname);
  183. if (libname.empty()) {
  184. isc_throw(DhcpConfigError, "hooks library configuration"
  185. " error: value of 'library' element must not be"
  186. " blank (" <<
  187. entry_item.second->getPosition() << ")");
  188. }
  189. // Note we have found the library name.
  190. lib_found = true;
  191. continue;
  192. }
  193. // If there are parameters, let's remember them.
  194. if (entry_item.first == "parameters") {
  195. parameters = entry_item.second;
  196. continue;
  197. }
  198. // For all other parameters we will throw.
  199. isc_throw(DhcpConfigError, "unknown hooks library parameter: "
  200. << entry_item.first << "("
  201. << library_entry->getPosition() << ")");
  202. }
  203. if (! lib_found) {
  204. isc_throw(DhcpConfigError, "hooks library configuration error:"
  205. " one or more hooks-libraries elements are missing the"
  206. " name of the library" <<
  207. " (" << library_entry->getPosition() << ")");
  208. }
  209. libraries_.push_back(make_pair(libname, parameters));
  210. }
  211. }
  212. void HooksLibrariesParser::verifyLibraries() {
  213. // Check if the list of libraries has changed. If not, nothing is done
  214. // - the command "DhcpN libreload" is required to reload the same
  215. // libraries (this prevents needless reloads when anything else in the
  216. // configuration is changed).
  217. // We no longer rely on this. Parameters can change. And even if the
  218. // parameters stay the same, they could point to files that could
  219. // change.
  220. vector<string> current_libraries = HooksManager::getLibraryNames();
  221. if (current_libraries.empty() && libraries_.empty()) {
  222. return;
  223. }
  224. // Library list has changed, validate each of the libraries specified.
  225. vector<string> lib_names = isc::hooks::extractNames(libraries_);
  226. vector<string> error_libs = HooksManager::validateLibraries(lib_names);
  227. if (!error_libs.empty()) {
  228. // Construct the list of libraries in error for the message.
  229. string error_list = error_libs[0];
  230. for (size_t i = 1; i < error_libs.size(); ++i) {
  231. error_list += (string(", ") + error_libs[i]);
  232. }
  233. isc_throw(DhcpConfigError, "hooks libraries failed to validate - "
  234. "library or libraries in error are: " << error_list
  235. << "(" << position_ << ")");
  236. }
  237. }
  238. void
  239. HooksLibrariesParser::loadLibraries() {
  240. /// Commits the list of libraries to the configuration manager storage if
  241. /// the list of libraries has changed.
  242. /// @todo: Delete any stored CalloutHandles before reloading the
  243. /// libraries
  244. if (!HooksManager::loadLibraries(libraries_)) {
  245. isc_throw(DhcpConfigError, "One or more hook libraries failed to load");
  246. }
  247. }
  248. // Method for testing
  249. void
  250. HooksLibrariesParser::getLibraries(isc::hooks::HookLibsCollection& libraries) {
  251. libraries = libraries_;
  252. }
  253. // **************************** OptionDataParser *************************
  254. OptionDataParser::OptionDataParser(const uint16_t address_family)
  255. : address_family_(address_family) {
  256. }
  257. std::pair<OptionDescriptor, std::string>
  258. OptionDataParser::parse(isc::data::ConstElementPtr single_option) {
  259. // Try to create the option instance.
  260. std::pair<OptionDescriptor, std::string> opt = createOption(single_option);
  261. if (!opt.first.option_) {
  262. isc_throw(isc::InvalidOperation,
  263. "parser logic error: no option has been configured and"
  264. " thus there is nothing to commit. Has build() been called?");
  265. }
  266. return (opt);
  267. }
  268. OptionalValue<uint32_t>
  269. OptionDataParser::extractCode(ConstElementPtr parent) const {
  270. uint32_t code;
  271. try {
  272. code = getInteger(parent, "code");
  273. } catch (const exception&) {
  274. // The code parameter was not found. Return an unspecified
  275. // value.
  276. return (OptionalValue<uint32_t>());
  277. }
  278. if (code == 0) {
  279. isc_throw(DhcpConfigError, "option code must not be zero "
  280. "(" << getPosition("code", parent) << ")");
  281. } else if (address_family_ == AF_INET &&
  282. code > std::numeric_limits<uint8_t>::max()) {
  283. isc_throw(DhcpConfigError, "invalid option code '" << code
  284. << "', it must not be greater than '"
  285. << static_cast<int>(std::numeric_limits<uint8_t>::max())
  286. << "' (" << getPosition("code", parent)
  287. << ")");
  288. } else if (address_family_ == AF_INET6 &&
  289. code > std::numeric_limits<uint16_t>::max()) {
  290. isc_throw(DhcpConfigError, "invalid option code '" << code
  291. << "', it must not exceed '"
  292. << std::numeric_limits<uint16_t>::max()
  293. << "' (" << getPosition("code", parent)
  294. << ")");
  295. }
  296. return (OptionalValue<uint32_t>(code, OptionalValueState(true)));
  297. }
  298. OptionalValue<std::string>
  299. OptionDataParser::extractName(ConstElementPtr parent) const {
  300. std::string name;
  301. try {
  302. name = getString(parent, "name");
  303. } catch (...) {
  304. return (OptionalValue<std::string>());
  305. }
  306. if (name.find(" ") != std::string::npos) {
  307. isc_throw(DhcpConfigError, "invalid option name '" << name
  308. << "', space character is not allowed ("
  309. << getPosition("name", parent) << ")");
  310. }
  311. return (OptionalValue<std::string>(name, OptionalValueState(true)));
  312. }
  313. std::string
  314. OptionDataParser::extractData(ConstElementPtr parent) const {
  315. std::string data;
  316. try {
  317. data = getString(parent, "data");
  318. } catch (...) {
  319. // The "data" parameter was not found. Return an empty value.
  320. return (data);
  321. }
  322. return (data);
  323. }
  324. OptionalValue<bool>
  325. OptionDataParser::extractCSVFormat(ConstElementPtr parent) const {
  326. bool csv_format = true;
  327. try {
  328. csv_format = getBoolean(parent, "csv-format");
  329. } catch (...) {
  330. return (OptionalValue<bool>(csv_format));
  331. }
  332. return (OptionalValue<bool>(csv_format, OptionalValueState(true)));
  333. }
  334. std::string
  335. OptionDataParser::extractSpace(ConstElementPtr parent) const {
  336. std::string space = address_family_ == AF_INET ?
  337. DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE;
  338. try {
  339. space = getString(parent, "space");
  340. } catch (...) {
  341. return (space);
  342. }
  343. try {
  344. if (!OptionSpace::validateName(space)) {
  345. isc_throw(DhcpConfigError, "invalid option space name '"
  346. << space << "'");
  347. }
  348. if ((space == DHCP4_OPTION_SPACE) && (address_family_ == AF_INET6)) {
  349. isc_throw(DhcpConfigError, "'" << DHCP4_OPTION_SPACE
  350. << "' option space name is reserved for DHCPv4 server");
  351. } else if ((space == DHCP6_OPTION_SPACE) &&
  352. (address_family_ == AF_INET)) {
  353. isc_throw(DhcpConfigError, "'" << DHCP6_OPTION_SPACE
  354. << "' option space name is reserved for DHCPv6 server");
  355. }
  356. } catch (std::exception& ex) {
  357. // Append position of the option space parameter. Note, that in the case
  358. // when 'space' was not specified a default value will be used and we
  359. // should never get here. Therefore, it is ok to call getPosition for
  360. // the space parameter here as this parameter will always be specified.
  361. isc_throw(DhcpConfigError, ex.what() << " ("
  362. << getPosition("space", parent) << ")");
  363. }
  364. return (space);
  365. }
  366. template<typename SearchKey>
  367. OptionDefinitionPtr
  368. OptionDataParser::findOptionDefinition(const std::string& option_space,
  369. const SearchKey& search_key) const {
  370. OptionDefinitionPtr def = LibDHCP::getOptionDef(option_space, search_key);
  371. if (!def) {
  372. // Check if this is a vendor-option. If it is, get vendor-specific
  373. // definition.
  374. uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(option_space);
  375. if (vendor_id) {
  376. const Option::Universe u = address_family_ == AF_INET ?
  377. Option::V4 : Option::V6;
  378. def = LibDHCP::getVendorOptionDef(u, vendor_id, search_key);
  379. }
  380. }
  381. if (!def) {
  382. // Check if this is an option specified by a user.
  383. def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef()
  384. ->get(option_space, search_key);
  385. }
  386. return (def);
  387. }
  388. std::pair<OptionDescriptor, std::string>
  389. OptionDataParser::createOption(ConstElementPtr option_data) {
  390. const Option::Universe universe = address_family_ == AF_INET ?
  391. Option::V4 : Option::V6;
  392. OptionalValue<uint32_t> code_param = extractCode(option_data);
  393. OptionalValue<std::string> name_param = extractName(option_data);
  394. OptionalValue<bool> csv_format_param = extractCSVFormat(option_data);
  395. std::string data_param = extractData(option_data);
  396. std::string space_param = extractSpace(option_data);
  397. // Require that option code or option name is specified.
  398. if (!code_param.isSpecified() && !name_param.isSpecified()) {
  399. isc_throw(DhcpConfigError, "option data configuration requires one of"
  400. " 'code' or 'name' parameters to be specified"
  401. << " (" << option_data->getPosition() << ")");
  402. }
  403. // Try to find a corresponding option definition using option code or
  404. // option name.
  405. OptionDefinitionPtr def = code_param.isSpecified() ?
  406. findOptionDefinition(space_param, code_param) :
  407. findOptionDefinition(space_param, name_param);
  408. // If there is no definition, the user must not explicitly enable the
  409. // use of csv-format.
  410. if (!def) {
  411. // If explicitly requested that the CSV format is to be used,
  412. // the option definition is a must.
  413. if (csv_format_param.isSpecified() && csv_format_param) {
  414. isc_throw(DhcpConfigError, "definition for the option '"
  415. << space_param << "." << name_param
  416. << "' having code '" << code_param
  417. << "' does not exist ("
  418. << getPosition("name", option_data)
  419. << ")");
  420. // If there is no option definition and the option code is not specified
  421. // we have no means to find the option code.
  422. } else if (name_param.isSpecified() && !code_param.isSpecified()) {
  423. isc_throw(DhcpConfigError, "definition for the option '"
  424. << space_param << "." << name_param
  425. << "' does not exist ("
  426. << getPosition("name", option_data)
  427. << ")");
  428. }
  429. }
  430. // Transform string of hexadecimal digits into binary format.
  431. std::vector<uint8_t> binary;
  432. std::vector<std::string> data_tokens;
  433. // If the definition is available and csv-format hasn't been explicitly
  434. // disabled, we will parse the data as comma separated values.
  435. if (def && (!csv_format_param.isSpecified() || csv_format_param)) {
  436. // If the option data is specified as a string of comma
  437. // separated values then we need to split this string into
  438. // individual values - each value will be used to initialize
  439. // one data field of an option.
  440. data_tokens = isc::util::str::tokens(data_param, ",");
  441. } else {
  442. // Otherwise, the option data is specified as a string of
  443. // hexadecimal digits that we have to turn into binary format.
  444. try {
  445. // The decodeHex function expects that the string contains an
  446. // even number of digits. If we don't meet this requirement,
  447. // we have to insert a leading 0.
  448. if (!data_param.empty() && ((data_param.length() % 2) != 0)) {
  449. data_param = data_param.insert(0, "0");
  450. }
  451. util::encode::decodeHex(data_param, binary);
  452. } catch (...) {
  453. isc_throw(DhcpConfigError, "option data is not a valid"
  454. << " string of hexadecimal digits: " << data_param
  455. << " ("
  456. << getPosition("data", option_data)
  457. << ")");
  458. }
  459. }
  460. OptionPtr option;
  461. OptionDescriptor desc(false);
  462. if (!def) {
  463. // @todo We have a limited set of option definitions initalized at
  464. // the moment. In the future we want to initialize option definitions
  465. // for all options. Consequently an error will be issued if an option
  466. // definition does not exist for a particular option code. For now it is
  467. // ok to create generic option if definition does not exist.
  468. OptionPtr option(new Option(universe, static_cast<uint16_t>(code_param),
  469. binary));
  470. desc.option_ = option;
  471. desc.persistent_ = false;
  472. } else {
  473. // Option name is specified it should match the name in the definition.
  474. if (name_param.isSpecified() && (def->getName() != name_param.get())) {
  475. isc_throw(DhcpConfigError, "specified option name '"
  476. << name_param << "' does not match the "
  477. << "option definition: '" << space_param
  478. << "." << def->getName() << "' ("
  479. << getPosition("name", option_data)
  480. << ")");
  481. }
  482. // Option definition has been found so let's use it to create
  483. // an instance of our option.
  484. try {
  485. OptionPtr option =
  486. !csv_format_param.isSpecified() || csv_format_param ?
  487. def->optionFactory(universe, def->getCode(), data_tokens) :
  488. def->optionFactory(universe, def->getCode(), binary);
  489. desc.option_ = option;
  490. desc.persistent_ = false;
  491. } catch (const isc::Exception& ex) {
  492. isc_throw(DhcpConfigError, "option data does not match"
  493. << " option definition (space: " << space_param
  494. << ", code: " << def->getCode() << "): "
  495. << ex.what() << " ("
  496. << getPosition("data", option_data)
  497. << ")");
  498. }
  499. }
  500. // All went good, so we can set the option space name.
  501. return make_pair(desc, space_param);
  502. }
  503. // **************************** OptionDataListParser *************************
  504. OptionDataListParser::OptionDataListParser(//const std::string&,
  505. //const CfgOptionPtr& cfg,
  506. const uint16_t address_family)
  507. : address_family_(address_family) {
  508. }
  509. void OptionDataListParser::parse(const CfgOptionPtr& cfg,
  510. isc::data::ConstElementPtr option_data_list) {
  511. OptionDataParser option_parser(address_family_);
  512. BOOST_FOREACH(ConstElementPtr data, option_data_list->listValue()) {
  513. std::pair<OptionDescriptor, std::string> option =
  514. option_parser.parse(data);
  515. cfg->add(option.first.option_, option.first.persistent_, option.second);
  516. cfg->encapsulate();
  517. }
  518. }
  519. // ******************************** OptionDefParser ****************************
  520. std::pair<isc::dhcp::OptionDefinitionPtr, std::string>
  521. OptionDefParser::parse(ConstElementPtr option_def) {
  522. // Get mandatory parameters.
  523. std::string name = getString(option_def, "name");
  524. uint32_t code = getInteger(option_def, "code");
  525. std::string type = getString(option_def, "type");
  526. // Get optional parameters. Whoever called this parser, should have
  527. // called SimpleParser::setDefaults first.
  528. bool array_type = getBoolean(option_def, "array");
  529. std::string record_types = getString(option_def, "record-types");
  530. std::string space = getString(option_def, "space");
  531. std::string encapsulates = getString(option_def, "encapsulate");
  532. if (!OptionSpace::validateName(space)) {
  533. isc_throw(DhcpConfigError, "invalid option space name '"
  534. << space << "' ("
  535. << getPosition("space", option_def) << ")");
  536. }
  537. // Create option definition.
  538. OptionDefinitionPtr def;
  539. // We need to check if user has set encapsulated option space
  540. // name. If so, different constructor will be used.
  541. if (!encapsulates.empty()) {
  542. // Arrays can't be used together with sub-options.
  543. if (array_type) {
  544. isc_throw(DhcpConfigError, "option '" << space << "."
  545. << "name" << "', comprising an array of data"
  546. << " fields may not encapsulate any option space ("
  547. << option_def->getPosition() << ")");
  548. } else if (encapsulates == space) {
  549. isc_throw(DhcpConfigError, "option must not encapsulate"
  550. << " an option space it belongs to: '"
  551. << space << "." << name << "' is set to"
  552. << " encapsulate '" << space << "' ("
  553. << option_def->getPosition() << ")");
  554. } else {
  555. def.reset(new OptionDefinition(name, code, type,
  556. encapsulates.c_str()));
  557. }
  558. } else {
  559. def.reset(new OptionDefinition(name, code, type, array_type));
  560. }
  561. // Split the list of record types into tokens.
  562. std::vector<std::string> record_tokens =
  563. isc::util::str::tokens(record_types, ",");
  564. // Iterate over each token and add a record type into
  565. // option definition.
  566. BOOST_FOREACH(std::string record_type, record_tokens) {
  567. try {
  568. boost::trim(record_type);
  569. if (!record_type.empty()) {
  570. def->addRecordField(record_type);
  571. }
  572. } catch (const Exception& ex) {
  573. isc_throw(DhcpConfigError, "invalid record type values"
  574. << " specified for the option definition: "
  575. << ex.what() << " ("
  576. << getPosition("record-types", option_def) << ")");
  577. }
  578. }
  579. // Validate the definition.
  580. try {
  581. def->validate();
  582. } catch (const std::exception& ex) {
  583. isc_throw(DhcpConfigError, ex.what()
  584. << " (" << option_def->getPosition() << ")");
  585. }
  586. // Option definition has been created successfully.
  587. return make_pair(def, space);
  588. }
  589. // ******************************** OptionDefListParser ************************
  590. void
  591. OptionDefListParser::parse(CfgOptionDefPtr storage, ConstElementPtr option_def_list) {
  592. if (!option_def_list) {
  593. isc_throw(DhcpConfigError, "parser error: a pointer to a list of"
  594. << " option definitions is NULL ("
  595. << option_def_list->getPosition() << ")");
  596. }
  597. OptionDefParser parser;
  598. BOOST_FOREACH(ConstElementPtr option_def, option_def_list->listValue()) {
  599. OptionDefinitionTuple def;
  600. def = parser.parse(option_def);
  601. try {
  602. storage->add(def.first, def.second);
  603. } catch (const std::exception& ex) {
  604. // Append position if there is a failure.
  605. isc_throw(DhcpConfigError, ex.what() << " ("
  606. << option_def->getPosition() << ")");
  607. }
  608. }
  609. // All definitions have been prepared. Put them as runtime options into
  610. // the libdhcp++.
  611. LibDHCP::setRuntimeOptionDefs(storage->getContainer());
  612. }
  613. //****************************** RelayInfoParser ********************************
  614. RelayInfoParser::RelayInfoParser(const Option::Universe& family)
  615. : family_(family) {
  616. };
  617. void
  618. RelayInfoParser::parse(const isc::dhcp::Subnet::RelayInfoPtr& cfg,
  619. ConstElementPtr relay_info) {
  620. // Let's start with some sanity checks.
  621. if (!relay_info || !cfg) {
  622. isc_throw(DhcpConfigError, "Logic error: RelayInfoParser::parse() called "
  623. "with at least one NULL parameter.");
  624. }
  625. if (relay_info->getType() != Element::map) {
  626. isc_throw(DhcpConfigError, "Configuration error: RelayInfoParser::parse() "
  627. "called with non-map parameter");
  628. }
  629. // Now create the default value.
  630. isc::asiolink::IOAddress ip(family_ == Option::V4 ? IOAddress::IPV4_ZERO_ADDRESS()
  631. : IOAddress::IPV6_ZERO_ADDRESS());
  632. // Now iterate over all parameters. Currently there's only one supported
  633. // parameter, so it should be an easy thing to check.
  634. bool ip_address_specified = false;
  635. BOOST_FOREACH(ConfigPair param, relay_info->mapValue()) {
  636. if (param.first == "ip-address") {
  637. ip_address_specified = true;
  638. try {
  639. ip = asiolink::IOAddress(param.second->stringValue());
  640. } catch (...) {
  641. isc_throw(DhcpConfigError, "Failed to parse ip-address "
  642. "value: " << param.second
  643. << " (" << param.second->getPosition() << ")");
  644. }
  645. // Check if the address family matches.
  646. if ( (ip.isV4() && family_ != Option::V4) ||
  647. (ip.isV6() && family_ != Option::V6) ) {
  648. isc_throw(DhcpConfigError, "ip-address field " << ip.toText()
  649. << " does not have IP address of expected family type: "
  650. << (family_ == Option::V4 ? "IPv4" : "IPv6")
  651. << " (" << param.second->getPosition() << ")");
  652. }
  653. } else {
  654. isc_throw(NotImplemented,
  655. "parser error: RelayInfoParser parameter not supported: "
  656. << param.second);
  657. }
  658. }
  659. if (!ip_address_specified) {
  660. isc_throw(DhcpConfigError, "'relay' specified, but mandatory 'ip-address' "
  661. "paramter in it is missing");
  662. }
  663. // Ok, we're done with parsing. Let's store the result in the structure
  664. // we were given as configuration storage.
  665. *cfg = isc::dhcp::Subnet::RelayInfo(ip);
  666. }
  667. //****************************** PoolParser ********************************
  668. void
  669. PoolParser::parse(PoolStoragePtr pools,
  670. ConstElementPtr pool_structure,
  671. const uint16_t address_family) {
  672. ConstElementPtr text_pool = pool_structure->get("pool");
  673. if (!text_pool) {
  674. isc_throw(DhcpConfigError, "Mandatory 'pool' entry missing in "
  675. "definition: (" << text_pool->getPosition() << ")");
  676. }
  677. // That should be a single pool representation. It should contain
  678. // text is form prefix/len or first - last. Note that spaces
  679. // are allowed
  680. string txt = text_pool->stringValue();
  681. // first let's remove any whitespaces
  682. boost::erase_all(txt, " "); // space
  683. boost::erase_all(txt, "\t"); // tabulation
  684. PoolPtr pool;
  685. // Is this prefix/len notation?
  686. size_t pos = txt.find("/");
  687. if (pos != string::npos) {
  688. isc::asiolink::IOAddress addr("::");
  689. uint8_t len = 0;
  690. try {
  691. addr = isc::asiolink::IOAddress(txt.substr(0, pos));
  692. // start with the first character after /
  693. string prefix_len = txt.substr(pos + 1);
  694. // It is lexical cast to int and then downcast to uint8_t.
  695. // Direct cast to uint8_t (which is really an unsigned char)
  696. // will result in interpreting the first digit as output
  697. // value and throwing exception if length is written on two
  698. // digits (because there are extra characters left over).
  699. // No checks for values over 128. Range correctness will
  700. // be checked in Pool4 constructor, here we only check
  701. // the representation fits in an uint8_t as this can't
  702. // be done by a direct lexical cast as explained...
  703. int val_len = boost::lexical_cast<int>(prefix_len);
  704. if ((val_len < std::numeric_limits<uint8_t>::min()) ||
  705. (val_len > std::numeric_limits<uint8_t>::max())) {
  706. // This exception will be handled 4 line later!
  707. isc_throw(OutOfRange, "");
  708. }
  709. len = static_cast<uint8_t>(val_len);
  710. } catch (...) {
  711. isc_throw(DhcpConfigError, "Failed to parse pool "
  712. "definition: " << txt << " ("
  713. << text_pool->getPosition() << ")");
  714. }
  715. try {
  716. pool = poolMaker(addr, len);
  717. pools->push_back(pool);
  718. } catch (const std::exception& ex) {
  719. isc_throw(DhcpConfigError, "Failed to create pool defined by: "
  720. << txt << " (" << text_pool->getPosition() << ")");
  721. }
  722. } else {
  723. isc::asiolink::IOAddress min("::");
  724. isc::asiolink::IOAddress max("::");
  725. // Is this min-max notation?
  726. pos = txt.find("-");
  727. if (pos != string::npos) {
  728. // using min-max notation
  729. try {
  730. min = isc::asiolink::IOAddress(txt.substr(0, pos));
  731. max = isc::asiolink::IOAddress(txt.substr(pos + 1));
  732. } catch (...) {
  733. isc_throw(DhcpConfigError, "Failed to parse pool "
  734. "definition: " << txt << " ("
  735. << text_pool->getPosition() << ")");
  736. }
  737. try {
  738. pool = poolMaker(min, max);
  739. pools->push_back(pool);
  740. } catch (const std::exception& ex) {
  741. isc_throw(DhcpConfigError, "Failed to create pool defined by: "
  742. << txt << " (" << text_pool->getPosition() << ")");
  743. }
  744. }
  745. }
  746. if (!pool) {
  747. isc_throw(DhcpConfigError, "invalid pool definition: "
  748. << text_pool->stringValue() <<
  749. ". There are two acceptable formats <min address-max address>"
  750. " or <prefix/len> ("
  751. << text_pool->getPosition() << ")");
  752. }
  753. // If there's user-context specified, store it.
  754. ConstElementPtr user_context = pool_structure->get("user-context");
  755. if (user_context) {
  756. if (user_context->getType() != Element::map) {
  757. isc_throw(isc::dhcp::DhcpConfigError, "User context has to be a map ("
  758. << user_context->getPosition() << ")");
  759. }
  760. pool->setUserContext(user_context);
  761. }
  762. // Parser pool specific options.
  763. ConstElementPtr option_data = pool_structure->get("option-data");
  764. if (option_data) {
  765. try {
  766. // Currently we don't support specifying options for the DHCPv4 server.
  767. if (address_family == AF_INET) {
  768. isc_throw(DhcpConfigError, "option-data is not supported for DHCPv4"
  769. " address pools");
  770. }
  771. CfgOptionPtr cfg = pool->getCfgOption();
  772. OptionDataListParser option_parser(address_family);
  773. option_parser.parse(cfg, option_data);
  774. } catch (const std::exception& ex) {
  775. isc_throw(isc::dhcp::DhcpConfigError, ex.what()
  776. << " (" << option_data->getPosition() << ")");
  777. }
  778. }
  779. }
  780. //****************************** SubnetConfigParser *************************
  781. SubnetConfigParser::SubnetConfigParser(uint16_t family)
  782. : pools_(new PoolStorage()),
  783. address_family_(family),
  784. options_(new CfgOption()) {
  785. string addr = family == AF_INET ? "0.0.0.0" : "::";
  786. relay_info_.reset(new isc::dhcp::Subnet::RelayInfo(IOAddress(addr)));
  787. }
  788. SubnetPtr
  789. SubnetConfigParser::parse(ConstElementPtr subnet) {
  790. ConstElementPtr options_params = subnet->get("option-data");
  791. if (options_params) {
  792. OptionDataListParser opt_parser(address_family_);
  793. opt_parser.parse(options_, options_params);
  794. }
  795. ConstElementPtr relay_params = subnet->get("relay");
  796. if (relay_params) {
  797. Option::Universe u = (address_family_ == AF_INET) ? Option::V4 : Option::V6;
  798. RelayInfoParser parser(u);
  799. parser.parse(relay_info_, relay_params);
  800. }
  801. // Create a subnet.
  802. try {
  803. createSubnet(subnet);
  804. } catch (const std::exception& ex) {
  805. isc_throw(DhcpConfigError,
  806. "subnet configuration failed: " << ex.what());
  807. }
  808. return (subnet_);
  809. }
  810. Subnet::HRMode
  811. SubnetConfigParser::hrModeFromText(const std::string& txt) {
  812. if ( (txt.compare("disabled") == 0) ||
  813. (txt.compare("off") == 0) ) {
  814. return (Subnet::HR_DISABLED);
  815. } else if (txt.compare("out-of-pool") == 0) {
  816. return (Subnet::HR_OUT_OF_POOL);
  817. } else if (txt.compare("all") == 0) {
  818. return (Subnet::HR_ALL);
  819. } else {
  820. isc_throw(BadValue, "Can't convert '" << txt
  821. << "' into any valid reservation-mode values");
  822. }
  823. }
  824. void
  825. SubnetConfigParser::createSubnet(ConstElementPtr params) {
  826. std::string subnet_txt;
  827. try {
  828. subnet_txt = getString(params, "subnet");
  829. } catch (const DhcpConfigError &) {
  830. // rethrow with precise error
  831. isc_throw(DhcpConfigError,
  832. "mandatory 'subnet' parameter is missing for a subnet being"
  833. " configured (" << params->getPosition() << ")");
  834. }
  835. // Remove any spaces or tabs.
  836. boost::erase_all(subnet_txt, " ");
  837. boost::erase_all(subnet_txt, "\t");
  838. // The subnet format is prefix/len. We are going to extract
  839. // the prefix portion of a subnet string to create IOAddress
  840. // object from it. IOAddress will be passed to the Subnet's
  841. // constructor later on. In order to extract the prefix we
  842. // need to get all characters preceding "/".
  843. size_t pos = subnet_txt.find("/");
  844. if (pos == string::npos) {
  845. ConstElementPtr elem = params->get("subnet");
  846. isc_throw(DhcpConfigError,
  847. "Invalid subnet syntax (prefix/len expected):" << subnet_txt
  848. << " (" << elem->getPosition() << ")");
  849. }
  850. // Try to create the address object. It also validates that
  851. // the address syntax is ok.
  852. isc::asiolink::IOAddress addr(subnet_txt.substr(0, pos));
  853. uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
  854. // Call the subclass's method to instantiate the subnet
  855. initSubnet(params, addr, len);
  856. // Add pools to it.
  857. for (PoolStorage::iterator it = pools_->begin(); it != pools_->end();
  858. ++it) {
  859. try {
  860. subnet_->addPool(*it);
  861. } catch (const BadValue& ex) {
  862. // addPool() can throw BadValue if the pool is overlapping or
  863. // is out of bounds for the subnet.
  864. isc_throw(DhcpConfigError, ex.what() << "(" << params->getPosition()
  865. << ")");
  866. }
  867. }
  868. // Now configure parameters that are common for v4 and v6:
  869. // Get interface name. If it is defined, then the subnet is available
  870. // directly over specified network interface.
  871. std::string iface = getString(params, "interface");
  872. if (!iface.empty()) {
  873. if (!IfaceMgr::instance().getIface(iface)) {
  874. ConstElementPtr error = params->get("interface");
  875. isc_throw(DhcpConfigError, "Specified network interface name " << iface
  876. << " for subnet " << subnet_->toText()
  877. << " is not present in the system ("
  878. << error->getPosition() << ")");
  879. }
  880. subnet_->setIface(iface);
  881. }
  882. // Let's set host reservation mode. If not specified, the default value of
  883. // all will be used.
  884. try {
  885. std::string hr_mode = getString(params, "reservation-mode");
  886. subnet_->setHostReservationMode(hrModeFromText(hr_mode));
  887. } catch (const BadValue& ex) {
  888. ConstElementPtr mode = params->get("reservation-mode");
  889. string pos("[missing]");
  890. if (mode) {
  891. pos = mode->getPosition().str();
  892. }
  893. isc_throw(DhcpConfigError, "Failed to process specified value "
  894. " of reservation-mode parameter: " << ex.what()
  895. << "(" << pos << ")");
  896. }
  897. // Try setting up client class.
  898. string client_class = getString(params, "client-class");
  899. if (!client_class.empty()) {
  900. subnet_->allowClientClass(client_class);
  901. }
  902. // Here globally defined options were merged to the subnet specific
  903. // options but this is no longer the case (they have a different
  904. // and not consecutive priority).
  905. // Copy options to the subnet configuration.
  906. options_->copyTo(*subnet_->getCfgOption());
  907. }
  908. //**************************** D2ClientConfigParser **********************
  909. uint32_t
  910. D2ClientConfigParser::getUint32(const std::string& name,
  911. ConstElementPtr value) const {
  912. return (extractInt<uint32_t, DhcpConfigError>(name, value));
  913. }
  914. namespace {
  915. IOAddress buildIOAddress(const std::string& str) { return (IOAddress(str)); }
  916. };
  917. IOAddress
  918. D2ClientConfigParser::getIOAddress(const std::string& name,
  919. ConstElementPtr value) const {
  920. return (extractConvert<IOAddress,
  921. buildIOAddress,
  922. DhcpConfigError>(name, "address", value));
  923. }
  924. dhcp_ddns::NameChangeProtocol
  925. D2ClientConfigParser::getProtocol(const std::string& name,
  926. ConstElementPtr value) const {
  927. return (extractConvert<dhcp_ddns::NameChangeProtocol,
  928. dhcp_ddns::stringToNcrProtocol,
  929. DhcpConfigError>(name,
  930. "NameChangeRequest protocol",
  931. value));
  932. }
  933. dhcp_ddns::NameChangeFormat
  934. D2ClientConfigParser::getFormat(const std::string& name,
  935. ConstElementPtr value) const {
  936. return (extractConvert<dhcp_ddns::NameChangeFormat,
  937. dhcp_ddns::stringToNcrFormat,
  938. DhcpConfigError>(name,
  939. "NameChangeRequest format",
  940. value));
  941. }
  942. D2ClientConfig::ReplaceClientNameMode
  943. D2ClientConfigParser::getMode(const std::string& name,
  944. ConstElementPtr value) const {
  945. return (extractConvert<D2ClientConfig::ReplaceClientNameMode,
  946. D2ClientConfig::stringToReplaceClientNameMode,
  947. DhcpConfigError>(name,
  948. "ReplaceClientName mode",
  949. value));
  950. }
  951. D2ClientConfigPtr
  952. D2ClientConfigParser::parse(isc::data::ConstElementPtr client_config) {
  953. D2ClientConfigPtr new_config;
  954. if (isShortCutDisabled(client_config)) {
  955. // If enable-updates is the only parameter and it is false then
  956. // we're done. This allows for an abbreviated configuration entry
  957. // that only contains that flag. Use the default D2ClientConfig
  958. // constructor to a create a disabled instance.
  959. new_config.reset(new D2ClientConfig());
  960. return (new_config);
  961. }
  962. // As isShortCutDisabled() was called this cannot fail
  963. bool enable_updates = client_config->get("enable-updates")->boolValue();
  964. // Get all parameters that are needed to create the D2ClientConfig.
  965. std::string qualifying_suffix;
  966. bool found_qualifying_suffix = false;
  967. IOAddress server_ip(0);
  968. uint32_t server_port = 0;
  969. std::string sender_ip_str;
  970. uint32_t sender_port = 0;
  971. uint32_t max_queue_size = 1024;
  972. dhcp_ddns::NameChangeProtocol ncr_protocol;
  973. dhcp_ddns::NameChangeFormat ncr_format;
  974. bool always_include_fqdn = false;
  975. bool allow_client_update;
  976. bool override_no_update = false;
  977. bool override_client_update = false;
  978. D2ClientConfig::ReplaceClientNameMode replace_client_name_mode =
  979. D2ClientConfig::ReplaceClientNameMode::RCM_NEVER;
  980. std::string generated_prefix;
  981. BOOST_FOREACH(ConfigPair param, client_config->mapValue()) {
  982. std::string entry(param.first);
  983. ConstElementPtr value(param.second);
  984. try {
  985. if (entry == "enable-updates") {
  986. // already done.
  987. } else if (entry == "qualifying-suffix") {
  988. qualifying_suffix = value->stringValue();
  989. found_qualifying_suffix = true;
  990. } else if (entry == "server-ip") {
  991. server_ip = getIOAddress("server-ip", value);
  992. } else if (entry == "server-port") {
  993. server_port = getUint32("server-port", value);
  994. } else if (entry == "sender-ip") {
  995. sender_ip_str = value->stringValue();
  996. } else if (entry == "sender-port") {
  997. sender_port = getUint32("sender-port", value);
  998. } else if (entry == "max-queue-size") {
  999. max_queue_size = getUint32("max-queue-size", value);
  1000. } else if (entry == "ncr-protocol") {
  1001. ncr_protocol = getProtocol("ncr-protocol", value);
  1002. } else if (entry == "ncr-format") {
  1003. ncr_format = getFormat("ncr-format", value);
  1004. } else if (entry == "always-include-fqdn") {
  1005. always_include_fqdn = value->boolValue();
  1006. } else if (entry == "allow-client-update") {
  1007. allow_client_update = value->boolValue();
  1008. // currently unused
  1009. (void)allow_client_update;
  1010. } else if (entry == "override-no-update") {
  1011. override_no_update = value->boolValue();
  1012. } else if (entry == "override-client-update") {
  1013. override_client_update = value->boolValue();
  1014. } else if (entry == "replace-client-name") {
  1015. replace_client_name_mode = getMode("replace-client-name", value);
  1016. } else if (entry == "generated-prefix") {
  1017. generated_prefix = value->stringValue();
  1018. } else {
  1019. isc_throw(DhcpConfigError,
  1020. "unsupported parameter '" << entry
  1021. << " (" << value->getPosition() << ")");
  1022. }
  1023. } catch (const isc::data::TypeError&) {
  1024. isc_throw(DhcpConfigError,
  1025. "invalid value type specified for parameter '" << entry
  1026. << " (" << value->getPosition() << ")");
  1027. }
  1028. }
  1029. // Qualifying-suffix is required when updates are enabled
  1030. if (enable_updates && !found_qualifying_suffix) {
  1031. isc_throw(DhcpConfigError,
  1032. "parameter 'qualifying-suffix' is required when "
  1033. "updates are enabled ("
  1034. << client_config->getPosition() << ")");
  1035. }
  1036. IOAddress sender_ip(0);
  1037. if (sender_ip_str.empty()) {
  1038. // The default sender IP depends on the server IP family
  1039. sender_ip = (server_ip.isV4() ? IOAddress::IPV4_ZERO_ADDRESS() :
  1040. IOAddress::IPV6_ZERO_ADDRESS());
  1041. } else {
  1042. try {
  1043. sender_ip = IOAddress(sender_ip_str);
  1044. } catch (const std::exception& ex) {
  1045. isc_throw(DhcpConfigError, "invalid address (" << sender_ip_str
  1046. << ") specified for parameter 'sender-ip' ("
  1047. << getPosition("sender-ip", client_config) << ")");
  1048. }
  1049. }
  1050. // Now we check for logical errors. This repeats what is done in
  1051. // D2ClientConfig::validate(), but doing it here permits us to
  1052. // emit meaningful parameter position info in the error.
  1053. if (ncr_format != dhcp_ddns::FMT_JSON) {
  1054. isc_throw(D2ClientError, "D2ClientConfig error: NCR Format: "
  1055. << dhcp_ddns::ncrFormatToString(ncr_format)
  1056. << " is not supported. ("
  1057. << getPosition("ncr-format", client_config) << ")");
  1058. }
  1059. if (ncr_protocol != dhcp_ddns::NCR_UDP) {
  1060. isc_throw(D2ClientError, "D2ClientConfig error: NCR Protocol: "
  1061. << dhcp_ddns::ncrProtocolToString(ncr_protocol)
  1062. << " is not supported. ("
  1063. << getPosition("ncr-protocol", client_config) << ")");
  1064. }
  1065. if (sender_ip.getFamily() != server_ip.getFamily()) {
  1066. isc_throw(D2ClientError,
  1067. "D2ClientConfig error: address family mismatch: "
  1068. << "server-ip: " << server_ip.toText()
  1069. << " is: " << (server_ip.isV4() ? "IPv4" : "IPv6")
  1070. << " while sender-ip: " << sender_ip.toText()
  1071. << " is: " << (sender_ip.isV4() ? "IPv4" : "IPv6")
  1072. << " (" << getPosition("sender-ip", client_config) << ")");
  1073. }
  1074. if (server_ip == sender_ip && server_port == sender_port) {
  1075. isc_throw(D2ClientError,
  1076. "D2ClientConfig error: server and sender cannot"
  1077. " share the exact same IP address/port: "
  1078. << server_ip.toText() << "/" << server_port
  1079. << " (" << getPosition("sender-ip", client_config) << ")");
  1080. }
  1081. try {
  1082. // Attempt to create the new client config.
  1083. new_config.reset(new D2ClientConfig(enable_updates,
  1084. server_ip,
  1085. server_port,
  1086. sender_ip,
  1087. sender_port,
  1088. max_queue_size,
  1089. ncr_protocol,
  1090. ncr_format,
  1091. always_include_fqdn,
  1092. override_no_update,
  1093. override_client_update,
  1094. replace_client_name_mode,
  1095. generated_prefix,
  1096. qualifying_suffix));
  1097. } catch (const std::exception& ex) {
  1098. isc_throw(DhcpConfigError, ex.what() << " ("
  1099. << client_config->getPosition() << ")");
  1100. }
  1101. return(new_config);
  1102. }
  1103. bool
  1104. D2ClientConfigParser::isShortCutDisabled(isc::data::ConstElementPtr d2_config) {
  1105. if (!d2_config->contains("enable-updates")) {
  1106. isc_throw(DhcpConfigError,
  1107. "Mandatory parameter 'enable-updates' missing ("
  1108. << d2_config->getPosition() << ")");
  1109. }
  1110. ConstElementPtr enable = d2_config->get("enable-updates");
  1111. if (enable->getType() != Element::boolean) {
  1112. isc_throw(DhcpConfigError,
  1113. "invalid value type specified for parameter"
  1114. " 'enable-updates' (" << enable->getPosition() << ")");
  1115. }
  1116. return (!enable->boolValue() && (d2_config->mapValue().size() == 1));
  1117. }
  1118. /// @brief This table defines default values for D2 client configuration
  1119. const SimpleDefaults D2ClientConfigParser::D2_CLIENT_CONFIG_DEFAULTS = {
  1120. // enable-updates is unconditionally required
  1121. { "server-ip", Element::string, "127.0.0.1" },
  1122. { "server-port", Element::integer, "53001" },
  1123. // default sender-ip depends on server-ip family, so we leave default blank
  1124. // parser knows to use the appropriate ZERO address based on server-ip
  1125. { "sender-ip", Element::string, "" },
  1126. { "sender-port", Element::integer, "0" },
  1127. { "max-queue-size", Element::integer, "1024" },
  1128. { "ncr-protocol", Element::string, "UDP" },
  1129. { "ncr-format", Element::string, "JSON" },
  1130. { "always-include-fqdn", Element::boolean, "false" },
  1131. { "override-no-update", Element::boolean, "false" },
  1132. { "override-client-update", Element::boolean, "false" },
  1133. { "replace-client-name", Element::string, "never" },
  1134. { "generated-prefix", Element::string, "myhost" }
  1135. // qualifying-suffix has no default
  1136. };
  1137. size_t
  1138. D2ClientConfigParser::setAllDefaults(isc::data::ConstElementPtr d2_config) {
  1139. ElementPtr mutable_d2 = boost::const_pointer_cast<Element>(d2_config);
  1140. return (SimpleParser::setDefaults(mutable_d2, D2_CLIENT_CONFIG_DEFAULTS));
  1141. }
  1142. }; // namespace dhcp
  1143. }; // namespace isc