config_parser_unittest.cc 109 KB

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