config_parser.cc 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192
  1. // Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <stdint.h>
  15. #include <iostream>
  16. #include <vector>
  17. #include <map>
  18. #include <boost/foreach.hpp>
  19. #include <boost/shared_ptr.hpp>
  20. #include <boost/scoped_ptr.hpp>
  21. #include <boost/lexical_cast.hpp>
  22. #include <boost/algorithm/string.hpp>
  23. #include <util/encode/hex.h>
  24. #include <asiolink/io_address.h>
  25. #include <cc/data.h>
  26. #include <config/ccsession.h>
  27. #include <log/logger_support.h>
  28. #include <dhcp/libdhcp++.h>
  29. #include <dhcp/triplet.h>
  30. #include <dhcp/pool.h>
  31. #include <dhcp/subnet.h>
  32. #include <dhcp/cfgmgr.h>
  33. #include <dhcp6/config_parser.h>
  34. #include <dhcp6/dhcp6_log.h>
  35. using namespace std;
  36. using namespace isc::data;
  37. using namespace isc::asiolink;
  38. namespace isc {
  39. namespace dhcp {
  40. /// @brief auxiliary type used for storing element name and its parser
  41. typedef pair<string, ConstElementPtr> ConfigPair;
  42. /// @brief a factory method that will create a parser for a given element name
  43. typedef DhcpConfigParser* ParserFactory(const std::string& config_id);
  44. /// @brief a collection of factories that creates parsers for specified element names
  45. typedef std::map<std::string, ParserFactory*> FactoryMap;
  46. /// @brief a collection of elements that store uint32 values (e.g. renew-timer = 900)
  47. typedef std::map<string, uint32_t> Uint32Storage;
  48. /// @brief a collection of elements that store string values
  49. typedef std::map<string, string> StringStorage;
  50. /// @brief a collection of pools
  51. ///
  52. /// That type is used as intermediate storage, when pools are parsed, but there is
  53. /// no subnet object created yet to store them.
  54. typedef std::vector<Pool6Ptr> PoolStorage;
  55. /// @brief Collection of options.
  56. typedef std::vector<OptionPtr> OptionStorage;
  57. /// @brief Global uint32 parameters that will be used as defaults.
  58. Uint32Storage uint32_defaults;
  59. /// @brief global string parameters that will be used as defaults.
  60. StringStorage string_defaults;
  61. /// @brief Global storage for options that will be used as defaults.
  62. OptionStorage option_defaults;
  63. /// @brief a dummy configuration parser
  64. ///
  65. /// It is a debugging parser. It does not configure anything,
  66. /// will accept any configuration and will just print it out
  67. /// on commit. Useful for debugging existing configurations and
  68. /// adding new ones.
  69. class DebugParser : public DhcpConfigParser {
  70. public:
  71. /// @brief Constructor
  72. ///
  73. /// See \ref DhcpConfigParser class for details.
  74. ///
  75. /// @param param_name name of the parsed parameter
  76. DebugParser(const std::string& param_name)
  77. :param_name_(param_name) {
  78. }
  79. /// @brief builds parameter value
  80. ///
  81. /// See \ref DhcpConfigParser class for details.
  82. ///
  83. /// @param new_config pointer to the new configuration
  84. virtual void build(ConstElementPtr new_config) {
  85. std::cout << "Build for token: [" << param_name_ << "] = ["
  86. << value_->str() << "]" << std::endl;
  87. value_ = new_config;
  88. }
  89. /// @brief pretends to apply the configuration
  90. ///
  91. /// This is a method required by base class. It pretends to apply the
  92. /// configuration, but in fact it only prints the parameter out.
  93. ///
  94. /// See \ref DhcpConfigParser class for details.
  95. virtual void commit() {
  96. // Debug message. The whole DebugParser class is used only for parser
  97. // debugging, and is not used in production code. It is very convenient
  98. // to keep it around. Please do not turn this cout into logger calls.
  99. std::cout << "Commit for token: [" << param_name_ << "] = ["
  100. << value_->str() << "]" << std::endl;
  101. }
  102. /// @brief factory that constructs DebugParser objects
  103. ///
  104. /// @param param_name name of the parameter to be parsed
  105. static DhcpConfigParser* Factory(const std::string& param_name) {
  106. return (new DebugParser(param_name));
  107. }
  108. protected:
  109. /// name of the parsed parameter
  110. std::string param_name_;
  111. /// pointer to the actual value of the parameter
  112. ConstElementPtr value_;
  113. };
  114. /// @brief Configuration parser for uint32 parameters
  115. ///
  116. /// This class is a generic parser that is able to handle any uint32 integer
  117. /// type. By default it stores the value in external global container
  118. /// (uint32_defaults). If used in smaller scopes (e.g. to parse parameters
  119. /// in subnet config), it can be pointed to a different storage, using
  120. /// setStorage() method. This class follows the parser interface, laid out
  121. /// in its base class, \ref DhcpConfigParser.
  122. ///
  123. /// For overview of usability of this generic purpose parser, see
  124. /// \ref dhcpv6-config-inherit page.
  125. ///
  126. /// @todo this class should be turned into the template class which
  127. /// will handle all uintX_types of data (see ticket #2415).
  128. class Uint32Parser : public DhcpConfigParser {
  129. public:
  130. /// @brief constructor for Uint32Parser
  131. /// @param param_name name of the configuration parameter being parsed
  132. Uint32Parser(const std::string& param_name)
  133. :storage_(&uint32_defaults), param_name_(param_name) {
  134. }
  135. /// @brief builds parameter value
  136. ///
  137. /// Parses configuration entry and stores it in a storage. See
  138. /// \ref setStorage() for details.
  139. ///
  140. /// @param value pointer to the content of parsed values
  141. virtual void build(ConstElementPtr value) {
  142. bool parse_error = false;
  143. // Cast the provided value to int64 value to check.
  144. int64_t int64value = 0;
  145. try {
  146. // Parsing the value as a int64 value allows to
  147. // check if the provided value is within the range
  148. // of uint32_t (is not negative or greater than
  149. // maximal uint32_t value.
  150. int64value = boost::lexical_cast<int64_t>(value->str());
  151. } catch (const boost::bad_lexical_cast&) {
  152. parse_error = true;
  153. }
  154. if (!parse_error) {
  155. if ((int64value < 0) ||
  156. (int64value > std::numeric_limits<uint32_t>::max())) {
  157. parse_error = true;
  158. } else {
  159. try {
  160. value_ = boost::lexical_cast<uint32_t>(value->str());
  161. } catch (const boost::bad_lexical_cast &) {
  162. parse_error = true;
  163. }
  164. }
  165. }
  166. if (parse_error) {
  167. isc_throw(BadValue, "Failed to parse value " << value->str()
  168. << " as unsigned 32-bit integer.");
  169. }
  170. storage_->insert(pair<string, uint32_t>(param_name_, value_));
  171. }
  172. /// @brief does nothing
  173. ///
  174. /// This method is required for all parser. The value itself
  175. /// is not commited anywhere. Higher level parsers are expected to
  176. /// use values stored in the storage, e.g. renew-timer for a given
  177. /// subnet is stored in subnet-specific storage. It is not commited
  178. /// here, but is rather used by \ref Subnet6Parser when constructing
  179. /// the subnet.
  180. virtual void commit() {
  181. }
  182. /// @brief factory that constructs Uint32Parser objects
  183. ///
  184. /// @param param_name name of the parameter to be parsed
  185. static DhcpConfigParser* Factory(const std::string& param_name) {
  186. return (new Uint32Parser(param_name));
  187. }
  188. /// @brief sets storage for value of this parameter
  189. ///
  190. /// See \ref dhcpv6-config-inherit for details.
  191. ///
  192. /// @param storage pointer to the storage container
  193. void setStorage(Uint32Storage* storage) {
  194. storage_ = storage;
  195. }
  196. protected:
  197. /// pointer to the storage, where parsed value will be stored
  198. Uint32Storage* storage_;
  199. /// name of the parameter to be parsed
  200. std::string param_name_;
  201. /// the actual parsed value
  202. uint32_t value_;
  203. };
  204. /// @brief Configuration parser for string parameters
  205. ///
  206. /// This class is a generic parser that is able to handle any string
  207. /// parameter. By default it stores the value in external global container
  208. /// (string_defaults). If used in smaller scopes (e.g. to parse parameters
  209. /// in subnet config), it can be pointed to a different storage, using
  210. /// setStorage() method. This class follows the parser interface, laid out
  211. /// in its base class, \ref DhcpConfigParser.
  212. ///
  213. /// For overview of usability of this generic purpose parser, see
  214. /// \ref dhcpv6-config-inherit page.
  215. class StringParser : public DhcpConfigParser {
  216. public:
  217. /// @brief constructor for StringParser
  218. /// @param param_name name of the configuration parameter being parsed
  219. StringParser(const std::string& param_name)
  220. :storage_(&string_defaults), param_name_(param_name) {
  221. }
  222. /// @brief parses parameter value
  223. ///
  224. /// Parses configuration entry and stored it in storage. See
  225. /// \ref setStorage() for details.
  226. ///
  227. /// @param value pointer to the content of parsed values
  228. virtual void build(ConstElementPtr value) {
  229. value_ = value->str();
  230. boost::erase_all(value_, "\"");
  231. storage_->insert(pair<string, string>(param_name_, value_));
  232. }
  233. /// @brief does nothing
  234. ///
  235. /// This method is required for all parser. The value itself
  236. /// is not commited anywhere. Higher level parsers are expected to
  237. /// use values stored in the storage, e.g. renew-timer for a given
  238. /// subnet is stored in subnet-specific storage. It is not commited
  239. /// here, but is rather used by its parent parser when constructing
  240. /// an object, e.g. the subnet.
  241. virtual void commit() {
  242. }
  243. /// @brief factory that constructs StringParser objects
  244. ///
  245. /// @param param_name name of the parameter to be parsed
  246. static DhcpConfigParser* Factory(const std::string& param_name) {
  247. return (new StringParser(param_name));
  248. }
  249. /// @brief sets storage for value of this parameter
  250. ///
  251. /// See \ref dhcpv6-config-inherit for details.
  252. ///
  253. /// @param storage pointer to the storage container
  254. void setStorage(StringStorage* storage) {
  255. storage_ = storage;
  256. }
  257. protected:
  258. /// pointer to the storage, where parsed value will be stored
  259. StringStorage* storage_;
  260. /// name of the parameter to be parsed
  261. std::string param_name_;
  262. /// the actual parsed value
  263. std::string value_;
  264. };
  265. /// @brief parser for interface list definition
  266. ///
  267. /// This parser handles Dhcp6/interface entry.
  268. /// It contains a list of network interfaces that the server listens on.
  269. /// In particular, it can contain an entry called "all" or "any" that
  270. /// designates all interfaces.
  271. ///
  272. /// It is useful for parsing Dhcp6/interface parameter.
  273. class InterfaceListConfigParser : public DhcpConfigParser {
  274. public:
  275. /// @brief constructor
  276. ///
  277. /// As this is a dedicated parser, it must be used to parse
  278. /// "interface" parameter only. All other types will throw exception.
  279. ///
  280. /// @param param_name name of the configuration parameter being parsed
  281. InterfaceListConfigParser(const std::string& param_name) {
  282. if (param_name != "interface") {
  283. isc_throw(NotImplemented, "Internal error. Interface configuration "
  284. "parser called for the wrong parameter: " << param_name);
  285. }
  286. }
  287. /// @brief parses parameters value
  288. ///
  289. /// Parses configuration entry (list of parameters) and stores it in
  290. /// storage. See \ref setStorage() for details.
  291. ///
  292. /// @param value pointer to the content of parsed values
  293. virtual void build(ConstElementPtr value) {
  294. BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
  295. interfaces_.push_back(iface->str());
  296. }
  297. }
  298. /// @brief commits interfaces list configuration
  299. virtual void commit() {
  300. /// @todo: Implement per interface listening. Currently always listening
  301. /// on all interfaces.
  302. }
  303. /// @brief factory that constructs InterfaceListConfigParser objects
  304. ///
  305. /// @param param_name name of the parameter to be parsed
  306. static DhcpConfigParser* Factory(const std::string& param_name) {
  307. return (new InterfaceListConfigParser(param_name));
  308. }
  309. protected:
  310. /// contains list of network interfaces
  311. vector<string> interfaces_;
  312. };
  313. /// @brief parser for pool definition
  314. ///
  315. /// This parser handles pool definitions, i.e. a list of entries of one
  316. /// of two syntaxes: min-max and prefix/len. Pool6 objects are created
  317. /// and stored in chosen PoolStorage container.
  318. ///
  319. /// As there are no default values for pool, setStorage() must be called
  320. /// before build(). Otherwise exception will be thrown.
  321. ///
  322. /// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
  323. class PoolParser : public DhcpConfigParser {
  324. public:
  325. /// @brief constructor.
  326. PoolParser(const std::string& /*param_name*/)
  327. :pools_(NULL) {
  328. // ignore parameter name, it is always Dhcp6/subnet6[X]/pool
  329. }
  330. /// @brief parses the actual list
  331. ///
  332. /// This method parses the actual list of interfaces.
  333. /// No validation is done at this stage, everything is interpreted as
  334. /// interface name.
  335. void build(ConstElementPtr pools_list) {
  336. // setStorage() should have been called before build
  337. if (!pools_) {
  338. isc_throw(NotImplemented, "Parser logic error. No pool storage set,"
  339. " but pool parser asked to parse pools");
  340. }
  341. BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) {
  342. // That should be a single pool representation. It should contain
  343. // text is form prefix/len or first - last. Note that spaces
  344. // are allowed
  345. string txt = text_pool->stringValue();
  346. // first let's remove any whitespaces
  347. boost::erase_all(txt, " "); // space
  348. boost::erase_all(txt, "\t"); // tabulation
  349. // Is this prefix/len notation?
  350. size_t pos = txt.find("/");
  351. if (pos != string::npos) {
  352. IOAddress addr("::");
  353. uint8_t len = 0;
  354. try {
  355. addr = IOAddress(txt.substr(0, pos));
  356. // start with the first character after /
  357. string prefix_len = txt.substr(pos + 1);
  358. // It is lexical cast to int and then downcast to uint8_t.
  359. // Direct cast to uint8_t (which is really an unsigned char)
  360. // will result in interpreting the first digit as output
  361. // value and throwing exception if length is written on two
  362. // digits (because there are extra characters left over).
  363. // No checks for values over 128. Range correctness will
  364. // be checked in Pool6 constructor.
  365. len = boost::lexical_cast<int>(prefix_len);
  366. } catch (...) {
  367. isc_throw(Dhcp6ConfigError, "Failed to parse pool "
  368. "definition: " << text_pool->stringValue());
  369. }
  370. Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, addr, len));
  371. pools_->push_back(pool);
  372. continue;
  373. }
  374. // Is this min-max notation?
  375. pos = txt.find("-");
  376. if (pos != string::npos) {
  377. // using min-max notation
  378. IOAddress min(txt.substr(0,pos - 1));
  379. IOAddress max(txt.substr(pos + 1));
  380. Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, min, max));
  381. pools_->push_back(pool);
  382. continue;
  383. }
  384. isc_throw(Dhcp6ConfigError, "Failed to parse pool definition:"
  385. << text_pool->stringValue() <<
  386. ". Does not contain - (for min-max) nor / (prefix/len)");
  387. }
  388. }
  389. /// @brief sets storage for value of this parameter
  390. ///
  391. /// See \ref dhcpv6-config-inherit for details.
  392. ///
  393. /// @param storage pointer to the storage container
  394. void setStorage(PoolStorage* storage) {
  395. pools_ = storage;
  396. }
  397. /// @brief does nothing.
  398. ///
  399. /// This method is required for all parser. The value itself
  400. /// is not commited anywhere. Higher level parsers (for subnet) are expected
  401. /// to use values stored in the storage.
  402. virtual void commit() {}
  403. /// @brief factory that constructs PoolParser objects
  404. ///
  405. /// @param param_name name of the parameter to be parsed
  406. static DhcpConfigParser* Factory(const std::string& param_name) {
  407. return (new PoolParser(param_name));
  408. }
  409. protected:
  410. /// @brief pointer to the actual Pools storage
  411. ///
  412. /// That is typically a storage somewhere in Subnet parser
  413. /// (an upper level parser).
  414. PoolStorage* pools_;
  415. };
  416. /// @brief Parser for option data value.
  417. ///
  418. /// This parser parses configuration entries that specify value of
  419. /// a single option. These entries include option name, option code
  420. /// and data carried by the option. If parsing is successful than
  421. /// instance of an option is created and added to the storage provided
  422. /// by the calling class.
  423. ///
  424. /// @todo This class parses and validates option name. However it is
  425. /// not used anywhere util support for option spaces is implemented
  426. /// (see tickets #2319, #2314). When option spaces are implemented
  427. /// there will be a way to reference the particular option using
  428. /// its type (code) or option name.
  429. class OptionDataParser : public DhcpConfigParser {
  430. public:
  431. /// @brief Constructor.
  432. ///
  433. /// Class constructor.
  434. OptionDataParser(const std::string&)
  435. : options_(NULL) { }
  436. /// @brief Parses the single option data.
  437. ///
  438. /// This method parses the data of a single option from the configuration.
  439. /// The option data includes option name, option code and data being
  440. /// carried by this option. Eventually it creates the instance of the
  441. /// option.
  442. ///
  443. /// @warning setStorage must be called with valid storage pointer prior
  444. /// to calling this method.
  445. ///
  446. /// @param option_data_entries collection of entries that define value
  447. /// for a particular option.
  448. /// @throw Dhcp6ConfigError if invalid parameter specified in
  449. /// the configuration.
  450. /// @throw isc::InvalidOperation if failed to set storage prior to
  451. /// calling build.
  452. /// @throw isc::BadValue if option data storage is invalid.
  453. virtual void build(ConstElementPtr option_data_entries) {
  454. if (options_ == NULL) {
  455. isc_throw(isc::InvalidOperation, "Parser logic error: storage must be set before "
  456. "parsing option data.");
  457. }
  458. BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
  459. ParserPtr parser;
  460. if (param.first == "name") {
  461. boost::shared_ptr<StringParser>
  462. name_parser(dynamic_cast<StringParser*>(StringParser::Factory(param.first)));
  463. if (name_parser) {
  464. name_parser->setStorage(&string_values_);
  465. parser = name_parser;
  466. }
  467. } else if (param.first == "code") {
  468. boost::shared_ptr<Uint32Parser>
  469. code_parser(dynamic_cast<Uint32Parser*>(Uint32Parser::Factory(param.first)));
  470. if (code_parser) {
  471. code_parser->setStorage(&uint32_values_);
  472. parser = code_parser;
  473. }
  474. } else if (param.first == "data") {
  475. boost::shared_ptr<StringParser>
  476. value_parser(dynamic_cast<StringParser*>(StringParser::Factory(param.first)));
  477. if (value_parser) {
  478. value_parser->setStorage(&string_values_);
  479. parser = value_parser;
  480. }
  481. } else {
  482. isc_throw(Dhcp6ConfigError,
  483. "Parser error: option-data parameter not supported: "
  484. << param.first);
  485. }
  486. parser->build(param.second);
  487. }
  488. // Try to create the option instance.
  489. createOption();
  490. }
  491. /// @brief Does nothing.
  492. ///
  493. /// This function does noting because option data is committed
  494. /// by a higher level parser.
  495. virtual void commit() { }
  496. /// @brief Set storage for the parser.
  497. ///
  498. /// Sets storage for the parser. This storage points to the
  499. /// vector of options and is used by multiple instances of
  500. /// OptionDataParser. Each instance creates exactly one object
  501. /// of dhcp::Option or derived type and appends it to this
  502. /// storage.
  503. ///
  504. /// @param storage pointer to the options storage
  505. void setStorage(OptionStorage* storage) {
  506. options_ = storage;
  507. }
  508. private:
  509. /// @brief Create option instance.
  510. ///
  511. /// Creates an instance of an option and adds it to the provided
  512. /// options storage. If the option data parsed by \ref build function
  513. /// are invalid or insufficient it emits exception.
  514. ///
  515. /// @warning this function does not check if options_ storage pointer
  516. /// is intitialized but this is not needed here because it is checked in
  517. /// \ref build function.
  518. ///
  519. /// @throw Dhcp6ConfigError if parameters provided in the configuration
  520. /// are invalid.
  521. void createOption() {
  522. // Option code is held in the uint32_t storage but is supposed to
  523. // be uint16_t value. We need to check that value in the configuration
  524. // does not exceed range of uint16_t and is not zero.
  525. uint32_t option_code = getUint32Param("code");
  526. if (option_code == 0) {
  527. isc_throw(Dhcp6ConfigError, "Parser error: value of 'code' must not"
  528. << " be equal to zero. Option code '0' is reserved in"
  529. << " DHCPv6.");
  530. } else if (option_code > std::numeric_limits<uint16_t>::max()) {
  531. isc_throw(Dhcp6ConfigError, "Parser error: value of 'code' must not"
  532. << " exceed " << std::numeric_limits<uint16_t>::max());
  533. }
  534. // Check the option name has been specified, is non-empty and does not
  535. // contain spaces.
  536. // @todo possibly some more restrictions apply here?
  537. std::string option_name = getStringParam("name");
  538. if (option_name.empty()) {
  539. isc_throw(Dhcp6ConfigError, "Parser error: option name must not be"
  540. << " empty");
  541. } else if (option_name.find(" ") != std::string::npos) {
  542. isc_throw(Dhcp6ConfigError, "Parser error: option name must not contain"
  543. << " spaces");
  544. }
  545. std::string option_data = getStringParam("data");
  546. std::vector<uint8_t> binary;
  547. try {
  548. util::encode::decodeHex(option_data, binary);
  549. } catch (...) {
  550. isc_throw(Dhcp6ConfigError, "Parser error: option data is not a valid"
  551. << " string of hexadecimal digits: " << option_data);
  552. }
  553. OptionDefContainer option_defs = LibDHCP::getOptionDefs(Option::V6);
  554. const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
  555. const OptionDefContainerTypeRange& range = idx.equal_range(option_code);
  556. size_t num_defs = std::distance(range.first, range.second);
  557. OptionPtr option;
  558. if (num_defs > 1) {
  559. isc_throw(Dhcp6ConfigError, "Internal error: currently it is not"
  560. << " supported to initialize multiple option definitions"
  561. << " for the same option code. This will be supported once"
  562. << " there option spaces are implemented.");
  563. } else if (num_defs == 0) {
  564. // Create the actual option.
  565. OptionPtr option(new Option(Option::V6, static_cast<uint16_t>(option_code),
  566. binary));
  567. // If option is created succesfully, add it to the storage.
  568. options_->push_back(option);
  569. } else {
  570. const OptionDefinitionPtr& def = *(range.first);
  571. // getFactory should never return NULL pointer so we skip
  572. // sanity check here.
  573. Option::Factory* factory = def->getFactory();
  574. try {
  575. OptionPtr option = factory(Option::V6, option_code, binary);
  576. options_->push_back(option);
  577. } catch (const isc::Exception& ex) {
  578. isc_throw(Dhcp6ConfigError, "Parser error: option data does not match"
  579. << " option definition (code " << option_code << "): "
  580. << ex.what());
  581. }
  582. }
  583. }
  584. /// @brief Get a parameter from the strings storage.
  585. ///
  586. /// @param param_id parameter identifier.
  587. /// @throw Dhcp6ConfigError if parameter has not been found.
  588. std::string getStringParam(const std::string& param_id) const {
  589. StringStorage::const_iterator param = string_values_.find(param_id);
  590. if (param == string_values_.end()) {
  591. isc_throw(Dhcp6ConfigError, "Parser error: option-data parameter"
  592. << " '" << param_id << "' not specified");
  593. }
  594. return (param->second);
  595. }
  596. /// @brief Get a parameter from the uint32 values storage.
  597. ///
  598. /// @param param_id parameter identifier.
  599. /// @throw Dhcp6ConfigError if parameter has not been found.
  600. uint32_t getUint32Param(const std::string& param_id) const {
  601. Uint32Storage::const_iterator param = uint32_values_.find(param_id);
  602. if (param == uint32_values_.end()) {
  603. isc_throw(Dhcp6ConfigError, "Parser error: option-data parameter"
  604. << " '" << param_id << "' not specified");
  605. }
  606. return (param->second);
  607. }
  608. /// Storage for uint32 values (e.g. option code).
  609. Uint32Storage uint32_values_;
  610. /// Storage for string values (e.g. option name or data).
  611. StringStorage string_values_;
  612. /// Pointer to options storage. This storage is provided by
  613. /// the calling class and is shared by all OptionDataParser objects.
  614. OptionStorage* options_;
  615. };
  616. /// @brief Parser for option data values with ina subnet.
  617. ///
  618. /// This parser iterates over all entries that define options
  619. /// data for a particular subnet and creates a collection of options.
  620. /// If parsing is successful, all these options are added to the Subnet
  621. /// object.
  622. class OptionDataListParser : public DhcpConfigParser {
  623. public:
  624. /// @brief Constructor.
  625. ///
  626. /// Unless otherwise specified, parsed options will be stored in
  627. /// a global option containers (option_default). That storage location
  628. /// is overriden on a subnet basis.
  629. OptionDataListParser(const std::string&)
  630. : options_(&option_defaults) { }
  631. /// @brief Parses entries that define options' data for a subnet.
  632. ///
  633. /// This method iterates over all entries that define option data
  634. /// for options within a single subnet and creates options' instances.
  635. ///
  636. /// @param option_data_list pointer to a list of options' data sets.
  637. /// @throw Dhcp6ConfigError if option parsing failed.
  638. void build(ConstElementPtr option_data_list) {
  639. BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
  640. boost::shared_ptr<OptionDataParser> parser(new OptionDataParser("option-data"));
  641. // options_ member will hold instances of all options thus
  642. // each OptionDataParser takes it as a storage.
  643. parser->setStorage(options_);
  644. // Build the instance of a singkle option.
  645. parser->build(option_value);
  646. }
  647. }
  648. /// @brief Set storage for option instances.
  649. ///
  650. /// @param storage pointer to options storage.
  651. void setStorage(OptionStorage* storage) {
  652. options_ = storage;
  653. }
  654. /// @brief Does nothing.
  655. ///
  656. /// @todo Currently this function does nothing but in the future
  657. /// we may need to extend it to commit at this level.
  658. void commit() { }
  659. /// @brief Create DhcpDataListParser object
  660. ///
  661. /// @param param_name param name.
  662. ///
  663. /// @return DhcpConfigParser object.
  664. static DhcpConfigParser* Factory(const std::string& param_name) {
  665. return (new OptionDataListParser(param_name));
  666. }
  667. /// Pointer to options instances storage.
  668. OptionStorage* options_;
  669. };
  670. /// @brief this class parses a single subnet
  671. ///
  672. /// This class parses the whole subnet definition. It creates parsers
  673. /// for received configuration parameters as needed.
  674. class Subnet6ConfigParser : public DhcpConfigParser {
  675. public:
  676. /// @brief constructor
  677. Subnet6ConfigParser(const std::string& ) {
  678. // The parameter should always be "subnet", but we don't check here
  679. // against it in case some wants to reuse this parser somewhere.
  680. }
  681. /// @brief parses parameter value
  682. ///
  683. /// @param subnet pointer to the content of subnet definition
  684. void build(ConstElementPtr subnet) {
  685. BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
  686. ParserPtr parser(createSubnet6ConfigParser(param.first));
  687. // The actual type of the parser is unknown here. We have to discover
  688. // parser type here to invoke corresponding setStorage function on it.
  689. // We discover parser type by trying to cast the parser to various
  690. // parser types and checking which one was successful. For this one
  691. // a setStorage and build methods are invoked.
  692. // Try uint32 type parser.
  693. if (buildParser<Uint32Parser, Uint32Storage >(parser, uint32_values_,
  694. param.second)) {
  695. // Storage set, build invoked on the parser, proceed with
  696. // next configuration element.
  697. continue;
  698. }
  699. // Try string type parser.
  700. if (buildParser<StringParser, StringStorage >(parser, string_values_,
  701. param.second)) {
  702. continue;
  703. }
  704. // Try pools parser.
  705. if (buildParser<PoolParser, PoolStorage >(parser, pools_,
  706. param.second)) {
  707. continue;
  708. }
  709. // Try option data parser.
  710. if (buildParser<OptionDataListParser, OptionStorage >(parser, options_,
  711. param.second)) {
  712. continue;
  713. }
  714. }
  715. // Ok, we now have subnet parsed
  716. }
  717. /// @brief commits received configuration.
  718. ///
  719. /// This method does most of the configuration. Many other parsers are just
  720. /// storing the values that are actually consumed here. Pool definitions
  721. /// created in other parsers are used here and added to newly created Subnet6
  722. /// objects. Subnet6 are then added to DHCP CfgMgr.
  723. void commit() {
  724. StringStorage::const_iterator it = string_values_.find("subnet");
  725. if (it == string_values_.end()) {
  726. isc_throw(Dhcp6ConfigError,
  727. "Mandatory subnet definition in subnet missing");
  728. }
  729. string subnet_txt = it->second;
  730. boost::erase_all(subnet_txt, " ");
  731. boost::erase_all(subnet_txt, "\t");
  732. size_t pos = subnet_txt.find("/");
  733. if (pos == string::npos) {
  734. isc_throw(Dhcp6ConfigError,
  735. "Invalid subnet syntax (prefix/len expected):" << it->second);
  736. }
  737. IOAddress addr(subnet_txt.substr(0, pos));
  738. uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
  739. Triplet<uint32_t> t1 = getParam("renew-timer");
  740. Triplet<uint32_t> t2 = getParam("rebind-timer");
  741. Triplet<uint32_t> pref = getParam("preferred-lifetime");
  742. Triplet<uint32_t> valid = getParam("valid-lifetime");
  743. /// @todo: Convert this to logger once the parser is working reliably
  744. stringstream tmp;
  745. tmp << addr.toText() << "/" << (int)len
  746. << " with params t1=" << t1 << ", t2=" << t2 << ", pref="
  747. << pref << ", valid=" << valid;
  748. LOG_INFO(dhcp6_logger, DHCP6_CONFIG_NEW_SUBNET).arg(tmp.str());
  749. Subnet6Ptr subnet(new Subnet6(addr, len, t1, t2, pref, valid));
  750. for (PoolStorage::iterator it = pools_.begin(); it != pools_.end(); ++it) {
  751. subnet->addPool6(*it);
  752. }
  753. const Subnet::OptionContainer& options = subnet->getOptions();
  754. const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
  755. // Add subnet specific options.
  756. BOOST_FOREACH(OptionPtr option, options_) {
  757. Subnet::OptionContainerTypeRange range = idx.equal_range(option->getType());
  758. if (std::distance(range.first, range.second) > 0) {
  759. LOG_WARN(dhcp6_logger, DHCP6_CONFIG_OPTION_DUPLICATE)
  760. .arg(option->getType()).arg(addr.toText());
  761. }
  762. subnet->addOption(option);
  763. }
  764. // Check all global options and add them to the subnet object if
  765. // they have been configured in the global scope. If they have been
  766. // configured in the subnet scope we don't add global option because
  767. // the one configured in the subnet scope always takes precedense.
  768. BOOST_FOREACH(OptionPtr option, option_defaults) {
  769. // Get all options specified locally in the subnet and having
  770. // code equal to global option's code.
  771. Subnet::OptionContainerTypeRange range = idx.equal_range(option->getType());
  772. // @todo: In the future we will be searching for options using either
  773. // option code or namespace. Currently we have only the option
  774. // code available so if there is at least one option found with the
  775. // specific code we don't add globally configured option.
  776. // @todo with this code the first globally configured option
  777. // with the given code will be added to a subnet. We may
  778. // want to issue warning about dropping configuration of
  779. // global option if one already exsist.
  780. if (std::distance(range.first, range.second) == 0) {
  781. subnet->addOption(option);
  782. }
  783. }
  784. CfgMgr::instance().addSubnet6(subnet);
  785. }
  786. private:
  787. /// @brief Set storage for a parser and invoke build.
  788. ///
  789. /// This helper method casts the provided parser pointer to specified
  790. /// type. If cast is successful it sets the corresponding storage for
  791. /// this parser, invokes build on it and save the parser.
  792. ///
  793. /// @tparam T parser type to which parser argument should be cast.
  794. /// @tparam Y storage type for the specified parser type.
  795. /// @param parser parser on which build must be invoked.
  796. /// @param storage reference to a storage that will be set for a parser.
  797. /// @param subnet subnet element read from the configuration and being parsed.
  798. /// @return true if parser pointer was successfully cast to specialized
  799. /// parser type provided as Y.
  800. template<typename T, typename Y>
  801. bool buildParser(const ParserPtr& parser, Y& storage, const ConstElementPtr& subnet) {
  802. // We need to cast to T in order to set storage for the parser.
  803. boost::shared_ptr<T> cast_parser = boost::dynamic_pointer_cast<T>(parser);
  804. // It is common that this cast is not successful because we try to cast to all
  805. // supported parser types as we don't know the type of a parser in advance.
  806. if (cast_parser) {
  807. // Cast, successful so we go ahead with setting storage and actual parse.
  808. cast_parser->setStorage(&storage);
  809. parser->build(subnet);
  810. parsers_.push_back(parser);
  811. // We indicate that cast was successful so as the calling function
  812. // may skip attempts to cast to other parser types and proceed to
  813. // next element.
  814. return (true);
  815. }
  816. // It was not successful. Indicate that another parser type
  817. // should be tried.
  818. return (false);
  819. }
  820. /// @brief creates parsers for entries in subnet definition
  821. ///
  822. /// @todo Add subnet-specific things here (e.g. subnet-specific options)
  823. ///
  824. /// @param config_id name od the entry
  825. /// @return parser object for specified entry name
  826. DhcpConfigParser* createSubnet6ConfigParser(const std::string& config_id) {
  827. FactoryMap factories;
  828. factories.insert(pair<string, ParserFactory*>(
  829. "preferred-lifetime", Uint32Parser::Factory));
  830. factories.insert(pair<string, ParserFactory*>(
  831. "valid-lifetime", Uint32Parser::Factory));
  832. factories.insert(pair<string, ParserFactory*>(
  833. "renew-timer", Uint32Parser::Factory));
  834. factories.insert(pair<string, ParserFactory*>(
  835. "rebind-timer", Uint32Parser::Factory));
  836. factories.insert(pair<string, ParserFactory*>(
  837. "subnet", StringParser::Factory));
  838. factories.insert(pair<string, ParserFactory*>(
  839. "pool", PoolParser::Factory));
  840. factories.insert(pair<string, ParserFactory*>(
  841. "option-data", OptionDataListParser::Factory));
  842. FactoryMap::iterator f = factories.find(config_id);
  843. if (f == factories.end()) {
  844. // Used for debugging only.
  845. // return new DebugParser(config_id);
  846. isc_throw(NotImplemented,
  847. "Parser error: Subnet6 parameter not supported: "
  848. << config_id);
  849. }
  850. return (f->second(config_id));
  851. }
  852. /// @brief returns value for a given parameter (after using inheritance)
  853. ///
  854. /// This method implements inheritance. For a given parameter name, it first
  855. /// checks if there is a global value for it and overwrites it with specific
  856. /// value if such value was defined in subnet.
  857. ///
  858. /// @param name name of the parameter
  859. /// @return triplet with the parameter name
  860. Triplet<uint32_t> getParam(const std::string& name) {
  861. uint32_t value = 0;
  862. bool found = false;
  863. Uint32Storage::iterator global = uint32_defaults.find(name);
  864. if (global != uint32_defaults.end()) {
  865. value = global->second;
  866. found = true;
  867. }
  868. Uint32Storage::iterator local = uint32_values_.find(name);
  869. if (local != uint32_values_.end()) {
  870. value = local->second;
  871. found = true;
  872. }
  873. if (found) {
  874. return (Triplet<uint32_t>(value));
  875. } else {
  876. isc_throw(Dhcp6ConfigError, "Mandatory parameter " << name
  877. << " missing (no global default and no subnet-"
  878. << "specific value)");
  879. }
  880. }
  881. /// storage for subnet-specific uint32 values
  882. Uint32Storage uint32_values_;
  883. /// storage for subnet-specific integer values
  884. StringStorage string_values_;
  885. /// storage for pools belonging to this subnet
  886. PoolStorage pools_;
  887. /// storage for options belonging to this subnet
  888. OptionStorage options_;
  889. /// parsers are stored here
  890. ParserCollection parsers_;
  891. };
  892. /// @brief this class parses list of subnets
  893. ///
  894. /// This is a wrapper parser that handles the whole list of Subnet6
  895. /// definitions. It iterates over all entries and creates Subnet6ConfigParser
  896. /// for each entry.
  897. class Subnets6ListConfigParser : public DhcpConfigParser {
  898. public:
  899. /// @brief constructor
  900. ///
  901. Subnets6ListConfigParser(const std::string&) {
  902. /// parameter name is ignored
  903. }
  904. /// @brief parses contents of the list
  905. ///
  906. /// Iterates over all entries on the list and creates Subnet6ConfigParser
  907. /// for each entry.
  908. ///
  909. /// @param subnets_list pointer to a list of IPv6 subnets
  910. void build(ConstElementPtr subnets_list) {
  911. // No need to define FactoryMap here. There's only one type
  912. // used: Subnet6ConfigParser
  913. BOOST_FOREACH(ConstElementPtr subnet, subnets_list->listValue()) {
  914. ParserPtr parser(new Subnet6ConfigParser("subnet"));
  915. parser->build(subnet);
  916. subnets_.push_back(parser);
  917. }
  918. }
  919. /// @brief commits subnets definitions.
  920. ///
  921. /// Iterates over all Subnet6 parsers. Each parser contains definitions
  922. /// of a single subnet and its parameters and commits each subnet separately.
  923. void commit() {
  924. // @todo: Implement more subtle reconfiguration than toss
  925. // the old one and replace with the new one.
  926. // remove old subnets
  927. CfgMgr::instance().deleteSubnets6();
  928. BOOST_FOREACH(ParserPtr subnet, subnets_) {
  929. subnet->commit();
  930. }
  931. }
  932. /// @brief Returns Subnet6ListConfigParser object
  933. /// @param param_name name of the parameter
  934. /// @return Subnets6ListConfigParser object
  935. static DhcpConfigParser* Factory(const std::string& param_name) {
  936. return (new Subnets6ListConfigParser(param_name));
  937. }
  938. /// @brief collection of subnet parsers.
  939. ParserCollection subnets_;
  940. };
  941. /// @brief creates global parsers
  942. ///
  943. /// This method creates global parsers that parse global parameters, i.e.
  944. /// those that take format of Dhcp6/param1, Dhcp6/param2 and so forth.
  945. ///
  946. /// @param config_id pointer to received global configuration entry
  947. /// @return parser for specified global DHCPv6 parameter
  948. DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
  949. FactoryMap factories;
  950. factories.insert(pair<string, ParserFactory*>(
  951. "preferred-lifetime", Uint32Parser::Factory));
  952. factories.insert(pair<string, ParserFactory*>(
  953. "valid-lifetime", Uint32Parser::Factory));
  954. factories.insert(pair<string, ParserFactory*>(
  955. "renew-timer", Uint32Parser::Factory));
  956. factories.insert(pair<string, ParserFactory*>(
  957. "rebind-timer", Uint32Parser::Factory));
  958. factories.insert(pair<string, ParserFactory*>(
  959. "interface", InterfaceListConfigParser::Factory));
  960. factories.insert(pair<string, ParserFactory*>(
  961. "subnet6", Subnets6ListConfigParser::Factory));
  962. factories.insert(pair<string, ParserFactory*>(
  963. "option-data", OptionDataListParser::Factory));
  964. factories.insert(pair<string, ParserFactory*>(
  965. "version", StringParser::Factory));
  966. FactoryMap::iterator f = factories.find(config_id);
  967. if (f == factories.end()) {
  968. // Used for debugging only.
  969. // return new DebugParser(config_id);
  970. isc_throw(NotImplemented,
  971. "Parser error: Global configuration parameter not supported: "
  972. << config_id);
  973. }
  974. return (f->second(config_id));
  975. }
  976. /// @brief configures DHCPv6 server
  977. ///
  978. /// This function is called every time a new configuration is received. The extra
  979. /// parameter is a reference to DHCPv6 server component. It is currently not used
  980. /// and CfgMgr::instance() is accessed instead.
  981. ///
  982. /// This method does not throw. It catches all exceptions and returns them as
  983. /// reconfiguration statuses. It may return the following response codes:
  984. /// 0 - configuration successful
  985. /// 1 - malformed configuration (parsing failed)
  986. /// 2 - logical error (parsing was successful, but the values are invalid)
  987. ///
  988. /// @param config_set a new configuration for DHCPv6 server
  989. /// @return answer that contains result of reconfiguration
  990. ConstElementPtr
  991. configureDhcp6Server(Dhcpv6Srv& , ConstElementPtr config_set) {
  992. if (!config_set) {
  993. isc_throw(Dhcp6ConfigError,
  994. "Null pointer is passed to configuration parser");
  995. }
  996. /// Reset global storage. Containers being reset below may contain
  997. /// data from the previous configuration attempts.
  998. option_defaults.clear();
  999. uint32_defaults.clear();
  1000. string_defaults.clear();
  1001. /// @todo: append most essential info here (like "2 new subnets configured")
  1002. string config_details;
  1003. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_START).arg(config_set->str());
  1004. ParserCollection parsers;
  1005. try {
  1006. BOOST_FOREACH(ConfigPair config_pair, config_set->mapValue()) {
  1007. ParserPtr parser(createGlobalDhcpConfigParser(config_pair.first));
  1008. parser->build(config_pair.second);
  1009. parsers.push_back(parser);
  1010. }
  1011. } catch (const isc::Exception& ex) {
  1012. ConstElementPtr answer = isc::config::createAnswer(1,
  1013. string("Configuration parsing failed:") + ex.what());
  1014. return (answer);
  1015. } catch (...) {
  1016. // for things like bad_cast in boost::lexical_cast
  1017. ConstElementPtr answer = isc::config::createAnswer(1,
  1018. string("Configuration parsing failed"));
  1019. }
  1020. try {
  1021. BOOST_FOREACH(ParserPtr parser, parsers) {
  1022. parser->commit();
  1023. }
  1024. }
  1025. catch (const isc::Exception& ex) {
  1026. ConstElementPtr answer = isc::config::createAnswer(2,
  1027. string("Configuration commit failed:") + ex.what());
  1028. return (answer);
  1029. } catch (...) {
  1030. // for things like bad_cast in boost::lexical_cast
  1031. ConstElementPtr answer = isc::config::createAnswer(2,
  1032. string("Configuration commit failed"));
  1033. }
  1034. LOG_INFO(dhcp6_logger, DHCP6_CONFIG_COMPLETE).arg(config_details);
  1035. ConstElementPtr answer = isc::config::createAnswer(0, "Configuration commited.");
  1036. return (answer);
  1037. }
  1038. }; // end of isc::dhcp namespace
  1039. }; // end of isc namespace