dhcp_parsers.cc 41 KB

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