config_parser_unittest.cc 89 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491
  1. // Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <arpa/inet.h>
  16. #include <gtest/gtest.h>
  17. #include <config/ccsession.h>
  18. #include <dhcp4/dhcp4_srv.h>
  19. #include <dhcp4/config_parser.h>
  20. #include <dhcp/option4_addrlst.h>
  21. #include <dhcp/option_custom.h>
  22. #include <dhcp/option_int.h>
  23. #include <dhcp/docsis3_option_defs.h>
  24. #include <dhcpsrv/subnet.h>
  25. #include <dhcpsrv/cfgmgr.h>
  26. #include <hooks/hooks_manager.h>
  27. #include "marker_file.h"
  28. #include "test_libraries.h"
  29. #include <boost/foreach.hpp>
  30. #include <boost/scoped_ptr.hpp>
  31. #include <iostream>
  32. #include <fstream>
  33. #include <sstream>
  34. #include <limits.h>
  35. using namespace isc;
  36. using namespace isc::asiolink;
  37. using namespace isc::config;
  38. using namespace isc::data;
  39. using namespace isc::dhcp;
  40. using namespace isc::dhcp::test;
  41. using namespace isc::hooks;
  42. using namespace std;
  43. namespace {
  44. class Dhcp4ParserTest : public ::testing::Test {
  45. public:
  46. Dhcp4ParserTest()
  47. :rcode_(-1) {
  48. // Open port 0 means to not do anything at all. We don't want to
  49. // deal with sockets here, just check if configuration handling
  50. // is sane.
  51. srv_.reset(new Dhcpv4Srv(0));
  52. CfgMgr::instance().deleteActiveIfaces();
  53. }
  54. // Check that no hooks libraries are loaded. This is a pre-condition for
  55. // a number of tests, so is checked in one place. As this uses an
  56. // ASSERT call - and it is not clear from the documentation that Gtest
  57. // predicates can be used in a constructor - the check is placed in SetUp.
  58. void SetUp() {
  59. std::vector<std::string> libraries = HooksManager::getLibraryNames();
  60. ASSERT_TRUE(libraries.empty());
  61. }
  62. // Checks if global parameter of name have expected_value
  63. void checkGlobalUint32(string name, uint32_t expected_value) {
  64. const Uint32StoragePtr uint32_defaults =
  65. globalContext()->uint32_values_;
  66. try {
  67. uint32_t actual_value = uint32_defaults->getParam(name);
  68. EXPECT_EQ(expected_value, actual_value);
  69. } catch (DhcpConfigError) {
  70. ADD_FAILURE() << "Expected uint32 with name " << name
  71. << " not found";
  72. }
  73. }
  74. // Checks if the result of DHCP server configuration has
  75. // expected code (0 for success, other for failures).
  76. // Also stores result in rcode_ and comment_.
  77. void checkResult(ConstElementPtr status, int expected_code) {
  78. ASSERT_TRUE(status);
  79. comment_ = parseAnswer(rcode_, status);
  80. EXPECT_EQ(expected_code, rcode_);
  81. }
  82. ~Dhcp4ParserTest() {
  83. resetConfiguration();
  84. // ... and delete the hooks library marker files if present
  85. unlink(LOAD_MARKER_FILE);
  86. unlink(UNLOAD_MARKER_FILE);
  87. };
  88. /// @brief Create the simple configuration with single option.
  89. ///
  90. /// This function allows to set one of the parameters that configure
  91. /// option value. These parameters are: "name", "code", "data",
  92. /// "csv-format" and "space".
  93. ///
  94. /// @param param_value string holding option parameter value to be
  95. /// injected into the configuration string.
  96. /// @param parameter name of the parameter to be configured with
  97. /// param value.
  98. /// @return configuration string containing custom values of parameters
  99. /// describing an option.
  100. std::string createConfigWithOption(const std::string& param_value,
  101. const std::string& parameter) {
  102. std::map<std::string, std::string> params;
  103. if (parameter == "name") {
  104. params["name"] = param_value;
  105. params["space"] = "dhcp4";
  106. params["code"] = "56";
  107. params["data"] = "AB CDEF0105";
  108. params["csv-format"] = "False";
  109. } else if (parameter == "space") {
  110. params["name"] = "dhcp-message";
  111. params["space"] = param_value;
  112. params["code"] = "56";
  113. params["data"] = "AB CDEF0105";
  114. params["csv-format"] = "False";
  115. } else if (parameter == "code") {
  116. params["name"] = "dhcp-message";
  117. params["space"] = "dhcp4";
  118. params["code"] = param_value;
  119. params["data"] = "AB CDEF0105";
  120. params["csv-format"] = "False";
  121. } else if (parameter == "data") {
  122. params["name"] = "dhcp-message";
  123. params["space"] = "dhcp4";
  124. params["code"] = "56";
  125. params["data"] = param_value;
  126. params["csv-format"] = "False";
  127. } else if (parameter == "csv-format") {
  128. params["name"] = "dhcp-message";
  129. params["space"] = "dhcp4";
  130. params["code"] = "56";
  131. params["data"] = "AB CDEF0105";
  132. params["csv-format"] = param_value;
  133. }
  134. return (createConfigWithOption(params));
  135. }
  136. /// @brief Create simple configuration with single option.
  137. ///
  138. /// This function creates a configuration for a single option with
  139. /// custom values for all parameters that describe the option.
  140. ///
  141. /// @params params map holding parameters and their values.
  142. /// @return configuration string containing custom values of parameters
  143. /// describing an option.
  144. std::string createConfigWithOption(const std::map<std::string, std::string>& params) {
  145. std::ostringstream stream;
  146. stream << "{ \"interfaces\": [ \"*\" ],"
  147. "\"rebind-timer\": 2000, "
  148. "\"renew-timer\": 1000, "
  149. "\"subnet4\": [ { "
  150. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  151. " \"subnet\": \"192.0.2.0/24\", "
  152. " \"option-data\": [ {";
  153. bool first = true;
  154. typedef std::pair<std::string, std::string> ParamPair;
  155. BOOST_FOREACH(ParamPair param, params) {
  156. if (!first) {
  157. stream << ", ";
  158. } else {
  159. // cppcheck-suppress unreadVariable
  160. first = false;
  161. }
  162. if (param.first == "name") {
  163. stream << "\"name\": \"" << param.second << "\"";
  164. } else if (param.first == "space") {
  165. stream << "\"space\": \"" << param.second << "\"";
  166. } else if (param.first == "code") {
  167. stream << "\"code\": " << param.second << "";
  168. } else if (param.first == "data") {
  169. stream << "\"data\": \"" << param.second << "\"";
  170. } else if (param.first == "csv-format") {
  171. stream << "\"csv-format\": " << param.second;
  172. }
  173. }
  174. stream <<
  175. " } ]"
  176. " } ],"
  177. "\"valid-lifetime\": 4000 }";
  178. return (stream.str());
  179. }
  180. /// @brief Test invalid option parameter value.
  181. ///
  182. /// This test function constructs the simple configuration
  183. /// string and injects invalid option configuration into it.
  184. /// It expects that parser will fail with provided option code.
  185. ///
  186. /// @param param_value string holding invalid option parameter value
  187. /// to be injected into configuration string.
  188. /// @param parameter name of the parameter to be configured with
  189. /// param_value (can be any of "name", "code", "data")
  190. void testInvalidOptionParam(const std::string& param_value,
  191. const std::string& parameter) {
  192. ConstElementPtr x;
  193. std::string config = createConfigWithOption(param_value, parameter);
  194. ElementPtr json = Element::fromJSON(config);
  195. EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
  196. ASSERT_TRUE(x);
  197. comment_ = parseAnswer(rcode_, x);
  198. ASSERT_EQ(1, rcode_);
  199. }
  200. /// @brief Test option against given code and data.
  201. ///
  202. /// @param option_desc option descriptor that carries the option to
  203. /// be tested.
  204. /// @param expected_code expected code of the option.
  205. /// @param expected_data expected data in the option.
  206. /// @param expected_data_len length of the reference data.
  207. /// @param extra_data if true extra data is allowed in an option
  208. /// after tested data.
  209. void testOption(const Subnet::OptionDescriptor& option_desc,
  210. uint16_t expected_code, const uint8_t* expected_data,
  211. size_t expected_data_len,
  212. bool extra_data = false) {
  213. // Check if option descriptor contains valid option pointer.
  214. ASSERT_TRUE(option_desc.option);
  215. // Verify option type.
  216. EXPECT_EQ(expected_code, option_desc.option->getType());
  217. // We may have many different option types being created. Some of them
  218. // have dedicated classes derived from Option class. In such case if
  219. // we want to verify the option contents against expected_data we have
  220. // to prepare raw buffer with the contents of the option. The easiest
  221. // way is to call pack() which will prepare on-wire data.
  222. util::OutputBuffer buf(option_desc.option->getData().size());
  223. option_desc.option->pack(buf);
  224. if (extra_data) {
  225. // The length of the buffer must be at least equal to size of the
  226. // reference data but it can sometimes be greater than that. This is
  227. // because some options carry suboptions that increase the overall
  228. // length.
  229. ASSERT_GE(buf.getLength() - option_desc.option->getHeaderLen(),
  230. expected_data_len);
  231. } else {
  232. ASSERT_EQ(buf.getLength() - option_desc.option->getHeaderLen(),
  233. expected_data_len);
  234. }
  235. // Verify that the data is correct. Do not verify suboptions and a header.
  236. const uint8_t* data = static_cast<const uint8_t*>(buf.getData());
  237. EXPECT_EQ(0, memcmp(expected_data, data + option_desc.option->getHeaderLen(),
  238. expected_data_len));
  239. }
  240. /// @brief Parse and Execute configuration
  241. ///
  242. /// Parses a configuration and executes a configuration of the server.
  243. /// If the operation fails, the current test will register a failure.
  244. ///
  245. /// @param config Configuration to parse
  246. /// @param operation Operation being performed. In the case of an error,
  247. /// the error text will include the string "unable to <operation>.".
  248. ///
  249. /// @return true if the configuration succeeded, false if not. In the
  250. /// latter case, a failure will have been added to the current test.
  251. bool
  252. executeConfiguration(const std::string& config, const char* operation) {
  253. ConstElementPtr status;
  254. try {
  255. ElementPtr json = Element::fromJSON(config);
  256. status = configureDhcp4Server(*srv_, json);
  257. } catch (const std::exception& ex) {
  258. ADD_FAILURE() << "Unable to " << operation << ". "
  259. << "The following configuration was used: " << std::endl
  260. << config << std::endl
  261. << " and the following error message was returned:"
  262. << ex.what() << std::endl;
  263. return (false);
  264. }
  265. // The status object must not be NULL
  266. if (!status) {
  267. ADD_FAILURE() << "Unable to " << operation << ". "
  268. << "The configuration function returned a null pointer.";
  269. return (false);
  270. }
  271. // Store the answer if we need it.
  272. // Returned value should be 0 (configuration success)
  273. comment_ = parseAnswer(rcode_, status);
  274. if (rcode_ != 0) {
  275. string reason = "";
  276. if (comment_) {
  277. reason = string(" (") + comment_->stringValue() + string(")");
  278. }
  279. ADD_FAILURE() << "Unable to " << operation << ". "
  280. << "The configuration function returned error code "
  281. << rcode_ << reason;
  282. return (false);
  283. }
  284. return (true);
  285. }
  286. /// @brief Reset configuration database.
  287. ///
  288. /// This function resets configuration data base by
  289. /// removing all subnets and option-data. Reset must
  290. /// be performed after each test to make sure that
  291. /// contents of the database do not affect result of
  292. /// subsequent tests.
  293. void resetConfiguration() {
  294. string config = "{ \"interfaces\": [ \"*\" ],"
  295. "\"hooks-libraries\": [ ], "
  296. "\"rebind-timer\": 2000, "
  297. "\"renew-timer\": 1000, "
  298. "\"valid-lifetime\": 4000, "
  299. "\"subnet4\": [ ], "
  300. "\"option-def\": [ ], "
  301. "\"option-data\": [ ] }";
  302. static_cast<void>(executeConfiguration(config,
  303. "reset configuration database"));
  304. }
  305. boost::scoped_ptr<Dhcpv4Srv> srv_; // DHCP4 server under test
  306. int rcode_; // Return code from element parsing
  307. ConstElementPtr comment_; // Reason for parse fail
  308. };
  309. // Goal of this test is a verification if a very simple config update
  310. // with just a bumped version number. That's the simplest possible
  311. // config update.
  312. TEST_F(Dhcp4ParserTest, version) {
  313. ConstElementPtr x;
  314. EXPECT_NO_THROW(x = configureDhcp4Server(*srv_,
  315. Element::fromJSON("{\"version\": 0}")));
  316. // returned value must be 0 (configuration accepted)
  317. checkResult(x, 0);
  318. }
  319. /// The goal of this test is to verify that the code accepts only
  320. /// valid commands and malformed or unsupported parameters are rejected.
  321. TEST_F(Dhcp4ParserTest, bogusCommand) {
  322. ConstElementPtr x;
  323. EXPECT_NO_THROW(x = configureDhcp4Server(*srv_,
  324. Element::fromJSON("{\"bogus\": 5}")));
  325. // returned value must be 1 (configuration parse error)
  326. checkResult(x, 1);
  327. }
  328. /// The goal of this test is to verify if wrongly defined subnet will
  329. /// be rejected. Properly defined subnet must include at least one
  330. /// pool definition.
  331. TEST_F(Dhcp4ParserTest, emptySubnet) {
  332. ConstElementPtr status;
  333. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
  334. Element::fromJSON("{ \"interfaces\": [ \"*\" ],"
  335. "\"rebind-timer\": 2000, "
  336. "\"renew-timer\": 1000, "
  337. "\"subnet4\": [ ], "
  338. "\"valid-lifetime\": 4000 }")));
  339. // returned value should be 0 (success)
  340. checkResult(status, 0);
  341. checkGlobalUint32("rebind-timer", 2000);
  342. checkGlobalUint32("renew-timer", 1000);
  343. checkGlobalUint32("valid-lifetime", 4000);
  344. }
  345. /// The goal of this test is to verify if defined subnet uses global
  346. /// parameter timer definitions.
  347. TEST_F(Dhcp4ParserTest, subnetGlobalDefaults) {
  348. ConstElementPtr status;
  349. string config = "{ \"interfaces\": [ \"*\" ],"
  350. "\"rebind-timer\": 2000, "
  351. "\"renew-timer\": 1000, "
  352. "\"subnet4\": [ { "
  353. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  354. " \"subnet\": \"192.0.2.0/24\" } ],"
  355. "\"valid-lifetime\": 4000 }";
  356. ElementPtr json = Element::fromJSON(config);
  357. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  358. // check if returned status is OK
  359. checkResult(status, 0);
  360. // Now check if the configuration was indeed handled and we have
  361. // expected pool configured.
  362. Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
  363. ASSERT_TRUE(subnet);
  364. EXPECT_EQ(1000, subnet->getT1());
  365. EXPECT_EQ(2000, subnet->getT2());
  366. EXPECT_EQ(4000, subnet->getValid());
  367. // Check that subnet-id is 1
  368. EXPECT_EQ(1, subnet->getID());
  369. }
  370. // Goal of this test is to verify that multiple subnets get unique
  371. // subnet-ids. Also, test checks that it's possible to do reconfiguration
  372. // multiple times.
  373. TEST_F(Dhcp4ParserTest, multipleSubnets) {
  374. ConstElementPtr x;
  375. string config = "{ \"interfaces\": [ \"*\" ],"
  376. "\"rebind-timer\": 2000, "
  377. "\"renew-timer\": 1000, "
  378. "\"subnet4\": [ { "
  379. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  380. " \"subnet\": \"192.0.2.0/24\" "
  381. " },"
  382. " {"
  383. " \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
  384. " \"subnet\": \"192.0.3.0/24\" "
  385. " },"
  386. " {"
  387. " \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
  388. " \"subnet\": \"192.0.4.0/24\" "
  389. " },"
  390. " {"
  391. " \"pool\": [ \"192.0.5.101 - 192.0.5.150\" ],"
  392. " \"subnet\": \"192.0.5.0/24\" "
  393. " } ],"
  394. "\"valid-lifetime\": 4000 }";
  395. ElementPtr json = Element::fromJSON(config);
  396. EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
  397. ASSERT_TRUE(x);
  398. comment_ = parseAnswer(rcode_, x);
  399. ASSERT_EQ(0, rcode_);
  400. int cnt = 0; // Number of reconfigurations
  401. do {
  402. ElementPtr json = Element::fromJSON(config);
  403. EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
  404. ASSERT_TRUE(x);
  405. comment_ = parseAnswer(rcode_, x);
  406. ASSERT_EQ(0, rcode_);
  407. const Subnet4Collection* subnets = CfgMgr::instance().getSubnets4();
  408. ASSERT_TRUE(subnets);
  409. ASSERT_EQ(4, subnets->size()); // We expect 4 subnets
  410. // Check subnet-ids of each subnet (it should be monotonously increasing)
  411. EXPECT_EQ(1, subnets->at(0)->getID());
  412. EXPECT_EQ(2, subnets->at(1)->getID());
  413. EXPECT_EQ(3, subnets->at(2)->getID());
  414. EXPECT_EQ(4, subnets->at(3)->getID());
  415. // Repeat reconfiguration process 10 times and check that the subnet-id
  416. // is set to the same value. Technically, just two iterations would be
  417. // sufficient, but it's nice to have a test that exercises reconfiguration
  418. // a bit.
  419. } while (++cnt < 10);
  420. }
  421. // Goal of this test is to verify that a previously configured subnet can be
  422. // deleted in subsequent reconfiguration.
  423. TEST_F(Dhcp4ParserTest, reconfigureRemoveSubnet) {
  424. ConstElementPtr x;
  425. // All four subnets
  426. string config4 = "{ \"interfaces\": [ \"*\" ],"
  427. "\"rebind-timer\": 2000, "
  428. "\"renew-timer\": 1000, "
  429. "\"subnet4\": [ { "
  430. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  431. " \"subnet\": \"192.0.2.0/24\" "
  432. " },"
  433. " {"
  434. " \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
  435. " \"subnet\": \"192.0.3.0/24\" "
  436. " },"
  437. " {"
  438. " \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
  439. " \"subnet\": \"192.0.4.0/24\" "
  440. " },"
  441. " {"
  442. " \"pool\": [ \"192.0.5.101 - 192.0.5.150\" ],"
  443. " \"subnet\": \"192.0.5.0/24\" "
  444. " } ],"
  445. "\"valid-lifetime\": 4000 }";
  446. // Three subnets (the last one removed)
  447. string config_first3 = "{ \"interfaces\": [ \"*\" ],"
  448. "\"rebind-timer\": 2000, "
  449. "\"renew-timer\": 1000, "
  450. "\"subnet4\": [ { "
  451. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  452. " \"subnet\": \"192.0.2.0/24\" "
  453. " },"
  454. " {"
  455. " \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
  456. " \"subnet\": \"192.0.3.0/24\" "
  457. " },"
  458. " {"
  459. " \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
  460. " \"subnet\": \"192.0.4.0/24\" "
  461. " } ],"
  462. "\"valid-lifetime\": 4000 }";
  463. // Second subnet removed
  464. string config_second_removed = "{ \"interfaces\": [ \"*\" ],"
  465. "\"rebind-timer\": 2000, "
  466. "\"renew-timer\": 1000, "
  467. "\"subnet4\": [ { "
  468. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  469. " \"subnet\": \"192.0.2.0/24\" "
  470. " },"
  471. " {"
  472. " \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
  473. " \"subnet\": \"192.0.4.0/24\" "
  474. " },"
  475. " {"
  476. " \"pool\": [ \"192.0.5.101 - 192.0.5.150\" ],"
  477. " \"subnet\": \"192.0.5.0/24\" "
  478. " } ],"
  479. "\"valid-lifetime\": 4000 }";
  480. // CASE 1: Configure 4 subnets, then reconfigure and remove the
  481. // last one.
  482. ElementPtr json = Element::fromJSON(config4);
  483. EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
  484. ASSERT_TRUE(x);
  485. comment_ = parseAnswer(rcode_, x);
  486. ASSERT_EQ(0, rcode_);
  487. const Subnet4Collection* subnets = CfgMgr::instance().getSubnets4();
  488. ASSERT_TRUE(subnets);
  489. ASSERT_EQ(4, subnets->size()); // We expect 4 subnets
  490. // Do the reconfiguration (the last subnet is removed)
  491. json = Element::fromJSON(config_first3);
  492. EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
  493. ASSERT_TRUE(x);
  494. comment_ = parseAnswer(rcode_, x);
  495. ASSERT_EQ(0, rcode_);
  496. subnets = CfgMgr::instance().getSubnets4();
  497. ASSERT_TRUE(subnets);
  498. ASSERT_EQ(3, subnets->size()); // We expect 3 subnets now (4th is removed)
  499. // Check subnet-ids of each subnet (it should be monotonously increasing)
  500. EXPECT_EQ(1, subnets->at(0)->getID());
  501. EXPECT_EQ(2, subnets->at(1)->getID());
  502. EXPECT_EQ(3, subnets->at(2)->getID());
  503. /// CASE 2: Configure 4 subnets, then reconfigure and remove one
  504. /// from in between (not first, not last)
  505. #if 0
  506. /// @todo: Uncomment subnet removal test as part of #3281.
  507. json = Element::fromJSON(config4);
  508. EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
  509. ASSERT_TRUE(x);
  510. comment_ = parseAnswer(rcode_, x);
  511. ASSERT_EQ(0, rcode_);
  512. // Do reconfiguration
  513. json = Element::fromJSON(config_second_removed);
  514. EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
  515. ASSERT_TRUE(x);
  516. comment_ = parseAnswer(rcode_, x);
  517. ASSERT_EQ(0, rcode_);
  518. subnets = CfgMgr::instance().getSubnets4();
  519. ASSERT_TRUE(subnets);
  520. ASSERT_EQ(3, subnets->size()); // We expect 4 subnets
  521. EXPECT_EQ(1, subnets->at(0)->getID());
  522. // The second subnet (with subnet-id = 2) is no longer there
  523. EXPECT_EQ(3, subnets->at(1)->getID());
  524. EXPECT_EQ(4, subnets->at(2)->getID());
  525. #endif
  526. }
  527. /// @todo: implement subnet removal test as part of #3281.
  528. // Checks if the next-server defined as global parameter is taken into
  529. // consideration.
  530. TEST_F(Dhcp4ParserTest, nextServerGlobal) {
  531. ConstElementPtr status;
  532. string config = "{ \"interfaces\": [ \"*\" ],"
  533. "\"rebind-timer\": 2000, "
  534. "\"renew-timer\": 1000, "
  535. "\"next-server\": \"1.2.3.4\", "
  536. "\"subnet4\": [ { "
  537. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  538. " \"subnet\": \"192.0.2.0/24\" } ],"
  539. "\"valid-lifetime\": 4000 }";
  540. ElementPtr json = Element::fromJSON(config);
  541. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  542. // check if returned status is OK
  543. checkResult(status, 0);
  544. // Now check if the configuration was indeed handled and we have
  545. // expected pool configured.
  546. Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
  547. ASSERT_TRUE(subnet);
  548. EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText());
  549. }
  550. // Checks if the next-server defined as subnet parameter is taken into
  551. // consideration.
  552. TEST_F(Dhcp4ParserTest, nextServerSubnet) {
  553. ConstElementPtr status;
  554. string config = "{ \"interfaces\": [ \"*\" ],"
  555. "\"rebind-timer\": 2000, "
  556. "\"renew-timer\": 1000, "
  557. "\"subnet4\": [ { "
  558. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  559. " \"next-server\": \"1.2.3.4\", "
  560. " \"subnet\": \"192.0.2.0/24\" } ],"
  561. "\"valid-lifetime\": 4000 }";
  562. ElementPtr json = Element::fromJSON(config);
  563. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  564. // check if returned status is OK
  565. checkResult(status, 0);
  566. // Now check if the configuration was indeed handled and we have
  567. // expected pool configured.
  568. Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
  569. ASSERT_TRUE(subnet);
  570. EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText());
  571. }
  572. // Test checks several negative scenarios for next-server configuration: bogus
  573. // address, IPv6 adddress and empty string.
  574. TEST_F(Dhcp4ParserTest, nextServerNegative) {
  575. ConstElementPtr status;
  576. // Config with junk instead of next-server address
  577. string config_bogus1 = "{ \"interfaces\": [ \"*\" ],"
  578. "\"rebind-timer\": 2000, "
  579. "\"renew-timer\": 1000, "
  580. "\"subnet4\": [ { "
  581. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  582. " \"rebind-timer\": 2000, "
  583. " \"renew-timer\": 1000, "
  584. " \"next-server\": \"a.b.c.d\", "
  585. " \"subnet\": \"192.0.2.0/24\" } ],"
  586. "\"valid-lifetime\": 4000 }";
  587. // Config with IPv6 next server address
  588. string config_bogus2 = "{ \"interfaces\": [ \"*\" ],"
  589. "\"rebind-timer\": 2000, "
  590. "\"renew-timer\": 1000, "
  591. "\"subnet4\": [ { "
  592. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  593. " \"rebind-timer\": 2000, "
  594. " \"renew-timer\": 1000, "
  595. " \"next-server\": \"2001:db8::1\", "
  596. " \"subnet\": \"192.0.2.0/24\" } ],"
  597. "\"valid-lifetime\": 4000 }";
  598. // Config with empty next server address
  599. string config_bogus3 = "{ \"interfaces\": [ \"*\" ],"
  600. "\"rebind-timer\": 2000, "
  601. "\"renew-timer\": 1000, "
  602. "\"subnet4\": [ { "
  603. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  604. " \"rebind-timer\": 2000, "
  605. " \"renew-timer\": 1000, "
  606. " \"next-server\": \"\", "
  607. " \"subnet\": \"192.0.2.0/24\" } ],"
  608. "\"valid-lifetime\": 4000 }";
  609. ElementPtr json1 = Element::fromJSON(config_bogus1);
  610. ElementPtr json2 = Element::fromJSON(config_bogus2);
  611. ElementPtr json3 = Element::fromJSON(config_bogus3);
  612. // check if returned status is always a failure
  613. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json1));
  614. checkResult(status, 1);
  615. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json2));
  616. checkResult(status, 1);
  617. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json3));
  618. checkResult(status, 0);
  619. }
  620. // Checks if the next-server defined as global value is overridden by subnet
  621. // specific value.
  622. TEST_F(Dhcp4ParserTest, nextServerOverride) {
  623. ConstElementPtr status;
  624. string config = "{ \"interfaces\": [ \"*\" ],"
  625. "\"rebind-timer\": 2000, "
  626. "\"renew-timer\": 1000, "
  627. "\"next-server\": \"192.0.0.1\", "
  628. "\"subnet4\": [ { "
  629. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  630. " \"next-server\": \"1.2.3.4\", "
  631. " \"subnet\": \"192.0.2.0/24\" } ],"
  632. "\"valid-lifetime\": 4000 }";
  633. ElementPtr json = Element::fromJSON(config);
  634. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  635. // check if returned status is OK
  636. checkResult(status, 0);
  637. // Now check if the configuration was indeed handled and we have
  638. // expected pool configured.
  639. Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
  640. ASSERT_TRUE(subnet);
  641. EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText());
  642. }
  643. // Check whether it is possible to configure echo-client-id
  644. TEST_F(Dhcp4ParserTest, echoClientId) {
  645. ConstElementPtr status;
  646. string config_false = "{ \"interfaces\": [ \"*\" ],"
  647. "\"rebind-timer\": 2000, "
  648. "\"renew-timer\": 1000, "
  649. "\"echo-client-id\": false,"
  650. "\"subnet4\": [ { "
  651. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  652. " \"subnet\": \"192.0.2.0/24\" } ],"
  653. "\"valid-lifetime\": 4000 }";
  654. string config_true = "{ \"interfaces\": [ \"*\" ],"
  655. "\"rebind-timer\": 2000, "
  656. "\"renew-timer\": 1000, "
  657. "\"echo-client-id\": true,"
  658. "\"subnet4\": [ { "
  659. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  660. " \"subnet\": \"192.0.2.0/24\" } ],"
  661. "\"valid-lifetime\": 4000 }";
  662. ElementPtr json_false = Element::fromJSON(config_false);
  663. ElementPtr json_true = Element::fromJSON(config_true);
  664. // Let's check the default. It should be true
  665. ASSERT_TRUE(CfgMgr::instance().echoClientId());
  666. // Now check that "false" configuration is really applied.
  667. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json_false));
  668. ASSERT_FALSE(CfgMgr::instance().echoClientId());
  669. // Now check that "true" configuration is really applied.
  670. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json_true));
  671. ASSERT_TRUE(CfgMgr::instance().echoClientId());
  672. // In any case revert back to the default value (true)
  673. CfgMgr::instance().echoClientId(true);
  674. }
  675. // This test checks if it is possible to override global values
  676. // on a per subnet basis.
  677. TEST_F(Dhcp4ParserTest, subnetLocal) {
  678. ConstElementPtr status;
  679. string config = "{ \"interfaces\": [ \"*\" ],"
  680. "\"rebind-timer\": 2000, "
  681. "\"renew-timer\": 1000, "
  682. "\"subnet4\": [ { "
  683. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  684. " \"renew-timer\": 1, "
  685. " \"rebind-timer\": 2, "
  686. " \"valid-lifetime\": 4,"
  687. " \"subnet\": \"192.0.2.0/24\" } ],"
  688. "\"valid-lifetime\": 4000 }";
  689. ElementPtr json = Element::fromJSON(config);
  690. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  691. // returned value should be 0 (configuration success)
  692. checkResult(status, 0);
  693. Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
  694. ASSERT_TRUE(subnet);
  695. EXPECT_EQ(1, subnet->getT1());
  696. EXPECT_EQ(2, subnet->getT2());
  697. EXPECT_EQ(4, subnet->getValid());
  698. }
  699. // Test verifies that a subnet with pool values that do not belong to that
  700. // pool are rejected.
  701. TEST_F(Dhcp4ParserTest, poolOutOfSubnet) {
  702. ConstElementPtr status;
  703. string config = "{ \"interfaces\": [ \"*\" ],"
  704. "\"rebind-timer\": 2000, "
  705. "\"renew-timer\": 1000, "
  706. "\"subnet4\": [ { "
  707. " \"pool\": [ \"192.0.4.0/28\" ],"
  708. " \"subnet\": \"192.0.2.0/24\" } ],"
  709. "\"valid-lifetime\": 4000 }";
  710. ElementPtr json = Element::fromJSON(config);
  711. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  712. // returned value must be 1 (values error)
  713. // as the pool does not belong to that subnet
  714. checkResult(status, 1);
  715. }
  716. // Goal of this test is to verify if pools can be defined
  717. // using prefix/length notation. There is no separate test for min-max
  718. // notation as it was tested in several previous tests.
  719. TEST_F(Dhcp4ParserTest, poolPrefixLen) {
  720. ConstElementPtr status;
  721. string config = "{ \"interfaces\": [ \"*\" ],"
  722. "\"rebind-timer\": 2000, "
  723. "\"renew-timer\": 1000, "
  724. "\"subnet4\": [ { "
  725. " \"pool\": [ \"192.0.2.128/28\" ],"
  726. " \"subnet\": \"192.0.2.0/24\" } ],"
  727. "\"valid-lifetime\": 4000 }";
  728. ElementPtr json = Element::fromJSON(config);
  729. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  730. // returned value must be 0 (configuration accepted)
  731. checkResult(status, 0);
  732. Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
  733. ASSERT_TRUE(subnet);
  734. EXPECT_EQ(1000, subnet->getT1());
  735. EXPECT_EQ(2000, subnet->getT2());
  736. EXPECT_EQ(4000, subnet->getValid());
  737. }
  738. // The goal of this test is to check whether an option definition
  739. // that defines an option carrying an IPv4 address can be created.
  740. TEST_F(Dhcp4ParserTest, optionDefIpv4Address) {
  741. // Configuration string.
  742. std::string config =
  743. "{ \"option-def\": [ {"
  744. " \"name\": \"foo\","
  745. " \"code\": 100,"
  746. " \"type\": \"ipv4-address\","
  747. " \"array\": False,"
  748. " \"record-types\": \"\","
  749. " \"space\": \"isc\","
  750. " \"encapsulate\": \"\""
  751. " } ]"
  752. "}";
  753. ElementPtr json = Element::fromJSON(config);
  754. // Make sure that the particular option definition does not exist.
  755. OptionDefinitionPtr def = CfgMgr::instance().getOptionDef("isc", 100);
  756. ASSERT_FALSE(def);
  757. // Use the configuration string to create new option definition.
  758. ConstElementPtr status;
  759. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  760. ASSERT_TRUE(status);
  761. checkResult(status, 0);
  762. // The option definition should now be available in the CfgMgr.
  763. def = CfgMgr::instance().getOptionDef("isc", 100);
  764. ASSERT_TRUE(def);
  765. // Verify that the option definition data is valid.
  766. EXPECT_EQ("foo", def->getName());
  767. EXPECT_EQ(100, def->getCode());
  768. EXPECT_FALSE(def->getArrayType());
  769. EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType());
  770. EXPECT_TRUE(def->getEncapsulatedSpace().empty());
  771. }
  772. // The goal of this test is to check whether an option definition
  773. // that defines an option carrying a record of data fields can
  774. // be created.
  775. TEST_F(Dhcp4ParserTest, optionDefRecord) {
  776. // Configuration string.
  777. std::string config =
  778. "{ \"option-def\": [ {"
  779. " \"name\": \"foo\","
  780. " \"code\": 100,"
  781. " \"type\": \"record\","
  782. " \"array\": False,"
  783. " \"record-types\": \"uint16, ipv4-address, ipv6-address, string\","
  784. " \"space\": \"isc\","
  785. " \"encapsulate\": \"\""
  786. " } ]"
  787. "}";
  788. ElementPtr json = Element::fromJSON(config);
  789. // Make sure that the particular option definition does not exist.
  790. OptionDefinitionPtr def = CfgMgr::instance().getOptionDef("isc", 100);
  791. ASSERT_FALSE(def);
  792. // Use the configuration string to create new option definition.
  793. ConstElementPtr status;
  794. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  795. ASSERT_TRUE(status);
  796. checkResult(status, 0);
  797. // The option definition should now be available in the CfgMgr.
  798. def = CfgMgr::instance().getOptionDef("isc", 100);
  799. ASSERT_TRUE(def);
  800. // Check the option data.
  801. EXPECT_EQ("foo", def->getName());
  802. EXPECT_EQ(100, def->getCode());
  803. EXPECT_EQ(OPT_RECORD_TYPE, def->getType());
  804. EXPECT_FALSE(def->getArrayType());
  805. EXPECT_TRUE(def->getEncapsulatedSpace().empty());
  806. // The option comprises the record of data fields. Verify that all
  807. // fields are present and they are of the expected types.
  808. const OptionDefinition::RecordFieldsCollection& record_fields =
  809. def->getRecordFields();
  810. ASSERT_EQ(4, record_fields.size());
  811. EXPECT_EQ(OPT_UINT16_TYPE, record_fields[0]);
  812. EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, record_fields[1]);
  813. EXPECT_EQ(OPT_IPV6_ADDRESS_TYPE, record_fields[2]);
  814. EXPECT_EQ(OPT_STRING_TYPE, record_fields[3]);
  815. }
  816. // The goal of this test is to verify that multiple option definitions
  817. // can be created.
  818. TEST_F(Dhcp4ParserTest, optionDefMultiple) {
  819. // Configuration string.
  820. std::string config =
  821. "{ \"option-def\": [ {"
  822. " \"name\": \"foo\","
  823. " \"code\": 100,"
  824. " \"type\": \"uint32\","
  825. " \"array\": False,"
  826. " \"record-types\": \"\","
  827. " \"space\": \"isc\","
  828. " \"encapsulate\": \"\""
  829. " },"
  830. " {"
  831. " \"name\": \"foo-2\","
  832. " \"code\": 101,"
  833. " \"type\": \"ipv4-address\","
  834. " \"array\": False,"
  835. " \"record-types\": \"\","
  836. " \"space\": \"isc\","
  837. " \"encapsulate\": \"\""
  838. " } ]"
  839. "}";
  840. ElementPtr json = Element::fromJSON(config);
  841. // Make sure that the option definitions do not exist yet.
  842. ASSERT_FALSE(CfgMgr::instance().getOptionDef("isc", 100));
  843. ASSERT_FALSE(CfgMgr::instance().getOptionDef("isc", 101));
  844. // Use the configuration string to create new option definitions.
  845. ConstElementPtr status;
  846. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  847. ASSERT_TRUE(status);
  848. checkResult(status, 0);
  849. // Check the first definition we have created.
  850. OptionDefinitionPtr def1 = CfgMgr::instance().getOptionDef("isc", 100);
  851. ASSERT_TRUE(def1);
  852. // Check the option data.
  853. EXPECT_EQ("foo", def1->getName());
  854. EXPECT_EQ(100, def1->getCode());
  855. EXPECT_EQ(OPT_UINT32_TYPE, def1->getType());
  856. EXPECT_FALSE(def1->getArrayType());
  857. EXPECT_TRUE(def1->getEncapsulatedSpace().empty());
  858. // Check the second option definition we have created.
  859. OptionDefinitionPtr def2 = CfgMgr::instance().getOptionDef("isc", 101);
  860. ASSERT_TRUE(def2);
  861. // Check the option data.
  862. EXPECT_EQ("foo-2", def2->getName());
  863. EXPECT_EQ(101, def2->getCode());
  864. EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def2->getType());
  865. EXPECT_FALSE(def2->getArrayType());
  866. EXPECT_TRUE(def2->getEncapsulatedSpace().empty());
  867. }
  868. // The goal of this test is to verify that the duplicated option
  869. // definition is not accepted.
  870. TEST_F(Dhcp4ParserTest, optionDefDuplicate) {
  871. // Configuration string. Both option definitions have
  872. // the same code and belong to the same option space.
  873. // This configuration should not be accepted.
  874. std::string config =
  875. "{ \"option-def\": [ {"
  876. " \"name\": \"foo\","
  877. " \"code\": 100,"
  878. " \"type\": \"uint32\","
  879. " \"array\": False,"
  880. " \"record-types\": \"\","
  881. " \"space\": \"isc\","
  882. " \"encapsulate\": \"\""
  883. " },"
  884. " {"
  885. " \"name\": \"foo-2\","
  886. " \"code\": 100,"
  887. " \"type\": \"ipv4-address\","
  888. " \"array\": False,"
  889. " \"record-types\": \"\","
  890. " \"space\": \"isc\","
  891. " \"encapsulate\": \"\""
  892. " } ]"
  893. "}";
  894. ElementPtr json = Element::fromJSON(config);
  895. // Make sure that the option definition does not exist yet.
  896. ASSERT_FALSE(CfgMgr::instance().getOptionDef("isc", 100));
  897. // Use the configuration string to create new option definitions.
  898. ConstElementPtr status;
  899. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  900. ASSERT_TRUE(status);
  901. checkResult(status, 1);
  902. }
  903. // The goal of this test is to verify that the option definition
  904. // comprising an array of uint32 values can be created.
  905. TEST_F(Dhcp4ParserTest, optionDefArray) {
  906. // Configuration string. Created option definition should
  907. // comprise an array of uint32 values.
  908. std::string config =
  909. "{ \"option-def\": [ {"
  910. " \"name\": \"foo\","
  911. " \"code\": 100,"
  912. " \"type\": \"uint32\","
  913. " \"array\": True,"
  914. " \"record-types\": \"\","
  915. " \"space\": \"isc\","
  916. " \"encapsulate\": \"\""
  917. " } ]"
  918. "}";
  919. ElementPtr json = Element::fromJSON(config);
  920. // Make sure that the particular option definition does not exist.
  921. OptionDefinitionPtr def = CfgMgr::instance().getOptionDef("isc", 100);
  922. ASSERT_FALSE(def);
  923. // Use the configuration string to create new option definition.
  924. ConstElementPtr status;
  925. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  926. ASSERT_TRUE(status);
  927. checkResult(status, 0);
  928. // The option definition should now be available in the CfgMgr.
  929. def = CfgMgr::instance().getOptionDef("isc", 100);
  930. ASSERT_TRUE(def);
  931. // Check the option data.
  932. EXPECT_EQ("foo", def->getName());
  933. EXPECT_EQ(100, def->getCode());
  934. EXPECT_EQ(OPT_UINT32_TYPE, def->getType());
  935. EXPECT_TRUE(def->getArrayType());
  936. EXPECT_TRUE(def->getEncapsulatedSpace().empty());
  937. }
  938. // The purpose of this test to verify that encapsulated option
  939. // space name may be specified.
  940. TEST_F(Dhcp4ParserTest, optionDefEncapsulate) {
  941. // Configuration string. Included the encapsulated
  942. // option space name.
  943. std::string config =
  944. "{ \"option-def\": [ {"
  945. " \"name\": \"foo\","
  946. " \"code\": 100,"
  947. " \"type\": \"uint32\","
  948. " \"array\": False,"
  949. " \"record-types\": \"\","
  950. " \"space\": \"isc\","
  951. " \"encapsulate\": \"sub-opts-space\""
  952. " } ]"
  953. "}";
  954. ElementPtr json = Element::fromJSON(config);
  955. // Make sure that the particular option definition does not exist.
  956. OptionDefinitionPtr def = CfgMgr::instance().getOptionDef("isc", 100);
  957. ASSERT_FALSE(def);
  958. // Use the configuration string to create new option definition.
  959. ConstElementPtr status;
  960. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  961. ASSERT_TRUE(status);
  962. checkResult(status, 0);
  963. // The option definition should now be available in the CfgMgr.
  964. def = CfgMgr::instance().getOptionDef("isc", 100);
  965. ASSERT_TRUE(def);
  966. // Check the option data.
  967. EXPECT_EQ("foo", def->getName());
  968. EXPECT_EQ(100, def->getCode());
  969. EXPECT_EQ(OPT_UINT32_TYPE, def->getType());
  970. EXPECT_FALSE(def->getArrayType());
  971. EXPECT_EQ("sub-opts-space", def->getEncapsulatedSpace());
  972. }
  973. /// The purpose of this test is to verify that the option definition
  974. /// with invalid name is not accepted.
  975. TEST_F(Dhcp4ParserTest, optionDefInvalidName) {
  976. // Configuration string. The option name is invalid as it
  977. // contains the % character.
  978. std::string config =
  979. "{ \"option-def\": [ {"
  980. " \"name\": \"invalid%name\","
  981. " \"code\": 100,"
  982. " \"type\": \"string\","
  983. " \"array\": False,"
  984. " \"record-types\": \"\","
  985. " \"space\": \"isc\","
  986. " \"encapsulate\": \"\""
  987. " } ]"
  988. "}";
  989. ElementPtr json = Element::fromJSON(config);
  990. // Use the configuration string to create new option definition.
  991. ConstElementPtr status;
  992. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  993. ASSERT_TRUE(status);
  994. // Expecting parsing error (error code 1).
  995. checkResult(status, 1);
  996. }
  997. /// The purpose of this test is to verify that the option definition
  998. /// with invalid type is not accepted.
  999. TEST_F(Dhcp4ParserTest, optionDefInvalidType) {
  1000. // Configuration string. The option type is invalid. It is
  1001. // "sting" instead of "string".
  1002. std::string config =
  1003. "{ \"option-def\": [ {"
  1004. " \"name\": \"foo\","
  1005. " \"code\": 100,"
  1006. " \"type\": \"sting\","
  1007. " \"array\": False,"
  1008. " \"record-types\": \"\","
  1009. " \"space\": \"isc\","
  1010. " \"encapsulate\": \"\""
  1011. " } ]"
  1012. "}";
  1013. ElementPtr json = Element::fromJSON(config);
  1014. // Use the configuration string to create new option definition.
  1015. ConstElementPtr status;
  1016. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  1017. ASSERT_TRUE(status);
  1018. // Expecting parsing error (error code 1).
  1019. checkResult(status, 1);
  1020. }
  1021. /// The purpose of this test is to verify that the option definition
  1022. /// with invalid type is not accepted.
  1023. TEST_F(Dhcp4ParserTest, optionDefInvalidRecordType) {
  1024. // Configuration string. The third of the record fields
  1025. // is invalid. It is "sting" instead of "string".
  1026. std::string config =
  1027. "{ \"option-def\": [ {"
  1028. " \"name\": \"foo\","
  1029. " \"code\": 100,"
  1030. " \"type\": \"record\","
  1031. " \"array\": False,"
  1032. " \"record-types\": \"uint32,uint8,sting\","
  1033. " \"space\": \"isc\","
  1034. " \"encapsulate\": \"\""
  1035. " } ]"
  1036. "}";
  1037. ElementPtr json = Element::fromJSON(config);
  1038. // Use the configuration string to create new option definition.
  1039. ConstElementPtr status;
  1040. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  1041. ASSERT_TRUE(status);
  1042. // Expecting parsing error (error code 1).
  1043. checkResult(status, 1);
  1044. }
  1045. /// The goal of this test is to verify that the invalid encapsulated
  1046. /// option space name is not accepted.
  1047. TEST_F(Dhcp4ParserTest, optionDefInvalidEncapsulatedSpace) {
  1048. // Configuration string. The encapsulated option space
  1049. // name is invalid (% character is not allowed).
  1050. std::string config =
  1051. "{ \"option-def\": [ {"
  1052. " \"name\": \"foo\","
  1053. " \"code\": 100,"
  1054. " \"type\": \"uint32\","
  1055. " \"array\": False,"
  1056. " \"record-types\": \"\","
  1057. " \"space\": \"isc\","
  1058. " \"encapsulate\": \"invalid%space%name\""
  1059. " } ]"
  1060. "}";
  1061. ElementPtr json = Element::fromJSON(config);
  1062. // Use the configuration string to create new option definition.
  1063. ConstElementPtr status;
  1064. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  1065. ASSERT_TRUE(status);
  1066. // Expecting parsing error (error code 1).
  1067. checkResult(status, 1);
  1068. }
  1069. /// The goal of this test is to verify that the encapsulated
  1070. /// option space name can't be specified for the option that
  1071. /// comprises an array of data fields.
  1072. TEST_F(Dhcp4ParserTest, optionDefEncapsulatedSpaceAndArray) {
  1073. // Configuration string. The encapsulated option space
  1074. // name is set to non-empty value and the array flag
  1075. // is set.
  1076. std::string config =
  1077. "{ \"option-def\": [ {"
  1078. " \"name\": \"foo\","
  1079. " \"code\": 100,"
  1080. " \"type\": \"uint32\","
  1081. " \"array\": True,"
  1082. " \"record-types\": \"\","
  1083. " \"space\": \"isc\","
  1084. " \"encapsulate\": \"valid-space-name\""
  1085. " } ]"
  1086. "}";
  1087. ElementPtr json = Element::fromJSON(config);
  1088. // Use the configuration string to create new option definition.
  1089. ConstElementPtr status;
  1090. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  1091. ASSERT_TRUE(status);
  1092. // Expecting parsing error (error code 1).
  1093. checkResult(status, 1);
  1094. }
  1095. /// The goal of this test is to verify that the option may not
  1096. /// encapsulate option space it belongs to.
  1097. TEST_F(Dhcp4ParserTest, optionDefEncapsulateOwnSpace) {
  1098. // Configuration string. Option is set to encapsulate
  1099. // option space it belongs to.
  1100. std::string config =
  1101. "{ \"option-def\": [ {"
  1102. " \"name\": \"foo\","
  1103. " \"code\": 100,"
  1104. " \"type\": \"uint32\","
  1105. " \"array\": False,"
  1106. " \"record-types\": \"\","
  1107. " \"space\": \"isc\","
  1108. " \"encapsulate\": \"isc\""
  1109. " } ]"
  1110. "}";
  1111. ElementPtr json = Element::fromJSON(config);
  1112. // Use the configuration string to create new option definition.
  1113. ConstElementPtr status;
  1114. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  1115. ASSERT_TRUE(status);
  1116. // Expecting parsing error (error code 1).
  1117. checkResult(status, 1);
  1118. }
  1119. /// The purpose of this test is to verify that it is not allowed
  1120. /// to override the standard option (that belongs to dhcp4 option
  1121. /// space) and that it is allowed to define option in the dhcp4
  1122. /// option space that has a code which is not used by any of the
  1123. /// standard options.
  1124. TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
  1125. // Configuration string. The option code 109 is unassigned
  1126. // so it can be used for a custom option definition in
  1127. // dhcp4 option space.
  1128. std::string config =
  1129. "{ \"option-def\": [ {"
  1130. " \"name\": \"foo\","
  1131. " \"code\": 109,"
  1132. " \"type\": \"string\","
  1133. " \"array\": False,"
  1134. " \"record-types\": \"\","
  1135. " \"space\": \"dhcp4\","
  1136. " \"encapsulate\": \"\""
  1137. " } ]"
  1138. "}";
  1139. ElementPtr json = Element::fromJSON(config);
  1140. OptionDefinitionPtr def = CfgMgr::instance().getOptionDef("dhcp4", 109);
  1141. ASSERT_FALSE(def);
  1142. // Use the configuration string to create new option definition.
  1143. ConstElementPtr status;
  1144. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  1145. ASSERT_TRUE(status);
  1146. checkResult(status, 0);
  1147. // The option definition should now be available in the CfgMgr.
  1148. def = CfgMgr::instance().getOptionDef("dhcp4", 109);
  1149. ASSERT_TRUE(def);
  1150. // Check the option data.
  1151. EXPECT_EQ("foo", def->getName());
  1152. EXPECT_EQ(109, def->getCode());
  1153. EXPECT_EQ(OPT_STRING_TYPE, def->getType());
  1154. EXPECT_FALSE(def->getArrayType());
  1155. // The combination of option space and code is
  1156. // invalid. The 'dhcp4' option space groups
  1157. // standard options and the code 100 is reserved
  1158. // for one of them.
  1159. config =
  1160. "{ \"option-def\": [ {"
  1161. " \"name\": \"foo\","
  1162. " \"code\": 100,"
  1163. " \"type\": \"string\","
  1164. " \"array\": False,"
  1165. " \"record-types\": \"\","
  1166. " \"space\": \"dhcp4\","
  1167. " \"encapsulate\": \"\""
  1168. " } ]"
  1169. "}";
  1170. json = Element::fromJSON(config);
  1171. // Use the configuration string to create new option definition.
  1172. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  1173. ASSERT_TRUE(status);
  1174. // Expecting parsing error (error code 1).
  1175. checkResult(status, 1);
  1176. }
  1177. // Goal of this test is to verify that global option
  1178. // data is configured for the subnet if the subnet
  1179. // configuration does not include options configuration.
  1180. TEST_F(Dhcp4ParserTest, optionDataDefaults) {
  1181. ConstElementPtr x;
  1182. string config = "{ \"interfaces\": [ \"*\" ],"
  1183. "\"rebind-timer\": 2000,"
  1184. "\"renew-timer\": 1000,"
  1185. "\"option-data\": [ {"
  1186. " \"name\": \"dhcp-message\","
  1187. " \"space\": \"dhcp4\","
  1188. " \"code\": 56,"
  1189. " \"data\": \"AB CDEF0105\","
  1190. " \"csv-format\": False"
  1191. " },"
  1192. " {"
  1193. " \"name\": \"default-ip-ttl\","
  1194. " \"space\": \"dhcp4\","
  1195. " \"code\": 23,"
  1196. " \"data\": \"01\","
  1197. " \"csv-format\": False"
  1198. " } ],"
  1199. "\"subnet4\": [ { "
  1200. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  1201. " \"subnet\": \"192.0.2.0/24\""
  1202. " } ],"
  1203. "\"valid-lifetime\": 4000 }";
  1204. ElementPtr json = Element::fromJSON(config);
  1205. EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
  1206. ASSERT_TRUE(x);
  1207. comment_ = parseAnswer(rcode_, x);
  1208. ASSERT_EQ(0, rcode_);
  1209. Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
  1210. ASSERT_TRUE(subnet);
  1211. Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp4");
  1212. ASSERT_EQ(2, options->size());
  1213. // Get the search index. Index #1 is to search using option code.
  1214. const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
  1215. // Get the options for specified index. Expecting one option to be
  1216. // returned but in theory we may have multiple options with the same
  1217. // code so we get the range.
  1218. std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
  1219. Subnet::OptionContainerTypeIndex::const_iterator> range =
  1220. idx.equal_range(56);
  1221. // Expect single option with the code equal to 56.
  1222. ASSERT_EQ(1, std::distance(range.first, range.second));
  1223. const uint8_t foo_expected[] = {
  1224. 0xAB, 0xCD, 0xEF, 0x01, 0x05
  1225. };
  1226. // Check if option is valid in terms of code and carried data.
  1227. testOption(*range.first, 56, foo_expected, sizeof(foo_expected));
  1228. range = idx.equal_range(23);
  1229. ASSERT_EQ(1, std::distance(range.first, range.second));
  1230. // Do another round of testing with second option.
  1231. const uint8_t foo2_expected[] = {
  1232. 0x01
  1233. };
  1234. testOption(*range.first, 23, foo2_expected, sizeof(foo2_expected));
  1235. }
  1236. /// The goal of this test is to verify that two options having the same
  1237. /// option code can be added to different option spaces.
  1238. TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) {
  1239. // This configuration string is to configure two options
  1240. // sharing the code 56 and having different definitions
  1241. // and belonging to the different option spaces.
  1242. // The option definition must be provided for the
  1243. // option that belongs to the 'isc' option space.
  1244. // The definition is not required for the option that
  1245. // belongs to the 'dhcp4' option space as it is the
  1246. // standard option.
  1247. string config = "{ \"interfaces\": [ \"*\" ],"
  1248. "\"rebind-timer\": 2000,"
  1249. "\"renew-timer\": 1000,"
  1250. "\"option-data\": [ {"
  1251. " \"name\": \"dhcp-message\","
  1252. " \"space\": \"dhcp4\","
  1253. " \"code\": 56,"
  1254. " \"data\": \"AB CDEF0105\","
  1255. " \"csv-format\": False"
  1256. " },"
  1257. " {"
  1258. " \"name\": \"foo\","
  1259. " \"space\": \"isc\","
  1260. " \"code\": 56,"
  1261. " \"data\": \"1234\","
  1262. " \"csv-format\": True"
  1263. " } ],"
  1264. "\"option-def\": [ {"
  1265. " \"name\": \"foo\","
  1266. " \"code\": 56,"
  1267. " \"type\": \"uint32\","
  1268. " \"array\": False,"
  1269. " \"record-types\": \"\","
  1270. " \"space\": \"isc\","
  1271. " \"encapsulate\": \"\""
  1272. " } ],"
  1273. "\"subnet4\": [ { "
  1274. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  1275. " \"subnet\": \"192.0.2.0/24\""
  1276. " } ]"
  1277. "}";
  1278. ConstElementPtr status;
  1279. ElementPtr json = Element::fromJSON(config);
  1280. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  1281. ASSERT_TRUE(status);
  1282. checkResult(status, 0);
  1283. // Options should be now available for the subnet.
  1284. Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
  1285. ASSERT_TRUE(subnet);
  1286. // Try to get the option from the space dhcp4.
  1287. Subnet::OptionDescriptor desc1 = subnet->getOptionDescriptor("dhcp4", 56);
  1288. ASSERT_TRUE(desc1.option);
  1289. EXPECT_EQ(56, desc1.option->getType());
  1290. // Try to get the option from the space isc.
  1291. Subnet::OptionDescriptor desc2 = subnet->getOptionDescriptor("isc", 56);
  1292. ASSERT_TRUE(desc2.option);
  1293. EXPECT_EQ(56, desc1.option->getType());
  1294. // Try to get the non-existing option from the non-existing
  1295. // option space and expect that option is not returned.
  1296. Subnet::OptionDescriptor desc3 = subnet->getOptionDescriptor("non-existing", 56);
  1297. ASSERT_FALSE(desc3.option);
  1298. }
  1299. // The goal of this test is to verify that it is possible to
  1300. // encapsulate option space containing some options with
  1301. // another option. In this test we create base option that
  1302. // encapsulates option space 'isc' that comprises two other
  1303. // options. Also, for all options their definitions are
  1304. // created.
  1305. TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
  1306. // @todo DHCP configurations has many dependencies between
  1307. // parameters. First of all, configuration for subnet is
  1308. // inherited from the global values. Thus subnet has to be
  1309. // configured when all global values have been configured.
  1310. // Also, an option can encapsulate another option only
  1311. // if the latter has been configured. For this reason in this
  1312. // test we created two-stage configuration where first we
  1313. // created options that belong to encapsulated option space.
  1314. // In the second stage we add the base option. Also, the Subnet
  1315. // object is configured in the second stage so it is created
  1316. // at the very end (when all other parameters are configured).
  1317. // Starting stage 1. Configure sub-options and their definitions.
  1318. string config = "{ \"interfaces\": [ \"*\" ],"
  1319. "\"rebind-timer\": 2000,"
  1320. "\"renew-timer\": 1000,"
  1321. "\"option-data\": [ {"
  1322. " \"name\": \"foo\","
  1323. " \"space\": \"isc\","
  1324. " \"code\": 1,"
  1325. " \"data\": \"1234\","
  1326. " \"csv-format\": True"
  1327. " },"
  1328. " {"
  1329. " \"name\": \"foo2\","
  1330. " \"space\": \"isc\","
  1331. " \"code\": 2,"
  1332. " \"data\": \"192.168.2.1\","
  1333. " \"csv-format\": True"
  1334. " } ],"
  1335. "\"option-def\": [ {"
  1336. " \"name\": \"foo\","
  1337. " \"code\": 1,"
  1338. " \"type\": \"uint32\","
  1339. " \"array\": False,"
  1340. " \"record-types\": \"\","
  1341. " \"space\": \"isc\","
  1342. " \"encapsulate\": \"\""
  1343. " },"
  1344. " {"
  1345. " \"name\": \"foo2\","
  1346. " \"code\": 2,"
  1347. " \"type\": \"ipv4-address\","
  1348. " \"array\": False,"
  1349. " \"record-types\": \"\","
  1350. " \"space\": \"isc\","
  1351. " \"encapsulate\": \"\""
  1352. " } ]"
  1353. "}";
  1354. ConstElementPtr status;
  1355. ElementPtr json = Element::fromJSON(config);
  1356. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  1357. ASSERT_TRUE(status);
  1358. checkResult(status, 0);
  1359. // Stage 2. Configure base option and a subnet. Please note that
  1360. // the configuration from the stage 2 is repeated because BIND
  1361. // configuration manager sends whole configuration for the lists
  1362. // where at least one element is being modified or added.
  1363. config = "{ \"interfaces\": [ \"*\" ],"
  1364. "\"rebind-timer\": 2000,"
  1365. "\"renew-timer\": 1000,"
  1366. "\"option-data\": [ {"
  1367. " \"name\": \"base-option\","
  1368. " \"space\": \"dhcp4\","
  1369. " \"code\": 222,"
  1370. " \"data\": \"11\","
  1371. " \"csv-format\": True"
  1372. " },"
  1373. " {"
  1374. " \"name\": \"foo\","
  1375. " \"space\": \"isc\","
  1376. " \"code\": 1,"
  1377. " \"data\": \"1234\","
  1378. " \"csv-format\": True"
  1379. " },"
  1380. " {"
  1381. " \"name\": \"foo2\","
  1382. " \"space\": \"isc\","
  1383. " \"code\": 2,"
  1384. " \"data\": \"192.168.2.1\","
  1385. " \"csv-format\": True"
  1386. " } ],"
  1387. "\"option-def\": [ {"
  1388. " \"name\": \"base-option\","
  1389. " \"code\": 222,"
  1390. " \"type\": \"uint8\","
  1391. " \"array\": False,"
  1392. " \"record-types\": \"\","
  1393. " \"space\": \"dhcp4\","
  1394. " \"encapsulate\": \"isc\""
  1395. "},"
  1396. "{"
  1397. " \"name\": \"foo\","
  1398. " \"code\": 1,"
  1399. " \"type\": \"uint32\","
  1400. " \"array\": False,"
  1401. " \"record-types\": \"\","
  1402. " \"space\": \"isc\","
  1403. " \"encapsulate\": \"\""
  1404. " },"
  1405. " {"
  1406. " \"name\": \"foo2\","
  1407. " \"code\": 2,"
  1408. " \"type\": \"ipv4-address\","
  1409. " \"array\": False,"
  1410. " \"record-types\": \"\","
  1411. " \"space\": \"isc\","
  1412. " \"encapsulate\": \"\""
  1413. " } ],"
  1414. "\"subnet4\": [ { "
  1415. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  1416. " \"subnet\": \"192.0.2.0/24\""
  1417. " } ]"
  1418. "}";
  1419. json = Element::fromJSON(config);
  1420. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  1421. ASSERT_TRUE(status);
  1422. checkResult(status, 0);
  1423. // Get the subnet.
  1424. Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
  1425. ASSERT_TRUE(subnet);
  1426. // We should have one option available.
  1427. Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp4");
  1428. ASSERT_TRUE(options);
  1429. ASSERT_EQ(1, options->size());
  1430. // Get the option.
  1431. Subnet::OptionDescriptor desc = subnet->getOptionDescriptor("dhcp4", 222);
  1432. EXPECT_TRUE(desc.option);
  1433. EXPECT_EQ(222, desc.option->getType());
  1434. // This opton should comprise two sub-options.
  1435. // One of them is 'foo' with code 1.
  1436. OptionPtr option_foo = desc.option->getOption(1);
  1437. ASSERT_TRUE(option_foo);
  1438. EXPECT_EQ(1, option_foo->getType());
  1439. // ...another one 'foo2' with code 2.
  1440. OptionPtr option_foo2 = desc.option->getOption(2);
  1441. ASSERT_TRUE(option_foo2);
  1442. EXPECT_EQ(2, option_foo2->getType());
  1443. }
  1444. // Goal of this test is to verify options configuration
  1445. // for a single subnet. In particular this test checks
  1446. // that local options configuration overrides global
  1447. // option setting.
  1448. TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) {
  1449. ConstElementPtr x;
  1450. string config = "{ \"interfaces\": [ \"*\" ],"
  1451. "\"rebind-timer\": 2000, "
  1452. "\"renew-timer\": 1000, "
  1453. "\"option-data\": [ {"
  1454. " \"name\": \"dhcp-message\","
  1455. " \"space\": \"dhcp4\","
  1456. " \"code\": 56,"
  1457. " \"data\": \"AB\","
  1458. " \"csv-format\": False"
  1459. " } ],"
  1460. "\"subnet4\": [ { "
  1461. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  1462. " \"subnet\": \"192.0.2.0/24\", "
  1463. " \"option-data\": [ {"
  1464. " \"name\": \"dhcp-message\","
  1465. " \"space\": \"dhcp4\","
  1466. " \"code\": 56,"
  1467. " \"data\": \"AB CDEF0105\","
  1468. " \"csv-format\": False"
  1469. " },"
  1470. " {"
  1471. " \"name\": \"default-ip-ttl\","
  1472. " \"space\": \"dhcp4\","
  1473. " \"code\": 23,"
  1474. " \"data\": \"01\","
  1475. " \"csv-format\": False"
  1476. " } ]"
  1477. " } ],"
  1478. "\"valid-lifetime\": 4000 }";
  1479. ElementPtr json = Element::fromJSON(config);
  1480. EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
  1481. ASSERT_TRUE(x);
  1482. comment_ = parseAnswer(rcode_, x);
  1483. ASSERT_EQ(0, rcode_);
  1484. Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.24"));
  1485. ASSERT_TRUE(subnet);
  1486. Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp4");
  1487. ASSERT_EQ(2, options->size());
  1488. // Get the search index. Index #1 is to search using option code.
  1489. const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
  1490. // Get the options for specified index. Expecting one option to be
  1491. // returned but in theory we may have multiple options with the same
  1492. // code so we get the range.
  1493. std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
  1494. Subnet::OptionContainerTypeIndex::const_iterator> range =
  1495. idx.equal_range(56);
  1496. // Expect single option with the code equal to 100.
  1497. ASSERT_EQ(1, std::distance(range.first, range.second));
  1498. const uint8_t foo_expected[] = {
  1499. 0xAB, 0xCD, 0xEF, 0x01, 0x05
  1500. };
  1501. // Check if option is valid in terms of code and carried data.
  1502. testOption(*range.first, 56, foo_expected, sizeof(foo_expected));
  1503. range = idx.equal_range(23);
  1504. ASSERT_EQ(1, std::distance(range.first, range.second));
  1505. // Do another round of testing with second option.
  1506. const uint8_t foo2_expected[] = {
  1507. 0x01
  1508. };
  1509. testOption(*range.first, 23, foo2_expected, sizeof(foo2_expected));
  1510. }
  1511. // Goal of this test is to verify options configuration
  1512. // for multiple subnets.
  1513. TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
  1514. ConstElementPtr x;
  1515. string config = "{ \"interfaces\": [ \"*\" ],"
  1516. "\"rebind-timer\": 2000, "
  1517. "\"renew-timer\": 1000, "
  1518. "\"subnet4\": [ { "
  1519. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  1520. " \"subnet\": \"192.0.2.0/24\", "
  1521. " \"option-data\": [ {"
  1522. " \"name\": \"dhcp-message\","
  1523. " \"space\": \"dhcp4\","
  1524. " \"code\": 56,"
  1525. " \"data\": \"0102030405060708090A\","
  1526. " \"csv-format\": False"
  1527. " } ]"
  1528. " },"
  1529. " {"
  1530. " \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
  1531. " \"subnet\": \"192.0.3.0/24\", "
  1532. " \"option-data\": [ {"
  1533. " \"name\": \"default-ip-ttl\","
  1534. " \"space\": \"dhcp4\","
  1535. " \"code\": 23,"
  1536. " \"data\": \"FF\","
  1537. " \"csv-format\": False"
  1538. " } ]"
  1539. " } ],"
  1540. "\"valid-lifetime\": 4000 }";
  1541. ElementPtr json = Element::fromJSON(config);
  1542. EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
  1543. ASSERT_TRUE(x);
  1544. comment_ = parseAnswer(rcode_, x);
  1545. ASSERT_EQ(0, rcode_);
  1546. Subnet4Ptr subnet1 = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.100"));
  1547. ASSERT_TRUE(subnet1);
  1548. Subnet::OptionContainerPtr options1 = subnet1->getOptionDescriptors("dhcp4");
  1549. ASSERT_EQ(1, options1->size());
  1550. // Get the search index. Index #1 is to search using option code.
  1551. const Subnet::OptionContainerTypeIndex& idx1 = options1->get<1>();
  1552. // Get the options for specified index. Expecting one option to be
  1553. // returned but in theory we may have multiple options with the same
  1554. // code so we get the range.
  1555. std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
  1556. Subnet::OptionContainerTypeIndex::const_iterator> range1 =
  1557. idx1.equal_range(56);
  1558. // Expect single option with the code equal to 56.
  1559. ASSERT_EQ(1, std::distance(range1.first, range1.second));
  1560. const uint8_t foo_expected[] = {
  1561. 0x01, 0x02, 0x03, 0x04, 0x05,
  1562. 0x06, 0x07, 0x08, 0x09, 0x0A
  1563. };
  1564. // Check if option is valid in terms of code and carried data.
  1565. testOption(*range1.first, 56, foo_expected, sizeof(foo_expected));
  1566. // Test another subnet in the same way.
  1567. Subnet4Ptr subnet2 = CfgMgr::instance().getSubnet4(IOAddress("192.0.3.102"));
  1568. ASSERT_TRUE(subnet2);
  1569. Subnet::OptionContainerPtr options2 = subnet2->getOptionDescriptors("dhcp4");
  1570. ASSERT_EQ(1, options2->size());
  1571. const Subnet::OptionContainerTypeIndex& idx2 = options2->get<1>();
  1572. std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
  1573. Subnet::OptionContainerTypeIndex::const_iterator> range2 =
  1574. idx2.equal_range(23);
  1575. ASSERT_EQ(1, std::distance(range2.first, range2.second));
  1576. const uint8_t foo2_expected[] = { 0xFF };
  1577. testOption(*range2.first, 23, foo2_expected, sizeof(foo2_expected));
  1578. }
  1579. // Verify that empty option name is rejected in the configuration.
  1580. TEST_F(Dhcp4ParserTest, optionNameEmpty) {
  1581. // Empty option names not allowed.
  1582. testInvalidOptionParam("", "name");
  1583. }
  1584. // Verify that empty option name with spaces is rejected
  1585. // in the configuration.
  1586. TEST_F(Dhcp4ParserTest, optionNameSpaces) {
  1587. // Spaces in option names not allowed.
  1588. testInvalidOptionParam("option foo", "name");
  1589. }
  1590. // Verify that negative option code is rejected in the configuration.
  1591. TEST_F(Dhcp4ParserTest, optionCodeNegative) {
  1592. // Check negative option code -4. This should fail too.
  1593. testInvalidOptionParam("-4", "code");
  1594. }
  1595. // Verify that out of bounds option code is rejected in the configuration.
  1596. TEST_F(Dhcp4ParserTest, optionCodeNonUint8) {
  1597. // The valid option codes are uint16_t values so passing
  1598. // uint16_t maximum value incremented by 1 should result
  1599. // in failure.
  1600. testInvalidOptionParam("257", "code");
  1601. }
  1602. // Verify that zero option code is rejected in the configuration.
  1603. TEST_F(Dhcp4ParserTest, optionCodeZero) {
  1604. // Option code 0 is reserved and should not be accepted
  1605. // by configuration parser.
  1606. testInvalidOptionParam("0", "code");
  1607. }
  1608. // Verify that option data which contains non hexadecimal characters
  1609. // is rejected by the configuration.
  1610. TEST_F(Dhcp4ParserTest, optionDataInvalidChar) {
  1611. // Option code 0 is reserved and should not be accepted
  1612. // by configuration parser.
  1613. testInvalidOptionParam("01020R", "data");
  1614. }
  1615. // Verify that option data containing '0x' prefix is rejected
  1616. // by the configuration.
  1617. TEST_F(Dhcp4ParserTest, optionDataUnexpectedPrefix) {
  1618. // Option code 0 is reserved and should not be accepted
  1619. // by configuration parser.
  1620. testInvalidOptionParam("0x0102", "data");
  1621. }
  1622. // Verify that option data consisting od an odd number of
  1623. // hexadecimal digits is rejected in the configuration.
  1624. TEST_F(Dhcp4ParserTest, optionDataOddLength) {
  1625. // Option code 0 is reserved and should not be accepted
  1626. // by configuration parser.
  1627. testInvalidOptionParam("123", "data");
  1628. }
  1629. // Verify that either lower or upper case characters are allowed
  1630. // to specify the option data.
  1631. TEST_F(Dhcp4ParserTest, optionDataLowerCase) {
  1632. ConstElementPtr x;
  1633. std::string config = createConfigWithOption("0a0b0C0D", "data");
  1634. ElementPtr json = Element::fromJSON(config);
  1635. EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
  1636. ASSERT_TRUE(x);
  1637. comment_ = parseAnswer(rcode_, x);
  1638. ASSERT_EQ(0, rcode_);
  1639. Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
  1640. ASSERT_TRUE(subnet);
  1641. Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp4");
  1642. ASSERT_EQ(1, options->size());
  1643. // Get the search index. Index #1 is to search using option code.
  1644. const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
  1645. // Get the options for specified index. Expecting one option to be
  1646. // returned but in theory we may have multiple options with the same
  1647. // code so we get the range.
  1648. std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
  1649. Subnet::OptionContainerTypeIndex::const_iterator> range =
  1650. idx.equal_range(56);
  1651. // Expect single option with the code equal to 100.
  1652. ASSERT_EQ(1, std::distance(range.first, range.second));
  1653. const uint8_t foo_expected[] = {
  1654. 0x0A, 0x0B, 0x0C, 0x0D
  1655. };
  1656. // Check if option is valid in terms of code and carried data.
  1657. testOption(*range.first, 56, foo_expected, sizeof(foo_expected));
  1658. }
  1659. // Verify that specific option object is returned for standard
  1660. // option which has dedicated option class derived from Option.
  1661. TEST_F(Dhcp4ParserTest, stdOptionData) {
  1662. ConstElementPtr x;
  1663. std::map<std::string, std::string> params;
  1664. params["name"] = "nis-servers";
  1665. params["space"] = "dhcp4";
  1666. // Option code 41 means nis-servers.
  1667. params["code"] = "41";
  1668. // Specify option values in a CSV (user friendly) format.
  1669. params["data"] = "192.0.2.10, 192.0.2.1, 192.0.2.3";
  1670. params["csv-format"] = "True";
  1671. std::string config = createConfigWithOption(params);
  1672. ElementPtr json = Element::fromJSON(config);
  1673. EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
  1674. ASSERT_TRUE(x);
  1675. comment_ = parseAnswer(rcode_, x);
  1676. ASSERT_EQ(0, rcode_);
  1677. Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
  1678. ASSERT_TRUE(subnet);
  1679. Subnet::OptionContainerPtr options =
  1680. subnet->getOptionDescriptors("dhcp4");
  1681. ASSERT_TRUE(options);
  1682. ASSERT_EQ(1, options->size());
  1683. // Get the search index. Index #1 is to search using option code.
  1684. const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
  1685. // Get the options for specified index. Expecting one option to be
  1686. // returned but in theory we may have multiple options with the same
  1687. // code so we get the range.
  1688. std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
  1689. Subnet::OptionContainerTypeIndex::const_iterator> range =
  1690. idx.equal_range(DHO_NIS_SERVERS);
  1691. // Expect single option with the code equal to NIS_SERVERS option code.
  1692. ASSERT_EQ(1, std::distance(range.first, range.second));
  1693. // The actual pointer to the option is held in the option field
  1694. // in the structure returned.
  1695. OptionPtr option = range.first->option;
  1696. ASSERT_TRUE(option);
  1697. // Option object returned for here is expected to be Option6IA
  1698. // which is derived from Option. This class is dedicated to
  1699. // represent standard option IA_NA.
  1700. boost::shared_ptr<Option4AddrLst> option_addrs =
  1701. boost::dynamic_pointer_cast<Option4AddrLst>(option);
  1702. // If cast is unsuccessful than option returned was of a
  1703. // different type than Option6IA. This is wrong.
  1704. ASSERT_TRUE(option_addrs);
  1705. // Get addresses from the option.
  1706. Option4AddrLst::AddressContainer addrs = option_addrs->getAddresses();
  1707. // Verify that the addresses have been configured correctly.
  1708. ASSERT_EQ(3, addrs.size());
  1709. EXPECT_EQ("192.0.2.10", addrs[0].toText());
  1710. EXPECT_EQ("192.0.2.1", addrs[1].toText());
  1711. EXPECT_EQ("192.0.2.3", addrs[2].toText());
  1712. }
  1713. /// This test checks if Uint32Parser can really parse the whole range
  1714. /// and properly err of out of range values. As we can't call Uint32Parser
  1715. /// directly, we are exploiting the fact that it is used to parse global
  1716. /// parameter renew-timer and the results are stored in uint32_defaults.
  1717. /// We get the uint32_defaults using a getUint32Defaults functions which
  1718. /// is defined only to access the values from this test.
  1719. TEST_F(Dhcp4ParserTest, DISABLED_Uint32Parser) {
  1720. ConstElementPtr status;
  1721. // CASE 1: 0 - minimum value, should work
  1722. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
  1723. Element::fromJSON("{\"version\": 0,"
  1724. "\"renew-timer\": 0}")));
  1725. // returned value must be ok (0 is a proper value)
  1726. checkResult(status, 0);
  1727. checkGlobalUint32("renew-timer", 0);
  1728. // CASE 2: 4294967295U (UINT_MAX) should work as well
  1729. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
  1730. Element::fromJSON("{\"version\": 0,"
  1731. "\"renew-timer\": 4294967295}")));
  1732. // returned value must be ok (0 is a proper value)
  1733. checkResult(status, 0);
  1734. checkGlobalUint32("renew-timer", 4294967295U);
  1735. // CASE 3: 4294967296U (UINT_MAX + 1) should not work
  1736. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
  1737. Element::fromJSON("{\"version\": 0,"
  1738. "\"renew-timer\": 4294967296}")));
  1739. // returned value must be rejected (1 configuration error)
  1740. checkResult(status, 1);
  1741. // CASE 4: -1 (UINT_MIN -1 ) should not work
  1742. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
  1743. Element::fromJSON("{\"version\": 0,"
  1744. "\"renew-timer\": -1}")));
  1745. // returned value must be rejected (1 configuration error)
  1746. checkResult(status, 1);
  1747. }
  1748. // The goal of this test is to verify that the standard option can
  1749. // be configured to encapsulate multiple other options.
  1750. TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
  1751. // The configuration is two stage process in this test.
  1752. // In the first stahe we create definitions of suboptions
  1753. // that we will add to the base option.
  1754. // Let's create some dummy options: foo and foo2.
  1755. string config = "{ \"interfaces\": [ \"*\" ],"
  1756. "\"rebind-timer\": 2000,"
  1757. "\"renew-timer\": 1000,"
  1758. "\"option-data\": [ {"
  1759. " \"name\": \"foo\","
  1760. " \"space\": \"vendor-encapsulated-options-space\","
  1761. " \"code\": 1,"
  1762. " \"data\": \"1234\","
  1763. " \"csv-format\": True"
  1764. " },"
  1765. " {"
  1766. " \"name\": \"foo2\","
  1767. " \"space\": \"vendor-encapsulated-options-space\","
  1768. " \"code\": 2,"
  1769. " \"data\": \"192.168.2.1\","
  1770. " \"csv-format\": True"
  1771. " } ],"
  1772. "\"option-def\": [ {"
  1773. " \"name\": \"foo\","
  1774. " \"code\": 1,"
  1775. " \"type\": \"uint32\","
  1776. " \"array\": False,"
  1777. " \"record-types\": \"\","
  1778. " \"space\": \"vendor-encapsulated-options-space\","
  1779. " \"encapsulate\": \"\""
  1780. " },"
  1781. " {"
  1782. " \"name\": \"foo2\","
  1783. " \"code\": 2,"
  1784. " \"type\": \"ipv4-address\","
  1785. " \"array\": False,"
  1786. " \"record-types\": \"\","
  1787. " \"space\": \"vendor-encapsulated-options-space\","
  1788. " \"encapsulate\": \"\""
  1789. " } ]"
  1790. "}";
  1791. ConstElementPtr status;
  1792. ElementPtr json = Element::fromJSON(config);
  1793. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  1794. ASSERT_TRUE(status);
  1795. checkResult(status, 0);
  1796. // Once the definitions have been added we can configure the
  1797. // standard option #17. This option comprises an enterprise
  1798. // number and sub options. By convention (introduced in
  1799. // std_option_defs.h) option named 'vendor-opts'
  1800. // encapsulates the option space named 'vendor-opts-space'.
  1801. // We add our dummy options to this option space and thus
  1802. // they should be included as sub-options in the 'vendor-opts'
  1803. // option.
  1804. config = "{ \"interfaces\": [ \"*\" ],"
  1805. "\"rebind-timer\": 2000,"
  1806. "\"renew-timer\": 1000,"
  1807. "\"option-data\": [ {"
  1808. " \"name\": \"vendor-encapsulated-options\","
  1809. " \"space\": \"dhcp4\","
  1810. " \"code\": 43,"
  1811. " \"data\": \"\","
  1812. " \"csv-format\": False"
  1813. " },"
  1814. " {"
  1815. " \"name\": \"foo\","
  1816. " \"space\": \"vendor-encapsulated-options-space\","
  1817. " \"code\": 1,"
  1818. " \"data\": \"1234\","
  1819. " \"csv-format\": True"
  1820. " },"
  1821. " {"
  1822. " \"name\": \"foo2\","
  1823. " \"space\": \"vendor-encapsulated-options-space\","
  1824. " \"code\": 2,"
  1825. " \"data\": \"192.168.2.1\","
  1826. " \"csv-format\": True"
  1827. " } ],"
  1828. "\"option-def\": [ {"
  1829. " \"name\": \"foo\","
  1830. " \"code\": 1,"
  1831. " \"type\": \"uint32\","
  1832. " \"array\": False,"
  1833. " \"record-types\": \"\","
  1834. " \"space\": \"vendor-encapsulated-options-space\","
  1835. " \"encapsulate\": \"\""
  1836. " },"
  1837. " {"
  1838. " \"name\": \"foo2\","
  1839. " \"code\": 2,"
  1840. " \"type\": \"ipv4-address\","
  1841. " \"array\": False,"
  1842. " \"record-types\": \"\","
  1843. " \"space\": \"vendor-encapsulated-options-space\","
  1844. " \"encapsulate\": \"\""
  1845. " } ],"
  1846. "\"subnet4\": [ { "
  1847. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  1848. " \"subnet\": \"192.0.2.0/24\""
  1849. " } ]"
  1850. "}";
  1851. json = Element::fromJSON(config);
  1852. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  1853. ASSERT_TRUE(status);
  1854. checkResult(status, 0);
  1855. // Get the subnet.
  1856. Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
  1857. ASSERT_TRUE(subnet);
  1858. // We should have one option available.
  1859. Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp4");
  1860. ASSERT_TRUE(options);
  1861. ASSERT_EQ(1, options->size());
  1862. // Get the option.
  1863. Subnet::OptionDescriptor desc =
  1864. subnet->getOptionDescriptor("dhcp4", DHO_VENDOR_ENCAPSULATED_OPTIONS);
  1865. EXPECT_TRUE(desc.option);
  1866. EXPECT_EQ(DHO_VENDOR_ENCAPSULATED_OPTIONS, desc.option->getType());
  1867. // Option with the code 1 should be added as a sub-option.
  1868. OptionPtr option_foo = desc.option->getOption(1);
  1869. ASSERT_TRUE(option_foo);
  1870. EXPECT_EQ(1, option_foo->getType());
  1871. // This option comprises a single uint32_t value thus it is
  1872. // represented by OptionInt<uint32_t> class. Let's get the
  1873. // object of this type.
  1874. boost::shared_ptr<OptionInt<uint32_t> > option_foo_uint32 =
  1875. boost::dynamic_pointer_cast<OptionInt<uint32_t> >(option_foo);
  1876. ASSERT_TRUE(option_foo_uint32);
  1877. // Validate the value according to the configuration.
  1878. EXPECT_EQ(1234, option_foo_uint32->getValue());
  1879. // Option with the code 2 should be added as a sub-option.
  1880. OptionPtr option_foo2 = desc.option->getOption(2);
  1881. ASSERT_TRUE(option_foo2);
  1882. EXPECT_EQ(2, option_foo2->getType());
  1883. // This option comprises the IPV4 address. Such option is
  1884. // represented by OptionCustom object.
  1885. OptionCustomPtr option_foo2_v4 =
  1886. boost::dynamic_pointer_cast<OptionCustom>(option_foo2);
  1887. ASSERT_TRUE(option_foo2_v4);
  1888. // Get the IP address carried by this option and validate it.
  1889. EXPECT_EQ("192.168.2.1", option_foo2_v4->readAddress().toText());
  1890. // Option with the code 3 should not be added.
  1891. EXPECT_FALSE(desc.option->getOption(3));
  1892. }
  1893. // This test checks if vendor options can be specified in the config file
  1894. // (in hex format), and later retrieved from configured subnet
  1895. TEST_F(Dhcp4ParserTest, vendorOptionsHex) {
  1896. // This configuration string is to configure two options
  1897. // sharing the code 1 and belonging to the different vendor spaces.
  1898. // (different vendor-id values).
  1899. string config = "{ \"interfaces\": [ \"*\" ],"
  1900. "\"rebind-timer\": 2000,"
  1901. "\"renew-timer\": 1000,"
  1902. "\"option-data\": [ {"
  1903. " \"name\": \"option-one\","
  1904. " \"space\": \"vendor-4491\"," // VENDOR_ID_CABLE_LABS = 4491
  1905. " \"code\": 100," // just a random code
  1906. " \"data\": \"AB CDEF0105\","
  1907. " \"csv-format\": False"
  1908. " },"
  1909. " {"
  1910. " \"name\": \"option-two\","
  1911. " \"space\": \"vendor-1234\","
  1912. " \"code\": 100,"
  1913. " \"data\": \"1234\","
  1914. " \"csv-format\": False"
  1915. " } ],"
  1916. "\"subnet4\": [ { "
  1917. " \"pool\": [ \"192.0.2.1-192.0.2.10\" ],"
  1918. " \"subnet\": \"192.0.2.0/24\""
  1919. " } ]"
  1920. "}";
  1921. ConstElementPtr status;
  1922. ElementPtr json = Element::fromJSON(config);
  1923. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  1924. ASSERT_TRUE(status);
  1925. checkResult(status, 0);
  1926. // Options should be now available for the subnet.
  1927. Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
  1928. ASSERT_TRUE(subnet);
  1929. // Try to get the option from the vendor space 4491
  1930. Subnet::OptionDescriptor desc1 = subnet->getVendorOptionDescriptor(VENDOR_ID_CABLE_LABS, 100);
  1931. ASSERT_TRUE(desc1.option);
  1932. EXPECT_EQ(100, desc1.option->getType());
  1933. // Try to get the option from the vendor space 1234
  1934. Subnet::OptionDescriptor desc2 = subnet->getVendorOptionDescriptor(1234, 100);
  1935. ASSERT_TRUE(desc2.option);
  1936. EXPECT_EQ(100, desc1.option->getType());
  1937. // Try to get the non-existing option from the non-existing
  1938. // option space and expect that option is not returned.
  1939. Subnet::OptionDescriptor desc3 = subnet->getVendorOptionDescriptor(5678, 100);
  1940. ASSERT_FALSE(desc3.option);
  1941. }
  1942. // This test checks if vendor options can be specified in the config file,
  1943. // (in csv format), and later retrieved from configured subnet
  1944. TEST_F(Dhcp4ParserTest, vendorOptionsCsv) {
  1945. // This configuration string is to configure two options
  1946. // sharing the code 1 and belonging to the different vendor spaces.
  1947. // (different vendor-id values).
  1948. string config = "{ \"interfaces\": [ \"*\" ],"
  1949. "\"rebind-timer\": 2000,"
  1950. "\"renew-timer\": 1000,"
  1951. "\"option-data\": [ {"
  1952. " \"name\": \"foo\","
  1953. " \"space\": \"vendor-4491\","
  1954. " \"code\": 100,"
  1955. " \"data\": \"this is a string vendor-opt\","
  1956. " \"csv-format\": True"
  1957. " } ],"
  1958. "\"option-def\": [ {"
  1959. " \"name\": \"foo\","
  1960. " \"code\": 100,"
  1961. " \"type\": \"string\","
  1962. " \"array\": False,"
  1963. " \"record-types\": \"\","
  1964. " \"space\": \"vendor-4491\","
  1965. " \"encapsulate\": \"\""
  1966. " } ],"
  1967. "\"subnet4\": [ { "
  1968. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  1969. " \"subnet\": \"192.0.2.0/24\" "
  1970. " } ]"
  1971. "}";
  1972. ConstElementPtr status;
  1973. ElementPtr json = Element::fromJSON(config);
  1974. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  1975. ASSERT_TRUE(status);
  1976. checkResult(status, 0);
  1977. // Options should be now available for the subnet.
  1978. Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
  1979. ASSERT_TRUE(subnet);
  1980. // Try to get the option from the vendor space 4491
  1981. Subnet::OptionDescriptor desc1 = subnet->getVendorOptionDescriptor(VENDOR_ID_CABLE_LABS, 100);
  1982. ASSERT_TRUE(desc1.option);
  1983. EXPECT_EQ(100, desc1.option->getType());
  1984. // Try to get the non-existing option from the non-existing
  1985. // option space and expect that option is not returned.
  1986. Subnet::OptionDescriptor desc2 = subnet->getVendorOptionDescriptor(5678, 100);
  1987. ASSERT_FALSE(desc2.option);
  1988. }
  1989. // Tests of the hooks libraries configuration. All tests have the pre-
  1990. // condition (checked in the test fixture's SetUp() method) that no hooks
  1991. // libraries are loaded at the start of the tests.
  1992. // Helper function to return a configuration containing an arbitrary number
  1993. // of hooks libraries.
  1994. std::string
  1995. buildHooksLibrariesConfig(const std::vector<std::string>& libraries) {
  1996. const string quote("\"");
  1997. // Create the first part of the configuration string.
  1998. string config =
  1999. "{ \"interfaces\": [ \"*\" ],"
  2000. "\"hooks-libraries\": [";
  2001. // Append the libraries (separated by commas if needed)
  2002. for (int i = 0; i < libraries.size(); ++i) {
  2003. if (i > 0) {
  2004. config += string(", ");
  2005. }
  2006. config += (quote + libraries[i] + quote);
  2007. }
  2008. // Append the remainder of the configuration.
  2009. config += string(
  2010. "],"
  2011. "\"rebind-timer\": 2000,"
  2012. "\"renew-timer\": 1000,"
  2013. "\"option-data\": [ {"
  2014. " \"name\": \"dhcp-message\","
  2015. " \"space\": \"dhcp4\","
  2016. " \"code\": 56,"
  2017. " \"data\": \"AB CDEF0105\","
  2018. " \"csv-format\": False"
  2019. " },"
  2020. " {"
  2021. " \"name\": \"foo\","
  2022. " \"space\": \"isc\","
  2023. " \"code\": 56,"
  2024. " \"data\": \"1234\","
  2025. " \"csv-format\": True"
  2026. " } ],"
  2027. "\"option-def\": [ {"
  2028. " \"name\": \"foo\","
  2029. " \"code\": 56,"
  2030. " \"type\": \"uint32\","
  2031. " \"array\": False,"
  2032. " \"record-types\": \"\","
  2033. " \"space\": \"isc\","
  2034. " \"encapsulate\": \"\""
  2035. " } ],"
  2036. "\"subnet4\": [ { "
  2037. " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
  2038. " \"subnet\": \"192.0.2.0/24\""
  2039. " } ]"
  2040. "}");
  2041. return (config);
  2042. }
  2043. // Convenience function for creating hooks library configuration with one or
  2044. // two character string constants.
  2045. std::string
  2046. buildHooksLibrariesConfig(const char* library1 = NULL,
  2047. const char* library2 = NULL) {
  2048. std::vector<std::string> libraries;
  2049. if (library1 != NULL) {
  2050. libraries.push_back(string(library1));
  2051. if (library2 != NULL) {
  2052. libraries.push_back(string(library2));
  2053. }
  2054. }
  2055. return (buildHooksLibrariesConfig(libraries));
  2056. }
  2057. // The goal of this test is to verify the configuration of hooks libraries if
  2058. // none are specified.
  2059. TEST_F(Dhcp4ParserTest, NoHooksLibraries) {
  2060. // Parse a configuration containing no names.
  2061. string config = buildHooksLibrariesConfig();
  2062. if (!executeConfiguration(config,
  2063. "set configuration with no hooks libraries")) {
  2064. FAIL() << "Unable to execute configuration";
  2065. } else {
  2066. // No libraries should be loaded at the end of the test.
  2067. std::vector<std::string> libraries = HooksManager::getLibraryNames();
  2068. EXPECT_TRUE(libraries.empty());
  2069. }
  2070. }
  2071. // Verify parsing fails with one library that will fail validation.
  2072. TEST_F(Dhcp4ParserTest, InvalidLibrary) {
  2073. // Parse a configuration containing a failing library.
  2074. string config = buildHooksLibrariesConfig(NOT_PRESENT_LIBRARY);
  2075. ConstElementPtr status;
  2076. ElementPtr json = Element::fromJSON(config);
  2077. ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  2078. // The status object must not be NULL
  2079. ASSERT_TRUE(status);
  2080. // Returned value should not be 0
  2081. comment_ = parseAnswer(rcode_, status);
  2082. EXPECT_NE(0, rcode_);
  2083. }
  2084. // Verify the configuration of hooks libraries with two being specified.
  2085. TEST_F(Dhcp4ParserTest, LibrariesSpecified) {
  2086. // Marker files should not be present.
  2087. EXPECT_FALSE(checkMarkerFileExists(LOAD_MARKER_FILE));
  2088. EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
  2089. // Set up the configuration with two libraries and load them.
  2090. string config = buildHooksLibrariesConfig(CALLOUT_LIBRARY_1,
  2091. CALLOUT_LIBRARY_2);
  2092. ASSERT_TRUE(executeConfiguration(config,
  2093. "load two valid libraries"));
  2094. // Expect two libraries to be loaded in the correct order (load marker file
  2095. // is present, no unload marker file).
  2096. std::vector<std::string> libraries = HooksManager::getLibraryNames();
  2097. ASSERT_EQ(2, libraries.size());
  2098. EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "12"));
  2099. EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
  2100. // Unload the libraries. The load file should not have changed, but
  2101. // the unload one should indicate the unload() functions have been run.
  2102. config = buildHooksLibrariesConfig();
  2103. ASSERT_TRUE(executeConfiguration(config, "unloading libraries"));
  2104. EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "12"));
  2105. EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "21"));
  2106. // Expect the hooks system to say that none are loaded.
  2107. libraries = HooksManager::getLibraryNames();
  2108. EXPECT_TRUE(libraries.empty());
  2109. }
  2110. // This test verifies that it is possible to select subset of interfaces
  2111. // on which server should listen.
  2112. TEST_F(Dhcp4ParserTest, selectedInterfaces) {
  2113. ConstElementPtr x;
  2114. string config = "{ \"interfaces\": [ \"eth0\", \"eth1\" ],"
  2115. "\"rebind-timer\": 2000, "
  2116. "\"renew-timer\": 1000, "
  2117. "\"valid-lifetime\": 4000 }";
  2118. ElementPtr json = Element::fromJSON(config);
  2119. ConstElementPtr status;
  2120. // Make sure the config manager is clean and there is no hanging
  2121. // interface configuration.
  2122. ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth0"));
  2123. ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth1"));
  2124. ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth2"));
  2125. // Apply configuration.
  2126. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  2127. ASSERT_TRUE(status);
  2128. checkResult(status, 0);
  2129. // eth0 and eth1 were explicitly selected. eth2 was not.
  2130. EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth0"));
  2131. EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth1"));
  2132. EXPECT_FALSE(CfgMgr::instance().isActiveIface("eth2"));
  2133. }
  2134. // This test verifies that it is possible to configure the server in such a way
  2135. // that it listens on all interfaces.
  2136. TEST_F(Dhcp4ParserTest, allInterfaces) {
  2137. ConstElementPtr x;
  2138. // This configuration specifies two interfaces on which server should listen
  2139. // but it also includes asterisk. The asterisk switches server into the
  2140. // mode when it listens on all interfaces regardless of what interface names
  2141. // were specified in the "interfaces" parameter.
  2142. string config = "{ \"interfaces\": [ \"eth0\", \"*\", \"eth1\" ],"
  2143. "\"rebind-timer\": 2000, "
  2144. "\"renew-timer\": 1000, "
  2145. "\"valid-lifetime\": 4000 }";
  2146. ElementPtr json = Element::fromJSON(config);
  2147. ConstElementPtr status;
  2148. // Make sure there is no old configuration.
  2149. ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth0"));
  2150. ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth1"));
  2151. ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth2"));
  2152. // Apply configuration.
  2153. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  2154. ASSERT_TRUE(status);
  2155. checkResult(status, 0);
  2156. // All interfaces should be now active.
  2157. EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth0"));
  2158. EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth1"));
  2159. EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth2"));
  2160. }
  2161. }