dora_unittest.cc 70 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669
  1. // Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <asiolink/io_address.h>
  8. #include <cc/data.h>
  9. #include <dhcp/dhcp4.h>
  10. #include <dhcp/tests/iface_mgr_test_config.h>
  11. #include <dhcpsrv/cfgmgr.h>
  12. #include <dhcpsrv/host.h>
  13. #include <dhcpsrv/host_mgr.h>
  14. #include <dhcpsrv/subnet_id.h>
  15. #include <dhcpsrv/testutils/cql_schema.h>
  16. #include <dhcpsrv/testutils/mysql_schema.h>
  17. #include <dhcpsrv/testutils/pgsql_schema.h>
  18. #include <dhcp4/tests/dhcp4_test_utils.h>
  19. #include <dhcp4/tests/dhcp4_client.h>
  20. #include <boost/shared_ptr.hpp>
  21. #include <stats/stats_mgr.h>
  22. using namespace isc;
  23. using namespace isc::asiolink;
  24. using namespace isc::data;
  25. using namespace isc::dhcp;
  26. using namespace isc::dhcp::test;
  27. namespace {
  28. /// @brief Set of JSON configurations used throughout the DORA tests.
  29. ///
  30. /// - Configuration 0:
  31. /// - Used for testing direct traffic
  32. /// - 1 subnet: 10.0.0.0/24
  33. /// - 1 pool: 10.0.0.10-10.0.0.100
  34. /// - Router option present: 10.0.0.200 and 10.0.0.201
  35. /// - Domain Name Server option present: 10.0.0.202, 10.0.0.203.
  36. /// - Log Servers option present: 192.0.2.200 and 192.0.2.201
  37. /// - Quotes Servers option present: 192.0.2.202, 192.0.2.203.
  38. ///
  39. /// - Configuration 1:
  40. /// - Use for testing relayed messages
  41. /// - 1 subnet: 192.0.2.0/24
  42. /// - Router option present: 192.0.2.200 and 192.0.2.201
  43. /// - Domain Name Server option present: 192.0.2.202, 192.0.2.203.
  44. /// - Log Servers option present: 192.0.2.200 and 192.0.2.201
  45. /// - Quotes Servers option present: 192.0.2.202, 192.0.2.203.
  46. ///
  47. /// - Configuration 2:
  48. /// - Use for testing simple scenarios with host reservations
  49. /// - 1 subnet: 10.0.0.0/24
  50. /// - One reservation for the client using MAC address:
  51. /// aa:bb:cc:dd:ee:ff, reserved address 10.0.0.7
  52. ///
  53. /// - Configuration 3:
  54. /// - Use for testing match-client-id flag
  55. /// - 1 subnet: 10.0.0.0/24
  56. /// - 1 pool: 10.0.0.10-10.0.0.100
  57. /// - match-client-id flag is set to false, thus the server
  58. /// uses HW address for lease lookup, rather than client id
  59. ///
  60. /// - Configuration 4:
  61. /// - Used for testing host reservations where circuit-id takes precedence
  62. /// over hw-address, and the hw-address takes precedence over duid.
  63. /// - 1 subnet: 10.0.0.0/24
  64. /// - 3 reservations for this subnet:
  65. /// - IP address 10.0.0.7 for HW address aa:bb:cc:dd:ee:ff
  66. /// - IP address 10.0.0.8 for DUID 01:02:03:04:05
  67. /// - IP address 10.0.0.9 for circuit-id 'charter950'
  68. /// - IP address 10.0.0.1 for client-id
  69. ///
  70. /// - Configuration 5:
  71. /// - The same as configuration 4, but using the following order of
  72. /// host-reservation-identifiers: duid, circuit-id, hw-address,
  73. /// client-id.
  74. ///
  75. /// - Configuration 6:
  76. /// - This configuration provides reservations for next-server,
  77. /// server-hostname and boot-file-name value.
  78. /// - 1 subnet: 10.0.0.0/24
  79. /// - 1 reservation for this subnet:
  80. /// - Client's HW address: aa:bb:cc:dd:ee:ff
  81. /// - next-server = 10.0.0.7
  82. /// - server name = "some-name.example.org"
  83. /// - boot-file-name = "bootfile.efi"
  84. ///
  85. /// - Configuration 7:
  86. /// - Simple configuration with a single subnet and single pool
  87. /// - Using MySQL lease database backend to store leases
  88. ///
  89. /// - Configuration 8:
  90. /// - Simple configuration with a single subnet and single pool
  91. /// - Using PostgreSQL lease database backend to store leases
  92. ///
  93. /// - Configuration 9:
  94. /// - Simple configuration with a single subnet and single pool
  95. /// - Using Cassandra lease database backend to store leases
  96. const char* DORA_CONFIGS[] = {
  97. // Configuration 0
  98. "{ \"interfaces-config\": {"
  99. " \"interfaces\": [ \"*\" ]"
  100. "},"
  101. "\"valid-lifetime\": 600,"
  102. "\"subnet4\": [ { "
  103. " \"subnet\": \"10.0.0.0/24\", "
  104. " \"id\": 1,"
  105. " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
  106. " \"option-data\": [ {"
  107. " \"name\": \"routers\","
  108. " \"data\": \"10.0.0.200,10.0.0.201\""
  109. " },"
  110. " {"
  111. " \"name\": \"domain-name-servers\","
  112. " \"data\": \"10.0.0.202,10.0.0.203\""
  113. " },"
  114. " {"
  115. " \"name\": \"log-servers\","
  116. " \"data\": \"10.0.0.200,10.0.0.201\""
  117. " },"
  118. " {"
  119. " \"name\": \"cookie-servers\","
  120. " \"data\": \"10.0.0.202,10.0.0.203\""
  121. " } ]"
  122. " } ]"
  123. "}",
  124. // Configuration 1
  125. "{ \"interfaces-config\": {"
  126. " \"interfaces\": [ \"*\" ]"
  127. "},"
  128. "\"valid-lifetime\": 600,"
  129. "\"subnet4\": [ { "
  130. " \"subnet\": \"192.0.2.0/24\", "
  131. " \"option-data\": [ {"
  132. " \"name\": \"routers\","
  133. " \"data\": \"192.0.2.200,192.0.2.201\""
  134. " },"
  135. " {"
  136. " \"name\": \"domain-name-servers\","
  137. " \"data\": \"192.0.2.202,192.0.2.203\""
  138. " },"
  139. " {"
  140. " \"name\": \"log-servers\","
  141. " \"data\": \"10.0.0.200,10.0.0.201\""
  142. " },"
  143. " {"
  144. " \"name\": \"cookie-servers\","
  145. " \"data\": \"10.0.0.202,10.0.0.203\""
  146. " } ]"
  147. " } ]"
  148. "}",
  149. // Configuration 2
  150. "{ \"interfaces-config\": {"
  151. " \"interfaces\": [ \"*\" ]"
  152. "},"
  153. "\"valid-lifetime\": 600,"
  154. "\"subnet4\": [ { "
  155. " \"subnet\": \"10.0.0.0/24\", "
  156. " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
  157. " \"reservations\": [ "
  158. " {"
  159. " \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
  160. " \"ip-address\": \"10.0.0.7\""
  161. " },"
  162. " {"
  163. " \"duid\": \"01:02:03:04:05\","
  164. " \"ip-address\": \"10.0.0.8\""
  165. " },"
  166. " {"
  167. " \"circuit-id\": \"'charter950'\","
  168. " \"ip-address\": \"10.0.0.9\""
  169. " },"
  170. " {"
  171. " \"client-id\": \"01:11:22:33:44:55:66\","
  172. " \"ip-address\": \"10.0.0.1\""
  173. " }"
  174. " ]"
  175. "} ]"
  176. "}",
  177. // Configuration 3
  178. "{ \"interfaces-config\": {"
  179. " \"interfaces\": [ \"*\" ]"
  180. "},"
  181. "\"valid-lifetime\": 600,"
  182. "\"match-client-id\": false,"
  183. "\"subnet4\": [ { "
  184. " \"subnet\": \"10.0.0.0/24\", "
  185. " \"id\": 1,"
  186. " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
  187. " \"option-data\": [ {"
  188. " \"name\": \"routers\","
  189. " \"data\": \"10.0.0.200,10.0.0.201\""
  190. " } ]"
  191. " } ]"
  192. "}",
  193. // Configuration 4
  194. "{ \"interfaces-config\": {"
  195. " \"interfaces\": [ \"*\" ]"
  196. "},"
  197. "\"host-reservation-identifiers\": [ \"circuit-id\", \"hw-address\","
  198. " \"duid\", \"client-id\" ],"
  199. "\"valid-lifetime\": 600,"
  200. "\"subnet4\": [ { "
  201. " \"subnet\": \"10.0.0.0/24\", "
  202. " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
  203. " \"reservations\": [ "
  204. " {"
  205. " \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
  206. " \"ip-address\": \"10.0.0.7\""
  207. " },"
  208. " {"
  209. " \"duid\": \"01:02:03:04:05\","
  210. " \"ip-address\": \"10.0.0.8\""
  211. " },"
  212. " {"
  213. " \"circuit-id\": \"'charter950'\","
  214. " \"ip-address\": \"10.0.0.9\""
  215. " },"
  216. " {"
  217. " \"client-id\": \"01:11:22:33:44:55:66\","
  218. " \"ip-address\": \"10.0.0.1\""
  219. " }"
  220. " ]"
  221. "} ]"
  222. "}",
  223. // Configuration 5
  224. "{ \"interfaces-config\": {"
  225. " \"interfaces\": [ \"*\" ]"
  226. "},"
  227. "\"host-reservation-identifiers\": [ \"duid\", \"client-id\","
  228. " \"circuit-id\", \"hw-address\" ],"
  229. "\"valid-lifetime\": 600,"
  230. "\"subnet4\": [ { "
  231. " \"subnet\": \"10.0.0.0/24\", "
  232. " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
  233. " \"reservations\": [ "
  234. " {"
  235. " \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
  236. " \"ip-address\": \"10.0.0.7\""
  237. " },"
  238. " {"
  239. " \"duid\": \"01:02:03:04:05\","
  240. " \"ip-address\": \"10.0.0.8\""
  241. " },"
  242. " {"
  243. " \"circuit-id\": \"'charter950'\","
  244. " \"ip-address\": \"10.0.0.9\""
  245. " },"
  246. " {"
  247. " \"client-id\": \"01:11:22:33:44:55:66\","
  248. " \"ip-address\": \"10.0.0.1\""
  249. " }"
  250. " ]"
  251. "} ]"
  252. "}",
  253. // Configuration 6
  254. "{ \"interfaces-config\": {"
  255. " \"interfaces\": [ \"*\" ]"
  256. "},"
  257. "\"valid-lifetime\": 600,"
  258. "\"next-server\": \"10.0.0.1\","
  259. "\"server-hostname\": \"nohost\","
  260. "\"boot-file-name\": \"nofile\","
  261. "\"subnet4\": [ { "
  262. " \"subnet\": \"10.0.0.0/24\", "
  263. " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
  264. " \"reservations\": [ "
  265. " {"
  266. " \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
  267. " \"next-server\": \"10.0.0.7\","
  268. " \"server-hostname\": \"some-name.example.org\","
  269. " \"boot-file-name\": \"bootfile.efi\""
  270. " }"
  271. " ]"
  272. "} ]"
  273. "}",
  274. // Configuration 7
  275. "{ \"interfaces-config\": {"
  276. " \"interfaces\": [ \"*\" ]"
  277. "},"
  278. "\"lease-database\": {"
  279. " \"type\": \"mysql\","
  280. " \"name\": \"keatest\","
  281. " \"user\": \"keatest\","
  282. " \"password\": \"keatest\""
  283. "},"
  284. "\"valid-lifetime\": 600,"
  285. "\"subnet4\": [ { "
  286. " \"subnet\": \"10.0.0.0/24\", "
  287. " \"id\": 1,"
  288. " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]"
  289. " } ]"
  290. "}",
  291. // Configuration 8
  292. "{ \"interfaces-config\": {"
  293. " \"interfaces\": [ \"*\" ]"
  294. "},"
  295. "\"lease-database\": {"
  296. " \"type\": \"postgresql\","
  297. " \"name\": \"keatest\","
  298. " \"user\": \"keatest\","
  299. " \"password\": \"keatest\""
  300. "},"
  301. "\"valid-lifetime\": 600,"
  302. "\"subnet4\": [ { "
  303. " \"subnet\": \"10.0.0.0/24\", "
  304. " \"id\": 1,"
  305. " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]"
  306. " } ]"
  307. "}",
  308. // Configuration 9
  309. "{ \"interfaces-config\": {"
  310. " \"interfaces\": [ \"*\" ]"
  311. "},"
  312. "\"lease-database\": {"
  313. " \"type\": \"cql\","
  314. " \"name\": \"keatest\","
  315. " \"user\": \"keatest\","
  316. " \"password\": \"keatest\""
  317. "},"
  318. "\"valid-lifetime\": 600,"
  319. "\"subnet4\": [ { "
  320. " \"subnet\": \"10.0.0.0/24\", "
  321. " \"id\": 1,"
  322. " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]"
  323. " } ]"
  324. "}"
  325. };
  326. /// @brief Test fixture class for testing 4-way (DORA) exchanges.
  327. ///
  328. /// @todo Currently there is a limit number of test cases covered here.
  329. /// In the future it is planned that the tests from the
  330. /// dhcp4_srv_unittest.cc will be migrated here and will use the
  331. /// @c Dhcp4Client class.
  332. class DORATest : public Dhcpv4SrvTest {
  333. public:
  334. /// @brief Constructor.
  335. ///
  336. /// Sets up fake interfaces.
  337. DORATest()
  338. : Dhcpv4SrvTest(),
  339. iface_mgr_test_config_(true) {
  340. IfaceMgr::instance().openSockets4();
  341. // Let's wipe all existing statistics.
  342. isc::stats::StatsMgr::instance().removeAll();
  343. }
  344. /// @brief Destructor.
  345. ///
  346. /// Cleans up statistics after the test.
  347. ~DORATest() {
  348. // Let's wipe all existing statistics.
  349. isc::stats::StatsMgr::instance().removeAll();
  350. }
  351. /// @brief Test that server returns the same lease for the client which is
  352. /// sometimes using client identifier, sometimes not.
  353. ///
  354. /// This test checks the server's behavior in the following scenario:
  355. /// - Client identifies itself to the server using HW address, and may use
  356. /// client identifier.
  357. /// - Client performs the 4-way exchange and obtains a lease from the server.
  358. /// - If the client identifier was in use when the client has acquired the lease,
  359. /// the client uses null client identifier in the next exchange with the server.
  360. /// - If the client identifier was not in use when the client has acquired the
  361. /// lease, the client uses client identifier in the next exchange with the
  362. /// server.
  363. /// - When the client contacts the server for the second time using the
  364. /// DHCPDISCOVER the server determines (using HW address) that the client
  365. /// already has a lease and returns this lease to the client.
  366. /// - The client renews the existing lease.
  367. ///
  368. /// @param clientid_a Client identifier when the client initially allocates
  369. /// the lease. An empty value means "no client identifier".
  370. /// @param clientid_b Client identifier when the client sends the DHCPDISCOVER
  371. /// and then DHCPREQUEST to renew lease.
  372. void oneAllocationOverlapTest(const std::string& clientid_a,
  373. const std::string& clientid_b);
  374. /// @brief Test that the client using the same hardware address but
  375. /// multiple client identifiers will obtain multiple leases.
  376. ///
  377. /// This reflects the scenario of the OS installation over the network
  378. /// when BIOS, installer and the host request DHCPv4 lease assignment
  379. /// using the same MAC address/interface but generating different
  380. /// client identifiers.
  381. ///
  382. /// @param config_index Index of the configuration within the
  383. /// @c DORA_CONFIGS array.
  384. void testMultiStageBoot(const unsigned int config_index);
  385. /// @brief Interface Manager's fake configuration control.
  386. IfaceMgrTestConfig iface_mgr_test_config_;
  387. };
  388. /// This test verifies that the client in the SELECTING state can get
  389. /// an address when it doesn't request any specific address in the
  390. /// DHCPDISCOVER message.
  391. TEST_F(DORATest, selectingDoNotRequestAddress) {
  392. Dhcp4Client client(Dhcp4Client::SELECTING);
  393. // Configure DHCP server.
  394. configure(DORA_CONFIGS[0], *client.getServer());
  395. // Perform 4-way exchange with the server but to not request any
  396. // specific address in the DHCPDISCOVER message.
  397. ASSERT_NO_THROW(client.doDORA());
  398. // Make sure that the server responded.
  399. ASSERT_TRUE(client.getContext().response_);
  400. Pkt4Ptr resp = client.getContext().response_;
  401. // Make sure that the server has responded with DHCPACK.
  402. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  403. // Response must not be relayed.
  404. EXPECT_FALSE(resp->isRelayed());
  405. // Make sure that the server id is present.
  406. EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
  407. // Make sure that the client has got the lease with the requested address.
  408. ASSERT_NE(client.config_.lease_.addr_.toText(), "0.0.0.0");
  409. }
  410. /// This test verifies that multiple clients may use the DHCPv4 server
  411. /// and obtain unique leases.
  412. TEST_F(DORATest, selectingMultipleClients) {
  413. Dhcp4Client client(Dhcp4Client::SELECTING);
  414. // Configure DHCP server.
  415. configure(DORA_CONFIGS[0], *client.getServer());
  416. // Get the first lease.
  417. ASSERT_NO_THROW(client.doDORA());
  418. // Make sure that the server responded.
  419. Pkt4Ptr resp = client.getContext().response_;
  420. ASSERT_TRUE(resp);
  421. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  422. // Store the lease.
  423. Lease4 lease1 = client.config_.lease_;
  424. // Get the lease for a different client.
  425. client.modifyHWAddr();
  426. ASSERT_NO_THROW(client.doDORA());
  427. // Make sure that the server responded.
  428. resp = client.getContext().response_;
  429. ASSERT_TRUE(resp);
  430. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  431. // Store the lease.
  432. Lease4 lease2 = client.config_.lease_;
  433. // Get the lease for a different client.
  434. client.modifyHWAddr();
  435. ASSERT_NO_THROW(client.doDORA());
  436. // Make sure that the server responded.
  437. resp = client.getContext().response_;
  438. ASSERT_TRUE(resp);
  439. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  440. // Store the lease.
  441. Lease4 lease3 = client.config_.lease_;
  442. // Make sure that unique addresses have been assigned.
  443. EXPECT_NE(lease1.addr_, lease2.addr_);
  444. EXPECT_NE(lease2.addr_, lease3.addr_);
  445. EXPECT_NE(lease1.addr_, lease3.addr_);
  446. }
  447. // This test verifies that the client in a SELECTING state can request
  448. // a specific address and that this address will be assigned when
  449. // available. It also tests that if the client requests an address which
  450. // is in use the client will get a different address.
  451. TEST_F(DORATest, selectingRequestAddress) {
  452. Dhcp4Client client(Dhcp4Client::SELECTING);
  453. // Configure DHCP server.
  454. configure(DORA_CONFIGS[0], *client.getServer());
  455. // Perform 4-way exchange with the server.
  456. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  457. IOAddress>(new IOAddress("10.0.0.50"))));
  458. // Make sure that the server responded.
  459. ASSERT_TRUE(client.getContext().response_);
  460. Pkt4Ptr resp = client.getContext().response_;
  461. // Make sure that the server has responded with DHCPACK.
  462. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  463. // Response must not be relayed.
  464. EXPECT_FALSE(resp->isRelayed());
  465. // Make sure that the server id is present.
  466. EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
  467. // Make sure that the client has got the lease with the requested address.
  468. ASSERT_EQ("10.0.0.50", client.config_.lease_.addr_.toText());
  469. // Simulate different client requesting the same address.
  470. client.modifyHWAddr();
  471. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  472. IOAddress>(new IOAddress("10.0.0.50"))));
  473. resp = client.getContext().response_;
  474. // Make sure that the server responded.
  475. ASSERT_TRUE(resp);
  476. // Make sure that the server has responded with DHCPACK.
  477. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  478. // Response must not be relayed.
  479. EXPECT_FALSE(resp->isRelayed());
  480. // Make sure that the server id is present.
  481. EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
  482. // Make sure that the client has got some address.
  483. EXPECT_NE(client.config_.lease_.addr_.toText(), "0.0.0.0");
  484. // Make sure that the client has got a different address than requested
  485. // as the requested one is already in use.
  486. EXPECT_NE(client.config_.lease_.addr_.toText(), "10.0.0.50");
  487. }
  488. // This test verifies that the client will get the address that it has
  489. // been allocated when the client requests a different address.
  490. TEST_F(DORATest, selectingRequestNonMatchingAddress) {
  491. Dhcp4Client client(Dhcp4Client::SELECTING);
  492. // Configure DHCP server.
  493. configure(DORA_CONFIGS[0], *client.getServer());
  494. // Perform 4-way exchange with the server.
  495. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  496. IOAddress>(new IOAddress("10.0.0.50"))));
  497. // Make sure that the server responded.
  498. ASSERT_TRUE(client.getContext().response_);
  499. Pkt4Ptr resp = client.getContext().response_;
  500. // Make sure that the server has responded with DHCPACK.
  501. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  502. // Response must not be relayed.
  503. EXPECT_FALSE(resp->isRelayed());
  504. // Make sure that the server id is present.
  505. EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
  506. // Make sure that the client has got the lease with the requested address.
  507. ASSERT_EQ("10.0.0.50", client.config_.lease_.addr_.toText());
  508. // Let's request a different address. The server should respond with
  509. // the one that the client already has allocated.
  510. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  511. IOAddress>(new IOAddress("10.0.0.80"))));
  512. // Make sure that the server responded.
  513. ASSERT_TRUE(client.getContext().response_);
  514. resp = client.getContext().response_;
  515. // Make sure that the server has responded with DHCPACK.
  516. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  517. // Response must not be relayed.
  518. EXPECT_FALSE(resp->isRelayed());
  519. // Make sure that the server id is present.
  520. EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
  521. // Make sure that the client has got the lease with the address that
  522. // the client has recorded in the lease database.
  523. EXPECT_EQ("10.0.0.50", client.config_.lease_.addr_.toText());
  524. }
  525. // Test that the client in the INIT-REBOOT state can request the IP
  526. // address it has and the address is returned. Also, check that if
  527. // if the client requests invalid address the server sends a DHCPNAK.
  528. TEST_F(DORATest, initRebootRequest) {
  529. Dhcp4Client client(Dhcp4Client::SELECTING);
  530. // Configure DHCP server.
  531. configure(DORA_CONFIGS[0], *client.getServer());
  532. client.includeClientId("11:22");
  533. // Obtain a lease from the server using the 4-way exchange.
  534. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  535. IOAddress>(new IOAddress("10.0.0.50"))));
  536. // Make sure that the server responded.
  537. ASSERT_TRUE(client.getContext().response_);
  538. Pkt4Ptr resp = client.getContext().response_;
  539. // Make sure that the server has responded with DHCPACK.
  540. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  541. // Response must not be relayed.
  542. EXPECT_FALSE(resp->isRelayed());
  543. // Make sure that the server id is present.
  544. EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
  545. // Make sure that the client has got the lease with the requested address.
  546. ASSERT_EQ("10.0.0.50", client.config_.lease_.addr_.toText());
  547. // Client has a lease in the database. Let's transition the client
  548. // to the INIT_REBOOT state so as the client can request the cached
  549. // lease using the DHCPREQUEST message.
  550. client.setState(Dhcp4Client::INIT_REBOOT);
  551. ASSERT_NO_THROW(client.doRequest());
  552. // Make sure that the server responded.
  553. ASSERT_TRUE(client.getContext().response_);
  554. resp = client.getContext().response_;
  555. // Make sure that the server has responded with DHCPACK.
  556. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  557. // Response must not be relayed.
  558. EXPECT_FALSE(resp->isRelayed());
  559. // Make sure that the server id is present.
  560. EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
  561. // Make sure that the client has got the lease with the requested address.
  562. ASSERT_EQ("10.0.0.50", client.config_.lease_.addr_.toText());
  563. // Try to request a different address than the client has. The server
  564. // should respond with DHCPNAK.
  565. client.config_.lease_.addr_ = IOAddress("10.0.0.30");
  566. ASSERT_NO_THROW(client.doRequest());
  567. // Make sure that the server responded.
  568. ASSERT_TRUE(client.getContext().response_);
  569. resp = client.getContext().response_;
  570. EXPECT_EQ(DHCPNAK, static_cast<int>(resp->getType()));
  571. // Change client identifier. The server should treat the request
  572. // as a request from unknown client and ignore it.
  573. client.includeClientId("12:34");
  574. ASSERT_NO_THROW(client.doRequest());
  575. ASSERT_FALSE(client.getContext().response_);
  576. // Now let's fix the IP address. The client identifier is still
  577. // invalid so the message should be dropped.
  578. client.config_.lease_.addr_ = IOAddress("10.0.0.50");
  579. ASSERT_NO_THROW(client.doRequest());
  580. ASSERT_FALSE(client.getContext().response_);
  581. // Restore original client identifier.
  582. client.includeClientId("11:22");
  583. // Try to request from a different HW address. This should be successful
  584. // because the client identifier matches.
  585. client.modifyHWAddr();
  586. ASSERT_NO_THROW(client.doRequest());
  587. // Make sure that the server responded.
  588. ASSERT_TRUE(client.getContext().response_);
  589. resp = client.getContext().response_;
  590. // Make sure that the server has responded with DHCPACK.
  591. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  592. // Make sure that the client has got the lease with the requested address.
  593. ASSERT_EQ("10.0.0.50", client.config_.lease_.addr_.toText());
  594. }
  595. // Check that the ciaddr returned by the server is correct for DHCPOFFER and
  596. // DHCPNAK according to RFC2131, section 4.3.1.
  597. TEST_F(DORATest, ciaddr) {
  598. Dhcp4Client client(Dhcp4Client::SELECTING);
  599. // Configure DHCP server.
  600. configure(DORA_CONFIGS[0], *client.getServer());
  601. // Force ciaddr of Discover message to be non-zero.
  602. client.ciaddr_.specify(IOAddress("10.0.0.50"));
  603. // Obtain a lease from the server using the 4-way exchange.
  604. ASSERT_NO_THROW(client.doDiscover(boost::shared_ptr<
  605. IOAddress>(new IOAddress("10.0.0.50"))));
  606. // Make sure that the server responded.
  607. ASSERT_TRUE(client.getContext().response_);
  608. Pkt4Ptr resp = client.getContext().response_;
  609. // Make sure that the server has responded with DHCPOFFER.
  610. ASSERT_EQ(DHCPOFFER, static_cast<int>(resp->getType()));
  611. // Make sure ciaddr is not set for DHCPOFFER.
  612. EXPECT_EQ("0.0.0.0", resp->getCiaddr().toText());
  613. // Obtain a lease from the server using the 4-way exchange.
  614. ASSERT_NO_THROW(client.doRequest());
  615. // Make sure that the server responded.
  616. ASSERT_TRUE(client.getContext().response_);
  617. resp = client.getContext().response_;
  618. // Make sure that the server has responded with DHCPACK.
  619. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  620. // Let's transition the client to Renewing state.
  621. client.setState(Dhcp4Client::RENEWING);
  622. // Set the unicast destination address to indicate that it is a renewal.
  623. client.setDestAddress(IOAddress("10.0.0.1"));
  624. ASSERT_NO_THROW(client.doRequest());
  625. // The client is sending invalid ciaddr so the server should send a NAK.
  626. resp = client.getContext().response_;
  627. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  628. // For DHCPACK the ciaddr may be 0 or may be set to the ciaddr value
  629. // from the client's message. Kea sets it to the latter.
  630. EXPECT_EQ("10.0.0.50", resp->getCiaddr().toText());
  631. // Replace the address held by the client. The client will request
  632. // the assignment of this address but the server has a different
  633. // address for this client.
  634. client.ciaddr_.specify(IOAddress("192.168.0.30"));
  635. ASSERT_NO_THROW(client.doRequest());
  636. // The client is sending invalid ciaddr so the server should send a NAK.
  637. resp = client.getContext().response_;
  638. ASSERT_EQ(DHCPNAK, static_cast<int>(resp->getType()));
  639. // For DHCPNAK the ciaddr is always 0 (should not be copied) from the
  640. // client's message.
  641. EXPECT_EQ("0.0.0.0", resp->getCiaddr().toText());
  642. }
  643. void
  644. DORATest::oneAllocationOverlapTest(const std::string& clientid_a,
  645. const std::string& clientid_b) {
  646. // Allocate a lease by client using the 4-way exchange.
  647. Dhcp4Client client(Dhcp4Client::SELECTING);
  648. client.includeClientId(clientid_a);
  649. client.setHWAddress("01:02:03:04:05:06");
  650. configure(DORA_CONFIGS[0], *client.getServer());
  651. ASSERT_NO_THROW(client.doDORA());
  652. // Make sure that the server responded.
  653. ASSERT_TRUE(client.getContext().response_);
  654. Pkt4Ptr resp = client.getContext().response_;
  655. // Make sure that the server has responded with DHCPACK.
  656. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  657. Lease4Ptr lease_a = LeaseMgrFactory::instance().getLease4(client.config_.lease_.addr_);
  658. ASSERT_TRUE(lease_a);
  659. // Remember the allocated address.
  660. IOAddress yiaddr = lease_a->addr_;
  661. // Change client identifier. If parameters clientid_a and clientid_b
  662. // are specified correctly, this removes the client identifier from
  663. // client's requests if the lease has been acquired with the client
  664. // identifier, or adds the client identifier otherwise.
  665. client.includeClientId(clientid_b);
  666. // Check if the server will offer the same address.
  667. ASSERT_NO_THROW(client.doDiscover());
  668. resp = client.getContext().response_;
  669. ASSERT_TRUE(resp);
  670. EXPECT_EQ(yiaddr, resp->getYiaddr());
  671. // Client should also be able to renew its address.
  672. client.setState(Dhcp4Client::RENEWING);
  673. ASSERT_NO_THROW(client.doRequest());
  674. ASSERT_TRUE(client.getContext().response_);
  675. resp = client.getContext().response_;
  676. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  677. ASSERT_EQ(yiaddr, client.config_.lease_.addr_);
  678. }
  679. // This test checks the server behavior in the following situation:
  680. // - Client A identifies itself to the server using client identifier
  681. // and the hardware address and requests allocation of the new lease.
  682. // - Server allocates the lease to the client.
  683. // - Client B has the same hardware address but is using a different
  684. // client identifier then Client A.
  685. // - Client B sends DHCPDISCOVER.
  686. // - Server should determine that the client B is not client A, because
  687. // it is using a different client identifier, even though they use the
  688. // same HW address. As a consequence, the server should offer a
  689. // different address to the client B.
  690. // - The client B performs the 4-way exchange again and the server
  691. // allocates a new address to the client, which should be different
  692. // than the address used by the client A.
  693. // - Client B is in the renewing state and it successfully renews its
  694. // address.
  695. // - Client A also renews its address successfully.
  696. TEST_F(DORATest, twoAllocationsOverlap) {
  697. // Allocate a lease by client A using the 4-way exchange.
  698. Dhcp4Client client_a(Dhcp4Client::SELECTING);
  699. client_a.includeClientId("12:34");
  700. client_a.setHWAddress("01:02:03:04:05:06");
  701. configure(DORA_CONFIGS[0], *client_a.getServer());
  702. ASSERT_NO_THROW(client_a.doDORA());
  703. // Make sure that the server responded.
  704. ASSERT_TRUE(client_a.getContext().response_);
  705. Pkt4Ptr resp_a = client_a.getContext().response_;
  706. // Make sure that the server has responded with DHCPACK.
  707. ASSERT_EQ(DHCPACK, static_cast<int>(resp_a->getType()));
  708. // Make sure that the lease has been recorded by the server.
  709. Lease4Ptr lease_a = LeaseMgrFactory::instance().getLease4(client_a.config_.lease_.addr_);
  710. ASSERT_TRUE(lease_a);
  711. // Create client B.
  712. Dhcp4Client client_b(client_a.getServer(), Dhcp4Client::SELECTING);
  713. client_b.setHWAddress("01:02:03:04:05:06");
  714. client_b.includeClientId("45:67");
  715. // Send DHCPDISCOVER and expect the response.
  716. ASSERT_NO_THROW(client_b.doDiscover());
  717. Pkt4Ptr resp_b = client_b.getContext().response_;
  718. // Make sure that the server has responded with DHCPOFFER.
  719. ASSERT_EQ(DHCPOFFER, static_cast<int>(resp_b->getType()));
  720. // The offered address should be different than the address which
  721. // was obtained by the client A.
  722. ASSERT_NE(resp_b->getYiaddr(), client_a.config_.lease_.addr_);
  723. // Make sure that the client A lease hasn't been modified.
  724. lease_a = LeaseMgrFactory::instance().getLease4(client_a.config_.lease_.addr_);
  725. ASSERT_TRUE(lease_a);
  726. // Now that we know that the server will avoid assigning the same
  727. // address that the client A has, use the 4-way exchange to actually
  728. // allocate some address.
  729. ASSERT_NO_THROW(client_b.doDORA());
  730. // Make sure that the server responded.
  731. ASSERT_TRUE(client_b.getContext().response_);
  732. resp_b = client_b.getContext().response_;
  733. // Make sure that the server has responded with DHCPACK.
  734. ASSERT_EQ(DHCPACK, static_cast<int>(resp_b->getType()));
  735. // Again, make sure the assigned addresses are different.
  736. ASSERT_NE(client_b.config_.lease_.addr_, client_a.config_.lease_.addr_);
  737. // Make sure that the client A still has a lease.
  738. lease_a = LeaseMgrFactory::instance().getLease4(client_a.config_.lease_.addr_);
  739. ASSERT_TRUE(lease_a);
  740. // Make sure that the client B has a lease.
  741. Lease4Ptr lease_b = LeaseMgrFactory::instance().getLease4(client_b.config_.lease_.addr_);
  742. ASSERT_TRUE(lease_b);
  743. // Client B should be able to renew its address.
  744. client_b.setState(Dhcp4Client::RENEWING);
  745. ASSERT_NO_THROW(client_b.doRequest());
  746. ASSERT_TRUE(client_b.getContext().response_);
  747. resp_b = client_b.getContext().response_;
  748. ASSERT_EQ(DHCPACK, static_cast<int>(resp_b->getType()));
  749. ASSERT_NE(client_b.config_.lease_.addr_, client_a.config_.lease_.addr_);
  750. // Client A should also be able to renew its address.
  751. client_a.setState(Dhcp4Client::RENEWING);
  752. ASSERT_NO_THROW(client_a.doRequest());
  753. ASSERT_TRUE(client_a.getContext().response_);
  754. resp_b = client_a.getContext().response_;
  755. ASSERT_EQ(DHCPACK, static_cast<int>(resp_b->getType()));
  756. ASSERT_NE(client_a.config_.lease_.addr_, client_b.config_.lease_.addr_);
  757. }
  758. // This test checks the server behavior in the following situation:
  759. // - Client A identifies itself to the server using the hardware address
  760. // and client identifier.
  761. // - Client A performs the 4-way exchange and obtains a lease from the server.
  762. // - Client B uses the same HW address as the client A, but it doesn't use
  763. // the client identifier.
  764. // - Client B sends the DHCPDISCOVER to the server.
  765. // The server determines that there is a lease for the client A using the
  766. // same HW address as the client B. Server discards the client's message and
  767. // doesn't offer the lease for the client B to prevent allocation of the
  768. // lease without a unique identifier.
  769. // - The client sends the DHCPREQUEST and the server sends the DHCPNAK for the
  770. // same reason.
  771. // - Client A renews its address successfully.
  772. TEST_F(DORATest, oneAllocationOverlap1) {
  773. oneAllocationOverlapTest("12:34", "");
  774. }
  775. // This test is similar to oneAllocationOverlap2 but this time the client A
  776. // uses no client identifier, and the client B uses the HW address and the
  777. // client identifier. The server behaves as previously.
  778. TEST_F(DORATest, oneAllocationOverlap2) {
  779. oneAllocationOverlapTest("", "12:34");
  780. }
  781. // This is a simple test for the host reservation. It creates a reservation
  782. // for an address for a single client, identified by the HW address. The
  783. // test verifies that the client using this HW address will obtain a
  784. // lease for the reserved address. It also checks that the client using
  785. // a different HW address will obtain an address from the dynamic pool.
  786. TEST_F(DORATest, reservation) {
  787. // Client A is a one which will have a reservation.
  788. Dhcp4Client clientA(Dhcp4Client::SELECTING);
  789. // Set explicit HW address so as it matches the reservation in the
  790. // configuration used below.
  791. clientA.setHWAddress("aa:bb:cc:dd:ee:ff");
  792. // Configure DHCP server.
  793. configure(DORA_CONFIGS[2], *clientA.getServer());
  794. // Client A performs 4-way exchange and should obtain a reserved
  795. // address.
  796. ASSERT_NO_THROW(clientA.doDORA(boost::shared_ptr<
  797. IOAddress>(new IOAddress("0.0.0.0"))));
  798. // Make sure that the server responded.
  799. ASSERT_TRUE(clientA.getContext().response_);
  800. Pkt4Ptr resp = clientA.getContext().response_;
  801. // Make sure that the server has responded with DHCPACK.
  802. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  803. // Make sure that the client has got the lease for the reserved address.
  804. ASSERT_EQ("10.0.0.7", clientA.config_.lease_.addr_.toText());
  805. // Client B uses the same server as Client A.
  806. Dhcp4Client clientB(clientA.getServer(), Dhcp4Client::SELECTING);
  807. // Client B has no reservation so it should get the lease from
  808. // the dynamic pool.
  809. ASSERT_NO_THROW(clientB.doDORA(boost::shared_ptr<
  810. IOAddress>(new IOAddress("0.0.0.0"))));
  811. // Make sure that the server responded.
  812. ASSERT_TRUE(clientB.getContext().response_);
  813. resp = clientB.getContext().response_;
  814. // Make sure that the server has responded with DHCPACK.
  815. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  816. // Obtain the subnet to which the returned address belongs.
  817. Subnet4Ptr subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->
  818. selectSubnet(clientB.config_.lease_.addr_);
  819. ASSERT_TRUE(subnet);
  820. // Make sure that the address has been allocated from the dynamic pool.
  821. ASSERT_TRUE(subnet->inPool(Lease::TYPE_V4, clientB.config_.lease_.addr_));
  822. }
  823. // This test checks that it is possible to make a reservation by
  824. // DUID carried in the Client Identifier option.
  825. TEST_F(DORATest, reservationByDUID) {
  826. Dhcp4Client client(Dhcp4Client::SELECTING);
  827. // Use relay agent.
  828. client.useRelay(true, IOAddress("10.0.0.1"), IOAddress("10.0.0.2"));
  829. // Modify HW address so as the server doesn't assign reserved
  830. // address by HW address.
  831. client.modifyHWAddr();
  832. // Specify DUID for which address 10.0.0.8 is reserved.
  833. // The value specified as client id includes:
  834. // - FF is a client identifier type for DUID,
  835. // - 45454545 - represents 4 bytes for IAID
  836. // - 01:02:03:04:05 - is an actual DUID for which there is a
  837. // reservation.
  838. client.includeClientId("FF:45:45:45:45:01:02:03:04:05");
  839. // Configure DHCP server.
  840. configure(DORA_CONFIGS[2], *client.getServer());
  841. // Client A performs 4-way exchange and should obtain a reserved
  842. // address.
  843. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  844. IOAddress>(new IOAddress("0.0.0.0"))));
  845. // Make sure that the server responded.
  846. ASSERT_TRUE(client.getContext().response_);
  847. Pkt4Ptr resp = client.getContext().response_;
  848. // Make sure that the server has responded with DHCPACK.
  849. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  850. // Make sure that the client has got the lease for the reserved address.
  851. ASSERT_EQ("10.0.0.8", client.config_.lease_.addr_.toText());
  852. }
  853. // This test checks that it is possible to make a reservation by
  854. // circuit-id inserted by the relay agent.
  855. TEST_F(DORATest, reservationByCircuitId) {
  856. Dhcp4Client client(Dhcp4Client::SELECTING);
  857. // Use relay agent so as the circuit-id can be inserted.
  858. client.useRelay(true, IOAddress("10.0.0.1"), IOAddress("10.0.0.2"));
  859. // Specify circuit-id.
  860. client.setCircuitId("charter950");
  861. // Configure DHCP server.
  862. configure(DORA_CONFIGS[2], *client.getServer());
  863. // Client A performs 4-way exchange and should obtain a reserved
  864. // address.
  865. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  866. IOAddress>(new IOAddress("0.0.0.0"))));
  867. // Make sure that the server responded.
  868. ASSERT_TRUE(client.getContext().response_);
  869. Pkt4Ptr resp = client.getContext().response_;
  870. // Make sure that the server has responded with DHCPACK.
  871. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  872. // Make sure that the client has got the lease for the reserved address.
  873. ASSERT_EQ("10.0.0.9", client.config_.lease_.addr_.toText());
  874. }
  875. // This test checks that it is possible to make a reservation by
  876. // client-id.
  877. TEST_F(DORATest, reservationByClientId) {
  878. Dhcp4Client client(Dhcp4Client::SELECTING);
  879. // Use relay agent to make sure that the desired subnet is
  880. // selected for our client.
  881. client.useRelay(true, IOAddress("10.0.0.20"), IOAddress("10.0.0.21"));
  882. // Specify client identifier.
  883. client.includeClientId("01:11:22:33:44:55:66");
  884. // Configure DHCP server.
  885. configure(DORA_CONFIGS[2], *client.getServer());
  886. // Client A performs 4-way exchange and should obtain a reserved
  887. // address.
  888. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  889. IOAddress>(new IOAddress("0.0.0.0"))));
  890. // Make sure that the server responded.
  891. ASSERT_TRUE(client.getContext().response_);
  892. Pkt4Ptr resp = client.getContext().response_;
  893. // Make sure that the server has responded with DHCPACK.
  894. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  895. // Make sure that the client has got the lease for the reserved address.
  896. ASSERT_EQ("10.0.0.1", client.config_.lease_.addr_.toText());
  897. }
  898. // This test verifies that order in which host identifiers are used to
  899. // retrieve host reservations can be controlled.
  900. TEST_F(DORATest, hostIdentifiersOrder) {
  901. Dhcp4Client client(Dhcp4Client::SELECTING);
  902. client.setHWAddress("aa:bb:cc:dd:ee:ff");
  903. // Use relay agent so as the circuit-id can be inserted.
  904. client.useRelay(true, IOAddress("10.0.0.1"), IOAddress("10.0.0.2"));
  905. // Specify DUID for which address 10.0.0.8 is reserved.
  906. // The value specified as client id includes:
  907. // - FF is a client identifier type for DUID,
  908. // - 45454545 - represents 4 bytes for IAID
  909. // - 01:02:03:04:05 - is an actual DUID for which there is a
  910. // reservation.
  911. client.includeClientId("FF:45:45:45:45:01:02:03:04:05");
  912. // Specify circuit-id.
  913. client.setCircuitId("charter950");
  914. // Configure DHCP server.
  915. configure(DORA_CONFIGS[2], *client.getServer());
  916. // Perform 4-way exchange to obtain reserved address.
  917. // The client has in fact two reserved addresses, but the one assigned
  918. // should be by hw-address.
  919. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  920. IOAddress>(new IOAddress("0.0.0.0"))));
  921. // Make sure that the server responded.
  922. ASSERT_TRUE(client.getContext().response_);
  923. Pkt4Ptr resp = client.getContext().response_;
  924. // Make sure that the server has responded with DHCPACK.
  925. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  926. // Make sure that the client has got the lease for the reserved address.
  927. ASSERT_EQ("10.0.0.7", client.config_.lease_.addr_.toText());
  928. // Reconfigure the server to change the preference order of the
  929. // host identifiers. The 'circuit-id' should now take precedence over
  930. // the hw-address, duid and client-id.
  931. configure(DORA_CONFIGS[4], *client.getServer());
  932. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  933. IOAddress>(new IOAddress("0.0.0.0"))));
  934. // Make sure that the server responded.
  935. ASSERT_TRUE(client.getContext().response_);
  936. resp = client.getContext().response_;
  937. // Make sure that the server has responded with DHCPACK.
  938. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  939. // Make sure that the client has got the lease for the reserved address.
  940. ASSERT_EQ("10.0.0.9", client.config_.lease_.addr_.toText());
  941. // Reconfigure the server to change the preference order of the
  942. // host identifiers. The 'duid' should now take precedence over
  943. // the client-id, hw-address and circuit-id
  944. configure(DORA_CONFIGS[5], *client.getServer());
  945. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  946. IOAddress>(new IOAddress("0.0.0.0"))));
  947. // Make sure that the server responded.
  948. ASSERT_TRUE(client.getContext().response_);
  949. resp = client.getContext().response_;
  950. // Make sure that the server has responded with DHCPACK.
  951. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  952. // Make sure that the client has got the lease for the reserved address.
  953. ASSERT_EQ("10.0.0.8", client.config_.lease_.addr_.toText());
  954. // Replace the client identifier with the one for which address
  955. // 10.0.0.1 is reserved. Because the DUID is a special type of
  956. // client identifier, this change effectively removes the association
  957. // of the client with the DUID for which address 10.0.0.8 is reserved.
  958. // The next identifier type to be used by the server (after DUID) is
  959. // client-id and thus the server should assign address 10.0.0.1.
  960. client.includeClientId("01:11:22:33:44:55:66");
  961. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  962. IOAddress>(new IOAddress("0.0.0.0"))));
  963. // Make sure that the server responded.
  964. ASSERT_TRUE(client.getContext().response_);
  965. resp = client.getContext().response_;
  966. // Make sure that the server has responded with DHCPACK.
  967. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  968. // Make sure that the client has got the lease for the reserved address.
  969. ASSERT_EQ("10.0.0.1", client.config_.lease_.addr_.toText());
  970. }
  971. // This test checks that setting the match-client-id value to false causes
  972. // the server to ignore changing client identifier when the client is
  973. // using consistent HW address.
  974. TEST_F(DORATest, ignoreChangingClientId) {
  975. Dhcp4Client client(Dhcp4Client::SELECTING);
  976. // Configure DHCP server.
  977. configure(DORA_CONFIGS[3], *client.getServer());
  978. client.includeClientId("12:12");
  979. // Obtain the lease using 4-way exchange.
  980. ASSERT_NO_THROW(client.doDORA());
  981. // Make sure that the server responded.
  982. ASSERT_TRUE(client.getContext().response_);
  983. Pkt4Ptr resp = client.getContext().response_;
  984. // Make sure that the server has responded with DHCPACK.
  985. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  986. EXPECT_FALSE(client.config_.lease_.client_id_);
  987. // Remember address which the client has obtained.
  988. IOAddress leased_address = client.config_.lease_.addr_;
  989. // Modify client id. Because we have set the configuration flag which
  990. // forces the server to lookup leases using the HW address, the
  991. // client id modification should not matter and the client should
  992. // obtain the same lease.
  993. client.includeClientId("14:14");
  994. // Obtain the lease using 4-way exchange.
  995. ASSERT_NO_THROW(client.doDORA());
  996. // Make sure that the server responded.
  997. ASSERT_TRUE(client.getContext().response_);
  998. resp = client.getContext().response_;
  999. // Make sure that the server has responded with DHCPACK.
  1000. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  1001. // Make sure that the server assigned the same address, even though the
  1002. // client id has changed.
  1003. EXPECT_EQ(leased_address, client.config_.lease_.addr_);
  1004. // Check that the client id is not present in the lease.
  1005. EXPECT_FALSE(client.config_.lease_.client_id_);
  1006. }
  1007. // This test checks that the match-client-id parameter doesn't have
  1008. // effect on the lease lookup using the HW address.
  1009. TEST_F(DORATest, changingHWAddress) {
  1010. Dhcp4Client client(Dhcp4Client::SELECTING);
  1011. // Configure DHCP server.
  1012. configure(DORA_CONFIGS[3], *client.getServer());
  1013. client.includeClientId("12:12");
  1014. client.setHWAddress("00:01:02:03:04:05");
  1015. // Obtain the lease using 4-way exchange.
  1016. ASSERT_NO_THROW(client.doDORA());
  1017. // Make sure that the server responded.
  1018. ASSERT_TRUE(client.getContext().response_);
  1019. Pkt4Ptr resp = client.getContext().response_;
  1020. // Make sure that the server has responded with DHCPACK.
  1021. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  1022. // Check that the client id is not present in the lease.
  1023. EXPECT_FALSE(client.config_.lease_.client_id_);
  1024. // Remember address which the client has obtained.
  1025. IOAddress leased_address = client.config_.lease_.addr_;
  1026. // Modify HW address but leave client id in place. The value of the
  1027. // match-client-id set to false must not have any effect on the
  1028. // case when the HW address is changing. In such case the server will
  1029. // allocate the new address for the client.
  1030. client.setHWAddress("01:01:01:01:01:01");
  1031. // Obtain a lease.
  1032. ASSERT_NO_THROW(client.doDORA());
  1033. // Make sure that the server responded.
  1034. ASSERT_TRUE(client.getContext().response_);
  1035. resp = client.getContext().response_;
  1036. // Make sure that the server has responded with DHCPACK.
  1037. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  1038. // Client must assign different address because the client id is
  1039. // ignored and the HW address was changed.
  1040. EXPECT_NE(client.config_.lease_.addr_, leased_address);
  1041. // Check that the client id is not present in the lease.
  1042. EXPECT_FALSE(client.config_.lease_.client_id_);
  1043. }
  1044. // This test verifies that the server assigns reserved values for the
  1045. // siaddr, sname and file fields carried within DHCPv4 message.
  1046. TEST_F(DORATest, messageFieldsReservations) {
  1047. // Client has a reservation.
  1048. Dhcp4Client client(Dhcp4Client::SELECTING);
  1049. // Set explicit HW address so as it matches the reservation in the
  1050. // configuration used below.
  1051. client.setHWAddress("aa:bb:cc:dd:ee:ff");
  1052. // Configure DHCP server.
  1053. configure(DORA_CONFIGS[6], *client.getServer());
  1054. // Client performs 4-way exchange and should obtain a reserved
  1055. // address and fixed fields.
  1056. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  1057. IOAddress>(new IOAddress("0.0.0.0"))));
  1058. // Make sure that the server responded.
  1059. ASSERT_TRUE(client.getContext().response_);
  1060. Pkt4Ptr resp = client.getContext().response_;
  1061. // Make sure that the server has responded with DHCPACK.
  1062. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  1063. // Check that the reserved values have been assigned.
  1064. EXPECT_EQ("10.0.0.7", client.config_.siaddr_.toText());
  1065. EXPECT_EQ("some-name.example.org", client.config_.sname_);
  1066. EXPECT_EQ("bootfile.efi", client.config_.boot_file_name_);
  1067. }
  1068. // This test checks the following scenario:
  1069. // 1. Client A performs 4-way exchange and obtains a lease from the dynamic pool.
  1070. // 2. Reservation is created for the client A using an address out of the dynamic
  1071. // pool.
  1072. // 3. Client A renews the lease.
  1073. // 4. Server responds with DHCPNAK to indicate that the client should stop using
  1074. // an address for which it has a lease. Server doesn't want to renew an
  1075. // address for which the client doesn't have a reservation, while it has
  1076. // a reservation for a different address.
  1077. // 5. Client A receives a DHCPNAK and returns to the DHCP server discovery.
  1078. // 6. Client A performs a 4-way exchange with a server and the server allocates
  1079. // a reserved address to the Client A.
  1080. // 7. Client A renews the allocated address and the server returns a DHCPACK.
  1081. // 8. Reservation for the Client A is removed.
  1082. // 9. Client A renews the (previously reserved) lease and the server returns
  1083. // DHCPNAK because the address in use is neither reserved nor belongs to
  1084. // the dynamic pool.
  1085. // 10. Client A returns to the DHCP server discovery.
  1086. // 11. Client A uses 4-way exchange to obtain a lease from the dynamic pool.
  1087. // 12. The new address that the Client A is using is reserved for Client B.
  1088. // Client A still holds this address.
  1089. // 13. Client B uses 4-way exchange to obtain a new lease.
  1090. // 14. The server determines that the Client B has a reservation for the
  1091. // address which is in use by Client A and offers an address different
  1092. // than reserved.
  1093. // 15. Client B requests the allocation of the offered address and the server
  1094. // allocates this address.
  1095. // 16. Client A renews the lease.
  1096. // 17. The server determines that the address that Client A is using is reserved
  1097. // for Client B. The server returns DHCPNAK to the Client A.
  1098. // 18. Client B uses 4-way exchange to obtain the reserved lease but the lease
  1099. // for the Client A hasn't been removed yet. Client B is assigned the same
  1100. // address it has been using.
  1101. // 19. Client A uses 4-way exchange to allocate a new lease.
  1102. // 20. The server allocates a new lease from the dynamic pool but it avoids
  1103. // allocating the address reserved for the Client B.
  1104. // 21. Client B uses 4-way exchange to obtain a new lease.
  1105. // 22. The server finally allocates a reserved address to the Client B.
  1106. TEST_F(DORATest, reservationsWithConflicts) {
  1107. Dhcp4Client client(Dhcp4Client::SELECTING);
  1108. // Configure DHCP server.
  1109. configure(DORA_CONFIGS[0], *client.getServer());
  1110. // Client A performs 4-way exchange and obtains a lease from the
  1111. // dynamic pool.
  1112. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  1113. IOAddress>(new IOAddress("10.0.0.50"))));
  1114. // Make sure that the server responded.
  1115. ASSERT_TRUE(client.getContext().response_);
  1116. Pkt4Ptr resp = client.getContext().response_;
  1117. // Make sure that the server has responded with DHCPACK.
  1118. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  1119. // Make sure that the client has got the lease with the requested address.
  1120. ASSERT_EQ("10.0.0.50", client.config_.lease_.addr_.toText());
  1121. configure(DORA_CONFIGS[0], false);
  1122. // Reservation is created for the client A using an address out of the
  1123. // dynamic pool.
  1124. HostPtr host(new Host(&client.getHWAddress()->hwaddr_[0],
  1125. client.getHWAddress()->hwaddr_.size(),
  1126. Host::IDENT_HWADDR, SubnetID(1),
  1127. SubnetID(0), IOAddress("10.0.0.9")));
  1128. CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
  1129. CfgMgr::instance().commit();
  1130. // Let's transition the client to Renewing state.
  1131. client.setState(Dhcp4Client::RENEWING);
  1132. // Set the unicast destination address to indicate that it is a renewal.
  1133. client.setDestAddress(IOAddress("10.0.0.1"));
  1134. ASSERT_NO_THROW(client.doRequest());
  1135. // Client should get the DHCPNAK from the server because the client has
  1136. // a reservation for a different address that it is trying to renew.
  1137. resp = client.getContext().response_;
  1138. ASSERT_EQ(DHCPNAK, static_cast<int>(resp->getType()));
  1139. // A conforming client would go back to the server discovery.
  1140. client.setState(Dhcp4Client::SELECTING);
  1141. // Obtain a lease from the server using the 4-way exchange.
  1142. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  1143. IOAddress>(new IOAddress("0.0.0.0"))));
  1144. // Make sure that the server responded.
  1145. ASSERT_TRUE(client.getContext().response_);
  1146. resp = client.getContext().response_;
  1147. // Make sure that the server has responded with DHCPACK with a reserved
  1148. // address
  1149. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  1150. ASSERT_EQ("10.0.0.9", client.config_.lease_.addr_.toText());
  1151. // Client A renews the allocated address.
  1152. client.setState(Dhcp4Client::RENEWING);
  1153. // Set the unicast destination address to indicate that it is a renewal.
  1154. client.setDestAddress(IOAddress("10.0.0.1"));
  1155. ASSERT_NO_THROW(client.doRequest());
  1156. // Make sure the server responded and renewed the client's address.
  1157. resp = client.getContext().response_;
  1158. ASSERT_EQ("10.0.0.9", client.config_.lease_.addr_.toText());
  1159. // By reconfiguring the server, we remove the existing reservations.
  1160. configure(DORA_CONFIGS[0]);
  1161. // Try to renew the existing lease again.
  1162. ASSERT_NO_THROW(client.doRequest());
  1163. // The reservation has been removed, so the server should respond with
  1164. // a DHCPNAK because the address that the client is using doesn't belong
  1165. // to a dynamic pool.
  1166. resp = client.getContext().response_;
  1167. ASSERT_EQ(DHCPNAK, static_cast<int>(resp->getType()));
  1168. // A conforming client would go back to the server discovery.
  1169. client.setState(Dhcp4Client::SELECTING);
  1170. // Obtain a lease from the server using the 4-way exchange.
  1171. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  1172. IOAddress>(new IOAddress("0.0.0.0"))));
  1173. // Make sure that the server responded.
  1174. ASSERT_TRUE(client.getContext().response_);
  1175. resp = client.getContext().response_;
  1176. // Make sure that the server has responded with DHCPACK.
  1177. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  1178. // Obtain the subnet to which the returned address belongs.
  1179. Subnet4Ptr subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->
  1180. selectSubnet(client.config_.lease_.addr_);
  1181. ASSERT_TRUE(subnet);
  1182. // Make sure that the address has been allocated from the dynamic pool.
  1183. ASSERT_TRUE(subnet->inPool(Lease::TYPE_V4, client.config_.lease_.addr_));
  1184. // Remember the address allocated in the dynamic pool.
  1185. IOAddress in_pool_addr = client.config_.lease_.addr_;
  1186. // Create Client B.
  1187. Dhcp4Client clientB(client.getServer());
  1188. clientB.modifyHWAddr();
  1189. // Create reservation for the Client B, for the address that the
  1190. // Client A is using.
  1191. configure(DORA_CONFIGS[0], false);
  1192. host.reset(new Host(&clientB.getHWAddress()->hwaddr_[0],
  1193. clientB.getHWAddress()->hwaddr_.size(),
  1194. Host::IDENT_HWADDR, SubnetID(1),
  1195. SubnetID(0), in_pool_addr));
  1196. CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
  1197. CfgMgr::instance().commit();
  1198. // Client B performs a DHCPDISCOVER.
  1199. clientB.setState(Dhcp4Client::SELECTING);
  1200. // The server determines that the address reserved for Client B is
  1201. // in use by Client A so it offers a different address.
  1202. ASSERT_NO_THROW(clientB.doDORA(boost::shared_ptr<
  1203. IOAddress>(new IOAddress("0.0.0.0"))));
  1204. ASSERT_TRUE(clientB.getContext().response_);
  1205. ASSERT_EQ(DHCPACK, static_cast<int>(clientB.getContext().response_->getType()));
  1206. IOAddress client_b_addr = clientB.config_.lease_.addr_;
  1207. ASSERT_NE(client_b_addr, in_pool_addr);
  1208. // Client A renews the lease.
  1209. client.setState(Dhcp4Client::RENEWING);
  1210. // Set the unicast destination address to indicate that it is a renewal.
  1211. client.setDestAddress(IOAddress(in_pool_addr));
  1212. ASSERT_NO_THROW(client.doRequest());
  1213. // Client A should get a DHCPNAK because it is using an address reserved
  1214. // for Client B.
  1215. resp = client.getContext().response_;
  1216. ASSERT_EQ(DHCPNAK, static_cast<int>(resp->getType()));
  1217. // Client B performs 4-way exchange but still gets an address from the
  1218. // dynamic pool, because Client A hasn't obtained a new lease, so it is
  1219. // still using an address reserved for Client B.
  1220. clientB.setState(Dhcp4Client::SELECTING);
  1221. // Obtain a lease from the server using the 4-way exchange.
  1222. ASSERT_NO_THROW(clientB.doDORA(boost::shared_ptr<
  1223. IOAddress>(new IOAddress("0.0.0.0"))));
  1224. // Make sure that the server responded.
  1225. ASSERT_TRUE(clientB.getContext().response_);
  1226. ASSERT_EQ(DHCPACK, static_cast<int>(clientB.getContext().response_->getType()));
  1227. ASSERT_NE(clientB.config_.lease_.addr_, in_pool_addr);
  1228. ASSERT_EQ(client_b_addr, clientB.config_.lease_.addr_);
  1229. // Client B renews its lease.
  1230. clientB.setState(Dhcp4Client::RENEWING);
  1231. clientB.setDestAddress(IOAddress("10.0.0.1"));
  1232. ASSERT_NO_THROW(clientB.doRequest());
  1233. // The server should renew the client's B lease because the address
  1234. // reserved for client B is still in use by the client A.
  1235. ASSERT_TRUE(clientB.getContext().response_);
  1236. EXPECT_EQ(DHCPACK, static_cast<int>(clientB.getContext().response_->getType()));
  1237. ASSERT_NE(clientB.config_.lease_.addr_, in_pool_addr);
  1238. ASSERT_EQ(client_b_addr, clientB.config_.lease_.addr_);
  1239. // Client A performs 4-way exchange.
  1240. client.setState(Dhcp4Client::SELECTING);
  1241. // Revert to the broadcast address for the selecting client.
  1242. client.setDestAddress(IOAddress::IPV4_BCAST_ADDRESS());
  1243. // Obtain a lease from the server using the 4-way exchange.
  1244. ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
  1245. IOAddress>(new IOAddress("0.0.0.0"))));
  1246. // Make sure that the server responded.
  1247. ASSERT_TRUE(client.getContext().response_);
  1248. resp = client.getContext().response_;
  1249. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  1250. // The server should have assigned a different address than the one
  1251. // reserved for the Client B.
  1252. ASSERT_NE(client.config_.lease_.addr_, in_pool_addr);
  1253. subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->
  1254. selectSubnet(client.config_.lease_.addr_);
  1255. ASSERT_TRUE(subnet);
  1256. ASSERT_TRUE(subnet->inPool(Lease::TYPE_V4, client.config_.lease_.addr_));
  1257. // Client B renews again.
  1258. ASSERT_NO_THROW(clientB.doRequest());
  1259. // The client B should now receive the DHCPNAK from the server because
  1260. // the reserved address is now available and the client should
  1261. // revert to the DHCPDISCOVER to obtain it.
  1262. ASSERT_TRUE(clientB.getContext().response_);
  1263. EXPECT_EQ(DHCPNAK, static_cast<int>(clientB.getContext().response_->getType()));
  1264. // Client B performs 4-way exchange and obtains a lease for the
  1265. // reserved address.
  1266. clientB.setState(Dhcp4Client::SELECTING);
  1267. ASSERT_NO_THROW(clientB.doDORA(boost::shared_ptr<
  1268. IOAddress>(new IOAddress("0.0.0.0"))));
  1269. // Make sure that the server responded.
  1270. ASSERT_TRUE(clientB.getContext().response_);
  1271. resp = clientB.getContext().response_;
  1272. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  1273. ASSERT_EQ(in_pool_addr, clientB.config_.lease_.addr_);
  1274. }
  1275. /// This test verifies that after a client completes its DORA exchange,
  1276. /// appropriate statistics are updated.
  1277. TEST_F(DORATest, statisticsDORA) {
  1278. Dhcp4Client client(Dhcp4Client::SELECTING);
  1279. // Configure DHCP server.
  1280. configure(DORA_CONFIGS[0], *client.getServer());
  1281. // Perform 4-way exchange with the server but to not request any
  1282. // specific address in the DHCPDISCOVER message.
  1283. ASSERT_NO_THROW(client.doDORA());
  1284. // Make sure that the server responded.
  1285. ASSERT_TRUE(client.getContext().response_);
  1286. Pkt4Ptr resp = client.getContext().response_;
  1287. // Make sure that the server has responded with DHCPACK.
  1288. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  1289. // Ok, let's check the statistics.
  1290. using namespace isc::stats;
  1291. StatsMgr& mgr = StatsMgr::instance();
  1292. ObservationPtr pkt4_received = mgr.getObservation("pkt4-received");
  1293. ObservationPtr pkt4_discover_received = mgr.getObservation("pkt4-discover-received");
  1294. ObservationPtr pkt4_offer_sent = mgr.getObservation("pkt4-offer-sent");
  1295. ObservationPtr pkt4_request_received = mgr.getObservation("pkt4-request-received");
  1296. ObservationPtr pkt4_ack_sent = mgr.getObservation("pkt4-ack-sent");
  1297. ObservationPtr pkt4_sent = mgr.getObservation("pkt4-sent");
  1298. // All expected statistics must be present.
  1299. ASSERT_TRUE(pkt4_received);
  1300. ASSERT_TRUE(pkt4_discover_received);
  1301. ASSERT_TRUE(pkt4_offer_sent);
  1302. ASSERT_TRUE(pkt4_request_received);
  1303. ASSERT_TRUE(pkt4_ack_sent);
  1304. ASSERT_TRUE(pkt4_sent);
  1305. // They also must have expected values.
  1306. EXPECT_EQ(2, pkt4_received->getInteger().first);
  1307. EXPECT_EQ(1, pkt4_discover_received->getInteger().first);
  1308. EXPECT_EQ(1, pkt4_offer_sent->getInteger().first);
  1309. EXPECT_EQ(1, pkt4_request_received->getInteger().first);
  1310. EXPECT_EQ(1, pkt4_ack_sent->getInteger().first);
  1311. EXPECT_EQ(2, pkt4_sent->getInteger().first);
  1312. // Let the client send request 3 times, which should make the server
  1313. // to send 3 acks.
  1314. client.setState(Dhcp4Client::RENEWING);
  1315. ASSERT_NO_THROW(client.doRequest());
  1316. ASSERT_NO_THROW(client.doRequest());
  1317. ASSERT_NO_THROW(client.doRequest());
  1318. // Let's see if the stats are properly updated.
  1319. EXPECT_EQ(5, pkt4_received->getInteger().first);
  1320. EXPECT_EQ(1, pkt4_discover_received->getInteger().first);
  1321. EXPECT_EQ(1, pkt4_offer_sent->getInteger().first);
  1322. EXPECT_EQ(4, pkt4_request_received->getInteger().first);
  1323. EXPECT_EQ(4, pkt4_ack_sent->getInteger().first);
  1324. EXPECT_EQ(5, pkt4_sent->getInteger().first);
  1325. }
  1326. // This test verifies that after a client completes an exchange that result
  1327. // in NAK, appropriate statistics are updated.
  1328. TEST_F(DORATest, statisticsNAK) {
  1329. Dhcp4Client client(Dhcp4Client::SELECTING);
  1330. // Configure DHCP server.
  1331. configure(DORA_CONFIGS[0], *client.getServer());
  1332. // Obtain a lease from the server using the 4-way exchange.
  1333. // Get a lease.
  1334. client.doDORA();
  1335. // Wipe all stats.
  1336. isc::stats::StatsMgr::instance().removeAll();
  1337. client.setState(Dhcp4Client::INIT_REBOOT);
  1338. client.config_.lease_.addr_ = IOAddress("10.0.0.30");
  1339. ASSERT_NO_THROW(client.doRequest());
  1340. // Make sure that the server responded.
  1341. ASSERT_TRUE(client.getContext().response_);
  1342. Pkt4Ptr resp = client.getContext().response_;
  1343. EXPECT_EQ(DHCPNAK, static_cast<int>(resp->getType()));
  1344. using namespace isc::stats;
  1345. StatsMgr& mgr = StatsMgr::instance();
  1346. ObservationPtr pkt4_received = mgr.getObservation("pkt4-received");
  1347. ObservationPtr pkt4_request_received = mgr.getObservation("pkt4-request-received");
  1348. ObservationPtr pkt4_ack_sent = mgr.getObservation("pkt4-ack-sent");
  1349. ObservationPtr pkt4_nak_sent = mgr.getObservation("pkt4-nak-sent");
  1350. ObservationPtr pkt4_sent = mgr.getObservation("pkt4-sent");
  1351. // All expected statistics must be present.
  1352. ASSERT_TRUE(pkt4_received);
  1353. ASSERT_TRUE(pkt4_request_received);
  1354. ASSERT_FALSE(pkt4_ack_sent); // No acks were sent, no such statistic expected.
  1355. ASSERT_TRUE(pkt4_nak_sent);
  1356. ASSERT_TRUE(pkt4_sent);
  1357. // They also must have expected values.
  1358. EXPECT_EQ(1, pkt4_received->getInteger().first);
  1359. EXPECT_EQ(1, pkt4_request_received->getInteger().first);
  1360. EXPECT_EQ(1, pkt4_nak_sent->getInteger().first);
  1361. EXPECT_EQ(1, pkt4_sent->getInteger().first);
  1362. }
  1363. void
  1364. DORATest::testMultiStageBoot(const unsigned int config_index) {
  1365. Dhcp4Client client(Dhcp4Client::SELECTING);
  1366. // Configure DHCP server.
  1367. ASSERT_NO_THROW(configure(DORA_CONFIGS[config_index],
  1368. *client.getServer()));
  1369. // Stage 1: get the first lease for our client. In PXE boot, it would be
  1370. // a stage when the BIOS requests a lease.
  1371. // Include client id apart from the MAC address.
  1372. client.includeClientId("10:21:32:AB:CD:EF");
  1373. ASSERT_NO_THROW(client.doDORA());
  1374. // Make sure that the server responded.
  1375. ASSERT_TRUE(client.getContext().response_);
  1376. Pkt4Ptr resp = client.getContext().response_;
  1377. // Make sure that the server has responded with DHCPACK.
  1378. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  1379. // Make sure that the client has got the lease which belongs
  1380. // to a pool.
  1381. IOAddress leased_address1 = client.config_.lease_.addr_;
  1382. Subnet4Ptr subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->
  1383. selectSubnet(leased_address1);
  1384. ASSERT_TRUE(subnet);
  1385. // Make sure that the address has been allocated from the dynamic pool.
  1386. ASSERT_TRUE(subnet->inPool(Lease::TYPE_V4, leased_address1));
  1387. // Stage 2: the client with a given MAC address has a lease in the
  1388. // lease database. The installer comes up and uses the same MAC address
  1389. // but generates a different client id. The server should treat the
  1390. // client with modified client identifier as a different client and
  1391. // create a new lease for it.
  1392. // Modify client identifier.
  1393. client.includeClientId("11:54:45:AB:AA:FE");
  1394. ASSERT_NO_THROW(client.doDORA());
  1395. // Make sure that the server responded.
  1396. ASSERT_TRUE(client.getContext().response_);
  1397. resp = client.getContext().response_;
  1398. // Make sure that the server has responded with DHCPACK.
  1399. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  1400. // Make sure that the client has got the lease which belongs
  1401. // to a pool.
  1402. IOAddress leased_address2 = client.config_.lease_.addr_;
  1403. // Make sure that the address has been allocated from the dynamic pool.
  1404. ASSERT_TRUE(subnet->inPool(Lease::TYPE_V4, leased_address2));
  1405. // The client should have got a new lease.
  1406. ASSERT_NE(leased_address1, leased_address2);
  1407. // Modify client identifier again.
  1408. client.includeClientId("22:34:AC:BE:44:54");
  1409. ASSERT_NO_THROW(client.doDORA());
  1410. // Make sure that the server responded.
  1411. ASSERT_TRUE(client.getContext().response_);
  1412. resp = client.getContext().response_;
  1413. // Make sure that the server has responded with DHCPACK.
  1414. ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
  1415. // Make sure that the client has got the lease which belongs
  1416. // to a pool.
  1417. IOAddress leased_address3 = client.config_.lease_.addr_;
  1418. // Make sure that the address has been allocated from the dynamic pool.
  1419. ASSERT_TRUE(subnet->inPool(Lease::TYPE_V4, leased_address3));
  1420. // The client should have got a new lease.
  1421. ASSERT_NE(leased_address1, leased_address3);
  1422. ASSERT_NE(leased_address2, leased_address3);
  1423. }
  1424. // Test that the client using the same hardware address but multiple
  1425. // client identifiers will obtain multiple leases.
  1426. TEST_F(DORATest, multiStageBoot) {
  1427. // DORA_CONFIGS[0] to be used for server configuration.
  1428. testMultiStageBoot(0);
  1429. }
  1430. // Starting tests which require MySQL backend availability. Those tests
  1431. // will not be executed if Kea has been compiled without the
  1432. // --with-dhcp-mysql.
  1433. #ifdef HAVE_MYSQL
  1434. /// @brief Test fixture class for the test utilizing MySQL database backend.
  1435. class DORAMySQLTest : public DORATest {
  1436. public:
  1437. /// @brief Constructor.
  1438. ///
  1439. /// Recreates MySQL schema for a test.
  1440. DORAMySQLTest() : DORATest() {
  1441. destroyMySQLSchema();
  1442. createMySQLSchema();
  1443. }
  1444. /// @brief Destructor.
  1445. ///
  1446. /// Destroys MySQL schema.
  1447. virtual ~DORAMySQLTest() {
  1448. destroyMySQLSchema();
  1449. }
  1450. };
  1451. // Test that the client using the same hardware address but multiple
  1452. // client identifiers will obtain multiple leases (MySQL lease database).
  1453. TEST_F(DORAMySQLTest, multiStageBoot) {
  1454. // DORA_CONFIGS[7] to be used for server configuration.
  1455. testMultiStageBoot(7);
  1456. }
  1457. #endif
  1458. // Starting tests which require MySQL backend availability. Those tests
  1459. // will not be executed if Kea has been compiled without the
  1460. // --with-dhcp-pgsql.
  1461. #ifdef HAVE_PGSQL
  1462. /// @brief Test fixture class for the test utilizing PostgreSQL database backend.
  1463. class DORAPgSQLTest : public DORATest {
  1464. public:
  1465. /// @brief Constructor.
  1466. ///
  1467. /// Recreates PgSQL schema for a test.
  1468. DORAPgSQLTest() : DORATest() {
  1469. destroyPgSQLSchema();
  1470. createPgSQLSchema();
  1471. }
  1472. /// @brief Destructor.
  1473. ///
  1474. /// Destroys PgSQL schema.
  1475. virtual ~DORAPgSQLTest() {
  1476. destroyPgSQLSchema();
  1477. }
  1478. };
  1479. // Test that the client using the same hardware address but multiple
  1480. // client identifiers will obtain multiple leases (PostgreSQL lease database).
  1481. TEST_F(DORAPgSQLTest, multiStageBoot) {
  1482. // DORA_CONFIGS[8] to be used for server configuration.
  1483. testMultiStageBoot(8);
  1484. }
  1485. #endif
  1486. #ifdef HAVE_CQL
  1487. /// @brief Test fixture class for the test utilizing Cassandra database backend.
  1488. class DORACQLTest : public DORATest {
  1489. public:
  1490. /// @brief Constructor.
  1491. ///
  1492. /// Recreates CQL schema for a test.
  1493. DORACQLTest() : DORATest() {
  1494. destroyCqlSchema(false, true);
  1495. createCqlSchema(false, true);
  1496. }
  1497. /// @brief Destructor.
  1498. ///
  1499. /// Destroys CQL schema.
  1500. virtual ~DORACQLTest() {
  1501. destroyCqlSchema(false, true);
  1502. }
  1503. };
  1504. // Test that the client using the same hardware address but multiple
  1505. // client identifiers will obtain multiple leases (CQL lease database).
  1506. TEST_F(DORACQLTest, multiStageBoot) {
  1507. // DORA_CONFIGS[9] to be used for server configuration.
  1508. testMultiStageBoot(9);
  1509. }
  1510. #endif
  1511. } // end of anonymous namespace