config_parser_unittest.cc 177 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869
  1. // Copyright (C) 2012-2016 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 <cc/command_interpreter.h>
  8. #include <config/module_spec.h>
  9. #include <dhcp/libdhcp++.h>
  10. #include <dhcp/option6_ia.h>
  11. #include <dhcp/iface_mgr.h>
  12. #include <dhcp/option_custom.h>
  13. #include <dhcp/option_int.h>
  14. #include <dhcp/option6_addrlst.h>
  15. #include <dhcp/tests/iface_mgr_test_config.h>
  16. #include <dhcp6/json_config_parser.h>
  17. #include <dhcp6/dhcp6_srv.h>
  18. #include <dhcpsrv/addr_utilities.h>
  19. #include <dhcpsrv/cfgmgr.h>
  20. #include <dhcpsrv/cfg_expiration.h>
  21. #include <dhcpsrv/cfg_hosts.h>
  22. #include <dhcpsrv/subnet.h>
  23. #include <dhcpsrv/subnet_selector.h>
  24. #include <dhcpsrv/testutils/config_result_check.h>
  25. #include <hooks/hooks_manager.h>
  26. #include <defaults.h>
  27. #include "test_data_files_config.h"
  28. #include "test_libraries.h"
  29. #include "marker_file.h"
  30. #include <boost/foreach.hpp>
  31. #include <gtest/gtest.h>
  32. #include <fstream>
  33. #include <iostream>
  34. #include <fstream>
  35. #include <sstream>
  36. #include <string>
  37. #include <vector>
  38. #include <arpa/inet.h>
  39. #include <unistd.h>
  40. using namespace isc;
  41. using namespace isc::asiolink;
  42. using namespace isc::config;
  43. using namespace isc::data;
  44. using namespace isc::dhcp;
  45. using namespace isc::dhcp::test;
  46. using namespace isc::hooks;
  47. using namespace std;
  48. namespace {
  49. const char* PARSER_CONFIGS[] = {
  50. // CONFIGURATION 0: one subnet with one pool, no user contexts
  51. "{"
  52. " \"interfaces-config\": {"
  53. " \"interfaces\": [\"*\" ]"
  54. " },"
  55. " \"valid-lifetime\": 4000,"
  56. " \"preferred-lifetime\": 3000,"
  57. " \"rebind-timer\": 2000,"
  58. " \"renew-timer\": 1000,"
  59. " \"subnet6\": [ {"
  60. " \"pools\": [ "
  61. " { \"pool\": \"2001:db8::/64\" }"
  62. " ],"
  63. " \"subnet\": \"2001:db8::/32\""
  64. " } ]"
  65. "}",
  66. // Configuration 1: one pool with empty user context
  67. "{"
  68. " \"interfaces-config\": {"
  69. " \"interfaces\": [\"*\" ]"
  70. " },"
  71. " \"valid-lifetime\": 4000,"
  72. " \"preferred-lifetime\": 3000,"
  73. " \"rebind-timer\": 2000,"
  74. " \"renew-timer\": 1000,"
  75. " \"subnet6\": [ {"
  76. " \"pools\": [ "
  77. " { \"pool\": \"2001:db8::/64\","
  78. " \"user-context\": {"
  79. " }"
  80. " }"
  81. " ],"
  82. " \"subnet\": \"2001:db8::/32\""
  83. " } ]"
  84. "}",
  85. // Configuration 2: one pool with user context containing lw4over6 parameters
  86. "{"
  87. " \"interfaces-config\": {"
  88. " \"interfaces\": [\"*\" ]"
  89. " },"
  90. " \"valid-lifetime\": 4000,"
  91. " \"preferred-lifetime\": 3000,"
  92. " \"rebind-timer\": 2000,"
  93. " \"renew-timer\": 1000,"
  94. " \"subnet6\": [ {"
  95. " \"pools\": [ "
  96. " { \"pool\": \"2001:db8::/64\","
  97. " \"user-context\": {"
  98. " \"lw4over6-sharing-ratio\": 64,"
  99. " \"lw4over6-v4-pool\": \"192.0.2.0/24\","
  100. " \"lw4over6-sysports-exclude\": true,"
  101. " \"lw4over6-bind-prefix-len\": 56"
  102. " }"
  103. " }"
  104. " ],"
  105. " \"subnet\": \"2001:db8::/32\""
  106. " } ]"
  107. "}",
  108. // Configuration 3: pd-pool without any user-context
  109. "{"
  110. " \"interfaces-config\": {"
  111. " \"interfaces\": [\"*\" ]"
  112. " },"
  113. " \"valid-lifetime\": 4000,"
  114. " \"preferred-lifetime\": 3000,"
  115. " \"rebind-timer\": 2000,"
  116. " \"renew-timer\": 1000,"
  117. " \"subnet6\": [ {"
  118. " \"pd-pools\": [ "
  119. " { \"prefix\": \"2001:db8::\","
  120. " \"prefix-len\": 56,"
  121. " \"delegated-len\": 64 }"
  122. " ],"
  123. " \"subnet\": \"2001:db8::/32\""
  124. " } ]"
  125. "}",
  126. // Configuration 4: pd-pool with empty user-context
  127. "{"
  128. " \"interfaces-config\": {"
  129. " \"interfaces\": [\"*\" ]"
  130. " },"
  131. " \"valid-lifetime\": 4000,"
  132. " \"preferred-lifetime\": 3000,"
  133. " \"rebind-timer\": 2000,"
  134. " \"renew-timer\": 1000,"
  135. " \"subnet6\": [ {"
  136. " \"pd-pools\": [ "
  137. " { \"prefix\": \"2001:db8::\","
  138. " \"prefix-len\": 56,"
  139. " \"delegated-len\": 64,"
  140. " \"user-context\": { }"
  141. " }"
  142. " ],"
  143. " \"subnet\": \"2001:db8::/32\""
  144. " } ]"
  145. "}",
  146. // Configuration 5: pd-pool with user-context with lw4over6 parameters
  147. "{"
  148. " \"interfaces-config\": {"
  149. " \"interfaces\": [\"*\" ]"
  150. " },"
  151. " \"valid-lifetime\": 4000,"
  152. " \"preferred-lifetime\": 3000,"
  153. " \"rebind-timer\": 2000,"
  154. " \"renew-timer\": 1000,"
  155. " \"subnet6\": [ {"
  156. " \"pd-pools\": [ "
  157. " { \"prefix\": \"2001:db8::\","
  158. " \"prefix-len\": 56,"
  159. " \"delegated-len\": 64,"
  160. " \"user-context\": {"
  161. " \"lw4over6-sharing-ratio\": 64,"
  162. " \"lw4over6-v4-pool\": \"192.0.2.0/24\","
  163. " \"lw4over6-sysports-exclude\": true,"
  164. " \"lw4over6-bind-prefix-len\": 56"
  165. " }"
  166. " }"
  167. " ],"
  168. " \"subnet\": \"2001:db8::/32\""
  169. " } ]"
  170. "}"
  171. };
  172. std::string specfile(const std::string& name) {
  173. return (std::string(DHCP6_SRC_DIR) + "/" + name);
  174. }
  175. /// @brief Tests that the spec file is valid.
  176. /// Verifies that the DHCP6 configuration specification file is valid.
  177. TEST(Dhcp6SpecTest, basicSpec) {
  178. ASSERT_NO_THROW(isc::config::
  179. moduleSpecFromFile(specfile("dhcp6.spec")));
  180. }
  181. class Dhcp6ParserTest : public ::testing::Test {
  182. protected:
  183. // Check that no hooks libraries are loaded. This is a pre-condition for
  184. // a number of tests, so is checked in one place. As this uses an
  185. // ASSERT call - and it is not clear from the documentation that Gtest
  186. // predicates can be used in a constructor - the check is placed in SetUp.
  187. virtual void SetUp() {
  188. std::vector<std::string> libraries = HooksManager::getLibraryNames();
  189. ASSERT_TRUE(libraries.empty());
  190. }
  191. public:
  192. Dhcp6ParserTest() :rcode_(-1), srv_(0) {
  193. // srv_(0) means to not open any sockets. We don't want to
  194. // deal with sockets here, just check if configuration handling
  195. // is sane.
  196. const IfaceMgr::IfaceCollection& ifaces =
  197. IfaceMgr::instance().getIfaces();
  198. // There must be some interface detected
  199. if (ifaces.empty()) {
  200. // We can't use ASSERT in constructor
  201. ADD_FAILURE() << "No interfaces detected.";
  202. }
  203. valid_iface_ = (*ifaces.begin())->getName();
  204. bogus_iface_ = "nonexisting0";
  205. if (IfaceMgr::instance().getIface(bogus_iface_)) {
  206. ADD_FAILURE() << "The '" << bogus_iface_ << "' exists on this system"
  207. << " while the test assumes that it doesn't, to execute"
  208. << " some negative scenarios. Can't continue this test.";
  209. }
  210. // Reset configuration for each test.
  211. resetConfiguration();
  212. }
  213. ~Dhcp6ParserTest() {
  214. // Reset configuration database after each test.
  215. resetConfiguration();
  216. // ... and delete the hooks library marker files if present
  217. static_cast<void>(remove(LOAD_MARKER_FILE));
  218. static_cast<void>(remove(UNLOAD_MARKER_FILE));
  219. };
  220. // Checks if config_result (result of DHCP server configuration) has
  221. // expected code (0 for success, other for failures).
  222. // Also stores result in rcode_ and comment_.
  223. void checkResult(ConstElementPtr status, int expected_code) {
  224. ASSERT_TRUE(status);
  225. comment_ = parseAnswer(rcode_, status);
  226. EXPECT_EQ(expected_code, rcode_);
  227. }
  228. /// @brief Returns an interface configuration used by the most of the
  229. /// unit tests.
  230. std::string genIfaceConfig() const {
  231. return ("\"interfaces-config\": {"
  232. " \"interfaces\": [ \"*\" ]"
  233. "}");
  234. }
  235. /// @brief Create the simple configuration with single option.
  236. ///
  237. /// This function allows to set one of the parameters that configure
  238. /// option value. These parameters are: "name", "code", "data" and
  239. /// "csv-format".
  240. ///
  241. /// @param param_value string holding option parameter value to be
  242. /// injected into the configuration string.
  243. /// @param parameter name of the parameter to be configured with
  244. /// param value.
  245. std::string createConfigWithOption(const std::string& param_value,
  246. const std::string& parameter) {
  247. std::map<std::string, std::string> params;
  248. if (parameter == "name") {
  249. params["name"] = param_value;
  250. params["space"] = DHCP6_OPTION_SPACE;
  251. params["code"] = "38";
  252. params["data"] = "ABCDEF0105";
  253. params["csv-format"] = "False";
  254. } else if (parameter == "space") {
  255. params["name"] = "subscriber-id";
  256. params["space"] = param_value;
  257. params["code"] = "38";
  258. params["data"] = "ABCDEF0105";
  259. params["csv-format"] = "False";
  260. } else if (parameter == "code") {
  261. params["name"] = "subscriber-id";
  262. params["space"] = DHCP6_OPTION_SPACE;
  263. params["code"] = param_value;
  264. params["data"] = "ABCDEF0105";
  265. params["csv-format"] = "False";
  266. } else if (parameter == "data") {
  267. params["name"] = "subscriber-id";
  268. params["space"] = DHCP6_OPTION_SPACE;
  269. params["code"] = "38";
  270. params["data"] = param_value;
  271. params["csv-format"] = "False";
  272. } else if (parameter == "csv-format") {
  273. params["name"] = "subscriber-id";
  274. params["space"] = DHCP6_OPTION_SPACE;
  275. params["code"] = "38";
  276. params["data"] = "ABCDEF0105";
  277. params["csv-format"] = param_value;
  278. }
  279. return (createConfigWithOption(params));
  280. }
  281. /// @brief Create simple configuration with single option.
  282. ///
  283. /// This function creates a configuration for a single option with
  284. /// custom values for all parameters that describe the option.
  285. ///
  286. /// @params params map holding parameters and their values.
  287. /// @return configuration string containing custom values of parameters
  288. /// describing an option.
  289. std::string createConfigWithOption(const std::map<std::string,
  290. std::string>& params)
  291. {
  292. std::ostringstream stream;
  293. stream << "{ " << genIfaceConfig() << ","
  294. "\"preferred-lifetime\": 3000,"
  295. "\"rebind-timer\": 2000, "
  296. "\"renew-timer\": 1000, "
  297. "\"option-def\": [ {"
  298. " \"name\": \"bool-option\","
  299. " \"code\": 1000,"
  300. " \"type\": \"boolean\","
  301. " \"space\": \"dhcp6\""
  302. "} ],"
  303. "\"subnet6\": [ { "
  304. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  305. " \"subnet\": \"2001:db8:1::/64\", "
  306. " \"option-data\": [ {";
  307. bool first = true;
  308. typedef std::pair<std::string, std::string> ParamPair;
  309. BOOST_FOREACH(ParamPair param, params) {
  310. if (!first) {
  311. stream << ", ";
  312. } else {
  313. // cppcheck-suppress unreadVariable
  314. first = false;
  315. }
  316. if (param.first == "name") {
  317. stream << "\"name\": \"" << param.second << "\"";
  318. } else if (param.first == "space") {
  319. stream << "\"space\": \"" << param.second << "\"";
  320. } else if (param.first == "code") {
  321. stream << "\"code\": " << param.second;;
  322. } else if (param.first == "data") {
  323. stream << "\"data\": \"" << param.second << "\"";
  324. } else if (param.first == "csv-format") {
  325. stream << "\"csv-format\": " << param.second;
  326. }
  327. }
  328. stream <<
  329. " } ]"
  330. " } ],"
  331. "\"valid-lifetime\": 4000 }";
  332. return (stream.str());
  333. }
  334. /// @brief Returns an option from the subnet.
  335. ///
  336. /// This function returns an option from a subnet to which the
  337. /// specified subnet address belongs. The option is identified
  338. /// by its code.
  339. ///
  340. /// @param subnet_address Address which belongs to the subnet from
  341. /// which the option is to be returned.
  342. /// @param option_code Code of the option to be returned.
  343. /// @param expected_options_count Expected number of options in
  344. /// the particular subnet.
  345. ///
  346. /// @return Descriptor of the option. If the descriptor holds a
  347. /// NULL option pointer, it means that there was no such option
  348. /// in the subnet.
  349. OptionDescriptor
  350. getOptionFromSubnet(const IOAddress& subnet_address,
  351. const uint16_t option_code,
  352. const uint16_t expected_options_count = 1) {
  353. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  354. selectSubnet(subnet_address, classify_);
  355. if (!subnet) {
  356. /// @todo replace toText() with the use of operator <<.
  357. ADD_FAILURE() << "A subnet for the specified address "
  358. << subnet_address.toText()
  359. << " does not exist in Config Manager";
  360. }
  361. OptionContainerPtr options =
  362. subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
  363. if (expected_options_count != options->size()) {
  364. ADD_FAILURE() << "The number of options in the subnet '"
  365. << subnet_address.toText() << "' is different "
  366. " than expected number of options '"
  367. << expected_options_count << "'";
  368. }
  369. // Get the search index. Index #1 is to search using option code.
  370. const OptionContainerTypeIndex& idx = options->get<1>();
  371. // Get the options for specified index. Expecting one option to be
  372. // returned but in theory we may have multiple options with the same
  373. // code so we get the range.
  374. std::pair<OptionContainerTypeIndex::const_iterator,
  375. OptionContainerTypeIndex::const_iterator> range =
  376. idx.equal_range(option_code);
  377. if (std::distance(range.first, range.second) > 1) {
  378. ADD_FAILURE() << "There is more than one option having the"
  379. " option code '" << option_code << "' in a subnet '"
  380. << subnet_address.toText() << "'. Expected "
  381. " at most one option";
  382. } else if (std::distance(range.first, range.second) == 0) {
  383. return (OptionDescriptor(OptionPtr(), false));
  384. }
  385. return (*range.first);
  386. }
  387. /// @brief Parse and Execute configuration
  388. ///
  389. /// Parses a configuration and executes a configuration of the server.
  390. /// If the operation fails, the current test will register a failure.
  391. ///
  392. /// @param config Configuration to parse
  393. /// @param operation Operation being performed. In the case of an error,
  394. /// the error text will include the string "unable to <operation>.".
  395. ///
  396. /// @return true if the configuration succeeded, false if not. In the
  397. /// latter case, a failure will have been added to the current test.
  398. bool
  399. executeConfiguration(const std::string& config, const char* operation) {
  400. ConstElementPtr status;
  401. try {
  402. ElementPtr json = Element::fromJSON(config);
  403. status = configureDhcp6Server(srv_, json);
  404. } catch (const std::exception& ex) {
  405. ADD_FAILURE() << "Unable to " << operation << ". "
  406. << "The following configuration was used: " << std::endl
  407. << config << std::endl
  408. << " and the following error message was returned:"
  409. << ex.what() << std::endl;
  410. return (false);
  411. }
  412. // The status object must not be NULL
  413. if (!status) {
  414. ADD_FAILURE() << "Unable to " << operation << ". "
  415. << "The configuration function returned a null pointer.";
  416. return (false);
  417. }
  418. // Store the answer if we need it.
  419. // Returned value should be 0 (configuration success)
  420. comment_ = parseAnswer(rcode_, status);
  421. if (rcode_ != 0) {
  422. string reason = "";
  423. if (comment_) {
  424. reason = string(" (") + comment_->stringValue() + string(")");
  425. }
  426. ADD_FAILURE() << "Unable to " << operation << ". "
  427. << "The configuration function returned error code "
  428. << rcode_ << reason;
  429. return (false);
  430. }
  431. return (true);
  432. }
  433. /// @brief Reset configuration database.
  434. ///
  435. /// This function resets configuration data base by removing all subnets
  436. /// option-data, and hooks libraries. The reset must be performed after each
  437. /// test to make sure that contents of the database do not affect the
  438. /// results of subsequent tests.
  439. void resetConfiguration() {
  440. string config = "{ \"interfaces-config\": {"
  441. " \"interfaces\": [ ]"
  442. "},"
  443. "\"hooks-libraries\": [ ],"
  444. "\"preferred-lifetime\": 3000,"
  445. "\"rebind-timer\": 2000, "
  446. "\"renew-timer\": 1000, "
  447. "\"valid-lifetime\": 4000, "
  448. "\"subnet6\": [ ], "
  449. "\"dhcp-ddns\": { \"enable-updates\" : false }, "
  450. "\"option-def\": [ ], "
  451. "\"option-data\": [ ] }";
  452. static_cast<void>(executeConfiguration(config,
  453. "reset configuration database"));
  454. // The default setting is to listen on all interfaces. In order to
  455. // properly test interface configuration we disable listening on
  456. // all interfaces before each test and later check that this setting
  457. // has been overridden by the configuration used in the test.
  458. CfgMgr::instance().clear();
  459. // Create fresh context.
  460. globalContext()->copyContext(ParserContext(Option::V6));
  461. }
  462. /// @brief Retrieve an option associated with a host.
  463. ///
  464. /// The option is retrieved from the "dhcp6" option space.
  465. ///
  466. /// @param host Reference to a host for which an option should be retrieved.
  467. /// @param option_code Option code.
  468. /// @tparam ReturnType Type of the pointer object returned.
  469. ///
  470. /// @return Pointer to an option or NULL pointer if not found.
  471. template<typename ReturnType>
  472. ReturnType
  473. retrieveOption(const Host& host, const uint16_t option_code) const {
  474. return (retrieveOption<ReturnType>(host, DHCP6_OPTION_SPACE, option_code));
  475. }
  476. /// @brief Retrieve an option associated with a host.
  477. ///
  478. /// @param host Reference to a host for which an option should be retrieved.
  479. /// @param space Option space from which option should be retrieved.
  480. /// @param option_code Option code.
  481. /// @tparam ReturnType Type of the pointer object returned.
  482. ///
  483. /// @return Pointer to an option or NULL pointer if not found.
  484. template<typename ReturnType>
  485. ReturnType
  486. retrieveOption(const Host& host, const std::string& space,
  487. const uint16_t option_code) const {
  488. ConstCfgOptionPtr cfg_option = host.getCfgOption6();
  489. if (cfg_option) {
  490. OptionDescriptor opt_desc = cfg_option->get(space, option_code);
  491. if (opt_desc.option_) {
  492. return (boost::dynamic_pointer_cast<
  493. typename ReturnType::element_type>(opt_desc.option_));
  494. }
  495. }
  496. return (ReturnType());
  497. }
  498. /// @brief Test invalid option parameter value.
  499. ///
  500. /// This test function constructs the simple configuration
  501. /// string and injects invalid option configuration into it.
  502. /// It expects that parser will fail with provided option code.
  503. ///
  504. /// @param param_value string holding invalid option parameter value
  505. /// to be injected into configuration string.
  506. /// @param parameter name of the parameter to be configured with
  507. /// param_value (can be any of "name", "code", "data")
  508. void testInvalidOptionParam(const std::string& param_value,
  509. const std::string& parameter) {
  510. ConstElementPtr x;
  511. std::string config = createConfigWithOption(param_value, parameter);
  512. ElementPtr json = Element::fromJSON(config);
  513. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  514. checkResult(x, 1);
  515. EXPECT_TRUE(errorContainsPosition(x, "<string>"));
  516. CfgMgr::instance().clear();
  517. }
  518. /// @brief Test invalid option paramater value.
  519. ///
  520. /// This test function constructs the simple configuration
  521. /// string and injects invalid option configuration into it.
  522. /// It expects that parser will fail with provided option code.
  523. ///
  524. /// @param params Map of parameters defining an option.
  525. void
  526. testInvalidOptionParam(const std::map<std::string, std::string>& params) {
  527. ConstElementPtr x;
  528. std::string config = createConfigWithOption(params);
  529. ElementPtr json = Element::fromJSON(config);
  530. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  531. checkResult(x, 1);
  532. EXPECT_TRUE(errorContainsPosition(x, "<string>"));
  533. CfgMgr::instance().clear();
  534. }
  535. /// @brief Test option against given code and data.
  536. ///
  537. /// @param option_desc option descriptor that carries the option to
  538. /// be tested.
  539. /// @param expected_code expected code of the option.
  540. /// @param expected_data expected data in the option.
  541. /// @param expected_data_len length of the reference data.
  542. /// @param extra_data if true extra data is allowed in an option
  543. /// after tested data.
  544. void testOption(const OptionDescriptor& option_desc,
  545. uint16_t expected_code, const uint8_t* expected_data,
  546. size_t expected_data_len,
  547. bool extra_data = false) {
  548. // Check if option descriptor contains valid option pointer.
  549. ASSERT_TRUE(option_desc.option_);
  550. // Verify option type.
  551. EXPECT_EQ(expected_code, option_desc.option_->getType());
  552. // We may have many different option types being created. Some of them
  553. // have dedicated classes derived from Option class. In such case if
  554. // we want to verify the option contents against expected_data we have
  555. // to prepare raw buffer with the contents of the option. The easiest
  556. // way is to call pack() which will prepare on-wire data.
  557. util::OutputBuffer buf(option_desc.option_->getData().size());
  558. option_desc.option_->pack(buf);
  559. if (extra_data) {
  560. // The length of the buffer must be at least equal to size of the
  561. // reference data but it can sometimes be greater than that. This is
  562. // because some options carry suboptions that increase the overall
  563. // length.
  564. ASSERT_GE(buf.getLength() - option_desc.option_->getHeaderLen(),
  565. expected_data_len);
  566. } else {
  567. ASSERT_EQ(buf.getLength() - option_desc.option_->getHeaderLen(),
  568. expected_data_len);
  569. }
  570. // Verify that the data is correct. Do not verify suboptions and a header.
  571. const uint8_t* data = static_cast<const uint8_t*>(buf.getData());
  572. EXPECT_EQ(0, memcmp(expected_data, data + option_desc.option_->getHeaderLen(),
  573. expected_data_len));
  574. }
  575. /// @brief Test option configuration.
  576. ///
  577. /// This function creates a configuration for a specified option using
  578. /// a map of parameters specified as the argument. The map holds
  579. /// name/value pairs which identifies option's configuration parameters:
  580. /// - name
  581. /// - space
  582. /// - code
  583. /// - data
  584. /// - csv-format.
  585. /// This function applies a new server configuration and checks that the
  586. /// option being configured is inserted into CfgMgr. The raw contents of
  587. /// this option are compared with the binary data specified as expected
  588. /// data passed to this function.
  589. ///
  590. /// @param params Map of parameters defining an option.
  591. /// @param option_code Option code.
  592. /// @param expected_data Array containing binary data expected to be stored
  593. /// in the configured option.
  594. /// @param expected_data_len Length of the array holding reference data.
  595. void testConfiguration(const std::map<std::string, std::string>& params,
  596. const uint16_t option_code,
  597. const uint8_t* expected_data,
  598. const size_t expected_data_len) {
  599. CfgMgr::instance().clear();
  600. std::string config = createConfigWithOption(params);
  601. ASSERT_TRUE(executeConfiguration(config, "parse option configuration"));
  602. // The subnet should now hold one option with the specified code.
  603. OptionDescriptor desc =
  604. getOptionFromSubnet(IOAddress("2001:db8:1::5"), option_code);
  605. ASSERT_TRUE(desc.option_);
  606. testOption(desc, option_code, expected_data, expected_data_len);
  607. CfgMgr::instance().clear();
  608. }
  609. /// @brief Tests the Rapid Commit configuration for a subnet.
  610. ///
  611. /// This test configures the server with a given configuration and
  612. /// verifies if the Rapid Commit has been configured successfully
  613. /// for a subnet.
  614. ///
  615. /// @param config Server configuration, possibly including the
  616. /// 'rapid-commit' parameter.
  617. /// @param exp_rapid_commit Expected value of the Rapid Commit flag
  618. /// within a subnet.
  619. void testRapidCommit(const std::string& config,
  620. const bool exp_rapid_commit) {
  621. // Clear any existing configuration.
  622. CfgMgr::instance().clear();
  623. // Configure the server.
  624. ElementPtr json = Element::fromJSON(config);
  625. // Make sure that the configuration was successful.
  626. ConstElementPtr status;
  627. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  628. checkResult(status, 0);
  629. // Get the subnet.
  630. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  631. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  632. ASSERT_TRUE(subnet);
  633. // Check the Rapid Commit flag for the subnet.
  634. EXPECT_EQ(exp_rapid_commit, subnet->getRapidCommit());
  635. // Clear any existing configuration.
  636. CfgMgr::instance().clear();
  637. }
  638. /// @brief This utility method attempts to configure using specified
  639. /// config and then returns requested pool from requested subnet
  640. ///
  641. /// @param config configuration to be applied
  642. /// @param subnet_index index of the subnet to be returned (0 - the first subnet)
  643. /// @param pool_index index of the pool within a subnet (0 - the first pool)
  644. /// @param type Pool type (TYPE_NA or TYPE_PD)
  645. /// @param pool [out] Pool pointer will be stored here (if found)
  646. void getPool(const std::string& config, size_t subnet_index,
  647. size_t pool_index, Lease::Type type, PoolPtr& pool) {
  648. ConstElementPtr status;
  649. ElementPtr json = Element::fromJSON(config);
  650. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  651. ASSERT_TRUE(status);
  652. checkResult(status, 0);
  653. ConstCfgSubnets6Ptr subnets6 = CfgMgr::instance().getStagingCfg()->getCfgSubnets6();
  654. ASSERT_TRUE(subnets6);
  655. const Subnet6Collection* subnets = subnets6->getAll();
  656. ASSERT_TRUE(subnets);
  657. ASSERT_GE(subnets->size(), subnet_index + 1);
  658. const PoolCollection pools = subnets->at(subnet_index)->getPools(type);
  659. ASSERT_GE(pools.size(), pool_index + 1);
  660. pool = pools.at(pool_index);
  661. EXPECT_TRUE(pool);
  662. }
  663. int rcode_; ///< Return code (see @ref isc::config::parseAnswer)
  664. Dhcpv6Srv srv_; ///< Instance of the Dhcp6Srv used during tests
  665. ConstElementPtr comment_; ///< Comment (see @ref isc::config::parseAnswer)
  666. string valid_iface_; ///< Valid network interface name (present in system)
  667. string bogus_iface_; ///< invalid network interface name (not in system)
  668. isc::dhcp::ClientClasses classify_; ///< used in client classification
  669. };
  670. // Goal of this test is a verification if a very simple config update
  671. // with just a bumped version number. That's the simplest possible
  672. // config update.
  673. TEST_F(Dhcp6ParserTest, version) {
  674. ConstElementPtr x;
  675. EXPECT_NO_THROW(x = configureDhcp6Server(srv_,
  676. Element::fromJSON("{\"version\": 0}")));
  677. // returned value must be 0 (configuration accepted)
  678. checkResult(x, 0);
  679. }
  680. /// The goal of this test is to verify that the code accepts only
  681. /// valid commands and malformed or unsupported parameters are rejected.
  682. TEST_F(Dhcp6ParserTest, bogusCommand) {
  683. ConstElementPtr x;
  684. EXPECT_NO_THROW(x = configureDhcp6Server(srv_,
  685. Element::fromJSON("{\"bogus\": 5}")));
  686. // returned value must be 1 (configuration parse error)
  687. checkResult(x, 1);
  688. }
  689. /// The goal of this test is to verify if configuration without any
  690. /// subnets defined can be accepted.
  691. TEST_F(Dhcp6ParserTest, emptySubnet) {
  692. ConstElementPtr status;
  693. EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
  694. Element::fromJSON("{ " + genIfaceConfig() + ","
  695. "\"preferred-lifetime\": 3000,"
  696. "\"rebind-timer\": 2000, "
  697. "\"renew-timer\": 1000, "
  698. "\"subnet6\": [ ], "
  699. "\"valid-lifetime\": 4000 }")));
  700. // returned value should be 0 (success)
  701. checkResult(status, 0);
  702. }
  703. /// The goal of this test is to verify if defined subnet uses global
  704. /// parameter timer definitions.
  705. TEST_F(Dhcp6ParserTest, subnetGlobalDefaults) {
  706. ConstElementPtr status;
  707. string config = "{ " + genIfaceConfig() + ","
  708. "\"preferred-lifetime\": 3000,"
  709. "\"rebind-timer\": 2000, "
  710. "\"renew-timer\": 1000, "
  711. "\"subnet6\": [ { "
  712. " \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
  713. " \"subnet\": \"2001:db8:1::/64\" } ],"
  714. "\"valid-lifetime\": 4000 }";
  715. ElementPtr json = Element::fromJSON(config);
  716. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  717. // check if returned status is OK
  718. checkResult(status, 0);
  719. // Now check if the configuration was indeed handled and we have
  720. // expected pool configured.
  721. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  722. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  723. ASSERT_TRUE(subnet);
  724. EXPECT_EQ(1000, subnet->getT1());
  725. EXPECT_EQ(2000, subnet->getT2());
  726. EXPECT_EQ(3000, subnet->getPreferred());
  727. EXPECT_EQ(4000, subnet->getValid());
  728. // Check that subnet-id is 1
  729. EXPECT_EQ(1, subnet->getID());
  730. }
  731. // This test checks that multiple subnets can be defined and handled properly.
  732. TEST_F(Dhcp6ParserTest, multipleSubnets) {
  733. ConstElementPtr x;
  734. // Collection of four subnets for which ids should be autogenerated
  735. // - ids are unspecified or set to 0.
  736. string config = "{ " + genIfaceConfig() + ","
  737. "\"preferred-lifetime\": 3000,"
  738. "\"rebind-timer\": 2000, "
  739. "\"renew-timer\": 1000, "
  740. "\"subnet6\": [ { "
  741. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  742. " \"subnet\": \"2001:db8:1::/64\" "
  743. " },"
  744. " {"
  745. " \"pools\": [ { \"pool\": \"2001:db8:2::/80\" } ],"
  746. " \"subnet\": \"2001:db8:2::/64\", "
  747. " \"id\": 0"
  748. " },"
  749. " {"
  750. " \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
  751. " \"subnet\": \"2001:db8:3::/64\" "
  752. " },"
  753. " {"
  754. " \"pools\": [ { \"pool\": \"2001:db8:4::/80\" } ],"
  755. " \"subnet\": \"2001:db8:4::/64\" "
  756. " } ],"
  757. "\"valid-lifetime\": 4000 }";
  758. int cnt = 0; // Number of reconfigurations
  759. ElementPtr json = Element::fromJSON(config);
  760. do {
  761. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  762. checkResult(x, 0);
  763. CfgMgr::instance().commit();
  764. const Subnet6Collection* subnets =
  765. CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
  766. ASSERT_TRUE(subnets);
  767. ASSERT_EQ(4, subnets->size()); // We expect 4 subnets
  768. // Check subnet-ids of each subnet (it should be monotonously increasing)
  769. EXPECT_EQ(1, subnets->at(0)->getID());
  770. EXPECT_EQ(2, subnets->at(1)->getID());
  771. EXPECT_EQ(3, subnets->at(2)->getID());
  772. EXPECT_EQ(4, subnets->at(3)->getID());
  773. // Repeat reconfiguration process 10 times and check that the subnet-id
  774. // is set to the same value. Technically, just two iterations would be
  775. // sufficient, but it's nice to have a test that exercises reconfiguration
  776. // a bit.
  777. } while (++cnt < 10);
  778. }
  779. // This checks that it is possible to assign arbitrary ids for subnets.
  780. TEST_F(Dhcp6ParserTest, multipleSubnetsExplicitIDs) {
  781. ConstElementPtr x;
  782. // Four subnets with arbitrary subnet ids.
  783. string config = "{ " + genIfaceConfig() + ","
  784. "\"preferred-lifetime\": 3000,"
  785. "\"rebind-timer\": 2000, "
  786. "\"renew-timer\": 1000, "
  787. "\"subnet6\": [ { "
  788. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  789. " \"subnet\": \"2001:db8:1::/64\", "
  790. " \"id\": 1024"
  791. " },"
  792. " {"
  793. " \"pools\": [ { \"pool\": \"2001:db8:2::/80\" } ],"
  794. " \"subnet\": \"2001:db8:2::/64\", "
  795. " \"id\": 100"
  796. " },"
  797. " {"
  798. " \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
  799. " \"subnet\": \"2001:db8:3::/64\", "
  800. " \"id\": 1"
  801. " },"
  802. " {"
  803. " \"pools\": [ { \"pool\": \"2001:db8:4::/80\" } ],"
  804. " \"subnet\": \"2001:db8:4::/64\", "
  805. " \"id\": 34"
  806. " } ],"
  807. "\"valid-lifetime\": 4000 }";
  808. int cnt = 0; // Number of reconfigurations
  809. ElementPtr json = Element::fromJSON(config);
  810. do {
  811. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  812. checkResult(x, 0);
  813. CfgMgr::instance().commit();
  814. const Subnet6Collection* subnets =
  815. CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
  816. ASSERT_TRUE(subnets);
  817. ASSERT_EQ(4, subnets->size()); // We expect 4 subnets
  818. // Check that subnet ids are as expected.
  819. EXPECT_EQ(1024, subnets->at(0)->getID());
  820. EXPECT_EQ(100, subnets->at(1)->getID());
  821. EXPECT_EQ(1, subnets->at(2)->getID());
  822. EXPECT_EQ(34, subnets->at(3)->getID());
  823. // Repeat reconfiguration process 10 times and check that the subnet-id
  824. // is set to the same value.
  825. } while (++cnt < 3);
  826. }
  827. // CHeck that the configuration with two subnets having the same id is rejected.
  828. TEST_F(Dhcp6ParserTest, multipleSubnetsOverlapingIDs) {
  829. ConstElementPtr x;
  830. // Four subnets, two of them have the same id.
  831. string config = "{ " + genIfaceConfig() + ","
  832. "\"preferred-lifetime\": 3000,"
  833. "\"rebind-timer\": 2000, "
  834. "\"renew-timer\": 1000, "
  835. "\"subnet6\": [ { "
  836. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  837. " \"subnet\": \"2001:db8:1::/64\", "
  838. " \"id\": 1024"
  839. " },"
  840. " {"
  841. " \"pools\": [ { \"pool\": \"2001:db8:2::/80\" } ],"
  842. " \"subnet\": \"2001:db8:2::/64\", "
  843. " \"id\": 100"
  844. " },"
  845. " {"
  846. " \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
  847. " \"subnet\": \"2001:db8:3::/64\", "
  848. " \"id\": 1024"
  849. " },"
  850. " {"
  851. " \"pools\": [ { \"pool\": \"2001:db8:4::/80\" } ],"
  852. " \"subnet\": \"2001:db8:4::/64\", "
  853. " \"id\": 34"
  854. " } ],"
  855. "\"valid-lifetime\": 4000 }";
  856. ElementPtr json = Element::fromJSON(config);
  857. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  858. checkResult(x, 1);
  859. EXPECT_TRUE(errorContainsPosition(x, "<string>"));
  860. }
  861. // Goal of this test is to verify that a previously configured subnet can be
  862. // deleted in subsequent reconfiguration.
  863. TEST_F(Dhcp6ParserTest, reconfigureRemoveSubnet) {
  864. ConstElementPtr x;
  865. // All four subnets
  866. string config4 = "{ " + genIfaceConfig() + ","
  867. "\"preferred-lifetime\": 3000,"
  868. "\"rebind-timer\": 2000, "
  869. "\"renew-timer\": 1000, "
  870. "\"subnet6\": [ { "
  871. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  872. " \"subnet\": \"2001:db8:1::/64\", "
  873. " \"id\": 1"
  874. " },"
  875. " {"
  876. " \"pools\": [ { \"pool\": \"2001:db8:2::/80\" } ],"
  877. " \"subnet\": \"2001:db8:2::/64\", "
  878. " \"id\": 2"
  879. " },"
  880. " {"
  881. " \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
  882. " \"subnet\": \"2001:db8:3::/64\", "
  883. " \"id\": 3"
  884. " },"
  885. " {"
  886. " \"pools\": [ { \"pool\": \"2001:db8:4::/80\" } ],"
  887. " \"subnet\": \"2001:db8:4::/64\", "
  888. " \"id\": 4"
  889. " } ],"
  890. "\"valid-lifetime\": 4000 }";
  891. // Three subnets (the last one removed)
  892. string config_first3 = "{ " + genIfaceConfig() + ","
  893. "\"preferred-lifetime\": 3000,"
  894. "\"rebind-timer\": 2000, "
  895. "\"renew-timer\": 1000, "
  896. "\"subnet6\": [ { "
  897. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  898. " \"subnet\": \"2001:db8:1::/64\", "
  899. " \"id\": 1"
  900. " },"
  901. " {"
  902. " \"pools\": [ { \"pool\": \"2001:db8:2::/80\" } ],"
  903. " \"subnet\": \"2001:db8:2::/64\", "
  904. " \"id\": 2"
  905. " },"
  906. " {"
  907. " \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
  908. " \"subnet\": \"2001:db8:3::/64\", "
  909. " \"id\": 3"
  910. " } ],"
  911. "\"valid-lifetime\": 4000 }";
  912. // Second subnet removed
  913. string config_second_removed = "{ " + genIfaceConfig() + ","
  914. "\"preferred-lifetime\": 3000,"
  915. "\"rebind-timer\": 2000, "
  916. "\"renew-timer\": 1000, "
  917. "\"subnet6\": [ { "
  918. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  919. " \"subnet\": \"2001:db8:1::/64\", "
  920. " \"id\": 1"
  921. " },"
  922. " {"
  923. " \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
  924. " \"subnet\": \"2001:db8:3::/64\", "
  925. " \"id\": 3"
  926. " },"
  927. " {"
  928. " \"pools\": [ { \"pool\": \"2001:db8:4::/80\" } ],"
  929. " \"subnet\": \"2001:db8:4::/64\", "
  930. " \"id\": 4"
  931. " } ],"
  932. "\"valid-lifetime\": 4000 }";
  933. // CASE 1: Configure 4 subnets, then reconfigure and remove the
  934. // last one.
  935. ElementPtr json = Element::fromJSON(config4);
  936. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  937. checkResult(x, 0);
  938. CfgMgr::instance().commit();
  939. const Subnet6Collection* subnets =
  940. CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
  941. ASSERT_TRUE(subnets);
  942. ASSERT_EQ(4, subnets->size()); // We expect 4 subnets
  943. // Do the reconfiguration (the last subnet is removed)
  944. json = Element::fromJSON(config_first3);
  945. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  946. checkResult(x, 0);
  947. CfgMgr::instance().commit();
  948. subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
  949. ASSERT_TRUE(subnets);
  950. ASSERT_EQ(3, subnets->size()); // We expect 3 subnets now (4th is removed)
  951. EXPECT_EQ(1, subnets->at(0)->getID());
  952. EXPECT_EQ(2, subnets->at(1)->getID());
  953. EXPECT_EQ(3, subnets->at(2)->getID());
  954. /// CASE 2: Configure 4 subnets, then reconfigure and remove one
  955. /// from in between (not first, not last)
  956. json = Element::fromJSON(config4);
  957. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  958. checkResult(x, 0);
  959. CfgMgr::instance().commit();
  960. // Do reconfiguration
  961. json = Element::fromJSON(config_second_removed);
  962. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  963. checkResult(x, 0);
  964. CfgMgr::instance().commit();
  965. subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
  966. ASSERT_TRUE(subnets);
  967. ASSERT_EQ(3, subnets->size()); // We expect 4 subnets
  968. EXPECT_EQ(1, subnets->at(0)->getID());
  969. // The second subnet (with subnet-id = 2) is no longer there
  970. EXPECT_EQ(3, subnets->at(1)->getID());
  971. EXPECT_EQ(4, subnets->at(2)->getID());
  972. }
  973. // This test checks if it is possible to override global values
  974. // on a per subnet basis.
  975. TEST_F(Dhcp6ParserTest, subnetLocal) {
  976. ConstElementPtr status;
  977. string config = "{ " + genIfaceConfig() + ","
  978. "\"preferred-lifetime\": 3000,"
  979. "\"rebind-timer\": 2000, "
  980. "\"renew-timer\": 1000, "
  981. "\"subnet6\": [ { "
  982. " \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
  983. " \"renew-timer\": 1, "
  984. " \"rebind-timer\": 2, "
  985. " \"preferred-lifetime\": 3,"
  986. " \"valid-lifetime\": 4,"
  987. " \"subnet\": \"2001:db8:1::/64\" } ],"
  988. "\"valid-lifetime\": 4000 }";
  989. ElementPtr json = Element::fromJSON(config);
  990. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  991. // returned value should be 0 (configuration success)
  992. checkResult(status, 0);
  993. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  994. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  995. ASSERT_TRUE(subnet);
  996. EXPECT_EQ(1, subnet->getT1());
  997. EXPECT_EQ(2, subnet->getT2());
  998. EXPECT_EQ(3, subnet->getPreferred());
  999. EXPECT_EQ(4, subnet->getValid());
  1000. }
  1001. // This test checks if it is possible to define a subnet with an
  1002. // interface defined.
  1003. TEST_F(Dhcp6ParserTest, subnetInterface) {
  1004. ConstElementPtr status;
  1005. // There should be at least one interface
  1006. string config = "{ " + genIfaceConfig() + ","
  1007. "\"preferred-lifetime\": 3000,"
  1008. "\"rebind-timer\": 2000, "
  1009. "\"renew-timer\": 1000, "
  1010. "\"subnet6\": [ { "
  1011. " \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
  1012. " \"interface\": \"" + valid_iface_ + "\","
  1013. " \"subnet\": \"2001:db8:1::/64\" } ],"
  1014. "\"valid-lifetime\": 4000 }";
  1015. cout << config << endl;
  1016. ElementPtr json = Element::fromJSON(config);
  1017. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1018. // returned value should be 0 (configuration success)
  1019. checkResult(status, 0);
  1020. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  1021. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  1022. ASSERT_TRUE(subnet);
  1023. EXPECT_EQ(valid_iface_, subnet->getIface());
  1024. }
  1025. // This test checks if invalid interface name will be rejected in
  1026. // Subnet6 definition.
  1027. TEST_F(Dhcp6ParserTest, subnetInterfaceBogus) {
  1028. ConstElementPtr status;
  1029. // There should be at least one interface
  1030. string config = "{ " + genIfaceConfig() + ","
  1031. "\"preferred-lifetime\": 3000,"
  1032. "\"rebind-timer\": 2000, "
  1033. "\"renew-timer\": 1000, "
  1034. "\"subnet6\": [ { "
  1035. " \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
  1036. " \"interface\": \"" + bogus_iface_ + "\","
  1037. " \"subnet\": \"2001:db8:1::/64\" } ],"
  1038. "\"valid-lifetime\": 4000 }";
  1039. cout << config << endl;
  1040. ElementPtr json = Element::fromJSON(config);
  1041. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1042. // returned value should be 1 (configuration error)
  1043. checkResult(status, 1);
  1044. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  1045. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  1046. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  1047. EXPECT_FALSE(subnet);
  1048. }
  1049. // This test checks if it is not allowed to define global interface
  1050. // parameter.
  1051. TEST_F(Dhcp6ParserTest, interfaceGlobal) {
  1052. ConstElementPtr status;
  1053. string config = "{ " + genIfaceConfig() + ","
  1054. "\"preferred-lifetime\": 3000,"
  1055. "\"rebind-timer\": 2000, "
  1056. "\"renew-timer\": 1000, "
  1057. "\"interface\": \"" + valid_iface_ + "\"," // Not valid. Can be defined in subnet only
  1058. "\"subnet6\": [ { "
  1059. " \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
  1060. " \"subnet\": \"2001:db8:1::/64\" } ],"
  1061. "\"valid-lifetime\": 4000 }";
  1062. cout << config << endl;
  1063. ElementPtr json = Element::fromJSON(config);
  1064. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1065. // returned value should be 1 (parse error)
  1066. checkResult(status, 1);
  1067. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  1068. }
  1069. // This test checks if it is possible to define a subnet with an
  1070. // interface-id option defined.
  1071. TEST_F(Dhcp6ParserTest, subnetInterfaceId) {
  1072. const string valid_interface_id = "foobar";
  1073. const string bogus_interface_id = "blah";
  1074. // There should be at least one interface
  1075. const string config = "{ "
  1076. "\"preferred-lifetime\": 3000,"
  1077. "\"rebind-timer\": 2000, "
  1078. "\"renew-timer\": 1000, "
  1079. "\"subnet6\": [ { "
  1080. " \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
  1081. " \"interface-id\": \"" + valid_interface_id + "\","
  1082. " \"subnet\": \"2001:db8:1::/64\" } ],"
  1083. "\"valid-lifetime\": 4000 }";
  1084. ElementPtr json = Element::fromJSON(config);
  1085. ConstElementPtr status;
  1086. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1087. // Returned value should be 0 (configuration success)
  1088. checkResult(status, 0);
  1089. // Try to get a subnet based on bogus interface-id option
  1090. OptionBuffer tmp(bogus_interface_id.begin(), bogus_interface_id.end());
  1091. SubnetSelector selector;
  1092. selector.first_relay_linkaddr_ = IOAddress("5000::1");
  1093. selector.interface_id_.reset(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
  1094. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  1095. selectSubnet(selector);
  1096. EXPECT_FALSE(subnet);
  1097. // Now try to get subnet for valid interface-id value
  1098. tmp = OptionBuffer(valid_interface_id.begin(), valid_interface_id.end());
  1099. selector.interface_id_.reset(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
  1100. subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  1101. selectSubnet(selector);
  1102. ASSERT_TRUE(subnet);
  1103. EXPECT_TRUE(selector.interface_id_->equals(subnet->getInterfaceId()));
  1104. }
  1105. // This test checks if it is not allowed to define global interface
  1106. // parameter.
  1107. TEST_F(Dhcp6ParserTest, interfaceIdGlobal) {
  1108. const string config = "{ " + genIfaceConfig() + ","
  1109. "\"preferred-lifetime\": 3000,"
  1110. "\"rebind-timer\": 2000, "
  1111. "\"renew-timer\": 1000, "
  1112. "\"interface-id\": \"foobar\"," // Not valid. Can be defined in subnet only
  1113. "\"subnet6\": [ { "
  1114. " \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
  1115. " \"subnet\": \"2001:db8:1::/64\" } ],"
  1116. "\"valid-lifetime\": 4000 }";
  1117. ElementPtr json = Element::fromJSON(config);
  1118. ConstElementPtr status;
  1119. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1120. // Returned value should be 1 (parse error)
  1121. checkResult(status, 1);
  1122. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  1123. }
  1124. // This test checks if it is not possible to define a subnet with an
  1125. // interface (i.e. local subnet) and interface-id (remote subnet) defined.
  1126. TEST_F(Dhcp6ParserTest, subnetInterfaceAndInterfaceId) {
  1127. const string config = "{ \"preferred-lifetime\": 3000,"
  1128. "\"rebind-timer\": 2000, "
  1129. "\"renew-timer\": 1000, "
  1130. "\"subnet6\": [ { "
  1131. " \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
  1132. " \"interface\": \"" + valid_iface_ + "\","
  1133. " \"interface-id\": \"foobar\","
  1134. " \"subnet\": \"2001:db8:1::/64\" } ],"
  1135. "\"valid-lifetime\": 4000 }";
  1136. ElementPtr json = Element::fromJSON(config);
  1137. ConstElementPtr status;
  1138. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1139. // Returned value should be 1 (configuration error)
  1140. checkResult(status, 1);
  1141. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  1142. }
  1143. // This test checks the configuration of the Rapid Commit option
  1144. // support for the subnet.
  1145. TEST_F(Dhcp6ParserTest, subnetRapidCommit) {
  1146. {
  1147. // rapid-commit implicitly set to false.
  1148. SCOPED_TRACE("Default Rapid Commit setting");
  1149. testRapidCommit("{ \"preferred-lifetime\": 3000,"
  1150. "\"rebind-timer\": 2000, "
  1151. "\"renew-timer\": 1000, "
  1152. "\"subnet6\": [ { "
  1153. " \"pools\": [ { \"pool\": \"2001:db8:1::1 - "
  1154. "2001:db8:1::ffff\" } ],"
  1155. " \"subnet\": \"2001:db8:1::/64\" } ],"
  1156. "\"valid-lifetime\": 4000 }",
  1157. false);
  1158. }
  1159. {
  1160. // rapid-commit explicitly set to true.
  1161. SCOPED_TRACE("Enable Rapid Commit");
  1162. testRapidCommit("{ \"preferred-lifetime\": 3000,"
  1163. "\"rebind-timer\": 2000, "
  1164. "\"renew-timer\": 1000, "
  1165. "\"subnet6\": [ { "
  1166. " \"pools\": [ { \"pool\": \"2001:db8:1::1 - "
  1167. "2001:db8:1::ffff\" } ],"
  1168. " \"rapid-commit\": True,"
  1169. " \"subnet\": \"2001:db8:1::/64\" } ],"
  1170. "\"valid-lifetime\": 4000 }",
  1171. true);
  1172. }
  1173. {
  1174. // rapid-commit explicitly set to false.
  1175. SCOPED_TRACE("Disable Rapid Commit");
  1176. testRapidCommit("{ \"preferred-lifetime\": 3000,"
  1177. "\"rebind-timer\": 2000, "
  1178. "\"renew-timer\": 1000, "
  1179. "\"subnet6\": [ { "
  1180. " \"pools\": [ { \"pool\": \"2001:db8:1::1 - "
  1181. "2001:db8:1::ffff\" } ],"
  1182. " \"rapid-commit\": False,"
  1183. " \"subnet\": \"2001:db8:1::/64\" } ],"
  1184. "\"valid-lifetime\": 4000 }",
  1185. false);
  1186. }
  1187. }
  1188. // This test checks that multiple pools can be defined and handled properly.
  1189. // The test defines 2 subnets, each with 2 pools.
  1190. TEST_F(Dhcp6ParserTest, multiplePools) {
  1191. // Collection with two subnets, each with 2 pools.
  1192. string config = "{ " + genIfaceConfig() + ","
  1193. "\"preferred-lifetime\": 3000,"
  1194. "\"rebind-timer\": 2000, "
  1195. "\"renew-timer\": 1000, "
  1196. "\"subnet6\": [ { "
  1197. " \"pools\": [ "
  1198. " { \"pool\": \"2001:db8:1::/96\" },"
  1199. " { \"pool\": \"2001:db8:1:0:abcd::/112\" }"
  1200. " ],"
  1201. " \"subnet\": \"2001:db8:1::/64\" "
  1202. " },"
  1203. " {"
  1204. " \"pools\": [ "
  1205. " { \"pool\": \"2001:db8:2::1 - 2001:db8:2::ff\" },"
  1206. " { \"pool\": \"2001:db8:2::300 - 2001:db8:2::3ff\" }"
  1207. " ],"
  1208. " \"subnet\": \"2001:db8:2::/64\""
  1209. " } ],"
  1210. "\"valid-lifetime\": 4000 }";
  1211. ElementPtr json;
  1212. ASSERT_NO_THROW(json = Element::fromJSON(config));
  1213. ConstElementPtr status;
  1214. ASSERT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1215. checkResult(status, 0);
  1216. const Subnet6Collection* subnets =
  1217. CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->getAll();
  1218. ASSERT_TRUE(subnets);
  1219. ASSERT_EQ(2, subnets->size()); // We expect 2 subnets
  1220. // Check the first subnet
  1221. const PoolCollection& pools1 = subnets->at(0)->getPools(Lease::TYPE_NA);
  1222. ASSERT_EQ(2, pools1.size());
  1223. EXPECT_EQ("type=IA_NA, 2001:db8:1::-2001:db8:1::ffff:ffff, delegated_len=128",
  1224. pools1[0]->toText());
  1225. EXPECT_EQ("type=IA_NA, 2001:db8:1:0:abcd::-2001:db8:1:0:abcd::ffff, delegated_len=128",
  1226. pools1[1]->toText());
  1227. // There shouldn't be any TA or PD pools
  1228. EXPECT_TRUE(subnets->at(0)->getPools(Lease::TYPE_TA).empty());
  1229. EXPECT_TRUE(subnets->at(0)->getPools(Lease::TYPE_PD).empty());
  1230. // Check the second subnet
  1231. const PoolCollection& pools2 = subnets->at(1)->getPools(Lease::TYPE_NA);
  1232. ASSERT_EQ(2, pools2.size());
  1233. EXPECT_EQ("type=IA_NA, 2001:db8:2::1-2001:db8:2::ff, delegated_len=128",
  1234. pools2[0]->toText());
  1235. EXPECT_EQ("type=IA_NA, 2001:db8:2::300-2001:db8:2::3ff, delegated_len=128",
  1236. pools2[1]->toText());
  1237. // There shouldn't be any TA or PD pools
  1238. EXPECT_TRUE(subnets->at(0)->getPools(Lease::TYPE_TA).empty());
  1239. EXPECT_TRUE(subnets->at(0)->getPools(Lease::TYPE_PD).empty());
  1240. }
  1241. // Test verifies that a subnet with pool values that do not belong to that
  1242. // pool are rejected.
  1243. TEST_F(Dhcp6ParserTest, poolOutOfSubnet) {
  1244. ConstElementPtr status;
  1245. string config = "{ " + genIfaceConfig() + ","
  1246. "\"preferred-lifetime\": 3000,"
  1247. "\"rebind-timer\": 2000, "
  1248. "\"renew-timer\": 1000, "
  1249. "\"subnet6\": [ { "
  1250. " \"pools\": [ { \"pool\": \"4001:db8:1::/80\" } ],"
  1251. " \"subnet\": \"2001:db8:1::/64\" } ],"
  1252. "\"valid-lifetime\": 4000 }";
  1253. ElementPtr json = Element::fromJSON(config);
  1254. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1255. // returned value must be 1 (values error)
  1256. // as the pool does not belong to that subnet
  1257. checkResult(status, 1);
  1258. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  1259. }
  1260. // Goal of this test is to verify if pools can be defined
  1261. // using prefix/length notation. There is no separate test for min-max
  1262. // notation as it was tested in several previous tests.
  1263. // Note this test also verifies that subnets can be configured without
  1264. // prefix delegation pools.
  1265. TEST_F(Dhcp6ParserTest, poolPrefixLen) {
  1266. ConstElementPtr x;
  1267. string config = "{ " + genIfaceConfig() + ","
  1268. "\"preferred-lifetime\": 3000,"
  1269. "\"rebind-timer\": 2000, "
  1270. "\"renew-timer\": 1000, "
  1271. "\"subnet6\": [ { "
  1272. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  1273. " \"subnet\": \"2001:db8:1::/64\" } ],"
  1274. "\"valid-lifetime\": 4000 }";
  1275. ElementPtr json = Element::fromJSON(config);
  1276. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  1277. // returned value must be 1 (configuration parse error)
  1278. checkResult(x, 0);
  1279. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  1280. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  1281. ASSERT_TRUE(subnet);
  1282. EXPECT_EQ(1000, subnet->getT1());
  1283. EXPECT_EQ(2000, subnet->getT2());
  1284. EXPECT_EQ(3000, subnet->getPreferred());
  1285. EXPECT_EQ(4000, subnet->getValid());
  1286. }
  1287. // Goal of this test is to verify the basic parsing of a prefix delegation
  1288. // pool. It uses a single, valid pd pool.
  1289. TEST_F(Dhcp6ParserTest, pdPoolBasics) {
  1290. ConstElementPtr x;
  1291. // Define a single valid pd pool.
  1292. string config =
  1293. "{ " + genIfaceConfig() + ","
  1294. "\"preferred-lifetime\": 3000,"
  1295. "\"rebind-timer\": 2000, "
  1296. "\"renew-timer\": 1000, "
  1297. "\"subnet6\": [ { "
  1298. " \"subnet\": \"2001:db8:1::/64\","
  1299. " \"pd-pools\": ["
  1300. " { \"prefix\": \"2001:db8:1::\", "
  1301. " \"prefix-len\": 64, "
  1302. " \"delegated-len\": 128"
  1303. " } ],"
  1304. "\"valid-lifetime\": 4000 }"
  1305. "] }";
  1306. // Convert the JSON string into Elements.
  1307. ElementPtr json;
  1308. ASSERT_NO_THROW(json = Element::fromJSON(config));
  1309. // Verify that DHCP6 configuration processing succeeds.
  1310. // Returned value must be non-empty ConstElementPtr to config result.
  1311. // rcode should be 0 which indicates successful configuration processing.
  1312. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  1313. checkResult(x, 0);
  1314. // Test that we can retrieve the subnet.
  1315. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  1316. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  1317. ASSERT_TRUE(subnet);
  1318. // Fetch the collection of PD pools. It should have 1 entry.
  1319. PoolCollection pc;
  1320. ASSERT_NO_THROW(pc = subnet->getPools(Lease::TYPE_PD));
  1321. EXPECT_EQ(1, pc.size());
  1322. // Get a pointer to the pd pool instance, and verify its contents.
  1323. Pool6Ptr p6;
  1324. ASSERT_NO_THROW(p6 = boost::dynamic_pointer_cast<Pool6>(pc[0]));
  1325. ASSERT_TRUE(p6);
  1326. EXPECT_EQ("2001:db8:1::", p6->getFirstAddress().toText());
  1327. EXPECT_EQ(128, p6->getLength());
  1328. // prefix-len is not directly accessible after pool construction, so
  1329. // verify that it was interpreted correctly by checking the last address
  1330. // value.
  1331. isc::asiolink::IOAddress prefixAddress("2001:db8:1::");
  1332. EXPECT_EQ(lastAddrInPrefix(prefixAddress, 64), p6->getLastAddress());
  1333. }
  1334. // This test verifies that it is possible to specify a prefix pool with an
  1335. // excluded prefix (see RFC6603).
  1336. TEST_F(Dhcp6ParserTest, pdPoolPrefixExclude) {
  1337. ConstElementPtr x;
  1338. // Define a single valid pd pool.
  1339. string config =
  1340. "{ " + genIfaceConfig() + ","
  1341. "\"preferred-lifetime\": 3000,"
  1342. "\"rebind-timer\": 2000, "
  1343. "\"renew-timer\": 1000, "
  1344. "\"subnet6\": [ { "
  1345. " \"subnet\": \"2001:db8:1::/64\","
  1346. " \"pd-pools\": ["
  1347. " { \"prefix\": \"3000::\", "
  1348. " \"prefix-len\": 48, "
  1349. " \"delegated-len\": 64,"
  1350. " \"excluded-prefix\": \"3000:0:0:0:1000::\","
  1351. " \"excluded-prefix-len\": 72"
  1352. " } ],"
  1353. "\"valid-lifetime\": 4000 }"
  1354. "] }";
  1355. // Convert the JSON string into Elements.
  1356. ElementPtr json;
  1357. ASSERT_NO_THROW(json = Element::fromJSON(config));
  1358. // Verify that DHCP6 configuration processing succeeds.
  1359. // Returned value must be non-empty ConstElementPtr to config result.
  1360. // rcode should be 0 which indicates successful configuration processing.
  1361. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  1362. checkResult(x, 0);
  1363. // Test that we can retrieve the subnet.
  1364. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  1365. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  1366. ASSERT_TRUE(subnet);
  1367. // Fetch the collection of PD pools. It should have 1 entry.
  1368. PoolCollection pc;
  1369. ASSERT_NO_THROW(pc = subnet->getPools(Lease::TYPE_PD));
  1370. EXPECT_EQ(1, pc.size());
  1371. // Get a pointer to the pd pool instance, and verify its contents.
  1372. Pool6Ptr p6;
  1373. ASSERT_NO_THROW(p6 = boost::dynamic_pointer_cast<Pool6>(pc[0]));
  1374. ASSERT_TRUE(p6);
  1375. EXPECT_EQ("3000::", p6->getFirstAddress().toText());
  1376. EXPECT_EQ(64, p6->getLength());
  1377. // This pool should have Prefix Exclude option associated.
  1378. Option6PDExcludePtr pd_exclude_option = p6->getPrefixExcludeOption();
  1379. ASSERT_TRUE(pd_exclude_option);
  1380. // Pick a delegated prefix of 3000:0:0:3:1000::/64 which belongs to our
  1381. // pool of 3000::/48. For this prefix obtain a Prefix Exclude option and
  1382. // verify that it is correct.
  1383. EXPECT_EQ("3000:0:0:3:1000::",
  1384. pd_exclude_option->getExcludedPrefix(IOAddress("3000:0:0:3::"), 64).toText());
  1385. EXPECT_EQ(72, static_cast<unsigned>(pd_exclude_option->getExcludedPrefixLength()));
  1386. }
  1387. // Goal of this test is verify that a list of PD pools can be configured.
  1388. // It also verifies that a subnet may be configured with both regular pools
  1389. // and pd pools.
  1390. TEST_F(Dhcp6ParserTest, pdPoolList) {
  1391. ConstElementPtr x;
  1392. // We will configure three pools of prefixes for the subnet. Note that
  1393. // the 3rd prefix is out of the subnet prefix (the prefix doesn't match
  1394. // the subnet prefix).
  1395. const char* prefixes[] = {
  1396. "2001:db8:1:1::",
  1397. "2001:db8:1:2::",
  1398. "3000:1:3::"
  1399. };
  1400. string config =
  1401. "{ " + genIfaceConfig() + ","
  1402. "\"preferred-lifetime\": 3000,"
  1403. "\"rebind-timer\": 2000, "
  1404. "\"renew-timer\": 1000, "
  1405. "\"subnet6\": [ { "
  1406. " \"pools\": [ { \"pool\": \"2001:db8:1:04::/80\" } ],"
  1407. " \"subnet\": \"2001:db8:1::/40\","
  1408. " \"pd-pools\": ["
  1409. " { \"prefix\": \"2001:db8:1:01::\", "
  1410. " \"prefix-len\": 72, "
  1411. " \"delegated-len\": 80"
  1412. " },"
  1413. " { \"prefix\": \"2001:db8:1:02::\", "
  1414. " \"prefix-len\": 72, "
  1415. " \"delegated-len\": 88"
  1416. " },"
  1417. " { \"prefix\": \"3000:1:03::\", "
  1418. " \"prefix-len\": 72, "
  1419. " \"delegated-len\": 96"
  1420. " }"
  1421. "],"
  1422. "\"valid-lifetime\": 4000 }"
  1423. "] }";
  1424. // Convert the JSON string into Elements.
  1425. ElementPtr json = Element::fromJSON(config);
  1426. ASSERT_NO_THROW(json = Element::fromJSON(config));
  1427. // Verify that DHCP6 configuration processing succeeds.
  1428. // Returned value must be non-empty ConstElementPtr to config result.
  1429. // rcode should be 0 which indicates successful configuration processing.
  1430. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  1431. checkResult(x, 0);
  1432. // Test that we can retrieve the subnet.
  1433. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  1434. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  1435. ASSERT_TRUE(subnet);
  1436. // Fetch the collection of NA pools. It should have 1 entry.
  1437. PoolCollection pc;
  1438. ASSERT_NO_THROW(pc = subnet->getPools(Lease::TYPE_NA));
  1439. EXPECT_EQ(1, pc.size());
  1440. // Fetch the collection of PD pools. It should have 3 entries.
  1441. ASSERT_NO_THROW(pc = subnet->getPools(Lease::TYPE_PD));
  1442. EXPECT_EQ(3, pc.size());
  1443. // Loop through the pools and verify their contents.
  1444. for (unsigned int i = 0; i < 3; i++) {
  1445. Pool6Ptr p6;
  1446. ASSERT_NO_THROW(p6 = boost::dynamic_pointer_cast<Pool6>(pc[i]));
  1447. ASSERT_TRUE(p6);
  1448. EXPECT_EQ(prefixes[i], p6->getFirstAddress().toText());
  1449. EXPECT_EQ((80 + (i * 8)), p6->getLength());
  1450. }
  1451. }
  1452. // Goal of this test is to verify the a whole prefix can be delegated and that
  1453. // a whole subnet can be delegated.
  1454. TEST_F(Dhcp6ParserTest, subnetAndPrefixDelegated) {
  1455. ConstElementPtr x;
  1456. // Define a single valid pd pool.
  1457. string config =
  1458. "{ " + genIfaceConfig() + ","
  1459. "\"preferred-lifetime\": 3000,"
  1460. "\"rebind-timer\": 2000, "
  1461. "\"renew-timer\": 1000, "
  1462. "\"subnet6\": [ { "
  1463. " \"subnet\": \"2001:db8:1::/64\","
  1464. " \"pd-pools\": ["
  1465. " { \"prefix\": \"2001:db8:1::\", "
  1466. " \"prefix-len\": 64, "
  1467. " \"delegated-len\": 64"
  1468. " } ],"
  1469. "\"valid-lifetime\": 4000 }"
  1470. "] }";
  1471. // Convert the JSON string into Elements.
  1472. ElementPtr json;
  1473. ASSERT_NO_THROW(json = Element::fromJSON(config));
  1474. // Verify that DHCP6 configuration processing succeeds.
  1475. // Returned value must be non-empty ConstElementPtr to config result.
  1476. // rcode should be 0 which indicates successful configuration processing.
  1477. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  1478. checkResult(x, 0);
  1479. // Test that we can retrieve the subnet.
  1480. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  1481. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  1482. ASSERT_TRUE(subnet);
  1483. // Fetch the collection of PD pools. It should have 1 entry.
  1484. PoolCollection pc;
  1485. ASSERT_NO_THROW(pc = subnet->getPools(Lease::TYPE_PD));
  1486. EXPECT_EQ(1, pc.size());
  1487. // Get a pointer to the pd pool instance, and verify its contents.
  1488. Pool6Ptr p6;
  1489. ASSERT_NO_THROW(p6 = boost::dynamic_pointer_cast<Pool6>(pc[0]));
  1490. ASSERT_TRUE(p6);
  1491. EXPECT_EQ("2001:db8:1::", p6->getFirstAddress().toText());
  1492. EXPECT_EQ(64, p6->getLength());
  1493. // prefix-len is not directly accessible after pool construction, so
  1494. // verify that it was interpreted correctly by checking the last address
  1495. // value.
  1496. isc::asiolink::IOAddress prefixAddress("2001:db8:1::");
  1497. EXPECT_EQ(lastAddrInPrefix(prefixAddress, 64), p6->getLastAddress());
  1498. }
  1499. // Goal of this test is check for proper handling of invalid prefix delegation
  1500. // pool configuration. It uses an array of invalid configurations to check
  1501. // a variety of configuration errors.
  1502. TEST_F(Dhcp6ParserTest, invalidPdPools) {
  1503. ConstElementPtr x;
  1504. const char *config[] = {
  1505. // No prefix.
  1506. "{ \"interfaces-config\": { },"
  1507. "\"preferred-lifetime\": 3000,"
  1508. "\"rebind-timer\": 2000, "
  1509. "\"renew-timer\": 1000, "
  1510. "\"subnet6\": [ { "
  1511. " \"subnet\": \"2001:db8:1::/64\","
  1512. " \"pd-pools\": ["
  1513. " { "
  1514. " \"prefix-len\": 64, "
  1515. " \"delegated-len\": 128"
  1516. " } ],"
  1517. "\"valid-lifetime\": 4000 }"
  1518. "] }",
  1519. // No prefix-len.
  1520. "{ \"interfaces-config\": { },"
  1521. "\"preferred-lifetime\": 3000,"
  1522. "\"rebind-timer\": 2000, "
  1523. "\"renew-timer\": 1000, "
  1524. "\"subnet6\": [ { "
  1525. " \"subnet\": \"2001:db8:1::/64\","
  1526. " \"pd-pools\": ["
  1527. " { \"prefix\": \"2001:db8:1::\", "
  1528. " \"delegated-len\": 128"
  1529. " } ],"
  1530. "\"valid-lifetime\": 4000 }"
  1531. "] }",
  1532. // No delegated-len.
  1533. "{ \"interfaces-config\": { },"
  1534. "\"preferred-lifetime\": 3000,"
  1535. "\"rebind-timer\": 2000, "
  1536. "\"renew-timer\": 1000, "
  1537. "\"subnet6\": [ { "
  1538. " \"subnet\": \"2001:db8:1::/64\","
  1539. " \"pd-pools\": ["
  1540. " { \"prefix\": \"2001:db8:1::\", "
  1541. " \"prefix-len\": 64 "
  1542. " } ],"
  1543. "\"valid-lifetime\": 4000 }"
  1544. "] }",
  1545. // Delegated length is too short.
  1546. "{ \"interfaces-config\": { },"
  1547. "\"preferred-lifetime\": 3000,"
  1548. "\"rebind-timer\": 2000, "
  1549. "\"renew-timer\": 1000, "
  1550. "\"subnet6\": [ { "
  1551. " \"subnet\": \"2001:db8:1::/64\","
  1552. " \"pd-pools\": ["
  1553. " { \"prefix\": \"2001:db8:1::\", "
  1554. " \"prefix-len\": 128, "
  1555. " \"delegated-len\": 64"
  1556. " } ],"
  1557. "\"valid-lifetime\": 4000 }"
  1558. "] }"
  1559. };
  1560. ElementPtr json;
  1561. int num_msgs = sizeof(config)/sizeof(char*);
  1562. for (unsigned int i = 0; i < num_msgs; i++) {
  1563. // Convert JSON string to Elements.
  1564. ASSERT_NO_THROW(json = Element::fromJSON(config[i]));
  1565. // Configuration processing should fail without a throw.
  1566. ASSERT_NO_THROW(x = configureDhcp6Server(srv_, json));
  1567. // Returned value must be non-empty ConstElementPtr to config result.
  1568. // rcode should be 1 which indicates configuration error.
  1569. checkResult(x, 1);
  1570. EXPECT_TRUE(errorContainsPosition(x, "<string>"));
  1571. }
  1572. }
  1573. // The goal of this test is to check whether an option definition
  1574. // that defines an option carrying an IPv6 address can be created.
  1575. TEST_F(Dhcp6ParserTest, optionDefIpv6Address) {
  1576. // Configuration string.
  1577. std::string config =
  1578. "{ \"option-def\": [ {"
  1579. " \"name\": \"foo\","
  1580. " \"code\": 100,"
  1581. " \"type\": \"ipv6-address\","
  1582. " \"space\": \"isc\""
  1583. " } ]"
  1584. "}";
  1585. ElementPtr json = Element::fromJSON(config);
  1586. // Make sure that the particular option definition does not exist.
  1587. OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
  1588. getCfgOptionDef()->get("isc", 100);
  1589. ASSERT_FALSE(def);
  1590. // Use the configuration string to create new option definition.
  1591. ConstElementPtr status;
  1592. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1593. ASSERT_TRUE(status);
  1594. checkResult(status, 0);
  1595. // We need to commit option definitions because later in this test we
  1596. // will be checking if they get removed when "option-def" parameter
  1597. // is removed from a configuration.
  1598. LibDHCP::commitRuntimeOptionDefs();
  1599. // The option definition should now be available in the CfgMgr.
  1600. def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("isc", 100);
  1601. ASSERT_TRUE(def);
  1602. // Verify that the option definition data is valid.
  1603. EXPECT_EQ("foo", def->getName());
  1604. EXPECT_EQ(100, def->getCode());
  1605. EXPECT_FALSE(def->getArrayType());
  1606. EXPECT_EQ(OPT_IPV6_ADDRESS_TYPE, def->getType());
  1607. // The copy of the option definition should be available in the libdhcp++.
  1608. OptionDefinitionPtr def_libdhcp = LibDHCP::getRuntimeOptionDef("isc", 100);
  1609. ASSERT_TRUE(def_libdhcp);
  1610. // Both definitions should be held in distinct pointers but they should
  1611. // be equal.
  1612. EXPECT_TRUE(def_libdhcp != def);
  1613. EXPECT_TRUE(*def_libdhcp == *def);
  1614. // Let's apply empty configuration. This removes the option definitions
  1615. // configuration and should result in removal of the option 100 from the
  1616. // libdhcp++.
  1617. config = "{ }";
  1618. json = Element::fromJSON(config);
  1619. ASSERT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1620. checkResult(status, 0);
  1621. EXPECT_FALSE(LibDHCP::getRuntimeOptionDef("isc", 100));
  1622. }
  1623. // The goal of this test is to check whether an option definition
  1624. // that defines an option carrying a record of data fields can
  1625. // be created.
  1626. TEST_F(Dhcp6ParserTest, optionDefRecord) {
  1627. // Configuration string.
  1628. std::string config =
  1629. "{ \"option-def\": [ {"
  1630. " \"name\": \"foo\","
  1631. " \"code\": 100,"
  1632. " \"type\": \"record\","
  1633. " \"record-types\": \"uint16, ipv4-address, ipv6-address, string\","
  1634. " \"space\": \"isc\""
  1635. " } ]"
  1636. "}";
  1637. ElementPtr json = Element::fromJSON(config);
  1638. // Make sure that the particular option definition does not exist.
  1639. OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
  1640. getCfgOptionDef()->get("isc", 100);
  1641. ASSERT_FALSE(def);
  1642. // Use the configuration string to create new option definition.
  1643. ConstElementPtr status;
  1644. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1645. ASSERT_TRUE(status);
  1646. checkResult(status, 0);
  1647. // The option definition should now be available in the CfgMgr.
  1648. def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("isc", 100);
  1649. ASSERT_TRUE(def);
  1650. // Check the option data.
  1651. EXPECT_EQ("foo", def->getName());
  1652. EXPECT_EQ(100, def->getCode());
  1653. EXPECT_EQ(OPT_RECORD_TYPE, def->getType());
  1654. EXPECT_FALSE(def->getArrayType());
  1655. // The option comprises the record of data fields. Verify that all
  1656. // fields are present and they are of the expected types.
  1657. const OptionDefinition::RecordFieldsCollection& record_fields =
  1658. def->getRecordFields();
  1659. ASSERT_EQ(4, record_fields.size());
  1660. EXPECT_EQ(OPT_UINT16_TYPE, record_fields[0]);
  1661. EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, record_fields[1]);
  1662. EXPECT_EQ(OPT_IPV6_ADDRESS_TYPE, record_fields[2]);
  1663. EXPECT_EQ(OPT_STRING_TYPE, record_fields[3]);
  1664. }
  1665. // The goal of this test is to verify that multiple option definitions
  1666. // can be created.
  1667. TEST_F(Dhcp6ParserTest, optionDefMultiple) {
  1668. // Configuration string.
  1669. std::string config =
  1670. "{ \"option-def\": [ {"
  1671. " \"name\": \"foo\","
  1672. " \"code\": 100,"
  1673. " \"type\": \"uint32\","
  1674. " \"space\": \"isc\""
  1675. " },"
  1676. " {"
  1677. " \"name\": \"foo-2\","
  1678. " \"code\": 101,"
  1679. " \"type\": \"ipv4-address\","
  1680. " \"space\": \"isc\""
  1681. " } ]"
  1682. "}";
  1683. ElementPtr json = Element::fromJSON(config);
  1684. // Make sure that the option definitions do not exist yet.
  1685. ASSERT_FALSE(CfgMgr::instance().getStagingCfg()->
  1686. getCfgOptionDef()->get("isc", 100));
  1687. ASSERT_FALSE(CfgMgr::instance().getStagingCfg()->
  1688. getCfgOptionDef()->get("isc", 101));
  1689. // Use the configuration string to create new option definitions.
  1690. ConstElementPtr status;
  1691. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1692. ASSERT_TRUE(status);
  1693. checkResult(status, 0);
  1694. // Check the first definition we have created.
  1695. OptionDefinitionPtr def1 = CfgMgr::instance().getStagingCfg()->
  1696. getCfgOptionDef()->get("isc", 100);
  1697. ASSERT_TRUE(def1);
  1698. // Check the option data.
  1699. EXPECT_EQ("foo", def1->getName());
  1700. EXPECT_EQ(100, def1->getCode());
  1701. EXPECT_EQ(OPT_UINT32_TYPE, def1->getType());
  1702. EXPECT_FALSE(def1->getArrayType());
  1703. // Check the second option definition we have created.
  1704. OptionDefinitionPtr def2 = CfgMgr::instance().getStagingCfg()->
  1705. getCfgOptionDef()->get("isc", 101);
  1706. ASSERT_TRUE(def2);
  1707. // Check the option data.
  1708. EXPECT_EQ("foo-2", def2->getName());
  1709. EXPECT_EQ(101, def2->getCode());
  1710. EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def2->getType());
  1711. EXPECT_FALSE(def2->getArrayType());
  1712. }
  1713. // The goal of this test is to verify that the duplicated option
  1714. // definition is not accepted.
  1715. TEST_F(Dhcp6ParserTest, optionDefDuplicate) {
  1716. // Preconfigure libdhcp++ with option definitions. The new configuration
  1717. // should override it, but when the new configuration fails, it should
  1718. // revert to this original configuration.
  1719. OptionDefSpaceContainer defs;
  1720. OptionDefinitionPtr def(new OptionDefinition("bar", 233, "string"));
  1721. defs.addItem(def, "isc");
  1722. LibDHCP::setRuntimeOptionDefs(defs);
  1723. LibDHCP::commitRuntimeOptionDefs();
  1724. // Configuration string. Both option definitions have
  1725. // the same code and belong to the same option space.
  1726. // This configuration should not be accepted.
  1727. std::string config =
  1728. "{ \"option-def\": [ {"
  1729. " \"name\": \"foo\","
  1730. " \"code\": 100,"
  1731. " \"type\": \"uint32\","
  1732. " \"space\": \"isc\""
  1733. " },"
  1734. " {"
  1735. " \"name\": \"foo-2\","
  1736. " \"code\": 100,"
  1737. " \"type\": \"ipv4-address\","
  1738. " \"space\": \"isc\""
  1739. " } ]"
  1740. "}";
  1741. ElementPtr json = Element::fromJSON(config);
  1742. // Make sure that the option definition does not exist yet.
  1743. ASSERT_FALSE(CfgMgr::instance().getStagingCfg()->
  1744. getCfgOptionDef()->get("isc", 100));
  1745. // Use the configuration string to create new option definitions.
  1746. ConstElementPtr status;
  1747. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1748. ASSERT_TRUE(status);
  1749. checkResult(status, 1);
  1750. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  1751. // The new configuration should have inserted option 100, but
  1752. // once configuration failed (on the duplicate option definition)
  1753. // the original configuration in libdhcp++ should be reverted.
  1754. EXPECT_FALSE(LibDHCP::getRuntimeOptionDef("isc", 100));
  1755. def = LibDHCP::getRuntimeOptionDef("isc", 233);
  1756. ASSERT_TRUE(def);
  1757. EXPECT_EQ("bar", def->getName());
  1758. EXPECT_EQ(233, def->getCode());
  1759. }
  1760. // The goal of this test is to verify that the option definition
  1761. // comprising an array of uint32 values can be created.
  1762. TEST_F(Dhcp6ParserTest, optionDefArray) {
  1763. // Configuration string. Created option definition should
  1764. // comprise an array of uint32 values.
  1765. std::string config =
  1766. "{ \"option-def\": [ {"
  1767. " \"name\": \"foo\","
  1768. " \"code\": 100,"
  1769. " \"type\": \"uint32\","
  1770. " \"array\": True,"
  1771. " \"space\": \"isc\""
  1772. " } ]"
  1773. "}";
  1774. ElementPtr json = Element::fromJSON(config);
  1775. // Make sure that the particular option definition does not exist.
  1776. OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
  1777. getCfgOptionDef()->get("isc", 100);
  1778. ASSERT_FALSE(def);
  1779. // Use the configuration string to create new option definition.
  1780. ConstElementPtr status;
  1781. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1782. ASSERT_TRUE(status);
  1783. checkResult(status, 0);
  1784. // The option definition should now be available in the CfgMgr.
  1785. def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("isc", 100);
  1786. ASSERT_TRUE(def);
  1787. // Check the option data.
  1788. EXPECT_EQ("foo", def->getName());
  1789. EXPECT_EQ(100, def->getCode());
  1790. EXPECT_EQ(OPT_UINT32_TYPE, def->getType());
  1791. EXPECT_TRUE(def->getArrayType());
  1792. }
  1793. // The purpose of this test to verify that encapsulated option
  1794. // space name may be specified.
  1795. TEST_F(Dhcp6ParserTest, optionDefEncapsulate) {
  1796. // Configuration string. Included the encapsulated
  1797. // option space name.
  1798. std::string config =
  1799. "{ \"option-def\": [ {"
  1800. " \"name\": \"foo\","
  1801. " \"code\": 100,"
  1802. " \"type\": \"uint32\","
  1803. " \"space\": \"isc\","
  1804. " \"encapsulate\": \"sub-opts-space\""
  1805. " } ]"
  1806. "}";
  1807. ElementPtr json = Element::fromJSON(config);
  1808. // Make sure that the particular option definition does not exist.
  1809. OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
  1810. getCfgOptionDef()->get("isc", 100);
  1811. ASSERT_FALSE(def);
  1812. // Use the configuration string to create new option definition.
  1813. ConstElementPtr status;
  1814. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1815. ASSERT_TRUE(status);
  1816. checkResult(status, 0);
  1817. // The option definition should now be available in the CfgMgr.
  1818. def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("isc", 100);
  1819. ASSERT_TRUE(def);
  1820. // Check the option data.
  1821. EXPECT_EQ("foo", def->getName());
  1822. EXPECT_EQ(100, def->getCode());
  1823. EXPECT_EQ(OPT_UINT32_TYPE, def->getType());
  1824. EXPECT_FALSE(def->getArrayType());
  1825. EXPECT_EQ("sub-opts-space", def->getEncapsulatedSpace());
  1826. }
  1827. /// The purpose of this test is to verify that the option definition
  1828. /// with invalid name is not accepted.
  1829. TEST_F(Dhcp6ParserTest, optionDefInvalidName) {
  1830. // Configuration string. The option name is invalid as it
  1831. // contains the % character.
  1832. std::string config =
  1833. "{ \"option-def\": [ {"
  1834. " \"name\": \"invalid%name\","
  1835. " \"code\": 100,"
  1836. " \"type\": \"string\","
  1837. " \"space\": \"isc\""
  1838. " } ]"
  1839. "}";
  1840. ElementPtr json = Element::fromJSON(config);
  1841. // Use the configuration string to create new option definition.
  1842. ConstElementPtr status;
  1843. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1844. ASSERT_TRUE(status);
  1845. // Expecting parsing error (error code 1).
  1846. checkResult(status, 1);
  1847. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  1848. }
  1849. /// The purpose of this test is to verify that the option definition
  1850. /// with invalid type is not accepted.
  1851. TEST_F(Dhcp6ParserTest, optionDefInvalidType) {
  1852. // Configuration string. The option type is invalid. It is
  1853. // "sting" instead of "string".
  1854. std::string config =
  1855. "{ \"option-def\": [ {"
  1856. " \"name\": \"foo\","
  1857. " \"code\": 100,"
  1858. " \"type\": \"sting\","
  1859. " \"space\": \"isc\""
  1860. " } ]"
  1861. "}";
  1862. ElementPtr json = Element::fromJSON(config);
  1863. // Use the configuration string to create new option definition.
  1864. ConstElementPtr status;
  1865. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1866. ASSERT_TRUE(status);
  1867. // Expecting parsing error (error code 1).
  1868. checkResult(status, 1);
  1869. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  1870. }
  1871. /// The purpose of this test is to verify that the option definition
  1872. /// with invalid type is not accepted.
  1873. TEST_F(Dhcp6ParserTest, optionDefInvalidRecordType) {
  1874. // Configuration string. The third of the record fields
  1875. // is invalid. It is "sting" instead of "string".
  1876. std::string config =
  1877. "{ \"option-def\": [ {"
  1878. " \"name\": \"foo\","
  1879. " \"code\": 100,"
  1880. " \"type\": \"record\","
  1881. " \"record-types\": \"uint32,uint8,sting\","
  1882. " \"space\": \"isc\""
  1883. " } ]"
  1884. "}";
  1885. ElementPtr json = Element::fromJSON(config);
  1886. // Use the configuration string to create new option definition.
  1887. ConstElementPtr status;
  1888. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1889. ASSERT_TRUE(status);
  1890. // Expecting parsing error (error code 1).
  1891. checkResult(status, 1);
  1892. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  1893. }
  1894. /// The goal of this test is to verify that the invalid encapsulated
  1895. /// option space name is not accepted.
  1896. TEST_F(Dhcp6ParserTest, optionDefInvalidEncapsulatedSpace) {
  1897. // Configuration string. The encapsulated option space
  1898. // name is invalid (% character is not allowed).
  1899. std::string config =
  1900. "{ \"option-def\": [ {"
  1901. " \"name\": \"foo\","
  1902. " \"code\": 100,"
  1903. " \"type\": \"uint32\","
  1904. " \"space\": \"isc\","
  1905. " \"encapsulate\": \"invalid%space%name\""
  1906. " } ]"
  1907. "}";
  1908. ElementPtr json = Element::fromJSON(config);
  1909. // Use the configuration string to create new option definition.
  1910. ConstElementPtr status;
  1911. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1912. ASSERT_TRUE(status);
  1913. // Expecting parsing error (error code 1).
  1914. checkResult(status, 1);
  1915. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  1916. }
  1917. /// The goal of this test is to verify that the encapsulated
  1918. /// option space name can't be specified for the option that
  1919. /// comprises an array of data fields.
  1920. TEST_F(Dhcp6ParserTest, optionDefEncapsulatedSpaceAndArray) {
  1921. // Configuration string. The encapsulated option space
  1922. // name is set to non-empty value and the array flag
  1923. // is set.
  1924. std::string config =
  1925. "{ \"option-def\": [ {"
  1926. " \"name\": \"foo\","
  1927. " \"code\": 100,"
  1928. " \"type\": \"uint32\","
  1929. " \"array\": True,"
  1930. " \"space\": \"isc\","
  1931. " \"encapsulate\": \"valid-space-name\""
  1932. " } ]"
  1933. "}";
  1934. ElementPtr json = Element::fromJSON(config);
  1935. // Use the configuration string to create new option definition.
  1936. ConstElementPtr status;
  1937. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1938. ASSERT_TRUE(status);
  1939. // Expecting parsing error (error code 1).
  1940. checkResult(status, 1);
  1941. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  1942. }
  1943. /// The goal of this test is to verify that the option may not
  1944. /// encapsulate option space it belongs to.
  1945. TEST_F(Dhcp6ParserTest, optionDefEncapsulateOwnSpace) {
  1946. // Configuration string. Option is set to encapsulate
  1947. // option space it belongs to.
  1948. std::string config =
  1949. "{ \"option-def\": [ {"
  1950. " \"name\": \"foo\","
  1951. " \"code\": 100,"
  1952. " \"type\": \"uint32\","
  1953. " \"space\": \"isc\","
  1954. " \"encapsulate\": \"isc\""
  1955. " } ]"
  1956. "}";
  1957. ElementPtr json = Element::fromJSON(config);
  1958. // Use the configuration string to create new option definition.
  1959. ConstElementPtr status;
  1960. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1961. ASSERT_TRUE(status);
  1962. // Expecting parsing error (error code 1).
  1963. checkResult(status, 1);
  1964. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  1965. }
  1966. /// The purpose of this test is to verify that it is not allowed
  1967. /// to override the standard option (that belongs to dhcp6 option
  1968. /// space and has its definition) and that it is allowed to define
  1969. /// option in the dhcp6 option space that has a code which is not
  1970. /// used by any of the standard options.
  1971. TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
  1972. // Configuration string. The option code 100 is unassigned
  1973. // so it can be used for a custom option definition in
  1974. // dhcp6 option space.
  1975. std::string config =
  1976. "{ \"option-def\": [ {"
  1977. " \"name\": \"foo\","
  1978. " \"code\": 100,"
  1979. " \"type\": \"string\","
  1980. " \"space\": \"dhcp6\""
  1981. " } ]"
  1982. "}";
  1983. ElementPtr json = Element::fromJSON(config);
  1984. OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
  1985. getCfgOptionDef()->get(DHCP6_OPTION_SPACE, 100);
  1986. ASSERT_FALSE(def);
  1987. // Use the configuration string to create new option definition.
  1988. ConstElementPtr status;
  1989. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  1990. ASSERT_TRUE(status);
  1991. checkResult(status, 0);
  1992. // The option definition should now be available in the CfgMgr.
  1993. def = CfgMgr::instance().getStagingCfg()->
  1994. getCfgOptionDef()->get(DHCP6_OPTION_SPACE, 100);
  1995. ASSERT_TRUE(def);
  1996. // Check the option data.
  1997. EXPECT_EQ("foo", def->getName());
  1998. EXPECT_EQ(100, def->getCode());
  1999. EXPECT_EQ(OPT_STRING_TYPE, def->getType());
  2000. EXPECT_FALSE(def->getArrayType());
  2001. // The combination of option space and code is invalid. The 'dhcp6'
  2002. // option space groups standard options and the code 3 is reserved
  2003. // for one of them.
  2004. config =
  2005. "{ \"option-def\": [ {"
  2006. " \"name\": \"foo\","
  2007. " \"code\": 3,"
  2008. " \"type\": \"string\","
  2009. " \"space\": \"dhcp6\""
  2010. " } ]"
  2011. "}";
  2012. json = Element::fromJSON(config);
  2013. // Use the configuration string to create new option definition.
  2014. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  2015. ASSERT_TRUE(status);
  2016. // Expecting parsing error (error code 1).
  2017. checkResult(status, 1);
  2018. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  2019. /// @todo The option 63 is a standard DHCPv6 option. However, at this point
  2020. /// there is no definition for this option in libdhcp++, so it should be
  2021. /// allowed to define it from the configuration interface. This test will
  2022. /// have to be removed once definitions for remaining standard options are
  2023. /// created.
  2024. config =
  2025. "{ \"option-def\": [ {"
  2026. " \"name\": \"geolocation\","
  2027. " \"code\": 63,"
  2028. " \"type\": \"string\","
  2029. " \"space\": \"dhcp6\""
  2030. " } ]"
  2031. "}";
  2032. json = Element::fromJSON(config);
  2033. // Use the configuration string to create new option definition.
  2034. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  2035. ASSERT_TRUE(status);
  2036. // Expecting success.
  2037. checkResult(status, 0);
  2038. def = CfgMgr::instance().getStagingCfg()->
  2039. getCfgOptionDef()->get(DHCP6_OPTION_SPACE, 63);
  2040. ASSERT_TRUE(def);
  2041. // Check the option data.
  2042. EXPECT_EQ("geolocation", def->getName());
  2043. EXPECT_EQ(63, def->getCode());
  2044. EXPECT_EQ(OPT_STRING_TYPE, def->getType());
  2045. EXPECT_FALSE(def->getArrayType());
  2046. }
  2047. // Goal of this test is to verify that global option data is configured
  2048. TEST_F(Dhcp6ParserTest, optionDataDefaultsGlobal) {
  2049. ConstElementPtr x;
  2050. string config = "{ " + genIfaceConfig() + ","
  2051. "\"preferred-lifetime\": 3000,"
  2052. "\"rebind-timer\": 2000,"
  2053. "\"renew-timer\": 1000,"
  2054. "\"option-data\": [ {"
  2055. " \"name\": \"subscriber-id\","
  2056. " \"data\": \"ABCDEF0105\","
  2057. " \"csv-format\": False"
  2058. " },"
  2059. " {"
  2060. " \"name\": \"preference\","
  2061. " \"data\": \"01\""
  2062. " } ],"
  2063. "\"subnet6\": [ { "
  2064. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  2065. " \"subnet\": \"2001:db8:1::/64\""
  2066. " } ],"
  2067. "\"valid-lifetime\": 4000 }";
  2068. ElementPtr json = Element::fromJSON(config);
  2069. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  2070. checkResult(x, 0);
  2071. // These options are global
  2072. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  2073. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  2074. ASSERT_TRUE(subnet);
  2075. OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
  2076. ASSERT_EQ(0, options->size());
  2077. options = CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
  2078. ASSERT_EQ(2, options->size());
  2079. // Get the search index. Index #1 is to search using option code.
  2080. const OptionContainerTypeIndex& idx = options->get<1>();
  2081. // Get the options for specified index. Expecting one option to be
  2082. // returned but in theory we may have multiple options with the same
  2083. // code so we get the range.
  2084. std::pair<OptionContainerTypeIndex::const_iterator,
  2085. OptionContainerTypeIndex::const_iterator> range =
  2086. idx.equal_range(D6O_SUBSCRIBER_ID);
  2087. // Expect single option with the code equal to 38.
  2088. ASSERT_EQ(1, std::distance(range.first, range.second));
  2089. const uint8_t subid_expected[] = {
  2090. 0xAB, 0xCD, 0xEF, 0x01, 0x05
  2091. };
  2092. // Check if option is valid in terms of code and carried data.
  2093. testOption(*range.first, D6O_SUBSCRIBER_ID, subid_expected,
  2094. sizeof(subid_expected));
  2095. range = idx.equal_range(D6O_PREFERENCE);
  2096. ASSERT_EQ(1, std::distance(range.first, range.second));
  2097. // Do another round of testing with second option.
  2098. const uint8_t pref_expected[] = {
  2099. 0x01
  2100. };
  2101. testOption(*range.first, D6O_PREFERENCE, pref_expected,
  2102. sizeof(pref_expected));
  2103. // Check that options with other option codes are not returned.
  2104. for (uint16_t code = 47; code < 57; ++code) {
  2105. range = idx.equal_range(code);
  2106. EXPECT_EQ(0, std::distance(range.first, range.second));
  2107. }
  2108. }
  2109. // Goal of this test is to verify that subnet option data is configured
  2110. TEST_F(Dhcp6ParserTest, optionDataDefaultsSubnet) {
  2111. ConstElementPtr x;
  2112. string config = "{ " + genIfaceConfig() + ","
  2113. "\"preferred-lifetime\": 3000,"
  2114. "\"rebind-timer\": 2000,"
  2115. "\"renew-timer\": 1000,"
  2116. "\"subnet6\": [ { "
  2117. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  2118. " \"subnet\": \"2001:db8:1::/64\","
  2119. " \"option-data\": [ {"
  2120. " \"name\": \"subscriber-id\","
  2121. " \"data\": \"ABCDEF0105\","
  2122. " \"csv-format\": False"
  2123. " },"
  2124. " {"
  2125. " \"name\": \"preference\","
  2126. " \"data\": \"01\""
  2127. " } ]"
  2128. " } ],"
  2129. "\"valid-lifetime\": 4000 }";
  2130. ElementPtr json = Element::fromJSON(config);
  2131. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  2132. checkResult(x, 0);
  2133. // These options are subnet options
  2134. OptionContainerPtr options =
  2135. CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
  2136. ASSERT_EQ(0, options->size());
  2137. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  2138. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  2139. ASSERT_TRUE(subnet);
  2140. options = subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
  2141. ASSERT_EQ(2, options->size());
  2142. // Get the search index. Index #1 is to search using option code.
  2143. const OptionContainerTypeIndex& idx = options->get<1>();
  2144. // Get the options for specified index. Expecting one option to be
  2145. // returned but in theory we may have multiple options with the same
  2146. // code so we get the range.
  2147. std::pair<OptionContainerTypeIndex::const_iterator,
  2148. OptionContainerTypeIndex::const_iterator> range =
  2149. idx.equal_range(D6O_SUBSCRIBER_ID);
  2150. // Expect single option with the code equal to 38.
  2151. ASSERT_EQ(1, std::distance(range.first, range.second));
  2152. const uint8_t subid_expected[] = {
  2153. 0xAB, 0xCD, 0xEF, 0x01, 0x05
  2154. };
  2155. // Check if option is valid in terms of code and carried data.
  2156. testOption(*range.first, D6O_SUBSCRIBER_ID, subid_expected,
  2157. sizeof(subid_expected));
  2158. range = idx.equal_range(D6O_PREFERENCE);
  2159. ASSERT_EQ(1, std::distance(range.first, range.second));
  2160. // Do another round of testing with second option.
  2161. const uint8_t pref_expected[] = {
  2162. 0x01
  2163. };
  2164. testOption(*range.first, D6O_PREFERENCE, pref_expected,
  2165. sizeof(pref_expected));
  2166. // Check that options with other option codes are not returned.
  2167. for (uint16_t code = 47; code < 57; ++code) {
  2168. range = idx.equal_range(code);
  2169. EXPECT_EQ(0, std::distance(range.first, range.second));
  2170. }
  2171. }
  2172. /// The goal of this test is to verify that two options having the same
  2173. /// option code can be added to different option spaces.
  2174. TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) {
  2175. // This configuration string is to configure two options
  2176. // sharing the code 56 and having different definitions
  2177. // and belonging to the different option spaces.
  2178. // The option definition must be provided for the
  2179. // option that belongs to the 'isc' option space.
  2180. // The definition is not required for the option that
  2181. // belongs to the 'dhcp6' option space as it is the
  2182. // standard option.
  2183. string config = "{ " + genIfaceConfig() + ","
  2184. "\"preferred-lifetime\": 3000,"
  2185. "\"valid-lifetime\": 4000,"
  2186. "\"rebind-timer\": 2000,"
  2187. "\"renew-timer\": 1000,"
  2188. "\"option-data\": [ {"
  2189. " \"name\": \"subscriber-id\","
  2190. " \"data\": \"ABCDEF0105\","
  2191. " \"csv-format\": False"
  2192. " },"
  2193. " {"
  2194. " \"name\": \"foo\","
  2195. " \"space\": \"isc\","
  2196. " \"data\": \"1234\""
  2197. " } ],"
  2198. "\"option-def\": [ {"
  2199. " \"name\": \"foo\","
  2200. " \"code\": 38,"
  2201. " \"type\": \"uint32\","
  2202. " \"space\": \"isc\""
  2203. " } ],"
  2204. "\"subnet6\": [ { "
  2205. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  2206. " \"subnet\": \"2001:db8:1::/64\""
  2207. " } ]"
  2208. "}";
  2209. ConstElementPtr status;
  2210. ElementPtr json = Element::fromJSON(config);
  2211. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  2212. ASSERT_TRUE(status);
  2213. checkResult(status, 0);
  2214. // Options should be now available
  2215. // Try to get the option from the space dhcp6.
  2216. OptionDescriptor desc1 =
  2217. CfgMgr::instance().getStagingCfg()->getCfgOption()->get(DHCP6_OPTION_SPACE, 38);
  2218. ASSERT_TRUE(desc1.option_);
  2219. EXPECT_EQ(38, desc1.option_->getType());
  2220. // Try to get the option from the space isc.
  2221. OptionDescriptor desc2 =
  2222. CfgMgr::instance().getStagingCfg()->getCfgOption()->get("isc", 38);
  2223. ASSERT_TRUE(desc2.option_);
  2224. EXPECT_EQ(38, desc1.option_->getType());
  2225. // Try to get the non-existing option from the non-existing
  2226. // option space and expect that option is not returned.
  2227. OptionDescriptor desc3 = CfgMgr::instance().getStagingCfg()->
  2228. getCfgOption()->get("non-existing", 38);
  2229. ASSERT_FALSE(desc3.option_);
  2230. }
  2231. // The goal of this test is to verify that it is possible to
  2232. // encapsulate option space containing some options with
  2233. // another option. In this test we create base option that
  2234. // encapsulates option space 'isc' that comprises two other
  2235. // options. Also, for all options their definitions are
  2236. // created.
  2237. TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
  2238. // @todo DHCP configurations has many dependencies between
  2239. // parameters. First of all, configuration for subnet is
  2240. // inherited from the global values. Thus subnet has to be
  2241. // configured when all global values have been configured.
  2242. // Also, an option can encapsulate another option only
  2243. // if the latter has been configured. For this reason in this
  2244. // test we created two-stage configuration where first we
  2245. // created options that belong to encapsulated option space.
  2246. // In the second stage we add the base option. Also, the Subnet
  2247. // object is configured in the second stage so it is created
  2248. // at the very end (when all other parameters are configured).
  2249. // Starting stage 1. Configure sub-options and their definitions.
  2250. string config = "{ " + genIfaceConfig() + ","
  2251. "\"preferred-lifetime\": 3000,"
  2252. "\"valid-lifetime\": 4000,"
  2253. "\"rebind-timer\": 2000,"
  2254. "\"renew-timer\": 1000,"
  2255. "\"option-data\": [ {"
  2256. " \"name\": \"foo\","
  2257. " \"space\": \"isc\","
  2258. " \"data\": \"1234\""
  2259. " },"
  2260. " {"
  2261. " \"name\": \"foo2\","
  2262. " \"space\": \"isc\","
  2263. " \"data\": \"192.168.2.1\""
  2264. " } ],"
  2265. "\"option-def\": [ {"
  2266. " \"name\": \"foo\","
  2267. " \"code\": 110,"
  2268. " \"type\": \"uint32\","
  2269. " \"space\": \"isc\""
  2270. " },"
  2271. " {"
  2272. " \"name\": \"foo2\","
  2273. " \"code\": 111,"
  2274. " \"type\": \"ipv4-address\","
  2275. " \"space\": \"isc\""
  2276. " } ]"
  2277. "}";
  2278. ConstElementPtr status;
  2279. ElementPtr json = Element::fromJSON(config);
  2280. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  2281. ASSERT_TRUE(status);
  2282. checkResult(status, 0);
  2283. CfgMgr::instance().clear();
  2284. // Stage 2. Configure base option and a subnet. Please note that
  2285. // the configuration from the stage 2 is repeated because BIND
  2286. // configuration manager sends whole configuration for the lists
  2287. // where at least one element is being modified or added.
  2288. config = "{ " + genIfaceConfig() + ","
  2289. "\"preferred-lifetime\": 3000,"
  2290. "\"valid-lifetime\": 4000,"
  2291. "\"rebind-timer\": 2000,"
  2292. "\"renew-timer\": 1000,"
  2293. "\"option-data\": [ {"
  2294. " \"name\": \"base-option\","
  2295. " \"data\": \"11\""
  2296. " },"
  2297. " {"
  2298. " \"name\": \"foo\","
  2299. " \"space\": \"isc\","
  2300. " \"data\": \"1234\""
  2301. " },"
  2302. " {"
  2303. " \"name\": \"foo2\","
  2304. " \"space\": \"isc\","
  2305. " \"data\": \"192.168.2.1\""
  2306. " } ],"
  2307. "\"option-def\": [ {"
  2308. " \"name\": \"base-option\","
  2309. " \"code\": 100,"
  2310. " \"type\": \"uint8\","
  2311. " \"space\": \"dhcp6\","
  2312. " \"encapsulate\": \"isc\""
  2313. "},"
  2314. "{"
  2315. " \"name\": \"foo\","
  2316. " \"code\": 110,"
  2317. " \"type\": \"uint32\","
  2318. " \"space\": \"isc\""
  2319. " },"
  2320. " {"
  2321. " \"name\": \"foo2\","
  2322. " \"code\": 111,"
  2323. " \"type\": \"ipv4-address\","
  2324. " \"space\": \"isc\""
  2325. " } ],"
  2326. "\"subnet6\": [ { "
  2327. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  2328. " \"subnet\": \"2001:db8:1::/64\""
  2329. " } ]"
  2330. "}";
  2331. json = Element::fromJSON(config);
  2332. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  2333. ASSERT_TRUE(status);
  2334. checkResult(status, 0);
  2335. // We should have one option available.
  2336. OptionContainerPtr options =
  2337. CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
  2338. ASSERT_TRUE(options);
  2339. ASSERT_EQ(1, options->size());
  2340. // Get the option.
  2341. OptionDescriptor desc =
  2342. CfgMgr::instance().getStagingCfg()->getCfgOption()->get(DHCP6_OPTION_SPACE, 100);
  2343. EXPECT_TRUE(desc.option_);
  2344. EXPECT_EQ(100, desc.option_->getType());
  2345. // This opton should comprise two sub-options.
  2346. // Onf of them is 'foo' with code 110.
  2347. OptionPtr option_foo = desc.option_->getOption(110);
  2348. ASSERT_TRUE(option_foo);
  2349. EXPECT_EQ(110, option_foo->getType());
  2350. // ...another one 'foo2' with code 111.
  2351. OptionPtr option_foo2 = desc.option_->getOption(111);
  2352. ASSERT_TRUE(option_foo2);
  2353. EXPECT_EQ(111, option_foo2->getType());
  2354. }
  2355. // Goal of this test is to verify options configuration
  2356. // for multiple subnets.
  2357. TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
  2358. ConstElementPtr x;
  2359. string config = "{ " + genIfaceConfig() + ","
  2360. "\"preferred-lifetime\": 3000,"
  2361. "\"rebind-timer\": 2000, "
  2362. "\"renew-timer\": 1000, "
  2363. "\"subnet6\": [ { "
  2364. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  2365. " \"subnet\": \"2001:db8:1::/64\", "
  2366. " \"option-data\": [ {"
  2367. " \"name\": \"subscriber-id\","
  2368. " \"data\": \"0102030405060708090A\","
  2369. " \"csv-format\": False"
  2370. " } ]"
  2371. " },"
  2372. " {"
  2373. " \"pools\": [ { \"pool\": \"2001:db8:2::/80\" } ],"
  2374. " \"subnet\": \"2001:db8:2::/64\", "
  2375. " \"option-data\": [ {"
  2376. " \"name\": \"user-class\","
  2377. " \"data\": \"FFFEFDFCFB\","
  2378. " \"csv-format\": False"
  2379. " } ]"
  2380. " } ],"
  2381. "\"valid-lifetime\": 4000 }";
  2382. ElementPtr json = Element::fromJSON(config);
  2383. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  2384. checkResult(x, 0);
  2385. Subnet6Ptr subnet1 = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  2386. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  2387. ASSERT_TRUE(subnet1);
  2388. OptionContainerPtr options1 = subnet1->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
  2389. ASSERT_EQ(1, options1->size());
  2390. // Get the search index. Index #1 is to search using option code.
  2391. const OptionContainerTypeIndex& idx1 = options1->get<1>();
  2392. // Get the options for specified index. Expecting one option to be
  2393. // returned but in theory we may have multiple options with the same
  2394. // code so we get the range.
  2395. std::pair<OptionContainerTypeIndex::const_iterator,
  2396. OptionContainerTypeIndex::const_iterator> range1 =
  2397. idx1.equal_range(D6O_SUBSCRIBER_ID);
  2398. // Expect single option with the code equal to 38.
  2399. ASSERT_EQ(1, std::distance(range1.first, range1.second));
  2400. const uint8_t subid_expected[] = {
  2401. 0x01, 0x02, 0x03, 0x04, 0x05,
  2402. 0x06, 0x07, 0x08, 0x09, 0x0A
  2403. };
  2404. // Check if option is valid in terms of code and carried data.
  2405. testOption(*range1.first, D6O_SUBSCRIBER_ID, subid_expected,
  2406. sizeof(subid_expected));
  2407. // Test another subnet in the same way.
  2408. Subnet6Ptr subnet2 = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  2409. selectSubnet(IOAddress("2001:db8:2::4"), classify_);
  2410. ASSERT_TRUE(subnet2);
  2411. OptionContainerPtr options2 = subnet2->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
  2412. ASSERT_EQ(1, options2->size());
  2413. const OptionContainerTypeIndex& idx2 = options2->get<1>();
  2414. std::pair<OptionContainerTypeIndex::const_iterator,
  2415. OptionContainerTypeIndex::const_iterator> range2 =
  2416. idx2.equal_range(D6O_USER_CLASS);
  2417. ASSERT_EQ(1, std::distance(range2.first, range2.second));
  2418. const uint8_t user_class_expected[] = {
  2419. 0xFF, 0xFE, 0xFD, 0xFC, 0xFB
  2420. };
  2421. testOption(*range2.first, D6O_USER_CLASS, user_class_expected,
  2422. sizeof(user_class_expected));
  2423. }
  2424. // This test verifies that it is possible to specify options on
  2425. // pool levels.
  2426. TEST_F(Dhcp6ParserTest, optionDataMultiplePools) {
  2427. ConstElementPtr x;
  2428. string config = "{ " + genIfaceConfig() + ","
  2429. "\"preferred-lifetime\": 3000,"
  2430. "\"rebind-timer\": 2000, "
  2431. "\"renew-timer\": 1000, "
  2432. "\"subnet6\": [ { "
  2433. " \"pools\": [ { "
  2434. " \"pool\": \"2001:db8:1::10 - 2001:db8:1::100\","
  2435. " \"option-data\": [ {"
  2436. " \"name\": \"subscriber-id\","
  2437. " \"data\": \"0102030405060708090A\","
  2438. " \"csv-format\": False"
  2439. " } ]"
  2440. " },"
  2441. " {"
  2442. " \"pool\": \"2001:db8:1::300 - 2001:db8:1::400\","
  2443. " \"option-data\": [ {"
  2444. " \"name\": \"user-class\","
  2445. " \"data\": \"FFFEFDFCFB\","
  2446. " \"csv-format\": False"
  2447. " } ]"
  2448. " } ],"
  2449. " \"pd-pools\": [ { "
  2450. " \"prefix\": \"3000::\","
  2451. " \"prefix-len\": 48,"
  2452. " \"delegated-len\": 64,"
  2453. " \"option-data\": [ {"
  2454. " \"name\": \"subscriber-id\","
  2455. " \"data\": \"112233445566\","
  2456. " \"csv-format\": False"
  2457. " } ]"
  2458. " },"
  2459. " {"
  2460. " \"prefix\": \"3001::\","
  2461. " \"prefix-len\": 48,"
  2462. " \"delegated-len\": 64,"
  2463. " \"option-data\": [ {"
  2464. " \"name\": \"user-class\","
  2465. " \"data\": \"aabbccddee\","
  2466. " \"csv-format\": False"
  2467. " } ]"
  2468. " } ],"
  2469. " \"subnet\": \"2001:db8:1::/64\""
  2470. " } ],"
  2471. "\"valid-lifetime\": 4000 }";
  2472. ElementPtr json = Element::fromJSON(config);
  2473. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  2474. checkResult(x, 0);
  2475. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  2476. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  2477. ASSERT_TRUE(subnet);
  2478. PoolPtr pool = subnet->getPool(Lease::TYPE_PD, IOAddress("3000::"), false);
  2479. ASSERT_TRUE(pool);
  2480. Pool6Ptr pool6 = boost::dynamic_pointer_cast<Pool6>(pool);
  2481. ASSERT_TRUE(pool6);
  2482. OptionContainerPtr options1 = pool6->getCfgOption()->getAll("dhcp6");
  2483. ASSERT_EQ(1, options1->size());
  2484. // Get the search index. Index #1 is to search using option code.
  2485. const OptionContainerTypeIndex& idx1 = options1->get<1>();
  2486. // Get the options for specified index. Expecting one option to be
  2487. // returned but in theory we may have multiple options with the same
  2488. // code so we get the range.
  2489. std::pair<OptionContainerTypeIndex::const_iterator,
  2490. OptionContainerTypeIndex::const_iterator> range1 =
  2491. idx1.equal_range(D6O_SUBSCRIBER_ID);
  2492. // Expect a single Subscriber ID option instance.
  2493. ASSERT_EQ(1, std::distance(range1.first, range1.second));
  2494. const uint8_t subscriber_id_expected[] = {
  2495. 0x11, 0x22, 0x33, 0x44, 0x55, 0x66
  2496. };
  2497. // Check if option is valid in terms of code and carried data.
  2498. testOption(*range1.first, D6O_SUBSCRIBER_ID, subscriber_id_expected,
  2499. sizeof(subscriber_id_expected));
  2500. // Test another pool in the same way.
  2501. pool = subnet->getPool(Lease::TYPE_PD, IOAddress("3001::"), false);
  2502. ASSERT_TRUE(pool);
  2503. pool6 = boost::dynamic_pointer_cast<Pool6>(pool);
  2504. ASSERT_TRUE(pool6);
  2505. OptionContainerPtr options2 = pool6->getCfgOption()->getAll("dhcp6");
  2506. ASSERT_EQ(1, options2->size());
  2507. const OptionContainerTypeIndex& idx2 = options2->get<1>();
  2508. std::pair<OptionContainerTypeIndex::const_iterator,
  2509. OptionContainerTypeIndex::const_iterator> range2 =
  2510. idx2.equal_range(D6O_USER_CLASS);
  2511. ASSERT_EQ(1, std::distance(range2.first, range2.second));
  2512. const uint8_t user_class_expected[] = {
  2513. 0xAA, 0xBB, 0xCC, 0xDD, 0xEE
  2514. };
  2515. testOption(*range2.first, D6O_USER_CLASS, user_class_expected,
  2516. sizeof(user_class_expected));
  2517. // Test options in NA pools.
  2518. pool = subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8:1::10"));
  2519. ASSERT_TRUE(pool);
  2520. pool6 = boost::dynamic_pointer_cast<Pool6>(pool);
  2521. ASSERT_TRUE(pool6);
  2522. OptionContainerPtr options3 = pool6->getCfgOption()->getAll("dhcp6");
  2523. ASSERT_EQ(1, options3->size());
  2524. const OptionContainerTypeIndex& idx3 = options3->get<1>();
  2525. std::pair<OptionContainerTypeIndex::const_iterator,
  2526. OptionContainerTypeIndex::const_iterator> range3 =
  2527. idx3.equal_range(D6O_SUBSCRIBER_ID);
  2528. ASSERT_EQ(1, std::distance(range3.first, range3.second));
  2529. const uint8_t subscriber_id_expected2[] = {
  2530. 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A
  2531. };
  2532. testOption(*range3.first, D6O_SUBSCRIBER_ID, subscriber_id_expected2,
  2533. sizeof(subscriber_id_expected2));
  2534. pool = subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8:1::300"));
  2535. ASSERT_TRUE(pool);
  2536. pool6 = boost::dynamic_pointer_cast<Pool6>(pool);
  2537. ASSERT_TRUE(pool6);
  2538. OptionContainerPtr options4 = pool6->getCfgOption()->getAll("dhcp6");
  2539. ASSERT_EQ(1, options4->size());
  2540. const OptionContainerTypeIndex& idx4 = options4->get<1>();
  2541. std::pair<OptionContainerTypeIndex::const_iterator,
  2542. OptionContainerTypeIndex::const_iterator> range4 =
  2543. idx4.equal_range(D6O_USER_CLASS);
  2544. ASSERT_EQ(1, std::distance(range4.first, range4.second));
  2545. const uint8_t user_class_expected2[] = {
  2546. 0xFF, 0xFE, 0xFD, 0xFC, 0xFB
  2547. };
  2548. testOption(*range4.first, D6O_USER_CLASS, user_class_expected2,
  2549. sizeof(user_class_expected2));
  2550. }
  2551. // The goal of this test is to check that the option carrying a boolean
  2552. // value can be configured using one of the values: "true", "false", "0"
  2553. // or "1".
  2554. TEST_F(Dhcp6ParserTest, optionDataBoolean) {
  2555. // Create configuration. Use standard option 1000.
  2556. std::map<std::string, std::string> params;
  2557. params["name"] = "bool-option";
  2558. params["space"] = DHCP6_OPTION_SPACE;
  2559. params["code"] = "1000";
  2560. params["data"] = "true";
  2561. params["csv-format"] = "true";
  2562. std::string config = createConfigWithOption(params);
  2563. ASSERT_TRUE(executeConfiguration(config, "parse configuration with a"
  2564. " boolean value"));
  2565. // The subnet should now hold one option with the code 1000.
  2566. OptionDescriptor desc =
  2567. getOptionFromSubnet(IOAddress("2001:db8:1::5"), 1000);
  2568. ASSERT_TRUE(desc.option_);
  2569. // This option should be set to "true", represented as 0x1 in the option
  2570. // buffer.
  2571. uint8_t expected_option_data[] = {
  2572. 0x1
  2573. };
  2574. testConfiguration(params, 1000, expected_option_data,
  2575. sizeof(expected_option_data));
  2576. // Configure the option with the "1" value. This should have the same
  2577. // effect as if "true" was specified.
  2578. params["data"] = "1";
  2579. testConfiguration(params, 1000, expected_option_data,
  2580. sizeof(expected_option_data));
  2581. // The value of "1" with a few leading zeros should work too.
  2582. params["data"] = "00001";
  2583. testConfiguration(params, 1000, expected_option_data,
  2584. sizeof(expected_option_data));
  2585. // Configure the option with the "false" value.
  2586. params["data"] = "false";
  2587. // The option buffer should now hold the value of 0.
  2588. expected_option_data[0] = 0;
  2589. testConfiguration(params, 1000, expected_option_data,
  2590. sizeof(expected_option_data));
  2591. // Specifying "0" should have the same effect as "false".
  2592. params["data"] = "0";
  2593. testConfiguration(params, 1000, expected_option_data,
  2594. sizeof(expected_option_data));
  2595. // The same effect should be for multiple 0 chars.
  2596. params["data"] = "00000";
  2597. testConfiguration(params, 1000, expected_option_data,
  2598. sizeof(expected_option_data));
  2599. // Bogus values should not be accepted.
  2600. params["data"] = "bugus";
  2601. testInvalidOptionParam(params);
  2602. params["data"] = "2";
  2603. testInvalidOptionParam(params);
  2604. // Now let's test that it is possible to use binary format.
  2605. params["data"] = "0";
  2606. params["csv-format"] = "false";
  2607. testConfiguration(params, 1000, expected_option_data,
  2608. sizeof(expected_option_data));
  2609. // The binary 1 should work as well.
  2610. params["data"] = "1";
  2611. expected_option_data[0] = 1;
  2612. testConfiguration(params, 1000, expected_option_data,
  2613. sizeof(expected_option_data));
  2614. // As well as an even number of digits.
  2615. params["data"] = "01";
  2616. testConfiguration(params, 1000, expected_option_data,
  2617. sizeof(expected_option_data));
  2618. }
  2619. // Verify that empty option name is rejected in the configuration.
  2620. TEST_F(Dhcp6ParserTest, optionNameEmpty) {
  2621. // Empty option names not allowed.
  2622. testInvalidOptionParam("", "name");
  2623. }
  2624. // Verify that empty option name with spaces is rejected
  2625. // in the configuration.
  2626. TEST_F(Dhcp6ParserTest, optionNameSpaces) {
  2627. // Spaces in option names not allowed.
  2628. testInvalidOptionParam("option foo", "name");
  2629. }
  2630. // Verify that negative option code is rejected in the configuration.
  2631. TEST_F(Dhcp6ParserTest, optionCodeNegative) {
  2632. // Check negative option code -4. This should fail too.
  2633. testInvalidOptionParam("-4", "code");
  2634. }
  2635. // Verify that out of bounds option code is rejected in the configuration.
  2636. TEST_F(Dhcp6ParserTest, optionCodeNonUint16) {
  2637. // The valid option codes are uint16_t values so passing
  2638. // uint16_t maximum value incremented by 1 should result
  2639. // in failure.
  2640. testInvalidOptionParam("65536", "code");
  2641. }
  2642. // Verify that out of bounds option code is rejected in the configuration.
  2643. TEST_F(Dhcp6ParserTest, optionCodeHighNonUint16) {
  2644. // Another check for uint16_t overflow but this time
  2645. // let's pass even greater option code value.
  2646. testInvalidOptionParam("70000", "code");
  2647. }
  2648. // Verify that zero option code is rejected in the configuration.
  2649. TEST_F(Dhcp6ParserTest, optionCodeZero) {
  2650. // Option code 0 is reserved and should not be accepted
  2651. // by configuration parser.
  2652. testInvalidOptionParam("0", "code");
  2653. }
  2654. // Verify that option data which contains non hexadecimal characters
  2655. // is rejected by the configuration.
  2656. TEST_F(Dhcp6ParserTest, optionDataInvalidChar) {
  2657. // Option code 0 is reserved and should not be accepted
  2658. // by configuration parser.
  2659. testInvalidOptionParam("01020R", "data");
  2660. }
  2661. // Verify that option data containing '0x' prefix is rejected
  2662. // by the configuration.
  2663. TEST_F(Dhcp6ParserTest, optionDataUnexpectedPrefix) {
  2664. // Option code 0 is reserved and should not be accepted
  2665. // by configuration parser.
  2666. testInvalidOptionParam("0x0102", "data");
  2667. }
  2668. // Verify that either lower or upper case characters are allowed
  2669. // to specify the option data.
  2670. TEST_F(Dhcp6ParserTest, optionDataLowerCase) {
  2671. ConstElementPtr x;
  2672. std::string config = createConfigWithOption("0a0b0C0D", "data");
  2673. ElementPtr json = Element::fromJSON(config);
  2674. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  2675. checkResult(x, 0);
  2676. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  2677. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  2678. ASSERT_TRUE(subnet);
  2679. OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
  2680. ASSERT_EQ(1, options->size());
  2681. // Get the search index. Index #1 is to search using option code.
  2682. const OptionContainerTypeIndex& idx = options->get<1>();
  2683. // Get the options for specified index. Expecting one option to be
  2684. // returned but in theory we may have multiple options with the same
  2685. // code so we get the range.
  2686. std::pair<OptionContainerTypeIndex::const_iterator,
  2687. OptionContainerTypeIndex::const_iterator> range =
  2688. idx.equal_range(D6O_SUBSCRIBER_ID);
  2689. // Expect single option with the code equal to 38.
  2690. ASSERT_EQ(1, std::distance(range.first, range.second));
  2691. const uint8_t subid_expected[] = {
  2692. 0x0A, 0x0B, 0x0C, 0x0D
  2693. };
  2694. // Check if option is valid in terms of code and carried data.
  2695. testOption(*range.first, D6O_SUBSCRIBER_ID, subid_expected,
  2696. sizeof(subid_expected));
  2697. }
  2698. // Verify that specific option object is returned for standard
  2699. // option which has dedicated option class derived from Option.
  2700. TEST_F(Dhcp6ParserTest, stdOptionData) {
  2701. ConstElementPtr x;
  2702. std::map<std::string, std::string> params;
  2703. params["name"] = "ia-na";
  2704. params["space"] = DHCP6_OPTION_SPACE;
  2705. // Option code 3 means OPTION_IA_NA.
  2706. params["code"] = "3";
  2707. params["data"] = "12345, 6789, 1516";
  2708. params["csv-format"] = "True";
  2709. std::string config = createConfigWithOption(params);
  2710. ElementPtr json = Element::fromJSON(config);
  2711. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  2712. checkResult(x, 0);
  2713. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  2714. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  2715. ASSERT_TRUE(subnet);
  2716. OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
  2717. ASSERT_EQ(1, options->size());
  2718. // Get the search index. Index #1 is to search using option code.
  2719. const OptionContainerTypeIndex& idx = options->get<1>();
  2720. // Get the options for specified index. Expecting one option to be
  2721. // returned but in theory we may have multiple options with the same
  2722. // code so we get the range.
  2723. std::pair<OptionContainerTypeIndex::const_iterator,
  2724. OptionContainerTypeIndex::const_iterator> range =
  2725. idx.equal_range(D6O_IA_NA);
  2726. // Expect single option with the code equal to IA_NA option code.
  2727. ASSERT_EQ(1, std::distance(range.first, range.second));
  2728. // The actual pointer to the option is held in the option field
  2729. // in the structure returned.
  2730. OptionPtr option = range.first->option_;
  2731. ASSERT_TRUE(option);
  2732. // Option object returned for here is expected to be Option6IA
  2733. // which is derived from Option. This class is dedicated to
  2734. // represent standard option IA_NA.
  2735. boost::shared_ptr<Option6IA> optionIA =
  2736. boost::dynamic_pointer_cast<Option6IA>(option);
  2737. // If cast is unsuccessful than option returned was of a
  2738. // different type than Option6IA. This is wrong.
  2739. ASSERT_TRUE(optionIA);
  2740. // If cast was successful we may use accessors exposed by
  2741. // Option6IA to validate that the content of this option
  2742. // has been set correctly.
  2743. EXPECT_EQ(12345, optionIA->getIAID());
  2744. EXPECT_EQ(6789, optionIA->getT1());
  2745. EXPECT_EQ(1516, optionIA->getT2());
  2746. }
  2747. // This test checks if vendor options can be specified in the config file
  2748. // (in hex format), and later retrieved from configured subnet
  2749. TEST_F(Dhcp6ParserTest, vendorOptionsHex) {
  2750. // This configuration string is to configure two options
  2751. // sharing the code 1 and belonging to the different vendor spaces.
  2752. // (different vendor-id values).
  2753. string config = "{ " + genIfaceConfig() + ","
  2754. "\"preferred-lifetime\": 3000,"
  2755. "\"valid-lifetime\": 4000,"
  2756. "\"rebind-timer\": 2000,"
  2757. "\"renew-timer\": 1000,"
  2758. "\"option-data\": [ {"
  2759. " \"name\": \"option-one\","
  2760. " \"space\": \"vendor-4491\","
  2761. " \"code\": 100,"
  2762. " \"data\": \"ABCDEF0105\","
  2763. " \"csv-format\": False"
  2764. " },"
  2765. " {"
  2766. " \"name\": \"option-two\","
  2767. " \"space\": \"vendor-1234\","
  2768. " \"code\": 100,"
  2769. " \"data\": \"1234\","
  2770. " \"csv-format\": False"
  2771. " } ],"
  2772. "\"subnet6\": [ { "
  2773. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  2774. " \"subnet\": \"2001:db8:1::/64\""
  2775. " } ]"
  2776. "}";
  2777. ConstElementPtr status;
  2778. ElementPtr json = Element::fromJSON(config);
  2779. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  2780. ASSERT_TRUE(status);
  2781. checkResult(status, 0);
  2782. // Options should be now available
  2783. // Try to get the option from the vendor space 4491
  2784. OptionDescriptor desc1 =
  2785. CfgMgr::instance().getStagingCfg()->getCfgOption()->get(4491, 100);
  2786. ASSERT_TRUE(desc1.option_);
  2787. EXPECT_EQ(100, desc1.option_->getType());
  2788. // Try to get the option from the vendor space 1234
  2789. OptionDescriptor desc2 =
  2790. CfgMgr::instance().getStagingCfg()->getCfgOption()->get(1234, 100);
  2791. ASSERT_TRUE(desc2.option_);
  2792. EXPECT_EQ(100, desc1.option_->getType());
  2793. // Try to get the non-existing option from the non-existing
  2794. // option space and expect that option is not returned.
  2795. OptionDescriptor desc3 =
  2796. CfgMgr::instance().getStagingCfg()->getCfgOption()->get(5678, 38);
  2797. ASSERT_FALSE(desc3.option_);
  2798. }
  2799. // This test checks if vendor options can be specified in the config file,
  2800. // (in csv format), and later retrieved from configured subnet
  2801. TEST_F(Dhcp6ParserTest, vendorOptionsCsv) {
  2802. // This configuration string is to configure two options
  2803. // sharing the code 1 and belonging to the different vendor spaces.
  2804. // (different vendor-id values).
  2805. string config = "{ " + genIfaceConfig() + ","
  2806. "\"preferred-lifetime\": 3000,"
  2807. "\"valid-lifetime\": 4000,"
  2808. "\"rebind-timer\": 2000,"
  2809. "\"renew-timer\": 1000,"
  2810. "\"option-data\": [ {"
  2811. " \"name\": \"foo\","
  2812. " \"space\": \"vendor-4491\","
  2813. " \"code\": 100,"
  2814. " \"data\": \"this is a string vendor-opt\""
  2815. " } ],"
  2816. "\"option-def\": [ {"
  2817. " \"name\": \"foo\","
  2818. " \"code\": 100,"
  2819. " \"type\": \"string\","
  2820. " \"space\": \"vendor-4491\""
  2821. " } ],"
  2822. "\"subnet6\": [ { "
  2823. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  2824. " \"subnet\": \"2001:db8:1::/64\""
  2825. " } ]"
  2826. "}";
  2827. ConstElementPtr status;
  2828. ElementPtr json = Element::fromJSON(config);
  2829. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  2830. ASSERT_TRUE(status);
  2831. checkResult(status, 0);
  2832. // Options should be now available.
  2833. // Try to get the option from the vendor space 4491
  2834. OptionDescriptor desc1 =
  2835. CfgMgr::instance().getStagingCfg()->getCfgOption()->get(4491, 100);
  2836. ASSERT_TRUE(desc1.option_);
  2837. EXPECT_EQ(100, desc1.option_->getType());
  2838. // Try to get the non-existing option from the non-existing
  2839. // option space and expect that option is not returned.
  2840. OptionDescriptor desc2 =
  2841. CfgMgr::instance().getStagingCfg()->getCfgOption()->get(5678, 100);
  2842. ASSERT_FALSE(desc2.option_);
  2843. }
  2844. /// @todo add tests similar to vendorOptionsCsv and vendorOptionsHex, but for
  2845. /// vendor options defined in a subnet.
  2846. // The goal of this test is to verify that the standard option can
  2847. // be configured to encapsulate multiple other options.
  2848. /// @todo This test is currently disabled because it relies on the option
  2849. /// 17 which is treated differently than all other options. There are no
  2850. /// other standard options used by Kea which would encapsulate other
  2851. /// options and for which values could be configured here.
  2852. TEST_F(Dhcp6ParserTest, DISABLED_stdOptionDataEncapsulate) {
  2853. // The configuration is two stage process in this test.
  2854. // In the first stage we create definitions of suboptions
  2855. // that we will add to the base option.
  2856. // Let's create some dummy options: foo and foo2.
  2857. string config = "{ " + genIfaceConfig() + ","
  2858. "\"preferred-lifetime\": 3000,"
  2859. "\"valid-lifetime\": 4000,"
  2860. "\"rebind-timer\": 2000,"
  2861. "\"renew-timer\": 1000,"
  2862. "\"option-data\": [ {"
  2863. " \"name\": \"foo\","
  2864. " \"space\": \"vendor-opts-space\","
  2865. " \"data\": \"1234\""
  2866. " },"
  2867. " {"
  2868. " \"name\": \"foo2\","
  2869. " \"space\": \"vendor-opts-space\","
  2870. " \"data\": \"192.168.2.1\""
  2871. " } ],"
  2872. "\"option-def\": [ {"
  2873. " \"name\": \"foo\","
  2874. " \"code\": 110,"
  2875. " \"type\": \"uint32\","
  2876. " \"space\": \"vendor-opts-space\""
  2877. " },"
  2878. " {"
  2879. " \"name\": \"foo2\","
  2880. " \"code\": 111,"
  2881. " \"type\": \"ipv4-address\","
  2882. " \"space\": \"vendor-opts-space\""
  2883. " } ]"
  2884. "}";
  2885. ConstElementPtr status;
  2886. ElementPtr json = Element::fromJSON(config);
  2887. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  2888. ASSERT_TRUE(status);
  2889. checkResult(status, 0);
  2890. CfgMgr::instance().clear();
  2891. // Once the definitions have been added we can configure the
  2892. // standard option #17. This option comprises an enterprise
  2893. // number and sub options. By convention (introduced in
  2894. // std_option_defs.h) option named 'vendor-opts'
  2895. // encapsulates the option space named 'vendor-opts-space'.
  2896. // We add our dummy options to this option space and thus
  2897. // they should be included as sub-options in the 'vendor-opts'
  2898. // option.
  2899. config = "{ " + genIfaceConfig() + ","
  2900. "\"rebind-timer\": 2000,"
  2901. "\"renew-timer\": 1000,"
  2902. "\"option-data\": [ {"
  2903. " \"name\": \"vendor-opts\","
  2904. " \"data\": \"1234\""
  2905. " },"
  2906. " {"
  2907. " \"name\": \"foo\","
  2908. " \"space\": \"vendor-opts-space\","
  2909. " \"data\": \"1234\""
  2910. " },"
  2911. " {"
  2912. " \"name\": \"foo2\","
  2913. " \"space\": \"vendor-opts-space\","
  2914. " \"data\": \"192.168.2.1\""
  2915. " } ],"
  2916. "\"option-def\": [ {"
  2917. " \"name\": \"foo\","
  2918. " \"code\": 110,"
  2919. " \"type\": \"uint32\","
  2920. " \"space\": \"vendor-opts-space\""
  2921. " },"
  2922. " {"
  2923. " \"name\": \"foo2\","
  2924. " \"code\": 111,"
  2925. " \"type\": \"ipv4-address\","
  2926. " \"space\": \"vendor-opts-space\""
  2927. " } ],"
  2928. "\"subnet6\": [ { "
  2929. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  2930. " \"subnet\": \"2001:db8:1::/64\""
  2931. " } ]"
  2932. "}";
  2933. json = Element::fromJSON(config);
  2934. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  2935. ASSERT_TRUE(status);
  2936. checkResult(status, 0);
  2937. // Get the subnet.
  2938. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  2939. selectSubnet(IOAddress("2001:db8:1::5"), classify_);
  2940. ASSERT_TRUE(subnet);
  2941. // We should have one option available.
  2942. OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
  2943. ASSERT_TRUE(options);
  2944. ASSERT_EQ(1, options->size());
  2945. // Get the option.
  2946. OptionDescriptor desc = subnet->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_VENDOR_OPTS);
  2947. EXPECT_TRUE(desc.option_);
  2948. EXPECT_EQ(D6O_VENDOR_OPTS, desc.option_->getType());
  2949. // Option with the code 110 should be added as a sub-option.
  2950. OptionPtr option_foo = desc.option_->getOption(110);
  2951. ASSERT_TRUE(option_foo);
  2952. EXPECT_EQ(110, option_foo->getType());
  2953. // This option comprises a single uint32_t value thus it is
  2954. // represented by OptionInt<uint32_t> class. Let's get the
  2955. // object of this type.
  2956. boost::shared_ptr<OptionInt<uint32_t> > option_foo_uint32 =
  2957. boost::dynamic_pointer_cast<OptionInt<uint32_t> >(option_foo);
  2958. ASSERT_TRUE(option_foo_uint32);
  2959. // Validate the value according to the configuration.
  2960. EXPECT_EQ(1234, option_foo_uint32->getValue());
  2961. // Option with the code 111 should be added as a sub-option.
  2962. OptionPtr option_foo2 = desc.option_->getOption(111);
  2963. ASSERT_TRUE(option_foo2);
  2964. EXPECT_EQ(111, option_foo2->getType());
  2965. // This option comprises the IPV4 address. Such option is
  2966. // represented by OptionCustom object.
  2967. OptionCustomPtr option_foo2_v4 =
  2968. boost::dynamic_pointer_cast<OptionCustom>(option_foo2);
  2969. ASSERT_TRUE(option_foo2_v4);
  2970. // Get the IP address carried by this option and validate it.
  2971. EXPECT_EQ("192.168.2.1", option_foo2_v4->readAddress().toText());
  2972. // Option with the code 112 should not be added.
  2973. EXPECT_FALSE(desc.option_->getOption(112));
  2974. }
  2975. // Tests of the hooks libraries configuration. All tests have the pre-
  2976. // condition (checked in the test fixture's SetUp() method) that no hooks
  2977. // libraries are loaded at the start of the tests.
  2978. // Helper function to return a configuration containing an arbitrary number
  2979. // of hooks libraries.
  2980. std::string
  2981. buildHooksLibrariesConfig(const std::vector<std::string>& libraries) {
  2982. const string lbrace("{");
  2983. const string rbrace("}");
  2984. const string liblabel("\"library\": ");
  2985. const string quote("\"");
  2986. // Create the first part of the configuration string.
  2987. string config =
  2988. "{ \"interfaces-config\": { },"
  2989. "\"hooks-libraries\": [";
  2990. // Append the libraries (separated by commas if needed)
  2991. for (unsigned int i = 0; i < libraries.size(); ++i) {
  2992. if (i > 0) {
  2993. config += string(", ");
  2994. }
  2995. config += (lbrace + liblabel + quote + libraries[i] + quote + rbrace);
  2996. }
  2997. // Append the remainder of the configuration.
  2998. config += string(
  2999. "],"
  3000. "\"rebind-timer\": 2000,"
  3001. "\"renew-timer\": 1000,"
  3002. "\"option-data\": [ {"
  3003. " \"name\": \"foo\","
  3004. " \"space\": \"vendor-opts-space\","
  3005. " \"data\": \"1234\""
  3006. " },"
  3007. " {"
  3008. " \"name\": \"foo2\","
  3009. " \"space\": \"vendor-opts-space\","
  3010. " \"data\": \"192.168.2.1\""
  3011. " } ],"
  3012. "\"option-def\": [ {"
  3013. " \"name\": \"foo\","
  3014. " \"code\": 110,"
  3015. " \"type\": \"uint32\","
  3016. " \"space\": \"vendor-opts-space\""
  3017. " },"
  3018. " {"
  3019. " \"name\": \"foo2\","
  3020. " \"code\": 111,"
  3021. " \"type\": \"ipv4-address\","
  3022. " \"space\": \"vendor-opts-space\""
  3023. " } ]"
  3024. "}");
  3025. return (config);
  3026. }
  3027. // Convenience function for creating hooks library configuration with one or
  3028. // two character string constants.
  3029. std::string
  3030. buildHooksLibrariesConfig(const char* library1 = NULL,
  3031. const char* library2 = NULL) {
  3032. std::vector<std::string> libraries;
  3033. if (library1 != NULL) {
  3034. libraries.push_back(string(library1));
  3035. if (library2 != NULL) {
  3036. libraries.push_back(string(library2));
  3037. }
  3038. }
  3039. return (buildHooksLibrariesConfig(libraries));
  3040. }
  3041. // The goal of this test is to verify the configuration of hooks libraries if
  3042. // none are specified.
  3043. TEST_F(Dhcp6ParserTest, NoHooksLibraries) {
  3044. // Parse a configuration containing no names.
  3045. string config = buildHooksLibrariesConfig();
  3046. if (!executeConfiguration(config,
  3047. "set configuration with no hooks libraries")) {
  3048. FAIL() << "Unable to execute configuration";
  3049. } else {
  3050. // No libraries should be loaded at the end of the test.
  3051. std::vector<std::string> libraries = HooksManager::getLibraryNames();
  3052. EXPECT_TRUE(libraries.empty());
  3053. }
  3054. }
  3055. // Verify parsing fails with one library that will fail validation.
  3056. TEST_F(Dhcp6ParserTest, InvalidLibrary) {
  3057. // Parse a configuration containing a failing library.
  3058. string config = buildHooksLibrariesConfig(NOT_PRESENT_LIBRARY);
  3059. ConstElementPtr status;
  3060. ElementPtr json = Element::fromJSON(config);
  3061. ASSERT_NO_THROW(status = configureDhcp6Server(srv_, json));
  3062. // The status object must not be NULL
  3063. ASSERT_TRUE(status);
  3064. // Returned value should not be 0
  3065. comment_ = parseAnswer(rcode_, status);
  3066. EXPECT_NE(0, rcode_);
  3067. }
  3068. // Verify the configuration of hooks libraries with two being specified.
  3069. TEST_F(Dhcp6ParserTest, LibrariesSpecified) {
  3070. // Marker files should not be present.
  3071. EXPECT_FALSE(checkMarkerFileExists(LOAD_MARKER_FILE));
  3072. EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
  3073. // Set up the configuration with two libraries and load them.
  3074. string config = buildHooksLibrariesConfig(CALLOUT_LIBRARY_1,
  3075. CALLOUT_LIBRARY_2);
  3076. ASSERT_TRUE(executeConfiguration(config,
  3077. "load two valid libraries"));
  3078. // Expect two libraries to be loaded in the correct order (load marker file
  3079. // is present, no unload marker file).
  3080. std::vector<std::string> libraries = HooksManager::getLibraryNames();
  3081. ASSERT_EQ(2, libraries.size());
  3082. EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "12"));
  3083. EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
  3084. CfgMgr::instance().commit();
  3085. // Unload the libraries. The load file should not have changed, but
  3086. // the unload one should indicate the unload() functions have been run.
  3087. config = buildHooksLibrariesConfig();
  3088. ASSERT_TRUE(executeConfiguration(config, "unloading libraries"));
  3089. EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "12"));
  3090. EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "21"));
  3091. // Expect the hooks system to say that none are loaded.
  3092. libraries = HooksManager::getLibraryNames();
  3093. EXPECT_TRUE(libraries.empty());
  3094. }
  3095. // This test verifies that it is possible to select subset of interfaces on
  3096. // which server should listen.
  3097. TEST_F(Dhcp6ParserTest, selectedInterfaces) {
  3098. IfaceMgrTestConfig test_config(true);
  3099. // Make sure there is no garbage interface configuration in the CfgMgr.
  3100. ASSERT_FALSE(test_config.socketOpen("eth0", AF_INET6));
  3101. ASSERT_FALSE(test_config.socketOpen("eth1", AF_INET6));
  3102. ConstElementPtr status;
  3103. string config = "{ \"interfaces-config\": {"
  3104. " \"interfaces\": [ \"eth0\" ]"
  3105. "},"
  3106. "\"preferred-lifetime\": 3000,"
  3107. "\"rebind-timer\": 2000, "
  3108. "\"renew-timer\": 1000, "
  3109. "\"valid-lifetime\": 4000 }";
  3110. ElementPtr json = Element::fromJSON(config);
  3111. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  3112. // returned value must be 1 (values error)
  3113. // as the pool does not belong to that subnet
  3114. checkResult(status, 0);
  3115. CfgMgr::instance().getStagingCfg()->getCfgIface()->openSockets(AF_INET6, 10000);
  3116. // eth0 and eth1 were explicitly selected. eth2 was not.
  3117. EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET6));
  3118. EXPECT_FALSE(test_config.socketOpen("eth1", AF_INET6));
  3119. }
  3120. // This test verifies that it is possible to configure the server to listen on
  3121. // all interfaces.
  3122. TEST_F(Dhcp6ParserTest, allInterfaces) {
  3123. IfaceMgrTestConfig test_config(true);
  3124. ASSERT_FALSE(test_config.socketOpen("eth0", AF_INET6));
  3125. ASSERT_FALSE(test_config.socketOpen("eth1", AF_INET6));
  3126. ConstElementPtr status;
  3127. // This configuration specifies two interfaces on which server should listen
  3128. // but also includes '*'. This keyword switches server into the
  3129. // mode when it listens on all interfaces regardless of what interface names
  3130. // were specified in the "interfaces" parameter.
  3131. string config = "{ \"interfaces-config\": {"
  3132. " \"interfaces\": [ \"eth0\", \"eth1\", \"*\" ]"
  3133. "},"
  3134. "\"preferred-lifetime\": 3000,"
  3135. "\"rebind-timer\": 2000, "
  3136. "\"renew-timer\": 1000, "
  3137. "\"valid-lifetime\": 4000 }";
  3138. ElementPtr json = Element::fromJSON(config);
  3139. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  3140. checkResult(status, 0);
  3141. CfgMgr::instance().getStagingCfg()->getCfgIface()->openSockets(AF_INET6, 10000);
  3142. // All interfaces should be now active.
  3143. EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET6));
  3144. EXPECT_TRUE(test_config.socketOpen("eth1", AF_INET6));
  3145. }
  3146. // This test checks if it is possible to specify relay information
  3147. TEST_F(Dhcp6ParserTest, subnetRelayInfo) {
  3148. ConstElementPtr status;
  3149. // A config with relay information.
  3150. string config = "{ " + genIfaceConfig() + ","
  3151. "\"rebind-timer\": 2000, "
  3152. "\"renew-timer\": 1000, "
  3153. "\"subnet6\": [ { "
  3154. " \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
  3155. " \"relay\": { "
  3156. " \"ip-address\": \"2001:db8:1::abcd\""
  3157. " },"
  3158. " \"subnet\": \"2001:db8:1::/64\" } ],"
  3159. "\"preferred-lifetime\": 3000, "
  3160. "\"valid-lifetime\": 4000 }";
  3161. ElementPtr json = Element::fromJSON(config);
  3162. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  3163. // returned value should be 0 (configuration success)
  3164. checkResult(status, 0);
  3165. Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
  3166. selectSubnet(IOAddress("2001:db8:1::1"), classify_);
  3167. ASSERT_TRUE(subnet);
  3168. EXPECT_EQ("2001:db8:1::abcd", subnet->getRelayInfo().addr_.toText());
  3169. }
  3170. // Goal of this test is to verify that multiple subnets can be configured
  3171. // with defined client classes.
  3172. TEST_F(Dhcp6ParserTest, classifySubnets) {
  3173. ConstElementPtr x;
  3174. string config = "{ " + genIfaceConfig() + ","
  3175. "\"preferred-lifetime\": 3000,"
  3176. "\"rebind-timer\": 2000, "
  3177. "\"renew-timer\": 1000, "
  3178. "\"subnet6\": [ { "
  3179. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  3180. " \"subnet\": \"2001:db8:1::/64\", "
  3181. " \"client-class\": \"alpha\" "
  3182. " },"
  3183. " {"
  3184. " \"pools\": [ { \"pool\": \"2001:db8:2::/80\" } ],"
  3185. " \"subnet\": \"2001:db8:2::/64\", "
  3186. " \"client-class\": \"beta\" "
  3187. " },"
  3188. " {"
  3189. " \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
  3190. " \"subnet\": \"2001:db8:3::/64\", "
  3191. " \"client-class\": \"gamma\" "
  3192. " },"
  3193. " {"
  3194. " \"pools\": [ { \"pool\": \"2001:db8:4::/80\" } ],"
  3195. " \"subnet\": \"2001:db8:4::/64\" "
  3196. " } ],"
  3197. "\"valid-lifetime\": 4000 }";
  3198. ElementPtr json = Element::fromJSON(config);
  3199. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  3200. checkResult(x, 0);
  3201. const Subnet6Collection* subnets =
  3202. CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->getAll();
  3203. ASSERT_TRUE(subnets);
  3204. ASSERT_EQ(4, subnets->size()); // We expect 4 subnets
  3205. // Let's check if client belonging to alpha class is supported in subnet[0]
  3206. // and not supported in any other subnet (except subnet[3], which allows
  3207. // everyone).
  3208. ClientClasses classes;
  3209. classes.insert("alpha");
  3210. EXPECT_TRUE (subnets->at(0)->clientSupported(classes));
  3211. EXPECT_FALSE(subnets->at(1)->clientSupported(classes));
  3212. EXPECT_FALSE(subnets->at(2)->clientSupported(classes));
  3213. EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
  3214. // Let's check if client belonging to beta class is supported in subnet[1]
  3215. // and not supported in any other subnet (except subnet[3], which allows
  3216. // everyone).
  3217. classes.clear();
  3218. classes.insert("beta");
  3219. EXPECT_FALSE(subnets->at(0)->clientSupported(classes));
  3220. EXPECT_TRUE (subnets->at(1)->clientSupported(classes));
  3221. EXPECT_FALSE(subnets->at(2)->clientSupported(classes));
  3222. EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
  3223. // Let's check if client belonging to gamma class is supported in subnet[2]
  3224. // and not supported in any other subnet (except subnet[3], which allows
  3225. // everyone).
  3226. classes.clear();
  3227. classes.insert("gamma");
  3228. EXPECT_FALSE(subnets->at(0)->clientSupported(classes));
  3229. EXPECT_FALSE(subnets->at(1)->clientSupported(classes));
  3230. EXPECT_TRUE (subnets->at(2)->clientSupported(classes));
  3231. EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
  3232. // Let's check if client belonging to some other class (not mentioned in
  3233. // the config) is supported only in subnet[3], which allows everyone.
  3234. classes.clear();
  3235. classes.insert("delta");
  3236. EXPECT_FALSE(subnets->at(0)->clientSupported(classes));
  3237. EXPECT_FALSE(subnets->at(1)->clientSupported(classes));
  3238. EXPECT_FALSE(subnets->at(2)->clientSupported(classes));
  3239. EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
  3240. // Finally, let's check class-less client. He should be allowed only in
  3241. // the last subnet, which does not have any class restrictions.
  3242. classes.clear();
  3243. EXPECT_FALSE(subnets->at(0)->clientSupported(classes));
  3244. EXPECT_FALSE(subnets->at(1)->clientSupported(classes));
  3245. EXPECT_FALSE(subnets->at(2)->clientSupported(classes));
  3246. EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
  3247. }
  3248. // This test checks the ability of the server to parse a configuration
  3249. // containing a full, valid dhcp-ddns (D2ClientConfig) entry.
  3250. TEST_F(Dhcp6ParserTest, d2ClientConfig) {
  3251. ConstElementPtr status;
  3252. // Verify that the D2 configuraiton can be fetched and is set to disabled.
  3253. D2ClientConfigPtr d2_client_config = CfgMgr::instance().getD2ClientConfig();
  3254. EXPECT_FALSE(d2_client_config->getEnableUpdates());
  3255. // Verify that the convenience method agrees.
  3256. ASSERT_FALSE(CfgMgr::instance().ddnsEnabled());
  3257. string config_str = "{ " + genIfaceConfig() + ","
  3258. "\"preferred-lifetime\": 3000,"
  3259. "\"valid-lifetime\": 4000,"
  3260. "\"rebind-timer\": 2000, "
  3261. "\"renew-timer\": 1000, "
  3262. "\"subnet6\": [ { "
  3263. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  3264. " \"subnet\": \"2001:db8:1::/64\" } ], "
  3265. " \"dhcp-ddns\" : {"
  3266. " \"enable-updates\" : true, "
  3267. " \"server-ip\" : \"3001::1\", "
  3268. " \"server-port\" : 777, "
  3269. " \"sender-ip\" : \"3001::2\", "
  3270. " \"sender-port\" : 778, "
  3271. " \"max-queue-size\" : 2048, "
  3272. " \"ncr-protocol\" : \"UDP\", "
  3273. " \"ncr-format\" : \"JSON\", "
  3274. " \"always-include-fqdn\" : true, "
  3275. " \"allow-client-update\" : true, "
  3276. " \"override-no-update\" : true, "
  3277. " \"override-client-update\" : true, "
  3278. " \"replace-client-name\" : \"when-present\", "
  3279. " \"generated-prefix\" : \"test.prefix\", "
  3280. " \"qualifying-suffix\" : \"test.suffix.\" },"
  3281. "\"valid-lifetime\": 4000 }";
  3282. // Convert the JSON string to configuration elements.
  3283. ElementPtr config;
  3284. ASSERT_NO_THROW(config = Element::fromJSON(config_str));
  3285. // Pass the configuration in for parsing.
  3286. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, config));
  3287. // check if returned status is OK
  3288. checkResult(status, 0);
  3289. // Verify that DHCP-DDNS updating is enabled.
  3290. EXPECT_TRUE(CfgMgr::instance().ddnsEnabled());
  3291. // Verify that the D2 configuration can be retrieved.
  3292. d2_client_config = CfgMgr::instance().getD2ClientConfig();
  3293. ASSERT_TRUE(d2_client_config);
  3294. // Verify that the configuration values are correct.
  3295. EXPECT_TRUE(d2_client_config->getEnableUpdates());
  3296. EXPECT_EQ("3001::1", d2_client_config->getServerIp().toText());
  3297. EXPECT_EQ(777, d2_client_config->getServerPort());
  3298. EXPECT_EQ("3001::2", d2_client_config->getSenderIp().toText());
  3299. EXPECT_EQ(778, d2_client_config->getSenderPort());
  3300. EXPECT_EQ(2048, d2_client_config->getMaxQueueSize());
  3301. EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
  3302. EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
  3303. EXPECT_TRUE(d2_client_config->getAlwaysIncludeFqdn());
  3304. EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
  3305. EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
  3306. EXPECT_EQ(D2ClientConfig::RCM_WHEN_PRESENT, d2_client_config->getReplaceClientNameMode());
  3307. EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix());
  3308. EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
  3309. }
  3310. // This test checks the ability of the server to handle a configuration
  3311. // containing an invalid dhcp-ddns (D2ClientConfig) entry.
  3312. TEST_F(Dhcp6ParserTest, invalidD2ClientConfig) {
  3313. ConstElementPtr status;
  3314. // Configuration string with an invalid D2 client config,
  3315. // "server-ip" is invalid.
  3316. string config_str = "{ " + genIfaceConfig() + ","
  3317. "\"rebind-timer\": 2000, "
  3318. "\"renew-timer\": 1000, "
  3319. "\"subnet6\": [ { "
  3320. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  3321. " \"subnet\": \"2001:db8:1::/64\" } ], "
  3322. " \"dhcp-ddns\" : {"
  3323. " \"enable-updates\" : true, "
  3324. " \"server-ip\" : \"bogus-value\", "
  3325. " \"server-port\" : 5301, "
  3326. " \"ncr-protocol\" : \"UDP\", "
  3327. " \"ncr-format\" : \"JSON\", "
  3328. " \"always-include-fqdn\" : true, "
  3329. " \"allow-client-update\" : true, "
  3330. " \"override-no-update\" : true, "
  3331. " \"override-client-update\" : true, "
  3332. " \"replace-client-name\" : \"when-present\", "
  3333. " \"generated-prefix\" : \"test.prefix\", "
  3334. " \"qualifying-suffix\" : \"test.suffix.\" },"
  3335. "\"valid-lifetime\": 4000 }";
  3336. // Convert the JSON string to configuration elements.
  3337. ElementPtr config;
  3338. ASSERT_NO_THROW(config = Element::fromJSON(config_str));
  3339. // Configuration should not throw, but should fail.
  3340. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, config));
  3341. // check if returned status is failed.
  3342. checkResult(status, 1);
  3343. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  3344. // Verify that the D2 configuraiton can be fetched and is set to disabled.
  3345. D2ClientConfigPtr d2_client_config = CfgMgr::instance().getD2ClientConfig();
  3346. EXPECT_FALSE(d2_client_config->getEnableUpdates());
  3347. // Verify that the convenience method agrees.
  3348. ASSERT_FALSE(CfgMgr::instance().ddnsEnabled());
  3349. }
  3350. /// @brief Checks if the reservation is in the range of reservations.
  3351. ///
  3352. /// @param resrv Reservation to be searched for.
  3353. /// @param range Range of reservations returned by the @c Host object
  3354. /// in which the reservation will be searched.
  3355. bool reservationExists(const IPv6Resrv& resrv, const IPv6ResrvRange& range) {
  3356. for (IPv6ResrvIterator it = range.first; it != range.second;
  3357. ++it) {
  3358. if (resrv == it->second) {
  3359. return (true);
  3360. }
  3361. }
  3362. return (false);
  3363. }
  3364. // This test verifies that the host reservations can be specified for
  3365. // respective IPv6 subnets.
  3366. TEST_F(Dhcp6ParserTest, reservations) {
  3367. ConstElementPtr x;
  3368. string config = "{ " + genIfaceConfig() + ","
  3369. "\"rebind-timer\": 2000, "
  3370. "\"renew-timer\": 1000, "
  3371. "\"subnet6\": [ "
  3372. " { "
  3373. " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
  3374. " \"subnet\": \"2001:db8:1::/64\", "
  3375. " \"id\": 123,"
  3376. " \"reservations\": ["
  3377. " ]"
  3378. " },"
  3379. " {"
  3380. " \"reservations\": ["
  3381. " {"
  3382. " \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
  3383. " \"ip-addresses\": [ \"2001:db8:2::1234\" ],"
  3384. " \"hostname\": \"\","
  3385. " \"option-data\": ["
  3386. " {"
  3387. " \"name\": \"dns-servers\","
  3388. " \"data\": \"2001:db8:2::1111\""
  3389. " },"
  3390. " {"
  3391. " \"name\": \"preference\","
  3392. " \"data\": \"11\""
  3393. " }"
  3394. " ]"
  3395. " },"
  3396. " {"
  3397. " \"hw-address\": \"01:02:03:04:05:06\","
  3398. " \"ip-addresses\": [ \"2001:db8:2::abcd\" ],"
  3399. " \"hostname\": \"\","
  3400. " \"option-data\": ["
  3401. " {"
  3402. " \"name\": \"dns-servers\","
  3403. " \"data\": \"2001:db8:2::abbc\""
  3404. " },"
  3405. " {"
  3406. " \"name\": \"preference\","
  3407. " \"data\": \"25\""
  3408. " }"
  3409. " ]"
  3410. " }"
  3411. " ],"
  3412. " \"pools\": [ ],"
  3413. " \"subnet\": \"2001:db8:2::/64\", "
  3414. " \"id\": 234"
  3415. " },"
  3416. " {"
  3417. " \"pools\": [ ],"
  3418. " \"subnet\": \"2001:db8:3::/64\", "
  3419. " \"id\": 542,"
  3420. " \"reservations\": ["
  3421. " {"
  3422. " \"duid\": \"0A:09:08:07:06:05:04:03:02:01\","
  3423. " \"prefixes\": [ \"2001:db8:3:2::/96\" ],"
  3424. " \"hostname\": \"\","
  3425. " \"option-data\": ["
  3426. " {"
  3427. " \"name\": \"dns-servers\","
  3428. " \"data\": \"2001:db8:3::3333\""
  3429. " },"
  3430. " {"
  3431. " \"name\": \"preference\","
  3432. " \"data\": \"33\""
  3433. " }"
  3434. " ]"
  3435. " },"
  3436. " {"
  3437. " \"hw-address\": \"06:05:04:03:02:01\","
  3438. " \"prefixes\": [ \"2001:db8:3:1::/96\" ],"
  3439. " \"hostname\": \"\""
  3440. " }"
  3441. " ]"
  3442. " } "
  3443. "], "
  3444. "\"preferred-lifetime\": 3000,"
  3445. "\"valid-lifetime\": 4000 }";
  3446. ElementPtr json = Element::fromJSON(config);
  3447. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  3448. checkResult(x, 0);
  3449. // Make sure all subnets have been successfully configured. There is no
  3450. // need to sanity check the subnet properties because it should have
  3451. // been already tested by other tests.
  3452. const Subnet6Collection* subnets =
  3453. CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->getAll();
  3454. ASSERT_TRUE(subnets);
  3455. ASSERT_EQ(3, subnets->size());
  3456. // Hosts configuration must be available.
  3457. CfgHostsPtr hosts_cfg = CfgMgr::instance().getStagingCfg()->getCfgHosts();
  3458. ASSERT_TRUE(hosts_cfg);
  3459. // Let's create an object holding hardware address of the host having
  3460. // a reservation in the subnet having id of 234. For simplicity the
  3461. // address is a collection of numbers from 1 to 6.
  3462. std::vector<uint8_t> hwaddr_vec;
  3463. for (unsigned int i = 1; i < 7; ++i) {
  3464. hwaddr_vec.push_back(static_cast<uint8_t>(i));
  3465. }
  3466. HWAddrPtr hwaddr(new HWAddr(hwaddr_vec, HTYPE_ETHER));
  3467. // Retrieve the reservation and sanity check the address reserved.
  3468. ConstHostPtr host = hosts_cfg->get6(234, DuidPtr(), hwaddr);
  3469. ASSERT_TRUE(host);
  3470. IPv6ResrvRange resrv = host->getIPv6Reservations(IPv6Resrv::TYPE_NA);
  3471. ASSERT_EQ(1, std::distance(resrv.first, resrv.second));
  3472. EXPECT_TRUE(reservationExists(IPv6Resrv(IPv6Resrv::TYPE_NA,
  3473. IOAddress("2001:db8:2::abcd")),
  3474. resrv));
  3475. // This reservation should be solely assigned to the subnet 234,
  3476. // and not to other two.
  3477. EXPECT_FALSE(hosts_cfg->get6(123, DuidPtr(), hwaddr));
  3478. EXPECT_FALSE(hosts_cfg->get6(542, DuidPtr(), hwaddr));
  3479. // Check that options are assigned correctly.
  3480. Option6AddrLstPtr opt_dns =
  3481. retrieveOption<Option6AddrLstPtr>(*host, D6O_NAME_SERVERS);
  3482. ASSERT_TRUE(opt_dns);
  3483. Option6AddrLst::AddressContainer dns_addrs = opt_dns->getAddresses();
  3484. ASSERT_EQ(1, dns_addrs.size());
  3485. EXPECT_EQ("2001:db8:2::abbc", dns_addrs[0].toText());
  3486. OptionUint8Ptr opt_prf =
  3487. retrieveOption<OptionUint8Ptr>(*host, D6O_PREFERENCE);
  3488. ASSERT_TRUE(opt_prf);
  3489. EXPECT_EQ(25, static_cast<int>(opt_prf->getValue()));
  3490. // Do the same test for the DUID based reservation.
  3491. std::vector<uint8_t> duid_vec;
  3492. for (unsigned int i = 1; i < 0xb; ++i) {
  3493. duid_vec.push_back(static_cast<uint8_t>(i));
  3494. }
  3495. DuidPtr duid(new DUID(duid_vec));
  3496. host = hosts_cfg->get6(234, duid);
  3497. ASSERT_TRUE(host);
  3498. resrv = host->getIPv6Reservations(IPv6Resrv::TYPE_NA);
  3499. ASSERT_EQ(1, std::distance(resrv.first, resrv.second));
  3500. EXPECT_TRUE(reservationExists(IPv6Resrv(IPv6Resrv::TYPE_NA,
  3501. IOAddress("2001:db8:2::1234")),
  3502. resrv));
  3503. EXPECT_FALSE(hosts_cfg->get6(123, duid));
  3504. EXPECT_FALSE(hosts_cfg->get6(542, duid));
  3505. // Check that options are assigned correctly.
  3506. opt_dns = retrieveOption<Option6AddrLstPtr>(*host, D6O_NAME_SERVERS);
  3507. ASSERT_TRUE(opt_dns);
  3508. dns_addrs = opt_dns->getAddresses();
  3509. ASSERT_EQ(1, dns_addrs.size());
  3510. EXPECT_EQ("2001:db8:2::1111", dns_addrs[0].toText());
  3511. opt_prf = retrieveOption<OptionUint8Ptr>(*host, D6O_PREFERENCE);
  3512. ASSERT_TRUE(opt_prf);
  3513. EXPECT_EQ(11, static_cast<int>(opt_prf->getValue()));
  3514. // The HW address used for one of the reservations in the subnet 542
  3515. // consists of numbers from 6 to 1. So, let's just reverse the order
  3516. // of the address from the previous test.
  3517. hwaddr->hwaddr_.assign(hwaddr_vec.rbegin(), hwaddr_vec.rend());
  3518. host = hosts_cfg->get6(542, DuidPtr(), hwaddr);
  3519. EXPECT_TRUE(host);
  3520. resrv = host->getIPv6Reservations(IPv6Resrv::TYPE_PD);
  3521. ASSERT_EQ(1, std::distance(resrv.first, resrv.second));
  3522. EXPECT_TRUE(reservationExists(IPv6Resrv(IPv6Resrv::TYPE_PD,
  3523. IOAddress("2001:db8:3:1::"),
  3524. 96), resrv));
  3525. // This reservation must not belong to other subnets.
  3526. EXPECT_FALSE(hosts_cfg->get6(123, DuidPtr(), hwaddr));
  3527. EXPECT_FALSE(hosts_cfg->get6(234, DuidPtr(), hwaddr));
  3528. // Repeat the test for the DUID based reservation in this subnet.
  3529. duid.reset(new DUID(std::vector<uint8_t>(duid_vec.rbegin(),
  3530. duid_vec.rend())));
  3531. host = hosts_cfg->get6(542, duid);
  3532. ASSERT_TRUE(host);
  3533. resrv = host->getIPv6Reservations(IPv6Resrv::TYPE_PD);
  3534. ASSERT_EQ(1, std::distance(resrv.first, resrv.second));
  3535. EXPECT_TRUE(reservationExists(IPv6Resrv(IPv6Resrv::TYPE_PD,
  3536. IOAddress("2001:db8:3:2::"),
  3537. 96), resrv));
  3538. EXPECT_FALSE(hosts_cfg->get6(123, duid));
  3539. EXPECT_FALSE(hosts_cfg->get6(234, duid));
  3540. // Check that options are assigned correctly.
  3541. opt_dns = retrieveOption<Option6AddrLstPtr>(*host, D6O_NAME_SERVERS);
  3542. ASSERT_TRUE(opt_dns);
  3543. dns_addrs = opt_dns->getAddresses();
  3544. ASSERT_EQ(1, dns_addrs.size());
  3545. EXPECT_EQ("2001:db8:3::3333", dns_addrs[0].toText());
  3546. opt_prf = retrieveOption<OptionUint8Ptr>(*host, D6O_PREFERENCE);
  3547. ASSERT_TRUE(opt_prf);
  3548. EXPECT_EQ(33, static_cast<int>(opt_prf->getValue()));
  3549. }
  3550. // This test checks that it is possible to configure option data for a
  3551. // host using a user defined option format.
  3552. TEST_F(Dhcp6ParserTest, reservationWithOptionDefinition) {
  3553. ConstElementPtr x;
  3554. // The following configuration contains host declaration in which
  3555. // a non-standard option is used. This option has option definition
  3556. // specified in the configuration.
  3557. string config = "{ " + genIfaceConfig() + ","
  3558. "\"rebind-timer\": 2000, "
  3559. "\"renew-timer\": 1000, "
  3560. "\"option-def\": [ {"
  3561. " \"name\": \"foo\","
  3562. " \"code\": 100,"
  3563. " \"type\": \"uint32\","
  3564. " \"space\": \"isc\""
  3565. "} ],"
  3566. "\"subnet6\": [ "
  3567. " {"
  3568. " \"reservations\": ["
  3569. " {"
  3570. " \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
  3571. " \"ip-addresses\": [ \"2001:db8:2::1234\" ],"
  3572. " \"hostname\": \"\","
  3573. " \"option-data\": ["
  3574. " {"
  3575. " \"name\": \"foo\","
  3576. " \"data\": \"11\","
  3577. " \"space\": \"isc\""
  3578. " }"
  3579. " ]"
  3580. " }"
  3581. " ],"
  3582. " \"pools\": [ ],"
  3583. " \"subnet\": \"2001:db8:2::/64\", "
  3584. " \"id\": 234"
  3585. " }"
  3586. "],"
  3587. "\"preferred-lifetime\": 3000,"
  3588. "\"valid-lifetime\": 4000 }";
  3589. ElementPtr json = Element::fromJSON(config);
  3590. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  3591. checkResult(x, 0);
  3592. // Hosts configuration must be available.
  3593. CfgHostsPtr hosts_cfg = CfgMgr::instance().getStagingCfg()->getCfgHosts();
  3594. ASSERT_TRUE(hosts_cfg);
  3595. // Let's create an object holding DUID of the host. For simplicity the
  3596. // address is a collection of numbers from 1 to A.
  3597. std::vector<uint8_t> duid_vec;
  3598. for (unsigned int i = 1; i < 0xB; ++i) {
  3599. duid_vec.push_back(static_cast<uint8_t>(i));
  3600. }
  3601. DuidPtr duid(new DUID(duid_vec));
  3602. // Retrieve the reservation and sanity check the address reserved.
  3603. ConstHostPtr host = hosts_cfg->get6(234, duid);
  3604. ASSERT_TRUE(host);
  3605. // Check if the option has been parsed.
  3606. OptionUint32Ptr opt_foo = retrieveOption<OptionUint32Ptr>(*host, "isc",
  3607. 100);
  3608. ASSERT_TRUE(opt_foo);
  3609. EXPECT_EQ(100, opt_foo->getType());
  3610. EXPECT_EQ(11, opt_foo->getValue());
  3611. }
  3612. // This test verifies that the bogus host reservation would trigger a
  3613. // server configuration error.
  3614. TEST_F(Dhcp6ParserTest, reservationBogus) {
  3615. // Case 1: misspelled "duid" parameter.
  3616. ConstElementPtr x;
  3617. string config = "{ " + genIfaceConfig() + ","
  3618. "\"rebind-timer\": 2000, "
  3619. "\"renew-timer\": 1000, "
  3620. "\"subnet6\": [ "
  3621. " { "
  3622. " \"pools\": [ ],"
  3623. " \"subnet\": \"2001:db8:3::/64\", "
  3624. " \"id\": 542,"
  3625. " \"reservations\": ["
  3626. " {"
  3627. " \"dui\": \"0A:09:08:07:06:05:04:03:02:01\","
  3628. " \"prefixes\": [ \"2001:db8:3:2::/96\" ],"
  3629. " \"hostname\": \"\""
  3630. " }"
  3631. " ]"
  3632. " } "
  3633. "], "
  3634. "\"preferred-lifetime\": 3000,"
  3635. "\"valid-lifetime\": 4000 }";
  3636. ElementPtr json = Element::fromJSON(config);
  3637. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  3638. checkResult(x, 1);
  3639. // Case 2: DUID and HW Address both specified.
  3640. config = "{ " + genIfaceConfig() + ","
  3641. "\"rebind-timer\": 2000, "
  3642. "\"renew-timer\": 1000, "
  3643. "\"subnet6\": [ "
  3644. " { "
  3645. " \"pools\": [ ],"
  3646. " \"subnet\": \"2001:db8:3::/64\", "
  3647. " \"id\": 542,"
  3648. " \"reservations\": ["
  3649. " {"
  3650. " \"hw-address\": \"01:02:03:04:05:06\","
  3651. " \"duid\": \"0A:09:08:07:06:05:04:03:02:01\","
  3652. " \"prefixes\": [ \"2001:db8:3:2::/96\" ],"
  3653. " \"hostname\": \"\""
  3654. " }"
  3655. " ]"
  3656. " } "
  3657. "], "
  3658. "\"preferred-lifetime\": 3000,"
  3659. "\"valid-lifetime\": 4000 }";
  3660. json = Element::fromJSON(config);
  3661. // Remove existing configuration, if any.
  3662. CfgMgr::instance().clear();
  3663. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  3664. checkResult(x, 1);
  3665. // Case 3: Neither ip address nor hostname specified.
  3666. config = "{ " + genIfaceConfig() + ","
  3667. "\"rebind-timer\": 2000, "
  3668. "\"renew-timer\": 1000, "
  3669. "\"subnet6\": [ "
  3670. " { "
  3671. " \"pools\": [ ],"
  3672. " \"subnet\": \"2001:db8:3::/64\", "
  3673. " \"id\": 542,"
  3674. " \"reservations\": ["
  3675. " {"
  3676. " \"duid\": \"0A:09:08:07:06:05:04:03:02:01\""
  3677. " }"
  3678. " ]"
  3679. " } "
  3680. "], "
  3681. "\"preferred-lifetime\": 3000,"
  3682. "\"valid-lifetime\": 4000 }";
  3683. json = Element::fromJSON(config);
  3684. // Remove existing configuration, if any.
  3685. CfgMgr::instance().clear();
  3686. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  3687. checkResult(x, 1);
  3688. // Case 4: Broken specification of option data.
  3689. config = "{ " + genIfaceConfig() + ","
  3690. "\"rebind-timer\": 2000, "
  3691. "\"renew-timer\": 1000, "
  3692. "\"subnet6\": [ "
  3693. " { "
  3694. " \"pools\": [ ],"
  3695. " \"subnet\": \"2001:db8:3::/64\", "
  3696. " \"id\": 542,"
  3697. " \"reservations\": ["
  3698. " {"
  3699. " \"duid\": \"0A:09:08:07:06:05:04:03:02:01\","
  3700. " \"option-data\": ["
  3701. " {"
  3702. " \"name\": \"dns-servers\","
  3703. " \"data\": \"invalid-ip-address\""
  3704. " }"
  3705. " ]"
  3706. " }"
  3707. " ]"
  3708. " } "
  3709. "], "
  3710. "\"preferred-lifetime\": 3000,"
  3711. "\"valid-lifetime\": 4000 }";
  3712. json = Element::fromJSON(config);
  3713. // Remove existing configuration, if any.
  3714. CfgMgr::instance().clear();
  3715. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  3716. checkResult(x, 1);
  3717. }
  3718. /// The goal of this test is to verify that configuration can include
  3719. /// MAC/Hardware sources. This test also checks if the aliases are
  3720. /// handled properly (rfc6939 = client-addr-relay, rfc4649 = remote-id,
  3721. /// rfc4580 = subscriber-id).
  3722. TEST_F(Dhcp6ParserTest, macSources) {
  3723. ConstElementPtr status;
  3724. EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
  3725. Element::fromJSON("{ " + genIfaceConfig() + ","
  3726. "\"mac-sources\": [ \"rfc6939\", \"rfc4649\", \"rfc4580\","
  3727. "\"client-link-addr-option\", \"remote-id\", \"subscriber-id\"],"
  3728. "\"preferred-lifetime\": 3000,"
  3729. "\"rebind-timer\": 2000, "
  3730. "\"renew-timer\": 1000, "
  3731. "\"subnet6\": [ ], "
  3732. "\"valid-lifetime\": 4000 }")));
  3733. // returned value should be 0 (success)
  3734. checkResult(status, 0);
  3735. CfgMACSources mac_sources = CfgMgr::instance().getStagingCfg()->getMACSources().get();
  3736. ASSERT_EQ(6, mac_sources.size());
  3737. // Let's check the aliases. They should be recognized to their base methods.
  3738. EXPECT_EQ(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION, mac_sources[0]);
  3739. EXPECT_EQ(HWAddr::HWADDR_SOURCE_REMOTE_ID, mac_sources[1]);
  3740. EXPECT_EQ(HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID, mac_sources[2]);
  3741. // Let's check if the actual methods are recognized properly.
  3742. EXPECT_EQ(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION, mac_sources[3]);
  3743. EXPECT_EQ(HWAddr::HWADDR_SOURCE_REMOTE_ID, mac_sources[4]);
  3744. EXPECT_EQ(HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID, mac_sources[5]);
  3745. }
  3746. /// The goal of this test is to verify that MAC sources configuration can be
  3747. /// empty.
  3748. TEST_F(Dhcp6ParserTest, macSourcesEmpty) {
  3749. ConstElementPtr status;
  3750. EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
  3751. Element::fromJSON("{ " + genIfaceConfig() + ","
  3752. "\"mac-sources\": [ ],"
  3753. "\"preferred-lifetime\": 3000,"
  3754. "\"rebind-timer\": 2000, "
  3755. "\"renew-timer\": 1000, "
  3756. "\"subnet6\": [ ], "
  3757. "\"valid-lifetime\": 4000 }")));
  3758. // returned value should be 0 (success)
  3759. checkResult(status, 0);
  3760. CfgMACSources mac_sources = CfgMgr::instance().getStagingCfg()->getMACSources().get();
  3761. EXPECT_EQ(0, mac_sources.size());
  3762. }
  3763. /// The goal of this test is to verify that MAC sources configuration can
  3764. /// only use valid parameters.
  3765. TEST_F(Dhcp6ParserTest, macSourcesBogus) {
  3766. ConstElementPtr status;
  3767. EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
  3768. Element::fromJSON("{ " + genIfaceConfig() + ","
  3769. "\"mac-sources\": [ \"from-wire\" ],"
  3770. "\"preferred-lifetime\": 3000,"
  3771. "\"rebind-timer\": 2000, "
  3772. "\"renew-timer\": 1000, "
  3773. "\"subnet6\": [ ], "
  3774. "\"valid-lifetime\": 4000 }")));
  3775. // returned value should be 1 (failure)
  3776. checkResult(status, 1);
  3777. }
  3778. /// The goal of this test is to verify that Host Reservation modes can be
  3779. /// specified on a per-subnet basis.
  3780. TEST_F(Dhcp6ParserTest, hostReservationPerSubnet) {
  3781. /// - Configuration:
  3782. /// - only addresses (no prefixes)
  3783. /// - 4 subnets with:
  3784. /// - 2001:db8:1::/64 (all reservations enabled)
  3785. /// - 2001:db8:2::/64 (out-of-pool reservations)
  3786. /// - 2001:db8:3::/64 (reservations disabled)
  3787. /// - 2001:db8:3::/64 (reservations not specified)
  3788. const char* HR_CONFIG =
  3789. "{"
  3790. "\"preferred-lifetime\": 3000,"
  3791. "\"rebind-timer\": 2000, "
  3792. "\"renew-timer\": 1000, "
  3793. "\"subnet6\": [ { "
  3794. " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
  3795. " \"subnet\": \"2001:db8:1::/48\", "
  3796. " \"reservation-mode\": \"all\""
  3797. " },"
  3798. " {"
  3799. " \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ],"
  3800. " \"subnet\": \"2001:db8:2::/48\", "
  3801. " \"reservation-mode\": \"out-of-pool\""
  3802. " },"
  3803. " {"
  3804. " \"pools\": [ { \"pool\": \"2001:db8:3::/64\" } ],"
  3805. " \"subnet\": \"2001:db8:3::/48\", "
  3806. " \"reservation-mode\": \"disabled\""
  3807. " },"
  3808. " {"
  3809. " \"pools\": [ { \"pool\": \"2001:db8:4::/64\" } ],"
  3810. " \"subnet\": \"2001:db8:4::/48\" "
  3811. " } ],"
  3812. "\"valid-lifetime\": 4000 }";
  3813. ConstElementPtr status;
  3814. EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
  3815. Element::fromJSON(HR_CONFIG)));
  3816. // returned value should be 0 (success)
  3817. checkResult(status, 0);
  3818. CfgMgr::instance().commit();
  3819. // Let's get all subnets and check that there are 4 of them.
  3820. ConstCfgSubnets6Ptr subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6();
  3821. ASSERT_TRUE(subnets);
  3822. const Subnet6Collection* subnet_col = subnets->getAll();
  3823. ASSERT_EQ(4, subnet_col->size()); // We expect 4 subnets
  3824. // Let's check if the parsed subnets have correct HR modes.
  3825. // Subnet 1
  3826. Subnet6Ptr subnet;
  3827. subnet = subnets->selectSubnet(IOAddress("2001:db8:1::1"));
  3828. ASSERT_TRUE(subnet);
  3829. EXPECT_EQ(Subnet::HR_ALL, subnet->getHostReservationMode());
  3830. // Subnet 2
  3831. subnet = subnets->selectSubnet(IOAddress("2001:db8:2::1"));
  3832. ASSERT_TRUE(subnet);
  3833. EXPECT_EQ(Subnet::HR_OUT_OF_POOL, subnet->getHostReservationMode());
  3834. // Subnet 3
  3835. subnet = subnets->selectSubnet(IOAddress("2001:db8:3::1"));
  3836. ASSERT_TRUE(subnet);
  3837. EXPECT_EQ(Subnet::HR_DISABLED, subnet->getHostReservationMode());
  3838. // Subnet 4
  3839. subnet = subnets->selectSubnet(IOAddress("2001:db8:4::1"));
  3840. ASSERT_TRUE(subnet);
  3841. EXPECT_EQ(Subnet::HR_ALL, subnet->getHostReservationMode());
  3842. }
  3843. /// The goal of this test is to verify that configuration can include
  3844. /// Relay Supplied options (specified as numbers).
  3845. TEST_F(Dhcp6ParserTest, rsooNumbers) {
  3846. ConstElementPtr status;
  3847. EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
  3848. Element::fromJSON("{ " + genIfaceConfig() + ","
  3849. "\"relay-supplied-options\": [ \"10\", \"20\", \"30\" ],"
  3850. "\"preferred-lifetime\": 3000,"
  3851. "\"rebind-timer\": 2000, "
  3852. "\"renew-timer\": 1000, "
  3853. "\"subnet6\": [ ], "
  3854. "\"valid-lifetime\": 4000 }")));
  3855. // returned value should be 0 (success)
  3856. checkResult(status, 0);
  3857. // The following codes should be enabled now
  3858. EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()->enabled(10));
  3859. EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()->enabled(20));
  3860. EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()->enabled(30));
  3861. // This option is on the IANA list, so it should be allowed all the time
  3862. // (http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml)
  3863. EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()
  3864. ->enabled(D6O_ERP_LOCAL_DOMAIN_NAME));
  3865. // Those options are not enabled
  3866. EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()->enabled(25));
  3867. EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()->enabled(1));
  3868. }
  3869. /// The goal of this test is to verify that configuration can include
  3870. /// Relay Supplied options (specified as names).
  3871. TEST_F(Dhcp6ParserTest, rsooNames) {
  3872. ConstElementPtr status;
  3873. EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
  3874. Element::fromJSON("{ " + genIfaceConfig() + ","
  3875. "\"relay-supplied-options\": [ \"dns-servers\", \"remote-id\" ],"
  3876. "\"preferred-lifetime\": 3000,"
  3877. "\"rebind-timer\": 2000, "
  3878. "\"renew-timer\": 1000, "
  3879. "\"subnet6\": [ ], "
  3880. "\"valid-lifetime\": 4000 }")));
  3881. // returned value should be 0 (success)
  3882. checkResult(status, 0);
  3883. for (uint16_t code = 0; code < D6O_NAME_SERVERS; ++code) {
  3884. EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()
  3885. ->enabled(code)) << " for option code " << code;
  3886. }
  3887. // The following code should be enabled now
  3888. EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()
  3889. ->enabled(D6O_NAME_SERVERS));
  3890. for (uint16_t code = D6O_NAME_SERVERS + 1; code < D6O_REMOTE_ID; ++code) {
  3891. EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()
  3892. ->enabled(code)) << " for option code " << code;
  3893. }
  3894. // Check remote-id. It should be enabled.
  3895. EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()
  3896. ->enabled(D6O_REMOTE_ID));
  3897. for (uint16_t code = D6O_REMOTE_ID + 1; code < D6O_ERP_LOCAL_DOMAIN_NAME; ++code) {
  3898. EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()
  3899. ->enabled(code)) << " for option code " << code;
  3900. }
  3901. // This option is on the IANA list, so it should be allowed all the time
  3902. // (http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml)
  3903. EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()
  3904. ->enabled(D6O_ERP_LOCAL_DOMAIN_NAME));
  3905. for (uint16_t code = D6O_ERP_LOCAL_DOMAIN_NAME + 1; code < 300; ++code) {
  3906. EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()
  3907. ->enabled(code)) << " for option code " << code;
  3908. }
  3909. }
  3910. TEST_F(Dhcp6ParserTest, rsooNegativeNumber) {
  3911. ConstElementPtr status;
  3912. EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
  3913. Element::fromJSON("{ " + genIfaceConfig() + ","
  3914. "\"relay-supplied-options\": [ \"80\", \"-2\" ],"
  3915. "\"preferred-lifetime\": 3000,"
  3916. "\"rebind-timer\": 2000, "
  3917. "\"renew-timer\": 1000, "
  3918. "\"subnet6\": [ ], "
  3919. "\"valid-lifetime\": 4000 }")));
  3920. // returned value should be 0 (success)
  3921. checkResult(status, 1);
  3922. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  3923. }
  3924. TEST_F(Dhcp6ParserTest, rsooBogusName) {
  3925. ConstElementPtr status;
  3926. EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
  3927. Element::fromJSON("{ " + genIfaceConfig() + ","
  3928. "\"relay-supplied-options\": [ \"bogus\", \"dns-servers\" ],"
  3929. "\"preferred-lifetime\": 3000,"
  3930. "\"rebind-timer\": 2000, "
  3931. "\"renew-timer\": 1000, "
  3932. "\"subnet6\": [ ], "
  3933. "\"valid-lifetime\": 4000 }")));
  3934. // returned value should be 0 (success)
  3935. checkResult(status, 1);
  3936. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  3937. }
  3938. /// Check that the decline-probation-period value can be set properly.
  3939. TEST_F(Dhcp6ParserTest, declineTimerDefault) {
  3940. ConstElementPtr status;
  3941. string config_txt = "{ " + genIfaceConfig() + ","
  3942. "\"subnet6\": [ ] "
  3943. "}";
  3944. ElementPtr config = Element::fromJSON(config_txt);
  3945. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, config));
  3946. // returned value should be 0 (success)
  3947. checkResult(status, 0);
  3948. // The value of decline-probation-period must be equal to the
  3949. // default value.
  3950. EXPECT_EQ(DEFAULT_DECLINE_PROBATION_PERIOD,
  3951. CfgMgr::instance().getStagingCfg()->getDeclinePeriod());
  3952. }
  3953. /// Check that the decline-probation-period value can be set properly.
  3954. TEST_F(Dhcp6ParserTest, declineTimer) {
  3955. ConstElementPtr status;
  3956. string config = "{ " + genIfaceConfig() + "," +
  3957. "\"decline-probation-period\": 12345,"
  3958. "\"subnet6\": [ ]"
  3959. "}";
  3960. ElementPtr json = Element::fromJSON(config);
  3961. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  3962. // returned value should be 0 (success)
  3963. checkResult(status, 0);
  3964. // The value of decline-probation-period must be equal to the
  3965. // value specified.
  3966. EXPECT_EQ(12345,
  3967. CfgMgr::instance().getStagingCfg()->getDeclinePeriod());
  3968. }
  3969. /// Check that an incorrect decline-probation-period value will be caught.
  3970. TEST_F(Dhcp6ParserTest, declineTimerError) {
  3971. ConstElementPtr status;
  3972. string config = "{ " + genIfaceConfig() + "," +
  3973. "\"decline-probation-period\": \"soon\","
  3974. "\"subnet6\": [ ]"
  3975. "}";
  3976. ElementPtr json = Element::fromJSON(config);
  3977. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  3978. // returned value should be 1 (error)
  3979. checkResult(status, 1);
  3980. // Check that the error contains error position.
  3981. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  3982. }
  3983. // Check that configuration for the expired leases processing may be
  3984. // specified.
  3985. TEST_F(Dhcp6ParserTest, expiredLeasesProcessing) {
  3986. // Create basic configuration with the expiration specific parameters.
  3987. string config = "{ " + genIfaceConfig() + "," +
  3988. "\"expired-leases-processing\": "
  3989. "{"
  3990. " \"reclaim-timer-wait-time\": 20,"
  3991. " \"flush-reclaimed-timer-wait-time\": 35,"
  3992. " \"hold-reclaimed-time\": 1800,"
  3993. " \"max-reclaim-leases\": 50,"
  3994. " \"max-reclaim-time\": 100,"
  3995. " \"unwarned-reclaim-cycles\": 10"
  3996. "},"
  3997. "\"subnet6\": [ ]"
  3998. "}";
  3999. ElementPtr json = Element::fromJSON(config);
  4000. ConstElementPtr status;
  4001. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  4002. // Returned value should be 0 (success)
  4003. checkResult(status, 0);
  4004. // The value of decline-probation-period must be equal to the
  4005. // value specified.
  4006. CfgExpirationPtr cfg = CfgMgr::instance().getStagingCfg()->getCfgExpiration();
  4007. ASSERT_TRUE(cfg);
  4008. // Verify that parameters are correct.
  4009. EXPECT_EQ(20, cfg->getReclaimTimerWaitTime());
  4010. EXPECT_EQ(35, cfg->getFlushReclaimedTimerWaitTime());
  4011. EXPECT_EQ(1800, cfg->getHoldReclaimedTime());
  4012. EXPECT_EQ(50, cfg->getMaxReclaimLeases());
  4013. EXPECT_EQ(100, cfg->getMaxReclaimTime());
  4014. EXPECT_EQ(10, cfg->getUnwarnedReclaimCycles());
  4015. }
  4016. // Check that invalid configuration for the expired leases processing is
  4017. // causing an error.
  4018. TEST_F(Dhcp6ParserTest, expiredLeasesProcessingError) {
  4019. // Create basic configuration with the expiration specific parameters.
  4020. // One of the parameters holds invalid value.
  4021. string config = "{ " + genIfaceConfig() + "," +
  4022. "\"expired-leases-processing\": "
  4023. "{"
  4024. " \"reclaim-timer-wait-time\": -5,"
  4025. " \"flush-reclaimed-timer-wait-time\": 35,"
  4026. " \"hold-reclaimed-time\": 1800,"
  4027. " \"max-reclaim-leases\": 50,"
  4028. " \"max-reclaim-time\": 100,"
  4029. " \"unwarned-reclaim-cycles\": 10"
  4030. "},"
  4031. "\"subnet6\": [ ]"
  4032. "}";
  4033. ElementPtr json = Element::fromJSON(config);
  4034. ConstElementPtr status;
  4035. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  4036. // Returned value should be 0 (error)
  4037. checkResult(status, 1);
  4038. // Check that the error contains error position.
  4039. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  4040. }
  4041. // Verifies that simple list of valid classes parses and
  4042. // is staged for commit.
  4043. TEST_F(Dhcp6ParserTest, validClientClassDictionary) {
  4044. string config = "{ " + genIfaceConfig() + ","
  4045. "\"preferred-lifetime\": 3000, \n"
  4046. "\"rebind-timer\": 2000, \n"
  4047. "\"renew-timer\": 1000, \n"
  4048. "\"client-classes\" : [ \n"
  4049. " { \n"
  4050. " \"name\": \"one\" \n"
  4051. " }, \n"
  4052. " { \n"
  4053. " \"name\": \"two\" \n"
  4054. " }, \n"
  4055. " { \n"
  4056. " \"name\": \"three\" \n"
  4057. " } \n"
  4058. "], \n"
  4059. "\"subnet6\": [ { \n"
  4060. " \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ], \n"
  4061. " \"subnet\": \"2001:db8:1::/64\" } ], \n"
  4062. "\"valid-lifetime\": 4000 } \n";
  4063. ConstElementPtr status;
  4064. ElementPtr json = Element::fromJSON(config);
  4065. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  4066. ASSERT_TRUE(status);
  4067. checkResult(status, 0);
  4068. // We check staging config because CfgMgr::commit hasn't been executed.
  4069. ClientClassDictionaryPtr dictionary;
  4070. dictionary = CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
  4071. ASSERT_TRUE(dictionary);
  4072. EXPECT_EQ(3, dictionary->getClasses()->size());
  4073. // Execute the commit
  4074. ASSERT_NO_THROW(CfgMgr::instance().commit());
  4075. // Verify that after commit, the current config has the correct dictionary
  4076. dictionary = CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
  4077. ASSERT_TRUE(dictionary);
  4078. EXPECT_EQ(3, dictionary->getClasses()->size());
  4079. }
  4080. // Verifies that a class list containing an invalid
  4081. // class definition causes a configuration error.
  4082. TEST_F(Dhcp6ParserTest, invalidClientClassDictionary) {
  4083. string config = "{ " + genIfaceConfig() + "," +
  4084. "\"valid-lifetime\": 4000, \n"
  4085. "\"rebind-timer\": 2000, \n"
  4086. "\"renew-timer\": 1000, \n"
  4087. "\"client-classes\" : [ \n"
  4088. " { \n"
  4089. " \"name\": \"one\", \n"
  4090. " \"bogus\": \"bad\" \n"
  4091. " } \n"
  4092. "], \n"
  4093. "\"subnet4\": [ { \n"
  4094. " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ], \n"
  4095. " \"subnet\": \"192.0.2.0/24\" \n"
  4096. " } ] \n"
  4097. "} \n";
  4098. ConstElementPtr status;
  4099. ElementPtr json = Element::fromJSON(config);
  4100. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  4101. ASSERT_TRUE(status);
  4102. checkResult(status, 1);
  4103. }
  4104. // Test verifies that regular configuration does not provide any user context
  4105. // in the address pool.
  4106. TEST_F(Dhcp6ParserTest, poolUserContextMissing) {
  4107. PoolPtr pool;
  4108. getPool(string(PARSER_CONFIGS[0]), 0, 0, Lease::TYPE_NA, pool);
  4109. ASSERT_TRUE(pool);
  4110. EXPECT_FALSE(pool->getContext());
  4111. }
  4112. // Test verifies that it's possible to specify empty user context in the
  4113. // address pool.
  4114. TEST_F(Dhcp6ParserTest, poolUserContextEmpty) {
  4115. PoolPtr pool;
  4116. getPool(string(PARSER_CONFIGS[1]), 0, 0, Lease::TYPE_NA, pool);
  4117. ASSERT_TRUE(pool);
  4118. ConstElementPtr ctx = pool->getContext();
  4119. ASSERT_TRUE(ctx);
  4120. // The context should be of type map and not contain any parameters.
  4121. EXPECT_EQ(Element::map, ctx->getType());
  4122. EXPECT_EQ(0, ctx->size());
  4123. }
  4124. // Test verifies that it's possible to specify parameters in the user context
  4125. // in the address pool.
  4126. TEST_F(Dhcp6ParserTest, poolUserContextlw4over6) {
  4127. PoolPtr pool;
  4128. getPool(string(PARSER_CONFIGS[2]), 0, 0, Lease::TYPE_NA, pool);
  4129. ASSERT_TRUE(pool);
  4130. ConstElementPtr ctx = pool->getContext();
  4131. ASSERT_TRUE(ctx);
  4132. // The context should be of type map and contain 4 parameters.
  4133. EXPECT_EQ(Element::map, ctx->getType());
  4134. EXPECT_EQ(4, ctx->size());
  4135. ConstElementPtr ratio = ctx->get("lw4over6-sharing-ratio");
  4136. ConstElementPtr v4pool = ctx->get("lw4over6-v4-pool");
  4137. ConstElementPtr exclude = ctx->get("lw4over6-sysports-exclude");
  4138. ConstElementPtr v6len = ctx->get("lw4over6-bind-prefix-len");
  4139. ASSERT_TRUE(ratio);
  4140. ASSERT_EQ(Element::integer, ratio->getType());
  4141. int64_t int_value;
  4142. EXPECT_NO_THROW(ratio->getValue(int_value));
  4143. EXPECT_EQ(64L, int_value);
  4144. ASSERT_TRUE(v4pool);
  4145. ASSERT_EQ(Element::string, v4pool->getType());
  4146. EXPECT_EQ("192.0.2.0/24", v4pool->stringValue());
  4147. ASSERT_TRUE(exclude);
  4148. bool bool_value;
  4149. ASSERT_EQ(Element::boolean, exclude->getType());
  4150. EXPECT_NO_THROW(exclude->getValue(bool_value));
  4151. EXPECT_EQ(true, bool_value);
  4152. ASSERT_TRUE(v6len);
  4153. ASSERT_EQ(Element::integer, v6len->getType());
  4154. EXPECT_NO_THROW(v6len->getValue(int_value));
  4155. EXPECT_EQ(56L, int_value);
  4156. }
  4157. // Test verifies that regular configuration does not provide any user context
  4158. // in the address pool.
  4159. TEST_F(Dhcp6ParserTest, pdPoolUserContextMissing) {
  4160. PoolPtr pool;
  4161. getPool(string(PARSER_CONFIGS[3]), 0, 0, Lease::TYPE_PD, pool);
  4162. ASSERT_TRUE(pool);
  4163. EXPECT_FALSE(pool->getContext());
  4164. }
  4165. // Test verifies that it's possible to specify empty user context in the
  4166. // address pool.
  4167. TEST_F(Dhcp6ParserTest, pdPoolUserContextEmpty) {
  4168. PoolPtr pool;
  4169. getPool(string(PARSER_CONFIGS[4]), 0, 0, Lease::TYPE_PD, pool);
  4170. ASSERT_TRUE(pool);
  4171. ConstElementPtr ctx = pool->getContext();
  4172. ASSERT_TRUE(ctx);
  4173. // The context should be of type map and not contain any parameters.
  4174. EXPECT_EQ(Element::map, ctx->getType());
  4175. EXPECT_EQ(0, ctx->size());
  4176. }
  4177. // Test verifies that it's possible to specify parameters in the user context
  4178. // in the address pool.
  4179. TEST_F(Dhcp6ParserTest, pdPoolUserContextlw4over6) {
  4180. PoolPtr pool;
  4181. getPool(string(PARSER_CONFIGS[5]), 0, 0, Lease::TYPE_PD, pool);
  4182. ASSERT_TRUE(pool);
  4183. ConstElementPtr ctx = pool->getContext();
  4184. ASSERT_TRUE(ctx);
  4185. // The context should be of type map and contain 4 parameters.
  4186. EXPECT_EQ(Element::map, ctx->getType());
  4187. EXPECT_EQ(4, ctx->size());
  4188. ConstElementPtr ratio = ctx->get("lw4over6-sharing-ratio");
  4189. ConstElementPtr v4pool = ctx->get("lw4over6-v4-pool");
  4190. ConstElementPtr exclude = ctx->get("lw4over6-sysports-exclude");
  4191. ConstElementPtr v6len = ctx->get("lw4over6-bind-prefix-len");
  4192. ASSERT_TRUE(ratio);
  4193. ASSERT_EQ(Element::integer, ratio->getType());
  4194. int64_t int_value;
  4195. EXPECT_NO_THROW(ratio->getValue(int_value));
  4196. EXPECT_EQ(64L, int_value);
  4197. ASSERT_TRUE(v4pool);
  4198. ASSERT_EQ(Element::string, v4pool->getType());
  4199. EXPECT_EQ("192.0.2.0/24", v4pool->stringValue());
  4200. ASSERT_TRUE(exclude);
  4201. bool bool_value;
  4202. ASSERT_EQ(Element::boolean, exclude->getType());
  4203. EXPECT_NO_THROW(exclude->getValue(bool_value));
  4204. EXPECT_EQ(true, bool_value);
  4205. ASSERT_TRUE(v6len);
  4206. ASSERT_EQ(Element::integer, v6len->getType());
  4207. EXPECT_NO_THROW(v6len->getValue(int_value));
  4208. EXPECT_EQ(56L, int_value);
  4209. }
  4210. };