hooks_unittest.cc 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562
  1. // Copyright (C) 2015 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 <dhcp4/tests/dhcp4_test_utils.h>
  16. #include <dhcp4/json_config_parser.h>
  17. #include <cc/command_interpreter.h>
  18. #include <config/command_mgr.h>
  19. #include <hooks/server_hooks.h>
  20. #include <hooks/hooks_manager.h>
  21. #include <dhcpsrv/cfgmgr.h>
  22. #include <dhcp/tests/iface_mgr_test_config.h>
  23. #include <dhcp/option.h>
  24. #include <asiolink/io_address.h>
  25. #include <vector>
  26. using namespace std;
  27. using namespace isc::asiolink;
  28. using namespace isc::data;
  29. using namespace isc::hooks;
  30. using namespace isc::config;
  31. using namespace isc::dhcp::test;
  32. using namespace isc::dhcp;
  33. // Checks if hooks are registered properly.
  34. TEST_F(Dhcpv4SrvTest, Hooks) {
  35. NakedDhcpv4Srv srv(0);
  36. // check if appropriate hooks are registered
  37. int hook_index_buffer4_receive = -1;
  38. int hook_index_pkt4_receive = -1;
  39. int hook_index_select_subnet = -1;
  40. int hook_index_lease4_release = -1;
  41. int hook_index_pkt4_send = -1;
  42. int hook_index_buffer4_send = -1;
  43. // check if appropriate indexes are set
  44. EXPECT_NO_THROW(hook_index_buffer4_receive = ServerHooks::getServerHooks()
  45. .getIndex("buffer4_receive"));
  46. EXPECT_NO_THROW(hook_index_pkt4_receive = ServerHooks::getServerHooks()
  47. .getIndex("pkt4_receive"));
  48. EXPECT_NO_THROW(hook_index_select_subnet = ServerHooks::getServerHooks()
  49. .getIndex("subnet4_select"));
  50. EXPECT_NO_THROW(hook_index_lease4_release = ServerHooks::getServerHooks()
  51. .getIndex("lease4_release"));
  52. EXPECT_NO_THROW(hook_index_pkt4_send = ServerHooks::getServerHooks()
  53. .getIndex("pkt4_send"));
  54. EXPECT_NO_THROW(hook_index_buffer4_send = ServerHooks::getServerHooks()
  55. .getIndex("buffer4_send"));
  56. EXPECT_TRUE(hook_index_buffer4_receive > 0);
  57. EXPECT_TRUE(hook_index_pkt4_receive > 0);
  58. EXPECT_TRUE(hook_index_select_subnet > 0);
  59. EXPECT_TRUE(hook_index_lease4_release > 0);
  60. EXPECT_TRUE(hook_index_pkt4_send > 0);
  61. EXPECT_TRUE(hook_index_buffer4_send > 0);
  62. }
  63. // A dummy MAC address, padded with 0s
  64. const uint8_t dummyChaddr[16] = {0, 1, 2, 3, 4, 5, 0, 0,
  65. 0, 0, 0, 0, 0, 0, 0, 0 };
  66. // Let's use some creative test content here (128 chars + \0)
  67. const uint8_t dummyFile[] = "Lorem ipsum dolor sit amet, consectetur "
  68. "adipiscing elit. Proin mollis placerat metus, at "
  69. "lacinia orci ornare vitae. Mauris amet.";
  70. // Yet another type of test content (64 chars + \0)
  71. const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur "
  72. "adipiscing elit posuere.";
  73. /// @brief a class dedicated to Hooks testing in DHCPv4 server
  74. ///
  75. /// This class has a number of static members, because each non-static
  76. /// method has implicit 'this' parameter, so it does not match callout
  77. /// signature and couldn't be registered. Furthermore, static methods
  78. /// can't modify non-static members (for obvious reasons), so many
  79. /// fields are declared static. It is still better to keep them as
  80. /// one class rather than unrelated collection of global objects.
  81. class HooksDhcpv4SrvTest : public Dhcpv4SrvTest {
  82. public:
  83. /// @brief creates Dhcpv4Srv and prepares buffers for callouts
  84. HooksDhcpv4SrvTest() {
  85. // Allocate new DHCPv6 Server
  86. srv_ = new NakedDhcpv4Srv(0);
  87. // clear static buffers
  88. resetCalloutBuffers();
  89. }
  90. /// @brief destructor (deletes Dhcpv4Srv)
  91. virtual ~HooksDhcpv4SrvTest() {
  92. HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("buffer4_receive");
  93. HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("buffer4_send");
  94. HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("pkt4_receive");
  95. HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("pkt4_send");
  96. HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("subnet4_select");
  97. HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease4_renew");
  98. HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease4_release");
  99. HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease4_decline");
  100. delete srv_;
  101. }
  102. /// @brief creates an option with specified option code
  103. ///
  104. /// This method is static, because it is used from callouts
  105. /// that do not have a pointer to HooksDhcpv4SSrvTest object
  106. ///
  107. /// @param option_code code of option to be created
  108. ///
  109. /// @return pointer to create option object
  110. static OptionPtr createOption(uint16_t option_code) {
  111. char payload[] = {
  112. 0xa, 0xb, 0xc, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14
  113. };
  114. OptionBuffer tmp(payload, payload + sizeof(payload));
  115. return OptionPtr(new Option(Option::V4, option_code, tmp));
  116. }
  117. /// @brief Generates test packet.
  118. ///
  119. /// Allocates and generates on-wire buffer that represents test packet, with all
  120. /// fixed fields set to non-zero values. Content is not always reasonable.
  121. ///
  122. /// See generateTestPacket1() function that returns exactly the same packet as
  123. /// Pkt4 object.
  124. ///
  125. /// @return pointer to allocated Pkt4 object
  126. // Returns a vector containing a DHCPv4 packet header.
  127. Pkt4Ptr
  128. generateSimpleDiscover() {
  129. // That is only part of the header. It contains all "short" fields,
  130. // larger fields are constructed separately.
  131. uint8_t hdr[] = {
  132. 1, 6, 6, 13, // op, htype, hlen, hops,
  133. 0x12, 0x34, 0x56, 0x78, // transaction-id
  134. 0, 42, 0x80, 0x00, // 42 secs, BROADCAST flags
  135. 192, 0, 2, 1, // ciaddr
  136. 1, 2, 3, 4, // yiaddr
  137. 192, 0, 2, 255, // siaddr
  138. 192, 0, 2, 50, // giaddr
  139. };
  140. // Initialize the vector with the header fields defined above.
  141. vector<uint8_t> buf(hdr, hdr + sizeof(hdr));
  142. // Append the large header fields.
  143. copy(dummyChaddr, dummyChaddr + Pkt4::MAX_CHADDR_LEN, back_inserter(buf));
  144. copy(dummySname, dummySname + Pkt4::MAX_SNAME_LEN, back_inserter(buf));
  145. copy(dummyFile, dummyFile + Pkt4::MAX_FILE_LEN, back_inserter(buf));
  146. // Should now have all the header, so check. The "static_cast" is used
  147. // to get round an odd bug whereby the linker appears not to find the
  148. // definition of DHCPV4_PKT_HDR_LEN if it appears within an EXPECT_EQ().
  149. EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), buf.size());
  150. // Add magic cookie
  151. buf.push_back(0x63);
  152. buf.push_back(0x82);
  153. buf.push_back(0x53);
  154. buf.push_back(0x63);
  155. // Add message type DISCOVER
  156. buf.push_back(static_cast<uint8_t>(DHO_DHCP_MESSAGE_TYPE));
  157. buf.push_back(1); // length (just one byte)
  158. buf.push_back(static_cast<uint8_t>(DHCPDISCOVER));
  159. Pkt4Ptr dis(new Pkt4(&buf[0], buf.size()));
  160. // Interface must be selected for a Discover. Server will use the interface
  161. // name to select a subnet for a client. This test is using fake interfaces
  162. // and the fake eth0 interface has IPv4 address matching the subnet
  163. // currently configured for this test.
  164. dis->setIface("eth1");
  165. return (dis);
  166. }
  167. /// Test callback that stores received callout name and pkt4 value
  168. /// @param callout_handle handle passed by the hooks framework
  169. /// @return always 0
  170. static int
  171. buffer4_receive_callout(CalloutHandle& callout_handle) {
  172. callback_name_ = string("buffer4_receive");
  173. callout_handle.getArgument("query4", callback_pkt4_);
  174. callback_argument_names_ = callout_handle.getArgumentNames();
  175. return (0);
  176. }
  177. /// Test callback that changes hwaddr value
  178. /// @param callout_handle handle passed by the hooks framework
  179. /// @return always 0
  180. static int
  181. buffer4_receive_change_hwaddr(CalloutHandle& callout_handle) {
  182. Pkt4Ptr pkt;
  183. callout_handle.getArgument("query4", pkt);
  184. // If there is at least one option with data
  185. if (pkt->data_.size() >= Pkt4::DHCPV4_PKT_HDR_LEN) {
  186. // Offset of the first byte of the CHWADDR field. Let's the first
  187. // byte to some new value that we could later check
  188. pkt->data_[28] = 0xff;
  189. }
  190. // Carry on as usual
  191. return buffer4_receive_callout(callout_handle);
  192. }
  193. /// Test callback that sets skip flag
  194. /// @param callout_handle handle passed by the hooks framework
  195. /// @return always 0
  196. static int
  197. buffer4_receive_skip(CalloutHandle& callout_handle) {
  198. callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
  199. // Carry on as usual
  200. return buffer4_receive_callout(callout_handle);
  201. }
  202. /// test callback that stores received callout name and pkt4 value
  203. /// @param callout_handle handle passed by the hooks framework
  204. /// @return always 0
  205. static int
  206. pkt4_receive_callout(CalloutHandle& callout_handle) {
  207. callback_name_ = string("pkt4_receive");
  208. callout_handle.getArgument("query4", callback_pkt4_);
  209. callback_argument_names_ = callout_handle.getArgumentNames();
  210. return (0);
  211. }
  212. /// test callback that changes client-id value
  213. /// @param callout_handle handle passed by the hooks framework
  214. /// @return always 0
  215. static int
  216. pkt4_receive_change_clientid(CalloutHandle& callout_handle) {
  217. Pkt4Ptr pkt;
  218. callout_handle.getArgument("query4", pkt);
  219. // get rid of the old client-id
  220. pkt->delOption(DHO_DHCP_CLIENT_IDENTIFIER);
  221. // add a new option
  222. pkt->addOption(createOption(DHO_DHCP_CLIENT_IDENTIFIER));
  223. // carry on as usual
  224. return pkt4_receive_callout(callout_handle);
  225. }
  226. /// test callback that deletes client-id
  227. /// @param callout_handle handle passed by the hooks framework
  228. /// @return always 0
  229. static int
  230. pkt4_receive_delete_clientid(CalloutHandle& callout_handle) {
  231. Pkt4Ptr pkt;
  232. callout_handle.getArgument("query4", pkt);
  233. // get rid of the old client-id (and no HWADDR)
  234. vector<uint8_t> mac;
  235. pkt->delOption(DHO_DHCP_CLIENT_IDENTIFIER);
  236. pkt->setHWAddr(1, 0, mac); // HWtype 1, hardware len = 0
  237. // carry on as usual
  238. return pkt4_receive_callout(callout_handle);
  239. }
  240. /// test callback that sets skip flag
  241. /// @param callout_handle handle passed by the hooks framework
  242. /// @return always 0
  243. static int
  244. pkt4_receive_skip(CalloutHandle& callout_handle) {
  245. Pkt4Ptr pkt;
  246. callout_handle.getArgument("query4", pkt);
  247. callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
  248. // carry on as usual
  249. return pkt4_receive_callout(callout_handle);
  250. }
  251. /// Test callback that stores received callout name and pkt4 value
  252. /// @param callout_handle handle passed by the hooks framework
  253. /// @return always 0
  254. static int
  255. pkt4_send_callout(CalloutHandle& callout_handle) {
  256. callback_name_ = string("pkt4_send");
  257. callout_handle.getArgument("response4", callback_pkt4_);
  258. callback_argument_names_ = callout_handle.getArgumentNames();
  259. return (0);
  260. }
  261. // Test callback that changes server-id
  262. /// @param callout_handle handle passed by the hooks framework
  263. /// @return always 0
  264. static int
  265. pkt4_send_change_serverid(CalloutHandle& callout_handle) {
  266. Pkt4Ptr pkt;
  267. callout_handle.getArgument("response4", pkt);
  268. // get rid of the old server-id
  269. pkt->delOption(DHO_DHCP_SERVER_IDENTIFIER);
  270. // add a new option
  271. pkt->addOption(createOption(DHO_DHCP_SERVER_IDENTIFIER));
  272. // carry on as usual
  273. return pkt4_send_callout(callout_handle);
  274. }
  275. /// test callback that deletes server-id
  276. /// @param callout_handle handle passed by the hooks framework
  277. /// @return always 0
  278. static int
  279. pkt4_send_delete_serverid(CalloutHandle& callout_handle) {
  280. Pkt4Ptr pkt;
  281. callout_handle.getArgument("response4", pkt);
  282. // get rid of the old client-id
  283. pkt->delOption(DHO_DHCP_SERVER_IDENTIFIER);
  284. // carry on as usual
  285. return pkt4_send_callout(callout_handle);
  286. }
  287. /// Test callback that sets skip flag
  288. /// @param callout_handle handle passed by the hooks framework
  289. /// @return always 0
  290. static int
  291. pkt4_send_skip(CalloutHandle& callout_handle) {
  292. Pkt4Ptr pkt;
  293. callout_handle.getArgument("response4", pkt);
  294. callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
  295. // carry on as usual
  296. return pkt4_send_callout(callout_handle);
  297. }
  298. /// Test callback that stores received callout name and pkt4 value
  299. /// @param callout_handle handle passed by the hooks framework
  300. /// @return always 0
  301. static int
  302. buffer4_send_callout(CalloutHandle& callout_handle) {
  303. callback_name_ = string("buffer4_send");
  304. callout_handle.getArgument("response4", callback_pkt4_);
  305. callback_argument_names_ = callout_handle.getArgumentNames();
  306. return (0);
  307. }
  308. /// Test callback changes the output buffer to a hardcoded value
  309. /// @param callout_handle handle passed by the hooks framework
  310. /// @return always 0
  311. static int
  312. buffer4_send_change_callout(CalloutHandle& callout_handle) {
  313. Pkt4Ptr pkt;
  314. callout_handle.getArgument("response4", pkt);
  315. // modify buffer to set a different payload
  316. pkt->getBuffer().clear();
  317. pkt->getBuffer().writeData(dummyFile, sizeof(dummyFile));
  318. return (0);
  319. }
  320. /// Test callback that stores received callout name and pkt4 value
  321. /// @param callout_handle handle passed by the hooks framework
  322. /// @return always 0
  323. static int
  324. skip_callout(CalloutHandle& callout_handle) {
  325. callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
  326. return (0);
  327. }
  328. /// Test callback that stores received callout name and subnet4 values
  329. /// @param callout_handle handle passed by the hooks framework
  330. /// @return always 0
  331. static int
  332. subnet4_select_callout(CalloutHandle& callout_handle) {
  333. callback_name_ = string("subnet4_select");
  334. callout_handle.getArgument("query4", callback_pkt4_);
  335. callout_handle.getArgument("subnet4", callback_subnet4_);
  336. callout_handle.getArgument("subnet4collection", callback_subnet4collection_);
  337. callback_argument_names_ = callout_handle.getArgumentNames();
  338. return (0);
  339. }
  340. /// Test callback that picks the other subnet if possible.
  341. /// @param callout_handle handle passed by the hooks framework
  342. /// @return always 0
  343. static int
  344. subnet4_select_different_subnet_callout(CalloutHandle& callout_handle) {
  345. // Call the basic callout to record all passed values
  346. subnet4_select_callout(callout_handle);
  347. const Subnet4Collection* subnets;
  348. Subnet4Ptr subnet;
  349. callout_handle.getArgument("subnet4", subnet);
  350. callout_handle.getArgument("subnet4collection", subnets);
  351. // Let's change to a different subnet
  352. if (subnets->size() > 1) {
  353. subnet = (*subnets)[1]; // Let's pick the other subnet
  354. callout_handle.setArgument("subnet4", subnet);
  355. }
  356. return (0);
  357. }
  358. /// Test callback that stores received callout name passed parameters
  359. /// @param callout_handle handle passed by the hooks framework
  360. /// @return always 0
  361. static int
  362. lease4_release_callout(CalloutHandle& callout_handle) {
  363. callback_name_ = string("lease4_release");
  364. callout_handle.getArgument("query4", callback_pkt4_);
  365. callout_handle.getArgument("lease4", callback_lease4_);
  366. callback_argument_names_ = callout_handle.getArgumentNames();
  367. return (0);
  368. }
  369. /// Test callback that stores received callout name and subnet4 values
  370. /// @param callout_handle handle passed by the hooks framework
  371. /// @return always 0
  372. static int
  373. lease4_renew_callout(CalloutHandle& callout_handle) {
  374. callback_name_ = string("lease4_renew");
  375. callout_handle.getArgument("subnet4", callback_subnet4_);
  376. callout_handle.getArgument("lease4", callback_lease4_);
  377. callout_handle.getArgument("hwaddr", callback_hwaddr_);
  378. callout_handle.getArgument("clientid", callback_clientid_);
  379. callback_argument_names_ = callout_handle.getArgumentNames();
  380. return (0);
  381. }
  382. /// Test lease4_decline callback that stores received parameters.
  383. ///
  384. /// @param callout_handle handle passed by the hooks framework
  385. /// @return always 0
  386. static int
  387. lease4_decline_callout(CalloutHandle& callout_handle) {
  388. callback_name_ = string("lease4_decline");
  389. callout_handle.getArgument("query4", callback_pkt4_);
  390. callout_handle.getArgument("lease4", callback_lease4_);
  391. return (0);
  392. }
  393. /// Test lease4_decline callback that sets next step to DROP.
  394. ///
  395. /// @param callout_handle handle passed by the hooks framework
  396. /// @return always 0
  397. static int
  398. lease4_decline_drop_callout(CalloutHandle& callout_handle) {
  399. callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
  400. return (lease4_decline_callout(callout_handle));
  401. }
  402. /// resets buffers used to store data received by callouts
  403. void resetCalloutBuffers() {
  404. callback_name_ = string("");
  405. callback_pkt4_.reset();
  406. callback_lease4_.reset();
  407. callback_hwaddr_.reset();
  408. callback_clientid_.reset();
  409. callback_subnet4_.reset();
  410. callback_subnet4collection_ = NULL;
  411. callback_argument_names_.clear();
  412. }
  413. /// pointer to Dhcpv4Srv that is used in tests
  414. NakedDhcpv4Srv* srv_;
  415. // The following fields are used in testing pkt4_receive_callout
  416. /// String name of the received callout
  417. static string callback_name_;
  418. /// Pkt4 structure returned in the callout
  419. static Pkt4Ptr callback_pkt4_;
  420. /// Lease4 structure returned in the callout
  421. static Lease4Ptr callback_lease4_;
  422. /// Hardware address returned in the callout
  423. static HWAddrPtr callback_hwaddr_;
  424. /// Client-id returned in the callout
  425. static ClientIdPtr callback_clientid_;
  426. /// Pointer to a subnet received by callout
  427. static Subnet4Ptr callback_subnet4_;
  428. /// A list of all available subnets (received by callout)
  429. static const Subnet4Collection* callback_subnet4collection_;
  430. /// A list of all received arguments
  431. static vector<string> callback_argument_names_;
  432. };
  433. // The following fields are used in testing pkt4_receive_callout.
  434. // See fields description in the class for details
  435. string HooksDhcpv4SrvTest::callback_name_;
  436. Pkt4Ptr HooksDhcpv4SrvTest::callback_pkt4_;
  437. Subnet4Ptr HooksDhcpv4SrvTest::callback_subnet4_;
  438. HWAddrPtr HooksDhcpv4SrvTest::callback_hwaddr_;
  439. ClientIdPtr HooksDhcpv4SrvTest::callback_clientid_;
  440. Lease4Ptr HooksDhcpv4SrvTest::callback_lease4_;
  441. const Subnet4Collection* HooksDhcpv4SrvTest::callback_subnet4collection_;
  442. vector<string> HooksDhcpv4SrvTest::callback_argument_names_;
  443. // Checks if callouts installed on pkt4_receive are indeed called and the
  444. // all necessary parameters are passed.
  445. //
  446. // Note that the test name does not follow test naming convention,
  447. // but the proper hook name is "buffer4_receive".
  448. TEST_F(HooksDhcpv4SrvTest, Buffer4ReceiveSimple) {
  449. IfaceMgrTestConfig test_config(true);
  450. IfaceMgr::instance().openSockets4();
  451. // Install pkt4_receive_callout
  452. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  453. "buffer4_receive", buffer4_receive_callout));
  454. // Let's create a simple DISCOVER
  455. Pkt4Ptr dis = generateSimpleDiscover();
  456. // Simulate that we have received that traffic
  457. srv_->fakeReceive(dis);
  458. // Server will now process to run its normal loop, but instead of calling
  459. // IfaceMgr::receive4(), it will read all packets from the list set by
  460. // fakeReceive()
  461. // In particular, it should call registered buffer4_receive callback.
  462. srv_->run();
  463. // Check that the callback called is indeed the one we installed
  464. EXPECT_EQ("buffer4_receive", callback_name_);
  465. // Check that pkt4 argument passing was successful and returned proper value
  466. EXPECT_TRUE(callback_pkt4_.get() == dis.get());
  467. // Check that all expected parameters are there
  468. vector<string> expected_argument_names;
  469. expected_argument_names.push_back(string("query4"));
  470. EXPECT_TRUE(expected_argument_names == callback_argument_names_);
  471. }
  472. // Checks if callouts installed on buffer4_receive is able to change
  473. // the values and the parameters are indeed used by the server.
  474. TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveValueChange) {
  475. IfaceMgrTestConfig test_config(true);
  476. IfaceMgr::instance().openSockets4();
  477. // Install callback that modifies MAC addr of incoming packet
  478. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  479. "buffer4_receive", buffer4_receive_change_hwaddr));
  480. // Let's create a simple DISCOVER
  481. Pkt4Ptr discover = generateSimpleDiscover();
  482. // Simulate that we have received that traffic
  483. srv_->fakeReceive(discover);
  484. // Server will now process to run its normal loop, but instead of calling
  485. // IfaceMgr::receive6(), it will read all packets from the list set by
  486. // fakeReceive()
  487. // In particular, it should call registered buffer4_receive callback.
  488. srv_->run();
  489. // Check that the server did send a response
  490. ASSERT_EQ(1, srv_->fake_sent_.size());
  491. // Make sure that we received a response
  492. Pkt4Ptr offer = srv_->fake_sent_.front();
  493. ASSERT_TRUE(offer);
  494. // Get client-id...
  495. HWAddrPtr hwaddr = offer->getHWAddr();
  496. ASSERT_TRUE(hwaddr); // basic sanity check. HWAddr is always present
  497. // ... and check if it is the modified value
  498. ASSERT_FALSE(hwaddr->hwaddr_.empty()); // there must be a MAC address
  499. EXPECT_EQ(0xff, hwaddr->hwaddr_[0]); // check that its first byte was modified
  500. }
  501. // Checks if callouts installed on buffer4_receive is able to set skip flag that
  502. // will cause the server to not parse the packet. Even though the packet is valid,
  503. // the server should eventually drop it, because there won't be mandatory options
  504. // (or rather option objects) in it.
  505. TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveSkip) {
  506. IfaceMgrTestConfig test_config(true);
  507. IfaceMgr::instance().openSockets4();
  508. // Install pkt4_receive_callout
  509. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  510. "buffer4_receive", buffer4_receive_skip));
  511. // Let's create a simple DISCOVER
  512. Pkt4Ptr discover = generateSimpleDiscover();
  513. // Simulate that we have received that traffic
  514. srv_->fakeReceive(discover);
  515. // Server will now process to run its normal loop, but instead of calling
  516. // IfaceMgr::receive6(), it will read all packets from the list set by
  517. // fakeReceive()
  518. // In particular, it should call registered pkt4_receive callback.
  519. srv_->run();
  520. // Check that the server dropped the packet and did not produce any response
  521. ASSERT_EQ(0, srv_->fake_sent_.size());
  522. }
  523. // Checks if callouts installed on pkt4_receive are indeed called and the
  524. // all necessary parameters are passed.
  525. //
  526. // Note that the test name does not follow test naming convention,
  527. // but the proper hook name is "pkt4_receive".
  528. TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSimple) {
  529. IfaceMgrTestConfig test_config(true);
  530. IfaceMgr::instance().openSockets4();
  531. // Install pkt4_receive_callout
  532. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  533. "pkt4_receive", pkt4_receive_callout));
  534. // Let's create a simple DISCOVER
  535. Pkt4Ptr sol = generateSimpleDiscover();
  536. // Simulate that we have received that traffic
  537. srv_->fakeReceive(sol);
  538. // Server will now process to run its normal loop, but instead of calling
  539. // IfaceMgr::receive4(), it will read all packets from the list set by
  540. // fakeReceive()
  541. // In particular, it should call registered pkt4_receive callback.
  542. srv_->run();
  543. // check that the callback called is indeed the one we installed
  544. EXPECT_EQ("pkt4_receive", callback_name_);
  545. // check that pkt4 argument passing was successful and returned proper value
  546. EXPECT_TRUE(callback_pkt4_.get() == sol.get());
  547. // Check that all expected parameters are there
  548. vector<string> expected_argument_names;
  549. expected_argument_names.push_back(string("query4"));
  550. EXPECT_TRUE(expected_argument_names == callback_argument_names_);
  551. }
  552. // Checks if callouts installed on pkt4_received is able to change
  553. // the values and the parameters are indeed used by the server.
  554. TEST_F(HooksDhcpv4SrvTest, valueChange_pkt4_receive) {
  555. IfaceMgrTestConfig test_config(true);
  556. IfaceMgr::instance().openSockets4();
  557. // Install pkt4_receive_callout
  558. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  559. "pkt4_receive", pkt4_receive_change_clientid));
  560. // Let's create a simple DISCOVER
  561. Pkt4Ptr sol = generateSimpleDiscover();
  562. // Simulate that we have received that traffic
  563. srv_->fakeReceive(sol);
  564. // Server will now process to run its normal loop, but instead of calling
  565. // IfaceMgr::receive4(), it will read all packets from the list set by
  566. // fakeReceive()
  567. // In particular, it should call registered pkt4_receive callback.
  568. srv_->run();
  569. // check that the server did send a response
  570. ASSERT_EQ(1, srv_->fake_sent_.size());
  571. // Make sure that we received a response
  572. Pkt4Ptr adv = srv_->fake_sent_.front();
  573. ASSERT_TRUE(adv);
  574. // Get client-id...
  575. OptionPtr clientid = adv->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
  576. // ... and check if it is the modified value
  577. OptionPtr expected = createOption(DHO_DHCP_CLIENT_IDENTIFIER);
  578. EXPECT_TRUE(clientid->equals(expected));
  579. }
  580. // Checks if callouts installed on pkt4_received is able to delete
  581. // existing options and that change impacts server processing (mandatory
  582. // client-id option is deleted, so the packet is expected to be dropped)
  583. TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveDeleteClientId) {
  584. IfaceMgrTestConfig test_config(true);
  585. IfaceMgr::instance().openSockets4();
  586. // Install pkt4_receive_callout
  587. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  588. "pkt4_receive", pkt4_receive_delete_clientid));
  589. // Let's create a simple DISCOVER
  590. Pkt4Ptr sol = generateSimpleDiscover();
  591. // Simulate that we have received that traffic
  592. srv_->fakeReceive(sol);
  593. // Server will now process to run its normal loop, but instead of calling
  594. // IfaceMgr::receive4(), it will read all packets from the list set by
  595. // fakeReceive()
  596. // In particular, it should call registered pkt4_receive callback.
  597. srv_->run();
  598. // Check that the server dropped the packet and did not send a response
  599. ASSERT_EQ(0, srv_->fake_sent_.size());
  600. }
  601. // Checks if callouts installed on pkt4_received is able to set skip flag that
  602. // will cause the server to not process the packet (drop), even though it is valid.
  603. TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSkip) {
  604. IfaceMgrTestConfig test_config(true);
  605. IfaceMgr::instance().openSockets4();
  606. // Install pkt4_receive_callout
  607. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  608. "pkt4_receive", pkt4_receive_skip));
  609. // Let's create a simple DISCOVER
  610. Pkt4Ptr sol = generateSimpleDiscover();
  611. // Simulate that we have received that traffic
  612. srv_->fakeReceive(sol);
  613. // Server will now process to run its normal loop, but instead of calling
  614. // IfaceMgr::receive4(), it will read all packets from the list set by
  615. // fakeReceive()
  616. // In particular, it should call registered pkt4_receive callback.
  617. srv_->run();
  618. // check that the server dropped the packet and did not produce any response
  619. ASSERT_EQ(0, srv_->fake_sent_.size());
  620. }
  621. // Checks if callouts installed on pkt4_send are indeed called and the
  622. // all necessary parameters are passed.
  623. TEST_F(HooksDhcpv4SrvTest, pkt4SendSimple) {
  624. IfaceMgrTestConfig test_config(true);
  625. IfaceMgr::instance().openSockets4();
  626. // Install pkt4_receive_callout
  627. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  628. "pkt4_send", pkt4_send_callout));
  629. // Let's create a simple DISCOVER
  630. Pkt4Ptr sol = generateSimpleDiscover();
  631. // Simulate that we have received that traffic
  632. srv_->fakeReceive(sol);
  633. // Server will now process to run its normal loop, but instead of calling
  634. // IfaceMgr::receive4(), it will read all packets from the list set by
  635. // fakeReceive()
  636. // In particular, it should call registered pkt4_receive callback.
  637. srv_->run();
  638. // Check that the callback called is indeed the one we installed
  639. EXPECT_EQ("pkt4_send", callback_name_);
  640. // Check that there is one packet sent
  641. ASSERT_EQ(1, srv_->fake_sent_.size());
  642. Pkt4Ptr adv = srv_->fake_sent_.front();
  643. // Check that pkt4 argument passing was successful and returned proper value
  644. EXPECT_TRUE(callback_pkt4_.get() == adv.get());
  645. // Check that all expected parameters are there
  646. vector<string> expected_argument_names;
  647. expected_argument_names.push_back(string("response4"));
  648. EXPECT_TRUE(expected_argument_names == callback_argument_names_);
  649. }
  650. // Checks if callouts installed on pkt4_send is able to change
  651. // the values and the packet sent contains those changes
  652. TEST_F(HooksDhcpv4SrvTest, pkt4SendValueChange) {
  653. IfaceMgrTestConfig test_config(true);
  654. IfaceMgr::instance().openSockets4();
  655. // Install pkt4_receive_callout
  656. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  657. "pkt4_send", pkt4_send_change_serverid));
  658. // Let's create a simple DISCOVER
  659. Pkt4Ptr sol = generateSimpleDiscover();
  660. // Simulate that we have received that traffic
  661. srv_->fakeReceive(sol);
  662. // Server will now process to run its normal loop, but instead of calling
  663. // IfaceMgr::receive4(), it will read all packets from the list set by
  664. // fakeReceive()
  665. // In particular, it should call registered pkt4_receive callback.
  666. srv_->run();
  667. // check that the server did send a response
  668. ASSERT_EQ(1, srv_->fake_sent_.size());
  669. // Make sure that we received a response
  670. Pkt4Ptr adv = srv_->fake_sent_.front();
  671. ASSERT_TRUE(adv);
  672. // Get client-id...
  673. OptionPtr clientid = adv->getOption(DHO_DHCP_SERVER_IDENTIFIER);
  674. // ... and check if it is the modified value
  675. OptionPtr expected = createOption(DHO_DHCP_SERVER_IDENTIFIER);
  676. EXPECT_TRUE(clientid->equals(expected));
  677. }
  678. // Checks if callouts installed on pkt4_send is able to delete
  679. // existing options and that server applies those changes. In particular,
  680. // we are trying to send a packet without server-id. The packet should
  681. // be sent
  682. TEST_F(HooksDhcpv4SrvTest, pkt4SendDeleteServerId) {
  683. IfaceMgrTestConfig test_config(true);
  684. IfaceMgr::instance().openSockets4();
  685. // Install pkt4_receive_callout
  686. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  687. "pkt4_send", pkt4_send_delete_serverid));
  688. // Let's create a simple DISCOVER
  689. Pkt4Ptr sol = generateSimpleDiscover();
  690. // Simulate that we have received that traffic
  691. srv_->fakeReceive(sol);
  692. // Server will now process to run its normal loop, but instead of calling
  693. // IfaceMgr::receive4(), it will read all packets from the list set by
  694. // fakeReceive()
  695. // In particular, it should call registered pkt4_receive callback.
  696. srv_->run();
  697. // Check that the server indeed sent a malformed ADVERTISE
  698. ASSERT_EQ(1, srv_->fake_sent_.size());
  699. // Get that ADVERTISE
  700. Pkt4Ptr adv = srv_->fake_sent_.front();
  701. ASSERT_TRUE(adv);
  702. // Make sure that it does not have server-id
  703. EXPECT_FALSE(adv->getOption(DHO_DHCP_SERVER_IDENTIFIER));
  704. }
  705. // Checks if callouts installed on pkt4_skip is able to set skip flag that
  706. // will cause the server to not process the packet (drop), even though it is valid.
  707. TEST_F(HooksDhcpv4SrvTest, skip_pkt4_send) {
  708. IfaceMgrTestConfig test_config(true);
  709. IfaceMgr::instance().openSockets4();
  710. // Install pkt4_receive_callout
  711. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  712. "pkt4_send", pkt4_send_skip));
  713. // Let's create a simple REQUEST
  714. Pkt4Ptr sol = generateSimpleDiscover();
  715. // Simulate that we have received that traffic
  716. srv_->fakeReceive(sol);
  717. // Server will now process to run its normal loop, but instead of calling
  718. // IfaceMgr::receive4(), it will read all packets from the list set by
  719. // fakeReceive()
  720. // In particular, it should call registered pkt4_send callback.
  721. srv_->run();
  722. // Check that the server sent the message
  723. ASSERT_EQ(1, srv_->fake_sent_.size());
  724. // Get the first packet and check that it has zero length (i.e. the server
  725. // did not do packing on its own)
  726. Pkt4Ptr sent = srv_->fake_sent_.front();
  727. EXPECT_EQ(0, sent->getBuffer().getLength());
  728. }
  729. // Checks if callouts installed on buffer4_send are indeed called and the
  730. // all necessary parameters are passed.
  731. TEST_F(HooksDhcpv4SrvTest, buffer4SendSimple) {
  732. IfaceMgrTestConfig test_config(true);
  733. IfaceMgr::instance().openSockets4();
  734. // Install pkt4_receive_callout
  735. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  736. "buffer4_send", buffer4_send_callout));
  737. // Let's create a simple DISCOVER
  738. Pkt4Ptr discover = generateSimpleDiscover();
  739. // Simulate that we have received that traffic
  740. srv_->fakeReceive(discover);
  741. // Server will now process to run its normal loop, but instead of calling
  742. // IfaceMgr::receive4(), it will read all packets from the list set by
  743. // fakeReceive()
  744. // In particular, it should call registered pkt4_receive callback.
  745. srv_->run();
  746. // Check that the callback called is indeed the one we installed
  747. EXPECT_EQ("buffer4_send", callback_name_);
  748. // Check that there is one packet sent
  749. ASSERT_EQ(1, srv_->fake_sent_.size());
  750. Pkt4Ptr adv = srv_->fake_sent_.front();
  751. // Check that pkt4 argument passing was successful and returned proper value
  752. EXPECT_TRUE(callback_pkt4_.get() == adv.get());
  753. // Check that all expected parameters are there
  754. vector<string> expected_argument_names;
  755. expected_argument_names.push_back(string("response4"));
  756. EXPECT_TRUE(expected_argument_names == callback_argument_names_);
  757. }
  758. // Checks if callouts installed on buffer4_send are indeed called and that
  759. // the output buffer can be changed.
  760. TEST_F(HooksDhcpv4SrvTest, buffer4Send) {
  761. IfaceMgrTestConfig test_config(true);
  762. IfaceMgr::instance().openSockets4();
  763. // Install pkt4_receive_callout
  764. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  765. "buffer4_send", buffer4_send_change_callout));
  766. // Let's create a simple DISCOVER
  767. Pkt4Ptr discover = generateSimpleDiscover();
  768. // Simulate that we have received that traffic
  769. srv_->fakeReceive(discover);
  770. // Server will now process to run its normal loop, but instead of calling
  771. // IfaceMgr::receive4(), it will read all packets from the list set by
  772. // fakeReceive()
  773. // In particular, it should call registered pkt4_receive callback.
  774. srv_->run();
  775. // Check that there is one packet sent
  776. ASSERT_EQ(1, srv_->fake_sent_.size());
  777. Pkt4Ptr adv = srv_->fake_sent_.front();
  778. // The callout is supposed to fill the output buffer with dummyFile content
  779. ASSERT_EQ(sizeof(dummyFile), adv->getBuffer().getLength());
  780. EXPECT_EQ(0, memcmp(adv->getBuffer().getData(), dummyFile, sizeof(dummyFile)));
  781. }
  782. // Checks if callouts installed on buffer4_send can set skip flag and that flag
  783. // causes the packet to not be sent
  784. TEST_F(HooksDhcpv4SrvTest, buffer4SendSkip) {
  785. IfaceMgrTestConfig test_config(true);
  786. IfaceMgr::instance().openSockets4();
  787. // Install pkt4_receive_callout
  788. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  789. "buffer4_send", skip_callout));
  790. // Let's create a simple DISCOVER
  791. Pkt4Ptr discover = generateSimpleDiscover();
  792. // Simulate that we have received that traffic
  793. srv_->fakeReceive(discover);
  794. // Server will now process to run its normal loop, but instead of calling
  795. // IfaceMgr::receive4(), it will read all packets from the list set by
  796. // fakeReceive()
  797. // In particular, it should call registered pkt4_receive callback.
  798. srv_->run();
  799. // Check that there is no packet sent.
  800. ASSERT_EQ(0, srv_->fake_sent_.size());
  801. }
  802. // This test checks if subnet4_select callout is triggered and reports
  803. // valid parameters
  804. TEST_F(HooksDhcpv4SrvTest, subnet4SelectSimple) {
  805. IfaceMgrTestConfig test_config(true);
  806. IfaceMgr::instance().openSockets4();
  807. // Install pkt4_receive_callout
  808. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  809. "subnet4_select", subnet4_select_callout));
  810. // Configure 2 subnets, both directly reachable over local interface
  811. // (let's not complicate the matter with relays)
  812. string config = "{ \"interfaces-config\": {"
  813. " \"interfaces\": [ \"*\" ]"
  814. "},"
  815. "\"rebind-timer\": 2000, "
  816. "\"renew-timer\": 1000, "
  817. "\"subnet4\": [ { "
  818. " \"pools\": [ { \"pool\": \"192.0.2.0/25\" } ],"
  819. " \"subnet\": \"192.0.2.0/24\", "
  820. " \"interface\": \"eth0\" "
  821. " }, {"
  822. " \"pools\": [ { \"pool\": \"192.0.3.0/25\" } ],"
  823. " \"subnet\": \"192.0.3.0/24\" "
  824. " } ],"
  825. "\"valid-lifetime\": 4000 }";
  826. ElementPtr json = Element::fromJSON(config);
  827. ConstElementPtr status;
  828. // Configure the server and make sure the config is accepted
  829. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  830. ASSERT_TRUE(status);
  831. comment_ = parseAnswer(rcode_, status);
  832. ASSERT_EQ(0, rcode_);
  833. // Commit the config
  834. CfgMgr::instance().commit();
  835. // Prepare discover packet. Server should select first subnet for it
  836. Pkt4Ptr sol = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
  837. sol->setRemoteAddr(IOAddress("192.0.2.1"));
  838. sol->setIface("eth1");
  839. OptionPtr clientid = generateClientId();
  840. sol->addOption(clientid);
  841. // Pass it to the server and get an advertise
  842. Pkt4Ptr adv = srv_->processDiscover(sol);
  843. // check if we get response at all
  844. ASSERT_TRUE(adv);
  845. // Check that the callback called is indeed the one we installed
  846. EXPECT_EQ("subnet4_select", callback_name_);
  847. // Check that pkt4 argument passing was successful and returned proper value
  848. EXPECT_TRUE(callback_pkt4_.get() == sol.get());
  849. const Subnet4Collection* exp_subnets =
  850. CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
  851. // The server is supposed to pick the first subnet, because of matching
  852. // interface. Check that the value is reported properly.
  853. ASSERT_TRUE(callback_subnet4_);
  854. EXPECT_EQ(exp_subnets->front().get(), callback_subnet4_.get());
  855. // Server is supposed to report two subnets
  856. ASSERT_EQ(exp_subnets->size(), callback_subnet4collection_->size());
  857. ASSERT_GE(exp_subnets->size(), 2);
  858. // Compare that the available subnets are reported as expected
  859. EXPECT_TRUE((*exp_subnets)[0].get() == (*callback_subnet4collection_)[0].get());
  860. EXPECT_TRUE((*exp_subnets)[1].get() == (*callback_subnet4collection_)[1].get());
  861. }
  862. // This test checks if callout installed on subnet4_select hook point can pick
  863. // a different subnet.
  864. TEST_F(HooksDhcpv4SrvTest, subnet4SelectChange) {
  865. IfaceMgrTestConfig test_config(true);
  866. IfaceMgr::instance().openSockets4();
  867. // Install a callout
  868. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  869. "subnet4_select", subnet4_select_different_subnet_callout));
  870. // Configure 2 subnets, both directly reachable over local interface
  871. // (let's not complicate the matter with relays)
  872. string config = "{ \"interfaces-config\": {"
  873. " \"interfaces\": [ \"*\" ]"
  874. "},"
  875. "\"rebind-timer\": 2000, "
  876. "\"renew-timer\": 1000, "
  877. "\"subnet4\": [ { "
  878. " \"pools\": [ { \"pool\": \"192.0.2.0/25\" } ],"
  879. " \"subnet\": \"192.0.2.0/24\", "
  880. " \"interface\": \"eth0\" "
  881. " }, {"
  882. " \"pools\": [ { \"pool\": \"192.0.3.0/25\" } ],"
  883. " \"subnet\": \"192.0.3.0/24\" "
  884. " } ],"
  885. "\"valid-lifetime\": 4000 }";
  886. ElementPtr json = Element::fromJSON(config);
  887. ConstElementPtr status;
  888. // Configure the server and make sure the config is accepted
  889. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
  890. ASSERT_TRUE(status);
  891. comment_ = parseAnswer(rcode_, status);
  892. ASSERT_EQ(0, rcode_);
  893. CfgMgr::instance().commit();
  894. // Prepare discover packet. Server should select first subnet for it
  895. Pkt4Ptr sol = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
  896. sol->setRemoteAddr(IOAddress("192.0.2.1"));
  897. sol->setIface("eth0");
  898. OptionPtr clientid = generateClientId();
  899. sol->addOption(clientid);
  900. // Pass it to the server and get an advertise
  901. Pkt4Ptr adv = srv_->processDiscover(sol);
  902. // check if we get response at all
  903. ASSERT_TRUE(adv);
  904. // The response should have an address from second pool, so let's check it
  905. IOAddress addr = adv->getYiaddr();
  906. EXPECT_NE("0.0.0.0", addr.toText());
  907. // Get all subnets and use second subnet for verification
  908. const Subnet4Collection* subnets =
  909. CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
  910. ASSERT_EQ(2, subnets->size());
  911. // Advertised address must belong to the second pool (in subnet's range,
  912. // in dynamic pool)
  913. EXPECT_TRUE((*subnets)[1]->inRange(addr));
  914. EXPECT_TRUE((*subnets)[1]->inPool(Lease::TYPE_V4, addr));
  915. }
  916. // This test verifies that incoming (positive) REQUEST/Renewing can be handled
  917. // properly and that callout installed on lease4_renew is triggered with
  918. // expected parameters.
  919. TEST_F(HooksDhcpv4SrvTest, lease4RenewSimple) {
  920. IfaceMgrTestConfig test_config(true);
  921. IfaceMgr::instance().openSockets4();
  922. const IOAddress addr("192.0.2.106");
  923. const uint32_t temp_t1 = 50;
  924. const uint32_t temp_t2 = 75;
  925. const uint32_t temp_valid = 100;
  926. const time_t temp_timestamp = time(NULL) - 10;
  927. // Install a callout
  928. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  929. "lease4_renew", lease4_renew_callout));
  930. // Generate client-id also sets client_id_ member
  931. OptionPtr clientid = generateClientId();
  932. // Check that the address we are about to use is indeed in pool
  933. ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
  934. // let's create a lease and put it in the LeaseMgr
  935. uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
  936. HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER));
  937. Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2,
  938. &client_id_->getDuid()[0], client_id_->getDuid().size(),
  939. temp_valid, temp_t1, temp_t2, temp_timestamp,
  940. subnet_->getID()));
  941. ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
  942. // Check that the lease is really in the database
  943. Lease4Ptr l = LeaseMgrFactory::instance().getLease4(addr);
  944. ASSERT_TRUE(l);
  945. // Let's create a RENEW
  946. Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
  947. req->setRemoteAddr(IOAddress(addr));
  948. req->setYiaddr(addr);
  949. req->setCiaddr(addr); // client's address
  950. req->setIface("eth0");
  951. req->setHWAddr(hwaddr2);
  952. req->addOption(clientid);
  953. req->addOption(srv_->getServerID());
  954. // Pass it to the server and hope for a REPLY
  955. Pkt4Ptr ack = srv_->processRequest(req);
  956. // Check if we get response at all
  957. checkResponse(ack, DHCPACK, 1234);
  958. // Check that the lease is really in the database
  959. l = checkLease(ack, clientid, req->getHWAddr(), addr);
  960. ASSERT_TRUE(l);
  961. // Check that T1, T2, preferred, valid and cltt were really updated
  962. EXPECT_EQ(l->t1_, subnet_->getT1());
  963. EXPECT_EQ(l->t2_, subnet_->getT2());
  964. EXPECT_EQ(l->valid_lft_, subnet_->getValid());
  965. // Check that the callback called is indeed the one we installed
  966. EXPECT_EQ("lease4_renew", callback_name_);
  967. // Check that hwaddr parameter is passed properly
  968. ASSERT_TRUE(callback_hwaddr_);
  969. EXPECT_TRUE(*callback_hwaddr_ == *req->getHWAddr());
  970. // Check that the subnet is passed properly
  971. ASSERT_TRUE(callback_subnet4_);
  972. EXPECT_EQ(callback_subnet4_->toText(), subnet_->toText());
  973. ASSERT_TRUE(callback_clientid_);
  974. ASSERT_TRUE(client_id_);
  975. EXPECT_TRUE(*client_id_ == *callback_clientid_);
  976. // Check if all expected parameters were really received
  977. vector<string> expected_argument_names;
  978. expected_argument_names.push_back("subnet4");
  979. expected_argument_names.push_back("clientid");
  980. expected_argument_names.push_back("hwaddr");
  981. expected_argument_names.push_back("lease4");
  982. sort(callback_argument_names_.begin(), callback_argument_names_.end());
  983. sort(expected_argument_names.begin(), expected_argument_names.end());
  984. EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
  985. EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
  986. }
  987. // This test verifies that a callout installed on lease4_renew can trigger
  988. // the server to not renew a lease.
  989. TEST_F(HooksDhcpv4SrvTest, lease4RenewSkip) {
  990. IfaceMgrTestConfig test_config(true);
  991. IfaceMgr::instance().openSockets4();
  992. const IOAddress addr("192.0.2.106");
  993. const uint32_t temp_t1 = 50;
  994. const uint32_t temp_t2 = 75;
  995. const uint32_t temp_valid = 100;
  996. const time_t temp_timestamp = time(NULL) - 10;
  997. // Install a callout
  998. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  999. "lease4_renew", skip_callout));
  1000. // Generate client-id also sets client_id_ member
  1001. OptionPtr clientid = generateClientId();
  1002. // Check that the address we are about to use is indeed in pool
  1003. ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
  1004. // let's create a lease and put it in the LeaseMgr
  1005. uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
  1006. HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER));
  1007. Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2,
  1008. &client_id_->getDuid()[0], client_id_->getDuid().size(),
  1009. temp_valid, temp_t1, temp_t2, temp_timestamp,
  1010. subnet_->getID()));
  1011. ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
  1012. // Check that the lease is really in the database
  1013. Lease4Ptr l = LeaseMgrFactory::instance().getLease4(addr);
  1014. ASSERT_TRUE(l);
  1015. // Check that T1, T2, preferred, valid and cltt really set.
  1016. // Constructed lease looks as if it was assigned 10 seconds ago
  1017. // EXPECT_EQ(l->t1_, temp_t1);
  1018. // EXPECT_EQ(l->t2_, temp_t2);
  1019. EXPECT_EQ(l->valid_lft_, temp_valid);
  1020. EXPECT_EQ(l->cltt_, temp_timestamp);
  1021. // Let's create a RENEW
  1022. Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
  1023. req->setRemoteAddr(IOAddress(addr));
  1024. req->setYiaddr(addr);
  1025. req->setCiaddr(addr); // client's address
  1026. req->setIface("eth0");
  1027. req->setHWAddr(hwaddr2);
  1028. req->addOption(clientid);
  1029. req->addOption(srv_->getServerID());
  1030. // Pass it to the server and hope for a REPLY
  1031. Pkt4Ptr ack = srv_->processRequest(req);
  1032. ASSERT_TRUE(ack);
  1033. // Check that the lease is really in the database
  1034. l = checkLease(ack, clientid, req->getHWAddr(), addr);
  1035. ASSERT_TRUE(l);
  1036. // Check that T1, T2, valid and cltt were NOT updated
  1037. EXPECT_EQ(temp_t1, l->t1_);
  1038. EXPECT_EQ(temp_t2, l->t2_);
  1039. EXPECT_EQ(temp_valid, l->valid_lft_);
  1040. EXPECT_EQ(temp_timestamp, l->cltt_);
  1041. EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
  1042. }
  1043. // This test verifies that valid RELEASE triggers lease4_release callouts
  1044. TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSimple) {
  1045. IfaceMgrTestConfig test_config(true);
  1046. IfaceMgr::instance().openSockets4();
  1047. const IOAddress addr("192.0.2.106");
  1048. const uint32_t temp_t1 = 50;
  1049. const uint32_t temp_t2 = 75;
  1050. const uint32_t temp_valid = 100;
  1051. const time_t temp_timestamp = time(NULL) - 10;
  1052. // Install a callout
  1053. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  1054. "lease4_release", lease4_release_callout));
  1055. // Generate client-id also duid_
  1056. OptionPtr clientid = generateClientId();
  1057. // Check that the address we are about to use is indeed in pool
  1058. ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
  1059. // Let's create a lease and put it in the LeaseMgr
  1060. uint8_t mac_addr[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
  1061. HWAddrPtr hw(new HWAddr(mac_addr, sizeof(mac_addr), HTYPE_ETHER));
  1062. Lease4Ptr used(new Lease4(addr, hw,
  1063. &client_id_->getDuid()[0], client_id_->getDuid().size(),
  1064. temp_valid, temp_t1, temp_t2, temp_timestamp,
  1065. subnet_->getID()));
  1066. ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
  1067. // Check that the lease is really in the database
  1068. Lease4Ptr l = LeaseMgrFactory::instance().getLease4(addr);
  1069. ASSERT_TRUE(l);
  1070. // Let's create a RELEASE
  1071. // Generate client-id also duid_
  1072. Pkt4Ptr rel = Pkt4Ptr(new Pkt4(DHCPRELEASE, 1234));
  1073. rel->setRemoteAddr(addr);
  1074. rel->setCiaddr(addr);
  1075. rel->addOption(clientid);
  1076. rel->addOption(srv_->getServerID());
  1077. rel->setHWAddr(hw);
  1078. // Pass it to the server and hope for a REPLY
  1079. // Note: this is no response to RELEASE in DHCPv4
  1080. EXPECT_NO_THROW(srv_->processRelease(rel));
  1081. // The lease should be gone from LeaseMgr
  1082. l = LeaseMgrFactory::instance().getLease4(addr);
  1083. EXPECT_FALSE(l);
  1084. // Try to get the lease by hardware address
  1085. // @todo: Uncomment this once trac2592 is implemented
  1086. // Lease4Collection leases = LeaseMgrFactory::instance().getLease4(hw->hwaddr_);
  1087. // EXPECT_EQ(leases.size(), 0);
  1088. // Try to get it by hw/subnet_id combination
  1089. l = LeaseMgrFactory::instance().getLease4(hw->hwaddr_, subnet_->getID());
  1090. EXPECT_FALSE(l);
  1091. // Try by client-id
  1092. // @todo: Uncomment this once trac2592 is implemented
  1093. //Lease4Collection leases = LeaseMgrFactory::instance().getLease4(*client_id_);
  1094. //EXPECT_EQ(leases.size(), 0);
  1095. // Try by client-id/subnet-id
  1096. l = LeaseMgrFactory::instance().getLease4(*client_id_, subnet_->getID());
  1097. EXPECT_FALSE(l);
  1098. // Ok, the lease is *really* not there.
  1099. // Check that the callback called is indeed the one we installed
  1100. EXPECT_EQ("lease4_release", callback_name_);
  1101. // Check that pkt4 argument passing was successful and returned proper value
  1102. EXPECT_TRUE(callback_pkt4_.get() == rel.get());
  1103. // Check if all expected parameters were really received
  1104. vector<string> expected_argument_names;
  1105. expected_argument_names.push_back("query4");
  1106. expected_argument_names.push_back("lease4");
  1107. sort(callback_argument_names_.begin(), callback_argument_names_.end());
  1108. sort(expected_argument_names.begin(), expected_argument_names.end());
  1109. EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
  1110. }
  1111. // This test verifies that skip flag returned by a callout installed on the
  1112. // lease4_release hook point will keep the lease
  1113. TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSkip) {
  1114. IfaceMgrTestConfig test_config(true);
  1115. IfaceMgr::instance().openSockets4();
  1116. const IOAddress addr("192.0.2.106");
  1117. const uint32_t temp_t1 = 50;
  1118. const uint32_t temp_t2 = 75;
  1119. const uint32_t temp_valid = 100;
  1120. const time_t temp_timestamp = time(NULL) - 10;
  1121. // Install a callout
  1122. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  1123. "lease4_release", skip_callout));
  1124. // Generate client-id also duid_
  1125. OptionPtr clientid = generateClientId();
  1126. // Check that the address we are about to use is indeed in pool
  1127. ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
  1128. // Let's create a lease and put it in the LeaseMgr
  1129. uint8_t mac_addr[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
  1130. HWAddrPtr hw(new HWAddr(mac_addr, sizeof(mac_addr), HTYPE_ETHER));
  1131. Lease4Ptr used(new Lease4(addr, hw,
  1132. &client_id_->getDuid()[0], client_id_->getDuid().size(),
  1133. temp_valid, temp_t1, temp_t2, temp_timestamp,
  1134. subnet_->getID()));
  1135. ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
  1136. // Check that the lease is really in the database
  1137. Lease4Ptr l = LeaseMgrFactory::instance().getLease4(addr);
  1138. ASSERT_TRUE(l);
  1139. // Let's create a RELEASE
  1140. // Generate client-id also duid_
  1141. Pkt4Ptr rel = Pkt4Ptr(new Pkt4(DHCPRELEASE, 1234));
  1142. rel->setRemoteAddr(addr);
  1143. rel->setYiaddr(addr);
  1144. rel->addOption(clientid);
  1145. rel->addOption(srv_->getServerID());
  1146. rel->setHWAddr(hw);
  1147. // Pass it to the server and hope for a REPLY
  1148. // Note: this is no response to RELEASE in DHCPv4
  1149. EXPECT_NO_THROW(srv_->processRelease(rel));
  1150. // The lease should be still there
  1151. l = LeaseMgrFactory::instance().getLease4(addr);
  1152. EXPECT_TRUE(l);
  1153. // Try by client-id/subnet-id
  1154. l = LeaseMgrFactory::instance().getLease4(*client_id_, subnet_->getID());
  1155. EXPECT_TRUE(l);
  1156. // Try to get the lease by hardware address, should succeed
  1157. Lease4Collection leases = LeaseMgrFactory::instance().getLease4(*hw);
  1158. EXPECT_EQ(leases.size(), 1);
  1159. // Try by client-id, should be successful as well.
  1160. leases = LeaseMgrFactory::instance().getLease4(*client_id_);
  1161. EXPECT_EQ(leases.size(), 1);
  1162. }
  1163. // Checks that decline4 hooks (lease4_decline) are triggered properly.
  1164. TEST_F(HooksDhcpv4SrvTest, HooksDecline) {
  1165. IfaceMgrTestConfig test_config(true);
  1166. IfaceMgr::instance().openSockets4();
  1167. // Install a callout
  1168. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  1169. "lease4_decline", lease4_decline_callout));
  1170. // Conduct the actual DORA + Decline.
  1171. acquireAndDecline("01:02:03:04:05:06", "12:14",
  1172. "01:02:03:04:05:06", "12:14",
  1173. SHOULD_PASS);
  1174. EXPECT_EQ("lease4_decline", callback_name_);
  1175. // Verifying DHCPDECLINE is a bit tricky, as it is created somewhere in
  1176. // acquireAndDecline. We'll just verify that it's really a DECLINE
  1177. // and that its address is equal to what we have in LeaseMgr.
  1178. ASSERT_TRUE(callback_pkt4_);
  1179. ASSERT_TRUE(callback_lease4_);
  1180. // Check that it's the proper packet that was reported.
  1181. EXPECT_EQ(DHCPDECLINE, callback_pkt4_->getType());
  1182. // Extract the address being declined.
  1183. OptionCustomPtr opt_declined_addr = boost::dynamic_pointer_cast<
  1184. OptionCustom>(callback_pkt4_->getOption(DHO_DHCP_REQUESTED_ADDRESS));
  1185. ASSERT_TRUE(opt_declined_addr);
  1186. IOAddress addr(opt_declined_addr->readAddress());
  1187. // And try to get a matching lease from the lease manager.
  1188. Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(addr);
  1189. ASSERT_TRUE(from_mgr);
  1190. EXPECT_EQ(Lease::STATE_DECLINED, from_mgr->state_);
  1191. // Let's now check that those 3 things (packet, lease returned and lease from
  1192. // the lease manager) all match.
  1193. EXPECT_EQ(addr, from_mgr->addr_);
  1194. EXPECT_EQ(addr, callback_lease4_->addr_);
  1195. }
  1196. // Checks that decline4 hook is able to drop the packet.
  1197. TEST_F(HooksDhcpv4SrvTest, HooksDeclineDrop) {
  1198. IfaceMgrTestConfig test_config(true);
  1199. IfaceMgr::instance().openSockets4();
  1200. // Install a callout
  1201. EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
  1202. "lease4_decline", lease4_decline_drop_callout));
  1203. // Conduct the actual DORA + Decline. The DECLINE should fail, as the
  1204. // hook will set the status to DROP.
  1205. acquireAndDecline("01:02:03:04:05:06", "12:14",
  1206. "01:02:03:04:05:06", "12:14",
  1207. SHOULD_FAIL);
  1208. EXPECT_EQ("lease4_decline", callback_name_);
  1209. // Verifying DHCPDECLINE is a bit tricky, as it is created somewhere in
  1210. // acquireAndDecline. We'll just verify that it's really a DECLINE
  1211. // and that its address is equal to what we have in LeaseMgr.
  1212. ASSERT_TRUE(callback_pkt4_);
  1213. ASSERT_TRUE(callback_lease4_);
  1214. // Check that it's the proper packet that was reported.
  1215. EXPECT_EQ(DHCPDECLINE, callback_pkt4_->getType());
  1216. // Extract the address being declined.
  1217. OptionCustomPtr opt_declined_addr = boost::dynamic_pointer_cast<
  1218. OptionCustom>(callback_pkt4_->getOption(DHO_DHCP_REQUESTED_ADDRESS));
  1219. ASSERT_TRUE(opt_declined_addr);
  1220. IOAddress addr(opt_declined_addr->readAddress());
  1221. // And try to get a matching lease from the lease manager. The lease should
  1222. // still be there in default state, not in declined state.
  1223. Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(addr);
  1224. ASSERT_TRUE(from_mgr);
  1225. EXPECT_EQ(Lease::STATE_DEFAULT, from_mgr->state_);
  1226. // As a final sanity check, let's now check that those 3 things (packet,
  1227. // lease returned and lease from the lease manager) all match.
  1228. EXPECT_EQ(addr, from_mgr->addr_);
  1229. EXPECT_EQ(addr, callback_lease4_->addr_);
  1230. }