BIND 10 trac3035, updated. c587763b64ff0e38bab72961ead737b5462ebcce [3035] Merge branch 'master' into trac3035

BIND 10 source code commits bind10-changes at lists.isc.org
Thu Nov 21 11:31:57 UTC 2013


The branch, trac3035 has been updated
       via  c587763b64ff0e38bab72961ead737b5462ebcce (commit)
       via  6443d1198b26c6fec9461301ff887258651fe423 (commit)
       via  4921d7de6b5623c7e85d2baf8bc978686877345b (commit)
       via  e03adbe6fe2a91adb5065396bb00aa16fa5fc45f (commit)
       via  e03cb3e3bc2bebf0924288afc7c62ab3382ec8d1 (commit)
       via  e144ab7b4fa2cbaa258849b8f2d31746326a43ea (commit)
       via  070f123aef2d20da364f3f12d57e9be4e070d854 (commit)
       via  7e75d22f278f460b7b61470c62063b345a1f436c (commit)
       via  ad5750db8c708d8849695dec43ff79e64a256078 (commit)
       via  32e3681b8ed317fbf9e3e195a854a0830fccbb08 (commit)
       via  578392d0e3f5fd8abbce315321d3ec270d283eb1 (commit)
       via  d6d7f7d201eb0dcfbcfda4e8589b556b596e826b (commit)
       via  61f52a04ef4df1c8ba17ebf75baaa25e88b90342 (commit)
       via  4655c110afa0ec6f5669bf53245bffe6b30ece4b (commit)
       via  e4475a2cd90aa4247482b1a4b96ec3dac21b802b (commit)
       via  2696320ef2eda1e405748b89b854ac517fa3506c (commit)
       via  dfd3f41f975a491ae9f49df3c1ff8d276e99df7e (commit)
       via  d2f1cc6e6d1d29b33f8885cc2e7b2c1e46d62456 (commit)
       via  66518462614dd93c7a0ca3111940952d171fbef9 (commit)
       via  da04b4807344089a3f6e008cf7a448cb4b4ad1d9 (commit)
       via  ccb253d801db7496dfc6a93a90db4a23cf2300ae (commit)
       via  f8fac8823dd928e2b5d6f4bf7bb31ca992dc0562 (commit)
       via  c18a49b0435c656669e6f87ef65d44dc98e0e726 (commit)
       via  495195f672876310d4d10a997b0c2ac6c3c23117 (commit)
       via  b756633d52f492b02efce5303432f9ec97646389 (commit)
       via  75867fdfdc6e53045e934d597bc601fca2caf204 (commit)
       via  66f2939830926f4337623b159210103b5a8e2434 (commit)
       via  445a1ea248aee12202c7e076f7b85192384f57fd (commit)
       via  64ae9936d165c0e070bdf17809971918c29ee838 (commit)
       via  ab5d8f7f2ce5d95cd09ee0d7ad6999ed7b2c09b4 (commit)
       via  ed43618a2c7b2387d76f99a5a4b1a3e05ac70f5e (commit)
       via  fab2fd65c02fe2e215a004b094916c449f6d994c (commit)
       via  2dbe0c8aa52634f064b285c8133255abd28cf324 (commit)
       via  243ded15bbed0d35e230d00f4e3ee42c3609616c (commit)
       via  5bb29d3c8d9f318fd4242e3c5a34a59b70964311 (commit)
       via  276e655555df9302ba56ea5520a5713d1915bd7f (commit)
       via  db31405ea79f587bb7a1ec9b94c02edacb758271 (commit)
       via  476e1924b7fe11c3a46408df4aede27e84f2c225 (commit)
       via  1d28cc4d6ad1cc69ba00cc4e1d6822092a259e63 (commit)
       via  6f6251bbd761809634aa470f36480d046b4d2a20 (commit)
       via  ff2b7624b7d1bba8ce38c85018b625aecca937ab (commit)
       via  bc5486ec4a8bffd32dd161d7238177c4a50fc94e (commit)
       via  e065bd5759ab046d5dc4ce071a8424db8bf975e7 (commit)
       via  925a6f65ff995b19c877f064bdfdfb846d4c5b06 (commit)
       via  83950286387c7e41661c810dc173171e8b9e1fd9 (commit)
       via  37f108c1fb43466d8ca55ee3175a938281eb99ce (commit)
       via  2b15e02a8b27e4a03ef6bdd2826acc44a50a5f0d (commit)
       via  d67f893b43f1c293d7342dbd02a6cbf044d3a201 (commit)
       via  faea98cbf4274229b207b20adbb97524a5d3e748 (commit)
       via  7f26bb20177605c9826f85ebecda30cb1ceaa0e8 (commit)
       via  2e283ec70fa4c751835e1afcba65c4554fd78d15 (commit)
       via  9dc05967b079038d66d4a79ba607ac553500e23b (commit)
       via  4cad8855373838b302370a00adee1501ee080d04 (commit)
       via  95fb9fb9e6a8a45b74169ece6c0ab8b092b85b4a (commit)
       via  748d5e585d6c4bcd5bb18498f768ad70f7e3bf0f (commit)
       via  7fe44b1d9400284a0bd22cac9d197bf0776cce1d (commit)
       via  ed672a898d28d6249ff0c96df12384b0aee403c8 (commit)
       via  3cbdec67c78ea16cf26742a3865046332774d2d7 (commit)
       via  f3f429b0e05eac677146e0419b50bc7704d1eb73 (commit)
       via  ea8ba75f55dceb13773d98a5836d075e231b2b6f (commit)
       via  4b0a0a96a7ac23db5d1c07c3e9511574e5d49c57 (commit)
       via  4ba8271b4050f3133dd6ca53721c0ce32ec715a3 (commit)
       via  639db0b57ac5c0d24c1cd021d42385f03d7ce818 (commit)
       via  f36aab92c85498f8511fbbe19fad5e3f787aef68 (commit)
       via  2c28d84f3657c10e6520c413705f532c0b42b1c8 (commit)
       via  99db31bc3494aece5861df6c64b821564cbb9348 (commit)
       via  541922b5300904a5de2eaeddc3666fc4b654ffba (commit)
       via  ea193899c17695b2a626cd5fe733506518eedcc0 (commit)
       via  d7460a8c4d81e973a45b70c27676d87b1a0523d3 (commit)
       via  3d320e51a5518a6bb3578ff8f804ac1479aee9b6 (commit)
       via  50d91e4c069c6de13680bfaaee3c56b68d6e4ab1 (commit)
       via  1a0dd0c77ad728edfd20bd329db0019f87fb64a4 (commit)
       via  c1ac5b336c1b1be2aa41e4202e997d7a57532935 (commit)
       via  72e601f2a57ab70b25d50877c8e49242739d1c9f (commit)
       via  45eb1980e4238717e3aee354587eb857759d74e8 (commit)
       via  c936de6a262f1509d14c3ce457c5c0b27e86a4e2 (commit)
       via  e135eb799810f5671b622a2e5d5e4bedf545c049 (commit)
       via  6ff2610c28228d3a91b428b178517ff2bb7fbe69 (commit)
       via  d8d20c9781e6a644c85339b966c5c6a31ad24a59 (commit)
       via  1bbab8c3c1d12e1a7aaa2e2fc36ab4fc5653c92e (commit)
       via  0764a74e461d71c8d3dcb25dc2399a4d18d553f8 (commit)
       via  e7fff7124061a6fb64dfb03d3e652de8b883305c (commit)
       via  d1397da715c4c2fd9039923250f380e3f1172fb3 (commit)
       via  78cb0f4ff0cfba61cf775c178632131f21432304 (commit)
       via  6e171110c4dd9ae3b1be828b9516efc65c33460b (commit)
       via  7f5a921d7005d7eb4d955a63321ffba7cb8cd995 (commit)
       via  4707a2dbce19333a1c6cb1bc9020ef2051eca1fe (commit)
       via  5baa1aeb9435170663bcce936e53fbac6d55eef8 (commit)
       via  9a9b9d8e466efb4a3a2e363a65c84ac0a37f2226 (commit)
       via  e9f4d7e510ae5392cfa25c40c5aebe1a49222c5b (commit)
       via  a1b91fbe2702b3fb60ac639f3ce7286c525c739a (commit)
       via  beac4c1be1f4063d1b8420d0241f5f384564bba3 (commit)
       via  5b11136c2a87ccac0c1ef100868dc647ea1feedb (commit)
       via  33026699dd29fd15841fb860d35b50a8c7114c26 (commit)
       via  732d98acba64fee4421a440b349069f10856451b (commit)
       via  28c612e504efd4f340fa1d9929ea17cd75565a67 (commit)
       via  7abead307c37c5d1f2a3ef3540e0648985997297 (commit)
       via  e76a6be3b3ffbc619ddb36b56e2a30d998ac9c36 (commit)
       via  358735b6bc9fe4caf2b12466e54a90eb69a4e673 (commit)
       via  07a72f62c2e93585de92d032b5af56c9a2ca562b (commit)
       via  ebeb291e7a086a17a6fcddb1abcab583dc59b46c (commit)
       via  a27c2a6a18ec14e5c79eae56beb238044ecb87d2 (commit)
       via  bd647c2a9820ecd3fc62852c42797265eca26ab1 (commit)
       via  e3683e45ff2288d6b1307a0c9d15271df9d0c722 (commit)
       via  4d71b071d1feafe88cc3756fd4e678301e5f0b70 (commit)
       via  770f4dfb5ac6abd620a01215ed85c1719b946f11 (commit)
       via  426203928242be40458c4e2ea861257e32424b14 (commit)
       via  f8487b5689486f3563f6b7546ecd867f473d74b1 (commit)
       via  11ec18030d858c037a862cc806a0329d3271d53a (commit)
       via  c95421cd2f4719b166700bac51361254483787ef (commit)
       via  6291862d54c72d64178fe3c5f5b489120e297e12 (commit)
       via  14918661a10e2f9c3367f668261656db1ee4bfed (commit)
       via  e98ccfe63db14be0d9a027797ffd75885b296969 (commit)
       via  f296e302ed073dadc5b03bc4edaff5a7da07376a (commit)
       via  f0edd52c244689cd370853d520bc6e97d23a3bb2 (commit)
       via  a9ec411b9522c9571f87afc39009bb7201be735f (commit)
       via  bf773a093fd8e7da0c93816fe3a9a744b689dd8c (commit)
       via  5eede0e0f040d932d1a4f4e93d46afcd39b91cd7 (commit)
       via  287389c049518bff66bdf6a5a49bb8768be02d8e (commit)
       via  09695b7ff9631f67e247fa43f89a6653635dd8fa (commit)
       via  b080f05c590ce7e24c38db62194c85889a321ae1 (commit)
       via  f73fba3cde9421acbeb9486c615900b0af58fa25 (commit)
       via  e01ba2041d11147012551c1434deb753c75a9b04 (commit)
       via  77c44b84587d190af775c8ab1d06e77857544d75 (commit)
       via  62e07f67a8f462d7e7a1f446376cea3650827973 (commit)
       via  4c0ef14158d57433edb9ae8aa1e2b0237773673b (commit)
       via  de9b4b42461156523d406375064d5c34d3d68b0d (commit)
       via  87b3de01a1d6d49443831f1fd77f1d5e8ef91f7b (commit)
       via  3207932815f58045acea84ae092e0a5aa7c4bfd7 (commit)
       via  6489d09af85dddff538944f6efab14783a46fe9c (commit)
       via  2838c0769e219dbd2c8a479efe87f9bfdb4da8e9 (commit)
       via  f1094390dc096d25df6465d737974a5d6f82e6ea (commit)
       via  d130980f515cc5cfab65df8dd0944b56d5fbcdea (commit)
       via  2d83a3a525c255b565aa57e3bd5efce5ca38a269 (commit)
       via  1644a516022739e5213b5c400ca240d354afda82 (commit)
       via  48f0f8bb242ebcb0f7f4402c6bcf90bfb2650a12 (commit)
       via  36eabc93eddd0eaf89ba2a5c29cf802bab4ad9e0 (commit)
       via  708a8dd0257f40c076f3a3db58502dacf26ff020 (commit)
       via  8e5d94583bc9da8374341509fbf0260d7a53edc2 (commit)
       via  33a9204a11501ee3289161543e5abcacf042a504 (commit)
       via  2115ed0befc5e58bc17960bfb89f5d209c2f9d71 (commit)
       via  dab092748d4083472373516cd4d576b0158aceeb (commit)
       via  c71753a27a64d409f28629f308dfe9f7afb7e0ae (commit)
       via  79569d47bdbece10f1ee4532f33ec23d4c8e00b4 (commit)
       via  4386caec66f57272c1239a3134dc8a0add870506 (commit)
       via  0d62790c36094e5789cdb7ff713e8e58ba9326fd (commit)
       via  235246a1c1aaf52a976896ab1bfc0df551abdb5a (commit)
       via  f42fbaf960614930dc0c23591e430411a9417624 (commit)
       via  4527401f4afbd8f7a9dafb7296863d01da0d8c09 (commit)
       via  2c20227ed4c9f1ccb5184e8c9698dcf7a7e79c4d (commit)
       via  a9cd7cf196ad37ab7a31bc2a26722d660078676e (commit)
       via  be15d120159f1b7380f88707efb05c4b11b121d4 (commit)
       via  6b33de4bea92eecb64b6c673bf1b8ae51f8edcf1 (commit)
       via  a0e73dd74658f2deb22fad2c7a1f56d122aa9021 (commit)
       via  e43c23bc2d1006c2abb5dd7df7ed575cfce00451 (commit)
       via  ff3ddd8332fbaab5ff76f140ae9f185a2d2d6e34 (commit)
       via  9cb427b4940ed7531facf3420f3bdd94afc37f64 (commit)
       via  0fcc98b098d285dddcbdc73a06c38aeb826cb16a (commit)
       via  b054ce924f26fba021c4ab86bd663d9acaa6ae34 (commit)
       via  988ee2ace62b93b41fd5261e9b27ed39335192ea (commit)
       via  d06b0d68f9d91b363183fe86a3d8ce9e46eb37f7 (commit)
       via  4da14c5ca0e659263f2abd8bcee8a79ad79e3722 (commit)
       via  daf53e75f5ff4b9019b3859f7989ea37c2c39053 (commit)
       via  bec7f21950fde94b6c845a23bc5202b6bc2232c1 (commit)
       via  a715f62a23e400828ce9ef9072e7690fac016551 (commit)
       via  361505f1c009d75af0c6a189d9433fda12d2a157 (commit)
       via  7f464b31a95cefc4cad54aad92b8a94d5f557dc7 (commit)
       via  9b7cc4afda911d762394c8e432fc429d027a106c (commit)
       via  b107f99d0ae8379ad635e7017d6bccb7d1c50882 (commit)
       via  7d1431b4c887f0c7ee1b26b9b82d3d3b8464b34f (commit)
       via  15c8b6ac63fd8f81b085fec8a0fd6d22085ea898 (commit)
       via  7ae935a62c0f2fb0638cfee0d53c6b1fd63dfceb (commit)
       via  b825c117ad54e0503e5629d74dd86d8f41102d60 (commit)
       via  4b8f75a145fbdd8006363c36ee32a140fd33ead9 (commit)
       via  a019cd162f1ad9584dcc67732ee545c6395bebb4 (commit)
       via  b48106123d817b95de69c2d58aeb24ef26a186e7 (commit)
       via  0d5c7903522b79906cb0335006d678911af1eee8 (commit)
       via  23c944640d1b697af40582f7999b40b05b17a44a (commit)
       via  70d44604584934a651d1b3d975a072513aa010ed (commit)
       via  eee878e7817bcdc270ff56867dc0671f1d223e31 (commit)
       via  acbe4afb25b5f7eb6da8e259d9d8a78526bccd80 (commit)
       via  582bafeae092fe5a1e67b03a0fcd74eba34dfe7f (commit)
       via  3d19eee4dbfabc7cf7ae528351ee9e3a334cae92 (commit)
       via  bb0ac07b4621308695146918e37d4f2a68e2a9bd (commit)
       via  d3dbe8e1643358d4f88cdbb7a16a32fd384b85b1 (commit)
       via  6f1227c27cded2a283ea3ec78e7497a825fdc005 (commit)
       via  e3f9c480c13dda60f83105377ced9f3255f9df22 (commit)
       via  ca691626a2be16f08754177bb27983a9f4984702 (commit)
       via  698682948e932cfacb1c8a7f3c068dd5f9ffd050 (commit)
       via  b17b792e4a1015e301327296b1c9f466d2cb793f (commit)
       via  79a22be33825bafa1a0cdfa24d5cb751ab1ae2d3 (commit)
       via  aa37f6861c45ad16577f327441f1dc2b546e258f (commit)
       via  4a4a6098ba06773512cb4e20033b58c8353c8201 (commit)
       via  1ef8a393e8257105a66a028c7dbc4141309139dc (commit)
       via  b5f8586097eb7bb1961fce2c767a71f534923d2b (commit)
       via  66158f78882b882fb8fcc1be29e2969b0dbae75b (commit)
       via  2e425ff153d59bf8f551c0aec716dce8e2d53996 (commit)
       via  4cc844f7cc82c8bd749296a2709ef67af8d9ba87 (commit)
       via  6660c4b27472285c5876eabf25ecc5b093861371 (commit)
       via  57d3634c3d37f204f7cce1c62313036cd023faf0 (commit)
       via  d84999478ba92ffc94741716d3b6ae8cea91f3fc (commit)
       via  6e9227b1b15448e834d1f60dd655e5633ff9745c (commit)
       via  a617211d81b31c5e72f73ed6043d183be61679cf (commit)
       via  e09660860573d1b76c65a200d3682d3592406b67 (commit)
       via  c83724e334548f586781ee77813348c75c111836 (commit)
       via  f67708b12cc011dcaa6a6352792b42f154527bec (commit)
       via  b2686c2da77de241fbc4bc0f2d642daaf3c9d402 (commit)
       via  e898091a969a7f4d6481be297d1af3676480069a (commit)
       via  82a00e6ef79987b9de80c0b63950a8389592b3dc (commit)
       via  4e7c1fb3533a53bd6cd21b563553f1e40f9bdf30 (commit)
       via  97c7bb8690967c253151462c72af68ec64da3bea (commit)
       via  33b1dda3335b747ca6fa0fb3fa421b50de867ce3 (commit)
       via  ef4ae0b32f759f25ba8fc20f1213406115fc6502 (commit)
       via  646763a3166e60b9a8920100b5ee1d02d10217c5 (commit)
       via  dcaf9bfe96a40471b82b2f97455c57828460fdd2 (commit)
       via  b6c658d2fd1bc056f66beca05ae11162a14afe09 (commit)
       via  7509a8d1bc64d4f688f6746d195987e5e22d4933 (commit)
       via  cbda6551031656b6d03f7d5578d0389c562a4238 (commit)
       via  d3f0e486be9ee295d1556ccb2afe11e843e16009 (commit)
       via  a282ac7153171ac4445c6b6eb2220937d097fd74 (commit)
       via  8ed4c169acd688c7d9389991272c2e699a4dd8fc (commit)
       via  242628300d9d7879e1dd90b2020f20274ee3bcf3 (commit)
       via  4e9470edf1e15d65d8291fa6e33118cc92f70086 (commit)
       via  08f0151628c90ba7c70412414dd0dd1019b708b8 (commit)
       via  3e08c03f168623554f4dac564cbfd4bc5160bf76 (commit)
       via  4b7664ccfdf1640cbdd914ddd032989149a3f0ef (commit)
       via  e295438305b351d4959001113670c6455f4a0abb (commit)
       via  86050220a664a736659cce5d32b44a8d58914082 (commit)
       via  7759a99d23a5d3246a160ad4363c33edfda2ef74 (commit)
       via  f8bbd7fa7def41d09c458282aadbbd2ca352f3a6 (commit)
       via  e33d14843af79aa49e86fe4f13b8e253fcbe7fb2 (commit)
       via  511e9c6eca07d842e90f080663d9a3ecadb430c2 (commit)
       via  7aeeab642dd16c978464a944fdc732246431a579 (commit)
       via  8842945c12181ee427f7dfc1e7c1e1032f9cc25f (commit)
       via  d7d74b349336faa877191ed4cc9f157858325596 (commit)
       via  b190045d32b46b927e40146ecb3b0af56d21e586 (commit)
       via  a26a75c9487a45b3d6d73abf41590ae379c811df (commit)
       via  7c0211c80bdf3cea4aad09a113e3afa513c55b90 (commit)
       via  1702164569ed176fdd638ddbd47f0d1f4a8f7932 (commit)
       via  65b6372b783cb1361fd56efe2b3247bfdbdc47ea (commit)
       via  ab67242d4cf7ae3bbdb7d0a57f55b44350cfc2fc (commit)
       via  e05189ee6561d06753b3bb2c8c6e4fad7c005625 (commit)
       via  08bc7db55cafdd916ae227ff7ca524542de280f4 (commit)
       via  eaff454676513de3bf85d76c9ab724cf8191c649 (commit)
       via  a0aaef8644a889594af32cd5a3898a63a225b7cb (commit)
       via  923a40c23023361591077e0560719a55f8e697e6 (commit)
       via  bd9f2d3c9e45d945b8e6ad9db79f93954ee0dcf9 (commit)
       via  52d44bc5004ae501ca039896db099b734781f00b (commit)
       via  74e7cfcd290d32ca6cfe2eccf959c796639a22cb (commit)
       via  f69dc1f6a5f9f350b8183f3969e43462182d0f92 (commit)
       via  b1217e691027f310f47bb7b23b621d5b8656ea6c (commit)
       via  7430591b4ae4c7052cab86ed17d0221db3b524a8 (commit)
       via  4bee63eec6164380cc042de3405102a6e4a74b67 (commit)
       via  807d78bb19ec4df52474e91831c4b670c76db043 (commit)
       via  43c722563552989ee5f7adfe99b0ba75bb56bb87 (commit)
       via  e6f0e89162bac0adae3ce3141437a282d5183162 (commit)
       via  dd4f121644d51c4bf1b943dda702bd72482eb090 (commit)
       via  9b98a0122bc1cb39afc90fdece0cd63616ae4cc5 (commit)
       via  e4fe1a798cb7d56561c992662d84a2be46af8ae3 (commit)
       via  c543008573eba65567e9c189824322954c6dd43b (commit)
       via  cc480a64481dbce14348038cfa254578fc51623b (commit)
       via  ecc58c5e3a9e10f06357fcffaf169568b507901f (commit)
       via  eeacc3ac637e400c5f1512fc434ab08523521e2e (commit)
       via  c37f3b910e63df55b46516943a7e2ffd51e14e5c (commit)
       via  22b8b335e30ab84bdbac60244d74553e5bf3b719 (commit)
       via  68fc48d1f7b01e65aeaadeeffb08785899b023af (commit)
       via  f6619e644f62317afb4259e2af7732caa9d10c25 (commit)
       via  6d99f3d904bb5a9688e6d95bedc9b0bc4c3a0e27 (commit)
       via  37069dfbb56bd41717c439339668807a28ca9be0 (commit)
       via  04eb5cebe1ab646507b4dab3964a87e730a79646 (commit)
       via  8d746f00a3e7014d78502b3744a426d54fc0e3ef (commit)
       via  18cf54ed89dee1dd1847053c5210f0ca220590c2 (commit)
       via  720c9d6f11290c69b8dfc6e99b3de3938286ceb4 (commit)
       via  2c152e0bf5106716d3360dcc549b3484945872b9 (commit)
       via  91c0c0605d92d36bf5e51a3b41548455542a26e3 (commit)
       via  928a9b6e56930f055d45f14f6f8d199e3470558d (commit)
       via  339f201e9a6c58980f8cc9e6577e80254f04e951 (commit)
       via  8b88f186238c9dc920a3a4dac668c8e5bf68927b (commit)
       via  b1e65b902324572544555903b45bc63ffcefcd2b (commit)
       via  c5c85a0d56fc30c9c0fc3b7040306360c961350b (commit)
       via  733a15db3855503efe1b5e0b89d5558015f67a86 (commit)
       via  384b365bc6895e251876cfbd440bee03076a87de (commit)
       via  fa1c3b6a4edfc169a86410ccf3b35263c6cb8d4a (commit)
       via  6b8e42d064b94f40f2c050705e0724093ab00c9f (commit)
       via  ad88197cef83ed46a3255fe5948a233efacb7402 (commit)
       via  faa4cc45bb9721a6ede085bdbaaeef7d3c69b3f9 (commit)
       via  4cfee008f5fff875b90416b3318f4b8e63b8d7e3 (commit)
       via  b1274061b9157ca486c9406422de7b913b904856 (commit)
       via  223d4a422bf123539160029f40e0a0fdfeaac8b0 (commit)
       via  7a9adddb3ba72d3b9e294d99b304d506b5a247d3 (commit)
       via  cca46d0df7a4c3008e83d84e863a9be877f42b12 (commit)
       via  d8a6c3dd5baed22bb4b1b8729ffc9ddcece48924 (commit)
       via  05a05d810be754e7a4d8ca181550867febf6dcc6 (commit)
       via  0e4c259dde279eafa1e3677a161ac9b67095c3d7 (commit)
       via  27e895a6ee41d4588f6822fc7aa9dd5eac3c5738 (commit)
       via  63b99b7f1c5d80b78da7d526f883d3b53fb116c4 (commit)
       via  bf6f8ee24cd9fee7c642114d3f1ca083625d6d9d (commit)
       via  154fbc8f1d2a8de45de50799228a52585726d823 (commit)
       via  48b5e32efb89d2c07b40c0d3e74200b56d984f75 (commit)
       via  7edd26567ac96016c1caad9b7229d75b404f95be (commit)
       via  e3454067b2f7f83723befe5728b984e510593d0d (commit)
       via  534a84a7d3f35a11204966317724bb8327a36282 (commit)
       via  3a844e85ecc3067ccd1c01841f4a61366cb278f4 (commit)
       via  9d3952fc9739348ccc34694c36036d7d46a5e273 (commit)
       via  537e404c74470a717a4044260d7076b4b1a805ce (commit)
       via  660f725b6fb6b326668d7c3c4395ec90b018535d (commit)
       via  8f3591204bb394d7b2aa00508e99ec71a5720a8b (commit)
       via  494ec81f8f76dc62a1ea51f1719ab0fb8fdc008c (commit)
       via  b5fe9ef4194ec0b3c2cec527834e27e3377d903e (commit)
       via  d21b3e92d6825acb41512747991b94f145d66199 (commit)
       via  9e3e043fd5b34eaf866bd590fae3a3a252ac2bff (commit)
       via  390b118ab527c2671062d52100137da674ced5c5 (commit)
       via  6a7aa9cb237b629b548bbf2d8c4041c8ec2b9a02 (commit)
       via  ea2cd98c25c7d51b4116203038105bc88a10ecab (commit)
       via  9273dca334ac61f89578b1b318b794325ced97d6 (commit)
       via  346892cca6784b947e11efb446f53676b143e4d6 (commit)
       via  0b0bb4a2785056f705dd7de8e5e9e30bc0eff80a (commit)
       via  abd29c04e21cac8115834506c2d9e44f69a4d716 (commit)
       via  681def2806a3296aacab813882712da9109ba72d (commit)
       via  bc9c1208f3fdb1e0bedb850be56940048cf0b915 (commit)
       via  079b862c9eb21056fdf957e560b8fe7b218441b6 (commit)
       via  351b73dcc7a084622b91868cbab14ad2b2d93469 (commit)
       via  95729b096ebac05beac6b42752b4734f4ca73028 (commit)
       via  2093c5f82308d58537cb01130a2e54109cfbd6d7 (commit)
       via  d5d77acc2f46862e365fb42ae983158bd47eee9b (commit)
       via  d79db92249d8e529bd8346cffbaf5c19680923f5 (commit)
       via  3984b23014333a678d3c78f8253312bede64a213 (commit)
       via  0edf1c3dd2e12a28ecb74e47bdc969d6dc15fcd4 (commit)
       via  9eeab2e6b02fcf316b81bcc72c0dadef84b9a07d (commit)
       via  63a7f8bf4e61b69fd29a189f9bc683b5de0ce57b (commit)
       via  8090d2c5186a96d80378072761625da2d8d421b0 (commit)
       via  05b3886971fb003b0bbd735975bbbf0c38a1970c (commit)
       via  151a2b857e77b32b10c007e9a37f37ddcfb03b8b (commit)
       via  a47c17e54da2b08afb9dae8f46a97ac3a5e5bda0 (commit)
       via  612d8e5a1a35b9e359d610654c86b9b6fd1246cc (commit)
       via  617c1d7c950beb8ddceb2ab03f37dca55959deb6 (commit)
       via  8e64776206cf6845c938ddfb1ad76c7d6a61377e (commit)
       via  3d4ea5002959fd0510241f2c3550e90d9899fcee (commit)
       via  06091ef843f52f38d0d13eb58bd1400f637ee96b (commit)
       via  ccb473862e3046f9cf0dcc923757a4ada7ba093f (commit)
       via  9b97fd86866e26db1da44237352f5c9312d79291 (commit)
       via  a0f6b0ff5fcf50bc6fef4b12615c59656242c246 (commit)
       via  b3172918d50077b640796787e22c908f8f6580c4 (commit)
       via  44af79cc0dfe2078bd7f85ffd555293935c8d659 (commit)
       via  91eca98edcc0af04cbfa27cb684dd3ed680565b9 (commit)
       via  a50e4235990f2b1b7090092e5fb19c7662480dd3 (commit)
       via  663bc38be6ba6371ac5d2c0e4e2db845fd384037 (commit)
       via  49c71998445264c8882c3b8389edd4bbd74c22f1 (commit)
       via  a11683be53db2f9f8f9b71c1d1c163511e0319b3 (commit)
       via  4c18226299237919f351e50742032bd4c6b953b6 (commit)
       via  92adc1fb42e8966816f96b4e6521bd2a18cead1f (commit)
       via  302a01c6707d2f46d9c4077eb9fb726533fad50c (commit)
       via  0a5bd2ebb43c45ebab9f466d102a3c9e4c024efa (commit)
       via  8380acd2fcbd511f0a72f1854e19baa874e34175 (commit)
       via  c0e91c529e7e110f68c30c127a8370643a1a9b78 (commit)
       via  b30ae178084dbfda0da6ccfee9f8d1e27c926b0d (commit)
       via  e2cb0a39c9819482df8e7cd32ba37ed405d715fd (commit)
       via  63e54b17e5b43ee480cb83a4fa0fa6ba08e2ccfa (commit)
       via  da0e9a81e8a4e4fac77caf257607bf4fe1bb0f0d (commit)
       via  b403553a567421006bccbf4b4f598b7aca738d54 (commit)
       via  066f45d1ddf9f6998904cfa8402ea0d3a3f287f8 (commit)
       via  49fa0bad8fa7bcc0355acadede93b9f3ce679f14 (commit)
       via  7cf392993b65c0b69443f1e749ae067c3d6f142c (commit)
       via  0467d75d487b5c7d559ccf9a73e9fe105cd485cd (commit)
       via  6024678d9b397613cf8f9b5454b14f9b98e1b176 (commit)
       via  967e79e9748de98d874df011184c03bf27c5e8dd (commit)
       via  ec1cb0b7b54e161404079ca63124aa85d1553104 (commit)
       via  1d38fe32f5f891958d302b46044cd961a074ec41 (commit)
       via  4ee1f3305b8704b7c5da130e163285bbfcbf36d7 (commit)
       via  d15a492eb3479310af51cb1a9dde60faa1d57249 (commit)
       via  313c564232efbcfc2cc8d34593ce9494ba96d629 (commit)
       via  8a4c8c4fec6d869d9ecbbbab9c0374658cfd7a99 (commit)
       via  c812d223a1a93535f29ede6a082ea96fc1f928fc (commit)
       via  128be57f78800c62ca957cd14350bac9627782e0 (commit)
       via  bb33775113793e5457116033bfafa8f97dd27d18 (commit)
       via  091b8584615ed5da2441c579bc37cf1e9681db80 (commit)
       via  5420c8bb51bd1a054df1962f86b34b6b0d4f4eb4 (commit)
       via  ba447c41b65a93c34f12fcb30f07352e12b16793 (commit)
       via  79b7d8ee017b57a81cec5099bc028e1494d7e2e9 (commit)
       via  2a07912bb6cb95f4df5caffdc6df4b07219b5dc3 (commit)
       via  275d63283fe322e93ede93045b6ad870a6eaac68 (commit)
       via  23078e9f9718a8dd92180f15a419ef2db1e4c69b (commit)
       via  73d7a9538986f78b96584f5f9ce4946f86f05536 (commit)
       via  927411d1d221265f82608e62a22683c203103209 (commit)
       via  b6ca9a816726b573004d1a9140ad38522bec50f8 (commit)
       via  38d091beb6348ef3f74175625ffef8348e39c51c (commit)
       via  e66020fdd4fc764996e892728dcd098a2121cf62 (commit)
       via  9da302197f39067d372cee065894e21303ad1034 (commit)
       via  7feab5bbe65b961ba416e21a280b442246666a07 (commit)
       via  46b7d9d9e329ea3ed9a338924e0cec4e3c83ee04 (commit)
       via  5b63f9693efd47c600139b8522c37ea2f01666a0 (commit)
       via  fb86d9c79d26bbaf2d5f54046b3d8b653f34bef4 (commit)
       via  1e6cf587beb55cb81fd4bf3614729cc72bd0c906 (commit)
       via  d9b6d7856906e61924a292f83dc2acb147386900 (commit)
       via  ddbff8acbadb104d53aa001deed5ad2777f368d2 (commit)
       via  a3f0255a3b6696ec8d2f21b78366ec44d13374d7 (commit)
       via  bd154b0950fd08ea60336c73249bc8b0de57779d (commit)
       via  3ae989196066a542911ea9bd3ead0159fa8fa488 (commit)
       via  d03eba7fae0715c7122f46d283cc9962404e0e6e (commit)
       via  4798d92c50c4b6ec09931646f1ba823882b268d2 (commit)
       via  8829d940017a15fc738f2ebf6dfb3c8da61c54cc (commit)
       via  bf298463e636da9989a877145fee8853733f7030 (commit)
       via  8a6e0a611829250157c74d6fe8a329629d66b537 (commit)
       via  65d3cd24fbee716e9e331a9f4541e34341e5e15b (commit)
       via  82fded3b3266bfe0c30dc89572b716898b5aaf8e (commit)
       via  4d5859d8e390631a868339c2d435916e75112674 (commit)
       via  fd0ef5a296cab4d4d7417c3d3ce0110ec21bba06 (commit)
       via  99b2d6f764a574071486cadf72e4eee3d3054ced (commit)
       via  31e8258bbee3b576531a1fcf762c3a1f61e27785 (commit)
       via  b86a58b5f172fa9cc9e6e47c647b7acafdb6c0a7 (commit)
       via  65052ea1fd8bb6b8a78b10743527ad331f1fa149 (commit)
       via  8465110adc375f4605dc0f6b88150306c2ab929c (commit)
       via  499e6f2e3f5bd141459ba46f90359d9dbc72c125 (commit)
       via  dc10e21d3ace93577e654885981ace4cae92ee09 (commit)
       via  90b12d797d33c23ec96cc877ecf4c84a42f6214a (commit)
       via  f7a34944f319ebffaced2e1ea8a7dd1c420a4c99 (commit)
       via  d9ee65e374924d0f1cde75f4aa9c242218d1ad85 (commit)
       via  d936a170390f87dca5f73751c7f61891a3f4c4c1 (commit)
       via  c33aa974e86c6f63e062e562fc0196a9de0ae23a (commit)
       via  1d32d636d7f81e1a5385a9107fb7da355aa26c08 (commit)
       via  b27c26f22c147e9bfe4eaf411ce2247f5a5218e9 (commit)
       via  9801de556f82d5c6156d120a2d70a23487524044 (commit)
       via  c542421c3e40b6417862a07a3e0b5d3fb46f89f9 (commit)
       via  715419df2abeba0aeea9664b3a4b832c669f84a2 (commit)
       via  8fbdef450e15140f9a9544c2f5ee3630fdf229fe (commit)
       via  f3c1bcd7bec13a0ff7246a66282bbd96bf032687 (commit)
       via  73fa815876d3f6aa20e4961e51839af26d74d4c0 (commit)
       via  a2bc0e20d045d1009438c4c1565689931d66b213 (commit)
       via  7dfb7839854252b4b6ec9ac626af0e4f2066d919 (commit)
       via  f09a4f68f8397b150a94b6f9b78da60b117a8fe1 (commit)
       via  5a860a011511c6977f354f2d74eef46e6d970896 (commit)
       via  8ce213f2e0eb1cfd313897288e08be41047cdbd7 (commit)
       via  50072746b495d5c14b473cb4466fc3b7fb6d87ea (commit)
       via  4b1b5fd99b7b11c8c77b1c36ef06cbe979f9c0f8 (commit)
       via  0207a121a00d0e419a6d067d75696a08b1d1e679 (commit)
       via  3669c95989ed36429f32a593d74f58dd917663b2 (commit)
       via  5207d193067cb884e6aa4ad63d1a96d52ef04f96 (commit)
       via  8bda455d6c105e505ed0d11223c708e2bd3b6f7a (commit)
       via  6be8194bd8c4dc90dc0b702ba1450e59184689a5 (commit)
       via  879bf7b43d45f1952473a5bb747f599ddb969bfd (commit)
       via  038082d3a8df1904538a11a6b803bec1804f9c8b (commit)
       via  5da41fa159bbf626a3c9acd0d4e49e96282d7589 (commit)
       via  918419085f722e43c567d4498e6ca581d925ce26 (commit)
       via  7b722a23d6a85ad0c3d903323e9a36ea2baefa0a (commit)
       via  0d893b54513dfba249cf209ac09b0271861437bd (commit)
       via  730aac841ed98bd35582d0a7c39a9b00835016a1 (commit)
       via  a5c4d0ad9dae4bceabac8b85f91ddbddb3ae6871 (commit)
       via  5ffd53da7b10bec4ade51ef974a9b9a23e1a0085 (commit)
       via  702505336da1317a8cfeeb5a04e8c5d1ac439469 (commit)
       via  7f0bb9294f71b325836f3c4fd8989b382627c19a (commit)
       via  c369c2c9a3bcb252843bd25680627484486b8802 (commit)
       via  3674f8287a482a77b9bc2f8a0f161ce2dc4e3f9f (commit)
       via  79c66d327a479ff5d6dca45c17e438a404bc9dec (commit)
       via  9b06e690a16a338fc943fd039d6cee03e5c9ca45 (commit)
       via  0b3041d1b0191e610a5ed3f8b4e93e846823041c (commit)
       via  36db9cbd518e50baabd62f645965d656c3cd7531 (commit)
       via  4d4b0dcd1dc2571a98b4e60bf846dd704ed7200d (commit)
       via  004adb24a8aac2527b2729aee398717b5ff505cd (commit)
       via  8ee0490dae879424b7e20fe1994ae4f2b294e3ef (commit)
       via  c3473496d36e6509c797fd6f55e9b9acd2ba8638 (commit)
       via  96ad742151cbbf6a21da7827f395a9365c18585d (commit)
       via  a3cb27a3800156227c362565a5778c1f3f9a9557 (commit)
       via  c7e1ff0338200957269ba996263aac8224987f62 (commit)
       via  1932ac5a9ffb435e2875f9e921beb486e2be8f4e (commit)
       via  c65345a08645b1a20f442c626f9498420d762c91 (commit)
       via  9136b0a06706f0ba4e7eb2207513930b14484400 (commit)
       via  7bf3c7b54ce1ad1ca86d3120c6cda7d39f909612 (commit)
       via  ad4c8681d9700216ccdcd347823c5131f2796d60 (commit)
       via  c185e035984740f3914a28f6a93f9d47ae8e75b2 (commit)
       via  09b659275c05d9bb215148b5c4976eed50ba4cba (commit)
       via  c276e3c22b719c6e604dc853309da85797b506c2 (commit)
       via  36a281824e92417f23c0ecbbc0c4a88a7e9182f3 (commit)
       via  d038572fe9fdd7dc3e05ed597df5297cb161cfd1 (commit)
       via  ea8b95396cb411187a22dc78fe460316ff2029ce (commit)
       via  08a44d8edc6c12dbbd54b9d8b68350732ed57668 (commit)
       via  21bc5facd59af83c64bea54baafa025e8ec63fa3 (commit)
       via  fe763709d177243f6bc22d849d6277423b71c32f (commit)
       via  71d687c3666c413e0460ee6d2b992d212547e01d (commit)
       via  68b075b70cd9b3570b81397d0be3ae27f3baa5ba (commit)
       via  ecd94090d4f063d308854041c4343775ab60108e (commit)
       via  3483361dfe0d9b0a4448fd7e53a7c3845e66be67 (commit)
       via  623eb1b10deb087d9d0dc6c6372140f09933f85f (commit)
       via  39db0756e576730953d3ff526c8c124103f5b5df (commit)
       via  b1a9280bd41ac9de63ebad586033f6ba1326b8b7 (commit)
       via  5498431f4deb81a175135d8334d5f6011455ea1f (commit)
       via  516bb5392ff827f17b7c91d42b6c4f3151a2bf44 (commit)
       via  f98caa5f314fd63d66e74461bbdd9b33910d850c (commit)
       via  516417bc1e769a0fb7e0d965ec9cfee51c342624 (commit)
       via  695d7c75d5b307bd0eebf042b78904396e3adfcf (commit)
       via  fafe5cb09e43b29a3b014a77bb0cf3c4c1ba47a9 (commit)
       via  88636c950bc80ce218aa5a98bb6cefc70b653fd2 (commit)
       via  81c1c17fd56ac7b087579ee46205349623f6be41 (commit)
       via  39588e5fbd781cec7344e07ddadda835aa74d8fb (commit)
       via  db29b326905b7915c5e1723b565900f06659eb30 (commit)
       via  0271260016349e40ec8d366b82077d93aac3ff47 (commit)
       via  e3dec9b39035a59bbdb561c12817872d66f34fbd (commit)
      from  d77a5642cc9696757b2559f53b103218b89e9bba (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit c587763b64ff0e38bab72961ead737b5462ebcce
Merge: d77a564 6443d11
Author: Marcin Siodelski <marcin at isc.org>
Date:   Thu Nov 21 12:31:27 2013 +0100

    [3035] Merge branch 'master' into trac3035
    
    Conflicts:
    	doc/devel/mainpage.dox
    	src/bin/dhcp4/dhcp4.dox
    	src/bin/dhcp4/dhcp4_srv.cc
    	src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
    	src/bin/dhcp4/tests/dhcp4_test_utils.h
    	src/lib/dhcp_ddns/tests/ncr_unittests.cc
    	src/lib/dhcpsrv/lease_mgr.cc
    	src/lib/dhcpsrv/lease_mgr.h
    	src/lib/dhcpsrv/tests/lease_mgr_unittest.cc

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                          |  208 +++-
 configure.ac                                       |  163 ++-
 doc/devel/mainpage.dox                             |    4 +-
 doc/guide/Makefile.am                              |    4 +-
 doc/guide/bind10-guide.xml                         |   90 +-
 ext/asio/README                                    |   11 +
 m4macros/ax_boost_for_bind10.m4                    |   14 +
 m4macros/ax_python_sqlite3.m4                      |   17 +
 m4macros/ax_sqlite3_for_bind10.m4                  |   21 +-
 src/Makefile.am                                    |    2 +
 src/bin/auth/Makefile.am                           |   14 +-
 src/bin/auth/query.cc                              |    2 +-
 src/bin/bind10/init.py.in                          |   32 +-
 src/bin/bind10/init_messages.mes                   |    7 +
 src/bin/bind10/tests/init_test.py.in               |    2 +-
 src/bin/bindctl/bindcmd.py                         |    5 +
 src/bin/bindctl/bindctl_main.py.in                 |    6 +-
 src/bin/cfgmgr/b10-cfgmgr.py.in                    |    3 +-
 src/bin/cmdctl/Makefile.am                         |    2 +-
 src/bin/cmdctl/cmdctl.py.in                        |    6 +-
 src/bin/d2/Makefile.am                             |   10 +-
 src/bin/d2/d2_cfg_mgr.h                            |    2 +-
 src/bin/d2/d2_messages.mes                         |    6 +
 src/bin/d2/d2_update_mgr.cc                        |    2 +-
 src/bin/d2/d2_update_mgr.h                         |   50 +-
 src/bin/d2/labeled_value.cc                        |  123 +++
 src/bin/d2/labeled_value.h                         |  184 ++++
 src/bin/d2/nc_trans.cc                             |  250 +++++
 src/bin/d2/nc_trans.h                              |  431 ++++++++
 src/bin/d2/state_model.cc                          |  380 +++++++
 src/bin/d2/state_model.h                           |  672 +++++++++++++
 src/bin/d2/tests/Makefile.am                       |    6 +
 src/bin/d2/tests/labeled_value_unittests.cc        |  107 ++
 src/bin/d2/tests/nc_trans_unittests.cc             |  599 +++++++++++
 src/bin/d2/tests/state_model_unittests.cc          |  839 ++++++++++++++++
 src/bin/dbutil/dbutil.py.in                        |    8 +-
 src/bin/ddns/ddns.py.in                            |    8 +-
 src/bin/ddns/ddns_messages.mes                     |    5 -
 src/bin/ddns/tests/ddns_test.py                    |    1 -
 src/bin/dhcp4/Makefile.am                          |    7 +-
 src/bin/dhcp4/config_parser.cc                     |   37 +-
 src/bin/dhcp4/dhcp4.dox                            |    9 +-
 src/bin/dhcp4/dhcp4.spec                           |   17 +-
 src/bin/dhcp4/dhcp4_messages.mes                   |    4 +-
 src/bin/dhcp4/dhcp4_srv.cc                         |  238 ++++-
 src/bin/dhcp4/dhcp4_srv.h                          |   24 +
 src/bin/dhcp4/tests/Makefile.am                    |   22 +-
 src/bin/dhcp4/tests/config_parser_unittest.cc      |  260 +++++
 src/bin/dhcp4/tests/dhcp4_srv_unittest.cc          |  598 ++++++++++-
 src/bin/dhcp4/tests/dhcp4_test_utils.cc            |  551 +++++++++++
 src/bin/dhcp4/tests/dhcp4_test_utils.h             |  689 ++++---------
 src/bin/dhcp4/tests/fqdn_unittest.cc               |    4 +-
 src/bin/dhcp4/tests/test_libraries.h.in            |   20 +-
 src/bin/dhcp4/tests/wireshark.cc                   |  134 +++
 src/bin/dhcp6/Makefile.am                          |    7 +-
 src/bin/dhcp6/config_parser.cc                     |  205 +++-
 src/bin/dhcp6/ctrl_dhcp6_srv.cc                    |    2 +-
 src/bin/dhcp6/dhcp6.dox                            |   23 +
 src/bin/dhcp6/dhcp6.spec                           |   36 +-
 src/bin/dhcp6/dhcp6_messages.mes                   |  176 +++-
 src/bin/dhcp6/dhcp6_srv.cc                         |  639 +++++++++++-
 src/bin/dhcp6/dhcp6_srv.h                          |   83 +-
 src/bin/dhcp6/tests/Makefile.am                    |   23 +-
 src/bin/dhcp6/tests/config_parser_unittest.cc      |  420 ++++++++
 src/bin/dhcp6/tests/dhcp6_srv_unittest.cc          |  953 +++++++++++-------
 src/bin/dhcp6/tests/dhcp6_test_utils.cc            |  557 +++++++++++
 src/bin/dhcp6/tests/dhcp6_test_utils.h             |  208 ++--
 src/bin/dhcp6/tests/hooks_unittest.cc              |   91 +-
 .../dhcp6/tests/test_data_files_config.h.in}       |   14 +-
 src/bin/dhcp6/tests/test_libraries.h.in            |   22 +-
 src/bin/dhcp6/tests/wireshark.cc                   |  163 +++
 src/bin/loadzone/loadzone.py.in                    |    6 +-
 src/bin/memmgr/memmgr.py.in                        |   71 +-
 src/bin/memmgr/tests/memmgr_test.py                |  106 ++
 src/bin/msgq/msgq.py.in                            |   37 +-
 src/bin/msgq/msgq_messages.mes                     |   10 +
 src/bin/msgq/tests/msgq_run_test.py                |   16 +
 src/bin/resolver/Makefile.am                       |    9 +-
 src/bin/resolver/tests/Makefile.am                 |    1 -
 src/bin/stats/stats.py.in                          |   90 +-
 src/bin/stats/stats_httpd.py.in                    |    6 +-
 src/bin/stats/stats_messages.mes                   |   11 +
 src/bin/stats/tests/stats_test.py                  |  284 +++++-
 src/bin/stats/tests/test_utils.py                  |   14 +-
 src/bin/xfrin/b10-xfrin.xml                        |  103 ++
 src/bin/xfrin/tests/xfrin_test.py                  |  287 +++++-
 src/bin/xfrin/xfrin.py.in                          |  165 +++-
 src/bin/xfrin/xfrin.spec                           |  245 +++++
 src/bin/xfrout/tests/xfrout_test.py.in             |   16 +-
 src/bin/xfrout/xfrout.py.in                        |   24 +-
 src/bin/zonemgr/zonemgr.py.in                      |    6 +-
 src/hooks/Makefile.am                              |    1 +
 src/hooks/dhcp/Makefile.am                         |    1 +
 src/hooks/dhcp/user_chk/Makefile.am                |   65 ++
 src/hooks/dhcp/user_chk/load_unload.cc             |  113 +++
 src/hooks/dhcp/user_chk/subnet_select_co.cc        |  237 +++++
 src/hooks/dhcp/user_chk/tests/Makefile.am          |   73 ++
 .../dhcp/user_chk/tests/run_unittests.cc}          |   18 +-
 .../user_chk/tests/test_data_files_config.h.in}    |   13 +-
 src/hooks/dhcp/user_chk/tests/test_users_1.txt     |    2 +
 src/hooks/dhcp/user_chk/tests/test_users_err.txt   |    2 +
 .../dhcp/user_chk/tests/user_file_unittests.cc     |  158 +++
 .../dhcp/user_chk/tests/user_registry_unittests.cc |  216 ++++
 src/hooks/dhcp/user_chk/tests/user_unittests.cc    |   96 ++
 src/hooks/dhcp/user_chk/tests/userid_unittests.cc  |  139 +++
 src/hooks/dhcp/user_chk/user.cc                    |  210 ++++
 src/hooks/dhcp/user_chk/user.h                     |  249 +++++
 .../dhcp/user_chk/user_chk_log.cc}                 |   11 +-
 .../dhcp/user_chk/user_chk_log.h}                  |   20 +-
 src/hooks/dhcp/user_chk/user_chk_messages.mes      |   43 +
 src/hooks/dhcp/user_chk/user_data_source.h         |   75 ++
 src/hooks/dhcp/user_chk/user_file.cc               |  159 +++
 src/hooks/dhcp/user_chk/user_file.h                |  135 +++
 src/hooks/dhcp/user_chk/user_registry.cc           |  122 +++
 src/hooks/dhcp/user_chk/user_registry.h            |  128 +++
 .../asiolink.h => hooks/dhcp/user_chk/version.cc}  |   16 +-
 src/lib/asiodns/Makefile.am                        |   14 +-
 src/lib/asiodns/tests/Makefile.am                  |    4 -
 src/lib/asiolink/Makefile.am                       |    4 -
 src/lib/asiolink/io_asio_socket.h                  |    8 +-
 src/lib/asiolink/tests/Makefile.am                 |    4 -
 src/lib/asiolink/tests/io_endpoint_unittest.cc     |    8 +-
 src/lib/asiolink/tests/io_socket_unittest.cc       |    6 +-
 src/lib/asiolink/tests/tcp_endpoint_unittest.cc    |    4 +-
 src/lib/asiolink/tests/udp_endpoint_unittest.cc    |    4 +-
 src/lib/cache/Makefile.am                          |    7 +-
 src/lib/cc/Makefile.am                             |   13 +-
 src/lib/cc/data.cc                                 |    5 +
 src/lib/cc/data.h                                  |    4 +
 src/lib/cc/session.cc                              |    2 +-
 src/lib/cc/tests/Makefile.am                       |    3 -
 src/lib/cc/tests/data_unittests.cc                 |    6 +
 src/lib/config/Makefile.am                         |    7 +-
 src/lib/config/ccsession.cc                        |   86 +-
 src/lib/config/ccsession.h                         |   55 ++
 src/lib/config/tests/ccsession_unittests.cc        |   59 ++
 src/lib/config/tests/fake_session.cc               |    8 +-
 src/lib/datasrc/Makefile.am                        |   12 +-
 src/lib/datasrc/client_list.cc                     |    1 +
 src/lib/datasrc/memory/Makefile.am                 |    7 +-
 src/lib/datasrc/memory/domaintree.h                | 1045 ++++++++++++++++++--
 src/lib/datasrc/memory/rdataset.cc                 |  166 +++-
 src/lib/datasrc/memory/rdataset.h                  |   53 +
 src/lib/datasrc/memory/zone_finder.cc              |    2 +-
 .../datasrc/tests/memory/domaintree_unittest.cc    |  504 ++++++++--
 src/lib/datasrc/tests/memory/rdataset_unittest.cc  |  145 ++-
 src/lib/dhcp/Makefile.am                           |    3 +
 src/lib/dhcp/dhcp4.h                               |   25 +-
 src/lib/dhcp/docsis3_option_defs.h                 |   66 ++
 src/lib/dhcp/iface_mgr.cc                          |   90 +-
 src/lib/dhcp/iface_mgr.h                           |   24 +
 src/lib/dhcp/libdhcp++.cc                          |  407 ++++++--
 src/lib/dhcp/libdhcp++.h                           |   76 +-
 src/lib/dhcp/option.cc                             |   17 +-
 src/lib/dhcp/option.h                              |   60 +-
 src/lib/dhcp/option6_ia.cc                         |   24 +-
 src/lib/dhcp/option6_iaaddr.cc                     |    9 +-
 src/lib/dhcp/option6_iaaddr.h                      |    8 +-
 .../{option6_iaaddr.cc => option6_iaprefix.cc}     |   73 +-
 .../dhcp/{option6_iaaddr.h => option6_iaprefix.h}  |   96 +-
 src/lib/dhcp/option_custom.cc                      |   22 +-
 src/lib/dhcp/option_definition.cc                  |  162 ++-
 src/lib/dhcp/option_definition.h                   |  100 +-
 src/lib/dhcp/option_int.h                          |    8 +-
 src/lib/dhcp/option_int_array.h                    |    4 +-
 src/lib/dhcp/option_vendor.cc                      |   85 ++
 src/lib/dhcp/option_vendor.h                       |  101 ++
 src/lib/dhcp/pkt4.cc                               |   71 +-
 src/lib/dhcp/pkt4.h                                |   15 +-
 src/lib/dhcp/pkt6.cc                               |   76 +-
 src/lib/dhcp/pkt6.h                                |   25 +-
 src/lib/dhcp/std_option_defs.h                     |   12 +-
 src/lib/dhcp/tests/Makefile.am                     |    2 +
 src/lib/dhcp/tests/iface_mgr_unittest.cc           |  134 +++
 src/lib/dhcp/tests/libdhcp++_unittest.cc           |  230 ++++-
 src/lib/dhcp/tests/option6_ia_unittest.cc          |  186 ++--
 src/lib/dhcp/tests/option6_iaaddr_unittest.cc      |   15 +
 src/lib/dhcp/tests/option6_iaprefix_unittest.cc    |  188 ++++
 src/lib/dhcp/tests/option_custom_unittest.cc       |  198 +++-
 src/lib/dhcp/tests/option_definition_unittest.cc   |   49 +-
 src/lib/dhcp/tests/option_unittest.cc              |  133 +++
 src/lib/dhcp/tests/option_vendor_unittest.cc       |  240 +++++
 src/lib/dhcp/tests/pkt4_unittest.cc                |  454 +++++----
 src/lib/dhcp/tests/pkt6_unittest.cc                |  167 +++-
 src/lib/dhcp/tests/protocol_util_unittest.cc       |    2 +-
 src/lib/dhcp_ddns/Makefile.am                      |    8 +-
 src/lib/dhcp_ddns/ncr_msg.cc                       |    5 +
 src/lib/dhcp_ddns/ncr_msg.h                        |   11 +
 src/lib/dhcp_ddns/tests/ncr_unittests.cc           |  255 ++---
 src/lib/dhcpsrv/Makefile.am                        |    8 +-
 src/lib/dhcpsrv/alloc_engine.cc                    |  303 ++++--
 src/lib/dhcpsrv/alloc_engine.h                     |  120 ++-
 src/lib/dhcpsrv/cfgmgr.cc                          |   37 +-
 src/lib/dhcpsrv/cfgmgr.h                           |   19 +-
 src/lib/dhcpsrv/dhcp_parsers.cc                    |   70 +-
 src/lib/dhcpsrv/dhcp_parsers.h                     |   13 +-
 src/lib/dhcpsrv/dhcpdb_create.mysql                |   15 +-
 src/lib/dhcpsrv/dhcpsrv_messages.mes               |   15 +-
 src/lib/dhcpsrv/{lease_mgr.cc => lease.cc}         |  135 +--
 src/lib/dhcpsrv/lease.h                            |  381 +++++++
 src/lib/dhcpsrv/lease_mgr.cc                       |  183 +---
 src/lib/dhcpsrv/lease_mgr.h                        |  349 +------
 src/lib/dhcpsrv/libdhcpsrv.dox                     |   54 +-
 src/lib/dhcpsrv/memfile_lease_mgr.cc               |  123 ++-
 src/lib/dhcpsrv/memfile_lease_mgr.h                |   66 +-
 src/lib/dhcpsrv/mysql_lease_mgr.cc                 |  299 ++++--
 src/lib/dhcpsrv/mysql_lease_mgr.h                  |   16 +-
 src/lib/dhcpsrv/option_space_container.h           |   19 +-
 src/lib/dhcpsrv/pool.cc                            |   68 +-
 src/lib/dhcpsrv/pool.h                             |  108 +-
 src/lib/dhcpsrv/subnet.cc                          |  221 ++++-
 src/lib/dhcpsrv/subnet.h                           |  185 +++-
 src/lib/dhcpsrv/tests/Makefile.am                  |   24 +-
 src/lib/dhcpsrv/tests/alloc_engine_unittest.cc     |  812 ++++++++++-----
 src/lib/dhcpsrv/tests/cfgmgr_unittest.cc           |   62 +-
 src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc     |   20 +-
 src/lib/dhcpsrv/tests/lease_mgr_unittest.cc        |  204 +++-
 .../dhcpsrv/tests/memfile_lease_mgr_unittest.cc    |  110 ++-
 src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc  |  586 ++++-------
 src/lib/dhcpsrv/tests/pool_unittest.cc             |  114 ++-
 src/lib/dhcpsrv/tests/schema_copy.h                |   12 +-
 src/lib/dhcpsrv/tests/subnet_unittest.cc           |  322 +++++-
 src/lib/dhcpsrv/tests/test_libraries.h.in          |   22 +-
 src/lib/dhcpsrv/tests/test_utils.cc                |  394 ++++++++
 src/lib/dhcpsrv/tests/test_utils.h                 |   69 ++
 src/lib/dns/Makefile.am                            |    8 +-
 src/lib/dns/master_loader.cc                       |   44 +-
 src/lib/dns/message.cc                             |   18 +-
 src/lib/dns/messagerenderer.h                      |    1 -
 src/lib/dns/rdata/any_255/tsig_250.cc              |   22 +-
 src/lib/dns/rrclass-placeholder.h                  |   24 +-
 src/lib/dns/rrclass.cc                             |    6 +-
 src/lib/dns/rrttl.cc                               |    6 +-
 src/lib/dns/rrttl.h                                |   24 +-
 src/lib/dns/tests/rdata_tsig_unittest.cc           |    4 +
 src/lib/dns/tests/rrclass_unittest.cc              |   14 +-
 src/lib/dns/tests/rrttl_unittest.cc                |   19 +-
 src/lib/dns/tests/tsigkey_unittest.cc              |   11 +
 src/lib/dns/tsigkey.cc                             |   12 +
 src/lib/dns/tsigkey.h                              |    1 +
 src/lib/hooks/Makefile.am                          |    9 +-
 src/lib/hooks/callout_handle.cc                    |    5 +-
 src/lib/hooks/callout_handle.h                     |    7 +
 src/lib/hooks/callout_manager.cc                   |   17 +-
 src/lib/hooks/callout_manager.h                    |    7 +
 src/lib/{nsas/asiolink.h => hooks/hooks.cc}        |   23 +-
 src/lib/hooks/hooks.h                              |   39 +
 src/lib/hooks/hooks_maintenance.dox                |  108 ++
 src/lib/hooks/hooks_messages.mes                   |   15 +
 src/lib/hooks/hooks_user.dox                       |  164 +--
 src/lib/hooks/library_manager.cc                   |    9 +
 src/lib/hooks/tests/Makefile.am                    |   64 +-
 src/lib/hooks/tests/basic_callout_library.cc       |   25 +-
 src/lib/hooks/tests/full_callout_library.cc        |    8 +-
 src/lib/hooks/tests/load_callout_library.cc        |    8 +-
 src/lib/hooks/tests/test_libraries.h.in            |   39 +-
 src/lib/log/Makefile.am                            |    4 -
 src/lib/log/logger.cc                              |    5 +
 src/lib/log/tests/Makefile.am                      |    7 +-
 src/lib/nsas/Makefile.am                           |    8 +-
 src/lib/nsas/README                                |    5 -
 src/lib/nsas/address_request_callback.h            |    1 -
 src/lib/nsas/nameserver_address.h                  |    1 -
 src/lib/nsas/nameserver_entry.h                    |    1 -
 src/lib/nsas/tests/nameserver_entry_unittest.cc    |    1 -
 src/lib/nsas/tests/zone_entry_unittest.cc          |    1 -
 src/lib/nsas/zone_entry.h                          |    1 -
 src/lib/python/bind10_config.py.in                 |   25 +
 src/lib/python/isc/log_messages/Makefile.am        |    2 +
 src/lib/python/isc/log_messages/util_messages.py   |    1 +
 src/lib/python/isc/log_messages/work/Makefile.am   |    2 +
 src/lib/python/isc/log_messages/work/README        |    5 +
 src/lib/python/isc/notify/notify_out.py            |   25 +-
 src/lib/python/isc/notify/tests/Makefile.am        |    2 +-
 src/lib/python/isc/notify/tests/notify_out_test.py |    4 +-
 .../isc/notify/tests/testdata/test_spec1.spec      |   57 ++
 src/lib/python/isc/server_common/Makefile.am       |    1 -
 .../{bind10_server.py.in => bind10_server.py}      |   30 +-
 src/lib/python/isc/statistics/counters.py          |   42 +-
 src/lib/python/isc/statistics/dns.py               |   69 +-
 .../python/isc/statistics/tests/counters_test.py   |   52 +-
 src/lib/python/isc/statistics/tests/dns_test.py    |   52 +-
 src/lib/python/isc/util/Makefile.am                |   17 +-
 src/lib/python/isc/util/tests/Makefile.am          |    2 +-
 .../isc/util/tests/address_formatter_test.py       |    2 -
 .../isc/util/tests/traceback_handler_test.py       |  101 ++
 src/lib/python/isc/util/traceback_handler.py       |   39 +
 src/lib/python/isc/util/util_messages.mes          |   26 +
 src/lib/resolve/Makefile.am                        |   12 +-
 src/lib/server_common/Makefile.am                  |    7 +-
 src/lib/server_common/tests/client_unittest.cc     |    6 +-
 src/lib/statistics/tests/Makefile.am               |    4 -
 src/lib/testutils/Makefile.am                      |    3 +-
 src/lib/util/tests/Makefile.am                     |    3 +-
 src/lib/util/threads/tests/Makefile.am             |    3 +-
 src/lib/xfr/Makefile.am                            |    6 +-
 tests/lettuce/features/example.feature             |   30 +
 tests/lettuce/features/terrain/querying.py         |   13 +-
 .../lettuce/features/xfrin_notify_handling.feature |  193 +++-
 tests/tools/badpacket/Makefile.am                  |    3 -
 tests/tools/perfdhcp/Makefile.am                   |    1 +
 tests/tools/perfdhcp/command_options.cc            |  181 +++-
 tests/tools/perfdhcp/command_options.h             |  104 +-
 tests/tools/perfdhcp/packet_storage.h              |  161 +++
 tests/tools/perfdhcp/perf_pkt6.cc                  |    2 +-
 tests/tools/perfdhcp/pkt_transform.cc              |   12 +-
 tests/tools/perfdhcp/pkt_transform.h               |    8 +-
 tests/tools/perfdhcp/stats_mgr.h                   |   21 +-
 tests/tools/perfdhcp/test_control.cc               |  271 ++++-
 tests/tools/perfdhcp/test_control.h                |  147 ++-
 tests/tools/perfdhcp/tests/Makefile.am             |    1 +
 .../tools/perfdhcp/tests/command_options_helper.h  |   10 +-
 .../perfdhcp/tests/command_options_unittest.cc     |  166 +++-
 .../perfdhcp/tests/packet_storage_unittest.cc      |  205 ++++
 tests/tools/perfdhcp/tests/stats_mgr_unittest.cc   |    7 +-
 .../tools/perfdhcp/tests/test_control_unittest.cc  |  347 ++++++-
 316 files changed, 24468 insertions(+), 4892 deletions(-)
 create mode 100644 m4macros/ax_python_sqlite3.m4
 create mode 100644 src/bin/d2/labeled_value.cc
 create mode 100644 src/bin/d2/labeled_value.h
 create mode 100644 src/bin/d2/nc_trans.cc
 create mode 100644 src/bin/d2/nc_trans.h
 create mode 100644 src/bin/d2/state_model.cc
 create mode 100644 src/bin/d2/state_model.h
 create mode 100644 src/bin/d2/tests/labeled_value_unittests.cc
 create mode 100644 src/bin/d2/tests/nc_trans_unittests.cc
 create mode 100644 src/bin/d2/tests/state_model_unittests.cc
 create mode 100644 src/bin/dhcp4/tests/dhcp4_test_utils.cc
 create mode 100644 src/bin/dhcp4/tests/wireshark.cc
 create mode 100644 src/bin/dhcp6/tests/dhcp6_test_utils.cc
 copy src/{lib/nsas/asiolink.h => bin/dhcp6/tests/test_data_files_config.h.in} (71%)
 create mode 100644 src/bin/dhcp6/tests/wireshark.cc
 create mode 100644 src/hooks/Makefile.am
 create mode 100644 src/hooks/dhcp/Makefile.am
 create mode 100644 src/hooks/dhcp/user_chk/Makefile.am
 create mode 100644 src/hooks/dhcp/user_chk/load_unload.cc
 create mode 100644 src/hooks/dhcp/user_chk/subnet_select_co.cc
 create mode 100644 src/hooks/dhcp/user_chk/tests/Makefile.am
 copy src/{lib/nsas/asiolink.h => hooks/dhcp/user_chk/tests/run_unittests.cc} (71%)
 copy src/{lib/nsas/asiolink.h => hooks/dhcp/user_chk/tests/test_data_files_config.h.in} (77%)
 create mode 100644 src/hooks/dhcp/user_chk/tests/test_users_1.txt
 create mode 100644 src/hooks/dhcp/user_chk/tests/test_users_err.txt
 create mode 100644 src/hooks/dhcp/user_chk/tests/user_file_unittests.cc
 create mode 100644 src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc
 create mode 100644 src/hooks/dhcp/user_chk/tests/user_unittests.cc
 create mode 100644 src/hooks/dhcp/user_chk/tests/userid_unittests.cc
 create mode 100644 src/hooks/dhcp/user_chk/user.cc
 create mode 100644 src/hooks/dhcp/user_chk/user.h
 copy src/{lib/nsas/asiolink.h => hooks/dhcp/user_chk/user_chk_log.cc} (78%)
 copy src/{lib/nsas/asiolink.h => hooks/dhcp/user_chk/user_chk_log.h} (60%)
 create mode 100644 src/hooks/dhcp/user_chk/user_chk_messages.mes
 create mode 100644 src/hooks/dhcp/user_chk/user_data_source.h
 create mode 100644 src/hooks/dhcp/user_chk/user_file.cc
 create mode 100644 src/hooks/dhcp/user_chk/user_file.h
 create mode 100644 src/hooks/dhcp/user_chk/user_registry.cc
 create mode 100644 src/hooks/dhcp/user_chk/user_registry.h
 copy src/{lib/nsas/asiolink.h => hooks/dhcp/user_chk/version.cc} (74%)
 create mode 100644 src/lib/dhcp/docsis3_option_defs.h
 copy src/lib/dhcp/{option6_iaaddr.cc => option6_iaprefix.cc} (57%)
 copy src/lib/dhcp/{option6_iaaddr.h => option6_iaprefix.h} (53%)
 create mode 100644 src/lib/dhcp/option_vendor.cc
 create mode 100644 src/lib/dhcp/option_vendor.h
 create mode 100644 src/lib/dhcp/tests/option6_iaprefix_unittest.cc
 create mode 100644 src/lib/dhcp/tests/option_vendor_unittest.cc
 copy src/lib/dhcpsrv/{lease_mgr.cc => lease.cc} (73%)
 create mode 100644 src/lib/dhcpsrv/lease.h
 rename src/lib/{nsas/asiolink.h => hooks/hooks.cc} (65%)
 create mode 100644 src/lib/python/isc/log_messages/util_messages.py
 create mode 100644 src/lib/python/isc/log_messages/work/README
 create mode 100644 src/lib/python/isc/notify/tests/testdata/test_spec1.spec
 rename src/lib/python/isc/server_common/{bind10_server.py.in => bind10_server.py} (88%)
 create mode 100644 src/lib/python/isc/util/tests/traceback_handler_test.py
 create mode 100644 src/lib/python/isc/util/traceback_handler.py
 create mode 100644 src/lib/python/isc/util/util_messages.mes
 create mode 100644 tests/tools/perfdhcp/packet_storage.h
 create mode 100644 tests/tools/perfdhcp/tests/packet_storage_unittest.cc

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index cfee686..419afaa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,210 @@
-669.	[func]	tmark
+705.	[bug]		kean
+	When commands are piped into bindctl, no longer attempt to query the
+	user name and password if no default user name and password file is
+	present, or it contains no valid entries.
+	(Trac #264, git 4921d7de6b5623c7e85d2baf8bc978686877345b)
+
+704.	[func]		naokikambe
+	New statistics items related to IP sockets added into b10-xfrin:
+	open, openfail, close, connfail, conn, senderr, and recverr.
+	Their values can be obtained by invoking "Stats show Xfrin" via
+	bindctl while b10-xfrin is running.
+	(Trac #2300, git 4655c110afa0ec6f5669bf53245bffe6b30ece4b)
+
+703.    [bug]		kean
+	A bug in b10-msgq was fixed where it would remove the socket file if
+	there was an existing copy of b10-msgq running. It now correctly
+	detects and reports this without removing the socket file.
+	(Trac #433, git c18a49b0435c656669e6f87ef65d44dc98e0e726)
+
+702.	[func]		marcin
+	perfdhcp: support for sending DHCPv6 Renew messages at the specified
+	rate and measure performance.
+	(Trac #3183, git 66f2939830926f4337623b159210103b5a8e2434)
+
+701.	[bug]		tomek
+	libdhcp++: Incoming DHCPv6 IAPREFIX option is now parsed properly.
+	(Trac #3211, git ed43618a2c7b2387d76f99a5a4b1a3e05ac70f5e)
+
+700.	[func]		tomek,marcin
+	b10-dhcp4,b10-dhcp6: Support for vendor options has been added. It
+	is now possible to configure vendor options. Server is able to
+	parse some CableLabs vendor options and send configured	vendor
+	options	in response. The support is not complete.
+	(Trac #3194, git 243ded15bbed0d35e230d00f4e3ee42c3609616c)
+
+699.	[bug]		marcin
+	libdhcp++: Options with defined suboptions are now handled properly.
+	In particular, Relay Agent Info options is now echoed back properly.
+	(Trac #3102, git 6f6251bbd761809634aa470f36480d046b4d2a20)
+
+698.	[bug]		muks
+	A bug was fixed in the interaction between b10-init and b10-msgq
+	that caused BIND 10 failures after repeated start/stop of
+	components.
+	(Trac #3094, git ed672a898d28d6249ff0c96df12384b0aee403c8
+
+697.	[func]	tmark
+	Implements "user_check" hooks shared library which supports subnet
+	selection based upon the contents of a list of known DHCP lease users
+	(i.e. clients).  Adds the following subdirectories to the bind10 src
+	directory for maintaining hooks shared libraries:
+		-bind10/src/hooks  - base directory for hooks shared libraries
+		-bind10/src/hooks/dhcp - base directory for all hooks libs pertaining
+		to DHCP(Kea)
+		-bind10/src/hooks/dhcp/user_check - directory containing the user_check
+		 hooks library
+	(Trac #3186, git f36aab92c85498f8511fbbe19fad5e3f787aef68)
+
+696.	[func]		tomek
+	b10-dhcp4: It is now possible to specify value of siaddr field
+	in DHCPv4 responses. It is used to point out to the next
+	server in the boot process (that typically is TFTP server).
+	(Trac #3191, git 541922b5300904a5de2eaeddc3666fc4b654ffba)
+
+695.	[func]		tomek
+	b10-dhcp6 is now able to listen on global IPv6 unicast addresses.
+	(Trac #3195, git 72e601f2a57ab70b25d50877c8e49242739d1c9f)
+
+694.	[bug]		tomek
+	b10-dhcp6 now handles exceptions better when processing initial
+	configuration. In particular, errors with socket binding do not
+	prevent b10-dhcp6 from establishing configuration session anymore.
+	(Trac #3195, git 72e601f2a57ab70b25d50877c8e49242739d1c9f)
+
+693.	[bug]		tomek
+	b10-dhcp6 now handles IPv6 interface enabling correctly.
+	(Trac #3195, git 72e601f2a57ab70b25d50877c8e49242739d1c9f)
+
+692.	[bug]		marcin
+	b10-dhcp4: Fix a bug whereby the Parameter Request List was not parsed
+	by the server and requested DHCPv4 options were not returned to the
+	client. Options are not sent back to the client if server failed to
+	assign a lease.
+	(Trac #3200, git 50d91e4c069c6de13680bfaaee3c56b68d6e4ab1)
+
+691.	[bug]		marcin
+	libdhcp++: Created definitions for standard DHCPv4 options:
+	tftp-server-name (66) and boot-file-name (67). Also, fixed definition
+	of DHCPv4 option time-offset (2).
+	(Trac #3199, git 6e171110c4dd9ae3b1be828b9516efc65c33460b)
+
+690.	[bug]		tomek
+	b10-dhcp4: Relay Agent Info option is now echoed back in
+	DHCPv4 responses.
+	(Trac #3184, git 287389c049518bff66bdf6a5a49bb8768be02d8e)
+
+689.	[func]*		marcin
+	b10-dhcp4 and b10-dhcp6 install callback functions which parse options
+	in the received DHCP packets.
+	(Trac #3180, git f73fba3cde9421acbeb9486c615900b0af58fa25)
+
+688.	[func]		tomek
+	b10-dhcp6: Prefix Delegation support is now extended to
+	Renew and Release messages.
+	(Trac #3153,#3154, git 3207932815f58045acea84ae092e0a5aa7c4bfd7)
+
+687.	[func]		tomek
+	b10-dhcp6: Prefix Delegation (IA_PD and IAPREFIX options) is now
+	supported in Solicit and Request messages.
+	(Trac #3152, git a0e73dd74658f2deb22fad2c7a1f56d122aa9021)
+
+686.	[bug]		tomek
+	b10-dhcp6 now sends back relayed traffic to proper port.
+	(Trac #3177, git 6b33de4bea92eecb64b6c673bf1b8ae51f8edcf1)
+
+685.	[func]		tomek
+	libdhcpsrv: Allocation Engine is now able to handle IPv6 prefixes.
+	This will be used in Prefix Delegation.
+	(Trac #3171, git 7d1431b4c887f0c7ee1b26b9b82d3d3b8464b34f)
+
+684.	[func]		muks, vorner
+	API support to delete zone data has been added. With this,
+	DomainTree and RdataSet which form the central zone data
+	structures of b10-auth allow deletion of names and RR data
+	respectively.
+	(Trac #2750, git d3dbe8e1643358d4f88cdbb7a16a32fd384b85b1)
+	(Trac #2751, git 7430591b4ae4c7052cab86ed17d0221db3b524a8)
+
+683.	[bug]		stephen
+	Modifications to fix problems running unit tests if they are statically
+	linked.  This includes provision of an initialization function that
+	must be called by user-written hooks libraries if they are loaded by a
+	statically-linked image.
+	(Trac #3113, git 3d19eee4dbfabc7cf7ae528351ee9e3a334cae92)
+
+682.	[func]		naokikambe
+	New statistics items added into b10-xfrin : ixfr_running, axfr_running,
+	and soa_in_progress.  Their values can be obtained by invoking "Stats
+	show Xfrin" via bindctl when b10-xfrin is running.
+	(Trac #2274, git ca691626a2be16f08754177bb27983a9f4984702)
+
+681.	[func]		tmark
+	Added support for prefix delegation configuration to b10-dhcp6
+	subnets.
+	(Trac# 3151, git 79a22be33825bafa1a0cdfa24d5cb751ab1ae2d3)
+
+680.	[func]		marcin
+	perfdhcp: Added support for requesting IPv6 prefixes using IA_PD
+	option being sent to the server.
+	(Trac #3173, git 4cc844f7cc82c8bd749296a2709ef67af8d9ba87)
+
+679.	[func]		tmark
+	b10-dhcp-ddns Finite state machine logic was refactored into its own class,
+	StateModel.
+	(Trac# 3156, git 6e9227b1b15448e834d1f60dd655e5633ff9745c)
+
+678.	[func]		tmark
+	MySQL backend used by b10-dhcp6 now uses lease type as a
+	filtering parameter in all IPv6 lease queries.
+	(Trac# 3147, git 65b6372b783cb1361fd56efe2b3247bfdbdc47ea)
+
+677.	[func]		tomek
+	libdhcpsrv: CfgMgr is now able to store IA, TA and PD pools in
+	Subnet6 structures.
+	(Trac #3150, git e6f0e89162bac0adae3ce3141437a282d5183162)
+
+676.	[bug]		muks
+	We now also allow the short name ("hmac-md5"), along with the long
+	name ("hmac-md5.sig-alg.reg.int") that was allowed before for
+	HMAC-MD5, so that it is more conveninent to configure TSIG keys
+	using it.
+	(Trac #2762, git c543008573eba65567e9c189824322954c6dd43b)
+
+675.    [func]      vorner
+	If there's an exception not handled in a Python BIND10 component,
+	it is now stored in a temporary file and properly logged, instead
+	of dumping to stderr.
+	(Trac #3095, git 18cf54ed89dee1dd1847053c5210f0ca220590c2)
+
+674.	[func]		tomek
+	Preparatory work for prefix delegation in LeaseMgr. getLease6()
+	renamed to getLeases6(). It now can return more than one lease.
+	(Trac #3146, git 05a05d810be754e7a4d8ca181550867febf6dcc6)
+
+673.	[func]		tomek
+	libdhcp: Added support for IA_PD and IAPREFIX options. New class
+	for IAPREFIX (Option6_IAPrefix) has been added.
+	(Trac #3145, git 3a844e85ecc3067ccd1c01841f4a61366cb278f4)
+
+672.	[func]		tmark
+	Added b10-dhcp-ddnsupdate transaction base class, NameChangeTransaction.
+	This class provides the common structure and methods to implement the state
+	models described in the DHCP_DDNS design, plus integration with DNSClient
+	and its callback mechanism for asynchronous IO with the DNS servers.
+	(Trac #3086, git 079b862c9eb21056fdf957e560b8fe7b218441b6)
+
+671.	[func]		dclink,tomek
+	memfile backend now supports getLease4(hwaddr) and getLease4(client-id)
+	methods. Thanks to David Carlier for contributing a patch.
+	(Trac #2592, git a11683be53db2f9f8f9b71c1d1c163511e0319b3)
+
+670.	[func]		marcin
+	libdhcpsrv: Added support to MySQL lease database backend to
+	store FQDN data for the lease.
+	(Trac #3084, git 79b7d8ee017b57a81cec5099bc028e1494d7e2e9)
+
+669.	[func]		tmark
 	Added main process event loop to D2Process which is the primary
 	application object in b10-dhcp-ddns. This allows DHCP-DDNS
 	to queue requests received from clients for processing while
diff --git a/configure.ac b/configure.ac
index e01f5cf..d9cd208 100644
--- a/configure.ac
+++ b/configure.ac
@@ -68,6 +68,13 @@ AC_CHECK_DECL([__SUNPRO_CC], [SUNCXX="yes"], [SUNCXX="no"])
 AC_CHECK_DECL([__clang__], [CLANGPP="yes"], [CLANGPP="no"])
 AM_CONDITIONAL(USE_CLANGPP, test "X${CLANGPP}" = "Xyes")
 
+dnl Determine if weare using GNU sed
+GNU_SED=no
+$SED --version 2> /dev/null | grep -q GNU
+if test $? -eq 0; then
+  GNU_SED=yes
+fi
+
 # Linker options
 
 # check -R, "-Wl,-R" or -rpath (we share the AX function defined in
@@ -105,9 +112,12 @@ AC_DEFUN([BIND10_CXX_TRY_FLAG], [
   AC_MSG_RESULT([$bind10_cxx_flag])
 ])
 
+CXX_VERSION="unknown"
+
 # SunStudio compiler requires special compiler options for boost
 # (http://blogs.sun.com/sga/entry/boost_mini_howto)
 if test "$SUNCXX" = "yes"; then
+CXX_VERSION=`$CXX -V 2> /dev/null | head -1`
 CXXFLAGS="$CXXFLAGS -library=stlport4 -features=tmplife -features=tmplrefstatic"
 MULTITHREADING_FLAG="-mt"
 fi
@@ -120,7 +130,8 @@ fi
 # we suppress this particular warning.  Note that it doesn't weaken checks
 # on the source code.
 if test "$CLANGPP" = "yes"; then
-	B10_CXXFLAGS="$B10_CXXFLAGS -Qunused-arguments"
+CXX_VERSION=`$CXX --version 2> /dev/null | head -1`
+B10_CXXFLAGS="$B10_CXXFLAGS -Qunused-arguments"
 fi
 
 BIND10_CXX_TRY_FLAG([-Wno-missing-field-initializers],
@@ -129,6 +140,7 @@ AC_SUBST(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 
 # gcc specific settings:
 if test "X$GXX" = "Xyes"; then
+CXX_VERSION=`$CXX --version 2> /dev/null | head -1`
 B10_CXXFLAGS="$B10_CXXFLAGS -Wall -Wextra -Wnon-virtual-dtor -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare"
 case "$host" in
 *-solaris*)
@@ -181,6 +193,7 @@ AC_HELP_STRING([--enable-static-link],
   [build programs with static link [[default=no]]]),
   [enable_static_link=yes], [enable_static_link=no])
 AM_CONDITIONAL(USE_STATIC_LINK, test $enable_static_link = yes)
+AM_COND_IF([USE_STATIC_LINK], [AC_DEFINE([USE_STATIC_LINK], [1], [BIND 10 was statically linked?])])
 
 # Check validity about some libtool options
 if test $enable_static_link = yes -a $enable_static = no; then
@@ -717,6 +730,21 @@ then
             BOTAN_INCLUDES="-I`${BOTAN_CONFIG} --prefix`/include ${BOTAN_INCLUDES}"
     fi
 fi
+
+dnl Determine the Botan version
+AC_MSG_CHECKING([Botan version])
+cat > conftest.cpp << EOF
+#include <botan/version.h>
+AUTOCONF_BOTAN_VERSION=BOTAN_VERSION_MAJOR . BOTAN_VERSION_MINOR . BOTAN_VERSION_PATCH
+EOF
+
+BOTAN_VERSION=`$CPP $CPPFLAGS $BOTAN_INCLUDES conftest.cpp | grep '^AUTOCONF_BOTAN_VERSION=' | $SED -e 's/^AUTOCONF_BOTAN_VERSION=//' -e 's/[[ 	]]//g' -e 's/"//g' 2> /dev/null`
+if test -z "$BOTAN_VERSION"; then
+  BOTAN_VERSION="unknown"
+fi
+$RM -f conftest.cpp
+AC_MSG_RESULT([$BOTAN_VERSION])
+
 # botan-config script (and the way we call pkg-config) returns -L and -l
 # as one string, but we need them in separate values
 BOTAN_LDFLAGS=
@@ -754,7 +782,24 @@ CPPFLAGS_SAVED=$CPPFLAGS
 CPPFLAGS="$BOTAN_INCLUDES $CPPFLAGS"
 LIBS_SAVED="$LIBS"
 LIBS="$LIBS $BOTAN_LIBS"
-AC_CHECK_HEADERS([botan/botan.h],,AC_MSG_ERROR([Missing required header files.]))
+
+# ac_header_preproc is an autoconf symbol (undocumented but stable) that
+# is set if the pre-processor phase passes. Thus by adding a custom
+# failure handler we can detect the difference between a header not existing
+# (or not even passing the pre-processor phase) and a header file resulting
+# in compilation failures.
+AC_CHECK_HEADERS([botan/botan.h],,[
+	if test "x$ac_header_preproc" = "xyes"; then
+		AC_MSG_ERROR([
+botan/botan.h was found but is unusable. The most common cause of this problem
+is attempting to use an updated C++ compiler with older C++ libraries, such as
+the version of Botan that comes with your distribution. If you have updated
+your C++ compiler we highly recommend that you use support libraries such as
+Boost and Botan that were compiled with the same compiler version.])
+	else
+		AC_MSG_ERROR([Missing required header files.])
+	fi]
+)
 AC_LINK_IFELSE(
         [AC_LANG_PROGRAM([#include <botan/botan.h>
                           #include <botan/hash.h>
@@ -796,6 +841,7 @@ if test "$MYSQL_CONFIG" != "" ; then
 
     MYSQL_CPPFLAGS=`$MYSQL_CONFIG --cflags`
     MYSQL_LIBS=`$MYSQL_CONFIG --libs`
+    MYSQL_VERSION=`$MYSQL_CONFIG --version`
 
     AC_SUBST(MYSQL_CPPFLAGS)
     AC_SUBST(MYSQL_LIBS)
@@ -874,6 +920,20 @@ AC_LINK_IFELSE(
          AC_MSG_ERROR([Needs log4cplus library])]
 )
 
+dnl Determine the log4cplus version, used mainly for config.report.
+AC_MSG_CHECKING([log4cplus version])
+cat > conftest.cpp << EOF
+#include <log4cplus/version.h>
+AUTOCONF_LOG4CPLUS_VERSION=LOG4CPLUS_VERSION_STR
+EOF
+
+LOG4CPLUS_VERSION=`$CPP $CPPFLAGS conftest.cpp | grep '^AUTOCONF_LOG4CPLUS_VERSION=' | $SED -e 's/^AUTOCONF_LOG4CPLUS_VERSION=//' -e 's/[[ 	]]//g' -e 's/"//g' 2> /dev/null`
+if test -z "$LOG4CPLUS_VERSION"; then
+  LOG4CPLUS_VERSION="unknown"
+fi
+$RM -f conftest.cpp
+AC_MSG_RESULT([$LOG4CPLUS_VERSION])
+
 CPPFLAGS=$CPPFLAGS_SAVED
 LIBS=$LIBS_SAVED
 
@@ -953,6 +1013,7 @@ AC_SUBST(MULTITHREADING_FLAG)
 GTEST_LDFLAGS=
 GTEST_LDADD=
 DISTCHECK_GTEST_CONFIGURE_FLAG=
+GTEST_VERSION="unknown"
 
 if test "x$enable_gtest" = "xyes" ; then
 
@@ -1008,6 +1069,7 @@ if test "$gtest_path" != "no" ; then
         GTEST_INCLUDES=`${GTEST_CONFIG} --cppflags`
         GTEST_LDFLAGS=`${GTEST_CONFIG} --ldflags`
         GTEST_LDADD=`${GTEST_CONFIG} --libs`
+        GTEST_VERSION=`${GTEST_CONFIG} --version`
         GTEST_FOUND="true"
     else
         AC_MSG_WARN([Unable to locate Google Test gtest-config.])
@@ -1093,6 +1155,8 @@ fi
 AX_SQLITE3_FOR_BIND10
 if test "x$have_sqlite" = "xyes" ; then
   enable_features="$enable_features SQLite3"
+
+  AX_PYTHON_SQLITE3
 fi
 
 #
@@ -1285,6 +1349,10 @@ AC_CONFIG_FILES([Makefile
                  src/bin/usermgr/Makefile
                  src/bin/usermgr/tests/Makefile
                  src/bin/tests/Makefile
+                 src/hooks/Makefile
+                 src/hooks/dhcp/Makefile
+                 src/hooks/dhcp/user_chk/Makefile
+                 src/hooks/dhcp/user_chk/tests/Makefile
                  src/lib/Makefile
                  src/lib/asiolink/Makefile
                  src/lib/asiolink/tests/Makefile
@@ -1461,9 +1529,11 @@ AC_OUTPUT([doc/version.ent
            src/bin/auth/gen-statisticsitems.py.pre
            src/bin/dhcp4/spec_config.h.pre
            src/bin/dhcp6/spec_config.h.pre
+           src/bin/dhcp6/tests/test_data_files_config.h
            src/bin/d2/spec_config.h.pre
            src/bin/d2/tests/test_data_files_config.h
            src/bin/tests/process_rename_test.py
+           src/hooks/dhcp/user_chk/tests/test_data_files_config.h
            src/lib/config/tests/data_def_unittests_config.h
            src/lib/dhcpsrv/tests/test_libraries.h
            src/lib/python/isc/config/tests/config_test
@@ -1471,7 +1541,6 @@ AC_OUTPUT([doc/version.ent
            src/lib/python/isc/notify/tests/notify_out_test
            src/lib/python/isc/log/tests/log_console.py
            src/lib/python/isc/log_messages/work/__init__.py
-           src/lib/python/isc/server_common/bind10_server.py
            src/lib/dns/gen-rdatacode.py
            src/lib/python/bind10_config.py
            src/lib/cc/session_config.h.pre
@@ -1535,39 +1604,69 @@ cat > config.report << END
     -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 
 Package:
-  Name:          $PACKAGE_NAME
-  Version:       $PACKAGE_VERSION
-
-C++ Compiler:    $CXX
-
-Flags:
-  DEFS:          $DEFS
-  CPPFLAGS:      $CPPFLAGS
-  CXXFLAGS:      $CXXFLAGS
-  LDFLAGS:       $LDFLAGS
-  B10_CXXFLAGS:  $B10_CXXFLAGS
-  OS Family:     $OS_TYPE
-dnl includes too
-  Python:        ${PYTHON_INCLUDES}
-                 ${PYTHON_CXXFLAGS}
-                 ${PYTHON_LDFLAGS}
-                 ${PYTHON_LIB}
-  Boost:         ${BOOST_INCLUDES}
-  Botan:         ${BOTAN_INCLUDES}
-                 ${BOTAN_LDFLAGS}
-                 ${BOTAN_LIBS}
-  Log4cplus:     ${LOG4CPLUS_INCLUDES}
-                 ${LOG4CPLUS_LIBS}
-  SQLite:        $SQLITE_CFLAGS
-                 $SQLITE_LIBS
+  Name:            ${PACKAGE_NAME}
+  Version:         ${PACKAGE_VERSION}
+  OS Family:       ${OS_TYPE}
+  Using GNU sed:   ${GNU_SED}
+
+C++ Compiler:
+  CXX:             ${CXX}
+  CXX_VERSION:     ${CXX_VERSION}
+  DEFS:            ${DEFS}
+  CPPFLAGS:        ${CPPFLAGS}
+  CXXFLAGS:        ${CXXFLAGS}
+  LDFLAGS:         ${LDFLAGS}
+  B10_CXXFLAGS:    ${B10_CXXFLAGS}
+
+Python:
+  PYTHON_VERSION:  ${PYTHON_VERSION}
+  PYTHON_INCLUDES: ${PYTHON_INCLUDES}
+  PYTHON_CXXFLAGS: ${PYTHON_CXXFLAGS}
+  PYTHON_LDFLAGS:  ${PYTHON_LDFLAGS}
+  PYTHON_LIB:      ${PYTHON_LIB}
+
+Boost:
+  BOOST_VERSION:   ${BOOST_VERSION}
+  BOOST_INCLUDES:  ${BOOST_INCLUDES}
+
+Botan:
+  BOTAN_VERSION:   ${BOTAN_VERSION}
+  BOTAN_INCLUDES:  ${BOTAN_INCLUDES}
+  BOTAN_LDFLAGS:   ${BOTAN_LDFLAGS}
+  BOTAN_LIBS:      ${BOTAN_LIBS}
+
+Log4cplus:
+  LOG4CPLUS_VERSION: ${LOG4CPLUS_VERSION}
+  LOG4CPLUS_INCLUDES: ${LOG4CPLUS_INCLUDES}
+  LOG4CPLUS_LIBS:  ${LOG4CPLUS_LIBS}
+
+SQLite:
+  SQLITE_VERSION:  ${SQLITE_VERSION}
+  SQLITE_CFLAGS:   ${SQLITE_CFLAGS}
+  SQLITE_LIBS:     ${SQLITE_LIBS}
 END
 
 # Avoid confusion on DNS/DHCP and only mention MySQL if it
 # were specified on the command line.
 if test "$MYSQL_CPPFLAGS" != "" ; then
 cat >> config.report << END
-  MySQL:         $MYSQL_CPPFLAGS
-                 $MYSQL_LIBS
+
+MySQL:
+  MYSQL_VERSION:   ${MYSQL_VERSION}
+  MYSQL_CPPFLAGS:  ${MYSQL_CPPFLAGS}
+  MYSQL_LIBS:      ${MYSQL_LIBS}
+END
+fi
+
+if test "$enable_gtest" != "no"; then
+cat >> config.report << END
+
+GTest:
+  GTEST_VERSION:   ${GTEST_VERSION}
+  GTEST_INCLUDES:  ${GTEST_INCLUDES}
+  GTEST_LDFLAGS:   ${GTEST_LDFLAGS}
+  GTEST_LDADD:     ${GTEST_LDADD}
+  GTEST_SOURCE:    ${GTEST_SOURCE}
 END
 fi
 
@@ -1590,6 +1689,8 @@ END
 cat config.report
 cat <<EOF
 
-  Now you can type "make" to build BIND 10
+  Now you can type "make" to build BIND 10. Note that if you intend to
+  run "make check", you must run "make" first as some files need to be
+  generated by "make" before "make check" can be run.
 
 EOF
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
index 7101759..04fbdc4 100644
--- a/doc/devel/mainpage.dox
+++ b/doc/devel/mainpage.dox
@@ -23,7 +23,7 @@
  * run-time and modifies its behavior you should read the section
  * @ref hooksdgDevelopersGuide.
  *
- * BIND 10 maintanenace information is divided into a number of sections
+ * BIND 10 maintenance information is divided into a number of sections
  * depending on focus.  DNS-specific issues are covered in the
  * @ref dnsMaintenanceGuide while information on DHCP-specific topics can
  * be found in the @ref dhcpMaintenanceGuide.  General BIND 10 topics, not
@@ -53,6 +53,7 @@
  *   - @subpage dhcpv4Session
  *   - @subpage dhcpv4ConfigParser
  *   - @subpage dhcpv4ConfigInherit
+ *   - @subpage dhcpv4OptionsParse
  *   - @subpage dhcpv4DDNSIntegration
  *   - @subpage dhcpv4Other
  * - @subpage dhcp6
@@ -60,6 +61,7 @@
  *   - @subpage dhcpv6ConfigParser
  *   - @subpage dhcpv6ConfigInherit
  *   - @subpage dhcpv6DDNSIntegration
+ *   - @subpage dhcpv6OptionsParse
  *   - @subpage dhcpv6Other
  * - @subpage libdhcp
  *   - @subpage libdhcpIntro
diff --git a/doc/guide/Makefile.am b/doc/guide/Makefile.am
index 8f3aaaf..3bffa1a 100644
--- a/doc/guide/Makefile.am
+++ b/doc/guide/Makefile.am
@@ -17,7 +17,7 @@ bind10-guide.html: bind10-guide.xml
 		-o $@ \
 		--stringparam section.autolabel 1 \
 		--stringparam section.label.includes.component.label 1 \
-		--stringparam html.stylesheet $(srcdir)/bind10-guide.css \
+		--stringparam html.stylesheet bind10-guide.css \
 		http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl \
 		$(srcdir)/bind10-guide.xml
 
@@ -28,7 +28,7 @@ bind10-messages.html: bind10-messages.xml
 	@XSLTPROC@ --novalid --xinclude --nonet \
 		--path $(top_builddir)/doc \
 		-o $@ \
-		--stringparam html.stylesheet $(srcdir)/bind10-guide.css \
+		--stringparam html.stylesheet bind10-guide.css \
 		http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl \
 		bind10-messages.xml
 
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index 6c538cd..6d52f42 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -3975,7 +3975,7 @@ Dhcp4/subnet4	[]	list	(default)
           </thead>
           <tbody>
 <row><entry>subnet-mask</entry><entry>1</entry><entry>ipv4-address</entry><entry>false</entry></row>
-<row><entry>time-offset</entry><entry>2</entry><entry>uint32</entry><entry>false</entry></row>
+<row><entry>time-offset</entry><entry>2</entry><entry>int32</entry><entry>false</entry></row>
 <row><entry>routers</entry><entry>3</entry><entry>ipv4-address</entry><entry>true</entry></row>
 <row><entry>time-servers</entry><entry>4</entry><entry>ipv4-address</entry><entry>true</entry></row>
 <row><entry>name-servers</entry><entry>5</entry><entry>ipv4-address</entry><entry>false</entry></row>
@@ -4069,6 +4069,8 @@ Dhcp4/subnet4	[]	list	(default)
 -->
 <row><entry>nwip-domain-name</entry><entry>62</entry><entry>string</entry><entry>false</entry></row>
 <row><entry>nwip-suboptions</entry><entry>63</entry><entry>binary</entry><entry>false</entry></row>
+<row><entry>tftp-server-name</entry><entry>66</entry><entry>string</entry><entry>false</entry></row>
+<row><entry>boot-file-name</entry><entry>67</entry><entry>string</entry><entry>false</entry></row>
 <row><entry>user-class</entry><entry>77</entry><entry>binary</entry><entry>false</entry></row>
 <row><entry>fqdn</entry><entry>81</entry><entry>record</entry><entry>false</entry></row>
 <row><entry>dhcp-agent-options</entry><entry>82</entry><entry>empty</entry><entry>false</entry></row>
@@ -4388,6 +4390,30 @@ Dhcp4/subnet4	[]	list	(default)
       </para>
     </section>
 
+    <section id="dhcp4-next-server">
+      <title>Next server (siaddr)</title>
+      <para>In some cases, clients want to obtain configuration from the TFTP server.
+      Although there is a dedicated option for it, some devices may use siaddr field
+      in the DHCPv4 packet for that purpose. That specific field can be configured
+      using next-server directive. It is possible to define it in global scope or
+      for a given subnet only. If both are defined, subnet value takes precedence.
+      The value in subnet can be set to 0.0.0.0, which means that next-server should
+      not be sent. It may also be set to empty string, which means the same as if
+      it was not defined at all - use global value.
+      </para>
+
+<screen>
+> <userinput>config add Dhcp4/next-server</userinput>
+> <userinput>config set Dhcp4/next-server "192.0.2.123"</userinput>
+> <userinput>config commit</userinput>
+<userinput></userinput>
+> <userinput>config add Dhcp4/subnet[0]/next-server</userinput>
+> <userinput>config set Dhcp4/subnet[0]/next-server "192.0.2.234"</userinput>
+> <userinput>config commit</userinput>
+</screen>
+
+    </section>
+
     <section id="dhcp4-std">
       <title>Supported Standards</title>
       <para>The following standards and draft standards are currently
@@ -4403,6 +4429,10 @@ Dhcp4/subnet4	[]	list	(default)
             Domain Name (15), DNS Servers (6), IP Address Lease Time
             (51), Subnet mask (1), and Routers (3).</simpara>
           </listitem>
+          <listitem>
+            <simpara><ulink url="http://tools.ietf.org/html/rfc3046">RFC 3046</ulink>:
+            Relay Agent Information option is supported.</simpara>
+          </listitem>
       </itemizedlist>
     </section>
 
@@ -4667,6 +4697,43 @@ Dhcp6/subnet6/	list
       </para>
     </section>
 
+    <section id="dhcp6-unicast">
+      <title>Unicast traffic support</title>
+      <para>
+        When DHCPv6 server starts up, by default it listens to the DHCP traffic
+        sent to multicast address ff02::1:2 on each interface that it is
+        configured to listen on (see <xref linkend="dhcp6-interface-selection"/>).
+        In some cases it is useful to configure a server to handle incoming
+        traffic sent to the global unicast addresses as well. The most common
+        reason for that is to have relays send their traffic to the server
+        directly. To configure server to listen on specific unicast address, a
+        notation to specify interfaces has been extended. Interface name can be
+        optionally followed by a slash, followed by global unicast address that
+        server should listen on. That will be done in addition to normal
+        link-local binding + listening on ff02::1:2 address. The sample commands
+        listed below show how to listen on 2001:db8::1 (a global address)
+        configured on the eth1 interface.
+      </para>
+      <para>
+        <screen>
+> <userinput>config set Dhcp6/interfaces[0] eth1/2001:db8::1</userinput>
+> <userinput>config commit</userinput></screen>
+        When configuration gets committed, the server will start to listen on
+        eth1 on link-local address, mutlicast group (ff02::1:2) and 2001:db8::1.
+      </para>
+      <para>
+        It is possible to mix interface names, wildcards and interface name/addresses
+        on the Dhcp6/interface list. It is not possible to specify more than one
+        unicast address on a given interface.
+      </para>
+      <para>
+        Care should be taken to specify proper unicast addresses. The server will
+        attempt to bind to those addresses specified, without any additional checks.
+        That approach is selected on purpose, so in the software can be used to
+        communicate over uncommon addresses if the administrator desires so.
+      </para>
+    </section>
+
     <section>
       <title>Subnet and Address Pool</title>
       <para>
@@ -4725,6 +4792,27 @@ Dhcp6/subnet6/	list
       </para>
     </section>
 
+    <section>
+<!-- @todo: add real meat to the prefix delegation config this is just place holder stuff -->
+      <title>Subnet and Prefix Delegation Pools</title>
+      <para>
+        Subnets may also be configured to delegate address prefixes....
+        A subnet may have one or more prefix delegation pools.  Each pool has
+        a prefixed address, which is specified as a prefix and a prefix length,
+        as well as a delegated prefix length.  A sample configuration is shown
+        below:
+      <screen>
+> <userinput>config add Dhcp6/subnet6</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/subnet "2001:db8:1::/64"</userinput>
+> <userinput>config show Dhcp6/subnet6[0]</userinput>
+> <userinput>config add Dhcp6/subnet6[0]/pd-pools</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/pd-pools[0]/prefix "2001:db8:1::"</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/pd-pools[0]/prefix-len 64</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/pd-pools[0]/delegated-len 96</userinput>
+> <userinput>config commit</userinput></screen>
+      </para>
+    </section>
+
     <section id="dhcp6-std-options">
       <title>Standard DHCPv6 options</title>
       <para>
diff --git a/ext/asio/README b/ext/asio/README
index da12a9d..c045eae 100644
--- a/ext/asio/README
+++ b/ext/asio/README
@@ -4,7 +4,18 @@ Downloaded from http://sourceforge.net/projects/asio/files
 Project page: http://think-async.com/Asio
 
 Local modifications:
+
+Imported a kqueue bug fix from Asio 1.5.1.
+git commit e4b2c2633ebb3859286e9a4c19e97e17bcac41b3
+See
+http://sourceforge.net/p/asio/git/ci/a50b53864f77fe762f21e44a281235982dd7e733/
+
 Added ASIO_DECL to a number of function definitions
 git commit c32718be9f5409b6f72d98ddcd0b1ccd4c5c2293
 See also the bug report at:
 http://sourceforge.net/tracker/?func=detail&aid=3291113&group_id=122478&atid=694037
+
+Imported a fix from Asio 1.5.2.
+git commit cf00216570a36d2e3e688b197deea781bfbe7d8d
+See
+http://sourceforge.net/p/asio/git/ci/4820fd6f0d257a6bb554fcd1f97f170330be0448/log/?path=/asio/include/asio/detail/impl/socket_ops.ipp
diff --git a/m4macros/ax_boost_for_bind10.m4 b/m4macros/ax_boost_for_bind10.m4
index 3045dfb..e8a7add 100644
--- a/m4macros/ax_boost_for_bind10.m4
+++ b/m4macros/ax_boost_for_bind10.m4
@@ -185,6 +185,20 @@ CXXFLAGS="$CXXFLAGS_SAVED"
 
 AC_SUBST(BOOST_INCLUDES)
 
+dnl Determine the Boost version, used mainly for config.report.
+AC_MSG_CHECKING([Boost version])
+cat > conftest.cpp << EOF
+#include <boost/version.hpp>
+AUTOCONF_BOOST_LIB_VERSION=BOOST_LIB_VERSION
+EOF
+
+BOOST_VERSION=`$CPP $CPPFLAGS conftest.cpp | grep '^AUTOCONF_BOOST_LIB_VERSION=' | $SED -e 's/^AUTOCONF_BOOST_LIB_VERSION=//' -e 's/_/./g' -e 's/"//g' 2> /dev/null`
+if test -z "$BOOST_VERSION"; then
+  BOOST_VERSION="unknown"
+fi
+$RM -f conftest.cpp
+AC_MSG_RESULT([$BOOST_VERSION])
+
 CPPFLAGS="$CPPFLAGS_SAVED"
 AC_LANG_RESTORE
 ])dnl AX_BOOST_FOR_BIND10
diff --git a/m4macros/ax_python_sqlite3.m4 b/m4macros/ax_python_sqlite3.m4
new file mode 100644
index 0000000..f4076ba
--- /dev/null
+++ b/m4macros/ax_python_sqlite3.m4
@@ -0,0 +1,17 @@
+dnl @synopsis AX_PYTHON_SQLITE3
+dnl
+dnl Test for the Python sqlite3 module used by BIND10's datasource
+dnl
+
+AC_DEFUN([AX_PYTHON_SQLITE3], [
+
+# Check for the python sqlite3 module
+AC_MSG_CHECKING(for python sqlite3 module)
+if "$PYTHON" -c 'import sqlite3' 2>/dev/null ; then
+    AC_MSG_RESULT(ok)
+else
+    AC_MSG_RESULT(missing)
+    AC_MSG_ERROR([Missing sqlite3 python module.])
+fi
+
+])dnl AX_PYTHON_SQLITE3
diff --git a/m4macros/ax_sqlite3_for_bind10.m4 b/m4macros/ax_sqlite3_for_bind10.m4
index 4eb7f94..476dcc7 100644
--- a/m4macros/ax_sqlite3_for_bind10.m4
+++ b/m4macros/ax_sqlite3_for_bind10.m4
@@ -13,8 +13,25 @@ dnl in PATH.
 AC_DEFUN([AX_SQLITE3_FOR_BIND10], [
 
 PKG_CHECK_MODULES(SQLITE, sqlite3 >= 3.3.9,
-    have_sqlite="yes",
-    have_sqlite="no (sqlite3 not detected)")
+    [have_sqlite="yes"
+dnl Determine the SQLite version, used mainly for config.report.
+CPPFLAGS_SAVED="$CPPFLAGS"
+CPPFLAGS="${CPPFLAGS} $SQLITE_CFLAGS"
+AC_MSG_CHECKING([SQLite version])
+cat > conftest.c << EOF
+#include <sqlite3.h>
+AUTOCONF_SQLITE_VERSION=SQLITE_VERSION
+EOF
+
+SQLITE_VERSION=`$CPP $CPPFLAGS conftest.c | grep '^AUTOCONF_SQLITE_VERSION=' | $SED -e 's/^AUTOCONF_SQLITE_VERSION=//' -e 's/"//g' 2> /dev/null`
+if test -z "$SQLITE_VERSION"; then
+  SQLITE_VERSION="unknown"
+fi
+$RM -f conftest.c
+AC_MSG_RESULT([$SQLITE_VERSION])
+
+CPPFLAGS="$CPPFLAGS_SAVED"
+    ],have_sqlite="no (sqlite3 not detected)")
 
 # Check for sqlite3 program
 AC_PATH_PROG(SQLITE3_PROGRAM, sqlite3, no)
diff --git a/src/Makefile.am b/src/Makefile.am
index 395553c..0e0109a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,4 +1,6 @@
 SUBDIRS = lib bin
+# @todo hooks lib could be a configurable switch
+SUBDIRS += hooks
 
 EXTRA_DIST = \
 	cppcheck-suppress.lst		\
diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am
index 1bb5ee9..81e477b 100644
--- a/src/bin/auth/Makefile.am
+++ b/src/bin/auth/Makefile.am
@@ -20,7 +20,7 @@ CLEANFILES  = *.gcno *.gcda auth.spec spec_config.h
 CLEANFILES += auth_messages.h auth_messages.cc
 CLEANFILES += gen-statisticsitems.py
 # auto-generated by gen-statisticsitems.py
-CLEANFILES += statistics.cc statistics_items.h b10-auth.xml tests/statistics_unittest.cc
+CLEANFILES += statistics.cc statistics_items.h b10-auth.xml tests/statistics_unittest.cc s-genstats s-messages
 
 man_MANS = b10-auth.8
 DISTCLEANFILES = $(man_MANS)
@@ -45,18 +45,24 @@ statistics_items.h: statistics_items.h.pre statistics_msg_items.def
 statistics.cc: statistics.cc.pre statistics_msg_items.def
 tests/statistics_unittest.cc: tests/statistics_unittest.cc.pre statistics_msg_items.def
 
-gen-statisticsitems.py: gen-statisticsitems.py.pre
+gen-statisticsitems.py: gen-statisticsitems.py.pre Makefile
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" gen-statisticsitems.py.pre >$@
 	chmod +x $@
 
-auth.spec b10-auth.xml statistics_items.h statistics.cc tests/statistics_unittest.cc: Makefile gen-statisticsitems.py
+auth.spec b10-auth.xml statistics_items.h statistics.cc tests/statistics_unittest.cc: s-genstats
+
+s-genstats: gen-statisticsitems.py
 	./gen-statisticsitems.py
+	touch $@
 
 spec_config.h: spec_config.h.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
 
-auth_messages.h auth_messages.cc: auth_messages.mes
+auth_messages.h auth_messages.cc: s-messages
+
+s-messages: auth_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/auth/auth_messages.mes
+	touch $@
 
 BUILT_SOURCES = spec_config.h auth_messages.h auth_messages.cc
 # auto-generated by gen-statisticsitems.py
diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc
index 65e5410..87f8e91 100644
--- a/src/bin/auth/query.cc
+++ b/src/bin/auth/query.cc
@@ -255,7 +255,7 @@ Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
     if (nsec->getRdataCount() == 0) {
         isc_throw(BadNSEC, "NSEC for WILDCARD_NXRRSET is empty");
     }
-    
+
     ConstZoneFinderContextPtr fcontext =
         finder.find(*qname_, RRType::NSEC(),
                     dnssec_opt_ | ZoneFinder::NO_WILDCARD);
diff --git a/src/bin/bind10/init.py.in b/src/bin/bind10/init.py.in
index 3bb7ea7..67491b3 100755
--- a/src/bin/bind10/init.py.in
+++ b/src/bin/bind10/init.py.in
@@ -78,6 +78,7 @@ from isc.log_messages.init_messages import *
 import isc.bind10.component
 import isc.bind10.special_component
 import isc.bind10.socket_cache
+import isc.util.traceback_handler
 import libutil_io_python
 import tempfile
 
@@ -336,6 +337,7 @@ class Init:
                 self.__propagate_component_config(new_config['components'])
             return isc.config.ccsession.create_answer(0)
         except Exception as e:
+            logger.error(BIND10_RECONFIGURE_ERROR, e)
             return isc.config.ccsession.create_answer(1, str(e))
 
     def get_processes(self):
@@ -488,7 +490,7 @@ class Init:
         self.log_starting("b10-msgq")
         msgq_proc = self._make_process_info("b10-msgq", ["b10-msgq"],
                                             self.c_channel_env,
-                                            True, not self.verbose)
+                                            not self.verbose, not self.verbose)
         msgq_proc.spawn()
         self.log_started(msgq_proc.pid)
 
@@ -596,6 +598,13 @@ class Init:
             process, the log_starting/log_started methods are not used.
         """
         logger.info(BIND10_STARTING_CC)
+
+        # Unsubscribe from the other CC session first, because we only
+        # monitor one and msgq expects all data sent to us to be read,
+        # or it will close its side of the socket.
+        if self.cc_session is not None:
+            self.cc_session.group_unsubscribe("Init")
+
         self.ccs = isc.config.ModuleCCSession(SPECFILE_LOCATION,
                                       self.config_handler,
                                       self.command_handler,
@@ -732,8 +741,12 @@ class Init:
         try:
             self.cc_session = isc.cc.Session(self.msgq_socket_file)
             logger.fatal(BIND10_MSGQ_ALREADY_RUNNING)
-            return "b10-msgq already running, or socket file not cleaned , " +\
-                "cannot start"
+            if self.msgq_socket_file is not None:
+              socket_name = "socket file '" + self.msg_socket_file + "'"
+            else:
+              socket_name = "default socket file"
+            return "b10-msgq already running, or " + socket_name +\
+                " not cleaned - cannot start"
         except isc.cc.session.SessionError:
             # this is the case we want, where the msgq is not running
             pass
@@ -763,9 +776,14 @@ class Init:
         it might want to choose if it is for this one).
         """
         logger.info(BIND10_STOP_PROCESS, process)
-        self.cc_session.group_sendmsg(isc.config.ccsession.
-                                      create_command('shutdown', {'pid': pid}),
-                                      recipient, recipient)
+        try:
+            self.cc_session.group_sendmsg(isc.config.ccsession.
+                                          create_command('shutdown',
+                                                         {'pid': pid}),
+                                          recipient, recipient)
+        except:
+            logger.error(BIND10_COMPONENT_SHUTDOWN_ERROR, process)
+            raise
 
     def component_shutdown(self, exitcode=0):
         """
@@ -1368,4 +1386,4 @@ def main():
     sys.exit(b10_init.exitcode)
 
 if __name__ == "__main__":
-    main()
+    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/bin/bind10/init_messages.mes b/src/bin/bind10/init_messages.mes
index 267790d..21cd142 100644
--- a/src/bin/bind10/init_messages.mes
+++ b/src/bin/bind10/init_messages.mes
@@ -325,3 +325,10 @@ the configuration manager to start up.  The total length of time Init
 will wait for the configuration manager before reporting an error is
 set with the command line --wait switch, which has a default value of
 ten seconds.
+
+% BIND10_RECONFIGURE_ERROR Error applying new config: %1
+A new configuration was received, but there was an error doing the
+re-configuration.
+
+% BIND10_COMPONENT_SHUTDOWN_ERROR An error occured stopping component %1
+An attempt to gracefully shutdown a component failed.
diff --git a/src/bin/bind10/tests/init_test.py.in b/src/bin/bind10/tests/init_test.py.in
index 913e642..af41eb6 100644
--- a/src/bin/bind10/tests/init_test.py.in
+++ b/src/bin/bind10/tests/init_test.py.in
@@ -1651,7 +1651,7 @@ class TestInitComponents(unittest.TestCase):
         pi = init.start_msgq()
         self.assertEqual('b10-msgq', pi.name)
         self.assertEqual(['b10-msgq'], pi.args)
-        self.assertTrue(pi.dev_null_stdout)
+        self.assertEqual(pi.dev_null_stdout, not verbose)
         self.assertEqual(pi.dev_null_stderr, not verbose)
         self.assertEqual({'FOO': 'an env string'}, pi.env)
 
diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py
index 03b5d6b..bcae95c 100644
--- a/src/bin/bindctl/bindcmd.py
+++ b/src/bin/bindctl/bindcmd.py
@@ -272,6 +272,11 @@ WARNING: The Python readline module isn't available, so some command line
         else:
             self._print('Login failed: either the user name or password is '
                         'invalid.\n')
+
+        # If this was not an interactive session do not prompt for login info.
+        if not sys.stdin.isatty():
+            return False
+
         while True:
             count = count + 1
             if count > 3:
diff --git a/src/bin/bindctl/bindctl_main.py.in b/src/bin/bindctl/bindctl_main.py.in
index 875b06e..68a6237 100755
--- a/src/bin/bindctl/bindctl_main.py.in
+++ b/src/bin/bindctl/bindctl_main.py.in
@@ -26,6 +26,7 @@ from bindctl import command_sets
 import pprint
 from optparse import OptionParser, OptionValueError
 import isc.util.process
+import isc.util.traceback_handler
 
 isc.util.process.rename()
 
@@ -150,7 +151,7 @@ def set_bindctl_options(parser):
                       default=None, action='store',
                       help='Directory to store the password CSV file')
 
-if __name__ == '__main__':
+def main():
     parser = OptionParser(version = VERSION)
     set_bindctl_options(parser)
     (options, args) = parser.parse_args()
@@ -161,3 +162,6 @@ if __name__ == '__main__':
     command_sets.prepare_execute_commands(tool)
     result = tool.run()
     sys.exit(result)
+
+if __name__ == '__main__':
+    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/bin/cfgmgr/b10-cfgmgr.py.in b/src/bin/cfgmgr/b10-cfgmgr.py.in
index 06b9b0f..4bc56df 100755
--- a/src/bin/cfgmgr/b10-cfgmgr.py.in
+++ b/src/bin/cfgmgr/b10-cfgmgr.py.in
@@ -30,6 +30,7 @@ import isc.log
 isc.log.init("b10-cfgmgr", buffer=True)
 from isc.config.cfgmgr import ConfigManager, ConfigManagerDataReadError, logger
 from isc.log_messages.cfgmgr_messages import *
+import isc.util.traceback_handler
 
 isc.util.process.rename()
 
@@ -128,4 +129,4 @@ def main():
     return 0
 
 if __name__ == "__main__":
-    sys.exit(main())
+    sys.exit(isc.util.traceback_handler.traceback_handler(main))
diff --git a/src/bin/cmdctl/Makefile.am b/src/bin/cmdctl/Makefile.am
index 54be50f..a2d04a3 100644
--- a/src/bin/cmdctl/Makefile.am
+++ b/src/bin/cmdctl/Makefile.am
@@ -78,7 +78,7 @@ install-data-local:
 	$(mkinstalldirs) $(DESTDIR)/@sysconfdir@/@PACKAGE@
 	for f in $(CERTFILES) ; do	\
 	  if test ! -f $(DESTDIR)$(sysconfdir)/@PACKAGE@/$$f; then	\
-	    ${INSTALL} -m 640 $(srcdir)/$$f $(DESTDIR)$(sysconfdir)/@PACKAGE@/ ;	\
+	    ${INSTALL} -m 640 $$f $(DESTDIR)$(sysconfdir)/@PACKAGE@/ ;	\
 	  fi ;	\
 	done
 
diff --git a/src/bin/cmdctl/cmdctl.py.in b/src/bin/cmdctl/cmdctl.py.in
index ea56da9..0e716b8 100755
--- a/src/bin/cmdctl/cmdctl.py.in
+++ b/src/bin/cmdctl/cmdctl.py.in
@@ -48,6 +48,7 @@ from optparse import OptionParser, OptionValueError
 from hashlib import sha1
 from isc.util import socketserver_mixin
 from isc.log_messages.cmdctl_messages import *
+import isc.util.traceback_handler
 
 isc.log.init("b10-cmdctl", buffer=True)
 logger = isc.log.Logger("cmdctl")
@@ -675,7 +676,7 @@ def set_cmd_options(parser):
     parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
             help="display more about what is going on")
 
-if __name__ == '__main__':
+def main():
     set_signal_handler()
     parser = OptionParser(version = __version__)
     set_cmd_options(parser)
@@ -701,3 +702,6 @@ if __name__ == '__main__':
     logger.info(CMDCTL_EXITING)
 
     sys.exit(result)
+
+if __name__ == '__main__':
+    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/bin/d2/Makefile.am b/src/bin/d2/Makefile.am
index f289815..f27fe38 100644
--- a/src/bin/d2/Makefile.am
+++ b/src/bin/d2/Makefile.am
@@ -16,7 +16,7 @@ endif
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 
-CLEANFILES  = *.gcno *.gcda spec_config.h d2_messages.h d2_messages.cc
+CLEANFILES  = *.gcno *.gcda spec_config.h d2_messages.h d2_messages.cc s-messages
 
 man_MANS = b10-dhcp-ddns.8
 DISTCLEANFILES = $(man_MANS)
@@ -39,8 +39,11 @@ endif
 spec_config.h: spec_config.h.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
 
-d2_messages.h d2_messages.cc: d2_messages.mes
+d2_messages.h d2_messages.cc: s-messages
+
+s-messages: d2_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/d2/d2_messages.mes
+	touch $@
 
 BUILT_SOURCES = spec_config.h d2_messages.h d2_messages.cc
 
@@ -59,6 +62,9 @@ b10_dhcp_ddns_SOURCES += d2_update_message.cc d2_update_message.h
 b10_dhcp_ddns_SOURCES += d2_update_mgr.cc d2_update_mgr.h
 b10_dhcp_ddns_SOURCES += d2_zone.cc d2_zone.h
 b10_dhcp_ddns_SOURCES += dns_client.cc dns_client.h
+b10_dhcp_ddns_SOURCES += labeled_value.cc labeled_value.h
+b10_dhcp_ddns_SOURCES += nc_trans.cc nc_trans.h
+b10_dhcp_ddns_SOURCES += state_model.cc state_model.h
 
 nodist_b10_dhcp_ddns_SOURCES = d2_messages.h d2_messages.cc
 EXTRA_DIST += d2_messages.mes
diff --git a/src/bin/d2/d2_cfg_mgr.h b/src/bin/d2/d2_cfg_mgr.h
index c9b794f..d95a890 100644
--- a/src/bin/d2/d2_cfg_mgr.h
+++ b/src/bin/d2/d2_cfg_mgr.h
@@ -199,7 +199,7 @@ public:
     /// output:
     ///0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.9.0.0.2.0.3.0.8.B.D.0.1.0.0.2.ip6.arpa.
     ///
-    /// @param address string containing a valid IPv6 address.
+    /// @param ioaddr string containing a valid IPv6 address.
     ///
     /// @return a std::string containing the reverse order address.
     ///
diff --git a/src/bin/d2/d2_messages.mes b/src/bin/d2/d2_messages.mes
index c2805fa..e25c122 100644
--- a/src/bin/d2/d2_messages.mes
+++ b/src/bin/d2/d2_messages.mes
@@ -252,3 +252,9 @@ in event loop.
 % DHCP_DDNS_SHUTDOWN application received shutdown command with args: %1
 This is informational message issued when the application has been instructed
 to shut down by the controller.
+
+% DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR application encountered an unexpected error while carrying out a NameChangeRequest: %1
+This is error message issued when the application fails to process a
+NameChangeRequest correctly. Some or all of the DNS updates requested as part
+of this update did not succeed. This is a programmatic error and should be
+reported.
diff --git a/src/bin/d2/d2_update_mgr.cc b/src/bin/d2/d2_update_mgr.cc
index e625c3e..b88b415 100644
--- a/src/bin/d2/d2_update_mgr.cc
+++ b/src/bin/d2/d2_update_mgr.cc
@@ -77,7 +77,7 @@ D2UpdateMgr::checkFinishedTransactions() {
     // NOTE: One must use postfix increments of the iterator on the calls
     // to erase.  This replaces the old iterator which becomes invalid by the
     // erase with a the next valid iterator.  Prefix incrementing will not
-    // work. 
+    // work.
     TransactionList::iterator it = transaction_list_.begin();
     while (it != transaction_list_.end()) {
         NameChangeTransactionPtr trans = (*it).second;
diff --git a/src/bin/d2/d2_update_mgr.h b/src/bin/d2/d2_update_mgr.h
index 555b7be..9653253 100644
--- a/src/bin/d2/d2_update_mgr.h
+++ b/src/bin/d2/d2_update_mgr.h
@@ -22,6 +22,7 @@
 #include <d2/d2_log.h>
 #include <d2/d2_queue_mgr.h>
 #include <d2/d2_cfg_mgr.h>
+#include <d2/nc_trans.h>
 
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
@@ -37,58 +38,9 @@ public:
         isc::Exception(file, line, what) { };
 };
 
-//@{
-/// @todo  This is a stub implementation of NameChangeTransaction that is here
-/// strictly to facilitate development of D2UpdateMgr. It will move to its own
-/// source file(s) once NameChangeTransaction class development begins.
-
-/// @brief Defines the key for transactions.
-typedef isc::dhcp_ddns::D2Dhcid TransactionKey;
-
-class NameChangeTransaction {
-public:
-    NameChangeTransaction(isc::asiolink::IOService& io_service,
-                          dhcp_ddns::NameChangeRequestPtr& ncr,
-                          DdnsDomainPtr forward_domain,
-                          DdnsDomainPtr reverse_domain)
-    : io_service_(io_service), ncr_(ncr), forward_domain_(forward_domain),
-      reverse_domain_(reverse_domain) {
-    }
-
-    ~NameChangeTransaction(){
-    }
-
-    const dhcp_ddns::NameChangeRequestPtr& getNcr() const {
-        return (ncr_);
-    }
-
-    const TransactionKey& getTransactionKey() const {
-        return (ncr_->getDhcid());
-    }
-
-    dhcp_ddns::NameChangeStatus getNcrStatus() const {
-        return (ncr_->getStatus());
-    }
-
-private:
-    isc::asiolink::IOService& io_service_;
-
-    dhcp_ddns::NameChangeRequestPtr ncr_;
-
-    DdnsDomainPtr forward_domain_;
-
-    DdnsDomainPtr reverse_domain_;
-};
-
-/// @brief Defines a pointer to a NameChangeTransaction.
-typedef boost::shared_ptr<NameChangeTransaction> NameChangeTransactionPtr;
-
-//@}
-
 /// @brief Defines a list of transactions.
 typedef std::map<TransactionKey, NameChangeTransactionPtr> TransactionList;
 
-
 /// @brief D2UpdateMgr creates and manages update transactions.
 ///
 /// D2UpdateMgr is the DHCP_DDNS task master, instantiating and then supervising
diff --git a/src/bin/d2/labeled_value.cc b/src/bin/d2/labeled_value.cc
new file mode 100644
index 0000000..cf836f7
--- /dev/null
+++ b/src/bin/d2/labeled_value.cc
@@ -0,0 +1,123 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <d2/labeled_value.h>
+
+namespace isc {
+namespace d2 {
+
+/**************************** LabeledValue ****************************/
+
+LabeledValue::LabeledValue(const int value, const std::string& label)
+    : value_(value), label_(label) {
+    if (label.empty()) {
+        isc_throw(LabeledValueError, "labels cannot be empty");
+    }
+}
+
+LabeledValue::~LabeledValue(){
+}
+
+int
+LabeledValue::getValue() const {
+    return (value_);
+}
+
+std::string
+LabeledValue::getLabel() const {
+    return (label_);
+}
+
+bool
+LabeledValue::operator==(const LabeledValue& other) const {
+    return (this->value_ == other.value_);
+}
+
+bool
+LabeledValue::operator!=(const LabeledValue& other) const {
+    return (this->value_ != other.value_);
+}
+
+bool
+LabeledValue::operator<(const LabeledValue& other) const {
+    return (this->value_ < other.value_);
+}
+
+std::ostream& operator<<(std::ostream& os, const LabeledValue& vlp) {
+    os << vlp.getLabel();
+    return (os);
+}
+
+/**************************** LabeledValueSet ****************************/
+
+const char* LabeledValueSet::UNDEFINED_LABEL = "UNDEFINED";
+
+LabeledValueSet::LabeledValueSet(){
+}
+
+LabeledValueSet::~LabeledValueSet() {
+}
+
+void
+LabeledValueSet::add(LabeledValuePtr entry) {
+    if (!entry) {
+        isc_throw(LabeledValueError, "cannot add an null entry to set");
+    }
+
+    const int value = entry->getValue();
+    if (isDefined(value)) {
+        isc_throw(LabeledValueError,
+                  "value: " << value << " is already defined as: "
+                  << getLabel(value));
+        }
+
+    map_[entry->getValue()]=entry;
+}
+
+void
+LabeledValueSet::add(const int value, const std::string& label) {
+    add (LabeledValuePtr(new LabeledValue(value,label)));
+}
+
+const LabeledValuePtr&
+LabeledValueSet::get(int value) {
+    static LabeledValuePtr undefined;
+    LabeledValueMap::iterator it = map_.find(value);
+    if (it != map_.end()) {
+        return ((*it).second);
+    }
+
+    // Return an empty pointer when not found.
+    return (undefined);
+}
+
+bool
+LabeledValueSet::isDefined(const int value) const {
+    LabeledValueMap::const_iterator it = map_.find(value);
+    return (it != map_.end());
+}
+
+std::string
+LabeledValueSet::getLabel(const int value) const {
+    LabeledValueMap::const_iterator it = map_.find(value);
+    if (it != map_.end()) {
+        const LabeledValuePtr& ptr = (*it).second;
+        return (ptr->getLabel());
+    }
+
+    return (std::string(UNDEFINED_LABEL));
+}
+
+} // namespace isc::d2
+} // namespace isc
diff --git a/src/bin/d2/labeled_value.h b/src/bin/d2/labeled_value.h
new file mode 100644
index 0000000..965df9d
--- /dev/null
+++ b/src/bin/d2/labeled_value.h
@@ -0,0 +1,184 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef LABELED_VALUE_H
+#define LABELED_VALUE_H
+
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+#include <ostream>
+#include <string>
+#include <map>
+
+/// @file labeled_value.h This file defines classes: LabeledValue and
+/// LabeledValueSet.
+
+namespace isc {
+namespace d2 {
+
+/// @brief Thrown if an error is encountered handling a LabeledValue.
+class LabeledValueError : public isc::Exception {
+public:
+    LabeledValueError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Implements the concept of a constant value with a text label.
+///
+/// This class implements an association between an constant integer value
+/// and a text label. It provides a single constructor, accessors for both
+/// the value and label, and boolean operators which treat the value as
+/// the "key" for comparisons.  This allows them to be assembled into
+/// dictionaries of unique values.  Note, that the labels are not required to
+/// be unique but in practice it makes little sense to for them to be
+/// otherwise.
+class LabeledValue {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param value the numeric constant value to be labeled.
+    /// @param label the text label to associate to this value.
+    ///
+    /// @throw LabeledValueError if label is empty.
+    LabeledValue(const int value, const std::string& label);
+
+    /// @brief Destructor.
+    ///
+    /// Destructor is virtual to permit derivations.
+    virtual ~LabeledValue();
+
+    /// @brief Gets the integer value of this instance.
+    ///
+    /// @return integer value of this instance.
+    int getValue() const;
+
+    /// @brief Gets the text label of this instance.
+    ///
+    /// @return The text label as string
+    std::string getLabel() const;
+
+    /// @brief  Equality operator
+    ///
+    /// @return True if a.value_ is equal to b.value_.
+    bool operator==(const LabeledValue& other) const;
+
+    /// @brief  Inequality operator
+    ///
+    /// @return True if a.value_ is not equal to b.value_.
+    bool operator!=(const LabeledValue& other) const;
+
+    /// @brief  Less-than operator
+    ///
+    /// @return True if a.value_ is less than b.value_.
+    bool operator<(const LabeledValue& other) const;
+
+private:
+    /// @brief The numeric value to label.
+    int value_;
+
+    /// @brief The text label for the value.
+    std::string label_;
+};
+
+/// @brief Dumps the label to ostream.
+std::ostream& operator<<(std::ostream& os, const LabeledValue& vlp);
+
+/// @brief Defines a shared pointer to a LabeledValue instance.
+typedef boost::shared_ptr<LabeledValue> LabeledValuePtr;
+
+/// @brief Defines a map of pointers to LabeledValues keyed by value.
+typedef std::map<unsigned int, LabeledValuePtr> LabeledValueMap;
+
+
+/// @brief Implements a set of unique LabeledValues.
+///
+/// This class is intended to function as a dictionary of numeric values
+/// and the labels associated with them.  It is essentially a thin wrapper
+/// around a std::map of LabeledValues, keyed by their values.  This is handy
+/// for defining a set of "valid" constants while conveniently associating a
+/// text label with each value.
+///
+/// This class offers two variants of an add method for adding entries to the
+/// set, and accessors for finding an entry or an entry's label by value.
+/// Note that the add methods may throw but all accessors are exception safe.
+/// It is up to the caller to determine when and if an undefined value is
+/// exception-worthy.
+///
+/// More interestingly, a derivation of this class can be used to "define"
+/// valid instances of derivations of LabeledValue.
+class LabeledValueSet {
+public:
+    /// @brief Defines a text label returned by when value is not found.
+    static const char* UNDEFINED_LABEL;
+
+    /// @brief Constructor
+    ///
+    /// Constructs an empty set.
+    LabeledValueSet();
+
+    /// @brief Destructor
+    ///
+    /// Destructor is virtual to permit derivations.
+    virtual ~LabeledValueSet();
+
+    /// @brief Adds the given entry to the set
+    ///
+    /// @param entry is the entry to add.
+    ///
+    /// @throw LabeledValuePtr if the entry is null or the set already
+    /// contains an entry with the same value.
+    void add(LabeledValuePtr entry);
+
+    /// @brief Adds an entry to the set for the given value and label
+    ///
+    /// @param value the numeric constant value to be labeled.
+    /// @param label the text label to associate to this value.
+    ///
+    /// @throw LabeledValuePtr if the label is empty, or if the set
+    /// already contains an entry with the same value.
+    void add(const int value, const std::string& label);
+
+    /// @brief Fetches a pointer to the entry associated with value
+    ///
+    /// @param value is the value of the entry desired.
+    ///
+    /// @return A pointer to the entry if the entry was found otherwise the
+    /// pointer is empty.
+    const LabeledValuePtr& get(int value);
+
+    /// @brief Tests if the set contains an entry for the given value.
+    ///
+    /// @param value is the value of the entry to test.
+    ///
+    /// @return True if an entry for value exists in the set, false if not.
+    bool isDefined(const int value) const;
+
+    /// @brief Fetches the label for the given value
+    ///
+    /// @param value is the value for which the label is desired.
+    ///
+    /// @return the label of the value if defined, otherwise it returns
+    /// UNDEFINED_LABEL.
+    std::string getLabel(const int value) const;
+
+private:
+    /// @brief The map of labeled values.
+    LabeledValueMap map_;
+};
+
+} // namespace isc::d2
+} // namespace isc
+#endif
diff --git a/src/bin/d2/nc_trans.cc b/src/bin/d2/nc_trans.cc
new file mode 100644
index 0000000..50741fe
--- /dev/null
+++ b/src/bin/d2/nc_trans.cc
@@ -0,0 +1,250 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <d2/d2_log.h>
+#include <d2/nc_trans.h>
+
+namespace isc {
+namespace d2 {
+
+// Common transaction states
+const int NameChangeTransaction::READY_ST;
+const int NameChangeTransaction::SELECTING_FWD_SERVER_ST;
+const int NameChangeTransaction::SELECTING_REV_SERVER_ST;
+const int NameChangeTransaction::PROCESS_TRANS_OK_ST;
+const int NameChangeTransaction::PROCESS_TRANS_FAILED_ST;
+
+const int NameChangeTransaction::NCT_DERIVED_STATE_MIN;
+
+// Common transaction events
+const int NameChangeTransaction::SELECT_SERVER_EVT;
+const int NameChangeTransaction::SERVER_SELECTED_EVT;
+const int NameChangeTransaction::SERVER_IO_ERROR_EVT;
+const int NameChangeTransaction::NO_MORE_SERVERS_EVT;
+const int NameChangeTransaction::IO_COMPLETED_EVT;
+const int NameChangeTransaction::UPDATE_OK_EVT;
+const int NameChangeTransaction::UPDATE_FAILED_EVT;
+
+const int NameChangeTransaction::NCT_DERIVED_EVENT_MIN;
+
+NameChangeTransaction::
+NameChangeTransaction(isc::asiolink::IOService& io_service,
+                      dhcp_ddns::NameChangeRequestPtr& ncr,
+                      DdnsDomainPtr& forward_domain,
+                      DdnsDomainPtr& reverse_domain)
+    : io_service_(io_service), ncr_(ncr), forward_domain_(forward_domain),
+     reverse_domain_(reverse_domain), dns_client_(),
+     dns_update_status_(DNSClient::OTHER), dns_update_response_(),
+     forward_change_completed_(false), reverse_change_completed_(false),
+     current_server_list_(), current_server_(), next_server_pos_(0) {
+    if (!ncr_) {
+        isc_throw(NameChangeTransactionError, "NameChangeRequest cannot null");
+    }
+
+    if (ncr_->isForwardChange() && !(forward_domain_)) {
+        isc_throw(NameChangeTransactionError,
+                 "Forward change must have a forward domain");
+    }
+
+    if (ncr_->isReverseChange() && !(reverse_domain_)) {
+        isc_throw(NameChangeTransactionError,
+                 "Reverse change must have a reverse domain");
+    }
+}
+
+NameChangeTransaction::~NameChangeTransaction(){
+}
+
+void
+NameChangeTransaction::startTransaction() {
+    startModel(READY_ST);
+}
+
+void
+NameChangeTransaction::operator()(DNSClient::Status status) {
+    // Stow the completion status and re-enter the run loop with the event
+    // set to indicate IO completed.
+    // runModel is exception safe so we are good to call it here.
+    // It won't exit until we hit the next IO wait or the state model ends.
+    setDnsUpdateStatus(status);
+    runModel(IO_COMPLETED_EVT);
+}
+
+void
+NameChangeTransaction::defineEvents() {
+    // Call superclass impl first.
+    StateModel::defineEvents();
+
+    // Define NCT events.
+    defineEvent(SELECT_SERVER_EVT, "SELECT_SERVER_EVT");
+    defineEvent(SERVER_SELECTED_EVT, "SERVER_SELECTED_EVT");
+    defineEvent(SERVER_IO_ERROR_EVT, "SERVER_IO_ERROR_EVT");
+    defineEvent(NO_MORE_SERVERS_EVT, "NO_MORE_SERVERS_EVT");
+    defineEvent(IO_COMPLETED_EVT, "IO_COMPLETED_EVT");
+    defineEvent(UPDATE_OK_EVT, "UPDATE_OK_EVT");
+    defineEvent(UPDATE_FAILED_EVT, "UPDATE_FAILED_EVT");
+}
+
+void
+NameChangeTransaction::verifyEvents() {
+    // Call superclass impl first.
+    StateModel::verifyEvents();
+
+    // Verify NCT events.
+    getEvent(SELECT_SERVER_EVT);
+    getEvent(SERVER_SELECTED_EVT);
+    getEvent(SERVER_IO_ERROR_EVT);
+    getEvent(NO_MORE_SERVERS_EVT);
+    getEvent(IO_COMPLETED_EVT);
+    getEvent(UPDATE_OK_EVT);
+    getEvent(UPDATE_FAILED_EVT);
+}
+
+void
+NameChangeTransaction::defineStates() {
+    // Call superclass impl first.
+    StateModel::defineStates();
+    // This class is "abstract" in that it does not supply handlers for its
+    // states, derivations must do that therefore they must define them.
+}
+
+void
+NameChangeTransaction::verifyStates() {
+    // Call superclass impl first.
+    StateModel::verifyStates();
+
+    // Verify NCT states. This ensures that derivations provide the handlers.
+    getState(READY_ST);
+    getState(SELECTING_FWD_SERVER_ST);
+    getState(SELECTING_REV_SERVER_ST);
+    getState(PROCESS_TRANS_OK_ST);
+    getState(PROCESS_TRANS_FAILED_ST);
+}
+
+void
+NameChangeTransaction::onModelFailure(const std::string& explanation) {
+    setNcrStatus(dhcp_ddns::ST_FAILED);
+    LOG_ERROR(dctl_logger, DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR)
+                  .arg(explanation);
+}
+
+void
+NameChangeTransaction::setDnsUpdateStatus(const DNSClient::Status& status) {
+    dns_update_status_ = status;
+}
+
+void
+NameChangeTransaction::setForwardChangeCompleted(const bool value) {
+    forward_change_completed_ = value;
+}
+
+void
+NameChangeTransaction::setReverseChangeCompleted(const bool value) {
+    reverse_change_completed_ = value;
+}
+
+const dhcp_ddns::NameChangeRequestPtr&
+NameChangeTransaction::getNcr() const {
+    return (ncr_);
+}
+
+const TransactionKey&
+NameChangeTransaction::getTransactionKey() const {
+    return (ncr_->getDhcid());
+}
+
+dhcp_ddns::NameChangeStatus
+NameChangeTransaction::getNcrStatus() const {
+    return (ncr_->getStatus());
+}
+
+DdnsDomainPtr&
+NameChangeTransaction::getForwardDomain() {
+    return (forward_domain_);
+}
+
+DdnsDomainPtr&
+NameChangeTransaction::getReverseDomain() {
+    return (reverse_domain_);
+}
+
+void
+NameChangeTransaction::initServerSelection(const DdnsDomainPtr& domain) {
+    if (!domain) {
+        isc_throw(NameChangeTransactionError,
+                  "initServerSelection called with an empty domain");
+    }
+    current_server_list_ = domain->getServers();
+    next_server_pos_ = 0;
+    current_server_.reset();
+}
+
+bool
+NameChangeTransaction::selectNextServer() {
+    if ((current_server_list_) &&
+        (next_server_pos_ < current_server_list_->size())) {
+        current_server_  = (*current_server_list_)[next_server_pos_];
+        dns_update_response_.reset(new
+                                   D2UpdateMessage(D2UpdateMessage::INBOUND));
+        // @todo  Protocol is set on DNSClient constructor.  We need
+        // to propagate a configuration value downward, probably starting
+        // at global, then domain, then server
+        // Once that is supported we need to add it here.
+        dns_client_.reset(new DNSClient(dns_update_response_ , this,
+                                        DNSClient::UDP));
+        ++next_server_pos_;
+        return (true);
+    }
+
+    return (false);
+}
+
+const DNSClientPtr&
+NameChangeTransaction::getDNSClient() const {
+    return (dns_client_);
+}
+
+const DnsServerInfoPtr&
+NameChangeTransaction::getCurrentServer() const {
+    return (current_server_);
+}
+
+
+void
+NameChangeTransaction::setNcrStatus(const dhcp_ddns::NameChangeStatus& status) {
+    return (ncr_->setStatus(status));
+}
+
+DNSClient::Status
+NameChangeTransaction::getDnsUpdateStatus() const {
+    return (dns_update_status_);
+}
+
+const D2UpdateMessagePtr&
+NameChangeTransaction::getDnsUpdateResponse() const {
+    return (dns_update_response_);
+}
+
+bool
+NameChangeTransaction::getForwardChangeCompleted() const {
+    return (forward_change_completed_);
+}
+
+bool
+NameChangeTransaction::getReverseChangeCompleted() const {
+    return (reverse_change_completed_);
+}
+
+} // namespace isc::d2
+} // namespace isc
diff --git a/src/bin/d2/nc_trans.h b/src/bin/d2/nc_trans.h
new file mode 100644
index 0000000..d30dff7
--- /dev/null
+++ b/src/bin/d2/nc_trans.h
@@ -0,0 +1,431 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef NC_TRANS_H
+#define NC_TRANS_H
+
+/// @file nc_trans.h This file defines the class NameChangeTransaction.
+
+#include <asiolink/io_service.h>
+#include <exceptions/exceptions.h>
+#include <d2/d2_config.h>
+#include <d2/dns_client.h>
+#include <d2/state_model.h>
+#include <dhcp_ddns/ncr_msg.h>
+
+#include <boost/shared_ptr.hpp>
+#include <map>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Thrown if the transaction encounters a general error.
+class NameChangeTransactionError : public isc::Exception {
+public:
+    NameChangeTransactionError(const char* file, size_t line,
+                               const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Defines the type used as the unique key for transactions.
+typedef isc::dhcp_ddns::D2Dhcid TransactionKey;
+
+/// @brief Embodies the "life-cycle" required to carry out a DDNS update.
+///
+/// NameChangeTransaction is the base class that provides the common state
+/// model mechanics and services performing the DNS updates needed to carry out
+/// a DHCP_DDNS request as described by a NameChangeRequest.  It is derived
+/// from StateModel which supplies a simple, general purpose FSM implementation.
+///
+/// Upon construction, each transaction has all of the information and
+/// resources required to carry out its assigned request, including the list(s)
+/// of DNS server(s) needed. It is responsible for knowing what conversations
+/// it must have with which servers and in the order necessary to fulfill the
+/// request. Upon fulfillment of the request, the transaction's work is complete
+/// and it is destroyed.
+///
+/// Fulfillment of the request is carried out through the performance of the
+/// transaction's state model.  Using a state driven implementation accounts
+/// for the conditional processing flow necessary to meet the DDNS RFCs as well
+/// as the asynchronous nature of IO with DNS servers.
+///
+/// Derivations of the class are responsible for defining the state model and
+/// conversations necessary to carry out the specific of request.
+///
+/// Conversations with DNS servers are done through the use of the DNSClient
+/// class.  The DNSClient provides a IOService-based means a service which
+/// performs a single, packet exchange with a given DNS server.  It sends a
+/// single update to the server and returns the response, asynchronously,
+/// through a callback.  At each point in a transaction's state model, where
+/// an update is to be sent, the model "suspends" until notified by the
+/// DNSClient via the callbacka.  Suspension is done by posting a
+/// StateModel::NOP_EVT as the next event, stopping the state model execution.
+///
+/// Resuming state model execution when a DNS update completes is done by a
+/// call to StateModel::runStateModel() from within the DNSClient callback,
+/// with an event value of IO_COMPLETED_EVT (described below).
+///
+/// This class defines a set of events and states that are a common to all
+/// transactions. Each derivation may add define additional states and events
+/// as needed, but it must support the common set.  NameChangeTransaction
+/// does not supply any state handlers.  These are the sole responsibility of
+/// derivations.
+class NameChangeTransaction : public DNSClient::Callback, public StateModel {
+public:
+
+    //@{ States common to all transactions.
+
+    /// @brief State from which a transaction is started.
+    static const int READY_ST = SM_DERIVED_STATE_MIN + 1;
+
+    /// @brief State in which forward DNS server selection is done.
+    ///
+    /// Within this state, the actual selection of the next forward server
+    /// to use is conducted.  Upon conclusion of this state the next server
+    /// is either selected or it should transition out with NO_MORE_SERVERS_EVT
+    /// event.
+    static const int SELECTING_FWD_SERVER_ST = SM_DERIVED_STATE_MIN + 2;
+
+    /// @brief State in which reverse DNS server  selection is done.
+    ///
+    /// Within this state, the actual selection of the next reverse server
+    /// to use is conducted.  Upon conclusion of this state the next server
+    /// is either selected or it should transition out with NO_MORE_SERVERS_EVT
+    /// event.
+    static const int SELECTING_REV_SERVER_ST = SM_DERIVED_STATE_MIN + 3;
+
+    /// @brief State which processes successful transaction conclusion.
+    static const int PROCESS_TRANS_OK_ST = SM_DERIVED_STATE_MIN + 4;
+
+    /// @brief State which processes an unsuccessful transaction conclusion.
+    static const int PROCESS_TRANS_FAILED_ST = SM_DERIVED_STATE_MIN + 5;
+
+    /// @brief Value at which custom states in a derived class should begin.
+    static const int NCT_DERIVED_STATE_MIN = SM_DERIVED_STATE_MIN + 101;
+    //@}
+
+    //@{ Events common to all transactions.
+    /// @brief Issued when a server needs to be selected.
+    static const int SELECT_SERVER_EVT = SM_DERIVED_EVENT_MIN + 1;
+
+    /// @brief Issued when a server  has been selected.
+    static const int SERVER_SELECTED_EVT = SM_DERIVED_EVENT_MIN + 2;
+
+    /// @brief Issued when an update fails due to an IO error.
+    static const int SERVER_IO_ERROR_EVT = SM_DERIVED_EVENT_MIN + 3;
+
+    /// @brief Issued when there are no more servers from which to select.
+    /// This occurs when none of the servers in the list can be reached to
+    /// perform the update.
+
+    static const int NO_MORE_SERVERS_EVT =SM_DERIVED_EVENT_MIN +  4;
+    /// @brief Issued when a DNS update packet exchange has completed.
+    /// This occurs whenever the DNSClient callback is invoked whether the
+    /// exchange was successful or not.
+
+    static const int IO_COMPLETED_EVT = SM_DERIVED_EVENT_MIN + 5;
+    /// @brief Issued when the attempted update successfully completed.
+    /// This occurs when an DNS update packet was successfully processed
+    /// by the server.
+
+    static const int UPDATE_OK_EVT = SM_DERIVED_EVENT_MIN + 6;
+
+    /// @brief Issued when the attempted update fails to complete.
+    /// This occurs when an DNS update packet fails to process. The nature of
+    /// the failure is given by the DNSClient return status and the response
+    /// packet (if one was received).
+    static const int UPDATE_FAILED_EVT = SM_DERIVED_EVENT_MIN + 7;
+
+    /// @brief Value at which custom events in a derived class should begin.
+    static const int NCT_DERIVED_EVENT_MIN = SM_DERIVED_EVENT_MIN + 101;
+    //@}
+
+    /// @brief Constructor
+    ///
+    /// Instantiates a transaction that is ready to be started.
+    ///
+    /// @param io_service IO service to be used for IO processing
+    /// @param ncr is the NameChangeRequest to fulfill
+    /// @param forward_domain is the domain to use for forward DNS updates
+    /// @param reverse_domain is the domain to use for reverse DNS updates
+    ///
+    /// @throw NameChangeTransactionError if given an null request,
+    /// if forward change is enabled but forward domain is null, if
+    /// reverse change is enabled but reverse domain is null.
+    NameChangeTransaction(isc::asiolink::IOService& io_service,
+                          dhcp_ddns::NameChangeRequestPtr& ncr,
+                          DdnsDomainPtr& forward_domain,
+                          DdnsDomainPtr& reverse_domain);
+
+    /// @brief Destructor
+    virtual ~NameChangeTransaction();
+
+    /// @brief Begins execution of the transaction.
+    ///
+    /// This method invokes StateModel::startModel() with a value of READY_ST.
+    /// This causes transaction's state model to attempt to begin execution
+    /// with the state handler for READY_ST.
+    void startTransaction();
+
+    /// @brief Serves as the DNSClient IO completion event handler.
+    ///
+    /// This is the implementation of the method inherited by our derivation
+    /// from DNSClient::Callback.  When the DNSClient completes an update it
+    /// invokes this method as the completion handler.  This method stores
+    /// the given status and invokes runStateModel() with an event value of
+    /// IO_COMPLETED_EVT.
+    ///
+    /// @param status is the outcome of the DNS update packet exchange.
+    /// This method is exception safe.
+    virtual void operator()(DNSClient::Status status);
+
+protected:
+    /// @brief Adds events defined by NameChangeTransaction to the event set.
+    ///
+    /// This method adds the events common to NCR transaction processing to
+    /// the set of define events.  It invokes the superclass's implementation
+    /// first to maitain the hierarchical chain of event defintion.
+    /// Derivations of NameChangeTransaction must invoke its implementation
+    /// in like fashion.
+    ///
+    /// @throw StateModelError if an event definition is invalid or a duplicate.
+    virtual void defineEvents();
+
+    /// @brief Validates the contents of the set of events.
+    ///
+    /// This method verifies that the events defined by both the superclass and
+    /// this class are defined.  As with defineEvents, this method calls the
+    /// superclass's implementation first, to verify events defined by it and
+    /// then this implementation to verify events defined by
+    /// NameChangeTransaction.
+    ///
+    /// @throw StateModelError if an event value is undefined.
+    virtual void verifyEvents();
+
+    /// @brief Adds states defined by NameChangeTransaction to the state set.
+    ///
+    /// This method adds the states common to NCR transaction processing to
+    /// the dictionary of states.  It invokes the superclass's implementation
+    /// first to maitain the hierarchical chain of state defintion.
+    /// Derivations of NameChangeTransaction must invoke its implementation
+    /// in like fashion.
+    ///
+    /// @throw StateModelError if an state definition is invalid or a duplicate.
+    virtual void defineStates();
+
+    /// @brief Validates the contents of the set of states.
+    ///
+    /// This method verifies that the states defined by both the superclass and
+    /// this class are defined.  As with defineStates, this method calls the
+    /// superclass's implementation first, to verify states defined by it and
+    /// then this implementation to verify states defined by
+    /// NameChangeTransaction.
+    ///
+    /// @throw StateModelError if an event value is undefined.
+    virtual void verifyStates();
+
+    /// @brief Handler for fatal model execution errors.
+    ///
+    /// This handler is called by the StateModel implementation when the model
+    /// execution encounters a model violation:  attempt to call an unmapped
+    /// state, an event not valid for the current state, or an uncaught
+    /// exception thrown during a state handler invocation.  When such an
+    /// error occurs the transaction is deemed inoperable, and futher model
+    /// execution cannot be performed.  It marks the transaction as failed by
+    /// setting the NCR status to dhcp_ddns::ST_FAILED
+    ///
+    /// @param explanation is text detailing the error
+    virtual void onModelFailure(const std::string& explanation);
+
+    /// @brief Sets the update status to the given status value.
+    ///
+    /// @param status is the new value for the update status.
+    void setDnsUpdateStatus(const DNSClient::Status& status);
+
+    /// @brief Sets the update response packet to the given packet.
+    ///
+    /// @param response is the new response packet to assign.
+    void setDnsUpdateResponse(D2UpdateMessagePtr& response);
+
+    /// @brief Sets the forward change completion flag to the given value.
+    ///
+    /// @param value is the new value to assign to the flag.
+    void setForwardChangeCompleted(const bool value);
+
+    /// @brief Sets the reverse change completion flag to the given value.
+    ///
+    /// @param value is the new value to assign to the flag.
+    void setReverseChangeCompleted(const bool value);
+
+    /// @brief Sets the status of the transaction's NameChangeRequest
+    ///
+    /// @param status is the new value to assign to the NCR status.
+    void setNcrStatus(const dhcp_ddns::NameChangeStatus& status);
+
+    /// @brief Initializes server selection from the given DDNS domain.
+    ///
+    /// Method prepares internal data to conduct server selection from the
+    /// list of servers supplied by the given domain.  This method should be
+    /// called when a transaction is ready to begin selecting servers from
+    /// a new list.  Typically this will be prior to starting the updates for
+    /// a given DNS direction.
+    ///
+    /// @param domain is the domain from which server selection is to be
+    /// conducted.
+    void initServerSelection(const DdnsDomainPtr& domain);
+
+    /// @brief Selects the next server in the current server list.
+    ///
+    /// This method is used to iterate over the list of servers.  If there are
+    /// no more servers in the list, it returns false.  Otherwise it sets the
+    /// the current server to the next server and creates a new DNSClient
+    /// instance.
+    ///
+    /// @return True if a server has been selected, false if there are no more
+    /// servers from which to select.
+    bool selectNextServer();
+
+    /// @brief Fetches the currently selected server.
+    ///
+    /// @return A const pointer reference to the DnsServerInfo of the current
+    /// server.
+    const DnsServerInfoPtr& getCurrentServer() const;
+
+    /// @brief Fetches the DNSClient instance
+    ///
+    /// @return A const pointer reference to the DNSClient
+    const DNSClientPtr& getDNSClient() const;
+
+public:
+    /// @brief Fetches the NameChangeRequest for this transaction.
+    ///
+    /// @return A const pointer reference to the NameChangeRequest.
+    const dhcp_ddns::NameChangeRequestPtr& getNcr() const;
+
+    /// @brief Fetches the unique key that identifies this transaction.
+    ///
+    /// Transactions are uniquely identified by a TransactionKey. Currently
+    /// this is wrapper around a D2Dhcid.
+    ///
+    /// @return A const reference to the TransactionKey.
+    const TransactionKey& getTransactionKey() const;
+
+    /// @brief Fetches the NameChangeRequest status of the transaction.
+    ///
+    /// This is the current status of the NameChangeRequest, not to
+    /// be confused with the state of the transaction.  Once the transaction
+    /// is reached its conclusion, the request will end up with a final
+    /// status.
+    ///
+    /// @return A dhcp_ddns::NameChangeStatus representing the current
+    /// status of the transaction.
+    dhcp_ddns::NameChangeStatus getNcrStatus() const;
+
+    /// @brief Fetches the forward DdnsDomain.
+    ///
+    /// @return A pointer reference to the forward DdnsDomain.  If the
+    /// the request does not include a forward change, the pointer will empty.
+    DdnsDomainPtr& getForwardDomain();
+
+    /// @brief Fetches the reverse DdnsDomain.
+    ///
+    /// @return A pointer reference to the reverse DdnsDomain.  If the
+    /// the request does not include a reverse change, the pointer will empty.
+    DdnsDomainPtr& getReverseDomain();
+
+    /// @brief Fetches the most recent DNS update status.
+    ///
+    /// @return A DNSClient::Status indicating the result of the most recent
+    /// DNS update to complete.
+    DNSClient::Status getDnsUpdateStatus() const;
+
+    /// @brief Fetches the most recent DNS update response packet.
+    ///
+    /// @return A const pointer reference to the D2UpdateMessage most recently
+    /// received.
+    const D2UpdateMessagePtr& getDnsUpdateResponse() const;
+
+    /// @brief Returns whether the forward change has completed or not.
+    ///
+    /// The value returned is only meaningful if the NameChangeRequest calls
+    /// for a forward change to be done. The value returned indicates if
+    /// forward change has been completed successfully.
+    ///
+    /// @return True if the forward change has been completed, false otherwise.
+    bool getForwardChangeCompleted() const;
+
+    /// @brief Returns whether the reverse change has completed or not.
+    ///
+    /// The value returned is only meaningful if the NameChangeRequest calls
+    /// for a reverse change to be done. The value returned indicates if
+    /// reverse change has been completed successfully.
+    ///
+    /// @return True if the reverse change has been completed, false otherwise.
+    bool getReverseChangeCompleted() const;
+
+private:
+    /// @brief The IOService which should be used to for IO processing.
+    isc::asiolink::IOService& io_service_;
+
+    /// @brief The NameChangeRequest that the transaction is to fulfill.
+    dhcp_ddns::NameChangeRequestPtr ncr_;
+
+    /// @brief The forward domain that matches the request.
+    ///
+    /// The forward "domain" is DdnsDomain which contains all of the information
+    /// necessary, including the list of DNS servers to be used for a forward
+    /// change.
+    DdnsDomainPtr forward_domain_;
+
+    /// @brief The reverse domain that matches the request.
+    ///
+    /// The reverse "domain" is DdnsDomain which contains all of the information
+    /// necessary, including the list of DNS servers to be used for a reverse
+    /// change.
+    DdnsDomainPtr reverse_domain_;
+
+    /// @brief The DNSClient instance that will carry out DNS packet exchanges.
+    DNSClientPtr dns_client_;
+
+    /// @brief The outcome of the most recently completed DNS packet exchange.
+    DNSClient::Status dns_update_status_;
+
+    /// @brief The DNS update response packet most recently received.
+    D2UpdateMessagePtr dns_update_response_;
+
+    /// @brief Indicator for whether or not the forward change completed ok.
+    bool forward_change_completed_;
+
+    /// @brief Indicator for whether or not the reverse change completed ok.
+    bool reverse_change_completed_;
+
+    /// @brief Pointer to the current server selection list.
+    DnsServerInfoStoragePtr current_server_list_;
+
+    /// @brief Pointer to the currently selected server.
+    DnsServerInfoPtr current_server_;
+
+    /// @brief Next server position in the list.
+    ///
+    /// This value is always the position of the next selection in the server
+    /// list, which may be beyond the end of the list.
+    size_t next_server_pos_;
+};
+
+/// @brief Defines a pointer to a NameChangeTransaction.
+typedef boost::shared_ptr<NameChangeTransaction> NameChangeTransactionPtr;
+
+} // namespace isc::d2
+} // namespace isc
+#endif
diff --git a/src/bin/d2/state_model.cc b/src/bin/d2/state_model.cc
new file mode 100644
index 0000000..6786e43
--- /dev/null
+++ b/src/bin/d2/state_model.cc
@@ -0,0 +1,380 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <d2/d2_log.h>
+#include <d2/state_model.h>
+
+#include <string>
+
+namespace isc {
+namespace d2 {
+
+/********************************** State *******************************/
+
+State::State(const int value, const std::string& label, StateHandler handler)
+        : LabeledValue(value, label), handler_(handler) {
+}
+
+State::~State() {
+}
+
+void
+State::run() {
+        (handler_)();
+}
+
+/********************************** StateSet *******************************/
+
+StateSet::StateSet() {
+}
+
+StateSet::~StateSet() {
+}
+
+void
+StateSet::add(const int value, const std::string& label, StateHandler handler) {
+    try {
+        LabeledValueSet::add(LabeledValuePtr(new State(value, label, handler)));
+    } catch (const std::exception& ex) {
+        isc_throw(StateModelError, "StateSet: cannot add state :" << ex.what());
+    }
+
+}
+
+const StatePtr
+StateSet::getState(int value) {
+    if (!isDefined(value)) {
+        isc_throw(StateModelError," StateSet: state is undefined");
+    }
+
+    // Since we have to use dynamic casting, to get a state pointer
+    // we can't return a reference.
+    StatePtr state = boost::dynamic_pointer_cast<State>(get(value));
+    return (state);
+}
+
+/********************************** StateModel *******************************/
+
+
+// Common state model states
+const int StateModel::NEW_ST;
+const int StateModel::END_ST;
+
+const int StateModel::SM_DERIVED_STATE_MIN;
+
+// Common state model events
+const int StateModel::NOP_EVT;
+const int StateModel::START_EVT;
+const int StateModel::END_EVT;
+const int StateModel::FAIL_EVT;
+
+const int StateModel::SM_DERIVED_EVENT_MIN;
+
+StateModel::StateModel() : events_(), states_(), dictionaries_initted_(false),
+                          curr_state_(NEW_ST), prev_state_(NEW_ST),
+                          last_event_(NOP_EVT), next_event_(NOP_EVT),
+                          on_entry_flag_(false), on_exit_flag_(false) {
+}
+
+StateModel::~StateModel(){
+}
+
+void
+StateModel::startModel(const int start_state) {
+    // First let's build and verify the dictionary of events.
+    try {
+        defineEvents();
+        verifyEvents();
+    } catch (const std::exception& ex) {
+        isc_throw(StateModelError, "Event set is invalid: " << ex.what());
+    }
+
+    // Next let's build and verify the dictionary of states.
+    try {
+        defineStates();
+        verifyStates();
+    } catch (const std::exception& ex) {
+        isc_throw(StateModelError, "State set is invalid: " << ex.what());
+    }
+
+    // Record that we are good to go.
+    dictionaries_initted_ = true;
+
+    // Set the current state to starting state and enter the run loop.
+    setState(start_state);
+    runModel(START_EVT);
+}
+
+void
+StateModel::runModel(unsigned int run_event) {
+    /// If the dictionaries aren't built bail out.
+    if (!dictionaries_initted_) {
+       abortModel("runModel invoked before model has been initialized");
+    }
+
+    try {
+        // Seed the loop with the given event as the next to process.
+        postNextEvent(run_event);
+        do {
+            // Invoke the current state's handler.  It should consume the
+            // next event, then determine what happens next by setting
+            // current state and/or the next event.
+            getState(curr_state_)->run();
+
+            // Keep going until a handler sets next event to a NOP_EVT.
+        } while (!isModelDone() && getNextEvent() != NOP_EVT);
+    }
+    catch (const std::exception& ex) {
+        // The model has suffered an unexpected exception. This constitutes
+        // a model violation and indicates a programmatic shortcoming.
+        // In theory, the model should account for all error scenarios and
+        // deal with them accordingly.  Transition to END_ST with FAILED_EVT
+        // via abortModel.
+        abortModel(ex.what());
+    }
+}
+
+void
+StateModel::nopStateHandler() {
+}
+
+
+void
+StateModel::defineEvent(unsigned int event_value, const std::string& label) {
+    if (!isModelNew()) {
+        // Don't allow for self-modifying models.
+        isc_throw(StateModelError, "Events may only be added to a new model."
+                   << event_value << " - " << label);
+    }
+
+    // Attempt to add the event to the set.
+    try {
+        events_.add(event_value, label);
+    } catch (const std::exception& ex) {
+        isc_throw(StateModelError, "Error adding event: " << ex.what());
+    }
+}
+
+const EventPtr&
+StateModel::getEvent(unsigned int event_value) {
+    if (!events_.isDefined(event_value)) {
+        isc_throw(StateModelError,
+                  "Event value is not defined:" << event_value);
+    }
+
+    return (events_.get(event_value));
+}
+
+void
+StateModel::defineState(unsigned int state_value, const std::string& label,
+    StateHandler handler) {
+    if (!isModelNew()) {
+        // Don't allow for self-modifying maps.
+        isc_throw(StateModelError, "States may only be added to a new model."
+                   << state_value << " - " << label);
+    }
+
+    // Attempt to add the state to the set.
+    try {
+        states_.add(state_value, label, handler);
+    } catch (const std::exception& ex) {
+        isc_throw(StateModelError, "Error adding state: " << ex.what());
+    }
+}
+
+const StatePtr
+StateModel::getState(unsigned int state_value) {
+    if (!states_.isDefined(state_value)) {
+        isc_throw(StateModelError,
+                  "State value is not defined:" << state_value);
+    }
+
+    return (states_.getState(state_value));
+}
+
+void
+StateModel::defineEvents() {
+    defineEvent(NOP_EVT, "NOP_EVT");
+    defineEvent(START_EVT, "START_EVT");
+    defineEvent(END_EVT, "END_EVT");
+    defineEvent(FAIL_EVT, "FAIL_EVT");
+}
+
+void
+StateModel::verifyEvents() {
+    getEvent(NOP_EVT);
+    getEvent(START_EVT);
+    getEvent(END_EVT);
+    getEvent(FAIL_EVT);
+}
+
+void
+StateModel::defineStates() {
+    defineState(NEW_ST, "NEW_ST",
+                boost::bind(&StateModel::nopStateHandler, this));
+    defineState(END_ST, "END_ST",
+                boost::bind(&StateModel::nopStateHandler, this));
+}
+
+void
+StateModel::verifyStates() {
+    getState(NEW_ST);
+    getState(END_ST);
+}
+
+void 
+StateModel::onModelFailure(const std::string&) {
+    // Empty implementation to make deriving classes simpler.
+}
+
+void
+StateModel::transition(unsigned int state, unsigned int event) {
+    setState(state);
+    postNextEvent(event);
+}
+
+void
+StateModel::endModel() {
+    transition(END_ST, END_EVT);
+}
+
+void
+StateModel::abortModel(const std::string& explanation) {
+    transition(END_ST, FAIL_EVT);
+
+    std::ostringstream stream ;
+    stream << explanation << " : " << getContextStr();
+    onModelFailure(stream.str());
+}
+
+void
+StateModel::setState(unsigned int state) {
+    if (state != END_ST && !states_.isDefined(state)) {
+        isc_throw(StateModelError,
+                  "Attempt to set state to an undefined value: " << state );
+    }
+
+    prev_state_ = curr_state_;
+    curr_state_ = state;
+
+    // Set the "do" flags if we are transitioning.
+    on_entry_flag_ = ((state != END_ST) && (prev_state_ != curr_state_));
+
+    // At this time they are calculated the same way.
+    on_exit_flag_ = on_entry_flag_;
+}
+
+void
+StateModel::postNextEvent(unsigned int event_value) {
+    // Check for FAIL_EVT as special case of model error before events are
+    // defined.
+    if (event_value != FAIL_EVT && !events_.isDefined(event_value)) {
+        isc_throw(StateModelError,
+                  "Attempt to post an undefined event, value: " << event_value);
+    }
+
+    last_event_ = next_event_;
+    next_event_ = event_value;
+}
+
+bool
+StateModel::doOnEntry() {
+    bool ret = on_entry_flag_;
+    on_entry_flag_ = false;
+    return (ret);
+}
+
+bool
+StateModel::doOnExit() {
+    bool ret = on_exit_flag_;
+    on_exit_flag_ = false;
+    return (ret);
+}
+
+unsigned int
+StateModel::getCurrState() const {
+    return (curr_state_);
+}
+
+unsigned int
+StateModel::getPrevState() const {
+    return (prev_state_);
+}
+
+unsigned int
+StateModel::getLastEvent() const {
+    return (last_event_);
+}
+
+unsigned int
+StateModel::getNextEvent() const {
+    return (next_event_);
+}
+bool
+StateModel::isModelNew() const {
+    return (curr_state_ == NEW_ST);
+}
+
+bool
+StateModel::isModelRunning() const {
+    return ((curr_state_ != NEW_ST) && (curr_state_ != END_ST));
+}
+
+bool
+StateModel::isModelWaiting() const {
+    return (isModelRunning() && (next_event_ == NOP_EVT));
+}
+
+bool
+StateModel::isModelDone() const {
+    return (curr_state_ == END_ST);
+}
+
+bool
+StateModel::didModelFail() const {
+    return (isModelDone() && (next_event_ == FAIL_EVT));
+}
+
+std::string
+StateModel::getStateLabel(const int state) const {
+    return (states_.getLabel(state));
+}
+
+std::string
+StateModel::getEventLabel(const int event) const {
+    return (events_.getLabel(event));
+}
+
+std::string
+StateModel::getContextStr() const {
+    std::ostringstream stream;
+    stream << "current state: [ "
+            << curr_state_ << " " << getStateLabel(curr_state_)
+            << " ] next event: [ "
+            << next_event_ << " " << getEventLabel(next_event_) << " ]";
+    return(stream.str());
+}
+
+std::string
+StateModel::getPrevContextStr() const {
+    std::ostringstream stream;
+    stream << "previous state: [ "
+           << prev_state_ << " " << getStateLabel(prev_state_)
+           << " ] last event: [ "
+           << next_event_ << " " << getEventLabel(last_event_) << " ]";
+    return(stream.str());
+}
+
+} // namespace isc::d2
+} // namespace isc
diff --git a/src/bin/d2/state_model.h b/src/bin/d2/state_model.h
new file mode 100644
index 0000000..1596bb6
--- /dev/null
+++ b/src/bin/d2/state_model.h
@@ -0,0 +1,672 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef STATE_MODEL_H
+#define STATE_MODEL_H
+
+/// @file state_model.h This file defines the class StateModel.
+
+#include <asiolink/io_service.h>
+#include <exceptions/exceptions.h>
+#include <d2/d2_config.h>
+#include <d2/dns_client.h>
+#include <d2/labeled_value.h>
+#include <dhcp_ddns/ncr_msg.h>
+
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include <string>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Thrown if the state machine encounters a general error.
+class StateModelError : public isc::Exception {
+public:
+    StateModelError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Define an Event.
+typedef LabeledValue Event;
+
+/// @brief Define Event pointer.
+typedef LabeledValuePtr EventPtr;
+
+/// @brief Defines a pointer to an instance method for handling a state.
+typedef boost::function<void()> StateHandler;
+
+/// @brief Defines a State within the State Model.
+///
+/// This class provides the means to define a state within a set or dictionary
+/// of states, and assign the state an handler method to execute the state's
+/// actions.  It derives from LabeledValue which allows a set of states to be
+/// keyed by integer constants.
+class State : public LabeledValue {
+public:
+    /// @brief Constructor
+    ///
+    /// @param value is the numeric value of the state
+    /// @param label is the text label to assign to the state
+    /// @param handler is the bound instance method which handles the state's
+    /// action.
+    ///
+    /// A typical invocation might look this:
+    ///
+    /// @code
+    ///     State(SOME_INT_VAL, "SOME_INT_VAL",
+    ///            boost::bind(&StateModelDerivation::someHandler, this));
+    /// @endcode
+    ///
+    /// @throw StateModelError if label is null or blank.
+    State(const int value, const std::string& label, StateHandler handler);
+
+    /// @brief Destructor
+    virtual ~State();
+
+    /// @brief Invokes the State's handler.
+    void run();
+
+private:
+    /// @brief Bound instance method pointer to the state's handler method.
+    StateHandler handler_;
+};
+
+/// @brief Defines a shared pointer to a State.
+typedef boost::shared_ptr<State> StatePtr;
+
+/// @brief Implements a unique set or dictionary of states.
+///
+/// This class provides the means to construct and access a unique set of
+/// states.  This provide the ability to validate state values, look up their
+/// text labels, and their handlers.
+class StateSet : public LabeledValueSet {
+public:
+    /// @brief Constructor
+    StateSet();
+
+    /// @brief Destructor
+    virtual ~StateSet();
+
+    /// @brief Adds a state definition to the set of states.
+    ///
+    /// @param value is the numeric value of the state
+    /// @param label is the text label to assig to the state
+    /// @param handler is the bound instance method which handles the state's
+    ///
+    /// @throw StateModelError if the value is already defined in the set, or
+    /// if the label is null or blank.
+    void add(const int value, const std::string& label, StateHandler handler);
+
+    /// @brief Fetches a state for the given value.
+    ///
+    /// @param value the numeric value of the state desired
+    ///
+    /// @return A constant pointer the State found.
+    /// Note, this relies on dynamic cast and cannot return a pointer reference.
+    ///
+    /// @throw StateModelError if the value is undefined.
+    const StatePtr getState(int value);
+};
+
+/// @brief Implements a finite state machine.
+///
+/// StateModel is an abstract class that provides the structure and mechanics
+/// of a basic finite state machine.
+///
+/// The state model implementation used is a very basic approach. The model
+/// uses numeric constants to identify events and states, and maintains
+/// dictionaries of defined events and states.  Event and state definitions
+/// include a text label for logging purposes.  Additionally, each state
+/// definition includes a state handler. State handlers are methods which
+/// implement the actions that need to occur when the model is "in" a given
+/// state.  The implementation provides methods to add entries to and verify
+/// the contents of both dictionaries.
+///
+/// During model execution, the following context is tracked:
+///
+/// * current state - The current state of the model
+/// * previous state -  The state the model was in prior to the current state
+/// * next event - The next event to be consumed
+/// * last event - The event most recently consumed
+///
+/// When invoked, a state handler determines what it should do based upon the
+/// next event including what the next state and event should be. In other
+/// words the state transition knowledge is distributed among the state
+/// handlers rather than encapsulated in some form of state transition table.
+///
+/// Events "posted" from within the state handlers are "internally" triggered
+/// events.  Events "posted" from outside the state model, such as through
+/// the invocation of a callback are "externally" triggered.
+///
+/// StateModel defines two states:
+///
+/// * NEW_ST - State that a model is in following instantiation. It remains in
+/// this state until model execution has begun.
+/// * END_ST - State that a model is in once it has reached its conclusion.
+///
+/// and the following events:
+///
+/// * START_EVT - Event used to start model execution.
+/// * NOP_EVT - Event used to signify that the model stop and wait for an
+/// external event, such as the completion of an asynchronous IO operation.
+/// * END_EVT - Event used to trigger a normal conclusion of the model. This
+/// means only that the model was traversed from start to finish, without any
+/// model violations (i.e. invalid state, event, or transition) or uncaught
+/// exceptions.
+/// * FAIL_EVT - Event to trigger an abnormal conclusion of the model. This
+/// event is posted internally when model execution fails due to a model
+/// violation or uncaught exception.  It signifies that the model has reached
+/// an inoperable condition.
+///
+/// Derivations add their own states and events appropriate for their state
+/// model.  Note that NEW_ST and END_ST do not support handlers.  No work can
+/// be done (events consumed) prior to starting the model nor can work be done
+/// once the model has ended.
+///
+/// Model execution consists of iteratively invoking the state handler
+/// indicated by the current state which should consume the next event. As the
+/// handlers post events and/or change the state, the model is traversed. The
+/// loop stops whenever the model cannot continue without an externally
+/// triggered event or when it has reached its final state.  In the case of
+/// the former, the loop may be re-entered upon arrival of the external event.
+///
+/// This loop is implemented in the runModel method.  This method accepts an
+/// event as argument which it "posts" as the next event.  It then retrieves the
+/// handler for the current state from the handler map and invokes it. runModel
+/// repeats this process until it either a NOP_EVT posts or the state changes
+/// to END_ST.  In other words each invocation of runModel causes the model to
+/// be traversed from the current state until it must wait or ends.
+///
+/// Re-entering the "loop" upon the occurrence of an external event is done by
+/// invoking runModel with the appropriate event.  As before, runModel will
+/// loop until either the NOP_EVT occurs or until the model reaches its end.
+///
+/// A StateModel (derivation) is in the NEW_ST when constructed and remains
+/// there until it has been "started".  Starting the model is done by invoking
+/// the startModel method which accepts a state as a parameter.  This parameter
+/// specifies the "start" state and it becomes the current state.
+
+/// The first task undertaken by startModel is to initialize and verify the
+/// the event and state dictionaries.  The following virtual methods are
+/// provided for this:
+///
+/// * defineEvents - define events
+/// * verifyEvents - verifies that the expected events are defined
+/// * defineStates - defines states
+/// * verifyStates - verifies that the expected states are defined
+///
+/// The concept behind the verify methods is to provide an initial sanity
+/// check of the dictionaries.  This should help avoid using undefined event
+/// or state values accidentally.
+///
+/// These methods are intended to be implemented by each "layer" in a StateModel
+/// derivation hierarchy.  This allows each layer to define additional events
+/// and states.
+///
+/// Once the dictionaries have been properly initialized, the startModel method
+/// invokes runModel with an event of START_EVT.  From this point forward and
+/// until the model reaches the END_ST or fails, it is considered to be
+/// "running".  If the model encounters a NOP_EVT then it is "running" and
+/// "waiting".   If the model reaches END_ST with an END_EVT it is considered
+/// "done".  If the  model fails (END_ST with a FAILED_EVT) it is considered
+/// "done" and "failed".  There are several boolean status methods which may
+/// be used to check these conditions.
+///
+/// To progress from one state to the another, state handlers invoke use
+/// the method, transition.  This method accepts a state and an event as
+/// parameters.  These values become the current state and the next event
+/// respectively.  This has the effect of entering the given state having posted
+/// the given event.  The postEvent method may be used to post a new event
+/// to the current state.
+///
+/// Bringing the model to a normal end is done by invoking the endModel method
+/// which transitions the model to END_ST with END_EVT.  Bringing the model to
+/// an abnormal end is done via the abortModel method, which transitions the
+/// model to END_ST with FAILED_EVT.
+class StateModel {
+public:
+
+    //@{ States common to all models.
+    /// @brief State that a state model is in immediately after construction.
+    static const int NEW_ST = 0;
+
+    /// @brief Final state, all the state model has reached its conclusion.
+    static const int END_ST = 1;
+
+    /// @brief Value at which custom states in a derived class should begin.
+    static const int SM_DERIVED_STATE_MIN = 11;
+    //@}
+
+    //@{ Events common to all state models.
+    /// @brief Signifies that no event has occurred.
+    /// This is event used to interrupt the event loop to allow waiting for
+    /// an IO event or when there is no more work to be done.
+    static const int NOP_EVT = 0;
+
+    /// @brief Event issued to start the model execution.
+    static const int START_EVT = 1;
+
+    /// @brief Event issued to end the model execution.
+    static const int END_EVT = 2;
+
+    /// @brief Event issued to abort the model execution.
+    static const int FAIL_EVT = 3;
+
+    /// @brief Value at which custom events in a derived class should begin.
+    static const int SM_DERIVED_EVENT_MIN = 11;
+    //@}
+
+    /// @brief Constructor
+    StateModel();
+
+    /// @brief Destructor
+    virtual ~StateModel();
+
+    /// @brief Begins execution of the model.
+    ///
+    /// This method invokes the define and verify methods for both events and
+    /// states to initialize their respective dictionaries. It then starts
+    /// the model execution setting the current state to the given start state,
+    /// and the event to START_EVT.
+    ///
+    /// @param start_state is the state in which to begin execution.
+    ///
+    /// @throw StateModelError or others indirectly, as this method calls
+    /// dictionary define and verify methods.
+    void startModel(const int start_state);
+
+    /// @brief Processes events through the state model
+    ///
+    /// This method implements the state model "execution loop".  It uses
+    /// the given event as the next event to process and begins invoking
+    /// the state handler for the current state.   As described above, the
+    /// invoked state handler consumes the next event and then determines the
+    /// next event and the current state as required to implement the business
+    /// logic. The method continues to loop until the next event posted is
+    /// NOP_EVT or the model ends.
+    ///
+    /// Any exception thrown during the loop is caught, logged, and the
+    /// model is aborted with a FAIL_EVT.  The derivation's state
+    /// model is expected to account for any possible errors so any that
+    /// escape are treated as unrecoverable.
+    ///
+    /// @param event is the next event to process
+    ///
+    /// This method is guaranteed not to throw.
+    void runModel(unsigned int event);
+
+    /// @brief Conducts a normal transition to the end of the model.
+    ///
+    /// This method posts an END_EVT and sets the current state to END_ST.
+    /// It should be called by any state handler in the model from which
+    /// an exit leads to the model end.  In other words, if the transition
+    /// out of a particular state is to the end of the model, that state's
+    /// handler should call endModel.
+    void endModel();
+
+    /// @brief An empty state handler.
+    ///
+    /// This method is primarily used to permit special states, NEW_ST and
+    /// END_ST to be included in the state dictionary.  Currently it is an
+    /// empty method.
+    void nopStateHandler();
+
+protected:
+    /// @brief Populates the set of events.
+    ///
+    /// This method is used to construct the set of valid events. Each class
+    /// within a StateModel derivation hierarchy uses this method to add any
+    /// events it defines to the set.  Each derivation's implementation must
+    /// also call it's superclass's implementation.  This allows each class
+    /// within the hierarchy to make contributions to the set of defined
+    /// events. Implementations use the method, defineEvent(), to add event
+    /// definitions.  An example of the derivation's implementation follows:
+    ///
+    /// @code
+    /// void StateModelDerivation::defineEvents() {
+    ///     // Call the superclass implementation.
+    ///     StateModelDerivation::defineEvents();
+    ///
+    ///     // Add the events defined by the derivation.
+    ///     defineEvent(SOME_CUSTOM_EVT_1, "CUSTOM_EVT_1");
+    ///     defineEvent(SOME_CUSTOM_EVT_2, "CUSTOM_EVT_2");
+    ///     :
+    /// }
+    /// @endcode
+    virtual void defineEvents();
+
+    /// @brief Adds an event value and associated label to the set of events.
+    ///
+    /// @param value is the numeric value of the event
+    /// @param label is the text label of the event used in log messages and
+    /// exceptions.
+    ///
+    /// @throw StateModelError if the model has already been started, if
+    /// the value is already defined, or if the label is empty.
+    void defineEvent(unsigned int value, const std::string& label);
+
+    /// @brief Fetches the event referred to by value.
+    ///
+    /// @param value is the numeric value of the event desired.
+    ///
+    /// @return returns a constant pointer reference to the event if found
+    ///
+    /// @throw StateModelError if the event is not defined.
+    const EventPtr& getEvent(unsigned int value);
+
+    /// @brief Validates the contents of the set of events.
+    ///
+    /// This method is invoked immediately after the defineEvents method and
+    /// is used to verify that all the required events are defined.  If the
+    /// event set is determined to be invalid this method should throw a
+    /// StateModelError.  As with the defineEvents method, each class within
+    /// a StateModel derivation hierarchy must supply an implementation
+    /// which calls it's superclass's implementation as well as verifying any
+    /// events added by the derivation.  Validating an event is accomplished
+    /// by simply attempting to fetch an event by its value from the event set.
+    /// An example of the derivation's implementation follows:
+    ///
+    /// @code
+    /// void StateModelDerivation::verifyEvents() {
+    ///     // Call the superclass implementation.
+    ///     StateModelDerivation::verifyEvents();
+    ///
+    ///     // Verify the events defined by the derivation.
+    ///     getEvent(SOME_CUSTOM_EVT_1, "CUSTOM_EVT_1");
+    ///     getEvent(SOME_CUSTOM_EVT_2, "CUSTOM_EVT_2");
+    ///     :
+    /// }
+    /// @endcode
+    virtual void verifyEvents();
+
+    /// @brief Populates the set of states.
+    ///
+    /// This method is used to construct the set of valid states. Each class
+    /// within a StateModel derivation hierarchy uses this method to add any
+    /// states it defines to the set.  Each derivation's implementation must
+    /// also call it's superclass's implementation.  This allows each class
+    /// within the hierarchy to make contributions to the set of defined
+    /// states. Implementations use the method, defineState(), to add state
+    /// definitions.  An example of the derivation's implementation follows:
+    ///
+    /// @code
+    /// void StateModelDerivation::defineStates() {
+    ///     // Call the superclass implementation.
+    ///     StateModelDerivation::defineStates();
+    ///
+    ///     // Add the states defined by the derivation.
+    ///     defineState(SOME_ST, "SOME_ST",
+    ///                 boost::bind(&StateModelDerivation::someHandler, this));
+    ///     :
+    /// }
+    /// @endcode
+    virtual void defineStates();
+
+    /// @brief Adds an state value and associated label to the set of states.
+    ///
+    /// @param value is the numeric value of the state
+    /// @param label is the text label of the state used in log messages and
+    /// exceptions.
+    /// @param handler is the bound instance method which implements the state's
+    /// actions.
+    ///
+    /// @throw StateModelError if the model has already been started, if
+    /// the value is already defined, or if the label is empty.
+    void defineState(unsigned int value, const std::string& label,
+                     StateHandler handler);
+
+    /// @brief Fetches the state referred to by value.
+    ///
+    /// @param value is the numeric value of the state desired.
+    ///
+    /// @return returns a constant pointer to the state if found
+    ///
+    /// @throw StateModelError if the state is not defined.
+    const StatePtr getState(unsigned int value);
+
+    /// @brief Validates the contents of the set of states.
+    ///
+    /// This method is invoked immediately after the defineStates method and
+    /// is used to verify that all the required states are defined.  If the
+    /// state set is determined to be invalid this method should throw a
+    /// StateModelError.  As with the defineStates method, each class within
+    /// a StateModel derivation hierarchy must supply an implementation
+    /// which calls it's superclass's implementation as well as verifying any
+    /// states added by the derivation.  Validating an state is accomplished
+    /// by simply attempting to fetch the state by its value from the state set.
+    /// An example of the derivation's implementation follows:
+    ///
+    /// @code
+    /// void StateModelDerivation::verifyStates() {
+    ///     // Call the superclass implementation.
+    ///     StateModelDerivation::verifyStates();
+    ///
+    ///     // Verify the states defined by the derivation.
+    ///     getState(SOME_CUSTOM_EVT_2);
+    ///     :
+    /// }
+    /// @endcode
+    virtual void verifyStates();
+
+    /// @brief Handler for fatal model execution errors.
+    ///
+    /// This method is called when an unexpected error renders during
+    /// model execution, such as a state handler throwing an exception.
+    /// It provides derivations an opportunity to act accordingly by setting
+    /// the appropriate status or taking other remedial action.   This allows
+    /// the model execution loop to remain exception safe.  This default
+    /// implementation does nothing.
+    ///
+    /// @param explanation text detailing the error and state machine context
+    virtual void onModelFailure(const std::string& explanation);
+
+    /// @brief Sets up the model to transition into given state with a given
+    /// event.
+    ///
+    /// This updates the model's notion of the current state and the next
+    /// event to process.  State handlers use this method to move from one state
+    /// to the next.
+    ///
+    /// @param state the new value to assign to the current state.
+    /// @param event the new value to assign to the next event.
+    ///
+    /// @throw StateModelError if the state is invalid.
+    void transition(unsigned int state, unsigned int event);
+
+    /// @brief Aborts model execution.
+    ///
+    /// This method posts a FAILED_EVT and sets the current state to END_ST.
+    /// It is called internally when a model violation occurs. Violations are
+    /// any sort of inconsistency such as attempting to reference an invalid
+    /// state, or if the next event is not valid for the current state, or a
+    /// state handler throws an uncaught exception.
+    ///
+    /// @param explanation is text detailing the reason for aborting.
+    void abortModel(const std::string& explanation);
+
+    /// @brief Sets the current state to the given state value.
+    ///
+    /// This updates the model's notion of the current state and is the
+    /// state whose handler will be executed on the next iteration of the run
+    /// loop.  This is intended primarily for internal use and testing. It is
+    /// unlikely that transitioning to a new state without a new event is of
+    /// much use.
+    ///
+    /// @param state the new value to assign to the current state.
+    ///
+    /// @throw StateModelError if the state is invalid.
+    void setState(unsigned int state);
+
+    /// @brief Sets the next event to the given event value.
+    ///
+    /// This updates the model's notion of the next event and is the
+    /// event that will be passed into the current state's handler on the next
+    /// iteration of the run loop.
+    ///
+    /// @param event the numeric event value to post as the next event.
+    ///
+    /// @throw StateModelError if the event is undefined
+    void postNextEvent(unsigned int event);
+
+    /// @brief Checks if on entry flag is true.
+    ///
+    /// This method acts as a one-shot test of whether or not  the model is
+    /// transitioning into a new state.  It returns true if the on-entry flag
+    /// is true upon entering this method and will set the flag false prior
+    /// to exit.  It may be used within state handlers to perform steps that
+    /// should only occur upon entry into the state.
+    ///
+    /// @return true if the on entry flag is true, false otherwise.
+    bool doOnEntry();
+
+    /// @brief Checks if on exit flag is true.
+    ///
+    /// This method acts as a one-shot test of whether or not the model is
+    /// transitioning out of the current state.  It returns true if the
+    /// on-exit flag is true upon entering this method and will set the flag
+    /// false prior to exiting.  It may be used within state handlers to perform
+    /// steps that should only occur upon exiting out of the current state.
+    ///
+    /// @return true if the on entry flag is true, false otherwise.
+    bool doOnExit();
+
+public:
+    /// @brief Fetches the model's current state.
+    ///
+    /// This returns the model's notion of the current state. It is the
+    /// state whose handler will be executed on the next iteration of the run
+    /// loop.
+    ///
+    /// @return An unsigned int representing the current state.
+    unsigned int getCurrState() const;
+
+    /// @brief Fetches the model's previous state.
+    ///
+    /// @return An unsigned int representing the previous state.
+    unsigned int getPrevState() const;
+
+    /// @brief Fetches the model's last event.
+    ///
+    /// @return An unsigned int representing the last event.
+    unsigned int getLastEvent() const;
+
+    /// @brief Fetches the model's next event.
+    ///
+    /// This returns the model's notion of the next event. It is the
+    /// event that will be passed into the current state's handler on the next
+    /// iteration of the run loop.
+    ///
+    /// @return An unsigned int representing the next event.
+    unsigned int getNextEvent() const;
+
+    /// @brief Returns whether or not the model is new.
+    ///
+    /// @return Boolean true if the model has not been started.
+    bool isModelNew() const;
+
+    /// @brief Returns whether or not the model is running.
+    ///
+    /// @return Boolean true if the model has been started but has not yet
+    /// ended.
+    bool isModelRunning() const;
+
+    /// @brief Returns whether or not the model is waiting.
+    ///
+    /// @return Boolean true if the model is running but is waiting for an
+    /// external event for resumption.
+    bool isModelWaiting() const;
+
+    /// @brief Returns whether or not the model has finished execution.
+    ///
+    /// @return Boolean true if the model has reached the END_ST.
+    bool isModelDone() const;
+
+    /// @brief Returns whether or not the model failed.
+    ///
+    /// @return Boolean true if the model has reached the END_ST and the last
+    /// event indicates a model violation, FAILED_EVT.
+    bool didModelFail() const;
+
+    /// @brief Fetches the label associated with an event value.
+    ///
+    /// @param event is the numeric event value for which the label is desired.
+    ///
+    /// @return Returns a string containing the event label or
+    /// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
+    std::string getEventLabel(const int event) const;
+
+    /// @brief Fetches the label associated with an state value.
+    ///
+    /// @param state is the numeric state value for which the label is desired.
+    ///
+    /// @return Returns a const char* containing the state label or
+    /// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
+    std::string getStateLabel(const int state) const;
+
+    /// @brief Convenience method which returns a string rendition of the
+    /// current state and next event.
+    ///
+    /// The string will be of the form:
+    ///
+    ///   current state: [ {state} {label} ] next event: [ {event} {label} ]
+    ///
+    /// @return Returns a std::string of the format described above.
+    std::string getContextStr() const;
+
+    /// @brief Convenience method which returns a string rendition of the
+    /// previous state and last event.
+    ///
+    /// The string will be of the form:
+    ///
+    ///   previous state: [ {state} {label} ] last event: [ {event} {label} ]
+    ///
+    /// @return Returns a std::string of the format described above.
+    std::string getPrevContextStr() const;
+
+private:
+    /// @brief The dictionary of valid events.
+    LabeledValueSet events_;
+
+    /// @brief The dictionary of valid states.
+    StateSet states_;
+
+    /// @brief Indicates if the event and state dictionaries have been initted.
+    bool dictionaries_initted_;
+
+    /// @brief The current state within the model's state model.
+    unsigned int curr_state_;
+
+    /// @brief The previous state within the model's state model.
+    unsigned int prev_state_;
+
+    /// @brief The event last processed by the model.
+    unsigned int last_event_;
+
+    /// @brief The event the model should process next.
+    unsigned int next_event_;
+
+    /// @brief Indicates if state entry logic should be executed.
+    bool on_entry_flag_;
+
+    /// @brief Indicates if state exit logic should be executed.
+    bool on_exit_flag_;
+};
+
+/// @brief Defines a pointer to a StateModel.
+typedef boost::shared_ptr<StateModel> StateModelPtr;
+
+} // namespace isc::d2
+} // namespace isc
+#endif
diff --git a/src/bin/d2/tests/Makefile.am b/src/bin/d2/tests/Makefile.am
index 6d0b894..0866ea0 100644
--- a/src/bin/d2/tests/Makefile.am
+++ b/src/bin/d2/tests/Makefile.am
@@ -64,6 +64,9 @@ d2_unittests_SOURCES += ../d2_update_message.cc ../d2_update_message.h
 d2_unittests_SOURCES += ../d2_update_mgr.cc ../d2_update_mgr.h
 d2_unittests_SOURCES += ../d2_zone.cc ../d2_zone.h
 d2_unittests_SOURCES += ../dns_client.cc ../dns_client.h
+d2_unittests_SOURCES += ../labeled_value.cc ../labeled_value.h
+d2_unittests_SOURCES += ../nc_trans.cc ../nc_trans.h
+d2_unittests_SOURCES += ../state_model.cc ../state_model.h
 d2_unittests_SOURCES += d_test_stubs.cc d_test_stubs.h
 d2_unittests_SOURCES += d2_unittests.cc
 d2_unittests_SOURCES += d2_process_unittests.cc
@@ -76,6 +79,9 @@ d2_unittests_SOURCES += d2_update_message_unittests.cc
 d2_unittests_SOURCES += d2_update_mgr_unittests.cc
 d2_unittests_SOURCES += d2_zone_unittests.cc
 d2_unittests_SOURCES += dns_client_unittests.cc
+d2_unittests_SOURCES += labeled_value_unittests.cc
+d2_unittests_SOURCES += nc_trans_unittests.cc
+d2_unittests_SOURCES += state_model_unittests.cc
 nodist_d2_unittests_SOURCES = ../d2_messages.h ../d2_messages.cc
 
 d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
diff --git a/src/bin/d2/tests/labeled_value_unittests.cc b/src/bin/d2/tests/labeled_value_unittests.cc
new file mode 100644
index 0000000..a4cfc01
--- /dev/null
+++ b/src/bin/d2/tests/labeled_value_unittests.cc
@@ -0,0 +1,107 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <d2/labeled_value.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+
+namespace {
+
+/// @brief Verifies basic construction and accessors for LabeledValue.
+TEST(LabeledValue, construction) {
+    /// Verify that an empty label is not allowed.
+    ASSERT_THROW(LabeledValue(1, ""), LabeledValueError);
+
+    /// Verify that a valid constructor works.
+    LabeledValuePtr lvp;
+    ASSERT_NO_THROW(lvp.reset(new LabeledValue(1, "NotBlank")));
+    ASSERT_TRUE(lvp);
+
+    // Verify that the value can be accessed.
+    EXPECT_EQ(1, lvp->getValue());
+
+    // Verify that the label can be accessed.
+    EXPECT_EQ("NotBlank", lvp->getLabel());
+}
+
+/// @brief Verifies the logical operators defined for LabeledValue.
+TEST(LabeledValue, operators) {
+    LabeledValuePtr lvp1;
+    LabeledValuePtr lvp1Also;
+    LabeledValuePtr lvp2;
+
+    // Create three instances, two of which have the same numeric value.
+    ASSERT_NO_THROW(lvp1.reset(new LabeledValue(1, "One")));
+    ASSERT_NO_THROW(lvp1Also.reset(new LabeledValue(1, "OneAlso")));
+    ASSERT_NO_THROW(lvp2.reset(new LabeledValue(2, "Two")));
+
+    // Verify each of the operators.
+    EXPECT_TRUE(*lvp1 == *lvp1Also);
+    EXPECT_TRUE(*lvp1 != *lvp2);
+    EXPECT_TRUE(*lvp1 < *lvp2);
+    EXPECT_FALSE(*lvp2 < *lvp1);
+}
+
+/// @brief Verifies the default constructor for LabeledValueSet.
+TEST(LabeledValueSet, construction) {
+    ASSERT_NO_THROW (LabeledValueSet());
+}
+
+/// @brief Verifies the basic operations of a LabeledValueSet.
+/// Essentially we verify that we can define a set of valid entries and
+/// look them up without issue.
+TEST(LabeledValueSet, basicOperation) {
+    const char* labels[] = {"Zero", "One", "Two", "Three" };
+    LabeledValueSet lvset;
+    LabeledValuePtr lvp;
+
+    // Verify the we cannot add an empty pointer to the set.
+    EXPECT_THROW(lvset.add(lvp), LabeledValueError);
+
+    // Verify that we can add an entry to the set via pointer.
+    ASSERT_NO_THROW(lvp.reset(new LabeledValue(0, labels[0])));
+    EXPECT_NO_THROW(lvset.add(lvp));
+
+    // Verify that we cannot add a duplicate entry.
+    EXPECT_THROW(lvset.add(lvp), LabeledValueError);
+
+    // Add the remaining entries using add(int,char*) variant.
+    for (int i = 1; i < 3; i++) {
+        EXPECT_NO_THROW(lvset.add(i, labels[i]));
+    }
+
+    // Verify that we can't add a duplicate entry this way either.
+    EXPECT_THROW ((lvset.add(0, labels[0])), LabeledValueError);
+
+    // Verify that we can look up all of the defined entries properly.
+    for (int i = 1; i < 3; i++) {
+        EXPECT_TRUE(lvset.isDefined(i));
+        EXPECT_NO_THROW(lvp = lvset.get(i));
+        EXPECT_EQ(lvp->getValue(), i);
+        EXPECT_EQ(lvp->getLabel(), labels[i]);
+        EXPECT_EQ(lvset.getLabel(i), labels[i]);
+    }
+
+    // Verify behavior for a value that is not defined.
+    EXPECT_FALSE(lvset.isDefined(4));
+    EXPECT_NO_THROW(lvp = lvset.get(4));
+    EXPECT_FALSE(lvp);
+    EXPECT_EQ(lvset.getLabel(4), LabeledValueSet::UNDEFINED_LABEL);
+}
+
+}
diff --git a/src/bin/d2/tests/nc_trans_unittests.cc b/src/bin/d2/tests/nc_trans_unittests.cc
new file mode 100644
index 0000000..31785a4
--- /dev/null
+++ b/src/bin/d2/tests/nc_trans_unittests.cc
@@ -0,0 +1,599 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <d2/nc_trans.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+
+namespace {
+
+/// @brief Test derivation of NameChangeTransaction for exercising state
+/// model mechanics.
+///
+/// This class facilitates testing by making non-public methods accessible so
+/// they can be invoked directly in test routines.  It implements a very
+/// rudimentary state model, sufficient to test the state model mechanics
+/// supplied by the base class.
+class NameChangeStub : public NameChangeTransaction {
+public:
+
+    // NameChangeStub states
+    static const int DOING_UPDATE_ST = NCT_DERIVED_STATE_MIN + 1;
+
+    // NameChangeStub events
+    static const int SEND_UPDATE_EVT = NCT_DERIVED_EVENT_MIN + 2;
+
+    /// @brief Constructor
+    ///
+    /// Parameters match those needed by NameChangeTransaction.
+    NameChangeStub(isc::asiolink::IOService& io_service,
+                   dhcp_ddns::NameChangeRequestPtr& ncr,
+                   DdnsDomainPtr forward_domain,
+                   DdnsDomainPtr reverse_domain)
+        : NameChangeTransaction(io_service, ncr, forward_domain,
+                                reverse_domain) {
+    }
+
+    /// @brief Destructor
+    virtual ~NameChangeStub() {
+    }
+
+    /// @brief Empty handler used to statisfy map verification.
+    void dummyHandler() {
+        isc_throw(NameChangeTransactionError,
+                  "dummyHandler - invalid event: " << getContextStr());
+    }
+
+    /// @brief State handler for the READY_ST.
+    ///
+    /// Serves as the starting state handler, it consumes the
+    /// START_EVT "transitioning" to the state, DOING_UPDATE_ST and
+    /// sets the next event to SEND_UPDATE_EVT.
+    void readyHandler() {
+        switch(getNextEvent()) {
+        case START_EVT:
+            transition(DOING_UPDATE_ST, SEND_UPDATE_EVT);
+            break;
+        default:
+            // its bogus
+            isc_throw(NameChangeTransactionError,
+                      "readyHandler - invalid event: " << getContextStr());
+        }
+    }
+
+    /// @brief State handler for the DOING_UPDATE_ST.
+    ///
+    /// Simulates a state that starts some form of asynchronous work.
+    /// When next event is SEND_UPDATE_EVT it sets the status to pending
+    /// and signals the state model must "wait" for an event by setting
+    /// next event to NOP_EVT.
+    ///
+    /// When next event is IO_COMPLETED_EVT, it transitions to the state,
+    /// PROCESS_TRANS_OK_ST, and sets the next event to UPDATE_OK_EVT.
+    void doingUpdateHandler() {
+        switch(getNextEvent()) {
+        case SEND_UPDATE_EVT:
+            setNcrStatus(dhcp_ddns::ST_PENDING);
+            postNextEvent(NOP_EVT);
+            break;
+        case IO_COMPLETED_EVT:
+            if (getDnsUpdateStatus() == DNSClient::SUCCESS) {
+                setForwardChangeCompleted(true);
+                transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT);
+            } else {
+                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+            }
+            break;
+        default:
+            // its bogus
+            isc_throw(NameChangeTransactionError,
+                      "doingUpdateHandler - invalid event: "
+                      << getContextStr());
+        }
+    }
+
+    /// @brief State handler for the PROCESS_TRANS_OK_ST.
+    ///
+    /// This is the last state in the model.  Note that it sets the
+    /// status to completed and next event to NOP_EVT.
+    void processTransDoneHandler() {
+        switch(getNextEvent()) {
+        case UPDATE_OK_EVT:
+            setNcrStatus(dhcp_ddns::ST_COMPLETED);
+            endModel();
+            break;
+        case UPDATE_FAILED_EVT:
+            setNcrStatus(dhcp_ddns::ST_FAILED);
+            endModel();
+            break;
+        default:
+            // its bogus
+            isc_throw(NameChangeTransactionError,
+                      "processTransDoneHandler - invalid event: "
+                      << getContextStr());
+        }
+    }
+
+    /// @brief Construct the event dictionary.
+    virtual void defineEvents() {
+        // Invoke the base call implementation first.
+        NameChangeTransaction::defineEvents();
+
+        // Define our events.
+        defineEvent(SEND_UPDATE_EVT, "SEND_UPDATE_EVT");
+    }
+
+    /// @brief Verify the event dictionary.
+    virtual void verifyEvents() {
+        // Invoke the base call implementation first.
+        NameChangeTransaction::verifyEvents();
+
+        // Define our events.
+        getEvent(SEND_UPDATE_EVT);
+    }
+
+    /// @brief Construct the state dictionary.
+    virtual void defineStates() {
+        // Invoke the base call implementation first.
+        NameChangeTransaction::defineStates();
+
+        // Define our states.
+        defineState(READY_ST, "READY_ST",
+                             boost::bind(&NameChangeStub::readyHandler, this));
+
+        defineState(SELECTING_FWD_SERVER_ST, "SELECTING_FWD_SERVER_ST",
+                             boost::bind(&NameChangeStub::dummyHandler, this));
+
+        defineState(SELECTING_REV_SERVER_ST, "SELECTING_REV_SERVER_ST",
+                             boost::bind(&NameChangeStub::dummyHandler, this));
+
+        defineState(DOING_UPDATE_ST, "DOING_UPDATE_ST",
+                             boost::bind(&NameChangeStub::doingUpdateHandler,
+                                         this));
+
+        defineState(PROCESS_TRANS_OK_ST, "PROCESS_TRANS_OK_ST",
+                             boost::bind(&NameChangeStub::
+                                         processTransDoneHandler, this));
+
+        defineState(PROCESS_TRANS_FAILED_ST, "PROCESS_TRANS_FAILED_ST",
+                             boost::bind(&NameChangeStub::
+                                         processTransDoneHandler, this));
+    }
+
+    /// @brief Verify the event dictionary.
+    virtual void verifyStates() {
+        // Invoke the base call implementation first.
+        NameChangeTransaction::verifyStates();
+
+        // Define our states.
+        getState(DOING_UPDATE_ST);
+    }
+
+    // Expose the protected methods to be tested.
+    using StateModel::runModel;
+    using NameChangeTransaction::initServerSelection;
+    using NameChangeTransaction::selectNextServer;
+    using NameChangeTransaction::getCurrentServer;
+    using NameChangeTransaction::getDNSClient;
+    using NameChangeTransaction::setNcrStatus;
+    using NameChangeTransaction::setDnsUpdateStatus;
+    using NameChangeTransaction::getDnsUpdateResponse;
+    using NameChangeTransaction::getForwardChangeCompleted;
+    using NameChangeTransaction::getReverseChangeCompleted;
+    using NameChangeTransaction::setForwardChangeCompleted;
+    using NameChangeTransaction::setReverseChangeCompleted;
+};
+
+/// @brief Defines a pointer to a NameChangeStubPtr instance.
+typedef boost::shared_ptr<NameChangeStub> NameChangeStubPtr;
+
+/// @brief Test fixture for testing NameChangeTransaction
+///
+/// Note this class uses NameChangeStub class to exercise non-public
+/// aspects of NameChangeTransaction.
+class NameChangeTransactionTest : public ::testing::Test {
+public:
+    isc::asiolink::IOService io_service_;
+    DdnsDomainPtr forward_domain_;
+    DdnsDomainPtr reverse_domain_;
+
+    virtual ~NameChangeTransactionTest() {
+    }
+
+    /// @brief Instantiates a NameChangeStub built around a canned
+    /// NameChangeRequest.
+    NameChangeStubPtr makeCannedTransaction() {
+        const char* msg_str =
+            "{"
+            " \"change_type\" : 0 , "
+            " \"forward_change\" : true , "
+            " \"reverse_change\" : true , "
+            " \"fqdn\" : \"example.com.\" , "
+            " \"ip_address\" : \"192.168.2.1\" , "
+            " \"dhcid\" : \"0102030405060708\" , "
+            " \"lease_expires_on\" : \"20130121132405\" , "
+            " \"lease_length\" : 1300 "
+            "}";
+
+        dhcp_ddns::NameChangeRequestPtr ncr;
+
+        DnsServerInfoStoragePtr servers(new DnsServerInfoStorage());
+        DnsServerInfoPtr server;
+
+        ncr = dhcp_ddns::NameChangeRequest::fromJSON(msg_str);
+
+        // make forward server list
+        server.reset(new DnsServerInfo("forward.example.com",
+                                       isc::asiolink::IOAddress("1.1.1.1")));
+        servers->push_back(server);
+        forward_domain_.reset(new DdnsDomain("*", "", servers));
+
+        // make reverse server list
+        servers->clear();
+        server.reset(new DnsServerInfo("reverse.example.com",
+                                       isc::asiolink::IOAddress("2.2.2.2")));
+        servers->push_back(server);
+        reverse_domain_.reset(new DdnsDomain("*", "", servers));
+        return (NameChangeStubPtr(new NameChangeStub(io_service_, ncr,
+                                  forward_domain_, reverse_domain_)));
+
+    }
+
+};
+
+/// @brief Tests NameChangeTransaction construction.
+/// This test verifies that:
+/// 1. Construction with null NameChangeRequest
+/// 2. Construction with null forward domain is not allowed when the request
+/// requires forward change.
+/// 3. Construction with null reverse domain is not allowed when the request
+/// requires reverse change.
+/// 4. Valid construction functions properly
+TEST(NameChangeTransaction, construction) {
+    isc::asiolink::IOService io_service;
+
+    const char* msg_str =
+        "{"
+        " \"change_type\" : 0 , "
+        " \"forward_change\" : true , "
+        " \"reverse_change\" : true , "
+        " \"fqdn\" : \"example.com.\" , "
+        " \"ip_address\" : \"192.168.2.1\" , "
+        " \"dhcid\" : \"0102030405060708\" , "
+        " \"lease_expires_on\" : \"20130121132405\" , "
+        " \"lease_length\" : 1300 "
+        "}";
+
+    dhcp_ddns::NameChangeRequestPtr ncr;
+
+    dhcp_ddns::NameChangeRequestPtr empty_ncr;
+    DnsServerInfoStoragePtr servers;
+    DdnsDomainPtr forward_domain;
+    DdnsDomainPtr reverse_domain;
+    DdnsDomainPtr empty_domain;
+
+    ASSERT_NO_THROW(ncr = dhcp_ddns::NameChangeRequest::fromJSON(msg_str));
+    ASSERT_NO_THROW(forward_domain.reset(new DdnsDomain("*", "", servers)));
+    ASSERT_NO_THROW(reverse_domain.reset(new DdnsDomain("*", "", servers)));
+
+    // Verify that construction with an empty NameChangeRequest throws.
+    EXPECT_THROW(NameChangeTransaction(io_service, empty_ncr,
+                                       forward_domain, reverse_domain),
+                                        NameChangeTransactionError);
+
+    // Verify that construction with an empty forward domain when the
+    // NameChangeRequest calls for a forward change throws.
+    EXPECT_THROW(NameChangeTransaction(io_service, ncr,
+                                       empty_domain, reverse_domain),
+                                       NameChangeTransactionError);
+
+    // Verify that construction with an empty reverse domain when the
+    // NameChangeRequest calls for a reverse change throws.
+    EXPECT_THROW(NameChangeTransaction(io_service, ncr,
+                                       forward_domain, empty_domain),
+                                       NameChangeTransactionError);
+
+    // Verify that a valid construction attempt works.
+    EXPECT_NO_THROW(NameChangeTransaction(io_service, ncr,
+                                          forward_domain, reverse_domain));
+
+    // Verify that an empty forward domain is allowed when the requests does
+    // not include a forward change.
+    ncr->setForwardChange(false);
+    ncr->setReverseChange(true);
+    EXPECT_NO_THROW(NameChangeTransaction(io_service, ncr,
+                                          empty_domain, reverse_domain));
+
+    // Verify that an empty reverse domain is allowed when the requests does
+    // not include a reverse change.
+    ncr->setForwardChange(true);
+    ncr->setReverseChange(false);
+    EXPECT_NO_THROW(NameChangeTransaction(io_service, ncr,
+                                          forward_domain, empty_domain));
+}
+
+/// @brief General testing of member accessors.
+/// Most if not all of these are also tested as a byproduct off larger tests.
+TEST_F(NameChangeTransactionTest, accessors) {
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+    // Verify that fetching the NameChangeRequest works.
+    dhcp_ddns::NameChangeRequestPtr ncr = name_change->getNcr();
+    ASSERT_TRUE(ncr);
+
+    // Verify that getTransactionKey works.
+    EXPECT_EQ(ncr->getDhcid(), name_change->getTransactionKey());
+
+    // Verify that NcrStatus can be set and retrieved.
+    EXPECT_NO_THROW(name_change->setNcrStatus(dhcp_ddns::ST_FAILED));
+    EXPECT_EQ(dhcp_ddns::ST_FAILED, ncr->getStatus());
+
+    // Verify that the forward domain can be retrieved.
+    ASSERT_TRUE(name_change->getForwardDomain());
+    EXPECT_EQ(forward_domain_, name_change->getForwardDomain());
+
+    // Verify that the reverse domain can be retrieved.
+    ASSERT_TRUE(name_change->getReverseDomain());
+    EXPECT_EQ(reverse_domain_, name_change->getReverseDomain());
+
+    // Neither of these have direct setters, but are tested under server
+    // selection.
+    EXPECT_FALSE(name_change->getDNSClient());
+    EXPECT_FALSE(name_change->getCurrentServer());
+
+    // Verify that DNS update status can be set and retrieved.
+    EXPECT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::TIMEOUT));
+    EXPECT_EQ(DNSClient::TIMEOUT, name_change->getDnsUpdateStatus());
+
+    // Verify that the DNS update response can be retrieved.
+    EXPECT_FALSE(name_change->getDnsUpdateResponse());
+
+    // Verify that the forward change complete flag can be set and fetched.
+    EXPECT_NO_THROW(name_change->setForwardChangeCompleted(true));
+    EXPECT_TRUE(name_change->getForwardChangeCompleted());
+
+    // Verify that the reverse change complete flag can be set and fetched.
+    EXPECT_NO_THROW(name_change->setReverseChangeCompleted(true));
+    EXPECT_TRUE(name_change->getReverseChangeCompleted());
+}
+
+/// @brief Tests event and state dictionary construction and verification.
+TEST_F(NameChangeTransactionTest, dictionaryCheck) {
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+    // Verify that the event and state dictionary validation fails prior
+    // dictionary construction.
+    ASSERT_THROW(name_change->verifyEvents(), StateModelError);
+    ASSERT_THROW(name_change->verifyStates(), StateModelError);
+
+    // Construct both dictionaries.
+    ASSERT_NO_THROW(name_change->defineEvents());
+    ASSERT_NO_THROW(name_change->defineStates());
+
+    // Verify both event and state dictionaries now pass validation.
+    ASSERT_NO_THROW(name_change->verifyEvents());
+    ASSERT_NO_THROW(name_change->verifyStates());
+}
+
+/// @brief Tests server selection methods.
+/// Each transaction has a list of one or more servers for each DNS direction
+/// it is required to update.  The transaction must be able to start at the
+/// beginning of a server list and cycle through them one at time, as needed,
+/// when a DNS exchange fails due to an IO error.  This test verifies the
+/// ability to iteratively select a server from the list as the current server.
+TEST_F(NameChangeTransactionTest, serverSelectionTest) {
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+    // Verify that the forward domain and its list of servers can be retrieved.
+    DdnsDomainPtr& domain = name_change->getForwardDomain();
+    ASSERT_TRUE(domain);
+    DnsServerInfoStoragePtr servers = domain->getServers();
+    ASSERT_TRUE(servers);
+
+    // Get the number of entries in the server list.
+    int num_servers = servers->size();
+    ASSERT_TRUE(num_servers > 0);
+
+    // Verify that we can initialize server selection.  This "resets" the
+    // selection process to start over using the list of servers in the
+    // given domain.
+    ASSERT_NO_THROW(name_change->initServerSelection(domain));
+
+    // The server selection process determines the current server,
+    // instantiates a new DNSClient, and a DNS response message buffer.
+    //  We need to save the values before each selection, so we can verify
+    // they are correct after each selection.
+    DnsServerInfoPtr prev_server = name_change->getCurrentServer();
+    DNSClientPtr prev_client = name_change->getDNSClient();
+    D2UpdateMessagePtr prev_response = name_change->getDnsUpdateResponse();
+
+    // Iteratively select through the list of servers.
+    int passes = 0;
+    while (name_change->selectNextServer()) {
+        // Get the new values after the selection has been made.
+        DnsServerInfoPtr server = name_change->getCurrentServer();
+        DNSClientPtr client = name_change->getDNSClient();
+        D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
+
+        // Verify that the new values are not empty.
+        EXPECT_TRUE(server);
+        EXPECT_TRUE(client);
+        EXPECT_TRUE(response);
+
+        // Verify that the new values are indeed new.
+        EXPECT_NE(server, prev_server);
+        EXPECT_NE(client, prev_client);
+        EXPECT_NE(response, prev_response);
+
+        // Remember the selected values for the next pass.
+        prev_server = server;
+        prev_client = client;
+        prev_response = response;
+
+        ++passes;
+    }
+
+    // Verify that the number of passes made equal the number of servers.
+    EXPECT_EQ (passes, num_servers);
+
+    // Repeat the same test using the reverse domain.
+    // Verify that the reverse domain and its list of servers can be retrieved.
+    domain = name_change->getReverseDomain();
+    ASSERT_TRUE(domain);
+    servers = domain->getServers();
+    ASSERT_TRUE(servers);
+
+    // Get the number of entries in the server list.
+    num_servers = servers->size();
+    ASSERT_TRUE(num_servers > 0);
+
+    // Verify that we can initialize server selection.  This "resets" the
+    // selection process to start over using the list of servers in the
+    // given domain.
+    ASSERT_NO_THROW(name_change->initServerSelection(domain));
+
+    // The server selection process determines the current server,
+    // instantiates a new DNSClient, and a DNS response message buffer.
+    // We need to save the values before each selection, so we can verify
+    // they are correct after each selection.
+    prev_server = name_change->getCurrentServer();
+    prev_client = name_change->getDNSClient();
+    prev_response = name_change->getDnsUpdateResponse();
+
+    // Iteratively select through the list of servers.
+    passes = 0;
+    while (name_change->selectNextServer()) {
+        // Get the new values after the selection has been made.
+        DnsServerInfoPtr server = name_change->getCurrentServer();
+        DNSClientPtr client = name_change->getDNSClient();
+        D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
+
+        // Verify that the new values are not empty.
+        EXPECT_TRUE(server);
+        EXPECT_TRUE(client);
+        EXPECT_TRUE(response);
+
+        // Verify that the new values are indeed new.
+        EXPECT_NE(server, prev_server);
+        EXPECT_NE(client, prev_client);
+        EXPECT_NE(response, prev_response);
+
+        // Remember the selected values for the next pass.
+        prev_server = server;
+        prev_client = client;
+        prev_response = response;
+
+        ++passes;
+    }
+
+    // Verify that the number of passes made equal the number of servers.
+    EXPECT_EQ (passes, num_servers);
+}
+
+/// @brief Tests that the transaction will be "failed" upon model errors.
+TEST_F(NameChangeTransactionTest, modelFailure) {
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+    // Now call runModel() with an undefined event which should not throw,
+    // but should result in a failed model and failed transaction.
+    EXPECT_NO_THROW(name_change->runModel(9999));
+
+    // Verify that the model reports are done but failed.
+    EXPECT_TRUE(name_change->isModelDone());
+    EXPECT_TRUE(name_change->didModelFail());
+
+    // Verify that the transaction has failed.
+    EXPECT_EQ(dhcp_ddns::ST_FAILED, name_change->getNcrStatus());
+}
+
+/// @brief Tests the ability to use startTransaction to initate the state
+/// model execution, and DNSClient callback, operator(), to resume the
+/// the model with a update successful outcome.
+TEST_F(NameChangeTransactionTest, successfulUpdateTest) {
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+    EXPECT_TRUE(name_change->isModelNew());
+    EXPECT_FALSE(name_change->getForwardChangeCompleted());
+
+    // Launch the transaction by calling startTransaction.  The state model
+    // should run up until the "IO" operation is initiated in DOING_UPDATE_ST.
+    ASSERT_NO_THROW(name_change->startTransaction());
+
+    // Verify that the model is running but waiting, and that forward change
+    // completion is still false.
+    EXPECT_TRUE(name_change->isModelRunning());
+    EXPECT_TRUE(name_change->isModelWaiting());
+    EXPECT_FALSE(name_change->getForwardChangeCompleted());
+
+    // Simulate completion of DNSClient exchange by invoking the callback, as
+    // DNSClient would.  This should cause the state model to progress through
+    // completion.
+    EXPECT_NO_THROW((*name_change)(DNSClient::SUCCESS));
+
+    // The model should have worked through to completion.
+    // Verify that the model is done and not failed.
+    EXPECT_TRUE(name_change->isModelDone());
+    EXPECT_FALSE(name_change->didModelFail());
+
+    // Verify that NCR status is completed, and that the forward change
+    // was completed.
+    EXPECT_EQ(dhcp_ddns::ST_COMPLETED, name_change->getNcrStatus());
+    EXPECT_TRUE(name_change->getForwardChangeCompleted());
+}
+
+/// @brief Tests the ability to use startTransaction to initate the state
+/// model execution, and DNSClient callback, operator(), to resume the
+/// the model with a update failure outcome.
+TEST_F(NameChangeTransactionTest, failedUpdateTest) {
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+    // Launch the transaction by calling startTransaction.  The state model
+    // should run up until the "IO" operation is initiated in DOING_UPDATE_ST.
+    ASSERT_NO_THROW(name_change->startTransaction());
+
+    // Vefity that the model is running but waiting, and that the forward
+    // change has not been completed.
+    EXPECT_TRUE(name_change->isModelRunning());
+    EXPECT_TRUE(name_change->isModelWaiting());
+    EXPECT_FALSE(name_change->getForwardChangeCompleted());
+
+    // Simulate completion of DNSClient exchange by invoking the callback, as
+    // DNSClient would.  This should cause the state model to progress through
+    // to completion.
+    EXPECT_NO_THROW((*name_change)(DNSClient::TIMEOUT));
+
+    // The model should have worked through to completion.
+    // Verify that the model is done and not failed.
+    EXPECT_TRUE(name_change->isModelDone());
+    EXPECT_FALSE(name_change->didModelFail());
+
+    // Verify that the NCR status is failed and that the forward change
+    // was not completed.
+    EXPECT_EQ(dhcp_ddns::ST_FAILED, name_change->getNcrStatus());
+    EXPECT_FALSE(name_change->getForwardChangeCompleted());
+}
+
+}
diff --git a/src/bin/d2/tests/state_model_unittests.cc b/src/bin/d2/tests/state_model_unittests.cc
new file mode 100644
index 0000000..63a0203
--- /dev/null
+++ b/src/bin/d2/tests/state_model_unittests.cc
@@ -0,0 +1,839 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <d2/state_model.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+
+namespace {
+
+/// @brief Test derivation of StateModel for exercising state model mechanics.
+///
+/// This class facilitates testing by making non-public methods accessible so
+/// they can be invoked directly in test routines.  It implements a very
+/// rudimentary state model, sufficient to test the state model mechanics
+/// supplied by the base class.
+class StateModelTest : public StateModel, public testing::Test {
+public:
+
+    ///@brief StateModelTest states
+    ///@brief Fake state used for handler mapping tests.
+    static const int DUMMY_ST = SM_DERIVED_STATE_MIN + 1;
+
+    ///@brief Starting state for the test state model.
+    static const int READY_ST = SM_DERIVED_STATE_MIN + 2;
+
+    ///@brief State which simulates doing asynchronous work.
+    static const int DO_WORK_ST = SM_DERIVED_STATE_MIN + 3;
+
+    ///@brief State which finishes off processing.
+    static const int DONE_ST = SM_DERIVED_STATE_MIN + 4;
+
+    // StateModelTest events
+    ///@brief Event used to trigger initiation of asynchronous work.
+    static const int WORK_START_EVT = SM_DERIVED_EVENT_MIN + 1;
+
+    ///@brief Event issued when the asynchronous work "completes".
+    static const int WORK_DONE_EVT = SM_DERIVED_EVENT_MIN + 2;
+
+    ///@brief Event issued when all the work is done.
+    static const int ALL_DONE_EVT = SM_DERIVED_EVENT_MIN + 3;
+
+    ///@brief Event used to trigger an attempt to transition to bad state
+    static const int FORCE_UNDEFINED_ST_EVT = SM_DERIVED_EVENT_MIN + 4;
+
+    ///@brief Event used to trigger an attempt to transition to bad state
+    static const int SIMULATE_ERROR_EVT = SM_DERIVED_EVENT_MIN + 5;
+
+    /// @brief Constructor
+    ///
+    /// Parameters match those needed by StateModel.
+    StateModelTest() : dummy_called_(false), work_completed_(false),
+                       failure_explanation_("") {
+    }
+    /// @brief Destructor
+    virtual ~StateModelTest() {
+    }
+
+    /// @brief Fetches the value of the dummy called flag.
+    bool getDummyCalled() {
+        return (dummy_called_);
+    }
+
+    /// @brief StateHandler for fake state, DummyState.
+    ///
+    /// It simply sets the dummy called flag to indicate that this method
+    /// was invoked.
+    void dummyHandler() {
+        dummy_called_ = true;
+    }
+
+    /// @brief Returns the failure explanation string.
+    ///
+    /// This value is set only via onModelFailure and it stores whatever
+    /// explanation that method was passed.
+    const std::string& getFailureExplanation() {
+        return (failure_explanation_);
+    }
+
+    /// @brief Returns indication of whether or not the model succeeded.
+    ///
+    /// If true, this indicates that the test model executed correctly through
+    /// to completion.  The flag is only by the DONE_ST handler.
+    bool getWorkCompleted() {
+        return (work_completed_);
+    }
+
+    /// @brief State handler for the READY_ST.
+    ///
+    /// Serves as the starting state handler, it consumes the
+    /// START_EVT "transitioning" to the state, DO_WORK_ST and
+    /// sets the next event to WORK_START_EVT.
+    void readyHandler() {
+        switch(getNextEvent()) {
+        case START_EVT:
+            transition(DO_WORK_ST, WORK_START_EVT);
+            break;
+        default:
+            // its bogus
+            isc_throw(StateModelError, "readyHandler:invalid event: "
+                      << getContextStr());
+        }
+    }
+
+    /// @brief State handler for the DO_WORK_ST.
+    ///
+    /// Simulates a state that starts some form of asynchronous work.
+    /// When next event is WORK_START_EVT it sets the status to pending
+    /// and signals the state model must "wait" for an event by setting
+    /// next event to NOP_EVT.
+    ///
+    /// When next event is IO_COMPLETED_EVT, it transitions to the state,
+    /// DONE_ST, and sets the next event to WORK_DONE_EVT.
+    void doWorkHandler() {
+        switch(getNextEvent()) {
+        case WORK_START_EVT:
+            postNextEvent(NOP_EVT);
+            break;
+        case WORK_DONE_EVT:
+            work_completed_ = true;
+            transition(DONE_ST, ALL_DONE_EVT);
+            break;
+        case FORCE_UNDEFINED_ST_EVT:
+            transition(9999, ALL_DONE_EVT);
+            break;
+        case SIMULATE_ERROR_EVT:
+            throw std::logic_error("Simulated Unexpected Error");
+            break;
+        default:
+            // its bogus
+            isc_throw(StateModelError, "doWorkHandler:invalid event: "
+                      <<  getContextStr());
+        }
+    }
+
+    /// @brief State handler for the DONE_ST.
+    ///
+    /// This is the last state in the model.  Note that it sets the
+    /// status to completed and next event to NOP_EVT.
+    void doneWorkHandler() {
+        switch(getNextEvent()) {
+        case ALL_DONE_EVT:
+            endModel();
+            break;
+        default:
+            // its bogus
+            isc_throw(StateModelError, "doneWorkHandler:invalid event: "
+                      << getContextStr());
+        }
+    }
+
+    /// @brief Construct the event dictionary.
+    virtual void defineEvents() {
+        // Invoke the base call implementation first.
+        StateModel::defineEvents();
+
+        // Define our events.
+        defineEvent(WORK_START_EVT, "WORK_START_EVT");
+        defineEvent(WORK_DONE_EVT , "WORK_DONE_EVT");
+        defineEvent(ALL_DONE_EVT, "ALL_DONE_EVT");
+        defineEvent(FORCE_UNDEFINED_ST_EVT, "FORCE_UNDEFINED_ST_EVT");
+        defineEvent(SIMULATE_ERROR_EVT, "SIMULATE_ERROR_EVT");
+    }
+
+    /// @brief Verify the event dictionary.
+    virtual void verifyEvents() {
+        // Invoke the base call implementation first.
+        StateModel::verifyEvents();
+
+        // Verify our events.
+        getEvent(WORK_START_EVT);
+        getEvent(WORK_DONE_EVT);
+        getEvent(ALL_DONE_EVT);
+        getEvent(FORCE_UNDEFINED_ST_EVT);
+        getEvent(SIMULATE_ERROR_EVT);
+    }
+
+    /// @brief Construct the state dictionary.
+    virtual void defineStates() {
+        // Invoke the base call implementation first.
+        StateModel::defineStates();
+
+        // Define our states.
+        defineState(DUMMY_ST, "DUMMY_ST",
+                    boost::bind(&StateModelTest::dummyHandler, this));
+
+        defineState(READY_ST, "READY_ST",
+            boost::bind(&StateModelTest::readyHandler, this));
+
+        defineState(DO_WORK_ST, "DO_WORK_ST",
+            boost::bind(&StateModelTest::doWorkHandler, this));
+
+        defineState(DONE_ST, "DONE_ST",
+            boost::bind(&StateModelTest::doneWorkHandler, this));
+    }
+
+    /// @brief Verify the state dictionary.
+    virtual void verifyStates() {
+        // Invoke the base call implementation first.
+        StateModel::verifyStates();
+
+        // Verify our states.
+        getState(DUMMY_ST);
+        getState(READY_ST);
+        getState(DO_WORK_ST);
+        getState(DONE_ST);
+    }
+
+    /// @brief  Manually construct the event and state dictionaries.
+    /// This allows testing without running startModel.
+    void initDictionaires() {
+        ASSERT_NO_THROW(defineEvents());
+        ASSERT_NO_THROW(verifyEvents());
+        ASSERT_NO_THROW(defineStates());
+        ASSERT_NO_THROW(verifyStates());
+    }
+
+    /// @brief Tests the event dictionary entry for the given event value.
+    bool checkEvent(const int value, const std::string& label) {
+        EventPtr event;
+        try  {
+            event = getEvent(value);
+            EXPECT_TRUE(event);
+            EXPECT_EQ(value, event->getValue());
+            EXPECT_EQ(label, event->getLabel());
+        } catch (const std::exception& ex) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /// @brief Tests the state dictionary entry for the given state value.
+    bool checkState(const int value, const std::string& label) {
+        EventPtr state;
+        try  {
+            state = getState(value);
+            EXPECT_TRUE(state);
+            EXPECT_EQ(value, state->getValue());
+            EXPECT_EQ(label, state->getLabel());
+        } catch (const std::exception& ex) {
+            return false;
+        }
+
+        return true;
+    }
+
+
+    /// @brief  Handler called when the model suffers an execution error.
+    virtual void onModelFailure(const std::string& explanation) {
+        failure_explanation_ = explanation;
+    }
+
+    /// @brief Indicator of whether or not the DUMMY_ST handler has been called.
+    bool dummy_called_;
+
+    /// @brief Indicator of whether or not DONE_ST handler has been called.
+    bool work_completed_;
+
+    /// @brief Stores the failure explanation
+    std::string failure_explanation_;
+};
+
+// Declare them so gtest can see them.
+const int StateModelTest::DUMMY_ST;
+const int StateModelTest::READY_ST;
+const int StateModelTest::DO_WORK_ST;
+const int StateModelTest::DONE_ST;
+const int StateModelTest::WORK_START_EVT;
+const int StateModelTest::WORK_DONE_EVT;
+const int StateModelTest::ALL_DONE_EVT;
+
+/// @brief Checks the fundamentals of defining and retrieving events.
+TEST_F(StateModelTest, eventDefinition) {
+    // After construction, the event dictionary should be empty. Verify that
+    // getEvent will throw when event is not defined.
+    EXPECT_THROW(getEvent(NOP_EVT), StateModelError);
+
+    // Verify that we can add a handler to the map.
+    ASSERT_NO_THROW(defineEvent(NOP_EVT, "NOP_EVT"));
+
+    // Verify that we can find the event by value and its content is correct.
+    EXPECT_TRUE(checkEvent(NOP_EVT, "NOP_EVT"));
+
+    // Verify that we cannot add a duplicate.
+    ASSERT_THROW(defineEvent(NOP_EVT, "NOP_EVT"), StateModelError);
+
+    // Verify that we can still find the event.
+    EXPECT_TRUE(checkEvent(NOP_EVT, "NOP_EVT"));
+}
+
+/// @brief Tests event dictionary construction and verification.
+TEST_F(StateModelTest, eventDictionary) {
+    // After construction, the event dictionary should be empty.
+    // Make sure that verifyEvents() throws.
+    EXPECT_THROW(verifyEvents(), StateModelError);
+
+    // Construct the dictionary and verify it.
+    EXPECT_NO_THROW(defineEvents());
+    EXPECT_NO_THROW(verifyEvents());
+
+    // Verify base class events are defined.
+    EXPECT_TRUE(checkEvent(NOP_EVT, "NOP_EVT"));
+    EXPECT_TRUE(checkEvent(START_EVT, "START_EVT"));
+    EXPECT_TRUE(checkEvent(END_EVT, "END_EVT"));
+    EXPECT_TRUE(checkEvent(FAIL_EVT, "FAIL_EVT"));
+
+    // Verify stub class events are defined.
+    EXPECT_TRUE(checkEvent(WORK_START_EVT, "WORK_START_EVT"));
+    EXPECT_TRUE(checkEvent(WORK_DONE_EVT, "WORK_DONE_EVT"));
+    EXPECT_TRUE(checkEvent(ALL_DONE_EVT, "ALL_DONE_EVT"));
+    EXPECT_TRUE(checkEvent(FORCE_UNDEFINED_ST_EVT, "FORCE_UNDEFINED_ST_EVT"));
+    EXPECT_TRUE(checkEvent(SIMULATE_ERROR_EVT, "SIMULATE_ERROR_EVT"));
+
+    // Verify that undefined events are handled correctly.
+    EXPECT_THROW(getEvent(9999), StateModelError);
+    EXPECT_EQ(LabeledValueSet::UNDEFINED_LABEL, getEventLabel(9999));
+}
+
+/// @brief General testing of event context accessors.
+/// Most if not all of these are also tested as a byproduct off larger tests.
+TEST_F(StateModelTest, eventContextAccessors) {
+    // Construct the event definitions, normally done by startModel.
+    ASSERT_NO_THROW(defineEvents());
+    ASSERT_NO_THROW(verifyEvents());
+
+    // Verify the post-construction values.
+    EXPECT_EQ(NOP_EVT, getNextEvent());
+    EXPECT_EQ(NOP_EVT, getLastEvent());
+
+    // Call setEvent which will update both next event and last event.
+    EXPECT_NO_THROW(postNextEvent(START_EVT));
+
+    // Verify the values are what we expect.
+    EXPECT_EQ(START_EVT, getNextEvent());
+    EXPECT_EQ(NOP_EVT, getLastEvent());
+
+    // Call setEvent again.
+    EXPECT_NO_THROW(postNextEvent(WORK_START_EVT));
+
+    // Verify the values are what we expect.
+    EXPECT_EQ(WORK_START_EVT, getNextEvent());
+    EXPECT_EQ(START_EVT, getLastEvent());
+
+    // Verify that posting an undefined event throws.
+    EXPECT_THROW(postNextEvent(9999), StateModelError);
+}
+
+/// @brief Tests the fundamental methods used for state handler mapping.
+/// Verifies the ability to search for and add entries in the state handler map.
+TEST_F(StateModelTest, stateDefinition) {
+    // After construction, the state dictionary should be empty. Verify that
+    // getState will throw when, state is not defined.
+    EXPECT_THROW(getState(READY_ST), StateModelError);
+
+    // Verify that we can add a state to the dictionary.
+    ASSERT_NO_THROW(defineState(READY_ST, "READY_ST",
+                                boost::bind(&StateModelTest::dummyHandler,
+                                            this)));
+
+    // Verify that we can find the state by its value.
+    StatePtr state;
+    EXPECT_NO_THROW(state = getState(READY_ST));
+    EXPECT_TRUE(state);
+
+    // Verify the state's value and label.
+    EXPECT_EQ(READY_ST, state->getValue());
+    EXPECT_EQ("READY_ST", state->getLabel());
+
+    // Now verify that retrieved state's handler executes the correct method.
+    // Make sure the dummy called flag is false prior to invocation.
+    EXPECT_FALSE(getDummyCalled());
+
+    // Invoke the state's handler.
+    EXPECT_NO_THROW(state->run());
+
+    // Verify the dummy called flag is now true.
+    EXPECT_TRUE(getDummyCalled());
+
+    // Verify that we cannot add a duplicate.
+    EXPECT_THROW(defineState(READY_ST, "READY_ST",
+                             boost::bind(&StateModelTest::readyHandler, this)),
+                 StateModelError);
+
+    // Verify that we can still find the state.
+    EXPECT_NO_THROW(getState(READY_ST));
+}
+
+/// @brief Tests state dictionary initialization and validation.
+/// This tests the basic concept of state dictionary initialization and
+/// verification by manually invoking the methods normally called by startModel.
+TEST_F(StateModelTest, stateDictionary) {
+    // Verify that the map validation throws prior to the dictionary being
+    // initialized.
+    EXPECT_THROW(verifyStates(), StateModelError);
+
+    // Construct the dictionary and verify it.
+    ASSERT_NO_THROW(defineStates());
+    EXPECT_NO_THROW(verifyStates());
+
+    // Verify the base class states.
+    EXPECT_TRUE(checkState(NEW_ST, "NEW_ST"));
+    EXPECT_TRUE(checkState(END_ST, "END_ST"));
+
+    // Verify stub class states.
+    EXPECT_TRUE(checkState(DUMMY_ST, "DUMMY_ST"));
+    EXPECT_TRUE(checkState(READY_ST, "READY_ST"));
+    EXPECT_TRUE(checkState(DO_WORK_ST, "DO_WORK_ST"));
+    EXPECT_TRUE(checkState(DONE_ST, "DONE_ST"));
+
+    // Verify that undefined states are handled correctly.
+    EXPECT_THROW(getState(9999), StateModelError);
+    EXPECT_EQ(LabeledValueSet::UNDEFINED_LABEL, getStateLabel(9999));
+}
+
+/// @brief General testing of state context accessors.
+/// Most if not all of these are also tested as a byproduct off larger tests.
+TEST_F(StateModelTest, stateContextAccessors) {
+    // setState will throw unless we initialize the handler map.
+    ASSERT_NO_THROW(defineStates());
+    ASSERT_NO_THROW(verifyStates());
+
+    // Verify post-construction state values.
+    EXPECT_EQ(NEW_ST, getCurrState());
+    EXPECT_EQ(NEW_ST, getPrevState());
+
+    // Call setState which will update both state and previous state.
+    EXPECT_NO_THROW(setState(READY_ST));
+
+    // Verify the values are what we expect.
+    EXPECT_EQ(READY_ST, getCurrState());
+    EXPECT_EQ(NEW_ST, getPrevState());
+
+    // Call setState again.
+    EXPECT_NO_THROW(setState(DO_WORK_ST));
+
+    // Verify the values are what we expect.
+    EXPECT_EQ(DO_WORK_ST, getCurrState());
+    EXPECT_EQ(READY_ST, getPrevState());
+
+    // Verify that calling setState with an state that has no handler
+    // will throw.
+    EXPECT_THROW(setState(-1), StateModelError);
+
+    // Verify that calling setState with NEW_ST is ok.
+    EXPECT_NO_THROW(setState(NEW_ST));
+
+    // Verify that calling setState with END_ST is ok.
+    EXPECT_NO_THROW(setState(END_ST));
+
+    // Verify that calling setState with an undefined state throws.
+    EXPECT_THROW(setState(9999), StateModelError);
+}
+
+/// @brief Checks that invoking runModel prior to startModel is not allowed.
+TEST_F(StateModelTest, runBeforeStart) {
+    // Verify that the failure explanation is empty and work is not done.
+    EXPECT_TRUE(getFailureExplanation().empty());
+
+    // Attempt to call runModel before startModel.  This should result in an
+    // orderly model failure.
+    ASSERT_NO_THROW(runModel(START_EVT));
+
+    // Check that state and event are correct.
+    EXPECT_EQ(END_ST, getCurrState());
+    EXPECT_EQ(FAIL_EVT, getNextEvent());
+
+    // Verify that failure explanation is not empty.
+    EXPECT_FALSE(getFailureExplanation().empty());
+}
+
+/// @brief Tests that the endModel may be used to transition the model to
+/// a normal conclusion.
+TEST_F(StateModelTest, transitionWithEnd) {
+    // Init dictionaries manually, normally done by startModel.
+    initDictionaires();
+
+    // call transition to move from NEW_ST to DUMMY_ST with START_EVT
+    EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
+
+    //  Verify that state and event members are as expected.
+    EXPECT_EQ(DUMMY_ST, getCurrState());
+    EXPECT_EQ(NEW_ST, getPrevState());
+    EXPECT_EQ(START_EVT, getNextEvent());
+    EXPECT_EQ(NOP_EVT, getLastEvent());
+
+    // Call endModel to transition us to the end of the model.
+    EXPECT_NO_THROW(endModel());
+
+    // Verify state and event members are correctly set.
+    EXPECT_EQ(END_ST, getCurrState());
+    EXPECT_EQ(DUMMY_ST, getPrevState());
+    EXPECT_EQ(END_EVT, getNextEvent());
+    EXPECT_EQ(START_EVT, getLastEvent());
+}
+
+/// @brief Tests that the abortModel may be used to transition the model to
+/// failed conclusion.
+TEST_F(StateModelTest, transitionWithAbort) {
+    // Init dictionaries manually, normally done by startModel.
+    initDictionaires();
+
+    // call transition to move from NEW_ST to DUMMY_ST with START_EVT
+    EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
+
+    //  Verify that state and event members are as expected.
+    EXPECT_EQ(DUMMY_ST, getCurrState());
+    EXPECT_EQ(NEW_ST, getPrevState());
+    EXPECT_EQ(START_EVT, getNextEvent());
+    EXPECT_EQ(NOP_EVT, getLastEvent());
+
+    // Call endModel to transition us to the end of the model.
+    EXPECT_NO_THROW(abortModel("test invocation"));
+
+    // Verify state and event members are correctly set.
+    EXPECT_EQ(END_ST, getCurrState());
+    EXPECT_EQ(DUMMY_ST, getPrevState());
+    EXPECT_EQ(FAIL_EVT, getNextEvent());
+    EXPECT_EQ(START_EVT, getLastEvent());
+}
+
+/// @brief Tests that the boolean indicators for on state entry and exit
+/// work properly.
+TEST_F(StateModelTest, doFlags) {
+    // Init dictionaries manually, normally done by startModel.
+    initDictionaires();
+
+    // Verify that "do" flags are false.
+    EXPECT_FALSE(doOnEntry());
+    EXPECT_FALSE(doOnExit());
+
+    // call transition to move from NEW_ST to DUMMY_ST with START_EVT
+    EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
+
+    // We are transitioning states, so "do" flags should be true.
+    EXPECT_TRUE(doOnEntry());
+    EXPECT_TRUE(doOnExit());
+
+    // "do" flags are one-shots, so they should now both be false.
+    EXPECT_FALSE(doOnEntry());
+    EXPECT_FALSE(doOnExit());
+
+    // call transition to re-enter same state, "do" flags should be false.
+    EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
+
+    // "do" flags should be false.
+    EXPECT_FALSE(doOnEntry());
+    EXPECT_FALSE(doOnExit());
+
+}
+
+/// @brief Verifies that the model status methods accurately reflect the model
+/// status.  It also verifies that the dictionaries can be modified before
+/// the model is running but not after.
+TEST_F(StateModelTest, statusMethods) {
+    // Init dictionaries manually, normally done by startModel.
+    initDictionaires();
+
+    // After construction, state model is "new", all others should be false.
+    EXPECT_TRUE(isModelNew());
+    EXPECT_FALSE(isModelRunning());
+    EXPECT_FALSE(isModelWaiting());
+    EXPECT_FALSE(isModelDone());
+    EXPECT_FALSE(didModelFail());
+
+    // Verify that events and states can be added before the model is started.
+    EXPECT_NO_THROW(defineEvent(9998, "9998"));
+    EXPECT_NO_THROW(defineState(9998, "9998",
+                                boost::bind(&StateModelTest::readyHandler,
+                                            this)));
+
+    // "START" the model.
+    // Fake out starting the model by calling transition to move from NEW_ST
+    // to DUMMY_ST with START_EVT.  If we used startModel this would blow by
+    // the status  of "running" but not "waiting".
+    EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
+
+
+    // Verify that events and states cannot be added after the model is started.
+    EXPECT_THROW(defineEvent(9999, "9999"), StateModelError);
+    EXPECT_THROW(defineState(9999, "9999",
+                             boost::bind(&StateModelTest::readyHandler, this)),
+                 StateModelError);
+
+    // The state and event combos set above, should show the model as
+    // "running", all others should be false.
+    EXPECT_FALSE(isModelNew());
+    EXPECT_TRUE(isModelRunning());
+    EXPECT_FALSE(isModelWaiting());
+    EXPECT_FALSE(isModelDone());
+    EXPECT_FALSE(didModelFail());
+
+    // call transition to submit NOP_EVT to current state, DUMMY_ST.
+    EXPECT_NO_THROW(transition(DUMMY_ST, NOP_EVT));
+
+    // Verify the status methods are correct: with next event set to NOP_EVT,
+    // model should be "running" and "waiting".
+    EXPECT_FALSE(isModelNew());
+    EXPECT_TRUE(isModelRunning());
+    EXPECT_TRUE(isModelWaiting());
+    EXPECT_FALSE(isModelDone());
+    EXPECT_FALSE(didModelFail());
+
+    // Call endModel to transition us to the end of the model.
+    EXPECT_NO_THROW(endModel());
+
+    // With state set to END_ST, model should be done.
+    EXPECT_FALSE(isModelNew());
+    EXPECT_FALSE(isModelRunning());
+    EXPECT_FALSE(isModelWaiting());
+    EXPECT_TRUE(isModelDone());
+    EXPECT_FALSE(didModelFail());
+}
+
+/// @brief Tests that the model status methods are correct after a model
+/// failure.
+TEST_F(StateModelTest, statusMethodsOnFailure) {
+    // Construct the event and state definitions, normally done by startModel.
+    ASSERT_NO_THROW(defineEvents());
+    ASSERT_NO_THROW(verifyEvents());
+    ASSERT_NO_THROW(defineStates());
+    ASSERT_NO_THROW(verifyStates());
+
+    // call transition to move from NEW_ST to DUMMY_ST with START_EVT
+    EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
+
+    // Call endModel to transition us to the end of the model.
+    EXPECT_NO_THROW(abortModel("test invocation"));
+
+    // With state set to END_ST, model should be done.
+    EXPECT_FALSE(isModelNew());
+    EXPECT_FALSE(isModelRunning());
+    EXPECT_FALSE(isModelWaiting());
+    EXPECT_TRUE(isModelDone());
+    EXPECT_TRUE(didModelFail());
+}
+
+/// @brief Checks that the context strings accurately reflect context and
+/// are safe to invoke.
+TEST_F(StateModelTest, contextStrs) {
+    // Verify context methods do not throw prior to dictionary init.
+    ASSERT_NO_THROW(getContextStr());
+    ASSERT_NO_THROW(getPrevContextStr());
+
+    // Construct the event and state definitions, normally done by startModel.
+    ASSERT_NO_THROW(defineEvents());
+    ASSERT_NO_THROW(verifyEvents());
+    ASSERT_NO_THROW(defineStates());
+    ASSERT_NO_THROW(verifyStates());
+
+    // transition uses setState and setEvent, testing it tests all three.
+    EXPECT_NO_THROW(transition(READY_ST, START_EVT));
+
+    // Verify the current context string depicts correct state and event.
+    std::string ctx_str;
+    ASSERT_NO_THROW(ctx_str = getContextStr());
+    EXPECT_NE(std::string::npos, ctx_str.find(getStateLabel(READY_ST)));
+    EXPECT_NE(std::string::npos, ctx_str.find(getEventLabel(START_EVT)));
+
+    // Verify the previous context string depicts correct state and event.
+    ASSERT_NO_THROW(ctx_str = getPrevContextStr());
+    EXPECT_NE(std::string::npos, ctx_str.find(getStateLabel(NEW_ST)));
+    EXPECT_NE(std::string::npos, ctx_str.find(getEventLabel(NOP_EVT)));
+}
+
+/// @brief Tests that undefined states are handled gracefully.
+/// This test verifies that attempting to transition to an undefined state,
+/// which constitutes a model violation, results in an orderly model failure.
+TEST_F(StateModelTest, undefinedState) {
+    // Verify that the failure explanation is empty and work is not done.
+    EXPECT_TRUE(getFailureExplanation().empty());
+    EXPECT_FALSE(getWorkCompleted());
+
+    // First, lets execute the state model to a known valid point, by
+    // calling startModel. This should run the model through to DO_WORK_ST.
+    ASSERT_NO_THROW(startModel(READY_ST));
+
+    // Verify we are in the state of DO_WORK_ST with event of NOP_EVT.
+    EXPECT_EQ(DO_WORK_ST, getCurrState());
+    EXPECT_EQ(NOP_EVT, getNextEvent());
+
+    // Resume the model with next event set to cause the DO_WORK_ST handler
+    // to transition to an undefined state. This should cause it to return
+    // without throwing and yield a failed model.
+    EXPECT_NO_THROW(runModel(FORCE_UNDEFINED_ST_EVT));
+
+    // Verify that status methods are correct: model is done but failed.
+    EXPECT_FALSE(isModelNew());
+    EXPECT_FALSE(isModelRunning());
+    EXPECT_FALSE(isModelWaiting());
+    EXPECT_TRUE(isModelDone());
+    EXPECT_TRUE(didModelFail());
+
+    // Verify that failure explanation is not empty.
+    EXPECT_FALSE(getFailureExplanation().empty());
+
+    // Verify that work completed flag is still false.
+    EXPECT_FALSE(getWorkCompleted());
+}
+
+/// @brief Tests that an unexpected exception thrown by a state handler is
+/// handled gracefully.  State models are supposed to account for and handle
+/// all errors that they actions (i.e. handlers) may cause.  In the event they
+/// do not, this constitutes a model violation.  This test verifies such
+/// violations are handled correctly and result in an orderly model failure.
+TEST_F(StateModelTest, unexpectedError) {
+    // Verify that the failure explanation is empty and work is not done.
+    EXPECT_TRUE(getFailureExplanation().empty());
+    EXPECT_FALSE(getWorkCompleted());
+
+    // First, lets execute the state model to a known valid point, by
+    // calling startModel with a start state of READY_ST.
+    // This should run the model through to DO_WORK_ST.
+    ASSERT_NO_THROW(startModel(READY_ST));
+
+    // Verify we are in the state of DO_WORK_ST with event of NOP_EVT.
+    EXPECT_EQ(DO_WORK_ST, getCurrState());
+    EXPECT_EQ(NOP_EVT, getNextEvent());
+
+    // Resume the model with next event set to cause the DO_WORK_ST handler
+    // to transition to an undefined state. This should cause it to return
+    // without throwing and yield a failed model.
+    EXPECT_NO_THROW(runModel(SIMULATE_ERROR_EVT));
+
+    // Verify that status methods are correct: model is done but failed.
+    EXPECT_FALSE(isModelNew());
+    EXPECT_FALSE(isModelRunning());
+    EXPECT_FALSE(isModelWaiting());
+    EXPECT_TRUE(isModelDone());
+    EXPECT_TRUE(didModelFail());
+
+    // Verify that failure explanation is not empty.
+    EXPECT_FALSE(getFailureExplanation().empty());
+
+    // Verify that work completed flag is still false.
+    EXPECT_FALSE(getWorkCompleted());
+}
+
+/// @brief Tests that undefined events are handled gracefully.
+/// This test verifies that submitting an undefined event to the state machine
+/// results, which constitutes a model violation, results in an orderly model
+/// failure.
+TEST_F(StateModelTest, undefinedEvent) {
+    // Verify that the failure explanation is empty and work is not done.
+    EXPECT_TRUE(getFailureExplanation().empty());
+    EXPECT_FALSE(getWorkCompleted());
+
+    // First, lets execute the state model to a known valid point, by
+    // calling startModel with a start state of READY_ST.
+    // This should run the model through to DO_WORK_ST.
+    ASSERT_NO_THROW(startModel(READY_ST));
+
+    // Verify we are in the state of DO_WORK_ST with event of NOP_EVT.
+    EXPECT_EQ(DO_WORK_ST, getCurrState());
+    EXPECT_EQ(NOP_EVT, getNextEvent());
+
+    // Attempting to post an undefined event within runModel should cause it
+    // to return without throwing and yield a failed model.
+    EXPECT_NO_THROW(runModel(9999));
+
+    // Verify that status methods are correct: model is done but failed.
+    EXPECT_FALSE(isModelNew());
+    EXPECT_FALSE(isModelRunning());
+    EXPECT_FALSE(isModelWaiting());
+    EXPECT_TRUE(isModelDone());
+    EXPECT_TRUE(didModelFail());
+
+    // Verify that failure explanation is not empty.
+    EXPECT_FALSE(getFailureExplanation().empty());
+
+    // Verify that work completed flag is still false.
+    EXPECT_FALSE(getWorkCompleted());
+}
+
+/// @brief Test the basic mechanics of state model execution.
+/// This test exercises the ability to execute state model from start to
+/// finish, including the handling of a asynchronous IO operation.
+TEST_F(StateModelTest, stateModelTest) {
+    // Verify that status methods are correct: model is new.
+    EXPECT_TRUE(isModelNew());
+    EXPECT_FALSE(isModelRunning());
+    EXPECT_FALSE(isModelWaiting());
+    EXPECT_FALSE(isModelDone());
+
+    // Verify that the failure explanation is empty and work is not done.
+    EXPECT_TRUE(getFailureExplanation().empty());
+    EXPECT_FALSE(getWorkCompleted());
+
+    // Launch the transaction by calling startModel.  The state model
+    // should run up until the simulated async work operation is initiated
+    // in DO_WORK_ST.
+    ASSERT_NO_THROW(startModel(READY_ST));
+
+    // Verify that we are now in state of DO_WORK_ST, the last event was
+    // WORK_START_EVT, the next event is NOP_EVT.
+    EXPECT_EQ(DO_WORK_ST, getCurrState());
+    EXPECT_EQ(WORK_START_EVT, getLastEvent());
+    EXPECT_EQ(NOP_EVT, getNextEvent());
+
+    // Simulate completion of async work completion by resuming runModel with
+    // an event of WORK_DONE_EVT.
+    ASSERT_NO_THROW(runModel(WORK_DONE_EVT));
+
+    // Verify that the state model has progressed through to completion:
+    // it is in the DONE_ST, the status is ST_COMPLETED, and the next event
+    // is NOP_EVT.
+    EXPECT_EQ(END_ST, getCurrState());
+    EXPECT_EQ(END_EVT, getNextEvent());
+
+    // Verify that status methods are correct: model done.
+    EXPECT_FALSE(isModelNew());
+    EXPECT_FALSE(isModelRunning());
+    EXPECT_FALSE(isModelWaiting());
+    EXPECT_TRUE(isModelDone());
+
+    // Verify that failure explanation is empty.
+    EXPECT_TRUE(getFailureExplanation().empty());
+
+    // Verify that work completed flag is true.
+    EXPECT_TRUE(getWorkCompleted());
+}
+
+}
diff --git a/src/bin/dbutil/dbutil.py.in b/src/bin/dbutil/dbutil.py.in
index 292a0ba..3b7b735 100755
--- a/src/bin/dbutil/dbutil.py.in
+++ b/src/bin/dbutil/dbutil.py.in
@@ -58,6 +58,7 @@ sys.excepthook = my_except_hook
 import os, sqlite3, shutil
 from optparse import OptionParser
 import isc.util.process
+import isc.util.traceback_handler
 import isc.log
 from isc.log_messages.dbutil_messages import *
 
@@ -566,9 +567,11 @@ def parse_command():
     sys.exit(EXIT_COMMAND_ERROR)
 
 
-if __name__ == "__main__":
+def main():
     (options, args) = parse_command()
 
+    global logger
+
     if options.verbose:
         isc.log.init("b10-dbutil", "DEBUG", 99)
         logger = isc.log.Logger("dbutil")
@@ -619,3 +622,6 @@ if __name__ == "__main__":
             exit_code = EXIT_UPGRADE_ERROR
 
     sys.exit(exit_code)
+
+if __name__ == "__main__":
+    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/bin/ddns/ddns.py.in b/src/bin/ddns/ddns.py.in
index 47f67ca..6bb81c5 100755
--- a/src/bin/ddns/ddns.py.in
+++ b/src/bin/ddns/ddns.py.in
@@ -28,6 +28,7 @@ from isc.config.ccsession import *
 from isc.config.module_spec import ModuleSpecError
 from isc.cc import SessionError, SessionTimeout, ProtocolError
 import isc.util.process
+import isc.util.traceback_handler
 import isc.util.cio.socketsession
 import isc.server_common.tsig_keyring
 from isc.server_common.dns_tcp import DNSTCPContext
@@ -738,9 +739,8 @@ def main(ddns_server=None):
         logger.error(DDNS_CONFIG_ERROR, str(e))
     except SessionTimeout as e:
         logger.error(DDNS_CC_SESSION_TIMEOUT_ERROR)
-    except Exception as e:
-        logger.error(DDNS_UNCAUGHT_EXCEPTION, type(e).__name__, str(e))
-    clear_socket()
+    finally:
+        clear_socket()
 
 if '__main__' == __name__:
-    main()
+    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/bin/ddns/ddns_messages.mes b/src/bin/ddns/ddns_messages.mes
index c537ae4..1beb6df 100644
--- a/src/bin/ddns/ddns_messages.mes
+++ b/src/bin/ddns/ddns_messages.mes
@@ -237,11 +237,6 @@ DDNS UPDATE messages, it will return SERVFAIL. However, this does point to
 an underlying problem in the messaging system, and should be inspected.
 The specific error is printed in the log message.
 
-% DDNS_UNCAUGHT_EXCEPTION uncaught exception of type %1: %2
-The b10-ddns process encountered an uncaught exception and will now shut
-down. This is indicative of a programming error and should not happen under
-normal circumstances. The exception type and message are printed.
-
 % DDNS_UPDATE_NOTIFY notified %1 of updates to %2
 Debug message.  b10-ddns has made updates to a zone based on an update
 request and has successfully notified an external module of the updates.
diff --git a/src/bin/ddns/tests/ddns_test.py b/src/bin/ddns/tests/ddns_test.py
index 4cf31be..66e87a4 100755
--- a/src/bin/ddns/tests/ddns_test.py
+++ b/src/bin/ddns/tests/ddns_test.py
@@ -1375,7 +1375,6 @@ class TestMain(unittest.TestCase):
         self.check_exception(isc.config.ModuleCCSessionError("error"))
         self.check_exception(ddns.DDNSConfigError("error"))
         self.check_exception(isc.cc.SessionTimeout("error"))
-        self.check_exception(Exception("error"))
 
         # Add one that is not a subclass of Exception, and hence not
         # caught. Misuse BaseException for that.
diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am
index 6251b51..a38919a 100644
--- a/src/bin/dhcp4/Makefile.am
+++ b/src/bin/dhcp4/Makefile.am
@@ -16,7 +16,7 @@ endif
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 
-CLEANFILES  = *.gcno *.gcda spec_config.h dhcp4_messages.h dhcp4_messages.cc
+CLEANFILES  = *.gcno *.gcda spec_config.h dhcp4_messages.h dhcp4_messages.cc s-messages
 
 man_MANS = b10-dhcp4.8
 DISTCLEANFILES = $(man_MANS)
@@ -39,8 +39,11 @@ endif
 spec_config.h: spec_config.h.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
 
-dhcp4_messages.h dhcp4_messages.cc: dhcp4_messages.mes
+dhcp4_messages.h dhcp4_messages.cc: s-messages
+
+s-messages: dhcp4_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/dhcp4/dhcp4_messages.mes
+	touch $@
 
 BUILT_SOURCES = spec_config.h dhcp4_messages.h dhcp4_messages.cc
 
diff --git a/src/bin/dhcp4/config_parser.cc b/src/bin/dhcp4/config_parser.cc
index 98393c1..a5acac0 100644
--- a/src/bin/dhcp4/config_parser.cc
+++ b/src/bin/dhcp4/config_parser.cc
@@ -94,6 +94,13 @@ protected:
         } else if (option_space == "dhcp6") {
             isc_throw(DhcpConfigError, "'dhcp6' option space name is reserved"
                      << " for DHCPv6 server");
+        } else {
+            // Check if this is a vendor-option. If it is, get vendor-specific
+            // definition.
+            uint32_t vendor_id = SubnetConfigParser::optionSpaceToVendorId(option_space);
+            if (vendor_id) {
+                def = LibDHCP::getVendorOptionDef(Option::V4, vendor_id, option_code);
+            }
         }
 
         return (def);
@@ -192,7 +199,8 @@ protected:
             (config_id.compare("rebind-timer") == 0))  {
             parser = new Uint32Parser(config_id, uint32_values_);
         } else if ((config_id.compare("subnet") == 0) ||
-                   (config_id.compare("interface") == 0)) {
+                   (config_id.compare("interface") == 0) ||
+                   (config_id.compare("next-server") == 0)) {
             parser = new StringParser(config_id, string_values_);
         } else if (config_id.compare("pool") == 0) {
             parser = new Pool4Parser(config_id, pools_);
@@ -257,14 +265,34 @@ protected:
         Triplet<uint32_t> t2 = getParam("rebind-timer");
         Triplet<uint32_t> valid = getParam("valid-lifetime");
 
-        /// @todo: Convert this to logger once the parser is working reliably
         stringstream tmp;
         tmp << addr.toText() << "/" << (int)len
             << " with params t1=" << t1 << ", t2=" << t2 << ", valid=" << valid;
 
         LOG_INFO(dhcp4_logger, DHCP4_CONFIG_NEW_SUBNET).arg(tmp.str());
 
-        subnet_.reset(new Subnet4(addr, len, t1, t2, valid));
+        Subnet4Ptr subnet4(new Subnet4(addr, len, t1, t2, valid));
+        subnet_ = subnet4;
+
+        // Try global value first
+        try {
+            string next_server = globalContext()->string_values_->getParam("next-server");
+            if (!next_server.empty()) {
+                subnet4->setSiaddr(IOAddress(next_server));
+            }
+        } catch (const DhcpConfigError&) {
+            // Don't care. next_server is optional. We can live without it
+        }
+
+        // Try subnet specific value if it's available
+        try {
+            string next_server = string_values_->getParam("next-server");
+            if (!next_server.empty()) {
+                subnet4->setSiaddr(IOAddress(next_server));
+            }
+        } catch (const DhcpConfigError&) {
+            // Don't care. next_server is optional. We can live without it
+        }
     }
 };
 
@@ -359,7 +387,8 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
     } else if (config_id.compare("option-def") == 0) {
         parser  = new OptionDefListParser(config_id,
                                           globalContext()->option_defs_);
-    } else if (config_id.compare("version") == 0) {
+    } else if ((config_id.compare("version") == 0) ||
+               (config_id.compare("next-server") == 0)) {
         parser  = new StringParser(config_id,
                                     globalContext()->string_values_);
     } else if (config_id.compare("lease-database") == 0) {
diff --git a/src/bin/dhcp4/dhcp4.dox b/src/bin/dhcp4/dhcp4.dox
index fc5d3f9..0d457c6 100644
--- a/src/bin/dhcp4/dhcp4.dox
+++ b/src/bin/dhcp4/dhcp4.dox
@@ -79,9 +79,14 @@ See \ref dhcpv6ConfigParser.
 Configuration inheritance in DHCPv4 follows exactly the same logic as its DHCPv6
 counterpart. See \ref dhcpv6ConfigInherit.
 
- at section dhcpv4DDNSIntegration DHCPv4 Server Support for the Dynamic DNS Updates
+ at section dhcpv4OptionsParse Custom functions to parse message options
+
+The DHCPv4 server uses the same logic to supply custom callback function to
+parse message option as DHCPv6 server implementation. See \ref dhcpv6OptionsParse.
 
-The DHCPv4 server supports processing of the DHCPv4 Client FQDN option (RFC4702)
+ at section dhcpv4DDNSIntegration DHCPv4 Server Support for the Dynamic DNS Updates
+T
+he DHCPv4 server supports processing of the DHCPv4 Client FQDN option (RFC4702)
 and the DHCPv4 Host Name option (RFC2132). Client may send one of these options
 to convey its fully qualified or partial name to the server. The server may use
 this name to perform DNS updates for the client. If server receives both options
diff --git a/src/bin/dhcp4/dhcp4.spec b/src/bin/dhcp4/dhcp4.spec
index b979d45..b507159 100644
--- a/src/bin/dhcp4/dhcp4.spec
+++ b/src/bin/dhcp4/dhcp4.spec
@@ -16,7 +16,7 @@
           "item_default": ""
         }
       },
- 
+
       { "item_name": "interfaces",
         "item_type": "list",
         "item_optional": false,
@@ -48,6 +48,12 @@
         "item_default": 4000
       },
 
+      { "item_name": "next-server",
+        "item_type": "string",
+        "item_optional": true,
+        "item_default": ""
+      },
+
       { "item_name": "option-def",
         "item_type": "list",
         "item_optional": false,
@@ -218,6 +224,13 @@
                   "item_optional": false,
                   "item_default": 7200
                 },
+
+                { "item_name": "next-server",
+                  "item_type": "string",
+                  "item_optional": true,
+                  "item_default": "0.0.0.0"
+                },
+
                 { "item_name": "pool",
                   "item_type": "list",
                   "item_optional": false,
@@ -290,7 +303,7 @@
 
         {
             "command_name": "libreload",
-            "command_description": "Reloads the current hooks libraries.", 
+            "command_description": "Reloads the current hooks libraries.",
             "command_args": []
         }
 
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
index b4b834f..90cf60e 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/src/bin/dhcp4/dhcp4_messages.mes
@@ -133,7 +133,7 @@ a lease. It is up to the client to choose one server out of othe advertised
 and continue allocation with that server. This is a normal behavior and
 indicates successful operation.
 
-% DHCP4_LEASE_ADVERT_FAIL failed to advertise a lease for client client-id %1, hwaddr %2
+% DHCP4_LEASE_ADVERT_FAIL failed to advertise a lease for client client-id %1, hwaddr %2, client sent yiaddr %3
 This message indicates that the server has failed to offer a lease to
 the specified client after receiving a DISCOVER message from it. There are
 many possible reasons for such a failure.
@@ -143,7 +143,7 @@ This debug message indicates that the server successfully granted a lease
 in response to client's REQUEST message. This is a normal behavior and
 indicates successful operation.
 
-% DHCP4_LEASE_ALLOC_FAIL failed to grant a lease for client-id %1, hwaddr %2
+% DHCP4_LEASE_ALLOC_FAIL failed to grant a lease for client-id %1, hwaddr %2, client sent yiaddr %3
 This message indicates that the server failed to grant a lease to the
 specified client after receiving a REQUEST message from it.  There are many
 possible reasons for such a failure. Additional messages will indicate the
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index 873a3e7..b178f53 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -20,7 +20,9 @@
 #include <dhcp/option4_addrlst.h>
 #include <dhcp/option_int.h>
 #include <dhcp/option_int_array.h>
+#include <dhcp/option_vendor.h>
 #include <dhcp/pkt4.h>
+#include <dhcp/docsis3_option_defs.h>
 #include <dhcp4/dhcp4_log.h>
 #include <dhcp4/dhcp4_srv.h>
 #include <dhcpsrv/addr_utilities.h>
@@ -36,6 +38,8 @@
 #include <util/strutil.h>
 
 #include <boost/algorithm/string/erase.hpp>
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
 
 #include <iomanip>
 #include <fstream>
@@ -167,7 +171,8 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
             .arg(LeaseMgrFactory::instance().getName());
 
         // Instantiate allocation engine
-        alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
+        alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100,
+                                            false /* false = IPv4 */));
 
         // Register hook points
         hook_index_pkt4_receive_   = Hooks.hook_index_pkt4_receive_;
@@ -228,12 +233,20 @@ Dhcpv4Srv::run() {
             continue;
         }
 
+        // In order to parse the DHCP options, the server needs to use some
+        // configuration information such as: existing option spaces, option
+        // definitions etc. This is the kind of information which is not
+        // available in the libdhcp, so we need to supply our own implementation
+        // of the option parsing function here, which would rely on the
+        // configuration data.
+        query->setCallback(boost::bind(&Dhcpv4Srv::unpackOptions, this,
+                                       _1, _2, _3));
+
         bool skip_unpack = false;
 
         // The packet has just been received so contains the uninterpreted wire
         // data; execute callouts registered for buffer4_receive.
-        if (HooksManager::getHooksManager()
-            .calloutsPresent(Hooks.hook_index_buffer4_receive_)) {
+        if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_receive_)) {
             CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
             // Delete previously set arguments
@@ -427,8 +440,7 @@ Dhcpv4Srv::run() {
             // Option objects modification does not make sense anymore. Hooks
             // can only manipulate wire buffer at this stage.
             // Let's execute all callouts registered for buffer4_send
-            if (HooksManager::getHooksManager()
-                .calloutsPresent(Hooks.hook_index_buffer4_send_)) {
+            if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_send_)) {
                 CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
                 // Delete previously set arguments
@@ -656,6 +668,12 @@ Dhcpv4Srv::copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer) {
     if (dst_hw_addr) {
         answer->setRemoteHWAddr(dst_hw_addr);
     }
+
+    // If this packet is relayed, we want to copy Relay Agent Info option
+    OptionPtr rai = question->getOption(DHO_DHCP_AGENT_OPTIONS);
+    if (rai) {
+        answer->addOption(rai);
+    }
 }
 
 void
@@ -701,20 +719,78 @@ Dhcpv4Srv::appendRequestedOptions(const Pkt4Ptr& question, Pkt4Ptr& msg) {
     // to be returned to the client.
     for (std::vector<uint8_t>::const_iterator opt = requested_opts.begin();
          opt != requested_opts.end(); ++opt) {
-        Subnet::OptionDescriptor desc =
-            subnet->getOptionDescriptor("dhcp4", *opt);
-        if (desc.option) {
-            msg->addOption(desc.option);
+        if (!msg->getOption(*opt)) {
+            Subnet::OptionDescriptor desc =
+                subnet->getOptionDescriptor("dhcp4", *opt);
+            if (desc.option && !msg->getOption(*opt)) {
+                msg->addOption(desc.option);
+            }
+        }
+    }
+}
+
+void
+Dhcpv4Srv::appendRequestedVendorOptions(const Pkt4Ptr& question, Pkt4Ptr& answer) {
+    // Get the configured subnet suitable for the incoming packet.
+    Subnet4Ptr subnet = selectSubnet(question);
+    // Leave if there is no subnet matching the incoming packet.
+    // There is no need to log the error message here because
+    // it will be logged in the assignLease() when it fails to
+    // pick the suitable subnet. We don't want to duplicate
+    // error messages in such case.
+    if (!subnet) {
+        return;
+    }
+
+    // Try to get the vendor option
+    boost::shared_ptr<OptionVendor> vendor_req =
+        boost::dynamic_pointer_cast<OptionVendor>(question->getOption(DHO_VIVSO_SUBOPTIONS));
+    if (!vendor_req) {
+        return;
+    }
+
+    uint32_t vendor_id = vendor_req->getVendorId();
+
+    // Let's try to get ORO within that vendor-option
+    /// @todo This is very specific to vendor-id=4491 (Cable Labs). Other vendors
+    /// may have different policies.
+    OptionUint8ArrayPtr oro =
+        boost::dynamic_pointer_cast<OptionUint8Array>(vendor_req->getOption(DOCSIS3_V4_ORO));
+
+    // Option ORO not found. Don't do anything then.
+    if (!oro) {
+        return;
+    }
+
+    boost::shared_ptr<OptionVendor> vendor_rsp(new OptionVendor(Option::V4, vendor_id));
+
+    // Get the list of options that client requested.
+    bool added = false;
+    const std::vector<uint8_t>& requested_opts = oro->getValues();
+
+    for (std::vector<uint8_t>::const_iterator code = requested_opts.begin();
+         code != requested_opts.end(); ++code) {
+        if  (!vendor_rsp->getOption(*code)) {
+            Subnet::OptionDescriptor desc = subnet->getVendorOptionDescriptor(vendor_id,
+                                                                              *code);
+            if (desc.option) {
+                vendor_rsp->addOption(desc.option);
+                added = true;
+            }
+        }
+
+        if (added) {
+            answer->addOption(vendor_rsp);
         }
     }
 }
 
+
 void
 Dhcpv4Srv::appendBasicOptions(const Pkt4Ptr& question, Pkt4Ptr& msg) {
     // Identify options that we always want to send to the
     // client (if they are configured).
     static const uint16_t required_options[] = {
-        DHO_SUBNET_MASK,
         DHO_ROUTERS,
         DHO_DOMAIN_NAME_SERVERS,
         DHO_DOMAIN_NAME };
@@ -1026,6 +1102,13 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
         return;
     }
 
+    // Set up siaddr. Perhaps assignLease is not the best place to call this
+    // as siaddr has nothing to do with a lease, but otherwise we would have
+    // to select subnet twice (performance hit) or update too many functions
+    // at once.
+    // @todo: move subnet selection to a common code
+    answer->setSiaddr(subnet->getSiaddr());
+
     LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_SUBNET_SELECTED)
         .arg(subnet->toText());
 
@@ -1079,12 +1162,12 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
     // be inserted into the LeaseMgr as well.
     // @todo pass the actual FQDN data.
     Lease4Ptr old_lease;
-    Lease4Ptr lease = alloc_engine_->allocateAddress4(subnet, client_id, hwaddr,
+    Lease4Ptr lease = alloc_engine_->allocateLease4(subnet, client_id, hwaddr,
                                                       hint, fqdn_fwd, fqdn_rev,
                                                       hostname,
-                                                      fake_allocation,
-                                                      callout_handle,
-                                                      old_lease);
+                                                    fake_allocation,
+                                                    callout_handle,
+                                                    old_lease);
 
     if (lease) {
         // We have a lease! Let's set it in the packet and send it back to
@@ -1138,13 +1221,6 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
         opt->setUint32(lease->valid_lft_);
         answer->addOption(opt);
 
-        // Router (type 3)
-        Subnet::OptionDescriptor opt_routers =
-            subnet->getOptionDescriptor("dhcp4", DHO_ROUTERS);
-        if (opt_routers.option) {
-            answer->addOption(opt_routers.option);
-        }
-
         // Subnet mask (type 1)
         answer->addOption(getNetmaskOption(subnet));
 
@@ -1259,7 +1335,6 @@ Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
 
     copyDefaultFields(discover, offer);
     appendDefaultOptions(offer, DHCPOFFER);
-    appendRequestedOptions(discover, offer);
 
     // If DISCOVER message contains the FQDN or Hostname option, server
     // may respond to the client with the appropriate FQDN or Hostname
@@ -1269,10 +1344,15 @@ Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
 
     assignLease(discover, offer);
 
-    // There are a few basic options that we always want to
-    // include in the response. If client did not request
-    // them we append them for him.
-    appendBasicOptions(discover, offer);
+    // Adding any other options makes sense only when we got the lease.
+    if (offer->getYiaddr() != IOAddress("0.0.0.0")) {
+        appendRequestedOptions(discover, offer);
+        appendRequestedVendorOptions(discover, offer);
+        // There are a few basic options that we always want to
+        // include in the response. If client did not request
+        // them we append them for him.
+        appendBasicOptions(discover, offer);
+    }
 
     return (offer);
 }
@@ -1288,7 +1368,6 @@ Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
 
     copyDefaultFields(request, ack);
     appendDefaultOptions(ack, DHCPACK);
-    appendRequestedOptions(request, ack);
 
     // If REQUEST message contains the FQDN or Hostname option, server
     // should respond to the client with the appropriate FQDN or Hostname
@@ -1301,10 +1380,15 @@ Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
     // or even rebinding.
     assignLease(request, ack);
 
-    // There are a few basic options that we always want to
-    // include in the response. If client did not request
-    // them we append them for him.
-    appendBasicOptions(request, ack);
+    // Adding any other options makes sense only when we got the lease.
+    if (ack->getYiaddr() != IOAddress("0.0.0.0")) {
+        appendRequestedOptions(request, ack);
+        appendRequestedVendorOptions(request, ack);
+        // There are a few basic options that we always want to
+        // include in the response. If client did not request
+        // them we append them for him.
+        appendBasicOptions(request, ack);
+    }
 
     return (ack);
 }
@@ -1359,8 +1443,7 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
         bool skip = false;
 
         // Execute all callouts registered for lease4_release
-        if (HooksManager::getHooksManager()
-            .calloutsPresent(Hooks.hook_index_lease4_release_)) {
+        if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_release_)) {
             CalloutHandlePtr callout_handle = getCalloutHandle(release);
 
             // Delete all previous arguments
@@ -1596,6 +1679,95 @@ Dhcpv4Srv::openActiveSockets(const uint16_t port,
     }
 }
 
+size_t
+Dhcpv4Srv::unpackOptions(const OptionBuffer& buf,
+                          const std::string& option_space,
+                          isc::dhcp::OptionCollection& options) {
+    size_t offset = 0;
+
+    OptionDefContainer option_defs;
+    if (option_space == "dhcp4") {
+        // Get the list of stdandard option definitions.
+        option_defs = LibDHCP::getOptionDefs(Option::V4);
+    } else if (!option_space.empty()) {
+        OptionDefContainerPtr option_defs_ptr =
+            CfgMgr::instance().getOptionDefs(option_space);
+        if (option_defs_ptr != NULL) {
+            option_defs = *option_defs_ptr;
+        }
+    }
+    // Get the search index #1. It allows to search for option definitions
+    // using option code.
+    const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
+
+    // The buffer being read comprises a set of options, each starting with
+    // a one-byte type code and a one-byte length field.
+    while (offset + 1 <= buf.size()) {
+        uint8_t opt_type = buf[offset++];
+
+        // DHO_END is a special, one octet long option
+        if (opt_type == DHO_END)
+            return (offset); // just return. Don't need to add DHO_END option
+
+        // DHO_PAD is just a padding after DHO_END. Let's continue parsing
+        // in case we receive a message without DHO_END.
+        if (opt_type == DHO_PAD)
+            continue;
+
+        if (offset + 1 >= buf.size()) {
+            // opt_type must be cast to integer so as it is not treated as
+            // unsigned char value (a number is presented in error message).
+            isc_throw(OutOfRange, "Attempt to parse truncated option "
+                      << static_cast<int>(opt_type));
+        }
+
+        uint8_t opt_len =  buf[offset++];
+        if (offset + opt_len > buf.size()) {
+            isc_throw(OutOfRange, "Option parse failed. Tried to parse "
+                      << offset + opt_len << " bytes from " << buf.size()
+                      << "-byte long buffer.");
+        }
+
+        // Get all definitions with the particular option code. Note that option code
+        // is non-unique within this container however at this point we expect
+        // to get one option definition with the particular code. If more are
+        // returned we report an error.
+        const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
+        // Get the number of returned option definitions for the option code.
+        size_t num_defs = distance(range.first, range.second);
+
+        OptionPtr opt;
+        if (num_defs > 1) {
+            // Multiple options of the same code are not supported right now!
+            isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
+                      " for option type " << static_cast<int>(opt_type)
+                      << " returned. Currently it is not supported to initialize"
+                      << " multiple option definitions for the same option code."
+                      << " This will be supported once support for option spaces"
+                      << " is implemented");
+        } else if (num_defs == 0) {
+            opt = OptionPtr(new Option(Option::V4, opt_type,
+                                       buf.begin() + offset,
+                                       buf.begin() + offset + opt_len));
+            opt->setEncapsulatedSpace("dhcp4");
+        } else {
+            // The option definition has been found. Use it to create
+            // the option instance from the provided buffer chunk.
+            const OptionDefinitionPtr& def = *(range.first);
+            assert(def);
+            opt = def->optionFactory(Option::V4, opt_type,
+                                     buf.begin() + offset,
+                                     buf.begin() + offset + opt_len,
+                                     boost::bind(&Dhcpv4Srv::unpackOptions,
+                                                 this, _1, _2, _3));
+        }
+
+        options.insert(std::make_pair(opt_type, opt));
+        offset += opt_len;
+    }
+    return (offset);
+}
+
 
 }   // namespace dhcp
 }   // namespace isc
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index 0e07a87..62139dc 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -238,6 +238,18 @@ protected:
     /// @param msg outgoing message (options will be added here)
     void appendRequestedOptions(const Pkt4Ptr& question, Pkt4Ptr& msg);
 
+    /// @brief Appends requested vendor options as requested by client.
+    ///
+    /// This method is similar to \ref appendRequestedOptions(), but uses
+    /// vendor options. The major difference is that vendor-options use
+    /// its own option spaces (there may be more than one distinct set of vendor
+    /// options, each with unique vendor-id). Vendor options are requested
+    /// using separate options within their respective vendor-option spaces.
+    ///
+    /// @param question DISCOVER or REQUEST message from a client.
+    /// @param msg outgoing message (options will be added here)
+    void appendRequestedVendorOptions(const Pkt4Ptr& question, Pkt4Ptr& answer);
+
     /// @brief Assigns a lease and appends corresponding options
     ///
     /// This method chooses the most appropriate lease for reqesting
@@ -493,6 +505,18 @@ protected:
     /// simulates transmission of a packet. For that purpose it is protected.
     virtual void sendPacket(const Pkt4Ptr& pkt);
 
+    /// @brief Implements a callback function to parse options in the message.
+    ///
+    /// @param buf a A buffer holding options in on-wire format.
+    /// @param option_space A name of the option space which holds definitions
+    /// of to be used to parse options in the packets.
+    /// @param [out] options A reference to the collection where parsed options
+    /// will be stored.
+    /// @return An offset to the first byte after last parsed option.
+    size_t unpackOptions(const OptionBuffer& buf,
+                         const std::string& option_space,
+                         isc::dhcp::OptionCollection& options);
+
 private:
 
     /// @brief Constructs netmask option based on subnet4
diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
index 4c25501..c72b8b0 100644
--- a/src/bin/dhcp4/tests/Makefile.am
+++ b/src/bin/dhcp4/tests/Makefile.am
@@ -40,25 +40,35 @@ if USE_CLANGPP
 AM_CXXFLAGS += -Wno-unused-parameter
 endif
 
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-endif
-
 TESTS_ENVIRONMENT = \
         $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST
-# Build shared libraries for testing.
+# Build shared libraries for testing. The libtool way to create a shared library
+# is to specify "-avoid-version -export-dynamic -module" in the library LDFLAGS
+# (see http://www.gnu.org/software/libtool/manual/html_node/Link-mode.html).
+# Use of these switches will guarantee that the .so files are created in the
+# .libs folder and they can be dlopened.
+# Note that the shared libraries with callouts should not be used together with
+# the --enable-static-link option. With this option, the bind10 libraries are
+# statically linked with the program and if the callout invokes the methods
+# which belong to these libraries, the library with the callout will get its
+# own copy of the static objects (e.g. logger, ServerHooks) and that will lead
+# to unexpected errors. For this reason, the --enable-static-link option is
+# ignored for unit tests built here.
+
 lib_LTLIBRARIES = libco1.la libco2.la
 
 libco1_la_SOURCES  = callout_library_1.cc callout_library_common.h
 libco1_la_CXXFLAGS = $(AM_CXXFLAGS)
 libco1_la_CPPFLAGS = $(AM_CPPFLAGS)
+libco1_la_LDFLAGS = -avoid-version -export-dynamic -module
 
 libco2_la_SOURCES  = callout_library_2.cc callout_library_common.h
 libco2_la_CXXFLAGS = $(AM_CXXFLAGS)
 libco2_la_CPPFLAGS = $(AM_CPPFLAGS)
+libco2_la_LDFLAGS = -avoid-version -export-dynamic -module
 
 TESTS += dhcp4_unittests
 
@@ -68,6 +78,8 @@ dhcp4_unittests_SOURCES += ../config_parser.cc ../config_parser.h
 dhcp4_unittests_SOURCES += dhcp4_test_utils.h
 dhcp4_unittests_SOURCES += dhcp4_unittests.cc
 dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc
+dhcp4_unittests_SOURCES += dhcp4_test_utils.cc dhcp4_test_utils.h
+dhcp4_unittests_SOURCES += wireshark.cc
 dhcp4_unittests_SOURCES += ctrl_dhcp4_srv_unittest.cc
 dhcp4_unittests_SOURCES += config_parser_unittest.cc
 dhcp4_unittests_SOURCES += fqdn_unittest.cc
diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc
index 7c01145..6e53d4d 100644
--- a/src/bin/dhcp4/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp4/tests/config_parser_unittest.cc
@@ -23,6 +23,7 @@
 #include <dhcp/option4_addrlst.h>
 #include <dhcp/option_custom.h>
 #include <dhcp/option_int.h>
+#include <dhcp/docsis3_option_defs.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <hooks/hooks_manager.h>
@@ -411,6 +412,151 @@ TEST_F(Dhcp4ParserTest, subnetGlobalDefaults) {
     EXPECT_EQ(4000, subnet->getValid());
 }
 
+// Checks if the next-server defined as global parameter is taken into
+// consideration.
+TEST_F(Dhcp4ParserTest, nextServerGlobal) {
+
+    ConstElementPtr status;
+
+    string config = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"next-server\": \"1.2.3.4\", "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Now check if the configuration was indeed handled and we have
+    // expected pool configured.
+    Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+    EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText());
+}
+
+// Checks if the next-server defined as subnet parameter is taken into
+// consideration.
+TEST_F(Dhcp4ParserTest, nextServerSubnet) {
+
+    ConstElementPtr status;
+
+    string config = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"next-server\": \"1.2.3.4\", "
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Now check if the configuration was indeed handled and we have
+    // expected pool configured.
+    Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+    EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText());
+}
+
+// Test checks several negative scenarios for next-server configuration: bogus
+// address, IPv6 adddress and empty string.
+TEST_F(Dhcp4ParserTest, nextServerNegative) {
+
+    ConstElementPtr status;
+
+    // Config with junk instead of next-server address
+    string config_bogus1 = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"rebind-timer\": 2000, "
+        "    \"renew-timer\": 1000, "
+        "    \"next-server\": \"a.b.c.d\", "
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    // Config with IPv6 next server address
+    string config_bogus2 = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"rebind-timer\": 2000, "
+        "    \"renew-timer\": 1000, "
+        "    \"next-server\": \"2001:db8::1\", "
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    // Config with empty next server address
+    string config_bogus3 = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"rebind-timer\": 2000, "
+        "    \"renew-timer\": 1000, "
+        "    \"next-server\": \"\", "
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json1 = Element::fromJSON(config_bogus1);
+    ElementPtr json2 = Element::fromJSON(config_bogus2);
+    ElementPtr json3 = Element::fromJSON(config_bogus3);
+
+    // check if returned status is always a failure
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json1));
+    checkResult(status, 1);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json2));
+    checkResult(status, 1);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json3));
+    checkResult(status, 0);
+}
+
+// Checks if the next-server defined as global value is overridden by subnet
+// specific value.
+TEST_F(Dhcp4ParserTest, nextServerOverride) {
+
+    ConstElementPtr status;
+
+    string config = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"next-server\": \"192.0.0.1\", "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"next-server\": \"1.2.3.4\", "
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Now check if the configuration was indeed handled and we have
+    // expected pool configured.
+    Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+    EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText());
+}
+
 // This test checks if it is possible to override global values
 // on a per subnet basis.
 TEST_F(Dhcp4ParserTest, subnetLocal) {
@@ -1794,6 +1940,120 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
     EXPECT_FALSE(desc.option->getOption(3));
 }
 
+// This test checks if vendor options can be specified in the config file
+// (in hex format), and later retrieved from configured subnet
+TEST_F(Dhcp4ParserTest, vendorOptionsHex) {
+
+    // This configuration string is to configure two options
+    // sharing the code 1 and belonging to the different vendor spaces.
+    // (different vendor-id values).
+    string config = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000,"
+        "\"renew-timer\": 1000,"
+        "\"option-data\": [ {"
+        "    \"name\": \"option-one\","
+        "    \"space\": \"vendor-4491\"," // VENDOR_ID_CABLE_LABS = 4491
+        "    \"code\": 100," // just a random code
+        "    \"data\": \"AB CDEF0105\","
+        "    \"csv-format\": False"
+        " },"
+        " {"
+        "    \"name\": \"option-two\","
+        "    \"space\": \"vendor-1234\","
+        "    \"code\": 100,"
+        "    \"data\": \"1234\","
+        "    \"csv-format\": False"
+        " } ],"
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1-192.0.2.10\" ],"
+        "    \"subnet\": \"192.0.2.0/24\""
+        " } ]"
+        "}";
+
+    ConstElementPtr status;
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // Options should be now available for the subnet.
+    Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
+    ASSERT_TRUE(subnet);
+
+    // Try to get the option from the vendor space 4491
+    Subnet::OptionDescriptor desc1 = subnet->getVendorOptionDescriptor(VENDOR_ID_CABLE_LABS, 100);
+    ASSERT_TRUE(desc1.option);
+    EXPECT_EQ(100, desc1.option->getType());
+    // Try to get the option from the vendor space 1234
+    Subnet::OptionDescriptor desc2 = subnet->getVendorOptionDescriptor(1234, 100);
+    ASSERT_TRUE(desc2.option);
+    EXPECT_EQ(100, desc1.option->getType());
+
+    // Try to get the non-existing option from the non-existing
+    // option space and  expect that option is not returned.
+    Subnet::OptionDescriptor desc3 = subnet->getVendorOptionDescriptor(5678, 100);
+    ASSERT_FALSE(desc3.option);
+}
+
+// This test checks if vendor options can be specified in the config file,
+// (in csv format), and later retrieved from configured subnet
+TEST_F(Dhcp4ParserTest, vendorOptionsCsv) {
+
+    // This configuration string is to configure two options
+    // sharing the code 1 and belonging to the different vendor spaces.
+    // (different vendor-id values).
+    string config = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000,"
+        "\"renew-timer\": 1000,"
+        "\"option-data\": [ {"
+        "    \"name\": \"foo\","
+        "    \"space\": \"vendor-4491\","
+        "    \"code\": 100,"
+        "    \"data\": \"this is a string vendor-opt\","
+        "    \"csv-format\": True"
+        " } ],"
+        "\"option-def\": [ {"
+        "    \"name\": \"foo\","
+        "    \"code\": 100,"
+        "    \"type\": \"string\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"vendor-4491\","
+        "    \"encapsulate\": \"\""
+        " } ],"
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"subnet\": \"192.0.2.0/24\" "
+        " } ]"
+        "}";
+
+    ConstElementPtr status;
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // Options should be now available for the subnet.
+    Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
+    ASSERT_TRUE(subnet);
+
+    // Try to get the option from the vendor space 4491
+    Subnet::OptionDescriptor desc1 = subnet->getVendorOptionDescriptor(VENDOR_ID_CABLE_LABS, 100);
+    ASSERT_TRUE(desc1.option);
+    EXPECT_EQ(100, desc1.option->getType());
+
+    // Try to get the non-existing option from the non-existing
+    // option space and  expect that option is not returned.
+    Subnet::OptionDescriptor desc2 = subnet->getVendorOptionDescriptor(5678, 100);
+    ASSERT_FALSE(desc2.option);
+}
+
+
+
 // Tests of the hooks libraries configuration.  All tests have the pre-
 // condition (checked in the test fixture's SetUp() method) that no hooks
 // libraries are loaded at the start of the tests.
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
index 0bba54a..dcaa230 100644
--- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -15,29 +15,53 @@
 #include <config.h>
 #include <sstream>
 
+#include <asiolink/io_address.h>
 #include <config/ccsession.h>
 #include <dhcp4/tests/dhcp4_test_utils.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/option.h>
+#include <dhcp/option_int.h>
+#include <dhcp/option4_addrlst.h>
+#include <dhcp/option_custom.h>
+#include <dhcp/option_int_array.h>
+#include <dhcp/option_vendor.h>
+#include <dhcp/pkt_filter.h>
+#include <dhcp/pkt_filter_inet.h>
+#include <dhcp/docsis3_option_defs.h>
+#include <dhcp4/dhcp4_srv.h>
+#include <dhcp4/dhcp4_log.h>
+#include <dhcp4/config_parser.h>
 #include <hooks/server_hooks.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/utils.h>
+#include <gtest/gtest.h>
 #include <hooks/server_hooks.h>
 #include <hooks/hooks_manager.h>
+#include <config/ccsession.h>
 
-#include <gtest/gtest.h>
 #include <boost/scoped_ptr.hpp>
 
 #include <fstream>
 #include <iostream>
 
+#include <arpa/inet.h>
+
 using namespace std;
 using namespace isc;
 using namespace isc::dhcp;
 using namespace isc::data;
 using namespace isc::asiolink;
 using namespace isc::hooks;
-using namespace isc::test;
+using namespace isc::dhcp::test;
 
 namespace {
 
+/// dummy server-id file location
+const char* SRVID_FILE = "server-id-test.txt";
+
 // Sanity check. Verifies that both Dhcpv4Srv and its derived
 // class NakedDhcpv4Srv can be instantiated and destroyed.
 TEST_F(Dhcpv4SrvTest, basic) {
@@ -730,7 +754,7 @@ TEST_F(Dhcpv4SrvTest, RenewBasic) {
     OptionPtr clientid = generateClientId();
 
     // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(addr));
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
 
     // let's create a lease and put it in the LeaseMgr
     uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
@@ -841,7 +865,7 @@ TEST_F(Dhcpv4SrvTest, ReleaseBasic) {
     OptionPtr clientid = generateClientId();
 
     // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(addr));
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
 
     // Let's create a lease and put it in the LeaseMgr
     uint8_t mac_addr[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
@@ -874,18 +898,16 @@ TEST_F(Dhcpv4SrvTest, ReleaseBasic) {
     EXPECT_FALSE(l);
 
     // Try to get the lease by hardware address
-    // @todo: Uncomment this once trac2592 is implemented
-    // Lease4Collection leases = LeaseMgrFactory::instance().getLease4(hw->hwaddr_);
-    // EXPECT_EQ(leases.size(), 0);
+    Lease4Collection leases = LeaseMgrFactory::instance().getLease4(hw->hwaddr_);
+    EXPECT_EQ(leases.size(), 0);
 
     // Try to get it by hw/subnet_id combination
     l = LeaseMgrFactory::instance().getLease4(hw->hwaddr_, subnet_->getID());
     EXPECT_FALSE(l);
 
     // Try by client-id
-    // @todo: Uncomment this once trac2592 is implemented
-    //Lease4Collection leases = LeaseMgrFactory::instance().getLease4(*client_id_);
-    //EXPECT_EQ(leases.size(), 0);
+    leases = LeaseMgrFactory::instance().getLease4(*client_id_);
+    EXPECT_EQ(leases.size(), 0);
 
     // Try by client-id/subnet-id
     l = LeaseMgrFactory::instance().getLease4(*client_id_, subnet_->getID());
@@ -919,7 +941,7 @@ TEST_F(Dhcpv4SrvTest, ReleaseReject) {
     OptionPtr clientid = generateClientId();
 
     // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(addr));
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
 
     // Let's create a RELEASE
     // Generate client-id also duid_
@@ -1017,6 +1039,129 @@ TEST_F(Dhcpv4SrvTest, ServerID) {
     EXPECT_EQ(srvid_text, text);
 }
 
+// Checks if received relay agent info option is echoed back to the client
+TEST_F(Dhcpv4SrvTest, relayAgentInfoEcho) {
+
+    NakedDhcpv4Srv srv(0);
+
+    // Let's create a relayed DISCOVER. This particular relayed DISCOVER has
+    // added option 82 (relay agent info) with 3 suboptions. The server
+    // is supposed to echo it back in its response.
+    Pkt4Ptr dis;
+    ASSERT_NO_THROW(dis = captureRelayedDiscover());
+
+    // Simulate that we have received that traffic
+    srv.fakeReceive(dis);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive4(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered buffer4_receive callback.
+    srv.run();
+
+    // Check that the server did send a reposonse
+    ASSERT_EQ(1, srv.fake_sent_.size());
+
+    // Make sure that we received a response
+    Pkt4Ptr offer = srv.fake_sent_.front();
+    ASSERT_TRUE(offer);
+
+    // Get Relay Agent Info from query...
+    OptionPtr rai_query = dis->getOption(DHO_DHCP_AGENT_OPTIONS);
+    ASSERT_TRUE(rai_query);
+
+    // Get Relay Agent Info from response...
+    OptionPtr rai_response = offer->getOption(DHO_DHCP_AGENT_OPTIONS);
+    ASSERT_TRUE(rai_response);
+
+    EXPECT_TRUE(rai_response->equal(rai_query));
+}
+
+/// @todo move vendor options tests to a separate file.
+/// @todo Add more extensive vendor options tests, including multiple
+///       vendor options
+
+// Checks if vendor options are parsed correctly and requested vendor options
+// are echoed back.
+TEST_F(Dhcpv4SrvTest, vendorOptionsDocsis) {
+
+    NakedDhcpv4Srv srv(0);
+
+    string config = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "    \"option-data\": [ {"
+        "          \"name\": \"tftp-servers\","
+        "          \"space\": \"vendor-4491\","
+        "          \"code\": 2,"
+        "          \"data\": \"10.253.175.16\","
+        "          \"csv-format\": True"
+        "        }],"
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"10.254.226.0/25\" ],"
+        "    \"subnet\": \"10.254.226.0/24\", "
+        "    \"rebind-timer\": 2000, "
+        "    \"renew-timer\": 1000, "
+        "    \"valid-lifetime\": 4000,"
+        "    \"interface\": \"" + valid_iface_ + "\" "
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+    ConstElementPtr status;
+
+    // Configure the server and make sure the config is accepted
+    EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
+    ASSERT_TRUE(status);
+    comment_ = config::parseAnswer(rcode_, status);
+    ASSERT_EQ(0, rcode_);
+
+    // Let's create a relayed DISCOVER. This particular relayed DISCOVER has
+    // added option 82 (relay agent info) with 3 suboptions. The server
+    // is supposed to echo it back in its response.
+    Pkt4Ptr dis;
+    ASSERT_NO_THROW(dis = captureRelayedDiscover());
+
+    // Simulate that we have received that traffic
+    srv.fakeReceive(dis);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive4(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered buffer4_receive callback.
+    srv.run();
+
+    // Check that the server did send a reposonse
+    ASSERT_EQ(1, srv.fake_sent_.size());
+
+    // Make sure that we received a response
+    Pkt4Ptr offer = srv.fake_sent_.front();
+    ASSERT_TRUE(offer);
+
+    // Get Relay Agent Info from query...
+    OptionPtr vendor_opt_response = offer->getOption(DHO_VIVSO_SUBOPTIONS);
+    ASSERT_TRUE(vendor_opt_response);
+
+    // Check if it's of a correct type
+    boost::shared_ptr<OptionVendor> vendor_opt =
+        boost::dynamic_pointer_cast<OptionVendor>(vendor_opt_response);
+    ASSERT_TRUE(vendor_opt);
+
+    // Get Relay Agent Info from response...
+    OptionPtr tftp_servers_generic = vendor_opt->getOption(DOCSIS3_V4_TFTP_SERVERS);
+    ASSERT_TRUE(tftp_servers_generic);
+
+    Option4AddrLstPtr tftp_servers =
+        boost::dynamic_pointer_cast<Option4AddrLst>(tftp_servers_generic);
+
+    ASSERT_TRUE(tftp_servers);
+
+    Option4AddrLst::AddressContainer addrs = tftp_servers->getAddresses();
+    ASSERT_EQ(1, addrs.size());
+    EXPECT_EQ("10.253.175.16", addrs[0].toText());
+}
+
+
 /// @todo Implement tests for subnetSelect See tests in dhcp6_srv_unittest.cc:
 /// selectSubnetAddr, selectSubnetIface, selectSubnetRelayLinkaddr,
 /// selectSubnetRelayInterfaceId. Note that the concept of interface-id is not
@@ -1057,21 +1202,224 @@ TEST_F(Dhcpv4SrvTest, Hooks) {
     EXPECT_TRUE(hook_index_buffer4_send > 0);
 }
 
-    // a dummy MAC address
-    const uint8_t dummyMacAddr[] = {0, 1, 2, 3, 4, 5};
+// This test verifies that the following option structure can be parsed:
+// - option (option space 'foobar')
+//   - sub option (option space 'foo')
+//      - sub option (option space 'bar')
+// @todo Add more thorough unit tests for unpackOptions.
+TEST_F(Dhcpv4SrvTest, unpackOptions) {
+    // Create option definition for each level of encapsulation. Each option
+    // definition is for the option code 1. Options may have the same
+    // option code because they belong to different option spaces.
+
+    // Top level option encapsulates options which belong to 'space-foo'.
+    OptionDefinitionPtr opt_def(new OptionDefinition("option-foobar", 1, "uint32",
+                                                      "space-foo"));\
+    // Middle option encapsulates options which belong to 'space-bar'
+    OptionDefinitionPtr opt_def2(new OptionDefinition("option-foo", 1, "uint16",
+                                                      "space-bar"));
+    // Low level option doesn't encapsulate any option space.
+    OptionDefinitionPtr opt_def3(new OptionDefinition("option-bar", 1,
+                                                      "uint8"));
+
+    // Add option definitions to the Configuration Manager. Each goes under
+    // different option space.
+    CfgMgr& cfgmgr = CfgMgr::instance();
+    ASSERT_NO_THROW(cfgmgr.addOptionDef(opt_def, "space-foobar"));
+    ASSERT_NO_THROW(cfgmgr.addOptionDef(opt_def2, "space-foo"));
+    ASSERT_NO_THROW(cfgmgr.addOptionDef(opt_def3, "space-bar"));
+
+    // Create the buffer holding the structure of options.
+    const char raw_data[] = {
+        // First option starts here.
+        0x01,                   // option code = 1
+        0x0B,                   // option length = 11
+        0x00, 0x01, 0x02, 0x03, // This option carries uint32 value
+        // Sub option starts here.
+        0x01,                   // option code = 1
+        0x05,                   // option length = 5
+        0x01, 0x02,             // this option carries uint16 value
+        // Last option starts here.
+        0x01,                   // option code = 1
+        0x01,                   // option length = 1
+        0x00                    // This option carries a single uint8
+                                // value and has no sub options.
+    };
+    OptionBuffer buf(raw_data, raw_data + sizeof(raw_data));
+
+    // Parse options.
+    NakedDhcpv4Srv srv(0);
+    OptionCollection options;
+    ASSERT_NO_THROW(srv.unpackOptions(buf, "space-foobar", options));
+
+    // There should be one top level option.
+    ASSERT_EQ(1, options.size());
+    boost::shared_ptr<OptionInt<uint32_t> > option_foobar =
+        boost::dynamic_pointer_cast<OptionInt<uint32_t> >(options.begin()->
+                                                          second);
+    ASSERT_TRUE(option_foobar);
+    EXPECT_EQ(1, option_foobar->getType());
+    EXPECT_EQ(0x00010203, option_foobar->getValue());
+    // There should be a middle level option held in option_foobar.
+    boost::shared_ptr<OptionInt<uint16_t> > option_foo =
+        boost::dynamic_pointer_cast<OptionInt<uint16_t> >(option_foobar->
+                                                          getOption(1));
+    ASSERT_TRUE(option_foo);
+    EXPECT_EQ(1, option_foo->getType());
+    EXPECT_EQ(0x0102, option_foo->getValue());
+    // Finally, there should be a low level option under option_foo.
+    boost::shared_ptr<OptionInt<uint8_t> > option_bar =
+        boost::dynamic_pointer_cast<OptionInt<uint8_t> >(option_foo->getOption(1));
+    ASSERT_TRUE(option_bar);
+    EXPECT_EQ(1, option_bar->getType());
+    EXPECT_EQ(0x0, option_bar->getValue());
+}
+
+// Checks whether the server uses default (0.0.0.0) siaddr value, unless
+// explicitly specified
+TEST_F(Dhcpv4SrvTest, siaddrDefault) {
+    boost::scoped_ptr<NakedDhcpv4Srv> srv;
+    ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
+    IOAddress hint("192.0.2.107");
+
+    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    dis->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    dis->addOption(clientid);
+    dis->setYiaddr(hint);
+
+    // Pass it to the server and get an offer
+    Pkt4Ptr offer = srv->processDiscover(dis);
+    ASSERT_TRUE(offer);
+
+    // Check if we get response at all
+    checkResponse(offer, DHCPOFFER, 1234);
+
+    // Verify that it is 0.0.0.0
+    EXPECT_EQ("0.0.0.0", offer->getSiaddr().toText());
+}
+
+// Checks whether the server uses specified siaddr value
+TEST_F(Dhcpv4SrvTest, siaddr) {
+    boost::scoped_ptr<NakedDhcpv4Srv> srv;
+    ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
+    subnet_->setSiaddr(IOAddress("192.0.2.123"));
+
+    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    dis->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    dis->addOption(clientid);
+
+    // Pass it to the server and get an offer
+    Pkt4Ptr offer = srv->processDiscover(dis);
+    ASSERT_TRUE(offer);
+
+    // Check if we get response at all
+    checkResponse(offer, DHCPOFFER, 1234);
+
+    // Verify that its value is proper
+    EXPECT_EQ("192.0.2.123", offer->getSiaddr().toText());
+}
+
+// Checks if the next-server defined as global value is overridden by subnet
+// specific value and returned in server messages. There's also similar test for
+// checking parser only configuration, see Dhcp4ParserTest.nextServerOverride in
+// config_parser_unittest.cc.
+TEST_F(Dhcpv4SrvTest, nextServerOverride) {
+
+    NakedDhcpv4Srv srv(0);
+
+    ConstElementPtr status;
+
+    string config = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"next-server\": \"192.0.0.1\", "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"next-server\": \"1.2.3.4\", "
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
+
+    // check if returned status is OK
+    ASSERT_TRUE(status);
+    comment_ = config::parseAnswer(rcode_, status);
+    ASSERT_EQ(0, rcode_);
+
+    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    dis->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    dis->addOption(clientid);
+
+    // Pass it to the server and get an offer
+    Pkt4Ptr offer = srv.processDiscover(dis);
+    ASSERT_TRUE(offer);
+    EXPECT_EQ(DHCPOFFER, offer->getType());
+
+    EXPECT_EQ("1.2.3.4", offer->getSiaddr().toText());
+}
+
+// Checks if the next-server defined as global value is used in responses
+// when there is no specific value defined in subnet and returned to the client
+// properly. There's also similar test for checking parser only configuration,
+// see Dhcp4ParserTest.nextServerGlobal in config_parser_unittest.cc.
+TEST_F(Dhcpv4SrvTest, nextServerGlobal) {
+
+    NakedDhcpv4Srv srv(0);
 
-    // A dummy MAC address, padded with 0s
-    const uint8_t dummyChaddr[16] = {0, 1, 2, 3, 4, 5, 0, 0,
-                                     0, 0, 0, 0, 0, 0, 0, 0 };
+    ConstElementPtr status;
 
-    // Let's use some creative test content here (128 chars + \0)
-    const uint8_t dummyFile[] = "Lorem ipsum dolor sit amet, consectetur "
-        "adipiscing elit. Proin mollis placerat metus, at "
-        "lacinia orci ornare vitae. Mauris amet.";
+    string config = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"next-server\": \"192.0.0.1\", "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
 
-    // Yet another type of test content (64 chars + \0)
-    const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur "
-        "adipiscing elit posuere.";
+    EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
+
+    // check if returned status is OK
+    ASSERT_TRUE(status);
+    comment_ = config::parseAnswer(rcode_, status);
+    ASSERT_EQ(0, rcode_);
+
+    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    dis->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    dis->addOption(clientid);
+
+    // Pass it to the server and get an offer
+    Pkt4Ptr offer = srv.processDiscover(dis);
+    ASSERT_TRUE(offer);
+    EXPECT_EQ(DHCPOFFER, offer->getType());
+
+    EXPECT_EQ("192.0.0.1", offer->getSiaddr().toText());
+}
+
+
+// a dummy MAC address
+const uint8_t dummyMacAddr[] = {0, 1, 2, 3, 4, 5};
+
+// A dummy MAC address, padded with 0s
+const uint8_t dummyChaddr[16] = {0, 1, 2, 3, 4, 5, 0, 0,
+                                 0, 0, 0, 0, 0, 0, 0, 0 };
+
+// Let's use some creative test content here (128 chars + \0)
+const uint8_t dummyFile[] = "Lorem ipsum dolor sit amet, consectetur "
+    "adipiscing elit. Proin mollis placerat metus, at "
+    "lacinia orci ornare vitae. Mauris amet.";
+
+// Yet another type of test content (64 chars + \0)
+const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur "
+    "adipiscing elit posuere.";
 
 /// @brief a class dedicated to Hooks testing in DHCPv4 server
 ///
@@ -1537,7 +1885,7 @@ vector<string> HooksDhcpv4SrvTest::callback_argument_names_;
 // but the proper hook name is "buffer4_receive".
 TEST_F(HooksDhcpv4SrvTest, Buffer4ReceiveSimple) {
 
-    // Install pkt6_receive_callout
+    // Install pkt4_receive_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "buffer4_receive", buffer4_receive_callout));
 
@@ -1586,7 +1934,7 @@ TEST_F(HooksDhcpv4SrvTest, buffer4RreceiveValueChange) {
     // In particular, it should call registered buffer4_receive callback.
     srv_->run();
 
-    // Check that the server did send a reposonce
+    // Check that the server did send a reposonse
     ASSERT_EQ(1, srv_->fake_sent_.size());
 
     // Make sure that we received a response
@@ -1609,7 +1957,7 @@ TEST_F(HooksDhcpv4SrvTest, buffer4RreceiveValueChange) {
 // (or rather option objects) in it.
 TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveSkip) {
 
-    // Install pkt6_receive_callout
+    // Install pkt4_receive_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "buffer4_receive", buffer4_receive_skip));
 
@@ -1622,7 +1970,7 @@ TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveSkip) {
     // Server will now process to run its normal loop, but instead of calling
     // IfaceMgr::receive6(), it will read all packets from the list set by
     // fakeReceive()
-    // In particular, it should call registered pkt6_receive callback.
+    // In particular, it should call registered pkt4_receive callback.
     srv_->run();
 
     // Check that the server dropped the packet and did not produce any response
@@ -2095,7 +2443,7 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectChange) {
     // Advertised address must belong to the second pool (in subnet's range,
     // in dynamic pool)
     EXPECT_TRUE((*subnets)[1]->inRange(addr));
-    EXPECT_TRUE((*subnets)[1]->inPool(addr));
+    EXPECT_TRUE((*subnets)[1]->inPool(Lease::TYPE_V4, addr));
 }
 
 // This test verifies that incoming (positive) REQUEST/Renewing can be handled
@@ -2117,7 +2465,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4RenewSimple) {
     OptionPtr clientid = generateClientId();
 
     // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(addr));
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
 
     // let's create a lease and put it in the LeaseMgr
     uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
@@ -2201,7 +2549,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4RenewSkip) {
     OptionPtr clientid = generateClientId();
 
     // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(addr));
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
 
     // let's create a lease and put it in the LeaseMgr
     uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
@@ -2265,7 +2613,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSimple) {
     OptionPtr clientid = generateClientId();
 
     // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(addr));
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
 
     // Let's create a lease and put it in the LeaseMgr
     uint8_t mac_addr[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
@@ -2350,7 +2698,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSkip) {
     OptionPtr clientid = generateClientId();
 
     // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(addr));
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
 
     // Let's create a lease and put it in the LeaseMgr
     uint8_t mac_addr[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
@@ -2397,4 +2745,188 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSkip) {
     //EXPECT_EQ(leases.size(), 1);
 }
 
-} // end of anonymous namespace
+// Checks if server is able to handle a relayed traffic from DOCSIS3.0 modems
+TEST_F(Dhcpv4SrvTest, docsisVendorOptionsParse) {
+
+    // Let's get a traffic capture from DOCSIS3.0 modem
+    Pkt4Ptr dis = captureRelayedDiscover();
+    ASSERT_NO_THROW(dis->unpack());
+
+    // Check if the packet contain
+    OptionPtr opt = dis->getOption(DHO_VIVSO_SUBOPTIONS);
+    ASSERT_TRUE(opt);
+
+    boost::shared_ptr<OptionVendor> vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+    ASSERT_TRUE(vendor);
+
+    // This particular capture that we have included options 1 and 5
+    EXPECT_TRUE(vendor->getOption(1));
+    EXPECT_TRUE(vendor->getOption(5));
+
+    // It did not include options any other options
+    EXPECT_FALSE(vendor->getOption(2));
+    EXPECT_FALSE(vendor->getOption(3));
+    EXPECT_FALSE(vendor->getOption(17));
+}
+
+// Checks if server is able to parse incoming docsis option and extract suboption 1 (docsis ORO)
+TEST_F(Dhcpv4SrvTest, docsisVendorORO) {
+
+    // Let's get a traffic capture from DOCSIS3.0 modem
+    Pkt4Ptr dis = captureRelayedDiscover();
+    EXPECT_NO_THROW(dis->unpack());
+
+    // Check if the packet contains vendor specific information option
+    OptionPtr opt = dis->getOption(DHO_VIVSO_SUBOPTIONS);
+    ASSERT_TRUE(opt);
+
+    boost::shared_ptr<OptionVendor> vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+    ASSERT_TRUE(vendor);
+
+    opt = vendor->getOption(DOCSIS3_V4_ORO);
+    ASSERT_TRUE(opt);
+
+    OptionUint8ArrayPtr oro = boost::dynamic_pointer_cast<OptionUint8Array>(opt);
+    EXPECT_TRUE(oro);
+}
+
+// This test checks if Option Request Option (ORO) in docsis (vendor-id=4491)
+// vendor options is parsed correctly and the requested options are actually assigned.
+TEST_F(Dhcpv4SrvTest, vendorOptionsORO) {
+
+    NakedDhcpv4Srv srv(0);
+
+    ConstElementPtr x;
+    string config = "{ \"interfaces\": [ \"all\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "    \"option-data\": [ {"
+        "          \"name\": \"tftp-servers\","
+        "          \"space\": \"vendor-4491\","
+        "          \"code\": 2,"
+        "          \"data\": \"192.0.2.1, 192.0.2.2\","
+        "          \"csv-format\": True"
+        "        }],"
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.0/25\" ],"
+        "    \"subnet\": \"192.0.2.0/24\", "
+        "    \"rebind-timer\": 2000, "
+        "    \"renew-timer\": 1000, "
+        "    \"valid-lifetime\": 4000,"
+        "    \"interface\": \"" + valid_iface_ + "\" "
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(x = configureDhcp4Server(srv, json));
+    ASSERT_TRUE(x);
+    comment_ = isc::config::parseAnswer(rcode_, x);
+    ASSERT_EQ(0, rcode_);
+
+    boost::shared_ptr<Pkt4> dis(new Pkt4(DHCPDISCOVER, 1234));
+    // Set the giaddr to non-zero address as if it was relayed.
+    dis->setGiaddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    dis->addOption(clientid);
+
+    // Pass it to the server and get an advertise
+    Pkt4Ptr offer = srv.processDiscover(dis);
+
+    // check if we get response at all
+    ASSERT_TRUE(offer);
+
+    // We did not include any vendor opts in DISCOVER, so there should be none
+    // in OFFER.
+    ASSERT_FALSE(offer->getOption(DHO_VIVSO_SUBOPTIONS));
+
+    // Let's add a vendor-option (vendor-id=4491) with a single sub-option.
+    // That suboption has code 1 and is a docsis ORO option.
+    boost::shared_ptr<OptionUint8Array> vendor_oro(new OptionUint8Array(Option::V4,
+                                                                        DOCSIS3_V4_ORO));
+    vendor_oro->addValue(DOCSIS3_V4_TFTP_SERVERS); // Request option 33
+    OptionPtr vendor(new OptionVendor(Option::V4, 4491));
+    vendor->addOption(vendor_oro);
+    dis->addOption(vendor);
+
+    // Need to process SOLICIT again after requesting new option.
+    offer = srv.processDiscover(dis);
+    ASSERT_TRUE(offer);
+
+    // Check if thre is vendor option response
+    OptionPtr tmp = offer->getOption(DHO_VIVSO_SUBOPTIONS);
+    ASSERT_TRUE(tmp);
+
+    // The response should be OptionVendor object
+    boost::shared_ptr<OptionVendor> vendor_resp =
+        boost::dynamic_pointer_cast<OptionVendor>(tmp);
+    ASSERT_TRUE(vendor_resp);
+
+    OptionPtr docsis2 = vendor_resp->getOption(DOCSIS3_V4_TFTP_SERVERS);
+    ASSERT_TRUE(docsis2);
+
+    Option4AddrLstPtr tftp_srvs = boost::dynamic_pointer_cast<Option4AddrLst>(docsis2);
+    ASSERT_TRUE(tftp_srvs);
+
+    Option4AddrLst::AddressContainer addrs = tftp_srvs->getAddresses();
+    ASSERT_EQ(2, addrs.size());
+    EXPECT_EQ("192.0.2.1", addrs[0].toText());
+    EXPECT_EQ("192.0.2.2", addrs[1].toText());
+}
+
+// Test checks whether it is possible to use option definitions defined in
+// src/lib/dhcp/docsis3_option_defs.h.
+TEST_F(Dhcpv4SrvTest, vendorOptionsDocsisDefinitions) {
+    ConstElementPtr x;
+    string config_prefix = "{ \"interfaces\": [ \"all\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "    \"option-data\": [ {"
+        "          \"name\": \"tftp-servers\","
+        "          \"space\": \"vendor-4491\","
+        "          \"code\": ";
+    string config_postfix = ","
+        "          \"data\": \"192.0.2.1\","
+        "          \"csv-format\": True"
+        "        }],"
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.50\" ],"
+        "    \"subnet\": \"192.0.2.0/24\", "
+        "    \"renew-timer\": 1000, "
+        "    \"rebind-timer\": 1000, "
+        "    \"valid-lifetime\": 4000,"
+        "    \"interface\": \"\""
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    // There is docsis3 (vendor-id=4491) vendor option 2, which is a
+    // tftp-server. Its format is list of IPv4 addresses.
+    string config_valid = config_prefix + "2" + config_postfix;
+
+    // There is no option 99 defined in vendor-id=4491. As there is no
+    // definition, the config should fail.
+    string config_bogus = config_prefix + "99" + config_postfix;
+
+    ElementPtr json_bogus = Element::fromJSON(config_bogus);
+    ElementPtr json_valid = Element::fromJSON(config_valid);
+
+    NakedDhcpv4Srv srv(0);
+
+    // This should fail (missing option definition)
+    EXPECT_NO_THROW(x = configureDhcp4Server(srv, json_bogus));
+    ASSERT_TRUE(x);
+    comment_ = isc::config::parseAnswer(rcode_, x);
+    ASSERT_EQ(1, rcode_);
+
+    // This should work (option definition present)
+    EXPECT_NO_THROW(x = configureDhcp4Server(srv, json_valid));
+    ASSERT_TRUE(x);
+    comment_ = isc::config::parseAnswer(rcode_, x);
+    ASSERT_EQ(0, rcode_);
+}
+
+}
+
+    /*I}; // end of isc::dhcp::test namespace
+}; // end of isc::dhcp namespace
+}; // end of isc namespace */
diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.cc b/src/bin/dhcp4/tests/dhcp4_test_utils.cc
new file mode 100644
index 0000000..56e96f1
--- /dev/null
+++ b/src/bin/dhcp4/tests/dhcp4_test_utils.cc
@@ -0,0 +1,551 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <asiolink/io_address.h>
+#include <config/ccsession.h>
+#include <dhcp4/tests/dhcp4_test_utils.h>
+#include <dhcp/option4_addrlst.h>
+#include <dhcp/option_int_array.h>
+#include <dhcp/option_custom.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/lease.h>
+#include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+
+using namespace std;
+using namespace isc::asiolink;
+
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// dummy server-id file location
+static const char* SRVID_FILE = "server-id-test.txt";
+
+Dhcpv4SrvTest::Dhcpv4SrvTest()
+:rcode_(-1) {
+    subnet_ = Subnet4Ptr(new Subnet4(IOAddress("192.0.2.0"), 24, 1000,
+                                     2000, 3000));
+    pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"), IOAddress("192.0.2.110")));
+    subnet_->addPool(pool_);
+
+    CfgMgr::instance().deleteSubnets4();
+    CfgMgr::instance().addSubnet4(subnet_);
+
+    // Add Router option.
+    Option4AddrLstPtr opt_routers(new Option4AddrLst(DHO_ROUTERS));
+    opt_routers->setAddress(IOAddress("192.0.2.2"));
+    subnet_->addOption(opt_routers, false, "dhcp4");
+
+    // it's ok if that fails. There should not be such a file anyway
+    unlink(SRVID_FILE);
+
+    const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
+
+    // There must be some interface detected
+    if (ifaces.empty()) {
+        // We can't use ASSERT in constructor
+        ADD_FAILURE() << "No interfaces detected.";
+    }
+
+    valid_iface_ = ifaces.begin()->getName();
+}
+
+void Dhcpv4SrvTest::addPrlOption(Pkt4Ptr& pkt) {
+
+    OptionUint8ArrayPtr option_prl =
+        OptionUint8ArrayPtr(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+
+    // Let's request options that have been configured for the subnet.
+    option_prl->addValue(DHO_DOMAIN_NAME_SERVERS);
+    option_prl->addValue(DHO_DOMAIN_NAME);
+    option_prl->addValue(DHO_LOG_SERVERS);
+    option_prl->addValue(DHO_COOKIE_SERVERS);
+    // Let's also request the option that hasn't been configured. In such
+    // case server should ignore request for this particular option.
+    option_prl->addValue(DHO_LPR_SERVERS);
+    // And add 'Parameter Request List' option into the DISCOVER packet.
+    pkt->addOption(option_prl);
+}
+
+void Dhcpv4SrvTest::configureRequestedOptions() {
+    // dns-servers
+    Option4AddrLstPtr
+        option_dns_servers(new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS));
+    option_dns_servers->addAddress(IOAddress("192.0.2.1"));
+    option_dns_servers->addAddress(IOAddress("192.0.2.100"));
+    ASSERT_NO_THROW(subnet_->addOption(option_dns_servers, false, "dhcp4"));
+
+    // domain-name
+    OptionDefinition def("domain-name", DHO_DOMAIN_NAME, OPT_FQDN_TYPE);
+    OptionCustomPtr option_domain_name(new OptionCustom(def, Option::V4));
+    option_domain_name->writeFqdn("example.com");
+    subnet_->addOption(option_domain_name, false, "dhcp4");
+
+    // log-servers
+    Option4AddrLstPtr option_log_servers(new Option4AddrLst(DHO_LOG_SERVERS));
+    option_log_servers->addAddress(IOAddress("192.0.2.2"));
+    option_log_servers->addAddress(IOAddress("192.0.2.10"));
+    ASSERT_NO_THROW(subnet_->addOption(option_log_servers, false, "dhcp4"));
+
+    // cookie-servers
+    Option4AddrLstPtr option_cookie_servers(new Option4AddrLst(DHO_COOKIE_SERVERS));
+    option_cookie_servers->addAddress(IOAddress("192.0.2.1"));
+    ASSERT_NO_THROW(subnet_->addOption(option_cookie_servers, false, "dhcp4"));
+}
+
+void Dhcpv4SrvTest::messageCheck(const Pkt4Ptr& q, const Pkt4Ptr& a) {
+    ASSERT_TRUE(q);
+    ASSERT_TRUE(a);
+
+    EXPECT_EQ(q->getHops(),   a->getHops());
+    EXPECT_EQ(q->getIface(),  a->getIface());
+    EXPECT_EQ(q->getIndex(),  a->getIndex());
+    EXPECT_EQ(q->getGiaddr(), a->getGiaddr());
+    // When processing an incoming packet the remote address
+    // is copied as a src address, and the source address is
+    // copied as a remote address to the response.
+    EXPECT_TRUE(q->getLocalHWAddr() == a->getLocalHWAddr());
+    EXPECT_TRUE(q->getRemoteHWAddr() == a->getRemoteHWAddr());
+
+    // Check that the server identifier is present in the response.
+    // Presence (or absence) of other options is checked elsewhere.
+    EXPECT_TRUE(a->getOption(DHO_DHCP_SERVER_IDENTIFIER));
+
+    // Check that something is offered
+    EXPECT_TRUE(a->getYiaddr().toText() != "0.0.0.0");
+}
+
+::testing::AssertionResult
+Dhcpv4SrvTest::basicOptionsPresent(const Pkt4Ptr& pkt) {
+    std::ostringstream errmsg;
+    errmsg << "option missing in the response";
+    if (!pkt->getOption(DHO_DOMAIN_NAME)) {
+        return (::testing::AssertionFailure(::testing::Message()
+                                            << "domain-name " << errmsg));
+
+    } else if (!pkt->getOption(DHO_DOMAIN_NAME_SERVERS)) {
+        return (::testing::AssertionFailure(::testing::Message()
+                                            << "dns-servers " << errmsg));
+
+    } else if (!pkt->getOption(DHO_SUBNET_MASK)) {
+        return (::testing::AssertionFailure(::testing::Message()
+                                            << "subnet-mask " << errmsg));
+
+    } else if (!pkt->getOption(DHO_ROUTERS)) {
+        return (::testing::AssertionFailure(::testing::Message() << "routers "
+                                            << errmsg));
+
+    } else if (!pkt->getOption(DHO_DHCP_LEASE_TIME)) {
+        return (::testing::AssertionFailure(::testing::Message() <<
+                                            "dhcp-lease-time " << errmsg));
+
+    }
+    return (::testing::AssertionSuccess());
+
+}
+
+::testing::AssertionResult
+Dhcpv4SrvTest::noBasicOptions(const Pkt4Ptr& pkt) {
+    std::ostringstream errmsg;
+    errmsg << "option present in the response";
+    if (pkt->getOption(DHO_DOMAIN_NAME)) {
+        return (::testing::AssertionFailure(::testing::Message()
+                                            << "domain-name " << errmsg));
+
+    } else if (pkt->getOption(DHO_DOMAIN_NAME_SERVERS)) {
+        return (::testing::AssertionFailure(::testing::Message()
+                                            << "dns-servers " << errmsg));
+
+    } else if (pkt->getOption(DHO_SUBNET_MASK)) {
+        return (::testing::AssertionFailure(::testing::Message()
+                                            << "subnet-mask " << errmsg));
+
+    } else if (pkt->getOption(DHO_ROUTERS)) {
+        return (::testing::AssertionFailure(::testing::Message() << "routers "
+                                            << errmsg));
+
+    } else if (pkt->getOption(DHO_DHCP_LEASE_TIME)) {
+        return (::testing::AssertionFailure(::testing::Message()
+                                            << "dhcp-lease-time " << errmsg));
+
+    }
+    return (::testing::AssertionSuccess());
+}
+
+::testing::AssertionResult
+Dhcpv4SrvTest::requestedOptionsPresent(const Pkt4Ptr& pkt) {
+    std::ostringstream errmsg;
+    errmsg << "option missing in the response";
+    if (!pkt->getOption(DHO_LOG_SERVERS)) {
+        return (::testing::AssertionFailure(::testing::Message()
+                                            << "log-servers " << errmsg));
+
+    } else if (!pkt->getOption(DHO_COOKIE_SERVERS)) {
+        return (::testing::AssertionFailure(::testing::Message()
+                                            << "cookie-servers " << errmsg));
+
+    }
+    return (::testing::AssertionSuccess());
+}
+
+::testing::AssertionResult
+Dhcpv4SrvTest::noRequestedOptions(const Pkt4Ptr& pkt) {
+    std::ostringstream errmsg;
+    errmsg << "option present in the response";
+    if (pkt->getOption(DHO_LOG_SERVERS)) {
+        return (::testing::AssertionFailure(::testing::Message()
+                                            << "log-servers " << errmsg));
+
+    } else if (pkt->getOption(DHO_COOKIE_SERVERS)) {
+        return (::testing::AssertionFailure(::testing::Message()
+                                            << "cookie-servers " << errmsg));
+
+    }
+    return (::testing::AssertionSuccess());
+}
+
+OptionPtr Dhcpv4SrvTest::generateClientId(size_t size /*= 4*/) {
+
+    OptionBuffer clnt_id(size);
+    for (int i = 0; i < size; i++) {
+        clnt_id[i] = 100 + i;
+    }
+
+    client_id_ = ClientIdPtr(new ClientId(clnt_id));
+
+    return (OptionPtr(new Option(Option::V4, DHO_DHCP_CLIENT_IDENTIFIER,
+                                 clnt_id.begin(),
+                                 clnt_id.begin() + size)));
+}
+
+HWAddrPtr Dhcpv4SrvTest::generateHWAddr(size_t size /*= 6*/) {
+    const uint8_t hw_type = 123; // Just a fake number (typically 6=HTYPE_ETHER, see dhcp4.h)
+    OptionBuffer mac(size);
+    for (int i = 0; i < size; ++i) {
+        mac[i] = 50 + i;
+    }
+    return (HWAddrPtr(new HWAddr(mac, hw_type)));
+}
+
+void Dhcpv4SrvTest::checkAddressParams(const Pkt4Ptr& rsp, const SubnetPtr subnet,
+                                       bool t1_mandatory /*= false*/,
+                                       bool t2_mandatory /*= false*/) {
+
+    // Technically inPool implies inRange, but let's be on the safe
+    // side and check both.
+    EXPECT_TRUE(subnet->inRange(rsp->getYiaddr()));
+    EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, rsp->getYiaddr()));
+
+    // Check lease time
+    OptionPtr opt = rsp->getOption(DHO_DHCP_LEASE_TIME);
+    if (!opt) {
+        ADD_FAILURE() << "Lease time option missing in response";
+    } else {
+        EXPECT_EQ(opt->getUint32(), subnet->getValid());
+    }
+
+    // Check T1 timer
+    opt = rsp->getOption(DHO_DHCP_RENEWAL_TIME);
+    if (opt) {
+        EXPECT_EQ(opt->getUint32(), subnet->getT1());
+    } else {
+        if (t1_mandatory) {
+            ADD_FAILURE() << "Required T1 option missing";
+        }
+    }
+
+    // Check T2 timer
+    opt = rsp->getOption(DHO_DHCP_REBINDING_TIME);
+    if (opt) {
+        EXPECT_EQ(opt->getUint32(), subnet->getT2());
+    } else {
+        if (t2_mandatory) {
+            ADD_FAILURE() << "Required T2 option missing";
+        }
+    }
+}
+
+void Dhcpv4SrvTest::checkResponse(const Pkt4Ptr& rsp, uint8_t expected_message_type,
+                                  uint32_t expected_transid) {
+    ASSERT_TRUE(rsp);
+    EXPECT_EQ(expected_message_type, rsp->getType());
+    EXPECT_EQ(expected_transid, rsp->getTransid());
+}
+
+Lease4Ptr Dhcpv4SrvTest::checkLease(const Pkt4Ptr& rsp,
+                                    const OptionPtr& client_id,
+                                    const HWAddrPtr&,
+                                    const IOAddress& expected_addr) {
+
+    ClientIdPtr id;
+    if (client_id) {
+        OptionBuffer data = client_id->getData();
+        id.reset(new ClientId(data));
+    }
+
+    Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(expected_addr);
+    if (!lease) {
+        cout << "Lease for " << expected_addr.toText()
+             << " not found in the database backend.";
+        return (Lease4Ptr());
+    }
+
+    EXPECT_EQ(rsp->getYiaddr().toText(), expected_addr.toText());
+
+    EXPECT_EQ(expected_addr.toText(), lease->addr_.toText());
+    if (client_id) {
+        EXPECT_TRUE(*lease->client_id_ == *id);
+    }
+    EXPECT_EQ(subnet_->getID(), lease->subnet_id_);
+
+    return (lease);
+}
+
+/// @brief Checks if server response (OFFER, ACK, NAK) includes proper server-id
+/// @param rsp response packet to be validated
+/// @param expected_srvid expected value of server-id
+void Dhcpv4SrvTest::checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid) {
+    // Check that server included its server-id
+    OptionPtr opt = rsp->getOption(DHO_DHCP_SERVER_IDENTIFIER);
+    ASSERT_TRUE(opt);
+    EXPECT_EQ(opt->getType(), expected_srvid->getType() );
+    EXPECT_EQ(opt->len(), expected_srvid->len() );
+    EXPECT_TRUE(opt->getData() == expected_srvid->getData());
+}
+
+void Dhcpv4SrvTest::checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid) {
+    // check that server included our own client-id
+    OptionPtr opt = rsp->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
+    ASSERT_TRUE(opt);
+    EXPECT_EQ(expected_clientid->getType(), opt->getType());
+    EXPECT_EQ(expected_clientid->len(), opt->len());
+    EXPECT_TRUE(expected_clientid->getData() == opt->getData());
+}
+
+::testing::AssertionResult
+Dhcpv4SrvTest::createPacketFromBuffer(const Pkt4Ptr& src_pkt,
+                                      Pkt4Ptr& dst_pkt) {
+    // Create on-wire format of the packet. If pack() has been called
+    // on this instance of the packet already, the next call to pack()
+    // should remove all contents of the output buffer.
+    try {
+        src_pkt->pack();
+    } catch (const Exception& ex) {
+        return (::testing::AssertionFailure(::testing::Message()
+                                            << "Failed to parse source packet: "
+                                            << ex.what()));
+    }
+    // Get the output buffer from the source packet.
+    const util::OutputBuffer& buf = src_pkt->getBuffer();
+    // Create a copy of the packet using the output buffer from the source
+    // packet.
+    try {
+        dst_pkt.reset(new Pkt4(static_cast<const uint8_t*>(buf.getData()),
+                               buf.getLength()));
+    } catch (const Exception& ex) {
+        return (::testing::AssertionFailure(::testing::Message()
+                                            << "Failed to create a"
+                                            " destination packet from"
+                                            " the buffer: "
+                                            << ex.what()));
+    }
+
+    try {
+        // Parse the new packet and return to the caller.
+        dst_pkt->unpack();
+    } catch (const Exception& ex) {
+        return (::testing::AssertionFailure(::testing::Message()
+                                            << "Failed to parse a"
+                                            << " destination packet: "
+                                            << ex.what()));
+    }
+
+    return (::testing::AssertionSuccess());
+}
+
+
+void Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
+    // Create an instance of the tested class.
+    boost::scoped_ptr<NakedDhcpv4Srv> srv(new NakedDhcpv4Srv(0));
+
+    // Initialize the source HW address.
+    vector<uint8_t> mac(6);
+    for (int i = 0; i < 6; ++i) {
+        mac[i] = i * 10;
+    }
+    // Initialized the destination HW address.
+    vector<uint8_t> dst_mac(6);
+    for (int i = 0; i < 6; ++i) {
+        dst_mac[i] = i * 20;
+    }
+    // Create a DHCP message. It will be used to simulate the
+    // incoming message.
+    boost::shared_ptr<Pkt4> req(new Pkt4(msg_type, 1234));
+    // Create a response message. It will hold a reponse packet.
+    // Initially, set it to NULL.
+    boost::shared_ptr<Pkt4> rsp;
+    // Set the name of the interface on which packet is received.
+    req->setIface("eth0");
+    // Set the interface index. It is just a dummy value and will
+    // not be interpreted.
+    req->setIndex(17);
+    // Set the target HW address. This value is normally used to
+    // construct the data link layer header.
+    req->setRemoteHWAddr(1, 6, dst_mac);
+    // Set the HW address. This value is set on DHCP level (in chaddr).
+    req->setHWAddr(1, 6, mac);
+    // Set local HW address. It is used to construct the data link layer
+    // header.
+    req->setLocalHWAddr(1, 6, mac);
+    // Set target IP address.
+    req->setRemoteAddr(IOAddress("192.0.2.55"));
+    // Set relay address.
+    req->setGiaddr(IOAddress("192.0.2.10"));
+
+    // We are going to test that certain options are returned
+    // in the response message when requested using 'Parameter
+    // Request List' option. Let's configure those options that
+    // are returned when requested.
+    configureRequestedOptions();
+
+    // Create a copy of the original packet by parsing its wire format.
+    // This simulates the real life scenario when we process the packet
+    // which was parsed from its wire format.
+    Pkt4Ptr received;
+    ASSERT_TRUE(createPacketFromBuffer(req, received));
+    if (msg_type == DHCPDISCOVER) {
+        ASSERT_NO_THROW(
+            rsp = srv->processDiscover(received);
+        );
+
+        // Should return OFFER
+        ASSERT_TRUE(rsp);
+        EXPECT_EQ(DHCPOFFER, rsp->getType());
+
+    } else {
+        ASSERT_NO_THROW(rsp = srv->processRequest(received));
+
+        // Should return ACK
+        ASSERT_TRUE(rsp);
+        EXPECT_EQ(DHCPACK, rsp->getType());
+
+    }
+
+    messageCheck(received, rsp);
+
+    // Basic options should be present when we got the lease.
+    EXPECT_TRUE(basicOptionsPresent(rsp));
+    // We did not request any options so these should not be present
+    // in the RSP.
+    EXPECT_TRUE(noRequestedOptions(rsp));
+
+    // Repeat the test but request some options.
+    // Add 'Parameter Request List' option.
+    addPrlOption(req);
+
+    ASSERT_TRUE(createPacketFromBuffer(req, received));
+    ASSERT_TRUE(received->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
+
+    if (msg_type == DHCPDISCOVER) {
+        ASSERT_NO_THROW(rsp = srv->processDiscover(received));
+
+        // Should return non-NULL packet.
+        ASSERT_TRUE(rsp);
+        EXPECT_EQ(DHCPOFFER, rsp->getType());
+
+    } else {
+        ASSERT_NO_THROW(rsp = srv->processRequest(received));
+
+        // Should return non-NULL packet.
+        ASSERT_TRUE(rsp);
+        EXPECT_EQ(DHCPACK, rsp->getType());
+    }
+
+    // Check that the requested options are returned.
+    EXPECT_TRUE(basicOptionsPresent(rsp));
+    EXPECT_TRUE(requestedOptionsPresent(rsp));
+
+    // The following part of the test will test that the NAK is sent when
+    // there is no address pool configured. In the same time, we expect
+    // that the requested options are not included in NAK message, but that
+    // they are only included when yiaddr is set to non-zero value.
+    ASSERT_NO_THROW(subnet_->delPools(Lease::TYPE_V4));
+
+    // There has been a lease allocated for the particular client. So,
+    // even though we deleted the subnet, the client would get the
+    // existing lease (not a NAK). Therefore, we have to change the chaddr
+    // in the packet so as the existing lease is not returned.
+    req->setHWAddr(1, 6, std::vector<uint8_t>(2, 6));
+    ASSERT_TRUE(createPacketFromBuffer(req, received));
+    ASSERT_TRUE(received->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
+
+    if (msg_type == DHCPDISCOVER) {
+        ASSERT_NO_THROW(rsp = srv->processDiscover(received));
+        // Should return non-NULL packet.
+        ASSERT_TRUE(rsp);
+    } else {
+        ASSERT_NO_THROW(rsp = srv->processRequest(received));
+        // Should return non-NULL packet.
+        ASSERT_TRUE(rsp);
+    }
+    // We should get the NAK packet with yiaddr set to 0.
+    EXPECT_EQ(DHCPNAK, rsp->getType());
+    ASSERT_EQ("0.0.0.0", rsp->getYiaddr().toText());
+
+    // Make sure that none of the requested options is returned in NAK.
+    // Also options such as Routers or Subnet Mask should not be there,
+    // because lease hasn't been acquired.
+    EXPECT_TRUE(noRequestedOptions(rsp));
+    EXPECT_TRUE(noBasicOptions(rsp));
+}
+
+/// @brief This function cleans up after the test.
+void Dhcpv4SrvTest::TearDown() {
+
+    CfgMgr::instance().deleteSubnets4();
+
+    // Let's clean up if there is such a file.
+    unlink(SRVID_FILE);
+
+    // Close all open sockets.
+    IfaceMgr::instance().closeSockets();
+
+    // Some unit tests override the default packet filtering class, used
+    // by the IfaceMgr. The dummy class, called PktFilterTest, reports the
+    // capability to directly respond to the clients without IP address
+    // assigned. This capability is not supported by the default packet
+    // filtering class: PktFilterInet. Therefore setting the dummy class
+    // allows to test scenarios, when server responds to the broadcast address
+    // on client's request, despite having support for direct response.
+    // The following call restores the use of original packet filtering class
+    // after the test.
+    try {
+        IfaceMgr::instance().setPacketFilter(PktFilterPtr(new PktFilterInet()));
+
+    } catch (const Exception& ex) {
+        FAIL() << "Failed to restore the default (PktFilterInet) packet filtering"
+               << " class after the test. Exception has been caught: "
+               << ex.what();
+    }
+}
+
+}; // end of isc::dhcp::test namespace
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h
index 75ec22a..3e9a3b8 100644
--- a/src/bin/dhcp4/tests/dhcp4_test_utils.h
+++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h
@@ -12,138 +12,28 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <config.h>
-#include <sstream>
+/// @file   dhcp4_test_utils.h
+///
+/// @brief  This file contains utility classes used for DHCPv4 server testing
 
-#include <asiolink/io_address.h>
-#include <dhcp/dhcp4.h>
-#include <dhcp/iface_mgr.h>
-#include <dhcp/option.h>
-#include <dhcp/option4_addrlst.h>
-#include <dhcp/option_custom.h>
-#include <dhcp/option_int_array.h>
+#ifndef DHCP4_TEST_UTILS_H
+#define DHCP4_TEST_UTILS_H
+
+#include <gtest/gtest.h>
+#include <dhcp/pkt4.h>
 #include <dhcp/pkt_filter.h>
 #include <dhcp/pkt_filter_inet.h>
-#include <dhcp4/config_parser.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/lease.h>
 #include <dhcp4/dhcp4_srv.h>
-#include <dhcpsrv/cfgmgr.h>
-#include <dhcpsrv/lease_mgr.h>
-#include <dhcpsrv/lease_mgr_factory.h>
-#include <gtest/gtest.h>
-
-#include <boost/scoped_ptr.hpp>
-
-#include <fstream>
-#include <iostream>
-
-#include <arpa/inet.h>
+#include <asiolink/io_address.h>
+#include <config/ccsession.h>
+#include <list>
 
 namespace isc {
+namespace dhcp {
 namespace test {
 
-using namespace isc;
-using namespace isc::dhcp;
-
-class NakedDhcpv4Srv: public Dhcpv4Srv {
-    // "Naked" DHCPv4 server, exposes internal fields
-public:
-
-    /// @brief Constructor.
-    ///
-    /// This constructor disables default modes of operation used by the
-    /// Dhcpv4Srv class:
-    /// - Send/receive broadcast messages through sockets on interfaces
-    /// which support broadcast traffic.
-    /// - Direct DHCPv4 traffic - communication with clients which do not
-    /// have IP address assigned yet.
-    ///
-    /// Enabling these modes requires root privilges so they must be
-    /// disabled for unit testing.
-    ///
-    /// Note, that disabling broadcast options on sockets does not impact
-    /// the operation of these tests because they use local loopback
-    /// interface which doesn't have broadcast capability anyway. It rather
-    /// prevents setting broadcast options on other (broadcast capable)
-    /// sockets which are opened on other interfaces in Dhcpv4Srv constructor.
-    ///
-    /// The Direct DHCPv4 Traffic capability can be disabled here because
-    /// it is tested with PktFilterLPFTest unittest. The tests which belong
-    /// to PktFilterLPFTest can be enabled on demand when root privileges can
-    /// be guaranteed.
-    ///
-    /// @param port port number to listen on; the default value 0 indicates
-    /// that sockets should not be opened.
-    NakedDhcpv4Srv(uint16_t port = 0)
-        : Dhcpv4Srv(port, "type=memfile", false, false) {
-    }
-
-    /// @brief fakes packet reception
-    /// @param timeout ignored
-    ///
-    /// The method receives all packets queued in receive queue, one after
-    /// another. Once the queue is empty, it initiates the shutdown procedure.
-    ///
-    /// See fake_received_ field for description
-    virtual Pkt4Ptr receivePacket(int /*timeout*/) {
-
-        // If there is anything prepared as fake incoming traffic, use it
-        if (!fake_received_.empty()) {
-            Pkt4Ptr pkt = fake_received_.front();
-            fake_received_.pop_front();
-            return (pkt);
-        }
-
-        // If not, just trigger shutdown and return immediately
-        shutdown();
-        return (Pkt4Ptr());
-    }
-
-    /// @brief fake packet sending
-    ///
-    /// Pretend to send a packet, but instead just store it in fake_send_ list
-    /// where test can later inspect server's response.
-    virtual void sendPacket(const Pkt4Ptr& pkt) {
-        fake_sent_.push_back(pkt);
-    }
-
-    /// @brief adds a packet to fake receive queue
-    ///
-    /// See fake_received_ field for description
-    void fakeReceive(const Pkt4Ptr& pkt) {
-        fake_received_.push_back(pkt);
-    }
-
-    virtual ~NakedDhcpv4Srv() {
-    }
-
-    /// @brief packets we pretend to receive
-    ///
-    /// Instead of setting up sockets on interfaces that change between OSes, it
-    /// is much easier to fake packet reception. This is a list of packets that
-    /// we pretend to have received. You can schedule new packets to be received
-    /// using fakeReceive() and NakedDhcpv4Srv::receivePacket() methods.
-    std::list<Pkt4Ptr> fake_received_;
-
-    std::list<Pkt4Ptr> fake_sent_;
-
-    using Dhcpv4Srv::adjustRemoteAddr;
-    using Dhcpv4Srv::computeDhcid;
-    using Dhcpv4Srv::createNameChangeRequests;
-    using Dhcpv4Srv::processDiscover;
-    using Dhcpv4Srv::processRequest;
-    using Dhcpv4Srv::processRelease;
-    using Dhcpv4Srv::processDecline;
-    using Dhcpv4Srv::processInform;
-    using Dhcpv4Srv::processClientName;
-    using Dhcpv4Srv::getServerID;
-    using Dhcpv4Srv::loadServerID;
-    using Dhcpv4Srv::generateServerID;
-    using Dhcpv4Srv::writeServerID;
-    using Dhcpv4Srv::sanityCheck;
-    using Dhcpv4Srv::srvidToString;
-    using Dhcpv4Srv::name_change_reqs_;
-};
-
 /// @brief Dummy Packet Filtering class.
 ///
 /// This class reports capability to respond directly to the client which
@@ -179,8 +69,6 @@ public:
 
 };
 
-static const char* SRVID_FILE = "server-id-test.txt";
-
 class Dhcpv4SrvTest : public ::testing::Test {
 public:
 
@@ -188,43 +76,15 @@ public:
     ///
     /// Initializes common objects used in many tests.
     /// Also sets up initial configuration in CfgMgr.
-    Dhcpv4SrvTest() :
-        rcode_(-1)
-    {
-        subnet_ = Subnet4Ptr(new Subnet4(isc::asiolink::IOAddress("192.0.2.0"),
-                                         24, 1000, 2000, 3000));
-        pool_ = Pool4Ptr(new Pool4(isc::asiolink::IOAddress("192.0.2.100"),
-                                   isc::asiolink::IOAddress("192.0.2.110")));
-        subnet_->addPool(pool_);
-
-        CfgMgr::instance().deleteSubnets4();
-        CfgMgr::instance().addSubnet4(subnet_);
-
-        // Add Router option.
-        Option4AddrLstPtr opt_routers(new Option4AddrLst(DHO_ROUTERS));
-        opt_routers->setAddress(isc::asiolink::IOAddress("192.0.2.2"));
-        subnet_->addOption(opt_routers, false, "dhcp4");
-
-        // it's ok if that fails. There should not be such a file anyway
-        unlink(SRVID_FILE);
-
-        const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
-
-        // There must be some interface detected
-        if (ifaces.empty()) {
-            // We can't use ASSERT in constructor
-            ADD_FAILURE() << "No interfaces detected.";
-        }
-
-        valid_iface_ = ifaces.begin()->getName();
-    }
+    Dhcpv4SrvTest();
 
+    /// @brief destructor
     virtual ~Dhcpv4SrvTest() {
     }
 
     /// @brief Add 'Parameter Request List' option to the packet.
     ///
-    /// This function PRL option comprising the following option codes:
+    /// This function adds PRL option comprising the following option codes:
     /// - 5 - Name Server
     /// - 15 - Domain Name
     /// - 7 - Log Server
@@ -232,23 +92,7 @@ public:
     /// - 9 - LPR Server
     ///
     /// @param pkt packet to add PRL option to.
-    void addPrlOption(Pkt4Ptr& pkt) {
-
-        OptionUint8ArrayPtr option_prl =
-            OptionUint8ArrayPtr(new OptionUint8Array(Option::V4,
-                                                     DHO_DHCP_PARAMETER_REQUEST_LIST));
-
-        // Let's request options that have been configured for the subnet.
-        option_prl->addValue(DHO_DOMAIN_NAME_SERVERS);
-        option_prl->addValue(DHO_DOMAIN_NAME);
-        option_prl->addValue(DHO_LOG_SERVERS);
-        option_prl->addValue(DHO_COOKIE_SERVERS);
-        // Let's also request the option that hasn't been configured. In such
-        // case server should ignore request for this particular option.
-        option_prl->addValue(DHO_LPR_SERVERS);
-        // And add 'Parameter Request List' option into the DISCOVER packet.
-        pkt->addOption(option_prl);
-    }
+    void addPrlOption(Pkt4Ptr& pkt);
 
     /// @brief Configures options being requested in the PRL option.
     ///
@@ -258,85 +102,39 @@ public:
     /// the server should not return it in its response. The goal
     /// of not configuring the requested option is to verify that
     /// the server will not return it.
-    void configureRequestedOptions() {
-        // dns-servers
-        Option4AddrLstPtr
-            option_dns_servers(new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS));
-        option_dns_servers->addAddress(isc::asiolink::IOAddress("192.0.2.1"));
-        option_dns_servers->addAddress(isc::asiolink::IOAddress("192.0.2.100"));
-        ASSERT_NO_THROW(subnet_->addOption(option_dns_servers, false, "dhcp4"));
-
-        // domain-name
-        OptionDefinition def("domain-name", DHO_DOMAIN_NAME, OPT_FQDN_TYPE);
-        OptionCustomPtr option_domain_name(new OptionCustom(def, Option::V4));
-        option_domain_name->writeFqdn("example.com");
-        subnet_->addOption(option_domain_name, false, "dhcp4");
-
-        // log-servers
-        Option4AddrLstPtr option_log_servers(new Option4AddrLst(DHO_LOG_SERVERS));
-        option_log_servers->addAddress(isc::asiolink::IOAddress("192.0.2.2"));
-        option_log_servers->addAddress(isc::asiolink::IOAddress("192.0.2.10"));
-        ASSERT_NO_THROW(subnet_->addOption(option_log_servers, false, "dhcp4"));
-
-        // cookie-servers
-        Option4AddrLstPtr option_cookie_servers(new Option4AddrLst(DHO_COOKIE_SERVERS));
-        option_cookie_servers->addAddress(isc::asiolink::IOAddress("192.0.2.1"));
-        ASSERT_NO_THROW(subnet_->addOption(option_cookie_servers, false,
-                                           "dhcp4"));
-    }
+    void configureRequestedOptions();
 
     /// @brief checks that the response matches request
     /// @param q query (client's message)
     /// @param a answer (server's message)
-    void messageCheck(const Pkt4Ptr& q, const Pkt4Ptr& a) {
-        ASSERT_TRUE(q);
-        ASSERT_TRUE(a);
-
-        EXPECT_EQ(q->getHops(),   a->getHops());
-        EXPECT_EQ(q->getIface(),  a->getIface());
-        EXPECT_EQ(q->getIndex(),  a->getIndex());
-        EXPECT_EQ(q->getGiaddr(), a->getGiaddr());
-        // When processing an incoming packet the remote address
-        // is copied as a src address, and the source address is
-        // copied as a remote address to the response.
-        EXPECT_TRUE(q->getLocalHWAddr() == a->getLocalHWAddr());
-        EXPECT_TRUE(q->getRemoteHWAddr() == a->getRemoteHWAddr());
-
-        // Check that bare minimum of required options are there.
-        // We don't check options requested by a client. Those
-        // are checked elsewhere.
-        EXPECT_TRUE(a->getOption(DHO_SUBNET_MASK));
-        EXPECT_TRUE(a->getOption(DHO_ROUTERS));
-        EXPECT_TRUE(a->getOption(DHO_DHCP_SERVER_IDENTIFIER));
-        EXPECT_TRUE(a->getOption(DHO_DHCP_LEASE_TIME));
-        EXPECT_TRUE(a->getOption(DHO_SUBNET_MASK));
-        EXPECT_TRUE(a->getOption(DHO_DOMAIN_NAME));
-        EXPECT_TRUE(a->getOption(DHO_DOMAIN_NAME_SERVERS));
-
-        // Check that something is offered
-        EXPECT_TRUE(a->getYiaddr().toText() != "0.0.0.0");
-    }
+    void messageCheck(const Pkt4Ptr& q, const Pkt4Ptr& a);
 
-    /// @brief Check that requested options are present.
-    ///
-    /// @param pkt packet to be checked.
-    void optionsCheck(const Pkt4Ptr& pkt) {
-        // Check that the requested and configured options are returned
-        // in the ACK message.
-        EXPECT_TRUE(pkt->getOption(DHO_DOMAIN_NAME))
-            << "domain-name not present in the response";
-        EXPECT_TRUE(pkt->getOption(DHO_DOMAIN_NAME_SERVERS))
-            << "dns-servers not present in the response";
-        EXPECT_TRUE(pkt->getOption(DHO_LOG_SERVERS))
-            << "log-servers not present in the response";
-        EXPECT_TRUE(pkt->getOption(DHO_COOKIE_SERVERS))
-            << "cookie-servers not present in the response";
-        // Check that the requested but not configured options are not
-        // returned in the ACK message.
-        EXPECT_FALSE(pkt->getOption(DHO_LPR_SERVERS))
-            << "domain-name present in the response but it is"
-            << " expected not to be present";
-    }
+    /// @brief Check that certain basic (always added when lease is acquired)
+    /// are present in a message.
+    ///
+    /// @param pkt A message to be checked.
+    /// @return Assertion result which indicates whether test passed or failed.
+    ::testing::AssertionResult basicOptionsPresent(const Pkt4Ptr& pkt);
+
+    /// @brief Check that certain basic (always added when lease is acquired)
+    /// are not present.
+    ///
+    /// @param pkt A packet to be checked.
+    /// @return Assertion result which indicates whether test passed or failed.
+    ::testing::AssertionResult noBasicOptions(const Pkt4Ptr& pkt);
+
+    /// @brief Check that certain requested options are present in the message.
+    ///
+    /// @param pkt A message to be checked.
+    /// @return Assertion result which indicates whether test passed or failed.
+    ::testing::AssertionResult requestedOptionsPresent(const Pkt4Ptr& pkt);
+
+    /// @brief Check that certain options (requested with PRL option)
+    /// are not present.
+    ///
+    /// @param pkt A packet to be checked.
+    /// @return Assertion result which indicates whether test passed or failed.
+    ::testing::AssertionResult noRequestedOptions(const Pkt4Ptr& pkt);
 
     /// @brief generates client-id option
     ///
@@ -346,32 +144,13 @@ public:
     /// tests generate client-ids on their own.
     /// Sets client_id_ field.
     /// @param size size of the client-id to be generated
-    OptionPtr generateClientId(size_t size = 4) {
-
-        OptionBuffer clnt_id(size);
-        for (int i = 0; i < size; i++) {
-            clnt_id[i] = 100 + i;
-        }
-
-        client_id_ = ClientIdPtr(new ClientId(clnt_id));
-
-        return (OptionPtr(new Option(Option::V4, DHO_DHCP_CLIENT_IDENTIFIER,
-                                     clnt_id.begin(),
-                                     clnt_id.begin() + size)));
-    }
+    OptionPtr generateClientId(size_t size = 4);
 
     /// @brief generate hardware address
     ///
     /// @param size size of the generated MAC address
     /// @param pointer to Hardware Address object
-    HWAddrPtr generateHWAddr(size_t size = 6) {
-        const uint8_t hw_type = 123; // Just a fake number (typically 6=HTYPE_ETHER, see dhcp4.h)
-        OptionBuffer mac(size);
-        for (int i = 0; i < size; ++i) {
-            mac[i] = 50 + i;
-        }
-        return (HWAddrPtr(new HWAddr(mac, hw_type)));
-    }
+    HWAddrPtr generateHWAddr(size_t size = 6);
 
     /// Check that address was returned from proper range, that its lease
     /// lifetime is correct, that T1 and T2 are returned properly
@@ -382,41 +161,7 @@ public:
     /// @param t2_mandatory is T2 mandatory?
     void checkAddressParams(const Pkt4Ptr& rsp, const SubnetPtr subnet,
                             bool t1_mandatory = false,
-                            bool t2_mandatory = false) {
-
-        // Technically inPool implies inRange, but let's be on the safe
-        // side and check both.
-        EXPECT_TRUE(subnet->inRange(rsp->getYiaddr()));
-        EXPECT_TRUE(subnet->inPool(rsp->getYiaddr()));
-
-        // Check lease time
-        OptionPtr opt = rsp->getOption(DHO_DHCP_LEASE_TIME);
-        if (!opt) {
-            ADD_FAILURE() << "Lease time option missing in response";
-        } else {
-            EXPECT_EQ(opt->getUint32(), subnet->getValid());
-        }
-
-        // Check T1 timer
-        opt = rsp->getOption(DHO_DHCP_RENEWAL_TIME);
-        if (opt) {
-            EXPECT_EQ(opt->getUint32(), subnet->getT1());
-        } else {
-            if (t1_mandatory) {
-                ADD_FAILURE() << "Required T1 option missing";
-            }
-        }
-
-        // Check T2 timer
-        opt = rsp->getOption(DHO_DHCP_REBINDING_TIME);
-        if (opt) {
-            EXPECT_EQ(opt->getUint32(), subnet->getT2());
-        } else {
-            if (t2_mandatory) {
-                ADD_FAILURE() << "Required T2 option missing";
-            }
-        }
-    }
+                            bool t2_mandatory = false);
 
     /// @brief Basic checks for generated response (message type and trans-id).
     ///
@@ -424,11 +169,7 @@ public:
     /// @param expected_message_type expected message type
     /// @param expected_transid expected transaction-id
     void checkResponse(const Pkt4Ptr& rsp, uint8_t expected_message_type,
-                       uint32_t expected_transid) {
-        ASSERT_TRUE(rsp);
-        EXPECT_EQ(expected_message_type, rsp->getType());
-        EXPECT_EQ(expected_transid, rsp->getTransid());
-    }
+                       uint32_t expected_transid);
 
     /// @brief Checks if the lease sent to client is present in the database
     ///
@@ -438,189 +179,78 @@ public:
     /// @param expected_addr expected address
     Lease4Ptr checkLease(const Pkt4Ptr& rsp, const OptionPtr& client_id,
                          const HWAddrPtr&,
-                         const isc::asiolink::IOAddress& expected_addr) {
-
-        ClientIdPtr id;
-        if (client_id) {
-            OptionBuffer data = client_id->getData();
-            id.reset(new ClientId(data));
-        }
-
-        Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(expected_addr);
-        if (!lease) {
-            std::cout << "Lease for " << expected_addr.toText()
-                      << " not found in the database backend.";
-            return (Lease4Ptr());
-        }
-
-        EXPECT_EQ(rsp->getYiaddr().toText(), expected_addr.toText());
-
-        EXPECT_EQ(expected_addr.toText(), lease->addr_.toText());
-        if (client_id) {
-            EXPECT_TRUE(*lease->client_id_ == *id);
-        }
-        EXPECT_EQ(subnet_->getID(), lease->subnet_id_);
-
-        return (lease);
-    }
+                         const isc::asiolink::IOAddress& expected_addr);
 
     /// @brief Checks if server response (OFFER, ACK, NAK) includes proper server-id
     /// @param rsp response packet to be validated
     /// @param expected_srvid expected value of server-id
-    void checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid) {
-        // Check that server included its server-id
-        OptionPtr opt = rsp->getOption(DHO_DHCP_SERVER_IDENTIFIER);
-        ASSERT_TRUE(opt);
-        EXPECT_EQ(opt->getType(), expected_srvid->getType() );
-        EXPECT_EQ(opt->len(), expected_srvid->len() );
-        EXPECT_TRUE(opt->getData() == expected_srvid->getData());
-    }
+    void checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid);
 
     /// @brief Checks if server response (OFFER, ACK, NAK) includes proper client-id
     /// @param rsp response packet to be validated
     /// @param expected_clientid expected value of client-id
-    void checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid) {
-        // check that server included our own client-id
-        OptionPtr opt = rsp->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
-        ASSERT_TRUE(opt);
-        EXPECT_EQ(expected_clientid->getType(), opt->getType());
-        EXPECT_EQ(expected_clientid->len(), opt->len());
-        EXPECT_TRUE(expected_clientid->getData() == opt->getData());
-    }
+    void checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid);
 
-    /// @brief Tests if Discover or Request message is processed correctly
+    /// @brief sets default fields in a captured packet
     ///
-    /// @param msg_type DHCPDISCOVER or DHCPREQUEST
-    void testDiscoverRequest(const uint8_t msg_type) {
-        // Create an instance of the tested class.
-        boost::scoped_ptr<NakedDhcpv4Srv> srv(new NakedDhcpv4Srv(0));
-
-        // Initialize the source HW address.
-        std::vector<uint8_t> mac(6);
-        for (int i = 0; i < 6; ++i) {
-            mac[i] = i * 10;
-        }
-        // Initialized the destination HW address.
-        std::vector<uint8_t> dst_mac(6);
-        for (int i = 0; i < 6; ++i) {
-            dst_mac[i] = i * 20;
-        }
-        // Create a DHCP message. It will be used to simulate the
-        // incoming message.
-        boost::shared_ptr<Pkt4> req(new Pkt4(msg_type, 1234));
-        // Create a response message. It will hold a reponse packet.
-        // Initially, set it to NULL.
-        boost::shared_ptr<Pkt4> rsp;
-        // Set the name of the interface on which packet is received.
-        req->setIface("eth0");
-        // Set the interface index. It is just a dummy value and will
-        // not be interpreted.
-        req->setIndex(17);
-        // Set the target HW address. This value is normally used to
-        // construct the data link layer header.
-        req->setRemoteHWAddr(1, 6, dst_mac);
-        // Set the HW address. This value is set on DHCP level (in chaddr).
-        req->setHWAddr(1, 6, mac);
-        // Set local HW address. It is used to construct the data link layer
-        // header.
-        req->setLocalHWAddr(1, 6, mac);
-        // Set target IP address.
-        req->setRemoteAddr(isc::asiolink::IOAddress("192.0.2.55"));
-        // Set relay address.
-        req->setGiaddr(isc::asiolink::IOAddress("192.0.2.10"));
-
-        // We are going to test that certain options are returned
-        // in the response message when requested using 'Parameter
-        // Request List' option. Let's configure those options that
-        // are returned when requested.
-        configureRequestedOptions();
-
-        if (msg_type == DHCPDISCOVER) {
-            ASSERT_NO_THROW(
-                rsp = srv->processDiscover(req);
-            );
-
-            // Should return OFFER
-            ASSERT_TRUE(rsp);
-            EXPECT_EQ(DHCPOFFER, rsp->getType());
-
-        } else {
-            ASSERT_NO_THROW(
-                rsp = srv->processRequest(req);
-            );
-
-            // Should return ACK
-            ASSERT_TRUE(rsp);
-            EXPECT_EQ(DHCPACK, rsp->getType());
-
-        }
-
-        messageCheck(req, rsp);
-
-        // We did not request any options so these should not be present
-        // in the RSP.
-        EXPECT_FALSE(rsp->getOption(DHO_LOG_SERVERS));
-        EXPECT_FALSE(rsp->getOption(DHO_COOKIE_SERVERS));
-        EXPECT_FALSE(rsp->getOption(DHO_LPR_SERVERS));
-
-        // Repeat the test but request some options.
-        // Add 'Parameter Request List' option.
-        addPrlOption(req);
-
-        if (msg_type == DHCPDISCOVER) {
-            ASSERT_NO_THROW(
-                rsp = srv->processDiscover(req);
-            );
-
-            // Should return non-NULL packet.
-            ASSERT_TRUE(rsp);
-            EXPECT_EQ(DHCPOFFER, rsp->getType());
-
-        } else {
-            ASSERT_NO_THROW(
-                rsp = srv->processRequest(req);
-            );
+    /// Sets UDP ports, addresses and interface.
+    ///
+    /// @param pkt packet to have default fields set
+    void captureSetDefaultFields(const Pkt4Ptr& pkt);
 
-            // Should return non-NULL packet.
-            ASSERT_TRUE(rsp);
-            EXPECT_EQ(DHCPACK, rsp->getType());
+    /// @brief returns captured DISCOVER that went through a relay
+    ///
+    /// See method code for a detailed explanation.
+    ///
+    /// @return relayed DISCOVER
+    Pkt4Ptr captureRelayedDiscover();
 
-        }
+    /// @brief Create packet from output buffer of another packet.
+    ///
+    /// This function creates a packet using an output buffer from another
+    /// packet. This imitates reception of a packet from the wire. The
+    /// unpack function is then called to parse packet contents and to
+    /// create a collection of the options carried by this packet.
+    ///
+    /// This function is useful for unit tests which verify that the received
+    /// packet is parsed correctly. In those cases it is usually inappropriate
+    /// to create an instance of the packet, add options, set packet
+    /// fields and use such packet as an input to processDiscover or
+    /// processRequest function. This is because, such a packet has certain
+    /// options already initialized and there is no way to verify that they
+    /// have been initialized when packet instance was created or wire buffer
+    /// processing. By creating a packet from the buffer we guarantee that the
+    /// new packet is entirely initialized during wire data parsing.
+    ///
+    /// @param src_pkt A source packet, to be copied.
+    /// @param [out] dst_pkt A destination packet.
+    ///
+    /// @return assertion result indicating if a function completed with
+    /// success or failure.
+    static ::testing::AssertionResult
+    createPacketFromBuffer(const Pkt4Ptr& src_pkt,
+                           Pkt4Ptr& dst_pkt);
 
-        // Check that the requested options are returned.
-        optionsCheck(rsp);
+    /// @brief generates a DHCPv4 packet based on provided hex string
+    ///
+    /// @return created packet
+    Pkt4Ptr packetFromCapture(const std::string& hex_string);
 
-    }
+    /// @brief Tests if Discover or Request message is processed correctly
+    ///
+    /// This test verifies that the Parameter Request List option is handled
+    /// correctly, i.e. it checks that certain options are present in the
+    /// server's response when they are requested and that they are not present
+    /// when they are not requested or NAK occurs.
+    ///
+    /// @todo We need an additional test for PRL option using real traffic
+    /// capture.
+    ///
+    /// @param msg_type DHCPDISCOVER or DHCPREQUEST
+    void testDiscoverRequest(const uint8_t msg_type);
 
     /// @brief This function cleans up after the test.
-    virtual void TearDown() {
-
-        CfgMgr::instance().deleteSubnets4();
-
-        // Let's clean up if there is such a file.
-        unlink(SRVID_FILE);
-
-        // Close all open sockets.
-        IfaceMgr::instance().closeSockets();
-
-        // Some unit tests override the default packet filtering class, used
-        // by the IfaceMgr. The dummy class, called PktFilterTest, reports the
-        // capability to directly respond to the clients without IP address
-        // assigned. This capability is not supported by the default packet
-        // filtering class: PktFilterInet. Therefore setting the dummy class
-        // allows to test scenarios, when server responds to the broadcast address
-        // on client's request, despite having support for direct response.
-        // The following call restores the use of original packet filtering class
-        // after the test.
-        try {
-            IfaceMgr::instance().setPacketFilter(PktFilterPtr(new PktFilterInet()));
-
-        } catch (const Exception& ex) {
-            FAIL() << "Failed to restore the default (PktFilterInet) packet filtering"
-                   << " class after the test. Exception has been caught: "
-                   << ex.what();
-        }
-    }
+    virtual void TearDown();
 
     /// @brief A subnet used in most tests
     Subnet4Ptr subnet_;
@@ -639,6 +269,109 @@ public:
     std::string valid_iface_;
 };
 
+/// @brief "Naked" DHCPv4 server, exposes internal fields
+class NakedDhcpv4Srv: public Dhcpv4Srv {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// This constructor disables default modes of operation used by the
+    /// Dhcpv4Srv class:
+    /// - Send/receive broadcast messages through sockets on interfaces
+    /// which support broadcast traffic.
+    /// - Direct DHCPv4 traffic - communication with clients which do not
+    /// have IP address assigned yet.
+    ///
+    /// Enabling these modes requires root privilges so they must be
+    /// disabled for unit testing.
+    ///
+    /// Note, that disabling broadcast options on sockets does not impact
+    /// the operation of these tests because they use local loopback
+    /// interface which doesn't have broadcast capability anyway. It rather
+    /// prevents setting broadcast options on other (broadcast capable)
+    /// sockets which are opened on other interfaces in Dhcpv4Srv constructor.
+    ///
+    /// The Direct DHCPv4 Traffic capability can be disabled here because
+    /// it is tested with PktFilterLPFTest unittest. The tests which belong
+    /// to PktFilterLPFTest can be enabled on demand when root privileges can
+    /// be guaranteed.
+    ///
+    /// @param port port number to listen on; the default value 0 indicates
+    /// that sockets should not be opened.
+    NakedDhcpv4Srv(uint16_t port = 0)
+        : Dhcpv4Srv(port, "type=memfile", false, false) {
+    }
+
+    /// @brief fakes packet reception
+    /// @param timeout ignored
+    ///
+    /// The method receives all packets queued in receive queue, one after
+    /// another. Once the queue is empty, it initiates the shutdown procedure.
+    ///
+    /// See fake_received_ field for description
+    virtual Pkt4Ptr receivePacket(int /*timeout*/) {
+
+        // If there is anything prepared as fake incoming traffic, use it
+        if (!fake_received_.empty()) {
+            Pkt4Ptr pkt = fake_received_.front();
+            fake_received_.pop_front();
+            return (pkt);
+        }
+
+        // If not, just trigger shutdown and return immediately
+        shutdown();
+        return (Pkt4Ptr());
+    }
+
+    /// @brief fake packet sending
+    ///
+    /// Pretend to send a packet, but instead just store it in fake_send_ list
+    /// where test can later inspect server's response.
+    virtual void sendPacket(const Pkt4Ptr& pkt) {
+        fake_sent_.push_back(pkt);
+    }
+
+    /// @brief adds a packet to fake receive queue
+    ///
+    /// See fake_received_ field for description
+    void fakeReceive(const Pkt4Ptr& pkt) {
+        fake_received_.push_back(pkt);
+    }
+
+    virtual ~NakedDhcpv4Srv() {
+    }
+
+    /// @brief packets we pretend to receive
+    ///
+    /// Instead of setting up sockets on interfaces that change between OSes, it
+    /// is much easier to fake packet reception. This is a list of packets that
+    /// we pretend to have received. You can schedule new packets to be received
+    /// using fakeReceive() and NakedDhcpv4Srv::receivePacket() methods.
+    std::list<Pkt4Ptr> fake_received_;
+
+    std::list<Pkt4Ptr> fake_sent_;
+
+    using Dhcpv4Srv::adjustRemoteAddr;
+    using Dhcpv4Srv::processDiscover;
+    using Dhcpv4Srv::processRequest;
+    using Dhcpv4Srv::processRelease;
+    using Dhcpv4Srv::processDecline;
+    using Dhcpv4Srv::processInform;
+    using Dhcpv4Srv::processClientName;
+    using Dhcpv4Srv::computeDhcid;
+    using Dhcpv4Srv::createNameChangeRequests;
+    using Dhcpv4Srv::getServerID;
+    using Dhcpv4Srv::loadServerID;
+    using Dhcpv4Srv::generateServerID;
+    using Dhcpv4Srv::writeServerID;
+    using Dhcpv4Srv::sanityCheck;
+    using Dhcpv4Srv::srvidToString;
+    using Dhcpv4Srv::unpackOptions;
+    using Dhcpv4Srv::name_change_reqs_;
+};
+
+}; // end of isc::dhcp::test namespace
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
 
-} // end of test namespace
-} // end of isc namespace
+#endif // DHCP4_TEST_UTILS_H
diff --git a/src/bin/dhcp4/tests/fqdn_unittest.cc b/src/bin/dhcp4/tests/fqdn_unittest.cc
index 5f111f9..20fe5d5 100644
--- a/src/bin/dhcp4/tests/fqdn_unittest.cc
+++ b/src/bin/dhcp4/tests/fqdn_unittest.cc
@@ -15,6 +15,7 @@
 #include <config.h>
 #include <asiolink/io_address.h>
 #include <dhcp/option4_client_fqdn.h>
+#include <dhcp/option_int_array.h>
 #include <dhcp4/tests/dhcp4_test_utils.h>
 #include <dhcp_ddns/ncr_msg.h>
 
@@ -24,10 +25,11 @@
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
+using namespace isc::dhcp::test;
 using namespace isc::dhcp_ddns;
-using namespace isc::test;
 
 namespace {
+
 class NameDhcpv4SrvTest : public Dhcpv4SrvTest {
 public:
     NameDhcpv4SrvTest() : Dhcpv4SrvTest() {
diff --git a/src/bin/dhcp4/tests/test_libraries.h.in b/src/bin/dhcp4/tests/test_libraries.h.in
index 8b03dc2..1cddf00 100644
--- a/src/bin/dhcp4/tests/test_libraries.h.in
+++ b/src/bin/dhcp4/tests/test_libraries.h.in
@@ -19,32 +19,20 @@
 
 namespace {
 
-
-// Take care of differences in DLL naming between operating systems.
-
-#ifdef OS_OSX
-#define DLL_SUFFIX ".dylib"
-
-#else
 #define DLL_SUFFIX ".so"
 
-#endif
-
-
 // Names of the libraries used in these tests.  These libraries are built using
 // libtool, so we need to look in the hidden ".libs" directory to locate the
 // shared library.
 
 // Library with load/unload functions creating marker files to check their
 // operation.
-const char* const CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1"
-                                           DLL_SUFFIX;
-const char* const CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2"
-                                           DLL_SUFFIX;
+const char* const CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1.so";
+const char* const CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2.so";
 
 // Name of a library which is not present.
-const char* const NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere"
-                                         DLL_SUFFIX;
+const char* const NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";
+
 } // anonymous namespace
 
 
diff --git a/src/bin/dhcp4/tests/wireshark.cc b/src/bin/dhcp4/tests/wireshark.cc
new file mode 100644
index 0000000..80b4737
--- /dev/null
+++ b/src/bin/dhcp4/tests/wireshark.cc
@@ -0,0 +1,134 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <dhcp4/tests/dhcp4_test_utils.h>
+#include <string>
+#include <asiolink/io_address.h>
+#include <util/encode/hex.h>
+
+/// @file   wireshark.cc
+///
+/// @brief  contains packet captures imported from Wireshark
+///
+/// These are actual packets captured over wire. They are used in various
+/// tests.
+///
+/// The procedure to export Wireshark -> unit-tests is manual, but rather
+/// easy to follow:
+/// 1. Open a file in wireshark
+/// 2. Find the packet you want to export
+/// 3. There's a protocol stack (Frame, Ethernet, IPv6, UDP, DHCPv6, ...)
+/// 4. Right click on DHCPv6 -> Copy -> Bytes -> Hex Stream
+/// 5. Paste it as: string hex_string="[paste here]";
+/// 6. Coding guidelines line restrictions apply, so wrap your code as necessary
+/// 7. Make sure you decribe the capture appropriately
+/// 8. Follow whatever rest of the methods are doing (set ports, ifaces etc.)
+/// 9. To easily copy packet description, click File... -> Extract packet
+///    dissections -> as plain text file...
+///    (Make sure that the packet is expanded in the view. The text file will
+///    contain whatever expansion level you have in the graphical tree.)
+
+using namespace std;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+Pkt4Ptr Dhcpv4SrvTest::packetFromCapture(const std::string& hex_string) {
+    std::vector<uint8_t> bin;
+
+    // Decode the hex string and store it in bin (which happens
+    // to be OptionBuffer format)
+    isc::util::encode::decodeHex(hex_string, bin);
+
+    Pkt4Ptr pkt(new Pkt4(&bin[0], bin.size()));
+    captureSetDefaultFields(pkt);
+
+    return (pkt);
+}
+
+void Dhcpv4SrvTest::captureSetDefaultFields(const Pkt4Ptr& pkt) {
+    pkt->setRemotePort(546);
+    pkt->setRemoteAddr(IOAddress("fe80::1"));
+    pkt->setLocalPort(0);
+    pkt->setLocalAddr(IOAddress("ff02::1:2"));
+    pkt->setIndex(2);
+    pkt->setIface("eth0");
+}
+
+Pkt4Ptr Dhcpv4SrvTest::captureRelayedDiscover() {
+
+/* string exported from Wireshark:
+
+User Datagram Protocol, Src Port: bootps (67), Dst Port: bootps (67)
+    Source port: bootps (67)
+    Destination port: bootps (67)
+    Length: 541
+    Checksum: 0x2181 [validation disabled]
+
+Bootstrap Protocol
+    Message type: Boot Request (1)
+    Hardware type: Ethernet
+    Hardware address length: 6
+    Hops: 1
+    Transaction ID: 0x5d05478d
+    Seconds elapsed: 0
+    Bootp flags: 0x0000 (Unicast)
+    Client IP address: 0.0.0.0 (0.0.0.0)
+    Your (client) IP address: 0.0.0.0 (0.0.0.0)
+    Next server IP address: 0.0.0.0 (0.0.0.0)
+    Relay agent IP address: 10.254.226.1 (10.254.226.1)
+    Client MAC address: Netgear_b8:15:14 (20:e5:2a:b8:15:14)
+    Client hardware address padding: 00000000000000000000
+    Server host name not given
+    Boot file name not given
+    Magic cookie: DHCP
+    Option: (53) DHCP Message Type
+    Option: (55) Parameter Request List
+    Option: (60) Vendor class identifier
+    Option: (125) V-I Vendor-specific Information
+      - suboption 1 (Option Request): requesting option 2
+      - suboption 5 (Modem Caps): 117 bytes
+    Option: (43) Vendor-Specific Information (CableLabs)
+    Option: (61) Client identifier
+    Option: (57) Maximum DHCP Message Size
+    Option: (82) Agent Information Option
+    Option: (255) End */
+
+    string hex_string =
+        "010106015d05478d000000000000000000000000000000000afee20120e52ab8151400"
+        "0000000000000000000000000000000000000000000000000000000000000000000000"
+        "0000000000000000000000000000000000000000000000000000000000000000000000"
+        "0000000000000000000000000000000000000000000000000000000000000000000000"
+        "0000000000000000000000000000000000000000000000000000000000000000000000"
+        "0000000000000000000000000000000000000000000000000000000000000000000000"
+        "0000000000000000000000000000000000000000000000000000638253633501013707"
+        "0102030407067d3c0a646f63736973332e303a7d7f0000118b7a010102057501010102"
+        "010303010104010105010106010107010f0801100901030a01010b01180c01010d0200"
+        "400e0200100f010110040000000211010014010015013f160101170101180104190104"
+        "1a01041b01201c01021d01081e01201f01102001102101022201012301002401002501"
+        "01260200ff2701012b59020345434d030b45434d3a45524f55544552040d3242523232"
+        "39553430303434430504312e3034060856312e33332e30330707322e332e3052320806"
+        "30303039354209094347333030304443520a074e657467656172fe01083d0fff2ab815"
+        "140003000120e52ab81514390205dc5219010420000002020620e52ab8151409090000"
+        "118b0401020300ff";
+
+    return (packetFromCapture(hex_string));
+}
+
+}; // end of isc::dhcp::test namespace
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
index 9b7972b..9c7a5df 100644
--- a/src/bin/dhcp6/Makefile.am
+++ b/src/bin/dhcp6/Makefile.am
@@ -17,7 +17,7 @@ endif
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 
-CLEANFILES = spec_config.h dhcp6_messages.h dhcp6_messages.cc
+CLEANFILES = spec_config.h dhcp6_messages.h dhcp6_messages.cc s-messages
 
 man_MANS = b10-dhcp6.8
 DISTCLEANFILES = $(man_MANS)
@@ -41,8 +41,11 @@ endif
 spec_config.h: spec_config.h.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
 
-dhcp6_messages.h dhcp6_messages.cc: dhcp6_messages.mes
+dhcp6_messages.h dhcp6_messages.cc: s-messages
+
+s-messages: dhcp6_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/dhcp6/dhcp6_messages.mes
+	touch $@
 
 BUILT_SOURCES = spec_config.h dhcp6_messages.h dhcp6_messages.cc
 
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index 63bda52..1bb3da0 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -109,9 +109,16 @@ protected:
         } else if (option_space == "dhcp4") {
             isc_throw(DhcpConfigError, "'dhcp4' option space name is reserved"
                      << " for DHCPv4 server");
+        } else {
+            // Check if this is a vendor-option. If it is, get vendor-specific
+            // definition.
+            uint32_t vendor_id = SubnetConfigParser::optionSpaceToVendorId(option_space);
+            if (vendor_id) {
+                def = LibDHCP::getVendorOptionDef(Option::V6, vendor_id, option_code);
+            }
         }
 
-        return def;
+        return (def);
     }
 };
 
@@ -141,13 +148,13 @@ protected:
     ///
     /// @param addr is the IPv6 prefix of the pool.
     /// @param len is the prefix length.
-    /// @param ptype is the type of IPv6 pool (Pool6::Pool6Type). Note this is
-    /// passed in as an int32_t and cast to Pool6Type to accommodate a
+    /// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
+    /// passed in as an int32_t and cast to PoolType to accommodate a
     /// polymorphic interface.
     /// @return returns a PoolPtr to the new Pool4 object.
     PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t ptype)
     {
-        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool6::Pool6Type>
+        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
                                   (ptype), addr, len)));
     }
 
@@ -155,17 +162,199 @@ protected:
     ///
     /// @param min is the first IPv6 address in the pool.
     /// @param max is the last IPv6 address in the pool.
-    /// @param ptype is the type of IPv6 pool (Pool6::Pool6Type). Note this is
-    /// passed in as an int32_t and cast to Pool6Type to accommodate a
+    /// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
+    /// passed in as an int32_t and cast to PoolType to accommodate a
     /// polymorphic interface.
     /// @return returns a PoolPtr to the new Pool4 object.
     PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t ptype)
     {
-        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool6::Pool6Type>
+        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
                                   (ptype), min, max)));
     }
 };
 
+/// @brief Parser for IPv6 prefix delegation definitions.
+///
+/// This class handles prefix delegation pool definitions for IPv6 subnets
+/// Pool6 objects are created and stored in the given PoolStorage container.
+///
+/// PdPool defintions currently support three elements: prefix, prefix-len,
+/// and delegated-len, as shown in the example JSON text below:
+///
+/// @code
+///
+/// {
+///     "prefix": "2001:db8:1::",
+///     "prefix-len": 64,
+///     "delegated-len": 128
+/// }
+/// @endcode
+///
+class PdPoolParser : public DhcpConfigParser {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// @param param_name name of the parameter. Note, it is passed through
+    /// but unused, parameter is currently always "Dhcp6/subnet6[X]/pool"
+    /// @param pools storage container in which to store the parsed pool
+    /// upon "commit"
+    PdPoolParser(const std::string&,  PoolStoragePtr pools)
+        : uint32_values_(new Uint32Storage()),
+          string_values_(new StringStorage()), pools_(pools) {
+        if (!pools_) {
+            isc_throw(isc::dhcp::DhcpConfigError,
+                      "PdPoolParser context storage may not be NULL");
+        }
+    }
+
+    /// @brief Builds a prefix delegation pool from the given configuration
+    ///
+    /// This function parses configuration entries and creates an instance
+    /// of a dhcp::Pool6 configured for prefix delegation.
+    ///
+    /// @param pd_pool_ pointer to an element that holds configuration entries
+    /// that define a prefix delegation pool.
+    ///
+    /// @throw DhcpConfigError if configuration parsing fails.
+    virtual void build(ConstElementPtr pd_pool_) {
+        // Parse the elements that make up the option definition.
+        BOOST_FOREACH(ConfigPair param, pd_pool_->mapValue()) {
+            std::string entry(param.first);
+            ParserPtr parser;
+            if (entry == "prefix") {
+                StringParserPtr str_parser(new StringParser(entry,
+                                                            string_values_));
+                parser = str_parser;
+            } else if (entry == "prefix-len" || entry == "delegated-len") {
+                Uint32ParserPtr code_parser(new Uint32Parser(entry,
+                                                             uint32_values_));
+                parser = code_parser;
+            } else {
+                isc_throw(DhcpConfigError, "invalid parameter: " << entry);
+            }
+
+            parser->build(param.second);
+            parser->commit();
+        }
+
+        try {
+            // We should now have all of the pool elements we need to create
+            // the pool.  Fetch them and pass them into the Pool6 constructor.
+            // The constructor is expected to enforce any value validation.
+            const std::string addr_str = string_values_->getParam("prefix");
+            IOAddress addr(addr_str);
+
+            uint32_t prefix_len = uint32_values_->getParam("prefix-len");
+
+            uint32_t delegated_len = uint32_values_->getParam("delegated-len");
+
+            // Attempt to construct the local pool.
+            pool_.reset(new Pool6(Lease::TYPE_PD, addr, prefix_len,
+                                 delegated_len));
+        } catch (const std::exception& ex) {
+            isc_throw(isc::dhcp::DhcpConfigError,
+                      "PdPoolParser failed to build pool: " << ex.what());
+        }
+    }
+
+    // @brief Commits the constructed local pool to the pool storage.
+    virtual void commit() {
+        // Add the local pool to the external storage ptr.
+        pools_->push_back(pool_);
+    }
+
+protected:
+    /// Storage for subnet-specific integer values.
+    Uint32StoragePtr uint32_values_;
+
+    /// Storage for subnet-specific string values.
+    StringStoragePtr string_values_;
+
+    /// Parsers are stored here.
+    ParserCollection parsers_;
+
+    /// Pointer to the created pool object.
+    isc::dhcp::Pool6Ptr pool_;
+
+    /// Pointer to storage to which the local pool is written upon commit.
+    isc::dhcp::PoolStoragePtr pools_;
+};
+
+/// @brief Parser for a list of prefix delegation pools.
+///
+/// This parser iterates over a list of prefix delegation pool entries and
+/// creates pool instances for each one. If the parsing is successful, the
+/// collection of pools is committed to the provided storage.
+class PdPoolListParser : public DhcpConfigParser {
+public:
+    /// @brief Constructor.
+    ///
+    /// @param dummy first argument is ignored, all Parser constructors
+    /// accept string as first argument.
+    /// @param storage is the pool storage in which to store the parsed
+    /// pools in this list
+    /// @throw isc::dhcp::DhcpConfigError if storage is null.
+    PdPoolListParser(const std::string&, PoolStoragePtr pools)
+        : local_pools_(new PoolStorage()), pools_(pools) {
+        if (!pools_) {
+            isc_throw(isc::dhcp::DhcpConfigError,
+                      "PdPoolListParser pools storage may not be NULL");
+        }
+    }
+
+    /// @brief Parse configuration entries.
+    ///
+    /// This function parses configuration entries and creates instances
+    /// of prefix delegation pools .
+    ///
+    /// @param pd_pool_list pointer to an element that holds entries
+    /// that define a prefix delegation pool.
+    ///
+    /// @throw DhcpConfigError if configuration parsing fails.
+    void build(isc::data::ConstElementPtr pd_pool_list) {
+        // Make sure the local list is empty.
+        local_pools_.reset(new PoolStorage());
+
+        // Make sure we have a configuration elements to parse.
+        if (!pd_pool_list) {
+            isc_throw(DhcpConfigError,
+                      "PdPoolListParser: list of pool definitions is empty");
+        }
+
+        // Loop through the list of pd pools.
+        BOOST_FOREACH(ConstElementPtr pd_pool, pd_pool_list->listValue()) {
+            boost::shared_ptr<PdPoolParser>
+                // Create the PdPool parser.
+                parser(new PdPoolParser("pd-pool", local_pools_));
+                // Build the pool instance
+                parser->build(pd_pool);
+                // Commit the pool to the local list of pools.
+                parser->commit();
+        }
+    }
+
+    /// @brief  Commits the pools created to the external storage area.
+    ///
+    /// Note that this method adds the local list of pools to the storage area
+    /// rather than replacing its contents.  This permits other parsers to
+    /// contribute to the set of pools.
+    void commit() {
+        // local_pools_ holds the values produced by the build function.
+        // At this point parsing should have completed successfully so
+        // we can append new data to the supplied storage.
+        pools_->insert(pools_->end(), local_pools_->begin(),
+                       local_pools_->end());
+    }
+
+private:
+    /// @brief storage for local pools
+    PoolStoragePtr local_pools_;
+
+    /// @brief External storage where pools are stored upon list commit.
+    PoolStoragePtr pools_;
+};
+
 /// @brief This class parses a single IPv6 subnet.
 ///
 /// This is the IPv6 derivation of the SubnetConfigParser class and it parses
@@ -219,6 +408,8 @@ protected:
             parser = new StringParser(config_id, string_values_);
         } else if (config_id.compare("pool") == 0) {
             parser = new Pool6Parser(config_id, pools_);
+        } else if (config_id.compare("pd-pools") == 0) {
+            parser = new PdPoolListParser(config_id, pools_);
         } else if (config_id.compare("option-data") == 0) {
            parser = new OptionDataListParser(config_id, options_,
                                              global_context_,
diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
index 7168b91..e42c83b 100644
--- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc
+++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
@@ -216,7 +216,7 @@ void ControlledDhcpv6Srv::establishSession() {
         // reopen sockets according to new configuration.
         openActiveSockets(getPort());
 
-    } catch (const DhcpConfigError& ex) {
+    } catch (const std::exception& ex) {
         LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(ex.what());
 
     }
diff --git a/src/bin/dhcp6/dhcp6.dox b/src/bin/dhcp6/dhcp6.dox
index 9281fbc..c0284c7 100644
--- a/src/bin/dhcp6/dhcp6.dox
+++ b/src/bin/dhcp6/dhcp6.dox
@@ -167,6 +167,29 @@ Once the configuration is implemented, these constants will be removed.
 
  @todo Add section about setting up options and their definitions with bindctl.
 
+ at section dhcpv6OptionsParse Custom functions to parse message options
+
+The DHCPv6 server implementation provides a generic support to define option
+formats and set option values. A number of options formats have been defined
+for standard options in libdhcp++. However, the formats for vendor specific
+options are dynamically configured by the server's administrator and thus can't
+be stored in libdhcp++. Such option formats are stored in the
+ at c isc::dhcp::CfgMgr. The libdhcp++ provides functions for recursive parsing
+of options which may be encapsulated by other options up to the any level of
+encapsulation but these functions are unaware of the option formats defined
+in the @c isc::dhcp::CfgMgr because they belong to a different library.
+Therefore, the generic functions @c isc::dhcp::LibDHCP::unpackOptions4 and
+ at c isc::dhcp::LibDHCP::unpackOptions6 are only useful to parse standard
+options which definitions are provided in the libdhcp++. In order to overcome
+this problem a callback mechanism has been implemented in @c Option and @c Pkt6
+classes. By installing a callback function on the instance of the @c Pkt6 the
+server may provide a custom implementation of the options parsing algorithm.
+This callback function will take precedence over the @c LibDHCP::unpackOptions6
+and @c LibDHCP::unpackOptions4 functions. With this approach, the callback is
+implemented within the context of the server and it has access to all objects
+which define its configuration (including dynamically created option
+definitions).
+
  @section dhcpv6Other Other DHCPv6 topics
 
  For hooks API support in DHCPv6, see @ref dhcpv6Hooks.
diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec
index 634b046..3810fad 100644
--- a/src/bin/dhcp6/dhcp6.spec
+++ b/src/bin/dhcp6/dhcp6.spec
@@ -16,7 +16,7 @@
           "item_default": ""
         }
       },
- 
+
       { "item_name": "interfaces",
         "item_type": "list",
         "item_optional": false,
@@ -254,6 +254,38 @@
                         "item_default": ""
                     }
                 },
+                {
+                  "item_name": "pd-pools",
+                  "item_type": "list",
+                  "item_optional": true,
+                  "item_default": [],
+                  "list_item_spec":
+                  {
+                      "item_name": "pd-pool",
+                      "item_type": "map",
+                      "item_optional": false,
+                      "item_default": {},
+                      "map_item_spec": [
+                      {
+                          "item_name": "prefix",
+                          "item_type": "string",
+                          "item_optional": false,
+                          "item_default": ""
+                       },
+                       {
+                           "item_name": "prefix-len",
+                           "item_type": "integer",
+                           "item_optional": false,
+                           "item_default": 128
+                       },
+                       {
+                           "item_name": "delegated-len",
+                           "item_type": "integer",
+                           "item_optional": false,
+                           "item_default": 128
+                       }]
+                    }
+                },
                 { "item_name": "option-data",
                   "item_type": "list",
                   "item_optional": false,
@@ -313,7 +345,7 @@
 
         {
             "command_name": "libreload",
-            "command_description": "Reloads the current hooks libraries.", 
+            "command_description": "Reloads the current hooks libraries.",
             "command_args": []
         }
     ]
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index f851a7b..9678b4f 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -82,7 +82,7 @@ to perform the DNS Update, which removes RRs from the DNS.
 This debug message is logged when FQDN mapping for a particular lease has
 been changed by the recent Request message. This mapping will be changed in DNS.
 
-% DHCP6_DDNS_LEASE_RENEW_FQDN_CHANGE FQDN for the renewed lease: %1 has changed.
+% DHCP6_DDNS_LEASE_RENEW_FQDN_CHANGE FQDN for the renewed lease: %1 has changed
 New  values: hostname = %2, reverse mapping = %3, forward mapping = %4
 This debug message is logged when FQDN mapping for a particular lease has been
 changed by the recent Renew message. This mapping will be changed in DNS.
@@ -112,19 +112,19 @@ that the DNS Update has been performed for it, but the FQDN held in the lease
 database has invalid format and can't be transformed to the canonical on-wire
 format.
 
-% DHCP6_HOOK_BUFFER_RCVD_SKIP received DHCPv6 buffer was dropped because a callout set the skip flag.
+% DHCP6_HOOK_BUFFER_RCVD_SKIP received DHCPv6 buffer was dropped because a callout set the skip flag
 This debug message is printed when a callout installed on buffer6_receive
 hook point set the skip flag. For this particular hook point, the
 setting of the flag by a callout instructs the server to drop the packet.
 
-% DHCP6_HOOK_BUFFER_SEND_SKIP prepared DHCPv6 response was dropped because a callout set the skip flag.
+% DHCP6_HOOK_BUFFER_SEND_SKIP prepared DHCPv6 response was dropped because a callout set the skip flag
 This debug message is printed when a callout installed on buffer6_send
 hook point set the skip flag. For this particular hook point, the
 setting of the flag by a callout instructs the server to drop the packet.
 Server completed all the processing (e.g. may have assigned, updated
 or released leases), but the response will not be send to the client.
 
-% DHCP6_HOOK_LEASE6_RENEW_SKIP DHCPv6 lease was not renewed because a callout set the skip flag.
+% DHCP6_HOOK_LEASE6_RENEW_SKIP DHCPv6 lease was not renewed because a callout set the skip flag
 This debug message is printed when a callout installed on lease6_renew
 hook point set the skip flag. For this particular hook point, the setting
 of the flag by a callout instructs the server to not renew a lease. If
@@ -132,7 +132,15 @@ client requested renewal of multiples leases (by sending multiple IA
 options), the server will skip the renewal of the one in question and
 will proceed with other renewals as usual.
 
-% DHCP6_HOOK_LEASE6_RELEASE_SKIP DHCPv6 lease was not released because a callout set the skip flag.
+% DHCP6_HOOK_LEASE6_RELEASE_NA_SKIP DHCPv6 address lease was not released because a callout set the skip flag
+This debug message is printed when a callout installed on the
+lease6_release hook point set the skip flag. For this particular hook
+point, the setting of the flag by a callout instructs the server to not
+release a lease. If a client requested the release of multiples leases
+(by sending multiple IA options), the server will retain this particular
+lease and proceed with other releases as usual.
+
+% DHCP6_HOOK_LEASE6_RELEASE_PD_SKIP DHCPv6 prefix lease was not released because a callout set the skip flag
 This debug message is printed when a callout installed on lease6_release
 hook point set the skip flag. For this particular hook point, the
 setting of the flag by a callout instructs the server to not release
@@ -140,12 +148,12 @@ a lease. If client requested release of multiples leases (by sending
 multiple IA options), the server will retains this particular lease and
 will proceed with other renewals as usual.
 
-% DHCP6_HOOK_PACKET_RCVD_SKIP received DHCPv6 packet was dropped because a callout set the skip flag.
+% DHCP6_HOOK_PACKET_RCVD_SKIP received DHCPv6 packet was dropped because a callout set the skip flag
 This debug message is printed when a callout installed on the pkt6_receive
 hook point set the skip flag. For this particular hook point, the
 setting of the flag by a callout instructs the server to drop the packet.
 
-% DHCP6_HOOK_PACKET_SEND_SKIP prepared DHCPv6 response was not sent because a callout set the skip flag.
+% DHCP6_HOOK_PACKET_SEND_SKIP prepared DHCPv6 response was not sent because a callout set the skip flag
 This debug message is printed when a callout installed on the pkt6_send
 hook point set the skip flag. For this particular hook point, the setting
 of the flag by a callout instructs the server to drop the packet. This
@@ -153,7 +161,7 @@ effectively means that the client will not get any response, even though
 the server processed client's request and acted on it (e.g. possibly
 allocated a lease).
 
-% DHCP6_HOOK_SUBNET6_SELECT_SKIP no subnet was selected because a callout set the skip flag.
+% DHCP6_HOOK_SUBNET6_SELECT_SKIP no subnet was selected because a callout set the skip flag
 This debug message is printed when a callout installed on the
 subnet6_select hook point set the skip flag. For this particular hook
 point, the setting of the flag instructs the server not to choose a
@@ -166,34 +174,65 @@ A "libreload" command was issued to reload the hooks libraries but for
 some reason the reload failed.  Other error messages issued from the
 hooks framework will indicate the nature of the problem.
 
-% DHCP6_LEASE_ADVERT lease %1 advertised (client duid=%2, iaid=%3)
+% DHCP6_LEASE_ADVERT address lease %1 advertised (client duid=%2, iaid=%3)
 This debug message indicates that the server successfully advertised
-a lease. It is up to the client to choose one server out of the
+an address lease. It is up to the client to choose one server out of the
 advertised servers and continue allocation with that server. This
 is a normal behavior and indicates successful operation.
 
-% DHCP6_LEASE_ADVERT_FAIL failed to advertise a lease for client duid=%1, iaid=%2
-This message indicates that the server failed to advertise (in response to
-received SOLICIT) a lease for a given client. There may be many reasons for
-such failure. Each specific failure is logged in a separate log entry.
+% DHCP6_PD_LEASE_ADVERT prefix lease %1/%2 advertised (client duid=%3, iaid=%4)
+This debug message indicates that the server successfully advertised
+a prefix lease. It is up to the client to choose one server out of the
+advertised servers and continue allocation with that server. This
+is a normal behavior and indicates successful operation.
 
-% DHCP6_LEASE_ALLOC lease %1 has been allocated (client duid=%2, iaid=%3)
-This debug message indicates that the server successfully granted (in
-response to client's REQUEST message) a lease. This is a normal behavior
-and indicates successful operation.
+% DHCP6_LEASE_ADVERT_FAIL failed to advertise an address lease for client duid=%1, iaid=%2
+This message indicates that in response to a received SOLICIT, the server
+failed to advertise a non-temporary lease for a given client. There may
+be many reasons for such failure. Each failure is logged in a separate
+log entry.
+
+% DHCP6_PD_LEASE_ADVERT_FAIL failed to advertise a prefix lease for client duid=%1, iaid=%2
+This message indicates that in response to a received SOLICIT, the
+server failed to advertise a prefix lease for the client. There may
+be many reasons for such failure. Each failure is logged in a separate
+log entry.
+
+% DHCP6_LEASE_ALLOC address lease %1 has been allocated (client duid=%2, iaid=%3)
+This debug message indicates that in response to a client's REQUEST
+message, the server successfully granted an non-temporary address
+lease. This is a normal behavior and indicates successful operation.
+
+% DHCP6_PD_LEASE_ALLOC prefix lease %1/%2 has been allocated (client duid=%3, iaid=%4)
+This debug message indicates that in response to a client's REQUEST
+message, the server successfully granted a prefix delegation lease. This
+is a normal behavior and indicates successful operation.
+
+% DHCP6_LEASE_ALLOC_FAIL failed to grant an address lease for client duid=%1, iaid=%2
+This message indicates that in response to a received REQUEST, the server
+failed to grant a non-temporary address lease for the client. There may
+be many reasons for such failure. Each failure is logged in a separate
+log entry.
 
-% DHCP6_LEASE_ALLOC_FAIL failed to grant a lease for client duid=%1, iaid=%2
+% DHCP6_PD_LEASE_ALLOC_FAIL failed to grant a prefix lease for client duid=%1, iaid=%2
 This message indicates that the server failed to grant (in response to
-received REQUEST) a lease for a given client. There may be many reasons for
-such failure. Each specific failure is logged in a separate log entry.
+received REQUEST) a prefix lease for a given client. There may be many reasons
+for such failure. Each failure is logged in a separate log entry.
 
-% DHCP6_LEASE_WITHOUT_DUID lease for address %1 does not have a DUID
-This error message indicates a database consistency failure. The lease
+% DHCP6_LEASE_NA_WITHOUT_DUID address lease for address %1 does not have a DUID
+This error message indicates a database consistency problem. The lease
 database has an entry indicating that the given address is in use,
 but the lease does not contain any client identification. This is most
 likely due to a software error: please raise a bug report. As a temporary
 workaround, manually remove the lease entry from the database.
 
+% DHCP6_LEASE_PD_WITHOUT_DUID prefix lease for address %1 does not have a DUID
+This error message indicates a database consistency failure. The lease
+database has an entry indicating that the given prefix is in use,
+but the lease does not contain any client identification. This is most
+likely due to a software error: please raise a bug report. As a temporary
+workaround, manually remove the lease entry from the database.
+
 % DHCP6_NOT_RUNNING IPv6 DHCP server is not running
 A warning message is issued when an attempt is made to shut down the
 IPv6 DHCP server but it is not running.
@@ -278,38 +317,68 @@ parsing actions and committal of changes failed.  The reason for the
 failure is given in the message.
 
 % DHCP6_PROCESS_IA_NA_REQUEST server is processing IA_NA option (duid=%1, iaid=%2, hint=%3)
-This is a debug message that indicates a processing of received IA_NA
-option. It may optionally contain an address that may be used by the server
-as a hint for possible requested address.
+This is a debug message that indicates the processing of a received
+IA_NA option. It may optionally contain an address that may be used by
+the server as a hint for possible requested address.
+
+% DHCP6_PROCESS_IA_PD_REQUEST server is processing IA_PD option (duid=%1, iaid=%2, hint=%3)
+This is a debug message that indicates a processing of received IA_PD
+option. It may optionally contain an prefix that may be used by the server
+as a hint for possible requested prefix.
 
 % DHCP6_QUERY_DATA received packet length %1, data length %2, data is %3
 A debug message listing the data received from the client or relay.
 
-% DHCP6_RELEASE address %1 belonging to client duid=%2, iaid=%3 was released properly.
+% DHCP6_RELEASE_NA address %1 belonging to client duid=%2, iaid=%3 was released properly
 This debug message indicates that an address was released properly. It
 is a normal operation during client shutdown.
 
-% DHCP6_RELEASE_FAIL failed to remove lease for address %1 for duid=%2, iaid=%3
-This error message indicates that the software failed to remove a
+% DHCP6_RELEASE_PD prefix %1 belonging to client duid=%2, iaid=%3 was released properly
+This debug message indicates that a prefix was released properly. It
+is a normal operation during client shutdown.
+
+% DHCP6_RELEASE_NA_FAIL failed to remove address lease for address %1 for duid=%2, iaid=%3
+This error message indicates that the software failed to remove an address
 lease from the lease database.  It probably due to an error during a
 database operation: resolution will most likely require administrator
 intervention (e.g. check if DHCP process has sufficient privileges to
 update the database). It may also be triggered if a lease was manually
 removed from the database during RELEASE message processing.
 
-% DHCP6_RELEASE_FAIL_WRONG_DUID client (duid=%1) tried to release address %2, but it belongs to client (duid=%3)
-This warning message indicates that client tried to release an address
+% DHCP6_RELEASE_PD_FAIL failed to remove prefix lease for address %1 for duid=%2, iaid=%3
+This error message indicates that the software failed to remove a prefix
+lease from the lease database.  It probably due to an error during a
+database operation: resolution will most likely require administrator
+intervention (e.g. check if DHCP process has sufficient privileges to
+update the database). It may also be triggered if a lease was manually
+removed from the database during RELEASE message processing.
+
+% DHCP6_RELEASE_NA_FAIL_WRONG_DUID client (duid=%1) tried to release address %2, but it belongs to another client (duid=%3)
+This warning message indicates that a client tried to release an address
 that belongs to a different client. This should not happen in normal
 circumstances and may indicate a misconfiguration of the client.  However,
 since the client releasing the address will stop using it anyway, there
 is a good chance that the situation will correct itself.
 
-% DHCP6_RELEASE_FAIL_WRONG_IAID client (duid=%1) tried to release address %2, but it used wrong IAID (expected %3, but got %4)
+% DHCP6_RELEASE_PD_FAIL_WRONG_DUID client (duid=%1) tried to release prefix %2, but it belongs to another client (duid=%3)
+This warning message indicates that client tried to release a prefix
+that belongs to a different client. This should not happen in normal
+circumstances and may indicate a misconfiguration of the client.  However,
+since the client releasing the prefix will stop using it anyway, there
+is a good chance that the situation will correct itself.
+
+% DHCP6_RELEASE_NA_FAIL_WRONG_IAID client (duid=%1) tried to release address %2, but it used wrong IAID (expected %3, but got %4)
 This warning message indicates that client tried to release an address
 that does belong to it, but the address was expected to be in a different
 IA (identity association) container. This probably means that the client's
 support for multiple addresses is flawed.
 
+% DHCP6_RELEASE_PD_FAIL_WRONG_IAID client (duid=%1) tried to release prefix %2, but it used wrong IAID (expected %3, but got %4)
+This warning message indicates that client tried to release a prefix
+that does belong to it, but the address was expected to be in a different
+IA (identity association) container. This probably means that the client's
+support for multiple prefixes is flawed.
+
 % DHCP6_RELEASE_MISSING_CLIENTID client (address=%1) sent RELEASE message without mandatory client-id
 This warning message indicates that client sent RELEASE message without
 mandatory client-id option. This is most likely caused by a buggy client
@@ -381,6 +450,9 @@ This debug message indicates that a shutdown of the IPv6 server has
 been requested via a call to the 'shutdown' method of the core Dhcpv6Srv
 object.
 
+% DHCP6_SOCKET_UNICAST server is about to open socket on address %1 on interface %2
+This is a debug message that inform that a unicast socket will be opened.
+
 % DHCP6_SRV_CONSTRUCT_ERROR error creating Dhcpv6Srv object, reason: %1
 This error message indicates that during startup, the construction of a
 core component within the IPv6 DHCP server (the Dhcpv6 server object)
@@ -418,17 +490,39 @@ addresses or prefixes.
 This debug message is printed when server receives a message of unknown type.
 That could either mean missing functionality or invalid or broken relay or client.
 The list of formally defined message types is available here:
-www.iana.org/assignments/dhcpv6-parameters.
+http://www.iana.org/assignments/dhcpv6-parameters.
+
+% DHCP6_UNKNOWN_RELEASE_NA received RELEASE from unknown client (IA_NA, duid=%1, iaid=%2)
+This warning message is printed when client attempts to release an address
+lease, but no such lease is known by the server. See the explanation
+of the status code DHCP6_UNKNOWN_RENEW_NA for possible reasons for
+such behavior.
+
+% DHCP6_UNKNOWN_RELEASE_PD received RELEASE from unknown client (IA_PD, duid=%1, iaid=%2)
+This warning message is printed when client attempts to release an prefix
+lease, but no such lease is known by the server. See the explanation
+of the status code DHCP6_UNKNOWN_RENEW_PD for possible reasons for
+such behavior.
+
+% DHCP6_UNKNOWN_RENEW_NA received unknown IA_NA RENEW from client (duid=%1, iaid=%2) in subnet %3
+This warning message is printed when client attempts to renew an address
+lease (in the IA_NA option) but no such lease is known by the server. It
+typically means that client has attempted to use its lease past its
+lifetime: causes of this include a adjustment of the client's date/time
+setting or poor support on the client for sleep/recovery. A properly
+implemented client will recover from such a situation by restarting the
+lease allocation process after receiving a negative reply from the server.
 
-% DHCP6_UNKNOWN_RELEASE received RELEASE from unknown client (duid=%1, iaid=%2)
-This warning message is printed when client attempts to release a lease,
-but no such lease is known by the server. See DHCP6_UNKNOWN_RENEW for
-possible reasons for such behavior.
+An alternative cause could be that the server has lost its database
+recently and does not recognize its well-behaving clients. This is more
+probable if you see many such messages. Clients will recover from this,
+but they will most likely get a different IP addresses and experience
+a brief service interruption.
 
-% DHCP6_UNKNOWN_RENEW received RENEW from client (duid=%1, iaid=%2) in subnet %3
-This warning message is printed when client attempts to renew a lease,
-but no such lease is known by the server. It typically means that
-client has attempted to use its lease past its lifetime: causes of this
+% DHCP6_UNKNOWN_RENEW_PD received unknown IA_NA RENEW from client (duid=%1, iaid=%2) in subnet %3
+This warning message is printed when client attempts to renew an address lease
+(in IA_NA option), but no such lease is known by the server. It typically means
+that client has attempted to use its lease past its lifetime: causes of this
 include a adjustment of the clients date/time setting or poor support
 for sleep/recovery. A properly implemented client will recover from such
 a situation by restarting the lease allocation process after receiving
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index d32a514..97ffc7d 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -17,6 +17,7 @@
 #include <asiolink/io_address.h>
 #include <dhcp_ddns/ncr_msg.h>
 #include <dhcp/dhcp6.h>
+#include <dhcp/docsis3_option_defs.h>
 #include <dhcp/duid.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/libdhcp++.h>
@@ -24,8 +25,9 @@
 #include <dhcp/option6_client_fqdn.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
-#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
 #include <dhcp/option_custom.h>
+#include <dhcp/option_vendor.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/pkt6.h>
 #include <dhcp6/dhcp6_log.h>
@@ -229,11 +231,20 @@ bool Dhcpv6Srv::run() {
             continue;
         }
 
+        // In order to parse the DHCP options, the server needs to use some
+        // configuration information such as: existing option spaces, option
+        // definitions etc. This is the kind of information which is not
+        // available in the libdhcp, so we need to supply our own implementation
+        // of the option parsing function here, which would rely on the
+        // configuration data.
+        query->setCallback(boost::bind(&Dhcpv6Srv::unpackOptions, this, _1, _2,
+                                       _3, _4, _5));
+
         bool skip_unpack = false;
 
         // The packet has just been received so contains the uninterpreted wire
         // data; execute callouts registered for buffer6_receive.
-        if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_buffer6_receive_)) {
+        if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_receive_)) {
             CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
             // Delete previously set arguments
@@ -276,7 +287,7 @@ bool Dhcpv6Srv::run() {
         // At this point the information in the packet has been unpacked into
         // the various packet fields and option objects has been cretated.
         // Execute callouts registered for packet6_receive.
-        if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_pkt6_receive_)) {
+        if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_receive_)) {
             CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
             // Delete previously set arguments
@@ -369,7 +380,15 @@ bool Dhcpv6Srv::run() {
         if (rsp) {
             rsp->setRemoteAddr(query->getRemoteAddr());
             rsp->setLocalAddr(query->getLocalAddr());
-            rsp->setRemotePort(DHCP6_CLIENT_PORT);
+
+            if (rsp->relay_info_.empty()) {
+                // Direct traffic, send back to the client directly
+                rsp->setRemotePort(DHCP6_CLIENT_PORT);
+            } else {
+                // Relayed traffic, send back to the relay agent
+                rsp->setRemotePort(DHCP6_SERVER_PORT);
+            }
+
             rsp->setLocalPort(DHCP6_SERVER_PORT);
             rsp->setIndex(query->getIndex());
             rsp->setIface(query->getIface());
@@ -381,7 +400,7 @@ bool Dhcpv6Srv::run() {
             // Options are represented by individual objects, but the
             // output wire data has not been prepared yet.
             // Execute all callouts registered for packet6_send
-            if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_pkt6_send_)) {
+            if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_send_)) {
                 CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
                 // Delete all previous arguments
@@ -425,7 +444,7 @@ bool Dhcpv6Srv::run() {
                 // Option objects modification does not make sense anymore. Hooks
                 // can only manipulate wire buffer at this stage.
                 // Let's execute all callouts registered for buffer6_send
-                if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_buffer6_send_)) {
+                if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_send_)) {
                     CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
                     // Delete previously set arguments
@@ -433,10 +452,10 @@ bool Dhcpv6Srv::run() {
 
                     // Pass incoming packet as argument
                     callout_handle->setArgument("response6", rsp);
-                    
+
                     // Call callouts
                     HooksManager::callCallouts(Hooks.hook_index_buffer6_send_, *callout_handle);
-                    
+
                     // Callouts decided to skip the next processing step. The next
                     // processing step would to parse the packet, so skip at this
                     // stage means drop.
@@ -444,7 +463,7 @@ bool Dhcpv6Srv::run() {
                         LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_BUFFER_SEND_SKIP);
                         continue;
                     }
-                    
+
                     callout_handle->getArgument("response6", rsp);
                 }
 
@@ -666,6 +685,57 @@ Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
     }
 }
 
+void
+Dhcpv6Srv::appendRequestedVendorOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
+    // Get the configured subnet suitable for the incoming packet.
+    Subnet6Ptr subnet = selectSubnet(question);
+    // Leave if there is no subnet matching the incoming packet.
+    // There is no need to log the error message here because
+    // it will be logged in the assignLease() when it fails to
+    // pick the suitable subnet. We don't want to duplicate
+    // error messages in such case.
+    if (!subnet) {
+        return;
+    }
+
+    // Try to get the vendor option
+    boost::shared_ptr<OptionVendor> vendor_req =
+        boost::dynamic_pointer_cast<OptionVendor>(question->getOption(D6O_VENDOR_OPTS));
+    if (!vendor_req) {
+        return;
+    }
+
+    // Let's try to get ORO within that vendor-option
+    /// @todo This is very specific to vendor-id=4491 (Cable Labs). Other vendors
+    /// may have different policies.
+    boost::shared_ptr<OptionUint16Array> oro =
+        boost::dynamic_pointer_cast<OptionUint16Array>(vendor_req->getOption(DOCSIS3_V6_ORO));
+
+    // Option ORO not found. Don't do anything then.
+    if (!oro) {
+        return;
+    }
+
+    uint32_t vendor_id = vendor_req->getVendorId();
+
+    boost::shared_ptr<OptionVendor> vendor_rsp(new OptionVendor(Option::V6, vendor_id));
+
+    // Get the list of options that client requested.
+    bool added = false;
+    const std::vector<uint16_t>& requested_opts = oro->getValues();
+    BOOST_FOREACH(uint16_t opt, requested_opts) {
+        Subnet::OptionDescriptor desc = subnet->getVendorOptionDescriptor(vendor_id, opt);
+        if (desc.option) {
+            vendor_rsp->addOption(desc.option);
+            added = true;
+        }
+    }
+
+    if (added) {
+        answer->addOption(vendor_rsp);
+    }
+}
+
 OptionPtr
 Dhcpv6Srv::createStatusCode(uint16_t code, const std::string& text) {
     // @todo This function uses OptionCustom class to manage contents
@@ -695,7 +765,7 @@ Dhcpv6Srv::createStatusCode(uint16_t code, const std::string& text) {
 void
 Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
                        RequirementLevel serverid) {
-    Option::OptionCollection client_ids = pkt->getOptions(D6O_CLIENTID);
+    OptionCollection client_ids = pkt->getOptions(D6O_CLIENTID);
     switch (clientid) {
     case MANDATORY:
         if (client_ids.size() != 1) {
@@ -716,7 +786,7 @@ Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
         break;
     }
 
-    Option::OptionCollection server_ids = pkt->getOptions(D6O_SERVERID);
+    OptionCollection server_ids = pkt->getOptions(D6O_SERVERID);
     switch (serverid) {
     case FORBIDDEN:
         if (!server_ids.empty()) {
@@ -777,7 +847,7 @@ Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
     }
 
     // Let's execute all callouts registered for subnet6_receive
-    if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_subnet6_select_)) {
+    if (HooksManager::calloutsPresent(Hooks.hook_index_subnet6_select_)) {
         CalloutHandlePtr callout_handle = getCalloutHandle(question);
 
         // We're reusing callout_handle from previous calls
@@ -817,7 +887,6 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
     // We need to allocate addresses for all IA_NA options in the client's
     // question (i.e. SOLICIT or REQUEST) message.
     // @todo add support for IA_TA
-    // @todo add support for IA_PD
 
     // We need to select a subnet the client is connected in.
     Subnet6Ptr subnet = selectSubnet(question);
@@ -862,7 +931,7 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
     //
     // @todo: expand this to cover IA_PD and IA_TA once we implement support for
     // prefix delegation and temporary addresses.
-    for (Option::OptionCollection::iterator opt = question->options_.begin();
+    for (OptionCollection::iterator opt = question->options_.begin();
          opt != question->options_.end(); ++opt) {
         switch (opt->second->getType()) {
         case D6O_IA_NA: {
@@ -875,6 +944,14 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
             }
             break;
         }
+        case D6O_IA_PD: {
+            OptionPtr answer_opt = assignIA_PD(subnet, duid, question,
+                                               boost::dynamic_pointer_cast<
+                                               Option6IA>(opt->second));
+            if (answer_opt) {
+                answer->addOption(answer_opt);
+            }
+        }
         default:
             break;
         }
@@ -1044,8 +1121,8 @@ Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer,
 
     // Get all IAs from the answer. For each IA, holding an address we will
     // create a corresponding NameChangeRequest.
-    Option::OptionCollection answer_ias = answer->getOptions(D6O_IA_NA);
-    for (Option::OptionCollection::const_iterator answer_ia =
+    OptionCollection answer_ias = answer->getOptions(D6O_IA_NA);
+    for (OptionCollection::const_iterator answer_ia =
              answer_ias.begin(); answer_ia != answer_ias.end(); ++answer_ia) {
         // @todo IA_NA may contain multiple addresses. We should process
         // each address individually. Currently we get only one.
@@ -1223,13 +1300,18 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
     // will try to honour the hint, but it is just a hint - some other address
     // may be used instead. If fake_allocation is set to false, the lease will
     // be inserted into the LeaseMgr as well.
-    Lease6Ptr lease = alloc_engine_->allocateAddress6(subnet, duid,
-                                                      ia->getIAID(),
-                                                      hint,
-                                                      do_fwd, do_rev,
-                                                      hostname,
-                                                      fake_allocation,
-                                                      callout_handle);
+    Lease6Collection leases = alloc_engine_->allocateLeases6(subnet, duid,
+                                                             ia->getIAID(),
+                                                             hint, Lease::TYPE_NA,
+                                                             do_fwd, do_rev,
+                                                             hostname,
+                                                             fake_allocation,
+                                                             callout_handle);
+    /// @todo: Handle more than one lease
+    Lease6Ptr lease;
+    if (!leases.empty()) {
+        lease = *leases.begin();
+    }
 
     // Create IA_NA that we will put in the response.
     // Do not use OptionDefinition to create option's instance so
@@ -1298,6 +1380,107 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
 }
 
 OptionPtr
+Dhcpv6Srv::assignIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
+                       const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia) {
+
+    // Create IA_PD that we will put in the response.
+    // Do not use OptionDefinition to create option's instance so
+    // as we can initialize IAID using a constructor.
+    boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
+
+    // If there is no subnet selected for handling this IA_PD, the only thing to
+    // do left is to say that we are sorry, but the user won't get an address.
+    // As a convenience, we use a different status text to indicate that
+    // (compare to the same status code, but different wording below)
+    if (!subnet) {
+
+        // Insert status code NoAddrsAvail.
+        ia_rsp->addOption(createStatusCode(STATUS_NoPrefixAvail,
+                                           "Sorry, no subnet available."));
+        return (ia_rsp);
+    }
+
+    // Check if the client sent us a hint in his IA_PD. Clients may send an
+    // address in their IA_NA options as a suggestion (e.g. the last address
+    // they used before).
+    boost::shared_ptr<Option6IAPrefix> hintOpt =
+      boost::dynamic_pointer_cast<Option6IAPrefix>(ia->getOption(D6O_IAPREFIX));
+    IOAddress hint("::");
+    if (hintOpt) {
+        hint = hintOpt->getAddress();
+    }
+
+    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_PD_REQUEST)
+        .arg(duid ? duid->toText() : "(no-duid)").arg(ia->getIAID())
+        .arg(hintOpt ? hint.toText() : "(no hint)");
+
+    // "Fake" allocation is processing of SOLICIT message. We pretend to do an
+    // allocation, but we do not put the lease in the database. That is ok,
+    // because we do not guarantee that the user will get that exact lease. If
+    // the user selects this server to do actual allocation (i.e. sends REQUEST)
+    // it should include this hint. That will help us during the actual lease
+    // allocation.
+    bool fake_allocation = (query->getType() == DHCPV6_SOLICIT);
+
+    CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
+    // Use allocation engine to pick a lease for this client. Allocation engine
+    // will try to honour the hint, but it is just a hint - some other address
+    // may be used instead. If fake_allocation is set to false, the lease will
+    // be inserted into the LeaseMgr as well.
+    Lease6Collection leases = alloc_engine_->allocateLeases6(subnet, duid,
+                                                            ia->getIAID(),
+                                                            hint, Lease::TYPE_PD,
+                                                            false, false,
+                                                            string(),
+                                                            fake_allocation,
+                                                            callout_handle);
+
+    if (!leases.empty()) {
+
+        ia_rsp->setT1(subnet->getT1());
+        ia_rsp->setT2(subnet->getT2());
+
+        for (Lease6Collection::iterator l = leases.begin();
+             l != leases.end(); ++l) {
+
+            // We have a lease! Let's wrap its content into IA_PD option
+            // with IAADDR suboption.
+            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, fake_allocation ?
+                      DHCP6_PD_LEASE_ADVERT : DHCP6_PD_LEASE_ALLOC)
+                .arg((*l)->addr_.toText())
+                .arg(static_cast<int>((*l)->prefixlen_))
+                .arg(duid ? duid->toText() : "(no-duid)")
+                .arg(ia->getIAID());
+
+            boost::shared_ptr<Option6IAPrefix>
+                addr(new Option6IAPrefix(D6O_IAPREFIX, (*l)->addr_,
+                                         (*l)->prefixlen_, (*l)->preferred_lft_,
+                                         (*l)->valid_lft_));
+            ia_rsp->addOption(addr);
+        }
+
+        // It would be possible to insert status code=0(success) as well,
+        // but this is considered waste of bandwidth as absence of status
+        // code is considered a success.
+
+    } else {
+        // Allocation engine did not allocate a lease. The engine logged
+        // cause of that failure. The only thing left is to insert
+        // status code to pass the sad news to the client.
+
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, fake_allocation ?
+                  DHCP6_PD_LEASE_ADVERT_FAIL : DHCP6_PD_LEASE_ALLOC_FAIL)
+            .arg(duid ? duid->toText() : "(no-duid)")
+            .arg(ia->getIAID());
+
+        ia_rsp->addOption(createStatusCode(STATUS_NoPrefixAvail,
+                          "Sorry, no prefixes could be allocated."));
+    }
+    return (ia_rsp);
+}
+
+OptionPtr
 Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
                       const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia,
                       const Option6ClientFqdnPtr& fqdn) {
@@ -1316,7 +1499,8 @@ Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
         return (ia_rsp);
     }
 
-    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(*duid, ia->getIAID(),
+    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                            *duid, ia->getIAID(),
                                                             subnet->getID());
 
     if (!lease) {
@@ -1329,7 +1513,7 @@ Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
         ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
                           "Sorry, no known leases for this duid/iaid."));
 
-        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_UNKNOWN_RENEW)
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_UNKNOWN_RENEW_NA)
             .arg(duid->toText())
             .arg(ia->getIAID())
             .arg(subnet->toText());
@@ -1398,7 +1582,7 @@ Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
 
     bool skip = false;
     // Execute all callouts registered for packet6_send
-    if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_lease6_renew_)) {
+    if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_renew_)) {
         CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
         // Delete all previous arguments
@@ -1438,6 +1622,107 @@ Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
     return (ia_rsp);
 }
 
+OptionPtr
+Dhcpv6Srv::renewIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
+                      const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia) {
+
+    // Let's create a IA_NA response and fill it in later
+    boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
+
+    if (!subnet) {
+        // Insert status code NoBinding
+        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
+                          "Sorry, no known leases for this duid/iaid."));
+
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_RENEW_UNKNOWN_SUBNET)
+            .arg(duid->toText())
+            .arg(ia->getIAID());
+
+        return (ia_rsp);
+    }
+
+    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD,
+                                                            *duid, ia->getIAID(),
+                                                            subnet->getID());
+
+    if (!lease) {
+        // Client is renewing a lease that we don't know about.
+
+        // Insert status code NoBinding
+        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
+                          "Sorry, no known leases for this duid/iaid."));
+
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_UNKNOWN_RENEW_PD)
+            .arg(duid->toText())
+            .arg(ia->getIAID())
+            .arg(subnet->toText());
+
+        return (ia_rsp);
+    }
+
+    // Keep the old data in case the callout tells us to skip update.
+    Lease6 old_data = *lease;
+
+    // Do the actual lease update
+    lease->preferred_lft_ = subnet->getPreferred();
+    lease->valid_lft_ = subnet->getValid();
+    lease->t1_ = subnet->getT1();
+    lease->t2_ = subnet->getT2();
+    lease->cltt_ = time(NULL);
+
+    // Also update IA_PD container with proper T1, T2 values
+    ia_rsp->setT1(subnet->getT1());
+    ia_rsp->setT2(subnet->getT2());
+
+    boost::shared_ptr<Option6IAPrefix>
+        prefix(new Option6IAPrefix(D6O_IAPREFIX, lease->addr_,
+                                   lease->prefixlen_, lease->preferred_lft_,
+                                   lease->valid_lft_));
+    ia_rsp->addOption(prefix);
+
+    bool skip = false;
+    // Execute all callouts registered for packet6_send
+    if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_renew_)) {
+        CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
+        // Delete all previous arguments
+        callout_handle->deleteAllArguments();
+
+        // Pass the original packet
+        callout_handle->setArgument("query6", query);
+
+        // Pass the lease to be updated
+        callout_handle->setArgument("lease6", lease);
+
+        // Pass the IA option to be sent in response
+        callout_handle->setArgument("ia_pd", ia_rsp);
+
+        // Call all installed callouts
+        HooksManager::callCallouts(Hooks.hook_index_lease6_renew_,
+                                   *callout_handle);
+
+        // Remember hook's instruction whether we want to skip update or not
+        skip = callout_handle->getSkip();
+    }
+
+    if (!skip) {
+        LeaseMgrFactory::instance().updateLease6(lease);
+    } else {
+        // Callouts decided to skip the next processing step. The next
+        // processing step would to actually renew the lease, so skip at this
+        // stage means "keep the old lease as it is".
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_LEASE6_RENEW_SKIP);
+
+        // Copy back the original date to the lease. For MySQL it doesn't make
+        // much sense, but for memfile, the Lease6Ptr points to the actual lease
+        // in memfile, so the actual update is performed when we manipulate
+        // fields of returned Lease6Ptr, the actual updateLease6() is no-op.
+        *lease = old_data;
+    }
+
+    return (ia_rsp);
+}
+
 void
 Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply,
                        const Option6ClientFqdnPtr& fqdn) {
@@ -1479,9 +1764,10 @@ Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply,
     }
     DuidPtr duid(new DUID(opt_duid->getData()));
 
-    for (Option::OptionCollection::iterator opt = renew->options_.begin();
+    for (OptionCollection::iterator opt = renew->options_.begin();
          opt != renew->options_.end(); ++opt) {
         switch (opt->second->getType()) {
+
         case D6O_IA_NA: {
             OptionPtr answer_opt = renewIA_NA(subnet, duid, renew,
                                               boost::dynamic_pointer_cast<
@@ -1492,6 +1778,17 @@ Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply,
             }
             break;
         }
+
+        case D6O_IA_PD: {
+            OptionPtr answer_opt = renewIA_PD(subnet, duid, renew,
+                                              boost::dynamic_pointer_cast<
+                                                  Option6IA>(opt->second));
+            if (answer_opt) {
+                reply->addOption(answer_opt);
+            }
+            break;
+        }
+
         default:
             break;
         }
@@ -1528,8 +1825,13 @@ Dhcpv6Srv::releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply) {
     }
     DuidPtr duid(new DUID(opt_duid->getData()));
 
+    // Let's set the status to be success by default. We can override it with
+    // error status if needed. The important thing to understand here is that
+    // the global status code may be set to success only if all IA options were
+    // handled properly. Therefore the releaseIA_NA and releaseIA_PD options
+    // may turn the status code to some error, but can't turn it back to success.
     int general_status = STATUS_Success;
-    for (Option::OptionCollection::iterator opt = release->options_.begin();
+    for (OptionCollection::iterator opt = release->options_.begin();
          opt != release->options_.end(); ++opt) {
         switch (opt->second->getType()) {
         case D6O_IA_NA: {
@@ -1540,7 +1842,14 @@ Dhcpv6Srv::releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply) {
             }
             break;
         }
-        // @todo: add support for IA_PD
+        case D6O_IA_PD: {
+            OptionPtr answer_opt = releaseIA_PD(duid, release, general_status,
+                                   boost::dynamic_pointer_cast<Option6IA>(opt->second));
+            if (answer_opt) {
+                reply->addOption(answer_opt);
+            }
+            break;
+        }
         // @todo: add support for IA_TA
         default:
             // remaining options are stateless and thus ignored in this context
@@ -1574,12 +1883,13 @@ Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
         (ia->getOption(D6O_IAADDR));
     if (!release_addr) {
         ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
-                                           "You did not include address in your RELEASE"));
+                                           "You did not include an address in your RELEASE"));
         general_status = STATUS_NoBinding;
         return (ia_rsp);
     }
 
-    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(release_addr->getAddress());
+    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                            release_addr->getAddress());
 
     if (!lease) {
         // client releasing a lease that we don't know about.
@@ -1589,7 +1899,7 @@ Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
                           "Sorry, no known leases for this duid/iaid, can't release."));
         general_status = STATUS_NoBinding;
 
-        LOG_INFO(dhcp6_logger, DHCP6_UNKNOWN_RELEASE)
+        LOG_INFO(dhcp6_logger, DHCP6_UNKNOWN_RELEASE_NA)
             .arg(duid->toText())
             .arg(ia->getIAID());
 
@@ -1601,7 +1911,7 @@ Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
         // have mandatory DUID information attached. Someone was messing with our
         // database.
 
-        LOG_ERROR(dhcp6_logger, DHCP6_LEASE_WITHOUT_DUID)
+        LOG_ERROR(dhcp6_logger, DHCP6_LEASE_NA_WITHOUT_DUID)
             .arg(release_addr->getAddress().toText());
 
         general_status = STATUS_UnspecFail;
@@ -1611,9 +1921,9 @@ Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
     }
 
     if (*duid != *(lease->duid_)) {
-        // Sorry, it's not your address. You can't release it.
 
-        LOG_INFO(dhcp6_logger, DHCP6_RELEASE_FAIL_WRONG_DUID)
+        // Sorry, it's not your address. You can't release it.
+        LOG_INFO(dhcp6_logger, DHCP6_RELEASE_NA_FAIL_WRONG_DUID)
             .arg(duid->toText())
             .arg(release_addr->getAddress().toText())
             .arg(lease->duid_->toText());
@@ -1626,7 +1936,7 @@ Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
 
     if (ia->getIAID() != lease->iaid_) {
         // This address belongs to this client, but to a different IA
-        LOG_WARN(dhcp6_logger, DHCP6_RELEASE_FAIL_WRONG_IAID)
+        LOG_WARN(dhcp6_logger, DHCP6_RELEASE_PD_FAIL_WRONG_IAID)
             .arg(duid->toText())
             .arg(release_addr->getAddress().toText())
             .arg(lease->iaid_)
@@ -1642,7 +1952,7 @@ Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
 
     bool skip = false;
     // Execute all callouts registered for packet6_send
-    if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_lease6_release_)) {
+    if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_release_)) {
         CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
         // Delete all previous arguments
@@ -1662,7 +1972,7 @@ Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
         // stage means "drop response".
         if (callout_handle->getSkip()) {
             skip = true;
-            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_LEASE6_RELEASE_SKIP);
+            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_LEASE6_RELEASE_NA_SKIP);
         }
     }
 
@@ -1680,7 +1990,7 @@ Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
         ia_rsp->addOption(createStatusCode(STATUS_UnspecFail,
                           "Server failed to release a lease"));
 
-        LOG_ERROR(dhcp6_logger, DHCP6_RELEASE_FAIL)
+        LOG_ERROR(dhcp6_logger, DHCP6_RELEASE_NA_FAIL)
             .arg(lease->addr_.toText())
             .arg(duid->toText())
             .arg(lease->iaid_);
@@ -1688,7 +1998,7 @@ Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
 
         return (ia_rsp);
     } else {
-        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_RELEASE)
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_RELEASE_NA)
             .arg(lease->addr_.toText())
             .arg(duid->toText())
             .arg(lease->iaid_);
@@ -1705,6 +2015,148 @@ Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
     }
 }
 
+OptionPtr
+Dhcpv6Srv::releaseIA_PD(const DuidPtr& duid, const Pkt6Ptr& query,
+                        int& general_status, boost::shared_ptr<Option6IA> ia) {
+    // Release can be done in one of two ways:
+    // Approach 1: extract address from client's IA_NA and see if it belongs
+    // to this particular client.
+    // Approach 2: find a subnet for this client, get a lease for
+    // this subnet/duid/iaid and check if its content matches to what the
+    // client is asking us to release.
+    //
+    // This method implements approach 1.
+
+    // That's our response. We will fill it in as we check the lease to be
+    // released.
+    boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
+
+    boost::shared_ptr<Option6IAPrefix> release_prefix =
+        boost::dynamic_pointer_cast<Option6IAPrefix>(ia->getOption(D6O_IAPREFIX));
+    if (!release_prefix) {
+        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
+                          "You did not include a prefix in your RELEASE"));
+        general_status = STATUS_NoBinding;
+        return (ia_rsp);
+    }
+
+    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD,
+                                                            release_prefix->getAddress());
+
+    if (!lease) {
+        // Client releasing a lease that we don't know about.
+
+        // Insert status code NoBinding.
+        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
+                          "Sorry, no known leases for this duid/iaid, can't release."));
+        general_status = STATUS_NoBinding;
+
+        LOG_INFO(dhcp6_logger, DHCP6_UNKNOWN_RELEASE_PD)
+            .arg(duid->toText())
+            .arg(ia->getIAID());
+
+        return (ia_rsp);
+    }
+
+    if (!lease->duid_) {
+        // Something is gravely wrong here. We do have a lease, but it does not
+        // have mandatory DUID information attached. Someone was messing with our
+        // database.
+        LOG_ERROR(dhcp6_logger, DHCP6_LEASE_PD_WITHOUT_DUID)
+            .arg(release_prefix->getAddress().toText());
+
+        general_status = STATUS_UnspecFail;
+        ia_rsp->addOption(createStatusCode(STATUS_UnspecFail,
+                          "Database consistency check failed when trying to RELEASE"));
+        return (ia_rsp);
+    }
+
+    if (*duid != *(lease->duid_)) {
+        // Sorry, it's not your address. You can't release it.
+        LOG_INFO(dhcp6_logger, DHCP6_RELEASE_PD_FAIL_WRONG_DUID)
+            .arg(duid->toText())
+            .arg(release_prefix->getAddress().toText())
+            .arg(lease->duid_->toText());
+
+        general_status = STATUS_NoBinding;
+        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
+                          "This address does not belong to you, you can't release it"));
+        return (ia_rsp);
+    }
+
+    if (ia->getIAID() != lease->iaid_) {
+        // This address belongs to this client, but to a different IA
+        LOG_WARN(dhcp6_logger, DHCP6_RELEASE_PD_FAIL_WRONG_IAID)
+            .arg(duid->toText())
+            .arg(release_prefix->getAddress().toText())
+            .arg(lease->iaid_)
+            .arg(ia->getIAID());
+        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
+                          "This is your address, but you used wrong IAID"));
+        general_status = STATUS_NoBinding;
+        return (ia_rsp);
+    }
+
+    // It is not necessary to check if the address matches as we used
+    // getLease6(addr) method that is supposed to return a proper lease.
+
+    bool skip = false;
+    // Execute all callouts registered for packet6_send
+    if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_release_)) {
+        CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
+        // Delete all previous arguments
+        callout_handle->deleteAllArguments();
+
+        // Pass the original packet
+        callout_handle->setArgument("query6", query);
+
+        // Pass the lease to be updated
+        callout_handle->setArgument("lease6", lease);
+
+        // Call all installed callouts
+        HooksManager::callCallouts(Hooks.hook_index_lease6_release_, *callout_handle);
+
+        skip = callout_handle->getSkip();
+    }
+
+    // Ok, we've passed all checks. Let's release this prefix.
+    bool success = false; // was the removal operation succeessful?
+
+    if (!skip) {
+        success = LeaseMgrFactory::instance().deleteLease(lease->addr_);
+    } else {
+        // Callouts decided to skip the next processing step. The next
+        // processing step would to send the packet, so skip at this
+        // stage means "drop response".
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_LEASE6_RELEASE_PD_SKIP);
+    }
+
+    // Here the success should be true if we removed lease successfully
+    // and false if skip flag was set or the removal failed for whatever reason
+
+    if (!success) {
+        ia_rsp->addOption(createStatusCode(STATUS_UnspecFail,
+                          "Server failed to release a lease"));
+
+        LOG_ERROR(dhcp6_logger, DHCP6_RELEASE_PD_FAIL)
+            .arg(lease->addr_.toText())
+            .arg(duid->toText())
+            .arg(lease->iaid_);
+        general_status = STATUS_UnspecFail;
+    } else {
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_RELEASE_PD)
+            .arg(lease->addr_.toText())
+            .arg(duid->toText())
+            .arg(lease->iaid_);
+
+        ia_rsp->addOption(createStatusCode(STATUS_Success,
+                          "Lease released. Thank you, please come again."));
+    }
+
+    return (ia_rsp);
+}
+
 Pkt6Ptr
 Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
 
@@ -1715,6 +2167,7 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
     copyDefaultOptions(solicit, advertise);
     appendDefaultOptions(solicit, advertise);
     appendRequestedOptions(solicit, advertise);
+    appendRequestedVendorOptions(solicit, advertise);
 
     Option6ClientFqdnPtr fqdn = processClientFqdn(solicit);
     assignLeases(solicit, advertise, fqdn);
@@ -1736,6 +2189,7 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
     copyDefaultOptions(request, reply);
     appendDefaultOptions(request, reply);
     appendRequestedOptions(request, reply);
+    appendRequestedVendorOptions(request, reply);
 
     Option6ClientFqdnPtr fqdn = processClientFqdn(request);
     assignLeases(request, reply, fqdn);
@@ -1830,7 +2284,7 @@ Dhcpv6Srv::openActiveSockets(const uint16_t port) {
                       << " trying to reopen sockets after reconfiguration");
         }
         if (CfgMgr::instance().isActiveIface(iface->getName())) {
-            iface_ptr->inactive4_ = false;
+            iface_ptr->inactive6_ = false;
             LOG_INFO(dhcp6_logger, DHCP6_ACTIVATE_INTERFACE)
                 .arg(iface->getFullName());
 
@@ -1843,6 +2297,15 @@ Dhcpv6Srv::openActiveSockets(const uint16_t port) {
             iface_ptr->inactive6_ = true;
 
         }
+
+        iface_ptr->clearUnicasts();
+
+        const IOAddress* unicast = CfgMgr::instance().getUnicast(iface->getName());
+        if (unicast) {
+            LOG_INFO(dhcp6_logger, DHCP6_SOCKET_UNICAST).arg(unicast->toText())
+                .arg(iface->getName());
+            iface_ptr->addUnicast(*unicast);
+        }
     }
     // Let's reopen active sockets. openSockets6 will check internally whether
     // sockets are marked active or inactive.
@@ -1853,5 +2316,99 @@ Dhcpv6Srv::openActiveSockets(const uint16_t port) {
     }
 }
 
+size_t
+Dhcpv6Srv::unpackOptions(const OptionBuffer& buf,
+                         const std::string& option_space,
+                         isc::dhcp::OptionCollection& options,
+                         size_t* relay_msg_offset,
+                         size_t* relay_msg_len) {
+    size_t offset = 0;
+    size_t length = buf.size();
+
+    OptionDefContainer option_defs;
+    if (option_space == "dhcp6") {
+        // Get the list of stdandard option definitions.
+        option_defs = LibDHCP::getOptionDefs(Option::V6);
+    } else if (!option_space.empty()) {
+        OptionDefContainerPtr option_defs_ptr =
+            CfgMgr::instance().getOptionDefs(option_space);
+        if (option_defs_ptr != NULL) {
+            option_defs = *option_defs_ptr;
+        }
+    }
+
+    // Get the search index #1. It allows to search for option definitions
+    // using option code.
+    const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
+
+    // The buffer being read comprises a set of options, each starting with
+    // a two-byte type code and a two-byte length field.
+    while (offset + 4 <= length) {
+        uint16_t opt_type = isc::util::readUint16(&buf[offset]);
+        offset += 2;
+
+        uint16_t opt_len = isc::util::readUint16(&buf[offset]);
+        offset += 2;
+
+        if (offset + opt_len > length) {
+            // @todo: consider throwing exception here.
+            return (offset);
+        }
+
+        if (opt_type == D6O_RELAY_MSG && relay_msg_offset && relay_msg_len) {
+            // remember offset of the beginning of the relay-msg option
+            *relay_msg_offset = offset;
+            *relay_msg_len = opt_len;
+
+            // do not create that relay-msg option
+            offset += opt_len;
+            continue;
+        }
+
+        // Get all definitions with the particular option code. Note that option
+        // code is non-unique within this container however at this point we
+        // expect to get one option definition with the particular code. If more
+        // are returned we report an error.
+        const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
+        // Get the number of returned option definitions for the option code.
+        size_t num_defs = distance(range.first, range.second);
+
+        OptionPtr opt;
+        if (num_defs > 1) {
+            // Multiple options of the same code are not supported right now!
+            isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
+                      " for option type " << opt_type << " returned. Currently it is not"
+                      " supported to initialize multiple option definitions"
+                      " for the same option code. This will be supported once"
+                      " support for option spaces is implemented");
+        } else if (num_defs == 0) {
+            // @todo Don't crash if definition does not exist because only a few
+            // option definitions are initialized right now. In the future
+            // we will initialize definitions for all options and we will
+            // remove this elseif. For now, return generic option.
+            opt = OptionPtr(new Option(Option::V6, opt_type,
+                                       buf.begin() + offset,
+                                       buf.begin() + offset + opt_len));
+            opt->setEncapsulatedSpace("dhcp6");
+        } else {
+            // The option definition has been found. Use it to create
+            // the option instance from the provided buffer chunk.
+            const OptionDefinitionPtr& def = *(range.first);
+            assert(def);
+            opt = def->optionFactory(Option::V6, opt_type,
+                                     buf.begin() + offset,
+                                     buf.begin() + offset + opt_len,
+                                     boost::bind(&Dhcpv6Srv::unpackOptions, this, _1, _2,
+                                                 _3, _4, _5));
+        }
+        // add option to options
+        options.insert(std::make_pair(opt_type, opt));
+        offset += opt_len;
+    }
+
+    return (offset);
+}
+
+
 };
 };
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index 55cdef1..20b6ea1 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -206,7 +206,7 @@ protected:
     /// @brief Processes IA_NA option (and assigns addresses if necessary).
     ///
     /// Generates response to IA_NA. This typically includes selecting (and
-    /// allocating a lease in case of REQUEST) a lease and creating
+    /// allocating a lease in case of REQUEST) an address lease and creating
     /// IAADDR option. In case of allocation failure, it may contain
     /// status code option with non-zero status, denoting cause of the
     /// allocation failure.
@@ -224,6 +224,23 @@ protected:
                           Option6IAPtr ia,
                           const Option6ClientFqdnPtr& fqdn);
 
+    /// @brief Processes IA_PD option (and assigns prefixes if necessary).
+    ///
+    /// Generates response to IA_PD. This typically includes selecting (and
+    /// allocating in the case of REQUEST) a prefix lease and creating an
+    /// IAPREFIX option. In case of an allocation failure, it may contain a
+    /// status code option with non-zero status denoting the cause of the
+    /// allocation failure.
+    ///
+    /// @param subnet subnet the client is connected to
+    /// @param duid client's duid
+    /// @param query client's message (typically SOLICIT or REQUEST)
+    /// @param ia pointer to client's IA_PD option (client's request)
+    /// @return IA_PD option (server's response)
+    OptionPtr assignIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
+                          const Pkt6Ptr& query,
+                          boost::shared_ptr<Option6IA> ia);
+
     /// @brief Renews specific IA_NA option
     ///
     /// Generates response to IA_NA in Renew. This typically includes finding a
@@ -240,6 +257,20 @@ protected:
                          const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia,
                          const Option6ClientFqdnPtr& fqdn);
 
+    /// @brief Renews specific IA_PD option
+    ///
+    /// Generates response to IA_PD in Renew. This typically includes finding a
+    /// lease that corresponds to the received prefix. If no such lease is
+    /// found, an IA_PD response is generated with an appropriate status code.
+    ///
+    /// @param subnet subnet the sender belongs to
+    /// @param duid client's duid
+    /// @param query client's message
+    /// @param ia IA_PD option that is being renewed
+    /// @return IA_PD option (server's response)
+    OptionPtr renewIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
+                         const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia);
+
     /// @brief Releases specific IA_NA option
     ///
     /// Generates response to IA_NA in Release message. This covers finding and
@@ -255,12 +286,28 @@ protected:
     /// @param duid client's duid
     /// @param query client's message
     /// @param general_status a global status (it may be updated in case of errors)
-    /// @param ia IA_NA option that is being renewed
+    /// @param ia IA_NA option that is being released
     /// @return IA_NA option (server's response)
     OptionPtr releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
                            int& general_status,
                            boost::shared_ptr<Option6IA> ia);
 
+    /// @brief Releases specific IA_PD option
+    ///
+    /// Generates response to IA_PD in Release message. This covers finding and
+    /// removal of a lease that corresponds to the received prefix(es). If no such
+    /// lease is found, an IA_PD response is generated with an appropriate
+    /// status code.
+    ///
+    /// @param duid client's duid
+    /// @param query client's message
+    /// @param general_status a global status (it may be updated in case of errors)
+    /// @param ia IA_PD option that is being released
+    /// @return IA_PD option (server's response)
+    OptionPtr releaseIA_PD(const DuidPtr& duid, const Pkt6Ptr& query,
+                           int& general_status,
+                           boost::shared_ptr<Option6IA> ia);
+
     /// @brief Copies required options from client message to server answer.
     ///
     /// Copies options that must appear in any server response (ADVERTISE, REPLY)
@@ -289,6 +336,15 @@ protected:
     /// @param answer server's message (options will be added here)
     void appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);
 
+    /// @brief Appends requested vendor options to server's answer.
+    ///
+    /// This is mostly useful for Cable Labs options for now, but the method
+    /// is easily extensible to other vendors.
+    ///
+    /// @param question client's message
+    /// @param answer server's message (vendor options will be added here)
+    void appendRequestedVendorOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);
+
     /// @brief Assigns leases.
     ///
     /// It supports addresses (IA_NA) only. It does NOT support temporary
@@ -459,6 +515,24 @@ protected:
     /// simulates transmission of a packet. For that purpose it is protected.
     virtual void sendPacket(const Pkt6Ptr& pkt);
 
+    /// @brief Implements a callback function to parse options in the message.
+    ///
+    /// @param buf a A buffer holding options in on-wire format.
+    /// @param option_space A name of the option space which holds definitions
+    /// of to be used to parse options in the packets.
+    /// @param [out] options A reference to the collection where parsed options
+    /// will be stored.
+    /// @param relay_msg_offset Reference to a size_t structure. If specified,
+    /// offset to beginning of relay_msg option will be stored in it.
+    /// @param relay_msg_len reference to a size_t structure. If specified,
+    /// length of the relay_msg option will be stored in it.
+    /// @return An offset to the first byte after last parsed option.
+    size_t unpackOptions(const OptionBuffer& buf,
+                         const std::string& option_space,
+                         isc::dhcp::OptionCollection& options,
+                         size_t* relay_msg_offset,
+                         size_t* relay_msg_len);
+
 private:
     /// @brief Allocation Engine.
     /// Pointer to the allocation engine that we are currently using
@@ -473,11 +547,6 @@ private:
     /// initiate server shutdown procedure.
     volatile bool shutdown_;
 
-    /// Indexes for registered hook points
-    int hook_index_pkt6_receive_;
-    int hook_index_subnet6_select_;
-    int hook_index_pkt6_send_;
-
     /// UDP port number on which server listens.
     uint16_t port_;
 
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index 7ea7163..84cc197 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -36,31 +36,41 @@ if USE_CLANGPP
 AM_CXXFLAGS += -Wno-unused-parameter
 endif
 
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-endif
-
 TESTS_ENVIRONMENT = \
         $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST
-# Build shared libraries for testing.
+# Build shared libraries for testing. The libtool way to create a shared library
+# is to specify "-avoid-version -export-dynamic -module" in the library LDFLAGS
+# (see http://www.gnu.org/software/libtool/manual/html_node/Link-mode.html).
+# Use of these switches will guarantee that the .so files are created in the
+# .libs folder and they can be dlopened.
+# Note that the shared libraries with callouts should not be used together with
+# the --enable-static-link option. With this option, the bind10 libraries are
+# statically linked with the program and if the callout invokes the methods
+# which belong to these libraries, the library with the callout will get its
+# own copy of the static objects (e.g. logger, ServerHooks) and that will lead
+# to unexpected errors. For this reason, the --enable-static-link option is
+# ignored for unit tests built here.
+
 lib_LTLIBRARIES = libco1.la libco2.la
 
 libco1_la_SOURCES  = callout_library_1.cc callout_library_common.h
 libco1_la_CXXFLAGS = $(AM_CXXFLAGS)
 libco1_la_CPPFLAGS = $(AM_CPPFLAGS)
+libco1_la_LDFLAGS = -avoid-version -export-dynamic -module
 
 libco2_la_SOURCES  = callout_library_2.cc callout_library_common.h
 libco2_la_CXXFLAGS = $(AM_CXXFLAGS)
 libco2_la_CPPFLAGS = $(AM_CPPFLAGS)
+libco2_la_LDFLAGS = -avoid-version -export-dynamic -module
 
 TESTS += dhcp6_unittests
 dhcp6_unittests_SOURCES  = dhcp6_unittests.cc
 dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += hooks_unittest.cc
-dhcp6_unittests_SOURCES += dhcp6_test_utils.h
+dhcp6_unittests_SOURCES += dhcp6_test_utils.cc dhcp6_test_utils.h
 dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += config_parser_unittest.cc
 dhcp6_unittests_SOURCES += marker_file.cc
@@ -68,6 +78,7 @@ dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc
 dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc
 dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc
 dhcp6_unittests_SOURCES += ../config_parser.cc ../config_parser.h
+dhcp6_unittests_SOURCES += wireshark.cc
 
 nodist_dhcp6_unittests_SOURCES  = ../dhcp6_messages.h ../dhcp6_messages.cc
 nodist_dhcp6_unittests_SOURCES += marker_file.h test_libraries.h
diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc
index 0c46d26..2420172 100644
--- a/src/bin/dhcp6/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp6/tests/config_parser_unittest.cc
@@ -22,10 +22,12 @@
 #include <dhcp/option_int.h>
 #include <dhcp6/config_parser.h>
 #include <dhcp6/dhcp6_srv.h>
+#include <dhcpsrv/addr_utilities.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/subnet.h>
 #include <hooks/hooks_manager.h>
 
+#include "test_data_files_config.h"
 #include "test_libraries.h"
 #include "marker_file.h"
 
@@ -53,6 +55,17 @@ using namespace std;
 
 namespace {
 
+std::string specfile(const std::string& name) {
+    return (std::string(DHCP6_SRC_DIR) + "/" + name);
+}
+
+/// @brief Tests that the spec file is valid.
+/// Verifies that the DHCP6 configuration specification file is valid.
+TEST(Dhcp6SpecTest, basicSpec) {
+    ASSERT_NO_THROW(isc::config::
+                    moduleSpecFromFile(specfile("dhcp6.spec")));
+}
+
 class Dhcp6ParserTest : public ::testing::Test {
 public:
     Dhcp6ParserTest() :rcode_(-1), srv_(0) {
@@ -682,6 +695,8 @@ TEST_F(Dhcp6ParserTest, poolOutOfSubnet) {
 // Goal of this test is to verify if pools can be defined
 // using prefix/length notation. There is no separate test for min-max
 // notation as it was tested in several previous tests.
+// Note this test also verifies that subnets can be configured without
+// prefix delegation pools.
 TEST_F(Dhcp6ParserTest, poolPrefixLen) {
 
     ConstElementPtr x;
@@ -712,6 +727,296 @@ TEST_F(Dhcp6ParserTest, poolPrefixLen) {
     EXPECT_EQ(4000, subnet->getValid());
 }
 
+// Goal of this test is to verify the basic parsing of a prefix delegation
+// pool. It uses a single, valid pd pool.
+TEST_F(Dhcp6ParserTest, pdPoolBasics) {
+
+    ConstElementPtr x;
+
+    // Define a single valid pd pool.
+    string config =
+        "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"subnet\": \"2001:db8:1::/64\","
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"2001:db8:1::\", "
+        "          \"prefix-len\": 64, "
+        "          \"delegated-len\": 128"
+        "        } ],"
+        "\"valid-lifetime\": 4000 }"
+        "] }";
+
+    // Convert the JSON string into Elements.
+    ElementPtr json;
+    ASSERT_NO_THROW(json = Element::fromJSON(config));
+
+    // Verify that DHCP6 configuration processing succeeds.
+    // Returned value must be non-empty ConstElementPtr to config result.
+    // rcode should be 0 which indicates successful configuration processing.
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    EXPECT_EQ(0, rcode_);
+
+    // Test that we can retrieve the subnet.
+    Subnet6Ptr subnet = CfgMgr::
+                        instance().getSubnet6(IOAddress("2001:db8:1::5"));
+
+    ASSERT_TRUE(subnet);
+
+    // Fetch the collection of PD pools.  It should have 1 entry.
+    PoolCollection pc;
+    ASSERT_NO_THROW(pc = subnet->getPools(Lease::TYPE_PD));
+    EXPECT_EQ(1, pc.size());
+
+    // Get a pointer to the pd pool instance, and verify its contents.
+    Pool6Ptr p6;
+    ASSERT_NO_THROW(p6 = boost::dynamic_pointer_cast<Pool6>(pc[0]));
+    ASSERT_TRUE(p6);
+    EXPECT_EQ("2001:db8:1::", p6->getFirstAddress().toText());
+    EXPECT_EQ(128, p6->getLength());
+
+    // prefix-len is not directly accessible after pool construction, so
+    // verify that it was interpreted correctly by checking the last address
+    // value.
+    isc::asiolink::IOAddress prefixAddress("2001:db8:1::");
+    EXPECT_EQ(lastAddrInPrefix(prefixAddress, 64).toText(),
+              p6->getLastAddress().toText());
+}
+
+// Goal of this test is verify that a list of PD pools can be configured.
+// It also verifies that a subnet may be configured with both regular pools
+// and pd pools.
+TEST_F(Dhcp6ParserTest, pdPoolList) {
+
+    ConstElementPtr x;
+
+    const char* prefixes[] = {
+        "2001:db8:1:1::",
+        "2001:db8:1:2::",
+        "2001:db8:1:3::"
+    };
+
+    string config =
+        "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pool\": [ \"2001:db8:1:04::/80\" ],"
+        "    \"subnet\": \"2001:db8:1::/40\","
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"2001:db8:1:01::\", "
+        "          \"prefix-len\": 72, "
+        "          \"delegated-len\": 80"
+        "        },"
+        "        { \"prefix\": \"2001:db8:1:02::\", "
+        "          \"prefix-len\": 72, "
+        "          \"delegated-len\": 88"
+        "        },"
+        "        { \"prefix\": \"2001:db8:1:03::\", "
+        "          \"prefix-len\": 72, "
+        "          \"delegated-len\": 96"
+        "        }"
+        "],"
+        "\"valid-lifetime\": 4000 }"
+        "] }";
+
+    // Convert the JSON string into Elements.
+    ElementPtr json = Element::fromJSON(config);
+    ASSERT_NO_THROW(json = Element::fromJSON(config));
+
+    // Verify that DHCP6 configuration processing succeeds.
+    // Returned value must be non-empty ConstElementPtr to config result.
+    // rcode should be 0 which indicates successful configuration processing.
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    EXPECT_EQ(0, rcode_);
+
+    // Test that we can retrieve the subnet.
+    Subnet6Ptr subnet = CfgMgr::
+                        instance().getSubnet6(IOAddress("2001:db8:1::5"));
+    ASSERT_TRUE(subnet);
+
+    // Fetch the collection of NA pools.  It should have 1 entry.
+    PoolCollection pc;
+    ASSERT_NO_THROW(pc = subnet->getPools(Lease::TYPE_NA));
+    EXPECT_EQ(1, pc.size());
+
+    // Fetch the collection of PD pools.  It should have 3 entries.
+    ASSERT_NO_THROW(pc = subnet->getPools(Lease::TYPE_PD));
+    EXPECT_EQ(3, pc.size());
+
+    // Loop through the pools and verify their contents.
+    for (int i = 0; i < 3; i++) {
+        Pool6Ptr p6;
+        ASSERT_NO_THROW(p6 = boost::dynamic_pointer_cast<Pool6>(pc[i]));
+        ASSERT_TRUE(p6);
+        EXPECT_EQ(prefixes[i], p6->getFirstAddress().toText());
+        EXPECT_EQ((80 + (i * 8)), p6->getLength());
+    }
+}
+
+// Goal of this test is to verify the a whole prefix can be delegated and that
+// a whole subnet can be delegated.
+TEST_F(Dhcp6ParserTest, subnetAndPrefixDelegated) {
+
+    ConstElementPtr x;
+
+    // Define a single valid pd pool.
+    string config =
+        "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"subnet\": \"2001:db8:1::/64\","
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"2001:db8:1::\", "
+        "          \"prefix-len\": 64, "
+        "          \"delegated-len\": 64"
+        "        } ],"
+        "\"valid-lifetime\": 4000 }"
+        "] }";
+
+    // Convert the JSON string into Elements.
+    ElementPtr json;
+    ASSERT_NO_THROW(json = Element::fromJSON(config));
+
+    // Verify that DHCP6 configuration processing succeeds.
+    // Returned value must be non-empty ConstElementPtr to config result.
+    // rcode should be 0 which indicates successful configuration processing.
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    EXPECT_EQ(0, rcode_);
+
+    // Test that we can retrieve the subnet.
+    Subnet6Ptr subnet = CfgMgr::
+                        instance().getSubnet6(IOAddress("2001:db8:1::5"));
+
+    ASSERT_TRUE(subnet);
+
+    // Fetch the collection of PD pools.  It should have 1 entry.
+    PoolCollection pc;
+    ASSERT_NO_THROW(pc = subnet->getPools(Lease::TYPE_PD));
+    EXPECT_EQ(1, pc.size());
+
+    // Get a pointer to the pd pool instance, and verify its contents.
+    Pool6Ptr p6;
+    ASSERT_NO_THROW(p6 = boost::dynamic_pointer_cast<Pool6>(pc[0]));
+    ASSERT_TRUE(p6);
+    EXPECT_EQ("2001:db8:1::", p6->getFirstAddress().toText());
+    EXPECT_EQ(64, p6->getLength());
+
+    // prefix-len is not directly accessible after pool construction, so
+    // verify that it was interpreted correctly by checking the last address
+    // value.
+    isc::asiolink::IOAddress prefixAddress("2001:db8:1::");
+    EXPECT_EQ(lastAddrInPrefix(prefixAddress, 64).toText(),
+              p6->getLastAddress().toText());
+}
+
+
+// Goal of this test is check for proper handling of invalid prefix delegation
+// pool configuration.  It uses an array of invalid configurations to check
+// a variety of configuration errors.
+TEST_F(Dhcp6ParserTest, invalidPdPools) {
+
+    ConstElementPtr x;
+
+    const char *config[] =  {
+        // No prefix.
+        "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"subnet\": \"2001:db8:1::/64\","
+        "    \"pd-pools\": ["
+        "        { "
+        "          \"prefix-len\": 64, "
+        "          \"delegated-len\": 128"
+        "        } ],"
+        "\"valid-lifetime\": 4000 }"
+        "] }",
+        // No prefix-len.
+        "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"subnet\": \"2001:db8:1::/64\","
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"2001:db8:1::\", "
+        "          \"delegated-len\": 128"
+        "        } ],"
+        "\"valid-lifetime\": 4000 }"
+        "] }",
+        // No delegated-len.
+        "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"subnet\": \"2001:db8:1::/64\","
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"2001:db8:1::\", "
+        "          \"prefix-len\": 64 "
+        "        } ],"
+        "\"valid-lifetime\": 4000 }"
+        "] }",
+        // Delegated length is too short.
+        "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"subnet\": \"2001:db8:1::/64\","
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"2001:db8:1::\", "
+        "          \"prefix-len\": 128, "
+        "          \"delegated-len\": 64"
+        "        } ],"
+        "\"valid-lifetime\": 4000 }"
+        "] }",
+        // Pool is not within the subnet.
+        "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"subnet\": \"2001:db8:1::/64\","
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"2001:db8:77::\", "
+        "          \"prefix-len\": 64, "
+        "          \"delegated-len\": 128"
+        "        } ],"
+        "\"valid-lifetime\": 4000 }"
+        "] }"
+        };
+
+    ElementPtr json;
+    int num_msgs = sizeof(config)/sizeof(char*);
+    for (int i = 0; i < num_msgs; i++) {
+        // Convert JSON string to Elements.
+        ASSERT_NO_THROW(json = Element::fromJSON(config[i]));
+
+        // Configuration processing should fail without a throw.
+        ASSERT_NO_THROW(x = configureDhcp6Server(srv_, json));
+
+        // Returned value must be non-empty ConstElementPtr to config result.
+        // rcode should be 1 which indicates configuration error.
+        ASSERT_TRUE(x);
+        comment_ = parseAnswer(rcode_, x);
+        EXPECT_EQ(1, rcode_);
+    }
+}
+
 // The goal of this test is to check whether an option definition
 // that defines an option carrying an IPv6 address can be created.
 TEST_F(Dhcp6ParserTest, optionDefIpv6Address) {
@@ -1744,6 +2049,121 @@ TEST_F(Dhcp6ParserTest, stdOptionData) {
     EXPECT_EQ(1516, optionIA->getT2());
 }
 
+// This test checks if vendor options can be specified in the config file
+// (in hex format), and later retrieved from configured subnet
+TEST_F(Dhcp6ParserTest, vendorOptionsHex) {
+
+    // This configuration string is to configure two options
+    // sharing the code 1 and belonging to the different vendor spaces.
+    // (different vendor-id values).
+    string config = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000,"
+        "\"renew-timer\": 1000,"
+        "\"option-data\": [ {"
+        "    \"name\": \"option-one\","
+        "    \"space\": \"vendor-4491\","
+        "    \"code\": 100,"
+        "    \"data\": \"AB CDEF0105\","
+        "    \"csv-format\": False"
+        " },"
+        " {"
+        "    \"name\": \"option-two\","
+        "    \"space\": \"vendor-1234\","
+        "    \"code\": 100,"
+        "    \"data\": \"1234\","
+        "    \"csv-format\": False"
+        " } ],"
+        "\"subnet6\": [ { "
+        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"subnet\": \"2001:db8:1::/64\""
+        " } ]"
+        "}";
+
+    ConstElementPtr status;
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // Options should be now available for the subnet.
+    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+    ASSERT_TRUE(subnet);
+
+    // Try to get the option from the vendor space 4491
+    Subnet::OptionDescriptor desc1 = subnet->getVendorOptionDescriptor(4491, 100);
+    ASSERT_TRUE(desc1.option);
+    EXPECT_EQ(100, desc1.option->getType());
+    // Try to get the option from the vendor space 1234
+    Subnet::OptionDescriptor desc2 = subnet->getVendorOptionDescriptor(1234, 100);
+    ASSERT_TRUE(desc2.option);
+    EXPECT_EQ(100, desc1.option->getType());
+
+    // Try to get the non-existing option from the non-existing
+    // option space and  expect that option is not returned.
+    Subnet::OptionDescriptor desc3 = subnet->getVendorOptionDescriptor(5678, 38);
+    ASSERT_FALSE(desc3.option);
+}
+
+// This test checks if vendor options can be specified in the config file,
+// (in csv format), and later retrieved from configured subnet
+TEST_F(Dhcp6ParserTest, vendorOptionsCsv) {
+
+    // This configuration string is to configure two options
+    // sharing the code 1 and belonging to the different vendor spaces.
+    // (different vendor-id values).
+    string config = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000,"
+        "\"renew-timer\": 1000,"
+        "\"option-data\": [ {"
+        "    \"name\": \"foo\","
+        "    \"space\": \"vendor-4491\","
+        "    \"code\": 100,"
+        "    \"data\": \"this is a string vendor-opt\","
+        "    \"csv-format\": True"
+        " } ],"
+        "\"option-def\": [ {"
+        "    \"name\": \"foo\","
+        "    \"code\": 100,"
+        "    \"type\": \"string\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"vendor-4491\","
+        "    \"encapsulate\": \"\""
+        " } ],"
+        "\"subnet6\": [ { "
+        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"subnet\": \"2001:db8:1::/64\""
+        " } ]"
+        "}";
+
+    ConstElementPtr status;
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // Options should be now available for the subnet.
+    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+    ASSERT_TRUE(subnet);
+
+    // Try to get the option from the vendor space 4491
+    Subnet::OptionDescriptor desc1 = subnet->getVendorOptionDescriptor(4491, 100);
+    ASSERT_TRUE(desc1.option);
+    EXPECT_EQ(100, desc1.option->getType());
+
+    // Try to get the non-existing option from the non-existing
+    // option space and  expect that option is not returned.
+    Subnet::OptionDescriptor desc2 = subnet->getVendorOptionDescriptor(5678, 100);
+    ASSERT_FALSE(desc2.option);
+}
+
+/// @todo add tests similar to vendorOptionsCsv and vendorOptionsHex, but for
+///       vendor options defined in a subnet.
+
 // The goal of this test is to verify that the standard option can
 // be configured to encapsulate multiple other options.
 TEST_F(Dhcp6ParserTest, stdOptionDataEncapsulate) {
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index c3435af..a8a76cb 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -24,10 +24,14 @@
 #include <dhcp/option6_client_fqdn.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
+#include <dhcp/option_int.h>
+#include <dhcp/option_vendor.h>
 #include <dhcp/option_int_array.h>
+#include <dhcp/option_string.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp6/config_parser.h>
 #include <dhcp/dhcp6.h>
+#include <dhcp/docsis3_option_defs.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
@@ -67,7 +71,7 @@ public:
         : Dhcpv6SrvTest() {
         // generateClientId assigns DUID to duid_.
         generateClientId();
-        lease_.reset(new Lease6(Lease6::LEASE_IA_NA, IOAddress("2001:db8:1::1"),
+        lease_.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
                                 duid_, 1234, 501, 502, 503,
                                 504, 1, 0));
 
@@ -97,7 +101,7 @@ public:
                                 OptionPtr srvid = OptionPtr()) {
         Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
         pkt->setRemoteAddr(IOAddress("fe80::abcd"));
-        Option6IAPtr ia = generateIA(234, 1500, 3000);
+        Option6IAPtr ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
 
         if (msg_type != DHCPV6_REPLY) {
             IOAddress hint("2001:db8:1:1::dead:beef");
@@ -149,7 +153,7 @@ public:
 
     // Adds IA option to the message. Option holds an address.
     void addIA(const uint32_t iaid, const IOAddress& addr, Pkt6Ptr& pkt) {
-        Option6IAPtr opt_ia = generateIA(iaid, 1500, 3000);
+        Option6IAPtr opt_ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
         Option6IAAddrPtr opt_iaaddr(new Option6IAAddr(D6O_IAADDR, addr,
                                                       300, 500));
         opt_ia->addOption(opt_iaaddr);
@@ -158,7 +162,7 @@ public:
 
     // Adds IA option to the message. Option holds status code.
     void addIA(const uint32_t iaid, const uint16_t status_code, Pkt6Ptr& pkt) {
-        Option6IAPtr opt_ia = generateIA(iaid, 1500, 3000);
+        Option6IAPtr opt_ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
         addStatusCode(status_code, "", opt_ia);
         pkt->addOption(opt_ia);
     }
@@ -265,8 +269,7 @@ public:
 
         // Check that we have got the address we requested.
         checkIAAddr(addr, IOAddress("2001:db8:1:1::dead:beef"),
-                    subnet_->getPreferred(),
-                    subnet_->getValid());
+                    Lease::TYPE_NA);
 
         if (msg_type != DHCPV6_SOLICIT) {
             // Check that the lease exists.
@@ -312,7 +315,7 @@ TEST_F(NakedDhcpv6SrvTest, SolicitNoSubnet) {
 
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
-    sol->addOption(generateIA(234, 1500, 3000));
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
 
@@ -335,7 +338,7 @@ TEST_F(NakedDhcpv6SrvTest, RequestNoSubnet) {
     // Let's create a REQUEST
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(234, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
 
     // with a hint
     IOAddress hint("2001:db8:1:1::dead:beef");
@@ -373,7 +376,7 @@ TEST_F(NakedDhcpv6SrvTest, RenewNoSubnet) {
     // Let's create a RENEW
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
     OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(renewed_addr_opt);
@@ -408,7 +411,7 @@ TEST_F(NakedDhcpv6SrvTest, ReleaseNoSubnet) {
     // Let's create a RELEASE
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
     OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(released_addr_opt);
@@ -563,7 +566,7 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
 
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
-    sol->addOption(generateIA(234, 1500, 3000));
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
 
@@ -647,7 +650,7 @@ TEST_F(Dhcpv6SrvTest, SolicitBasic) {
 
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
-    sol->addOption(generateIA(234, 1500, 3000));
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
 
@@ -663,7 +666,51 @@ TEST_F(Dhcpv6SrvTest, SolicitBasic) {
     ASSERT_TRUE(addr);
 
     // Check that the assigned address is indeed from the configured pool
-    checkIAAddr(addr, addr->getAddress(), subnet_->getPreferred(), subnet_->getValid());
+    checkIAAddr(addr, addr->getAddress(), Lease::TYPE_NA);
+
+    // check DUIDs
+    checkServerId(reply, srv.getServerID());
+    checkClientId(reply, clientid);
+}
+
+// This test verifies that incoming SOLICIT can be handled properly, that an
+// ADVERTISE is generated, that the response has a prefix and that prefix
+// really belongs to the configured pool.
+//
+// This test sends a SOLICIT without any hint in IA_PD.
+//
+// constructed very simple SOLICIT message with:
+// - client-id option (mandatory)
+// - IA option (a request for address, without any addresses)
+//
+// expected returned ADVERTISE message:
+// - copy of client-id
+// - server-id
+// - IA that includes IAPREFIX
+TEST_F(Dhcpv6SrvTest, pdSolicitBasic) {
+
+    NakedDhcpv6Srv srv(0);
+
+    Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+    sol->setRemoteAddr(IOAddress("fe80::abcd"));
+    sol->addOption(generateIA(D6O_IA_PD, 234, 1500, 3000));
+    OptionPtr clientid = generateClientId();
+    sol->addOption(clientid);
+
+    // Pass it to the server and get an advertise
+    Pkt6Ptr reply = srv.processSolicit(sol);
+
+    // check if we get response at all
+    checkResponse(reply, DHCPV6_ADVERTISE, 1234);
+
+    // check that IA_NA was returned and that there's an address included
+    boost::shared_ptr<Option6IAPrefix> prefix = checkIA_PD(reply, 234, subnet_->getT1(),
+                                                           subnet_->getT2());
+    ASSERT_TRUE(prefix);
+
+    // Check that the assigned prefix is indeed from the configured pool
+    checkIAAddr(prefix, prefix->getAddress(), Lease::TYPE_PD);
+    EXPECT_EQ(pd_pool_->getLength(), prefix->getLength());
 
     // check DUIDs
     checkServerId(reply, srv.getServerID());
@@ -691,11 +738,11 @@ TEST_F(Dhcpv6SrvTest, SolicitHint) {
     // Let's create a SOLICIT
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(234, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
 
     // with a valid hint
     IOAddress hint("2001:db8:1:1::dead:beef");
-    ASSERT_TRUE(subnet_->inPool(hint));
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, hint));
     OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
     ia->addOption(hint_opt);
     sol->addOption(ia);
@@ -717,7 +764,7 @@ TEST_F(Dhcpv6SrvTest, SolicitHint) {
     ASSERT_TRUE(addr);
 
     // check that we've got the address we requested
-    checkIAAddr(addr, hint, subnet_->getPreferred(), subnet_->getValid());
+    checkIAAddr(addr, hint, Lease::TYPE_NA);
 
     // check DUIDs
     checkServerId(reply, srv.getServerID());
@@ -745,9 +792,9 @@ TEST_F(Dhcpv6SrvTest, SolicitInvalidHint) {
     // Let's create a SOLICIT
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(234, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
     IOAddress hint("2001:db8:1::cafe:babe");
-    ASSERT_FALSE(subnet_->inPool(hint));
+    ASSERT_FALSE(subnet_->inPool(Lease::TYPE_NA, hint));
     OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
     ia->addOption(hint_opt);
     sol->addOption(ia);
@@ -766,8 +813,8 @@ TEST_F(Dhcpv6SrvTest, SolicitInvalidHint) {
     ASSERT_TRUE(addr);
 
     // Check that the assigned address is indeed from the configured pool
-    checkIAAddr(addr, addr->getAddress(), subnet_->getPreferred(), subnet_->getValid());
-    EXPECT_TRUE(subnet_->inPool(addr->getAddress()));
+    checkIAAddr(addr, addr->getAddress(), Lease::TYPE_NA);
+    EXPECT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr->getAddress()));
 
     // check DUIDs
     checkServerId(reply, srv.getServerID());
@@ -795,9 +842,9 @@ TEST_F(Dhcpv6SrvTest, ManySolicits) {
     sol2->setRemoteAddr(IOAddress("fe80::1223"));
     sol3->setRemoteAddr(IOAddress("fe80::3467"));
 
-    sol1->addOption(generateIA(1, 1500, 3000));
-    sol2->addOption(generateIA(2, 1500, 3000));
-    sol3->addOption(generateIA(3, 1500, 3000));
+    sol1->addOption(generateIA(D6O_IA_NA, 1, 1500, 3000));
+    sol2->addOption(generateIA(D6O_IA_NA, 2, 1500, 3000));
+    sol3->addOption(generateIA(D6O_IA_NA, 3, 1500, 3000));
 
     // different client-id sizes
     OptionPtr clientid1 = generateClientId(12);
@@ -830,9 +877,9 @@ TEST_F(Dhcpv6SrvTest, ManySolicits) {
     ASSERT_TRUE(addr3);
 
     // Check that the assigned address is indeed from the configured pool
-    checkIAAddr(addr1, addr1->getAddress(), subnet_->getPreferred(), subnet_->getValid());
-    checkIAAddr(addr2, addr2->getAddress(), subnet_->getPreferred(), subnet_->getValid());
-    checkIAAddr(addr3, addr3->getAddress(), subnet_->getPreferred(), subnet_->getValid());
+    checkIAAddr(addr1, addr1->getAddress(), Lease::TYPE_NA);
+    checkIAAddr(addr2, addr2->getAddress(), Lease::TYPE_NA);
+    checkIAAddr(addr3, addr3->getAddress(), Lease::TYPE_NA);
 
     // check DUIDs
     checkServerId(reply1, srv.getServerID());
@@ -872,11 +919,11 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
     // Let's create a REQUEST
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(234, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
 
     // with a valid hint
     IOAddress hint("2001:db8:1:1::dead:beef");
-    ASSERT_TRUE(subnet_->inPool(hint));
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, hint));
     OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
     ia->addOption(hint_opt);
     req->addOption(ia);
@@ -896,12 +943,13 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
     ASSERT_TRUE(tmp);
 
     // check that IA_NA was returned and that there's an address included
-    boost::shared_ptr<Option6IAAddr> addr = checkIA_NA(reply, 234, subnet_->getT1(),
-                                                subnet_->getT2());
+    boost::shared_ptr<Option6IAAddr> addr = checkIA_NA(reply, 234,
+                                                       subnet_->getT1(),
+                                                       subnet_->getT2());
     ASSERT_TRUE(addr);
 
     // check that we've got the address we requested
-    checkIAAddr(addr, hint, subnet_->getPreferred(), subnet_->getValid());
+    checkIAAddr(addr, hint, Lease::TYPE_NA);
 
     // check DUIDs
     checkServerId(reply, srv.getServerID());
@@ -913,6 +961,71 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
     LeaseMgrFactory::instance().deleteLease(addr->getAddress());
 }
 
+// This test verifies that incoming REQUEST can be handled properly, that a
+// REPLY is generated, that the response has a prefix and that prefix
+// really belongs to the configured pool.
+//
+// This test sends a REQUEST with IA_PD that contains a valid hint.
+//
+// constructed very simple REQUEST message with:
+// - client-id option (mandatory)
+// - IA option (a request for address, with an address that belongs to the
+//              configured pool, i.e. is valid as hint)
+//
+// expected returned REPLY message:
+// - copy of client-id
+// - server-id
+// - IA that includes IAPREFIX
+TEST_F(Dhcpv6SrvTest, pdRequestBasic) {
+
+    NakedDhcpv6Srv srv(0);
+
+    // Let's create a REQUEST
+    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
+    req->setRemoteAddr(IOAddress("fe80::abcd"));
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_PD, 234, 1500, 3000);
+
+    // with a valid hint
+    IOAddress hint("2001:db8:1:2:f::");
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_PD, hint));
+    OptionPtr hint_opt(new Option6IAPrefix(D6O_IAPREFIX, hint, 64, 300, 500));
+    ia->addOption(hint_opt);
+    req->addOption(ia);
+    OptionPtr clientid = generateClientId();
+    req->addOption(clientid);
+
+    // server-id is mandatory in REQUEST
+    req->addOption(srv.getServerID());
+
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRequest(req);
+
+    // check if we get response at all
+    checkResponse(reply, DHCPV6_REPLY, 1234);
+
+    OptionPtr tmp = reply->getOption(D6O_IA_PD);
+    ASSERT_TRUE(tmp);
+
+    // check that IA_NA was returned and that there's an address included
+    boost::shared_ptr<Option6IAPrefix> prf = checkIA_PD(reply, 234,
+                                                        subnet_->getT1(),
+                                                        subnet_->getT2());
+    ASSERT_TRUE(prf);
+
+    // check that we've got the address we requested
+    checkIAAddr(prf, hint, Lease::TYPE_PD);
+    EXPECT_EQ(pd_pool_->getLength(), prf->getLength());
+
+    // check DUIDs
+    checkServerId(reply, srv.getServerID());
+    checkClientId(reply, clientid);
+
+    // check that the lease is really in the database
+    Lease6Ptr l = checkPdLease(duid_, reply->getOption(D6O_IA_PD), prf);
+    EXPECT_TRUE(l);
+    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(prf->getAddress()));
+}
+
 // This test checks that the server is offering different addresses to different
 // clients in REQUEST. Please note that ADVERTISE is not a guarantee that such
 // and address will be assigned. Had the pool was very small and contained only
@@ -933,9 +1046,9 @@ TEST_F(Dhcpv6SrvTest, ManyRequests) {
     req2->setRemoteAddr(IOAddress("fe80::1223"));
     req3->setRemoteAddr(IOAddress("fe80::3467"));
 
-    req1->addOption(generateIA(1, 1500, 3000));
-    req2->addOption(generateIA(2, 1500, 3000));
-    req3->addOption(generateIA(3, 1500, 3000));
+    req1->addOption(generateIA(D6O_IA_NA, 1, 1500, 3000));
+    req2->addOption(generateIA(D6O_IA_NA, 2, 1500, 3000));
+    req3->addOption(generateIA(D6O_IA_NA, 3, 1500, 3000));
 
     // different client-id sizes
     OptionPtr clientid1 = generateClientId(12);
@@ -974,9 +1087,9 @@ TEST_F(Dhcpv6SrvTest, ManyRequests) {
     ASSERT_TRUE(addr3);
 
     // Check that the assigned address is indeed from the configured pool
-    checkIAAddr(addr1, addr1->getAddress(), subnet_->getPreferred(), subnet_->getValid());
-    checkIAAddr(addr2, addr2->getAddress(), subnet_->getPreferred(), subnet_->getValid());
-    checkIAAddr(addr3, addr3->getAddress(), subnet_->getPreferred(), subnet_->getValid());
+    checkIAAddr(addr1, addr1->getAddress(), Lease::TYPE_NA);
+    checkIAAddr(addr2, addr2->getAddress(), Lease::TYPE_NA);
+    checkIAAddr(addr3, addr3->getAddress(), Lease::TYPE_NA);
 
     // check DUIDs
     checkServerId(reply1, srv.getServerID());
@@ -1002,94 +1115,29 @@ TEST_F(Dhcpv6SrvTest, ManyRequests) {
 // expected:
 // - returned REPLY message has copy of client-id
 // - returned REPLY message has server-id
-// - returned REPLY message has IA that includes IAADDR
+// - returned REPLY message has IA_NA that includes IAADDR
 // - lease is actually renewed in LeaseMgr
-TEST_F(Dhcpv6SrvTest, RenewBasic) {
-    NakedDhcpv6Srv srv(0);
-
-    const IOAddress addr("2001:db8:1:1::cafe:babe");
-    const uint32_t iaid = 234;
-
-    // Generate client-id also duid_
-    OptionPtr clientid = generateClientId();
-
-    // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(addr));
-
-    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
-    // value on purpose. They should be updated during RENEW.
-    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid_, iaid,
-                               501, 502, 503, 504, subnet_->getID(), 0));
-    lease->cltt_ = 1234;
-    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
-
-    // Check that the lease is really in the database
-    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(addr);
-    ASSERT_TRUE(l);
-
-    // Check that T1, T2, preferred, valid and cltt really set and not using
-    // previous (500, 501, etc.) values
-    EXPECT_NE(l->t1_, subnet_->getT1());
-    EXPECT_NE(l->t2_, subnet_->getT2());
-    EXPECT_NE(l->preferred_lft_, subnet_->getPreferred());
-    EXPECT_NE(l->valid_lft_, subnet_->getValid());
-    EXPECT_NE(l->cltt_, time(NULL));
-
-    // Let's create a RENEW
-    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
-    req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
-
-    OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
-    ia->addOption(renewed_addr_opt);
-    req->addOption(ia);
-    req->addOption(clientid);
-
-    // Server-id is mandatory in RENEW
-    req->addOption(srv.getServerID());
-
-    // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRenew(req);
-
-    // Check if we get response at all
-    checkResponse(reply, DHCPV6_REPLY, 1234);
-
-    OptionPtr tmp = reply->getOption(D6O_IA_NA);
-    ASSERT_TRUE(tmp);
-
-    // Check that IA_NA was returned and that there's an address included
-    boost::shared_ptr<Option6IAAddr> addr_opt = checkIA_NA(reply, 234, subnet_->getT1(),
-                                                           subnet_->getT2());
-
-    ASSERT_TRUE(addr_opt);
-
-    // Check that we've got the address we requested
-    checkIAAddr(addr_opt, addr, subnet_->getPreferred(), subnet_->getValid());
-
-    // Check DUIDs
-    checkServerId(reply, srv.getServerID());
-    checkClientId(reply, clientid);
-
-    // Check that the lease is really in the database
-    l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr_opt);
-    ASSERT_TRUE(l);
-
-    // Check that T1, T2, preferred, valid and cltt were really updated
-    EXPECT_EQ(l->t1_, subnet_->getT1());
-    EXPECT_EQ(l->t2_, subnet_->getT2());
-    EXPECT_EQ(l->preferred_lft_, subnet_->getPreferred());
-    EXPECT_EQ(l->valid_lft_, subnet_->getValid());
-
-    // Checking for CLTT is a bit tricky if we want to avoid off by 1 errors
-    int32_t cltt = static_cast<int32_t>(l->cltt_);
-    int32_t expected = static_cast<int32_t>(time(NULL));
-    // equality or difference by 1 between cltt and expected is ok.
-    EXPECT_GE(1, abs(cltt - expected));
+TEST_F(Dhcpv6SrvTest, renewBasic) {
+    testRenewBasic(Lease::TYPE_NA, "2001:db8:1:1::cafe:babe",
+                   "2001:db8:1:1::cafe:babe", 128);
+}
 
-    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr_opt->getAddress()));
+// This test verifies that incoming (positive) PD RENEW can be handled properly,
+// that a REPLY is generated, that the response has a prefix and that prefix
+// really belongs to the configured pool and that lease is actually renewed.
+//
+// expected:
+// - returned REPLY message has copy of client-id
+// - returned REPLY message has server-id
+// - returned REPLY message has IA_PD that includes IAPREFIX
+// - lease is actually renewed in LeaseMgr
+TEST_F(Dhcpv6SrvTest, pdRenewBasic) {
+    testRenewBasic(Lease::TYPE_PD, "2001:db8:1:2::",
+                   "2001:db8:1:2::", pd_pool_->getLength());
 }
 
-// This test verifies that incoming (invalid) RENEW can be handled properly.
+// This test verifies that incoming (invalid) RENEW with an address
+// can be handled properly.
 //
 // This test checks 3 scenarios:
 // 1. there is no such lease at all
@@ -1099,181 +1147,59 @@ TEST_F(Dhcpv6SrvTest, RenewBasic) {
 // expected:
 // - returned REPLY message has copy of client-id
 // - returned REPLY message has server-id
-// - returned REPLY message has IA that includes STATUS-CODE
+// - returned REPLY message has IA_NA that includes STATUS-CODE
 // - No lease in LeaseMgr
 TEST_F(Dhcpv6SrvTest, RenewReject) {
-    NakedDhcpv6Srv srv(0);
-
-    const IOAddress addr("2001:db8:1:1::dead");
-    const uint32_t transid = 1234;
-    const uint32_t valid_iaid = 234;
-    const uint32_t bogus_iaid = 456;
-
-    // Quick sanity check that the address we're about to use is ok
-    ASSERT_TRUE(subnet_->inPool(addr));
-
-    // GenerateClientId() also sets duid_
-    OptionPtr clientid = generateClientId();
-
-    // Check that the lease is NOT in the database
-    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(addr);
-    ASSERT_FALSE(l);
-
-    // Let's create a RENEW
-    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, transid));
-    req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(bogus_iaid, 1500, 3000);
-
-    OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
-    ia->addOption(renewed_addr_opt);
-    req->addOption(ia);
-    req->addOption(clientid);
-
-    // Server-id is mandatory in RENEW
-    req->addOption(srv.getServerID());
-
-    // Case 1: No lease known to server
-
-    // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRenew(req);
-
-    // Check if we get response at all
-    checkResponse(reply, DHCPV6_REPLY, transid);
-    OptionPtr tmp = reply->getOption(D6O_IA_NA);
-    ASSERT_TRUE(tmp);
-    // Check that IA_NA was returned and that there's an address included
-    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
-    ASSERT_TRUE(ia);
-    checkIA_NAStatusCode(ia, STATUS_NoBinding);
-
-    // Check that there is no lease added
-    l = LeaseMgrFactory::instance().getLease6(addr);
-    ASSERT_FALSE(l);
-
-    // CASE 2: Lease is known and belongs to this client, but to a different IAID
-
-    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
-    // value on purpose. They should be updated during RENEW.
-    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid_, valid_iaid,
-                               501, 502, 503, 504, subnet_->getID(), 0));
-    lease->cltt_ = 123; // Let's use it as an indicator that the lease
-                        // was NOT updated.
-    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
-
-    // Pass it to the server and hope for a REPLY
-    reply = srv.processRenew(req);
-    checkResponse(reply, DHCPV6_REPLY, transid);
-    tmp = reply->getOption(D6O_IA_NA);
-    ASSERT_TRUE(tmp);
-    // Check that IA_NA was returned and that there's an address included
-    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
-    ASSERT_TRUE(ia);
-    checkIA_NAStatusCode(ia, STATUS_NoBinding);
-
-    // There is a iaid mis-match, so server should respond that there is
-    // no such address to renew.
-
-    // CASE 3: Lease belongs to a client with different client-id
-    req->delOption(D6O_CLIENTID);
-    ia = boost::dynamic_pointer_cast<Option6IA>(req->getOption(D6O_IA_NA));
-    ia->setIAID(valid_iaid); // Now iaid in renew matches that in leasemgr
-    req->addOption(generateClientId(13)); // generate different DUID
-                                          // (with length 13)
-
-    reply = srv.processRenew(req);
-    checkResponse(reply, DHCPV6_REPLY, transid);
-    tmp = reply->getOption(D6O_IA_NA);
-    ASSERT_TRUE(tmp);
-    // Check that IA_NA was returned and that there's an address included
-    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
-    ASSERT_TRUE(ia);
-    checkIA_NAStatusCode(ia, STATUS_NoBinding);
-
-    lease = LeaseMgrFactory::instance().getLease6(addr);
-    ASSERT_TRUE(lease);
-    // Verify that the lease was not updated.
-    EXPECT_EQ(123, lease->cltt_);
+    testRenewReject(Lease::TYPE_NA, IOAddress("2001:db8:1:1::dead"));
+}
 
-    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
+// This test verifies that incoming (invalid) RENEW with a prefix
+// can be handled properly.
+//
+// This test checks 3 scenarios:
+// 1. there is no such lease at all
+// 2. there is such a lease, but it is assigned to a different IAID
+// 3. there is such a lease, but it belongs to a different client
+//
+// expected:
+// - returned REPLY message has copy of client-id
+// - returned REPLY message has server-id
+// - returned REPLY message has IA_PD that includes STATUS-CODE
+// - No lease in LeaseMgr
+TEST_F(Dhcpv6SrvTest, pdRenewReject) {
+    testRenewReject(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"));
 }
 
-// This test verifies that incoming (positive) RELEASE can be handled properly,
-// that a REPLY is generated, that the response has status code and that the
-// lease is indeed removed from the database.
+// This test verifies that incoming (positive) RELEASE with address can be
+// handled properly, that a REPLY is generated, that the response has status
+// code and that the lease is indeed removed from the database.
 //
 // expected:
 // - returned REPLY message has copy of client-id
 // - returned REPLY message has server-id
-// - returned REPLY message has IA that does not include an IAADDR
+// - returned REPLY message has IA_NA that does not include an IAADDR
 // - lease is actually removed from LeaseMgr
 TEST_F(Dhcpv6SrvTest, ReleaseBasic) {
-    NakedDhcpv6Srv srv(0);
-
-    const IOAddress addr("2001:db8:1:1::cafe:babe");
-    const uint32_t iaid = 234;
-
-    // Generate client-id also duid_
-    OptionPtr clientid = generateClientId();
-
-    // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(addr));
-
-    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
-    // value on purpose. They should be updated during RENEW.
-    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid_, iaid,
-                               501, 502, 503, 504, subnet_->getID(), 0));
-    lease->cltt_ = 1234;
-    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
-
-    // Check that the lease is really in the database
-    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(addr);
-    ASSERT_TRUE(l);
-
-    // Let's create a RELEASE
-    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
-    req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
-
-    OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
-    ia->addOption(released_addr_opt);
-    req->addOption(ia);
-    req->addOption(clientid);
-
-    // Server-id is mandatory in RELEASE
-    req->addOption(srv.getServerID());
-
-    // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRelease(req);
-
-    // Check if we get response at all
-    checkResponse(reply, DHCPV6_REPLY, 1234);
-
-    OptionPtr tmp = reply->getOption(D6O_IA_NA);
-    ASSERT_TRUE(tmp);
-
-    // Check that IA_NA was returned and that there's an address included
-    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
-    checkIA_NAStatusCode(ia, STATUS_Success);
-    checkMsgStatusCode(reply, STATUS_Success);
-
-    // There should be no address returned in RELEASE (see RFC3315, 18.2.6)
-    EXPECT_FALSE(tmp->getOption(D6O_IAADDR));
-
-    // Check DUIDs
-    checkServerId(reply, srv.getServerID());
-    checkClientId(reply, clientid);
-
-    // Check that the lease is really gone in the database
-    // get lease by address
-    l = LeaseMgrFactory::instance().getLease6(addr);
-    ASSERT_FALSE(l);
+    testReleaseBasic(Lease::TYPE_NA, IOAddress("2001:db8:1:1::cafe:babe"),
+                     IOAddress("2001:db8:1:1::cafe:babe"));
+}
 
-    // get lease by subnetid/duid/iaid combination
-    l = LeaseMgrFactory::instance().getLease6(*duid_, iaid, subnet_->getID());
-    ASSERT_FALSE(l);
+// This test verifies that incoming (positive) RELEASE with prefix can be
+// handled properly, that a REPLY is generated, that the response has
+// status code and that the lease is indeed removed from the database.
+//
+// expected:
+// - returned REPLY message has copy of client-id
+// - returned REPLY message has server-id
+// - returned REPLY message has IA_PD that does not include an IAPREFIX
+// - lease is actually removed from LeaseMgr
+TEST_F(Dhcpv6SrvTest, pdReleaseBasic) {
+    testReleaseBasic(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"),
+                     IOAddress("2001:db8:1:2::"));
 }
 
-// This test verifies that incoming (invalid) RELEASE can be handled properly.
+// This test verifies that incoming (invalid) RELEASE with an address
+// can be handled properly.
 //
 // This test checks 3 scenarios:
 // 1. there is no such lease at all
@@ -1283,107 +1209,27 @@ TEST_F(Dhcpv6SrvTest, ReleaseBasic) {
 // expected:
 // - returned REPLY message has copy of client-id
 // - returned REPLY message has server-id
-// - returned REPLY message has IA that includes STATUS-CODE
+// - returned REPLY message has IA_NA that includes STATUS-CODE
 // - No lease in LeaseMgr
 TEST_F(Dhcpv6SrvTest, ReleaseReject) {
+    testReleaseReject(Lease::TYPE_NA, IOAddress("2001:db8:1:1::dead"));
+}
 
-    NakedDhcpv6Srv srv(0);
-
-    const IOAddress addr("2001:db8:1:1::dead");
-    const uint32_t transid = 1234;
-    const uint32_t valid_iaid = 234;
-    const uint32_t bogus_iaid = 456;
-
-    // Quick sanity check that the address we're about to use is ok
-    ASSERT_TRUE(subnet_->inPool(addr));
-
-    // GenerateClientId() also sets duid_
-    OptionPtr clientid = generateClientId();
-
-    // Check that the lease is NOT in the database
-    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(addr);
-    ASSERT_FALSE(l);
-
-    // Let's create a RELEASE
-    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, transid));
-    req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(bogus_iaid, 1500, 3000);
-
-    OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
-    ia->addOption(released_addr_opt);
-    req->addOption(ia);
-    req->addOption(clientid);
-
-    // Server-id is mandatory in RENEW
-    req->addOption(srv.getServerID());
-
-    // Case 1: No lease known to server
-    SCOPED_TRACE("CASE 1: No lease known to server");
-
-    // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRelease(req);
-
-    // Check if we get response at all
-    checkResponse(reply, DHCPV6_REPLY, transid);
-    OptionPtr tmp = reply->getOption(D6O_IA_NA);
-    ASSERT_TRUE(tmp);
-    // Check that IA_NA was returned and that there's an address included
-    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
-    ASSERT_TRUE(ia);
-    checkIA_NAStatusCode(ia, STATUS_NoBinding);
-    checkMsgStatusCode(reply, STATUS_NoBinding);
-
-    // Check that the lease is not there
-    l = LeaseMgrFactory::instance().getLease6(addr);
-    ASSERT_FALSE(l);
-
-    // CASE 2: Lease is known and belongs to this client, but to a different IAID
-    SCOPED_TRACE("CASE 2: Lease is known and belongs to this client, but to a different IAID");
-
-    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid_, valid_iaid,
-                               501, 502, 503, 504, subnet_->getID(), 0));
-    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
-
-    // Pass it to the server and hope for a REPLY
-    reply = srv.processRelease(req);
-    checkResponse(reply, DHCPV6_REPLY, transid);
-    tmp = reply->getOption(D6O_IA_NA);
-    ASSERT_TRUE(tmp);
-    // Check that IA_NA was returned and that there's an address included
-    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
-    ASSERT_TRUE(ia);
-    checkIA_NAStatusCode(ia, STATUS_NoBinding);
-    checkMsgStatusCode(reply, STATUS_NoBinding);
-
-    // Check that the lease is still there
-    l = LeaseMgrFactory::instance().getLease6(addr);
-    ASSERT_TRUE(l);
-
-    // CASE 3: Lease belongs to a client with different client-id
-    SCOPED_TRACE("CASE 3: Lease belongs to a client with different client-id");
-
-    req->delOption(D6O_CLIENTID);
-    ia = boost::dynamic_pointer_cast<Option6IA>(req->getOption(D6O_IA_NA));
-    ia->setIAID(valid_iaid); // Now iaid in renew matches that in leasemgr
-    req->addOption(generateClientId(13)); // generate different DUID
-                                          // (with length 13)
-
-    reply = srv.processRelease(req);
-    checkResponse(reply, DHCPV6_REPLY, transid);
-    tmp = reply->getOption(D6O_IA_NA);
-    ASSERT_TRUE(tmp);
-    // Check that IA_NA was returned and that there's an address included
-    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
-    ASSERT_TRUE(ia);
-    checkIA_NAStatusCode(ia, STATUS_NoBinding);
-    checkMsgStatusCode(reply, STATUS_NoBinding);
-
-    // Check that the lease is still there
-    l = LeaseMgrFactory::instance().getLease6(addr);
-    ASSERT_TRUE(l);
-
-    // Finally, let's cleanup the database
-    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
+// This test verifies that incoming (invalid) RELEASE with a prefix
+// can be handled properly.
+//
+// This test checks 3 scenarios:
+// 1. there is no such lease at all
+// 2. there is such a lease, but it is assigned to a different IAID
+// 3. there is such a lease, but it belongs to a different client
+//
+// expected:
+// - returned REPLY message has copy of client-id
+// - returned REPLY message has server-id
+// - returned REPLY message has IA_PD that includes STATUS-CODE
+// - No lease in LeaseMgr
+TEST_F(Dhcpv6SrvTest, pdReleaseReject) {
+    testReleaseReject(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"));
 }
 
 // This test verifies if the status code option is generated properly.
@@ -2099,6 +1945,353 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
 
 }
 
+// Checks if server responses are sent to the proper port.
+TEST_F(Dhcpv6SrvTest, portsDirectTraffic) {
+
+    NakedDhcpv6Srv srv(0);
+
+    // Let's create a simple SOLICIT
+    Pkt6Ptr sol = captureSimpleSolicit();
+
+    // Simulate that we have received that traffic
+    srv.fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive6(), it will read all packets from the list set by
+    // fakeReceive()
+    srv.run();
+
+    // Get Advertise...
+    ASSERT_FALSE(srv.fake_sent_.empty());
+    Pkt6Ptr adv = srv.fake_sent_.front();
+    ASSERT_TRUE(adv);
+
+    // This is sent back to client directly, should be port 546
+    EXPECT_EQ(DHCP6_CLIENT_PORT, adv->getRemotePort());
+}
+
+// Checks if server responses are sent to the proper port.
+TEST_F(Dhcpv6SrvTest, portsRelayedTraffic) {
+
+    NakedDhcpv6Srv srv(0);
+
+    // Let's create a simple SOLICIT
+    Pkt6Ptr sol = captureRelayedSolicit();
+
+    // Simulate that we have received that traffic
+    srv.fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive6(), it will read all packets from the list set by
+    // fakeReceive()
+    srv.run();
+
+    // Get Advertise...
+    ASSERT_FALSE(srv.fake_sent_.empty());
+    Pkt6Ptr adv = srv.fake_sent_.front();
+    ASSERT_TRUE(adv);
+
+    // This is sent back to relay, so port is 547
+    EXPECT_EQ(DHCP6_SERVER_PORT, adv->getRemotePort());
+}
+
+// Checks if server is able to handle a relayed traffic from DOCSIS3.0 modems
+// @todo Uncomment this test as part of #3180 work.
+// Kea code currently fails to handle docsis traffic.
+TEST_F(Dhcpv6SrvTest, docsisTraffic) {
+
+    NakedDhcpv6Srv srv(0);
+
+    // Let's get a traffic capture from DOCSIS3.0 modem
+    Pkt6Ptr sol = captureDocsisRelayedSolicit();
+
+    // Simulate that we have received that traffic
+    srv.fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive6(), it will read all packets from the list set by
+    // fakeReceive()
+    srv.run();
+
+    // We should have an Advertise in response
+    ASSERT_FALSE(srv.fake_sent_.empty());
+    Pkt6Ptr adv = srv.fake_sent_.front();
+    ASSERT_TRUE(adv);
+}
+
+// Checks if server is able to handle a relayed traffic from DOCSIS3.0 modems
+TEST_F(Dhcpv6SrvTest, docsisVendorOptionsParse) {
+
+    // Let's get a traffic capture from DOCSIS3.0 modem
+    Pkt6Ptr sol = captureDocsisRelayedSolicit();
+    EXPECT_NO_THROW(sol->unpack());
+
+    // Check if the packet contain
+    OptionPtr opt = sol->getOption(D6O_VENDOR_OPTS);
+    ASSERT_TRUE(opt);
+
+    boost::shared_ptr<OptionVendor> vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+    ASSERT_TRUE(vendor);
+
+    EXPECT_TRUE(vendor->getOption(DOCSIS3_V6_ORO));
+    EXPECT_TRUE(vendor->getOption(36));
+    EXPECT_TRUE(vendor->getOption(35));
+    EXPECT_TRUE(vendor->getOption(DOCSIS3_V6_DEVICE_TYPE));
+    EXPECT_TRUE(vendor->getOption(3));
+    EXPECT_TRUE(vendor->getOption(4));
+    EXPECT_TRUE(vendor->getOption(5));
+    EXPECT_TRUE(vendor->getOption(6));
+    EXPECT_TRUE(vendor->getOption(7));
+    EXPECT_TRUE(vendor->getOption(8));
+    EXPECT_TRUE(vendor->getOption(9));
+    EXPECT_TRUE(vendor->getOption(DOCSIS3_V6_VENDOR_NAME));
+    EXPECT_TRUE(vendor->getOption(15));
+
+    EXPECT_FALSE(vendor->getOption(20));
+    EXPECT_FALSE(vendor->getOption(11));
+    EXPECT_FALSE(vendor->getOption(17));
+}
+
+// Checks if server is able to parse incoming docsis option and extract suboption 1 (docsis ORO)
+TEST_F(Dhcpv6SrvTest, docsisVendorORO) {
+
+    NakedDhcpv6Srv srv(0);
+
+    // Let's get a traffic capture from DOCSIS3.0 modem
+    Pkt6Ptr sol = captureDocsisRelayedSolicit();
+    ASSERT_NO_THROW(sol->unpack());
+
+    // Check if the packet contains vendor options option
+    OptionPtr opt = sol->getOption(D6O_VENDOR_OPTS);
+    ASSERT_TRUE(opt);
+
+    boost::shared_ptr<OptionVendor> vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+    ASSERT_TRUE(vendor);
+
+    opt = vendor->getOption(DOCSIS3_V6_ORO);
+    ASSERT_TRUE(opt);
+
+    OptionUint16ArrayPtr oro = boost::dynamic_pointer_cast<OptionUint16Array>(opt);
+    EXPECT_TRUE(oro);
+}
+
+// This test checks if Option Request Option (ORO) in docsis (vendor-id=4491)
+// vendor options is parsed correctly and the requested options are actually assigned.
+TEST_F(Dhcpv6SrvTest, vendorOptionsORO) {
+    ConstElementPtr x;
+    string config = "{ \"interfaces\": [ \"all\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "    \"option-def\": [ {"
+        "        \"name\": \"config-file\","
+        "        \"code\": 33,"
+        "        \"type\": \"string\","
+        "        \"array\": False,"
+        "        \"record-types\": \"\","
+        "        \"space\": \"vendor-4491\","
+        "        \"encapsulate\": \"\""
+        "     } ],"
+        "    \"option-data\": [ {"
+        "          \"name\": \"config-file\","
+        "          \"space\": \"vendor-4491\","
+        "          \"code\": 33,"
+        "          \"data\": \"normal_erouter_v6.cm\","
+        "          \"csv-format\": True"
+        "        }],"
+        "\"subnet6\": [ { "
+        "    \"pool\": [ \"2001:db8:1::/64\" ],"
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"renew-timer\": 1000, "
+        "    \"rebind-timer\": 1000, "
+        "    \"preferred-lifetime\": 3000,"
+        "    \"valid-lifetime\": 4000,"
+        "    \"interface-id\": \"\","
+        "    \"interface\": \"\""
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    NakedDhcpv6Srv srv(0);
+
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+
+    ASSERT_EQ(0, rcode_);
+
+    Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+    sol->setRemoteAddr(IOAddress("fe80::abcd"));
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
+    OptionPtr clientid = generateClientId();
+    sol->addOption(clientid);
+
+    // Pass it to the server and get an advertise
+    Pkt6Ptr adv = srv.processSolicit(sol);
+
+    // check if we get response at all
+    ASSERT_TRUE(adv);
+
+    // We did not include any vendor opts in SOLCIT, so there should be none
+    // in ADVERTISE.
+    ASSERT_FALSE(adv->getOption(D6O_VENDOR_OPTS));
+
+    // Let's add a vendor-option (vendor-id=4491) with a single sub-option.
+    // That suboption has code 1 and is a docsis ORO option.
+    boost::shared_ptr<OptionUint16Array> vendor_oro(new OptionUint16Array(Option::V6,
+                                                                          DOCSIS3_V6_ORO));
+    vendor_oro->addValue(DOCSIS3_V6_CONFIG_FILE); // Request option 33
+    OptionPtr vendor(new OptionVendor(Option::V6, 4491));
+    vendor->addOption(vendor_oro);
+    sol->addOption(vendor);
+
+    // Need to process SOLICIT again after requesting new option.
+    adv = srv.processSolicit(sol);
+    ASSERT_TRUE(adv);
+
+    // Check if thre is vendor option response
+    OptionPtr tmp = adv->getOption(D6O_VENDOR_OPTS);
+    ASSERT_TRUE(tmp);
+
+    // The response should be OptionVendor object
+    boost::shared_ptr<OptionVendor> vendor_resp =
+        boost::dynamic_pointer_cast<OptionVendor>(tmp);
+    ASSERT_TRUE(vendor_resp);
+
+    OptionPtr docsis33 = vendor_resp->getOption(33);
+    ASSERT_TRUE(docsis33);
+
+    OptionStringPtr config_file = boost::dynamic_pointer_cast<OptionString>(docsis33);
+    ASSERT_TRUE(config_file);
+    EXPECT_EQ("normal_erouter_v6.cm", config_file->getValue());
+}
+
+// Test checks whether it is possible to use option definitions defined in
+// src/lib/dhcp/docsis3_option_defs.h.
+TEST_F(Dhcpv6SrvTest, vendorOptionsDocsisDefinitions) {
+    ConstElementPtr x;
+    string config_prefix = "{ \"interfaces\": [ \"all\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "    \"option-data\": [ {"
+        "          \"name\": \"config-file\","
+        "          \"space\": \"vendor-4491\","
+        "          \"code\": ";
+    string config_postfix = ","
+        "          \"data\": \"normal_erouter_v6.cm\","
+        "          \"csv-format\": True"
+        "        }],"
+        "\"subnet6\": [ { "
+        "    \"pool\": [ \"2001:db8:1::/64\" ],"
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"renew-timer\": 1000, "
+        "    \"rebind-timer\": 1000, "
+        "    \"preferred-lifetime\": 3000,"
+        "    \"valid-lifetime\": 4000,"
+        "    \"interface-id\": \"\","
+        "    \"interface\": \"\""
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    // There is docsis3 (vendor-id=4491) vendor option 33, which is a
+    // config-file. Its format is a single string.
+    string config_valid = config_prefix + "33" + config_postfix;
+
+    // There is no option 99 defined in vendor-id=4491. As there is no
+    // definition, the config should fail.
+    string config_bogus = config_prefix + "99" + config_postfix;
+
+    ElementPtr json_bogus = Element::fromJSON(config_bogus);
+    ElementPtr json_valid = Element::fromJSON(config_valid);
+
+    NakedDhcpv6Srv srv(0);
+
+    // This should fail (missing option definition)
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv, json_bogus));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    ASSERT_EQ(1, rcode_);
+
+    // This should work (option definition present)
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv, json_valid));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    ASSERT_EQ(0, rcode_);
+}
+
+// This test verifies that the following option structure can be parsed:
+// - option (option space 'foobar')
+//   - sub option (option space 'foo')
+//      - sub option (option space 'bar')
+TEST_F(Dhcpv6SrvTest, unpackOptions) {
+    // Create option definition for each level of encapsulation. Each option
+    // definition is for the option code 1. Options may have the same
+    // option code because they belong to different option spaces.
+
+    // Top level option encapsulates options which belong to 'space-foo'.
+    OptionDefinitionPtr opt_def(new OptionDefinition("option-foobar", 1, "uint32",
+                                                      "space-foo"));\
+    // Middle option encapsulates options which belong to 'space-bar'
+    OptionDefinitionPtr opt_def2(new OptionDefinition("option-foo", 1, "uint16",
+                                                      "space-bar"));
+    // Low level option doesn't encapsulate any option space.
+    OptionDefinitionPtr opt_def3(new OptionDefinition("option-bar", 1,
+                                                      "uint8"));
+
+    // Add option definitions to the Configuration Manager. Each goes under
+    // different option space.
+    CfgMgr& cfgmgr = CfgMgr::instance();
+    ASSERT_NO_THROW(cfgmgr.addOptionDef(opt_def, "space-foobar"));
+    ASSERT_NO_THROW(cfgmgr.addOptionDef(opt_def2, "space-foo"));
+    ASSERT_NO_THROW(cfgmgr.addOptionDef(opt_def3, "space-bar"));
+
+    // Create the buffer holding the structure of options.
+    const char raw_data[] = {
+        // First option starts here.
+        0x00, 0x01,   // option code = 1
+        0x00, 0x0F,   // option length = 15
+        0x00, 0x01, 0x02, 0x03, // This option carries uint32 value
+        // Sub option starts here.
+        0x00, 0x01,  // option code = 1
+        0x00, 0x07,  // option length = 7
+        0x01, 0x02,  // this option carries uint16 value
+        // Last option starts here.
+        0x00, 0x01,  // option code = 1
+        0x00, 0x01,  // option length = 1
+        0x00 // This option carries a single uint8 value and has no sub options.
+    };
+    OptionBuffer buf(raw_data, raw_data + sizeof(raw_data));
+
+    // Parse options.
+    NakedDhcpv6Srv srv(0);
+    OptionCollection options;
+    ASSERT_NO_THROW(srv.unpackOptions(buf, "space-foobar", options, 0, 0));
+
+    // There should be one top level option.
+    ASSERT_EQ(1, options.size());
+    boost::shared_ptr<OptionInt<uint32_t> > option_foobar =
+        boost::dynamic_pointer_cast<OptionInt<uint32_t> >(options.begin()->
+                                                          second);
+    ASSERT_TRUE(option_foobar);
+    EXPECT_EQ(1, option_foobar->getType());
+    EXPECT_EQ(0x00010203, option_foobar->getValue());
+    // There should be a middle level option held in option_foobar.
+    boost::shared_ptr<OptionInt<uint16_t> > option_foo =
+        boost::dynamic_pointer_cast<OptionInt<uint16_t> >(option_foobar->
+                                                          getOption(1));
+    ASSERT_TRUE(option_foo);
+    EXPECT_EQ(1, option_foo->getType());
+    EXPECT_EQ(0x0102, option_foo->getValue());
+    // Finally, there should be a low level option under option_foo.
+    boost::shared_ptr<OptionInt<uint8_t> > option_bar =
+        boost::dynamic_pointer_cast<OptionInt<uint8_t> >(option_foo->getOption(1));
+    ASSERT_TRUE(option_bar);
+    EXPECT_EQ(1, option_bar->getType());
+    EXPECT_EQ(0x0, option_bar->getValue());
+}
+
 
 /// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
 /// to call processX() methods.
diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.cc b/src/bin/dhcp6/tests/dhcp6_test_utils.cc
new file mode 100644
index 0000000..93b6e44
--- /dev/null
+++ b/src/bin/dhcp6/tests/dhcp6_test_utils.cc
@@ -0,0 +1,557 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+#include <dhcp6/tests/dhcp6_test_utils.h>
+
+namespace isc {
+namespace test {
+
+// Checks that server response (ADVERTISE or REPLY) contains proper IA_NA option
+// It returns IAADDR option for each chaining with checkIAAddr method.
+boost::shared_ptr<Option6IAAddr>
+Dhcpv6SrvTest::checkIA_NA(const Pkt6Ptr& rsp, uint32_t expected_iaid,
+                          uint32_t expected_t1, uint32_t expected_t2) {
+    OptionPtr tmp = rsp->getOption(D6O_IA_NA);
+    // Can't use ASSERT_TRUE() in method that returns something
+    if (!tmp) {
+        ADD_FAILURE() << "IA_NA option not present in response";
+        return (boost::shared_ptr<Option6IAAddr>());
+    }
+
+    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+    if (!ia) {
+        ADD_FAILURE() << "IA_NA cannot convert option ptr to Option6";
+        return (boost::shared_ptr<Option6IAAddr>());
+    }
+
+    EXPECT_EQ(expected_iaid, ia->getIAID());
+    EXPECT_EQ(expected_t1, ia->getT1());
+    EXPECT_EQ(expected_t2, ia->getT2());
+
+    tmp = ia->getOption(D6O_IAADDR);
+    boost::shared_ptr<Option6IAAddr> addr = boost::dynamic_pointer_cast<Option6IAAddr>(tmp);
+    return (addr);
+}
+
+boost::shared_ptr<Option6IAPrefix>
+Dhcpv6SrvTest::checkIA_PD(const Pkt6Ptr& rsp, uint32_t expected_iaid,
+                          uint32_t expected_t1, uint32_t expected_t2) {
+    OptionPtr tmp = rsp->getOption(D6O_IA_PD);
+    // Can't use ASSERT_TRUE() in method that returns something
+    if (!tmp) {
+        ADD_FAILURE() << "IA_PD option not present in response";
+        return (boost::shared_ptr<Option6IAPrefix>());
+    }
+
+    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+    if (!ia) {
+        ADD_FAILURE() << "IA_PD cannot convert option ptr to Option6";
+        return (boost::shared_ptr<Option6IAPrefix>());
+    }
+
+    EXPECT_EQ(expected_iaid, ia->getIAID());
+    EXPECT_EQ(expected_t1, ia->getT1());
+    EXPECT_EQ(expected_t2, ia->getT2());
+
+    tmp = ia->getOption(D6O_IAPREFIX);
+    boost::shared_ptr<Option6IAPrefix> addr = boost::dynamic_pointer_cast<Option6IAPrefix>(tmp);
+    return (addr);
+}
+
+// Checks if the lease sent to client is present in the database
+// and is valid when checked agasint the configured subnet
+Lease6Ptr
+Dhcpv6SrvTest::checkLease(const DuidPtr& duid, const OptionPtr& ia_na,
+                          boost::shared_ptr<Option6IAAddr> addr) {
+    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(ia_na);
+
+    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                            addr->getAddress());
+    if (!lease) {
+        std::cout << "Lease for " << addr->getAddress().toText()
+                  << " not found in the database backend.";
+        return (Lease6Ptr());
+    }
+
+    EXPECT_EQ(addr->getAddress().toText(), lease->addr_.toText());
+    EXPECT_TRUE(*lease->duid_ == *duid);
+    EXPECT_EQ(ia->getIAID(), lease->iaid_);
+    EXPECT_EQ(subnet_->getID(), lease->subnet_id_);
+
+    return (lease);
+}
+
+Lease6Ptr
+Dhcpv6SrvTest::checkPdLease(const DuidPtr& duid, const OptionPtr& ia_pd,
+                            boost::shared_ptr<Option6IAPrefix> prefix){
+    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(ia_pd);
+
+    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD,
+                                                            prefix->getAddress());
+    if (!lease) {
+        std::cout << "PD lease for " << prefix->getAddress().toText()
+                  << " not found in the database backend.";
+        return (Lease6Ptr());
+    }
+
+    EXPECT_EQ(prefix->getAddress().toText(), lease->addr_.toText());
+    EXPECT_TRUE(*lease->duid_ == *duid);
+    EXPECT_EQ(ia->getIAID(), lease->iaid_);
+    EXPECT_EQ(subnet_->getID(), lease->subnet_id_);
+
+    return (lease);
+}
+
+
+Pkt6Ptr
+Dhcpv6SrvTest::createMessage(uint8_t message_type, Lease::Type lease_type,
+                             const IOAddress& addr, const uint8_t prefix_len,
+                             uint32_t iaid) {
+    // Let's create a RENEW
+    Pkt6Ptr msg = Pkt6Ptr(new Pkt6(message_type, 1234));
+    msg->setRemoteAddr(IOAddress("fe80::abcd"));
+
+    uint16_t code;
+    OptionPtr subopt;
+    switch (lease_type) {
+    case Lease::TYPE_NA:
+        code = D6O_IA_NA;
+        subopt.reset(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
+        break;
+    case Lease::TYPE_PD:
+        code = D6O_IA_PD;
+        subopt.reset(new Option6IAPrefix(D6O_IAPREFIX, addr, prefix_len,
+                                         300, 500));
+        break;
+    default:
+        isc_throw(BadValue, "Invalid lease type specified");
+    }
+
+    boost::shared_ptr<Option6IA> ia = generateIA(code, iaid, 1500, 3000);
+
+    ia->addOption(subopt);
+    msg->addOption(ia);
+
+    return (msg);
+}
+
+void
+Dhcpv6SrvTest::testRenewBasic(Lease::Type type, const std::string& existing_addr,
+                              const std::string& renew_addr,
+                              const uint8_t prefix_len) {
+    NakedDhcpv6Srv srv(0);
+
+    const IOAddress existing(existing_addr);
+    const IOAddress renew(renew_addr);
+    const uint32_t iaid = 234;
+
+    // Generate client-id also duid_
+    OptionPtr clientid = generateClientId();
+
+    // Check that the address we are about to use is indeed in pool
+    ASSERT_TRUE(subnet_->inPool(type, existing));
+
+    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
+    // value on purpose. They should be updated during RENEW.
+    Lease6Ptr lease(new Lease6(type, existing, duid_, iaid, 501, 502, 503, 504,
+                               subnet_->getID(), prefix_len));
+    lease->cltt_ = 1234;
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+    // Check that the lease is really in the database
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(type, existing);
+    ASSERT_TRUE(l);
+
+    // Check that T1, T2, preferred, valid and cltt really set and not using
+    // previous (500, 501, etc.) values
+    EXPECT_NE(l->t1_, subnet_->getT1());
+    EXPECT_NE(l->t2_, subnet_->getT2());
+    EXPECT_NE(l->preferred_lft_, subnet_->getPreferred());
+    EXPECT_NE(l->valid_lft_, subnet_->getValid());
+    EXPECT_NE(l->cltt_, time(NULL));
+
+    Pkt6Ptr req = createMessage(DHCPV6_RENEW, type, IOAddress(renew_addr),
+                                prefix_len, iaid);
+    req->addOption(clientid);
+    req->addOption(srv.getServerID());
+
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRenew(req);
+
+    // Check if we get response at all
+    checkResponse(reply, DHCPV6_REPLY, 1234);
+
+    // Check DUIDs
+    checkServerId(reply, srv.getServerID());
+    checkClientId(reply, clientid);
+
+    switch (type) {
+    case Lease::TYPE_NA: {
+        // Check that IA_NA was returned and that there's an address included
+        boost::shared_ptr<Option6IAAddr>
+          addr_opt = checkIA_NA(reply, 234, subnet_->getT1(), subnet_->getT2());
+
+        ASSERT_TRUE(addr_opt);
+
+        // Check that we've got the address we requested
+        checkIAAddr(addr_opt, renew, Lease::TYPE_NA);
+
+        // Check that the lease is really in the database
+        l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr_opt);
+        ASSERT_TRUE(l);
+        break;
+    }
+
+    case Lease::TYPE_PD: {
+        // Check that IA_NA was returned and that there's an address included
+        boost::shared_ptr<Option6IAPrefix> prefix_opt
+            = checkIA_PD(reply, 234, subnet_->getT1(), subnet_->getT2());
+
+        ASSERT_TRUE(prefix_opt);
+
+        // Check that we've got the address we requested
+        checkIAAddr(prefix_opt, renew, Lease::TYPE_PD);
+        EXPECT_EQ(pd_pool_->getLength(), prefix_opt->getLength());
+
+        // Check that the lease is really in the database
+        l = checkLease(duid_, reply->getOption(D6O_IA_PD), prefix_opt);
+        ASSERT_TRUE(l);
+        break;
+    }
+    default:
+        isc_throw(BadValue, "Invalid lease type");
+    }
+
+    // Check that T1, T2, preferred, valid and cltt were really updated
+    EXPECT_EQ(l->t1_, subnet_->getT1());
+    EXPECT_EQ(l->t2_, subnet_->getT2());
+    EXPECT_EQ(l->preferred_lft_, subnet_->getPreferred());
+    EXPECT_EQ(l->valid_lft_, subnet_->getValid());
+
+    // Checking for CLTT is a bit tricky if we want to avoid off by 1 errors
+    int32_t cltt = static_cast<int32_t>(l->cltt_);
+    int32_t expected = static_cast<int32_t>(time(NULL));
+    // equality or difference by 1 between cltt and expected is ok.
+    EXPECT_GE(1, abs(cltt - expected));
+
+    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(renew_addr));
+}
+
+void
+Dhcpv6SrvTest::testRenewReject(Lease::Type type, const IOAddress& addr) {
+
+    NakedDhcpv6Srv srv(0);
+
+    const uint32_t transid = 1234;
+    const uint32_t valid_iaid = 234;
+    const uint32_t bogus_iaid = 456;
+
+    uint32_t code;
+    uint8_t prefix_len;
+    if (type == Lease::TYPE_NA) {
+        code = D6O_IA_NA;
+        prefix_len = 128;
+    } else if (type == Lease::TYPE_PD) {
+        code = D6O_IA_PD;
+        prefix_len = pd_pool_->getLength();
+    } else {
+        isc_throw(BadValue, "Invalid lease type");
+    }
+
+    // Quick sanity check that the address we're about to use is ok
+    ASSERT_TRUE(subnet_->inPool(type, addr));
+
+    // GenerateClientId() also sets duid_
+    OptionPtr clientid = generateClientId();
+
+    // Check that the lease is NOT in the database
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(type, addr);
+    ASSERT_FALSE(l);
+
+    // Let's create a RENEW
+    Pkt6Ptr req = createMessage(DHCPV6_RENEW, type, IOAddress(addr), prefix_len,
+                                bogus_iaid);
+    req->addOption(clientid);
+    req->addOption(srv.getServerID());
+
+    // Case 1: No lease known to server
+
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRenew(req);
+
+    // Check if we get response at all
+    checkResponse(reply, DHCPV6_REPLY, transid);
+    OptionPtr tmp = reply->getOption(code);
+    ASSERT_TRUE(tmp);
+
+    // Check that IA_?? was returned and that there's proper status code
+    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+    ASSERT_TRUE(ia);
+    checkIA_NAStatusCode(ia, STATUS_NoBinding);
+
+    // Check that there is no lease added
+    l = LeaseMgrFactory::instance().getLease6(type, addr);
+    ASSERT_FALSE(l);
+
+    // CASE 2: Lease is known and belongs to this client, but to a different IAID
+
+    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
+    // value on purpose. They should be updated during RENEW.
+    Lease6Ptr lease(new Lease6(type, addr, duid_, valid_iaid,
+                               501, 502, 503, 504, subnet_->getID(),
+                               prefix_len));
+    lease->cltt_ = 123; // Let's use it as an indicator that the lease
+                        // was NOT updated.
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+    // Pass it to the server and hope for a REPLY
+    reply = srv.processRenew(req);
+    checkResponse(reply, DHCPV6_REPLY, transid);
+    tmp = reply->getOption(code);
+    ASSERT_TRUE(tmp);
+
+    // Check that IA_?? was returned and that there's proper status code
+    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+    ASSERT_TRUE(ia);
+    checkIA_NAStatusCode(ia, STATUS_NoBinding);
+
+    // There is a iaid mis-match, so server should respond that there is
+    // no such address to renew.
+
+    // CASE 3: Lease belongs to a client with different client-id
+    req->delOption(D6O_CLIENTID);
+    ia = boost::dynamic_pointer_cast<Option6IA>(req->getOption(code));
+    ia->setIAID(valid_iaid); // Now iaid in renew matches that in leasemgr
+    req->addOption(generateClientId(13)); // generate different DUID
+                                          // (with length 13)
+
+    reply = srv.processRenew(req);
+    checkResponse(reply, DHCPV6_REPLY, transid);
+    tmp = reply->getOption(code);
+    ASSERT_TRUE(tmp);
+
+    // Check that IA_?? was returned and that there's proper status code
+    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+    ASSERT_TRUE(ia);
+    checkIA_NAStatusCode(ia, STATUS_NoBinding);
+
+    lease = LeaseMgrFactory::instance().getLease6(type, addr);
+    ASSERT_TRUE(lease);
+    // Verify that the lease was not updated.
+    EXPECT_EQ(123, lease->cltt_);
+
+    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
+}
+
+void
+Dhcpv6SrvTest::testReleaseBasic(Lease::Type type, const IOAddress& existing,
+                                const IOAddress& release_addr) {
+    NakedDhcpv6Srv srv(0);
+
+    const uint32_t iaid = 234;
+
+    uint32_t code; // option code of the container (IA_NA or IA_PD)
+    uint8_t prefix_len;
+    if (type == Lease::TYPE_NA) {
+        code = D6O_IA_NA;
+        prefix_len = 128;
+    } else if (type == Lease::TYPE_PD) {
+        code = D6O_IA_PD;
+        prefix_len = pd_pool_->getLength();
+    } else {
+        isc_throw(BadValue, "Invalid lease type");
+    }
+
+    // Generate client-id also duid_
+    OptionPtr clientid = generateClientId();
+
+    // Check that the address we are about to use is indeed in pool
+    ASSERT_TRUE(subnet_->inPool(type, existing));
+
+    // Let's prepopulate the database
+    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, existing, duid_, iaid,
+                               501, 502, 503, 504, subnet_->getID(),
+                               prefix_len));
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+    // Check that the lease is really in the database
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(type, existing);
+    ASSERT_TRUE(l);
+
+    // Let's create a RELEASE
+    Pkt6Ptr rel = createMessage(DHCPV6_RELEASE, type, release_addr, prefix_len,
+                                iaid);
+    rel->addOption(clientid);
+    rel->addOption(srv.getServerID());
+
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRelease(rel);
+
+    // Check if we get response at all
+    checkResponse(reply, DHCPV6_REPLY, 1234);
+
+    OptionPtr tmp = reply->getOption(code);
+    ASSERT_TRUE(tmp);
+
+    // Check that IA_NA was returned and that there's an address included
+    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+    checkIA_NAStatusCode(ia, STATUS_Success);
+    checkMsgStatusCode(reply, STATUS_Success);
+
+    // There should be no address returned in RELEASE (see RFC3315, 18.2.6)
+    // There should be no prefix
+    EXPECT_FALSE(tmp->getOption(D6O_IAADDR));
+    EXPECT_FALSE(tmp->getOption(D6O_IAPREFIX));
+
+    // Check DUIDs
+    checkServerId(reply, srv.getServerID());
+    checkClientId(reply, clientid);
+
+    // Check that the lease is really gone in the database
+    // get lease by address
+    l = LeaseMgrFactory::instance().getLease6(type, release_addr);
+    ASSERT_FALSE(l);
+
+    // get lease by subnetid/duid/iaid combination
+    l = LeaseMgrFactory::instance().getLease6(type, *duid_, iaid,
+                                              subnet_->getID());
+    ASSERT_FALSE(l);
+}
+
+void
+Dhcpv6SrvTest::testReleaseReject(Lease::Type type, const IOAddress& addr) {
+    NakedDhcpv6Srv srv(0);
+
+    const uint32_t transid = 1234;
+    const uint32_t valid_iaid = 234;
+    const uint32_t bogus_iaid = 456;
+
+    uint32_t code; // option code of the container (IA_NA or IA_PD)
+    uint8_t prefix_len;
+    if (type == Lease::TYPE_NA) {
+        code = D6O_IA_NA;
+        prefix_len = 128;
+    } else if (type == Lease::TYPE_PD) {
+        code = D6O_IA_PD;
+        prefix_len = pd_pool_->getLength();
+    } else {
+        isc_throw(BadValue, "Invalid lease type");
+    }
+
+    // Quick sanity check that the address we're about to use is ok
+    ASSERT_TRUE(subnet_->inPool(type, addr));
+
+    // GenerateClientId() also sets duid_
+    OptionPtr clientid = generateClientId();
+
+    // Check that the lease is NOT in the database
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(type, addr);
+    ASSERT_FALSE(l);
+
+    // Let's create a RELEASE
+    Pkt6Ptr rel = createMessage(DHCPV6_RELEASE, type, addr, prefix_len, valid_iaid);
+    rel->addOption(clientid);
+    rel->addOption(srv.getServerID());
+
+    // Case 1: No lease known to server
+    SCOPED_TRACE("CASE 1: No lease known to server");
+
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRelease(rel);
+
+    // Check if we get response at all
+    checkResponse(reply, DHCPV6_REPLY, transid);
+    OptionPtr tmp = reply->getOption(code);
+    ASSERT_TRUE(tmp);
+    // Check that IA_NA/IA_PD was returned and that there's status code in it
+    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+    ASSERT_TRUE(ia);
+    checkIA_NAStatusCode(ia, STATUS_NoBinding);
+    checkMsgStatusCode(reply, STATUS_NoBinding);
+
+    // Check that the lease is not there
+    l = LeaseMgrFactory::instance().getLease6(type, addr);
+    ASSERT_FALSE(l);
+
+    // CASE 2: Lease is known and belongs to this client, but to a different IAID
+    SCOPED_TRACE("CASE 2: Lease is known and belongs to this client, but to a different IAID");
+
+    Lease6Ptr lease(new Lease6(type, addr, duid_, valid_iaid, 501, 502, 503,
+                               504, subnet_->getID(), prefix_len));
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+    // Let's create a different RELEASE, with a bogus iaid
+    rel = createMessage(DHCPV6_RELEASE, type, addr, prefix_len, bogus_iaid);
+    rel->addOption(clientid);
+    rel->addOption(srv.getServerID());
+
+    // Pass it to the server and hope for a REPLY
+    reply = srv.processRelease(rel);
+    checkResponse(reply, DHCPV6_REPLY, transid);
+    tmp = reply->getOption(code);
+    ASSERT_TRUE(tmp);
+
+    // Check that IA_?? was returned and that there's proper status code
+    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+    ASSERT_TRUE(ia);
+    checkIA_NAStatusCode(ia, STATUS_NoBinding);
+    checkMsgStatusCode(reply, STATUS_NoBinding);
+
+    // Check that the lease is still there
+    l = LeaseMgrFactory::instance().getLease6(type, addr);
+    ASSERT_TRUE(l);
+
+    // CASE 3: Lease belongs to a client with different client-id
+    SCOPED_TRACE("CASE 3: Lease belongs to a client with different client-id");
+
+    rel->delOption(D6O_CLIENTID);
+    ia = boost::dynamic_pointer_cast<Option6IA>(rel->getOption(code));
+    ia->setIAID(valid_iaid); // Now iaid in renew matches that in leasemgr
+    rel->addOption(generateClientId(13)); // generate different DUID
+                                          // (with length 13)
+
+    reply = srv.processRelease(rel);
+    checkResponse(reply, DHCPV6_REPLY, transid);
+    tmp = reply->getOption(code);
+    ASSERT_TRUE(tmp);
+
+    // Check that IA_?? was returned and that there's proper status code
+    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+    ASSERT_TRUE(ia);
+    checkIA_NAStatusCode(ia, STATUS_NoBinding);
+    checkMsgStatusCode(reply, STATUS_NoBinding);
+
+    // Check that the lease is still there
+    l = LeaseMgrFactory::instance().getLease6(type, addr);
+    ASSERT_TRUE(l);
+
+    // Finally, let's cleanup the database
+    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
+}
+
+
+// Generate IA_NA option with specified parameters
+boost::shared_ptr<Option6IA>
+NakedDhcpv6SrvTest::generateIA(uint16_t type, uint32_t iaid, uint32_t t1,
+                               uint32_t t2) {
+    boost::shared_ptr<Option6IA> ia =
+        boost::shared_ptr<Option6IA>(new Option6IA(type, iaid));
+    ia->setT1(t1);
+    ia->setT2(t2);
+    return (ia);
+}
+
+}; // end of isc::test namespace
+}; // end of isc namespace
diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.h b/src/bin/dhcp6/tests/dhcp6_test_utils.h
index 5032857..2b6241e 100644
--- a/src/bin/dhcp6/tests/dhcp6_test_utils.h
+++ b/src/bin/dhcp6/tests/dhcp6_test_utils.h
@@ -16,11 +16,15 @@
 ///
 /// @brief  This file contains utility classes used for DHCPv6 server testing
 
+#ifndef DHCP6_TEST_UTILS_H
+#define DHCP6_TEST_UTILS_H
+
 #include <gtest/gtest.h>
 
 #include <dhcp/pkt6.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/option_custom.h>
 #include <dhcp/iface_mgr.h>
@@ -110,6 +114,7 @@ public:
     using Dhcpv6Srv::sanityCheck;
     using Dhcpv6Srv::loadServerID;
     using Dhcpv6Srv::writeServerID;
+    using Dhcpv6Srv::unpackOptions;
     using Dhcpv6Srv::name_change_reqs_;
 
     /// @brief packets we pretend to receive
@@ -145,14 +150,9 @@ public:
         valid_iface_ = ifaces.begin()->getName();
     }
 
-    // Generate IA_NA option with specified parameters
-    boost::shared_ptr<Option6IA> generateIA(uint32_t iaid, uint32_t t1, uint32_t t2) {
-        boost::shared_ptr<Option6IA> ia =
-            boost::shared_ptr<Option6IA>(new Option6IA(D6O_IA_NA, iaid));
-        ia->setT1(t1);
-        ia->setT2(t2);
-        return (ia);
-    }
+    // Generate IA_NA or IA_PD option with specified parameters
+    boost::shared_ptr<Option6IA> generateIA(uint16_t type, uint32_t iaid,
+                                            uint32_t t1, uint32_t t2);
 
     /// @brief generates interface-id option, based on text
     ///
@@ -317,55 +317,68 @@ class Dhcpv6SrvTest : public NakedDhcpv6SrvTest {
 public:
     /// Name of the server-id file (used in server-id tests)
 
-    // these are empty for now, but let's keep them around
+    /// @brief Constructor that initalizes a simple default configuration
+    ///
+    /// Sets up a single subnet6 with one pool for addresses and second
+    /// pool for prefixes.
     Dhcpv6SrvTest() {
         subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 48, 1000,
                                          2000, 3000, 4000));
-        pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:1::"), 64));
+        pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"),
+                                   64));
         subnet_->addPool(pool_);
 
         CfgMgr::instance().deleteSubnets6();
         CfgMgr::instance().addSubnet6(subnet_);
-    }
-
-    // Checks that server response (ADVERTISE or REPLY) contains proper IA_NA option
-    // It returns IAADDR option for each chaining with checkIAAddr method.
-    boost::shared_ptr<Option6IAAddr> checkIA_NA(const Pkt6Ptr& rsp, uint32_t expected_iaid,
-                                            uint32_t expected_t1, uint32_t expected_t2) {
-        OptionPtr tmp = rsp->getOption(D6O_IA_NA);
-        // Can't use ASSERT_TRUE() in method that returns something
-        if (!tmp) {
-            ADD_FAILURE() << "IA_NA option not present in response";
-            return (boost::shared_ptr<Option6IAAddr>());
-        }
 
-        boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
-        if (!ia) {
-            ADD_FAILURE() << "IA_NA cannot convert option ptr to Option6";
-            return (boost::shared_ptr<Option6IAAddr>());
-        }
+        // configure PD pool
+        pd_pool_ = Pool6Ptr(new Pool6(Lease::TYPE_PD,
+                                      IOAddress("2001:db8:1:2::"), 64, 80));
+        subnet_->addPool(pd_pool_);
+    }
 
-        EXPECT_EQ(expected_iaid, ia->getIAID());
-        EXPECT_EQ(expected_t1, ia->getT1());
-        EXPECT_EQ(expected_t2, ia->getT2());
+    /// @brief destructor
+    ///
+    /// Removes existing configuration.
+    ~Dhcpv6SrvTest() {
+        CfgMgr::instance().deleteSubnets6();
+    };
 
-        tmp = ia->getOption(D6O_IAADDR);
-        boost::shared_ptr<Option6IAAddr> addr = boost::dynamic_pointer_cast<Option6IAAddr>(tmp);
-        return (addr);
-    }
+    /// @brief Checks that server response (ADVERTISE or REPLY) contains proper
+    ///        IA_NA option
+    ///
+    /// @param rsp server's response
+    /// @param expected_iaid expected IAID value
+    /// @param expected_t1 expected T1 value
+    /// @param expected_t2 expected T2 value
+    /// @return IAADDR option for easy chaining with checkIAAddr method
+    boost::shared_ptr<Option6IAAddr>
+        checkIA_NA(const Pkt6Ptr& rsp, uint32_t expected_iaid,
+                   uint32_t expected_t1, uint32_t expected_t2);
+
+    /// @brief Checks that server response (ADVERTISE or REPLY) contains proper
+    ///        IA_PD option
+    ///
+    /// @param rsp server's response
+    /// @param expected_iaid expected IAID value
+    /// @param expected_t1 expected T1 value
+    /// @param expected_t2 expected T2 value
+    /// @return IAPREFIX option for easy chaining with checkIAAddr method
+    boost::shared_ptr<Option6IAPrefix>
+    checkIA_PD(const Pkt6Ptr& rsp, uint32_t expected_iaid,
+               uint32_t expected_t1, uint32_t expected_t2);
 
     // Check that generated IAADDR option contains expected address
     // and lifetime values match the configured subnet
     void checkIAAddr(const boost::shared_ptr<Option6IAAddr>& addr,
                      const IOAddress& expected_addr,
-                     uint32_t /* expected_preferred */,
-                     uint32_t /* expected_valid */) {
+                     Lease::Type type) {
 
         // Check that the assigned address is indeed from the configured pool.
         // Note that when comparing addresses, we compare the textual
         // representation. IOAddress does not support being streamed to
         // an ostream, which means it can't be used in EXPECT_EQ.
-        EXPECT_TRUE(subnet_->inPool(addr->getAddress()));
+        EXPECT_TRUE(subnet_->inPool(type, addr->getAddress()));
         EXPECT_EQ(expected_addr.toText(), addr->getAddress().toText());
         EXPECT_EQ(addr->getPreferred(), subnet_->getPreferred());
         EXPECT_EQ(addr->getValid(), subnet_->getValid());
@@ -374,34 +387,115 @@ public:
     // Checks if the lease sent to client is present in the database
     // and is valid when checked agasint the configured subnet
     Lease6Ptr checkLease(const DuidPtr& duid, const OptionPtr& ia_na,
-                         boost::shared_ptr<Option6IAAddr> addr) {
-        boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(ia_na);
-
-        Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(addr->getAddress());
-        if (!lease) {
-            std::cout << "Lease for " << addr->getAddress().toText()
-                      << " not found in the database backend.";
-            return (Lease6Ptr());
-        }
-
-        EXPECT_EQ(addr->getAddress().toText(), lease->addr_.toText());
-        EXPECT_TRUE(*lease->duid_ == *duid);
-        EXPECT_EQ(ia->getIAID(), lease->iaid_);
-        EXPECT_EQ(subnet_->getID(), lease->subnet_id_);
+                         boost::shared_ptr<Option6IAAddr> addr);
 
-        return (lease);
-    }
+    /// @brief Verifies received IAPrefix option
+    ///
+    /// Verifies if the received IAPrefix option matches the lease in the
+    /// database.
+    ///
+    /// @param duid client's DUID
+    /// @param ia_pd IA_PD option that contains the IAPRefix option
+    /// @param prefix pointer to the IAPREFIX option
+    /// @return corresponding IPv6 lease (if found)
+    Lease6Ptr checkPdLease(const DuidPtr& duid, const OptionPtr& ia_pd,
+                           boost::shared_ptr<Option6IAPrefix> prefix);
+
+    /// @brief Creates a message with specified IA
+    ///
+    /// A utility function that creates a message of the specified type with
+    /// a specified container (IA_NA or IA_PD) and an address or prefix
+    /// inside it.
+    ///
+    /// @param message_type type of the message (e.g. DHCPV6_SOLICIT)
+    /// @param lease_type type of a lease (TYPE_NA or TYPE_PD)
+    /// @param addr address or prefix to use in IADDRESS or IAPREFIX options
+    /// @param prefix_len length of the prefix (used for prefixes only)
+    /// @param iaid IA identifier (used in IA_XX option)
+    /// @return created message
+    Pkt6Ptr
+    createMessage(uint8_t message_type, Lease::Type lease_type,
+                  const IOAddress& addr, const uint8_t prefix_len,
+                  uint32_t iaid);
+
+    /// @brief Performs basic (positive) RENEW test
+    ///
+    /// See renewBasic and pdRenewBasic tests for detailed explanation.
+    /// In essence the test attempts to perform a successful RENEW scenario.
+    ///
+    /// This method does not throw, but uses gtest macros to signify failures.
+    ///
+    /// @param type type (TYPE_NA or TYPE_PD)
+    /// @param existing_addr address to be preinserted into the database
+    /// @param renew_addr address being sent in RENEW
+    /// @param prefix_len length of the prefix (128 for addresses)
+    void
+    testRenewBasic(Lease::Type type, const std::string& existing_addr,
+                   const std::string& renew_addr, const uint8_t prefix_len);
+
+    /// @brief Performs negative RENEW test
+    ///
+    /// See renewReject and pdRenewReject tests for detailed explanation.
+    /// In essence the test attempts to perform couple failed RENEW scenarios.
+    ///
+    /// This method does not throw, but uses gtest macros to signify failures.
+    ///
+    /// @param type type (TYPE_NA or TYPE_PD)
+    /// @param addr address being sent in RENEW
+    void
+    testRenewReject(Lease::Type type, const IOAddress& addr);
 
-    ~Dhcpv6SrvTest() {
-        CfgMgr::instance().deleteSubnets6();
-    };
+    /// @brief Performs basic (positive) RELEASE test
+    ///
+    /// See releaseBasic and pdReleaseBasic tests for detailed explanation.
+    /// In essence the test attempts to perform a successful RELEASE scenario.
+    ///
+    /// This method does not throw, but uses gtest macros to signify failures.
+    ///
+    /// @param type type (TYPE_NA or TYPE_PD)
+    /// @param existing address to be preinserted into the database
+    /// @param release_addr address being sent in RELEASE
+    void
+    testReleaseBasic(Lease::Type type, const IOAddress& existing,
+                     const IOAddress& release_addr);
+
+    /// @brief Performs negative RELEASE test
+    ///
+    /// See releaseReject and pdReleaseReject tests for detailed explanation.
+    /// In essence the test attempts to perform couple failed RELEASE scenarios.
+    ///
+    /// This method does not throw, but uses gtest macros to signify failures.
+    ///
+    /// @param type type (TYPE_NA or TYPE_PD)
+    /// @param addr address being sent in RELEASE
+    void
+    testReleaseReject(Lease::Type type, const IOAddress& addr);
+
+    // see wireshark.cc for descriptions
+    // The descriptions are too large and too closely related to the
+    // code, so it is kept in .cc rather than traditionally in .h
+    Pkt6Ptr captureSimpleSolicit();
+    Pkt6Ptr captureRelayedSolicit();
+    Pkt6Ptr captureDocsisRelayedSolicit();
+
+    /// @brief Auxiliary method that sets Pkt6 fields
+    ///
+    /// Used to reconstruct captured packets. Sets UDP ports, interface names,
+    /// and other fields to some believable values.
+    /// @param pkt packet that will have its fields set
+    void captureSetDefaultFields(const Pkt6Ptr& pkt);
 
     /// A subnet used in most tests
     Subnet6Ptr subnet_;
 
-    /// A pool used in most tests
+    /// A normal, non-temporary pool used in most tests
     Pool6Ptr pool_;
+
+    /// A prefix pool used in most tests
+    Pool6Ptr pd_pool_;
 };
 
 }; // end of isc::test namespace
 }; // end of isc namespace
+
+#endif // DHCP6_TEST_UTILS_H
diff --git a/src/bin/dhcp6/tests/hooks_unittest.cc b/src/bin/dhcp6/tests/hooks_unittest.cc
index b12374c..d4e8e5e 100644
--- a/src/bin/dhcp6/tests/hooks_unittest.cc
+++ b/src/bin/dhcp6/tests/hooks_unittest.cc
@@ -85,33 +85,6 @@ TEST_F(Dhcpv6SrvTest, Hooks) {
     EXPECT_TRUE(hook_index_lease6_release  > 0);
 }
 
-// This function returns buffer for very simple Solicit
-Pkt6* captureSimpleSolicit() {
-    Pkt6* pkt;
-    uint8_t data[] = {
-        1,  // type 1 = SOLICIT
-        0xca, 0xfe, 0x01, // trans-id = 0xcafe01
-        0, 1, // option type 1 (client-id)
-        0, 10, // option lenth 10
-        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // DUID
-        0, 3, // option type 3 (IA_NA)
-        0, 12, // option length 12
-        0, 0, 0, 1, // iaid = 1
-        0, 0, 0, 0, // T1 = 0
-        0, 0, 0, 0  // T2 = 0
-    };
-
-    pkt = new Pkt6(data, sizeof(data));
-    pkt->setRemotePort(546);
-    pkt->setRemoteAddr(IOAddress("fe80::1"));
-    pkt->setLocalPort(0);
-    pkt->setLocalAddr(IOAddress("ff02::1:2"));
-    pkt->setIndex(2);
-    pkt->setIface("eth0");
-
-    return (pkt);
-}
-
 /// @brief a class dedicated to Hooks testing in DHCPv6 server
 ///
 /// This class has a number of static members, because each non-static
@@ -951,7 +924,7 @@ TEST_F(HooksDhcpv6SrvTest, subnet6_select) {
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
     sol->setIface(valid_iface_);
-    sol->addOption(generateIA(234, 1500, 3000));
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
 
@@ -1019,7 +992,7 @@ TEST_F(HooksDhcpv6SrvTest, subnet_select_change) {
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
     sol->setIface(valid_iface_);
-    sol->addOption(generateIA(234, 1500, 3000));
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
 
@@ -1047,7 +1020,7 @@ TEST_F(HooksDhcpv6SrvTest, subnet_select_change) {
     // Advertised address must belong to the second pool (in subnet's range,
     // in dynamic pool)
     EXPECT_TRUE((*subnets)[1]->inRange(addr_opt->getAddress()));
-    EXPECT_TRUE((*subnets)[1]->inPool(addr_opt->getAddress()));
+    EXPECT_TRUE((*subnets)[1]->inPool(Lease::TYPE_NA, addr_opt->getAddress()));
 }
 
 // This test verifies that incoming (positive) RENEW can be handled properly,
@@ -1066,17 +1039,18 @@ TEST_F(HooksDhcpv6SrvTest, basic_lease6_renew) {
     OptionPtr clientid = generateClientId();
 
     // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(addr));
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr));
 
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // value on purpose. They should be updated during RENEW.
-    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid_, iaid,
+    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
                                501, 502, 503, 504, subnet_->getID(), 0));
     lease->cltt_ = 1234;
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
     // Check that the lease is really in the database
-    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(addr);
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                        addr);
     ASSERT_TRUE(l);
 
     // Check that T1, T2, preferred, valid and cltt really set and not using
@@ -1090,7 +1064,7 @@ TEST_F(HooksDhcpv6SrvTest, basic_lease6_renew) {
     // Let's create a RENEW
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
     OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(renewed_addr_opt);
@@ -1162,17 +1136,18 @@ TEST_F(HooksDhcpv6SrvTest, leaseUpdate_lease6_renew) {
     OptionPtr clientid = generateClientId();
 
     // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(addr));
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr));
 
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // value on purpose. They should be updated during RENEW.
-    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid_, iaid,
+    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
                                501, 502, 503, 504, subnet_->getID(), 0));
     lease->cltt_ = 1234;
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
     // Check that the lease is really in the database
-    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(addr);
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                        addr);
     ASSERT_TRUE(l);
 
     // Check that T1, T2, preferred, valid and cltt really set and not using
@@ -1186,7 +1161,7 @@ TEST_F(HooksDhcpv6SrvTest, leaseUpdate_lease6_renew) {
     // Let's create a RENEW
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
     OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(renewed_addr_opt);
@@ -1252,17 +1227,18 @@ TEST_F(HooksDhcpv6SrvTest, skip_lease6_renew) {
     OptionPtr clientid = generateClientId();
 
     // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(addr));
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr));
 
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // value on purpose. They should be updated during RENEW.
-    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid_, iaid,
+    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
                                501, 502, 503, 504, subnet_->getID(), 0));
     lease->cltt_ = 1234;
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
     // Check that the lease is really in the database
-    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(addr);
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                        addr);
     ASSERT_TRUE(l);
 
     // Check that T1, T2, preferred, valid and cltt really set and not using
@@ -1276,7 +1252,7 @@ TEST_F(HooksDhcpv6SrvTest, skip_lease6_renew) {
     // Let's create a RENEW
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
     OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(renewed_addr_opt);
@@ -1293,7 +1269,7 @@ TEST_F(HooksDhcpv6SrvTest, skip_lease6_renew) {
     // Check that our callback was called
     EXPECT_EQ("lease6_renew", callback_name_);
 
-    l = LeaseMgrFactory::instance().getLease6(addr);
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
 
     // Check that the old values are still there and they were not
     // updated by the renewal
@@ -1327,23 +1303,24 @@ TEST_F(HooksDhcpv6SrvTest, basic_lease6_release) {
     OptionPtr clientid = generateClientId();
 
     // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(addr));
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr));
 
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // value on purpose. They should be updated during RENEW.
-    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid_, iaid,
+    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
                                501, 502, 503, 504, subnet_->getID(), 0));
     lease->cltt_ = 1234;
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
     // Check that the lease is really in the database
-    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(addr);
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                        addr);
     ASSERT_TRUE(l);
 
     // Let's create a RELEASE
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
     OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(released_addr_opt);
@@ -1375,11 +1352,12 @@ TEST_F(HooksDhcpv6SrvTest, basic_lease6_release) {
 
     // Check that the lease is really gone in the database
     // get lease by address
-    l = LeaseMgrFactory::instance().getLease6(addr);
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
     ASSERT_FALSE(l);
 
     // Get lease by subnetid/duid/iaid combination
-    l = LeaseMgrFactory::instance().getLease6(*duid_, iaid, subnet_->getID());
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, *duid_, iaid,
+                                              subnet_->getID());
     ASSERT_FALSE(l);
 }
 
@@ -1406,23 +1384,24 @@ TEST_F(HooksDhcpv6SrvTest, skip_lease6_release) {
     OptionPtr clientid = generateClientId();
 
     // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(addr));
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr));
 
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // value on purpose. They should be updated during RENEW.
-    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid_, iaid,
+    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
                                501, 502, 503, 504, subnet_->getID(), 0));
     lease->cltt_ = 1234;
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
     // Check that the lease is really in the database
-    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(addr);
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                        addr);
     ASSERT_TRUE(l);
 
     // Let's create a RELEASE
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
     OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(released_addr_opt);
@@ -1442,11 +1421,13 @@ TEST_F(HooksDhcpv6SrvTest, skip_lease6_release) {
 
     // Check that the lease is still there
     // get lease by address
-    l = LeaseMgrFactory::instance().getLease6(addr);
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                              addr);
     ASSERT_TRUE(l);
 
     // Get lease by subnetid/duid/iaid combination
-    l = LeaseMgrFactory::instance().getLease6(*duid_, iaid, subnet_->getID());
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, *duid_, iaid,
+                                              subnet_->getID());
     ASSERT_TRUE(l);
 }
 
diff --git a/src/bin/dhcp6/tests/test_data_files_config.h.in b/src/bin/dhcp6/tests/test_data_files_config.h.in
new file mode 100644
index 0000000..8b09164
--- /dev/null
+++ b/src/bin/dhcp6/tests/test_data_files_config.h.in
@@ -0,0 +1,23 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @brief Path to DHCP6 source dir so tests against the dhcp6.spec file
+/// can find it reliably.
+
+#ifndef TEST_DATA_FILES_CONFIG_H
+#define TEST_DATA_FILES_CONFIG_H
+
+#define DHCP6_SRC_DIR "@abs_top_srcdir@/src/bin/dhcp6"
+
+#endif
diff --git a/src/bin/dhcp6/tests/test_libraries.h.in b/src/bin/dhcp6/tests/test_libraries.h.in
index 8b03dc2..f25d9e2 100644
--- a/src/bin/dhcp6/tests/test_libraries.h.in
+++ b/src/bin/dhcp6/tests/test_libraries.h.in
@@ -19,32 +19,18 @@
 
 namespace {
 
-
-// Take care of differences in DLL naming between operating systems.
-
-#ifdef OS_OSX
-#define DLL_SUFFIX ".dylib"
-
-#else
-#define DLL_SUFFIX ".so"
-
-#endif
-
-
 // Names of the libraries used in these tests.  These libraries are built using
 // libtool, so we need to look in the hidden ".libs" directory to locate the
 // shared library.
 
 // Library with load/unload functions creating marker files to check their
 // operation.
-const char* const CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1"
-                                           DLL_SUFFIX;
-const char* const CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2"
-                                           DLL_SUFFIX;
+const char* const CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1.so";
+const char* const CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2.so";
 
 // Name of a library which is not present.
-const char* const NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere"
-                                         DLL_SUFFIX;
+const char* const NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";
+
 } // anonymous namespace
 
 
diff --git a/src/bin/dhcp6/tests/wireshark.cc b/src/bin/dhcp6/tests/wireshark.cc
new file mode 100644
index 0000000..4e96548
--- /dev/null
+++ b/src/bin/dhcp6/tests/wireshark.cc
@@ -0,0 +1,163 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <dhcp6/tests/dhcp6_test_utils.h>
+#include <string>
+
+/// @file   wireshark.cc
+/// 
+/// @brief  contains packet captures imported from Wireshark
+/// 
+/// These are actual packets captured over wire. They are used in various
+/// tests.
+///
+/// The procedure to export Wireshark -> unit-tests is manual, but rather
+/// easy to follow:
+/// 1. Open a file in wireshark
+/// 2. Find the packet you want to export
+/// 3. There's a protocol stack (Frame, Ethernet, IPv6, UDP, DHCPv6, ...)
+/// 4. Right click on DHCPv6 -> Copy -> Bytes -> Hex Stream
+/// 5. Paste it as: string hex_string="[paste here]";
+/// 6. Coding guidelines line restrictions apply, so wrap your code as necessary
+/// 7. Make sure you decribe the capture appropriately
+/// 8. Follow whatever rest of the methods are doing (set ports, ifaces etc.)
+
+using namespace std;
+
+namespace isc {
+namespace test {
+
+void Dhcpv6SrvTest::captureSetDefaultFields(const Pkt6Ptr& pkt) {
+    pkt->setRemotePort(546);
+    pkt->setRemoteAddr(IOAddress("fe80::1"));
+    pkt->setLocalPort(0);
+    pkt->setLocalAddr(IOAddress("ff02::1:2"));
+    pkt->setIndex(2);
+    pkt->setIface("eth0");
+}
+
+// This function returns buffer for very simple Solicit
+Pkt6Ptr Dhcpv6SrvTest::captureSimpleSolicit() {
+    uint8_t data[] = {
+        1,  // type 1 = SOLICIT
+        0xca, 0xfe, 0x01, // trans-id = 0xcafe01
+        0, 1, // option type 1 (client-id)
+        0, 10, // option lenth 10
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // DUID
+        0, 3, // option type 3 (IA_NA)
+        0, 12, // option length 12
+        0, 0, 0, 1, // iaid = 1
+        0, 0, 0, 0, // T1 = 0
+        0, 0, 0, 0  // T2 = 0
+    };
+
+    Pkt6Ptr pkt(new Pkt6(data, sizeof(data)));
+    captureSetDefaultFields(pkt);
+
+    return (pkt);
+}
+
+Pkt6Ptr Dhcpv6SrvTest::captureRelayedSolicit() {
+
+    // This is a very simple relayed SOLICIT message:
+    // RELAY-FORW
+    // - interface-id
+    // - relay-message
+    //   - SOLICIT
+    //     - client-id
+    //     - IA_NA (iaid=1, t1=0, t2=0)
+    //     - ORO (7)
+
+    // string exported from Wireshark
+    string hex_string = 
+        "0c0500000000000000000000000000000000fc00000000000000000000000000000900"
+        "12000231350009002c010517100001000e0001000151b5e46208002758f1e80003000c"
+        "000000010000000000000000000600020007";
+
+    std::vector<uint8_t> bin;
+
+    // Decode the hex string and store it in bin (which happens
+    // to be OptionBuffer format)
+    isc::util::encode::decodeHex(hex_string, bin);
+
+    Pkt6Ptr pkt(new Pkt6(&bin[0], bin.size()));
+    captureSetDefaultFields(pkt);
+
+    return (pkt);
+}
+
+/// returns a buffer with relayed SOLICIT (from DOCSIS3.0 cable modem)
+Pkt6Ptr isc::test::Dhcpv6SrvTest::captureDocsisRelayedSolicit() {
+
+    // This is an actual DOCSIS packet
+    // RELAY-FORW (12) 
+    //  - Relay Message
+    //    - SOLICIT (1)
+    //      - client-id
+    //      - IA_NA (iaid=7f000788, t2=0, t2=0)
+    //        - IAAddress (::, pref=0,valid=0)
+    //      - rapid-commit
+    //      - ORO
+    //      - Reconfigure-accept
+    //      - Vendor-Class ("docsis3.0")
+    //      - Vendor-specific Info
+    //        - subopt 1: Option request = 32,33,34,37,38
+    //        - subopt 36: Device identifier
+    //        - subopt 35: TLV5
+    //        - subopt 2: Device type = ECM
+    //        - subopt 3: Embedded components
+    //        - subopt 4: Serial Number
+    //        - subopt 5: Hardware version
+    //        - subopt 6: Software version
+    //        - subopt 7: Boot ROM Version
+    //        - subopt 8: Organization Unique Identifier
+    //        - subopt 9: Model Number
+    //        - subopt 10: Vendor Name (Netgear)
+    //        - subopt 15: unknown
+    //  - Interface-Id
+    //  - Vendor-specific Information
+    //    - Suboption 1025: CMTS capabilities
+    //    - Suboption 1026: Cable Modem MAC addr = 10:0d:7f:00:07:88
+
+    // string exported from Wireshark
+    string hex_string = 
+        "0c002a0288fe00fe00015a8d09fffe7af955fe80000000000000120d7ffffe00078800"
+        "090189010d397f0001000a00030001100d7f000788000300287f000788000000000000"
+        "000000050018000000000000000000000000000000000000000000000000000e000000"
+        "0800020000000600020011001400000010000f0000118b0009646f63736973332e3000"
+        "1101200000118b0001000a0020002100220025002600240006100d7f00078800230081"
+        "0101010201030301010401010501010601010701180801080901000a01010b01180c01"
+        "010d0200400e0200100f01011004000000021101011301011401001501381601011701"
+        "011801041901041a01041b01281c01021d01081e01201f011020011821010222010123"
+        "010124011825010126020040270101120701100d7f00078a0002000345434d0003000b"
+        "45434d3a45524f555445520004000d3335463132395550303030353200050004332e31"
+        "310006000956312e30312e31315400070013505350552d426f6f7420312e302e31362e"
+        "323200080006303030393542000900084347343030305444000a00074e657467656172"
+        "000f000745524f5554455200120012427531264361312f3000100d7f00078800000011"
+        "00160000118b040100040102030004020006100d7f000788";
+
+    std::vector<uint8_t> bin;
+
+    // Decode the hex string and store it in bin (which happens
+    // to be OptionBuffer format)
+    isc::util::encode::decodeHex(hex_string, bin);
+
+    Pkt6Ptr pkt(new Pkt6(&bin[0], bin.size()));
+    captureSetDefaultFields(pkt);
+    return (pkt);
+}
+
+}; // end of isc::test namespace
+}; // end of isc namespace
diff --git a/src/bin/loadzone/loadzone.py.in b/src/bin/loadzone/loadzone.py.in
index 1203e45..955223a 100755
--- a/src/bin/loadzone/loadzone.py.in
+++ b/src/bin/loadzone/loadzone.py.in
@@ -23,6 +23,7 @@ from optparse import OptionParser
 from isc.dns import *
 from isc.datasrc import *
 import isc.util.process
+import isc.util.traceback_handler
 import isc.log
 from isc.log_messages.loadzone_messages import *
 from datetime import timedelta
@@ -351,11 +352,14 @@ class LoadZoneRunner:
             logger.error(LOADZONE_UNEXPECTED_FAILURE, ex)
         return 1
 
-if '__main__' == __name__:
+def main():
     runner = LoadZoneRunner(sys.argv[1:])
     ret = runner.run()
     sys.exit(ret)
 
+if '__main__' == __name__:
+    isc.util.traceback_handler.traceback_handler(main)
+
 ## Local Variables:
 ## mode: python
 ## End:
diff --git a/src/bin/memmgr/memmgr.py.in b/src/bin/memmgr/memmgr.py.in
index 4bf27fe..d1b93a8 100755
--- a/src/bin/memmgr/memmgr.py.in
+++ b/src/bin/memmgr/memmgr.py.in
@@ -29,9 +29,10 @@ from isc.log_messages.memmgr_messages import *
 from isc.server_common.bind10_server import BIND10Server, BIND10ServerFatal
 from isc.server_common.datasrc_clients_mgr \
     import DataSrcClientsMgr, ConfigError
-from isc.memmgr.datasrc_info import DataSrcInfo
+from isc.memmgr.datasrc_info import DataSrcInfo, SegmentInfo
 from isc.memmgr.builder import MemorySegmentBuilder
 import isc.util.process
+import isc.util.traceback_handler
 
 MODULE_NAME = 'memmgr'
 
@@ -124,18 +125,51 @@ class Memmgr(BIND10Server):
         # All copy, switch to the new configuration.
         self._config_params = new_config_params
 
-    def __notify_from_builder(self):
-        # Nothing is implemented here for now. This method should have
-        # code to handle responses from the builder in
-        # self._builder_response_queue[]. Access must be synchronized
-        # using self._builder_lock.
-        pass
+    def _cmd_to_builder(self, cmd):
+        """
+        Send a command to the builder, with proper synchronization.
+        """
+        assert isinstance(cmd, tuple)
+        with self._builder_cv:
+            self._builder_command_queue.append(cmd)
+            self._builder_cv.notify_all()
+
+    def _notify_from_builder(self):
+        """
+        Read the notifications from the builder thread.
+        """
+        self._master_sock.recv(1) # Clear the wake-up data
+        notifications = None
+        with self._builder_lock:
+            # Copy the notifications out and clear them from the
+            # original list. We may not assign [] to
+            # self._builder_response_queue to clear it, because there's
+            # another reference to it from the other thread and it would
+            # not keep the original list.
+            notifications = self._builder_response_queue[:]
+            del self._builder_response_queue[:]
+        for notification in notifications:
+            notif_name = notification[0]
+            if notif_name == 'load-completed':
+                (_, dsrc_info, rrclass, dsrc_name) = notification
+                sgmt_info = dsrc_info.segment_info_map[(rrclass, dsrc_name)]
+                cmd = sgmt_info.complete_update()
+                # It may return another load command on the same data source.
+                # If it is so, we execute it too, before we start
+                # synchronizing with the readers.
+                if cmd is not None:
+                    self._cmd_to_builder(cmd)
+                else:
+                    pass
+                    # TODO: Send to the readers, #2858
+            else:
+                raise ValueError('Unknown notification name: ' + notif_name)
 
     def __create_builder_thread(self):
         # We get responses from the builder thread on this socket pair.
         (self._master_sock, self._builder_sock) = \
             socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
-        self.watch_fileno(self._master_sock, rcallback=self.__notify_from_builder)
+        self.watch_fileno(self._master_sock, rcallback=self._notify_from_builder)
 
         # See the documentation for MemorySegmentBuilder on how the
         # following are used.
@@ -161,9 +195,7 @@ class Memmgr(BIND10Server):
 
         # This makes the MemorySegmentBuilder exit its main loop. It
         # should make the builder thread joinable.
-        with self._builder_cv:
-            self._builder_command_queue.append(('shutdown',))
-            self._builder_cv.notify_all()
+        self._cmd_to_builder(('shutdown',))
 
         self._builder_thread.join()
 
@@ -202,13 +234,28 @@ class Memmgr(BIND10Server):
             genid, clients_map = self._datasrc_clients_mgr.get_clients_map()
             datasrc_info = DataSrcInfo(genid, clients_map, self._config_params)
             self._datasrc_info_list.append(datasrc_info)
+            self._init_segments(datasrc_info)
 
             # Full datasrc reconfig will be rare, so would be worth logging
             # at the info level.
             logger.info(MEMMGR_DATASRC_RECONFIGURED, genid)
+
         except isc.server_common.datasrc_clients_mgr.ConfigError as ex:
             logger.error(MEMMGR_DATASRC_CONFIG_ERROR, ex)
 
-if '__main__' == __name__:
+    def _init_segments(self, datasrc_info):
+        for key, sgmt_info in datasrc_info.segment_info_map.items():
+            rrclass, dsrc_name = key
+            cmd = ('load', None, datasrc_info, rrclass, dsrc_name)
+            sgmt_info.add_event(cmd)
+            send_cmd = sgmt_info.start_update()
+            assert cmd == send_cmd and sgmt_info.get_state() == \
+                SegmentInfo.UPDATING
+            self._cmd_to_builder(cmd)
+
+def main():
     mgr = Memmgr()
     sys.exit(mgr.run(MODULE_NAME))
+
+if '__main__' == __name__:
+    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/bin/memmgr/tests/memmgr_test.py b/src/bin/memmgr/tests/memmgr_test.py
index 3dae17f..32d5c15 100755
--- a/src/bin/memmgr/tests/memmgr_test.py
+++ b/src/bin/memmgr/tests/memmgr_test.py
@@ -16,12 +16,14 @@
 import unittest
 import os
 import re
+import threading
 
 import isc.log
 from isc.dns import RRClass
 import isc.config
 from isc.config import parse_answer
 import memmgr
+from isc.memmgr.datasrc_info import SegmentInfo
 from isc.testutils.ccsession_mock import MockModuleCCSession
 
 class MyCCSession(MockModuleCCSession, isc.config.ConfigData):
@@ -190,9 +192,14 @@ class TestMemmgr(unittest.TestCase):
         cfg_data = MockConfigData(
             {"classes": {"IN": [{"type": "MasterFiles",
                                  "cache-enable": True, "params": {}}]}})
+        self.__init_called = None
+        def mock_init_segments(param):
+            self.__init_called = param
+        self.__mgr._init_segments = mock_init_segments
         self.__mgr._datasrc_config_handler({}, cfg_data)
         self.assertEqual(1, len(self.__mgr._datasrc_info_list))
         self.assertEqual(1, self.__mgr._datasrc_info_list[0].gen_id)
+        self.assertEqual(self.__init_called, self.__mgr._datasrc_info_list[0])
 
         # Below we're using a mock DataSrcClientMgr for easier tests
         class MockDataSrcClientMgr:
@@ -220,6 +227,7 @@ class TestMemmgr(unittest.TestCase):
             MockDataSrcClientMgr([('sqlite3', 'mapped', None)])
         self.__mgr._datasrc_config_handler(None, None) # params don't matter
         self.assertEqual(2, len(self.__mgr._datasrc_info_list))
+        self.assertEqual(self.__init_called, self.__mgr._datasrc_info_list[1])
         self.assertIsNotNone(
             self.__mgr._datasrc_info_list[1].segment_info_map[
                 (RRClass.IN, 'sqlite3')])
@@ -230,6 +238,104 @@ class TestMemmgr(unittest.TestCase):
         self.__mgr._datasrc_config_handler(None, None)
         self.assertEqual(2, len(self.__mgr._datasrc_info_list))
 
+    def test_init_segments(self):
+        """
+        Test the initialization of segments ‒ just load everything found in there.
+        """
+        # Fake a lot of things. These are objects hard to set up, so this is
+        # easier.
+        class SgmtInfo:
+            def __init__(self):
+                self.events = []
+                self.__state = None
+
+            def add_event(self, cmd):
+                self.events.append(cmd)
+                self.__state = SegmentInfo.UPDATING
+
+            def start_update(self):
+                return self.events[0]
+
+            def get_state(self):
+                return self.__state
+
+        sgmt_info = SgmtInfo()
+        class DataSrcInfo:
+            def __init__(self):
+                self.segment_info_map = \
+                    {(isc.dns.RRClass.IN, "name"): sgmt_info}
+        dsrc_info = DataSrcInfo()
+
+        # Pretend to have the builder thread
+        self.__mgr._builder_cv = threading.Condition()
+
+        # Run the initialization
+        self.__mgr._init_segments(dsrc_info)
+
+        # The event was pushed into the segment info
+        command = ('load', None, dsrc_info, isc.dns.RRClass.IN, 'name')
+        self.assertEqual([command], sgmt_info.events)
+        self.assertEqual([command], self.__mgr._builder_command_queue)
+        del self.__mgr._builder_command_queue[:]
+
+    def test_notify_from_builder(self):
+        """
+        Check the notify from builder thing eats the notifications and
+        handles them.
+        """
+        # Some mocks
+        class SgmtInfo:
+            def complete_update():
+                return 'command'
+        sgmt_info = SgmtInfo
+        class DataSrcInfo:
+            def __init__(self):
+                self.segment_info_map = \
+                    {(isc.dns.RRClass.IN, "name"): sgmt_info}
+        dsrc_info = DataSrcInfo()
+        class Sock:
+            def recv(self, size):
+                pass
+        self.__mgr._master_sock = Sock()
+        commands = []
+        def mock_cmd_to_builder(cmd):
+            commands.append(cmd)
+        self.__mgr._cmd_to_builder = mock_cmd_to_builder
+
+        self.__mgr._builder_lock = threading.Lock()
+        # Extract the reference for the queue. We get a copy of the reference
+        # to check it is cleared, not a new empty one installed
+        notif_ref = self.__mgr._builder_response_queue
+        notif_ref.append(('load-completed', dsrc_info, isc.dns.RRClass.IN,
+                          'name'))
+        # Wake up the main thread and let it process the notifications
+        self.__mgr._notify_from_builder()
+        # All notifications are now eaten
+        self.assertEqual([], notif_ref)
+        self.assertEqual(['command'], commands)
+        del commands[:]
+        # The new command is sent
+        # Once again the same, but with the last command - nothing new pushed
+        sgmt_info.complete_update = lambda: None
+        notif_ref.append(('load-completed', dsrc_info, isc.dns.RRClass.IN,
+                          'name'))
+        self.__mgr._notify_from_builder()
+        self.assertEqual([], notif_ref)
+        self.assertEqual([], commands)
+        # This is invalid (unhandled) notification name
+        notif_ref.append(('unhandled',))
+        self.assertRaises(ValueError, self.__mgr._notify_from_builder)
+        self.assertEqual([], notif_ref)
+
+    def test_send_to_builder(self):
+        """
+        Send command to the builder test.
+        """
+        self.__mgr._builder_cv = threading.Condition()
+        self.__mgr._cmd_to_builder(('test',))
+        self.assertEqual([('test',)], self.__mgr._builder_command_queue)
+        del self.__mgr._builder_command_queue[:]
+
 if __name__== "__main__":
     isc.log.resetUnitTestRootLogger()
     unittest.main()
diff --git a/src/bin/msgq/msgq.py.in b/src/bin/msgq/msgq.py.in
index a3e30d3..c68567c 100755
--- a/src/bin/msgq/msgq.py.in
+++ b/src/bin/msgq/msgq.py.in
@@ -33,6 +33,7 @@ import threading
 import isc.config.ccsession
 from optparse import OptionParser, OptionValueError
 import isc.util.process
+import isc.util.traceback_handler
 from isc.cc.proto_defs import *
 import isc.log
 from isc.log_messages.msgq_messages import *
@@ -73,6 +74,8 @@ SPECFILE_LOCATION = SPECFILE_PATH + "/msgq.spec"
 
 class MsgQReceiveError(Exception): pass
 
+class MsgQRunningError(Exception): pass
+
 class MsgQCloseOnReceive(Exception):
     """Exception raised when reading data from a socket results in 'shutdown'.
 
@@ -267,11 +270,26 @@ class MsgQ:
         """Set up the listener socket.  Internal function."""
         logger.debug(TRACE_BASIC, MSGQ_LISTENER_SETUP, self.socket_file)
 
-        self.listen_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-
         if os.path.exists(self.socket_file):
+            # Rather than just blindly removing the socket file, attempt to
+            # connect to the existing socket to see if there is an existing
+            # msgq running. Only if that fails do we remove the file and
+            # attempt to create a new socket.
+            existing_msgq = None
+            try:
+                existing_msgq = isc.cc.Session(self.socket_file)
+            except isc.cc.session.SessionError:
+                existing_msgq = None
+
+            if existing_msgq:
+                existing_msgq.close()
+                logger.fatal(MSGQ_ALREADY_RUNNING)
+                raise MsgQRunningError("b10-msgq already running")
+
             os.remove(self.socket_file)
+
         try:
+            self.listen_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
             self.listen_socket.bind(self.socket_file)
             self.listen_socket.listen(1024)
         except Exception as e:
@@ -524,7 +542,9 @@ class MsgQ:
             # Append it to buffer (but check the data go away)
             if fileno in self.sendbuffs:
                 (last_sent, buff) = self.sendbuffs[fileno]
-                if now - last_sent > 0.1:
+                tdelta = now - last_sent
+                if tdelta > 0.1:
+                    logger.error(MSGQ_SOCKET_TIMEOUT_ERROR, fileno, tdelta)
                     self.kill_socket(fileno, sock)
                     return False
                 buff += msg
@@ -779,7 +799,7 @@ def signal_handler(msgq, signal, frame):
     if msgq:
         msgq.stop()
 
-if __name__ == "__main__":
+def main():
     def check_port(option, opt_str, value, parser):
         """Function to insure that the port we are passed is actually
         a valid port number. Used by OptionParser() on startup."""
@@ -807,6 +827,12 @@ if __name__ == "__main__":
     signal.signal(signal.SIGTERM,
                   lambda signal, frame: signal_handler(msgq, signal, frame))
 
+    # Ignore SIGPIPE. We handle errors writing to children using try in the
+    # appropriate places and the delivery of a signal is very heavy handed.
+    # Windows doesn't support SIGPIPE so don't try it there.
+    if not sys.platform.startswith('win'):
+        signal.signal(signal.SIGPIPE, signal.SIG_IGN)
+
     try:
         msgq.setup()
     except Exception as e:
@@ -855,3 +881,6 @@ if __name__ == "__main__":
     msgq.shutdown()
 
     logger.info(MSGQ_EXITING)
+
+if __name__ == "__main__":
+    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/bin/msgq/msgq_messages.mes b/src/bin/msgq/msgq_messages.mes
index 909d6a3..3f6e6bc 100644
--- a/src/bin/msgq/msgq_messages.mes
+++ b/src/bin/msgq/msgq_messages.mes
@@ -19,6 +19,10 @@
 # <topsrcdir>/tools/reorder_message_file.py to make sure the
 # messages are in the correct order.
 
+% MSGQ_ALREADY_RUNNING Another copy of b10-msgq is already running.
+Only a single instance of b10-msgq should ever be run at one time.
+This instance will now terminate.
+
 % MSGQ_CFGMGR_SUBSCRIBED The config manager subscribed to message queue
 This is a debug message. The message queue has little bit of special handling
 for the configuration manager. This special handling is happening now.
@@ -142,3 +146,9 @@ data structure.
 % MSGQ_SUBS_NEW_TARGET Creating new target for subscription to group '%1' for instance '%2'
 Debug message. Creating a new subscription. Also creating a new data structure
 to hold it.
+
+% MSGQ_SOCKET_TIMEOUT_ERROR Killing socket %1 because timeout exceeded (%2)
+Outgoing data was queued up on a socket connected to msgq, but the other
+side is not reading it. It could be deadlocked, or may not be monitoring
+it. Both cases are programming errors and should be corrected. The socket
+is closed on the msgq side.
diff --git a/src/bin/msgq/tests/msgq_run_test.py b/src/bin/msgq/tests/msgq_run_test.py
index 9cf6da6..5b0c711 100644
--- a/src/bin/msgq/tests/msgq_run_test.py
+++ b/src/bin/msgq/tests/msgq_run_test.py
@@ -328,6 +328,22 @@ class MsgqRunTest(unittest.TestCase):
             'group': 'notifications/cc_members'
         }]}, msg)
 
+    def test_multiple_invocations(self):
+        """
+        Check to make sure that an attempt to start a second copy of the MsgQ
+        daemon fails.
+        """
+
+        self.assertTrue (os.path.exists(SOCKET_PATH))
+        self.__retcode = subprocess.call([MSGQ_PATH, '-s', SOCKET_PATH])
+        self.assertNotEqual(self.__retcode, 0)
+
+        # Verify that the socket still exists and works. We re-call
+        # test_send_direct as a means of testing that the existing
+        # daemon is still behaving correctly.
+        self.assertTrue (os.path.exists(SOCKET_PATH))
+        self.test_send_direct()
+
 if __name__ == '__main__':
     isc.log.init("msgq-tests")
     isc.log.resetUnitTestRootLogger()
diff --git a/src/bin/resolver/Makefile.am b/src/bin/resolver/Makefile.am
index 38f9bf6..36a302e 100644
--- a/src/bin/resolver/Makefile.am
+++ b/src/bin/resolver/Makefile.am
@@ -20,7 +20,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 CLEANFILES  = *.gcno *.gcda
 CLEANFILES += resolver.spec spec_config.h
-CLEANFILES += resolver_messages.cc resolver_messages.h
+CLEANFILES += resolver_messages.cc resolver_messages.h s-messages
 
 man_MANS = b10-resolver.8
 DISTCLEANFILES = $(man_MANS)
@@ -46,9 +46,11 @@ spec_config.h: spec_config.h.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
 
 # Define rule to build logging source files from message file
-resolver_messages.h resolver_messages.cc: resolver_messages.mes
-	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/resolver/resolver_messages.mes
+resolver_messages.h resolver_messages.cc: s-messages
 
+s-messages: resolver_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/resolver/resolver_messages.mes
+	touch $@
 
 BUILT_SOURCES = spec_config.h resolver_messages.cc resolver_messages.h
 
@@ -71,7 +73,6 @@ b10_resolver_LDADD += $(top_builddir)/src/lib/acl/libb10-dnsacl.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
-b10_resolver_LDADD += $(top_builddir)/src/lib/xfr/libb10-xfr.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/server_common/libb10-server-common.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/cache/libb10-cache.la
diff --git a/src/bin/resolver/tests/Makefile.am b/src/bin/resolver/tests/Makefile.am
index 52b39a1..8630e0e 100644
--- a/src/bin/resolver/tests/Makefile.am
+++ b/src/bin/resolver/tests/Makefile.am
@@ -45,7 +45,6 @@ run_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 run_unittests_LDADD += $(top_builddir)/src/lib/acl/libb10-dnsacl.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
-run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libb10-xfr.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libb10-server-common.la
 run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libb10-resolve.la
diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in
index 7ec530b..38d0b02 100755
--- a/src/bin/stats/stats.py.in
+++ b/src/bin/stats/stats.py.in
@@ -1,6 +1,6 @@
 #!@PYTHON@
 
-# Copyright (C) 2010, 2011, 2012  Internet Systems Consortium.
+# Copyright (C) 2010-2013  Internet Systems Consortium.
 #
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -29,6 +29,7 @@ import select
 import isc.cc
 import isc.config
 import isc.util.process
+import isc.util.traceback_handler
 import isc.log
 from isc.log_messages.stats_messages import *
 
@@ -124,8 +125,8 @@ def _accum(a, b):
                              if len(a) <= i ]
     # If both of args are integer or float type, two
     # values are added.
-    elif (type(a) is int and type(b) is int) \
-            or (type(a) is float or type(b) is float):
+    elif (type(a) is int or type(a) is float) \
+            and (type(b) is int or type(b) is float):
         return a + b
 
     # If both of args are string type,
@@ -186,6 +187,11 @@ class StatsError(Exception):
     """Exception class for Stats class"""
     pass
 
+class InitSessionTimeout(isc.cc.session.SessionTimeout):
+    """SessionTimeout Exception in the session between the stats module and the
+    init module"""
+    pass
+
 class Stats:
     """
     Main class of stats module
@@ -243,14 +249,12 @@ class Stats:
 
         """
         self.update_modules()
-        if self.update_statistics_data(
+        self.update_statistics_data(
             self.module_name,
             self.cc_session.lname,
             {'lname': self.cc_session.lname,
              'boot_time': get_datetime(_BASETIME),
-             'last_update_time': get_datetime()}):
-            logger.warn(STATS_RECEIVED_INVALID_STATISTICS_DATA,
-                        self.module_name)
+             'last_update_time': get_datetime()})
         # define the variable of the last time of polling
         self._lasttime_poll = 0.0
 
@@ -258,12 +262,8 @@ class Stats:
         """return the current value of 'poll-interval'"""
         return self.config['poll-interval']
 
-    def do_polling(self):
-        """Polls modules for statistics data. Return nothing. First
-           search multiple instances of same module. Second requests
-           each module to invoke 'getstats'. Finally updates internal
-           statistics data every time it gets from each instance."""
-
+    def _get_multi_module_list(self):
+        """Returns a module list which is running as multiple modules."""
         # It counts the number of instances of same module by
         # examining the third value from the array result of
         # 'show_processes' of Init
@@ -277,6 +277,8 @@ class Stats:
             # TODO: Is it OK to just pass? As part of refactoring, preserving
             # the original behaviour.
             value = None
+        except isc.cc.session.SessionTimeout as e:
+            raise InitSessionTimeout(e)
         modules = []
         if type(value) is list:
             # NOTE: For example, the "show_processes" command
@@ -306,6 +308,12 @@ class Stats:
             # release.
             modules = [ v[2] if type(v) is list and len(v) > 2 \
                             else None for v in value ]
+        return modules
+
+    def _query_statistics(self, modules):
+        """Queries each module statistics and returns sequences to use
+        for receiving that answers based on the argument 'modules' which
+        is a list of modules running as multiple modules"""
         # start requesting each module to collect statistics data
         sequences = []
         for (module_name, data) in self.get_statistics_data().items():
@@ -327,11 +335,17 @@ class Stats:
             if cnt > 1:
                 sequences = sequences + [ (module_name, seq) \
                                               for i in range(cnt-1) ]
+        return sequences
+
+    def _collect_statistics(self, sequences):
+        """Based on sequences which is a list of values returned from
+        group_sendmsg(), collects statistics data from each module. If
+        a SessionTimeout exception is raised when collecting from the
+        module, skips it and goes to collect from the next module."""
         # start receiving statistics data
         _statistics_data = []
-        while len(sequences) > 0:
+        for (module_name, seq) in sequences:
             try:
-                (module_name, seq) = sequences.pop(0)
                 answer, env = self.cc_session.group_recvmsg(False, seq)
                 if answer:
                     rcode, args = isc.config.ccsession.parse_answer(answer)
@@ -340,25 +354,37 @@ class Stats:
                             (module_name, env['from'], args))
             # skip this module if SessionTimeout raised
             except isc.cc.session.SessionTimeout:
-                pass
+                logger.warn(STATS_SKIP_COLLECTING, module_name)
+        return _statistics_data
 
+    def _refresh_statistics(self, statistics_data):
+        """Refreshes statistics_data into internal statistics data to
+        display, which is a list of a tuple of module_name, lname, and
+        args"""
         # update statistics data
+        _statistics_data = statistics_data[:]
         self.update_modules()
         while len(_statistics_data) > 0:
             (_module_name, _lname, _args) = _statistics_data.pop(0)
-            if self.update_statistics_data(_module_name, _lname, _args):
-                logger.warn(
-                STATS_RECEIVED_INVALID_STATISTICS_DATA,
-                _module_name)
-            else:
-                if self.update_statistics_data(
+            if not self.update_statistics_data(_module_name, _lname, _args):
+                self.update_statistics_data(
                     self.module_name,
                     self.cc_session.lname,
-                    {'last_update_time': get_datetime()}):
-                    logger.warn(
-                        STATS_RECEIVED_INVALID_STATISTICS_DATA,
-                        self.module_name)
-        # if successfully done, set the last time of polling
+                    {'last_update_time': get_datetime()})
+
+    def do_polling(self):
+        """Polls modules for statistics data. Return nothing. First
+           search multiple instances of same module. Second requests
+           each module to invoke 'getstats'. Finally updates internal
+           statistics data every time it gets from each instance."""
+        try:
+            modules = self._get_multi_module_list()
+            sequences = self._query_statistics(modules)
+            _statistics_data = self._collect_statistics(sequences)
+            self._refresh_statistics(_statistics_data)
+        except InitSessionTimeout:
+            logger.warn(STATS_SKIP_POLLING)
+        # if successfully done or skipped, set the last time of polling
         self._lasttime_poll = get_timestamp()
 
     def _check_command(self, nonblock=False):
@@ -601,7 +627,10 @@ class Stats:
                         self.statistics_data[m],
                         _accum_bymodule(self.statistics_data_bymid[m]))
 
-        if errors: return errors
+        if errors:
+            logger.warn(
+                STATS_RECEIVED_INVALID_STATISTICS_DATA, owner)
+            return errors
 
     def command_status(self):
         """
@@ -694,7 +723,7 @@ class Stats:
                 1, "specified arguments are incorrect: " \
                     + "owner: " + str(owner) + ", name: " + str(name))
 
-if __name__ == "__main__":
+def main():
     try:
         parser = OptionParser()
         parser.add_option(
@@ -718,3 +747,6 @@ if __name__ == "__main__":
         logger.info(STATS_STOPPED_BY_KEYBOARD)
 
     logger.info(STATS_EXITING)
+
+if __name__ == "__main__":
+    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in
index c3cdb76..df0c04c 100755
--- a/src/bin/stats/stats_httpd.py.in
+++ b/src/bin/stats/stats_httpd.py.in
@@ -35,6 +35,7 @@ import re
 import isc.cc
 import isc.config
 import isc.util.process
+import isc.util.traceback_handler
 from isc.util.address_formatter import AddressFormatter
 
 import isc.log
@@ -598,7 +599,7 @@ class StatsHttpd:
                 "%s: %s" % (err.__class__.__name__, err))
         return string.Template(lines)
 
-if __name__ == "__main__":
+def main():
     try:
         parser = OptionParser()
         parser.add_option(
@@ -622,3 +623,6 @@ if __name__ == "__main__":
         logger.info(STATSHTTPD_STOPPED_BY_KEYBOARD)
 
     logger.info(STATSHTTPD_EXITING)
+
+if __name__ == "__main__":
+    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/bin/stats/stats_messages.mes b/src/bin/stats/stats_messages.mes
index b6f0b16..38dddcc 100644
--- a/src/bin/stats/stats_messages.mes
+++ b/src/bin/stats/stats_messages.mes
@@ -64,6 +64,17 @@ will respond with an error and the command will be ignored.
 This debug message is printed when a request is sent to the module
 to send its data to the stats module.
 
+% STATS_SKIP_COLLECTING skipped collecting statistics from %1
+The stats module temporarily encountered an internal messaging bus error while
+collecting statistics from the module, then it skipped collecting statistics
+from it and will try to collect from the next module. The lack of statistics
+will be recovered at the next polling round.
+
+% STATS_SKIP_POLLING skipped polling statistics to modules
+The stats module temporarily encountered an internal messaging bus error while
+collecting initial information to collect statistics from the init module, then
+it skipped polling statistics and will try to do next time.
+
 % STATS_STARTING starting
 The stats module will be now starting.
 
diff --git a/src/bin/stats/tests/stats_test.py b/src/bin/stats/tests/stats_test.py
index dd1b79b..14e49f9 100644
--- a/src/bin/stats/tests/stats_test.py
+++ b/src/bin/stats/tests/stats_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2010, 2011, 2012  Internet Systems Consortium.
+# Copyright (C) 2010-2013  Internet Systems Consortium.
 #
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -31,6 +31,7 @@ import sys
 import stats
 import isc.log
 from test_utils import MyStats
+from isc.config.ccsession import create_answer
 
 class TestUtilties(unittest.TestCase):
     items = [
@@ -132,6 +133,8 @@ class TestUtilties(unittest.TestCase):
         self.assertEqual(stats._accum("a", None), "a")
         self.assertEqual(stats._accum(1, 2), 3)
         self.assertEqual(stats._accum(0.5, 0.3), 0.8)
+        self.assertEqual(stats._accum(1, 0.3), 1.3)
+        self.assertEqual(stats._accum(0.5, 2), 2.5)
         self.assertEqual(stats._accum('aa','bb'), 'bb')
         self.assertEqual(stats._accum('1970-01-01T09:00:00Z','2012-08-09T09:33:31Z'),
                          '2012-08-09T09:33:31Z')
@@ -296,6 +299,43 @@ class TestStats(unittest.TestCase):
         return isc.config.ccsession.parse_answer(
             stats.command_handler(command_name, params))
 
+    def test_check_command(self):
+        """Test _check_command sets proper timeout values and sets proper values
+        returned from group_recvmsg"""
+        stat = MyStats()
+        set_timeouts = []
+        orig_timeout = 1
+        stat.cc_session.get_timeout = lambda: orig_timeout
+        stat.cc_session.set_timeout = lambda x: set_timeouts.append(x)
+        msg = create_answer(0, 'msg')
+        env = {'from': 'frominit'}
+        stat._answers = [(msg, env)]
+        stat._check_command()
+        self.assertListEqual([500, orig_timeout], set_timeouts)
+        self.assertEqual(msg, stat.mccs._msg)
+        self.assertEqual(env, stat.mccs._env)
+
+    def test_check_command_sessiontimeout(self):
+        """Test _check_command doesn't perform but sets proper timeout values in
+        case that a SesstionTimeout exception is caught while doing
+        group_recvmsg()"""
+        stat = MyStats()
+        set_timeouts = []
+        orig_timeout = 1
+        stat.cc_session.get_timeout = lambda: orig_timeout
+        stat.cc_session.set_timeout = lambda x: set_timeouts.append(x)
+        msg = create_answer(0, 'msg')
+        env = {'from': 'frominit'}
+        stat._answers = [(msg, env)]
+        # SessionTimeout is raised while doing group_recvmsg()
+        ex = isc.cc.session.SessionTimeout
+        def __raise(*x): raise ex(*x)
+        stat.cc_session.group_recvmsg = lambda x: __raise()
+        stat._check_command()
+        self.assertListEqual([500, orig_timeout], set_timeouts)
+        self.assertEqual(None, stat.mccs._msg)
+        self.assertEqual(None, stat.mccs._env)
+
     def test_start(self):
         # Define a separate exception class so we can be sure that's actually
         # the one raised in __check_start() below
@@ -315,6 +355,25 @@ class TestStats(unittest.TestCase):
         self.assertRaises(CheckException, self.stats.start)
         self.assertEqual(self.__send_command(self.stats, "status"),
                          (0, "Stats is up. (PID " + str(os.getpid()) + ")"))
+        self.assertTrue(self.stats.mccs.stopped)
+
+    def test_start_set_next_polltime(self):
+        """Test start() properly sets the time next_polltime to do_poll() next
+        time"""
+        orig_get_timestamp = stats.get_timestamp
+        stats.get_timestamp = lambda : self.const_timestamp
+        stat = MyStats()
+        # manupilate next_polltime to go it through the inner if-condition
+        stat.next_polltime = self.const_timestamp - stat.get_interval() - 1
+        # stop an infinity loop at once
+        def __stop_running(): stat.running = False
+        stat.do_polling = __stop_running
+        # do nothing in _check_command()
+        stat._check_command = lambda: None
+        stat.start()
+        # check stat.next_polltime reassigned
+        self.assertEqual(self.const_timestamp, stat.next_polltime)
+        stats.get_timestamp = orig_get_timestamp
 
     def test_shutdown(self):
         def __check_shutdown(tested_stats):
@@ -697,7 +756,6 @@ class TestStats(unittest.TestCase):
 
         # We use the knowledge of what kind of messages are sent via
         # do_polling, and return the following faked answer directly.
-        create_answer = isc.config.ccsession.create_answer # shortcut
         self.stats._answers = [
             # Answer for "show_processes"
             (create_answer(0, [[1034, 'b10-auth-1', 'Auth'],
@@ -817,7 +875,6 @@ class TestStats(unittest.TestCase):
 
         # see the comment for test_update_statistics_data_withmid.  We abuse
         # do_polling here, too.  With #2781 we should make it more direct.
-        create_answer = isc.config.ccsession.create_answer # shortcut
         stat._answers = [\
             # Answer for "show_processes"
             (create_answer(0, []),  None),
@@ -868,7 +925,6 @@ class TestStats(unittest.TestCase):
         self.stats.update_modules = lambda: None
 
         # Test data borrowed from test_update_statistics_data_withmid
-        create_answer = isc.config.ccsession.create_answer # shortcut
         self.stats._answers = [
             (create_answer(0, [[1034, 'b10-auth-1', 'Auth'],
                                [1035, 'b10-auth-2', 'Auth']]),  None),
@@ -1281,6 +1337,163 @@ class TestStats(unittest.TestCase):
                          isc.config.create_answer(
                 1, "module name is not specified"))
 
+    def test_get_multi_module_list(self):
+        """Test _get_multi_module_list() returns a module list which is running
+        as multiple modules."""
+        stat = MyStats()
+        # no answer
+        self.assertListEqual([], stat._get_multi_module_list())
+        # proc list returned
+        proc_list = [
+            [29317, 'b10-xfrout', 'Xfrout'],
+            [29318, 'b10-xfrin', 'Xfrin'],
+            [20061, 'b10-auth','Auth'],
+            [20103, 'b10-auth-2', 'Auth']]
+        mod_list = [ a[2] for a in proc_list ]
+        stat._answers = [
+            # Answer for "show_processes"
+            (create_answer(0, proc_list), {'from': 'init'})
+            ]
+        self.assertListEqual(mod_list, stat._get_multi_module_list())
+        # invalid proc list
+        stat._answers = [
+            # Answer for "show_processes"
+            (create_answer(0, [[999, 'invalid', 'Invalid'], 'invalid']),
+             {'from': 'init'})
+            ]
+        self.assertListEqual(['Invalid', None], stat._get_multi_module_list())
+
+    def test_get_multi_module_list_rpcrecipientmissing(self):
+        """Test _get_multi_module_list() raises an RPCRecipientMissing exception
+        if rcp_call() raise the exception"""
+        # RPCRecipientMissing case
+        stat = MyStats()
+        ex = isc.config.RPCRecipientMissing
+        def __raise(*x): raise ex(*x)
+        stat.mccs.rpc_call = lambda x,y: __raise('Error')
+        self.assertRaises(ex, stat._get_multi_module_list)
+
+    def test_get_multi_module_list_rpcerror(self):
+        """Test _get_multi_module_list() returns an empty list if rcp_call()
+        raise an RPCError exception"""
+        # RPCError case
+        stat = MyStats()
+        ex = isc.config.RPCError
+        def __raise(*x): raise ex(*x)
+        stat.mccs.rpc_call = lambda x,y: __raise(99, 'Error')
+        self.assertListEqual([], stat._get_multi_module_list())
+
+    def test_get_multi_module_list_initsessiontimeout(self):
+        """Test _get_multi_module_list() raises an InitSeeionTimeout exception
+        if a CC session times out in rcp_call()"""
+        # InitSeeionTimeout case
+        stat = MyStats()
+        ex = isc.cc.session.SessionTimeout
+        def __raise(*x): raise ex(*x)
+        stat.mccs.rpc_call = lambda x,y: __raise()
+        self.assertRaises(stats.InitSessionTimeout, stat._get_multi_module_list)
+
+    def test_query_statistics(self):
+        """Test _query_statistics returns a list of pairs of module and
+        sequences from group_sendmsg()"""
+        stat = MyStats()
+        # imitate stat.get_statistics_data().items. The order of the array
+        # returned by items() should be preserved.
+        class DummyDict:
+            def items(self):
+                return [('Init', 'dummy'), ('Stats', 'dummy'), ('Auth', 'dummy')]
+        stat.get_statistics_data = lambda: DummyDict()
+        mod = ('Init', 'Auth', 'Auth')
+        seq = [('Init', stat._seq + 1),
+               ('Auth', stat._seq + 2),
+               ('Auth', stat._seq + 2) ]
+        self.assertListEqual(seq, stat._query_statistics(mod))
+
+    def test_collect_statistics(self):
+        """Test _collect_statistics() collects statistics data from each module
+        based on the sequences which is a list of values returned from
+        group_sendmsg()"""
+        stat = MyStats()
+        seq = [ ('Init', stat._seq + 1),
+                ('Auth', stat._seq + 2),
+                ('Auth', stat._seq + 2) ]
+        ret = [('Init', 'frominit', {'boot_time': '2013-01-01T00:00:00Z'}),
+               ('Auth', 'fromauth1', {'queries.tcp': 100}),
+               ('Auth', 'fromauth2', {'queries.udp': 200})]
+        stat._answers = [
+            (create_answer(0, r[2]), {'from': r[1]}) for r in ret
+            ]
+        self.assertListEqual(ret, stat._collect_statistics(seq))
+
+    def test_collect_statistics_nodata(self):
+        """Test _collect_statistics() returns empty statistics data if
+        a module returns an empty list"""
+        stat = MyStats()
+        seq = []
+        stat._answers = []
+        ret = []
+        self.assertListEqual(ret, stat._collect_statistics(seq))
+
+    def test_collect_statistics_nonzero_rcode(self):
+        """Test _collect_statistics() returns empty statistics data if
+        a module returns non-zero rcode"""
+        stat = MyStats()
+        seq = [('Init', stat._seq + 1)]
+        stat._answers = [
+            (create_answer(1, 'error'), {'from': 'frominit'})
+            ]
+        ret = []
+        self.assertListEqual(ret, stat._collect_statistics(seq))
+
+    def test_collect_statistics_sessiontimeout(self):
+        """Test _collect_statistics() collects statistics data from each module
+        based on the sequences which is a list of values returned from
+        group_sendmsg(). In this test case, SessionTimeout exceptions are raised
+        while collecting from Auth. This tests _collect_statistics skips
+        collecting from Auth."""
+        # SessionTimeout case
+        stat = MyStats()
+        ex = isc.cc.session.SessionTimeout
+        def __raise(*x): raise ex(*x)
+        # SessionTimeout is raised when asking to Auth
+        stat.cc_session.group_recvmsg = lambda x,seq: \
+            __raise() if seq == stat._seq + 2 else stat._answers.pop(0)
+        seq = [ ('Init', stat._seq + 1),
+                ('Auth', stat._seq + 2),
+                ('Auth', stat._seq + 2) ]
+        ret = [('Init', 'frominit', {'boot_time': '2013-01-01T00:00:00Z'})]
+        stat._answers = [
+            (create_answer(0, r[2]), {'from': r[1]}) for r in ret
+            ]
+        self.assertListEqual(ret, stat._collect_statistics(seq))
+
+    def test_refresh_statistics(self):
+        """Test _refresh_statistics() refreshes statistics data from given data
+        """
+        stat = MyStats()
+        self.assertEqual(self.const_default_datetime,
+                         stat.statistics_data['Init']['boot_time'])
+        self.assertEqual(0,
+                         stat.statistics_data['Auth']['queries.tcp'])
+        self.assertEqual(0,
+                         stat.statistics_data['Auth']['queries.udp'])
+        # change stats.get_datetime() for testing 'last_update_time'
+        orig_get_datetime = stats.get_datetime
+        stats.get_datetime = lambda : self.const_datetime
+        arg = [('Init', 'frominit', {'boot_time': '2013-01-01T00:00:00Z'}),
+               ('Auth', 'fromauth1', {'queries.tcp': 100}),
+               ('Auth', 'fromauth2', {'queries.udp': 200})]
+        stat._refresh_statistics(arg)
+        self.assertEqual('2013-01-01T00:00:00Z',
+                         stat.statistics_data['Init']['boot_time'])
+        self.assertEqual(100,
+                         stat.statistics_data['Auth']['queries.tcp'])
+        self.assertEqual(200,
+                         stat.statistics_data['Auth']['queries.udp'])
+        self.assertEqual(self.const_datetime,
+                         stat.statistics_data['Stats']['last_update_time'])
+        stats.get_datetime = orig_get_datetime
+
     def test_polling_init(self):
         """check statistics data of 'Init'."""
 
@@ -1295,7 +1508,6 @@ class TestStats(unittest.TestCase):
         del stat.statistics_data['Auth']
 
         stat.update_modules = lambda: None
-        create_answer = isc.config.ccsession.create_answer # shortcut
 
         stat._answers = [
             # Answer for "show_processes"
@@ -1314,7 +1526,6 @@ class TestStats(unittest.TestCase):
         """check statistics data of multiple instances of same module."""
         stat = MyStats()
         stat.update_modules = lambda: None
-        create_answer = isc.config.ccsession.create_answer # shortcut
 
         # Test data borrowed from test_update_statistics_data_withmid
         stat._answers = [
@@ -1380,35 +1591,52 @@ class TestStats(unittest.TestCase):
         self.assertEqual(stat.statistics_data['Stats']['lname'],
                          stat.mccs._session.lname)
 
-    def test_polling2(self):
-        """Test do_polling() doesn't incorporate broken statistics data.
-
-        Actually, this is not a test for do_polling() itself.  It's bad, but
-        fixing that is a subject of different ticket.
-
+    def test_refresh_statistics_broken_statistics_data(self):
+        """Test _refresh_statistics() doesn't incorporate broken statistics data
         """
         stat = MyStats()
         # check default statistics data of 'Init'
         self.assertEqual(
-             stat.statistics_data['Init'],
-             {'boot_time': self.const_default_datetime})
+            {'boot_time': self.const_default_datetime},
+            stat.statistics_data['Init'])
+        last_update_time = stat.statistics_data['Stats']['last_update_time']
+        # _refresh_statistics() should ignore the invalid statistics_data(type
+        # of boot_time is invalid); default data shouldn't be replaced.
+        arg = [('Init', 'lname', {'boot_time': 1})]
+        stat._refresh_statistics(arg)
+        self.assertEqual(
+            {'boot_time': self.const_default_datetime},
+            stat.statistics_data['Init'])
+        # 'last_update_time' doesn't change
+        self.assertEqual(
+            last_update_time,
+            stat.statistics_data['Stats']['last_update_time'])
 
-        # set invalid statistics
-        create_answer = isc.config.ccsession.create_answer # shortcut
-        stat._answers = [
-            # Answer for "show_processes"
-            (create_answer(0, []),  None),
-            # Answers for "getstats" for Init (type of boot_time is invalid)
-            (create_answer(0, {'boot_time': 1}), {'from': 'init'}),
-            ]
-        stat.update_modules = lambda: None
+    def test_polling_update_lasttime_poll(self):
+        """Test _lasttime_poll is updated after do_polling()
+        """
+        orig_get_timestamp = stats.get_timestamp
+        stats.get_timestamp = lambda : self.const_timestamp
+        stat = MyStats()
+        self.assertEqual(0.0, stat._lasttime_poll)
+        stat.do_polling()
+        self.assertEqual(self.const_timestamp, stat._lasttime_poll)
+        stats.get_timestamp = orig_get_timestamp
 
-        # do_polling() should ignore the invalid answer;
-        # default data shouldn't be replaced.
+    def test_polling_initsessiontimeout(self):
+        """Test _lasttime_poll is updated after do_polling() in case that it catches
+        InitSesionTimeout at _get_multi_module_list()
+        """
+        orig_get_timestamp = stats.get_timestamp
+        stats.get_timestamp = lambda : self.const_timestamp
+        ex = stats.InitSessionTimeout
+        def __raise(*x): raise ex(*x)
+        stat = MyStats()
+        self.assertEqual(0.0, stat._lasttime_poll)
+        stat._get_multi_module_list = lambda: __raise()
         stat.do_polling()
-        self.assertEqual(
-             stat.statistics_data['Init'],
-             {'boot_time': self.const_default_datetime})
+        self.assertEqual(self.const_timestamp, stat._lasttime_poll)
+        stats.get_timestamp = orig_get_timestamp
 
 class Z_TestOSEnv(unittest.TestCase):
     # Running this test would break logging setting.  To prevent it from
diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py
index 7456511..99cc980 100644
--- a/src/bin/stats/tests/test_utils.py
+++ b/src/bin/stats/tests/test_utils.py
@@ -311,6 +311,8 @@ class MyModuleCCSession(isc.config.ConfigData):
         self.stopped = False
         self.closed = False
         self.lname = 'mock_mod_ccs'
+        self._msg = None
+        self._env = None
 
     def start(self):
         pass
@@ -321,6 +323,10 @@ class MyModuleCCSession(isc.config.ConfigData):
     def close(self):
         self.closed = True
 
+    def check_command_without_recvmsg(self, msg, env):
+        self._msg = msg
+        self._env = env
+
 class MyStats(stats.Stats):
     """A faked Stats class for unit tests.
 
@@ -338,7 +344,7 @@ class MyStats(stats.Stats):
         # may want to inspect or tweak them.
 
         # initial seq num for faked group_sendmsg, arbitrary choice.
-        self.__seq = 4200
+        self._seq = 4200
         # if set, use them as faked response to group_recvmsg (see below).
         # it's a list of tuples, each of which is of (answer, envelope).
         self._answers = []
@@ -408,10 +414,10 @@ class MyStats(stats.Stats):
         generated sequence number.
 
         """
-        self.__seq += 1
-        return self.__seq
+        self._seq += 1
+        return self._seq
 
-    def __group_recvmsg(self, nonblocking, seq):
+    def __group_recvmsg(self, nonblocking = True, seq = None):
         """Faked ModuleCCSession.group_recvmsg for tests.
 
         Skipping actual network communication, and returning an internally
diff --git a/src/bin/xfrin/b10-xfrin.xml b/src/bin/xfrin/b10-xfrin.xml
index 903a3a0..0b58f9a 100644
--- a/src/bin/xfrin/b10-xfrin.xml
+++ b/src/bin/xfrin/b10-xfrin.xml
@@ -337,6 +337,109 @@ operation
         </listitem>
       </varlistentry><!-- end of zones -->
 
+      <varlistentry>
+        <term>ixfr_running</term>
+        <listitem><simpara>
+          Number of IXFRs in progress
+        </simpara></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>axfr_running</term>
+        <listitem><simpara>
+          Number of AXFRs in progress
+        </simpara></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>soa_in_progress</term>
+        <listitem><simpara>
+          Number of SOA queries in progress
+        </simpara></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>socket</term>
+        <listitem><simpara>
+          A directory name of socket statistics
+          </simpara>
+          <variablelist>
+
+            <varlistentry>
+              <term><replaceable>ipversion</replaceable></term>
+              <listitem><simpara>
+                A directory name of an IP version as ipv4 or ipv6
+                </simpara>
+                <variablelist>
+
+                  <varlistentry>
+                    <term>tcp</term>
+                    <listitem><simpara>
+                      A directory name of TCP statistics
+                      </simpara>
+                      <variablelist>
+
+                        <varlistentry>
+                          <term>open</term>
+                          <listitem><simpara>
+                           IPv4 or IPv6 TCP sockets opened successfully
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>openfail</term>
+                          <listitem><simpara>
+                           IPv4 or IPv6 TCP sockets open failures
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>close</term>
+                          <listitem><simpara>
+                           IPv4 or IPv6 TCP sockets closed
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>connfail</term>
+                          <listitem><simpara>
+                           IPv4 or IPv6 TCP sockets connection failures
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>conn</term>
+                          <listitem><simpara>
+                           IPv4 or IPv6 TCP connections established successfully
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>senderr</term>
+                          <listitem><simpara>
+                           IPv4 or IPv6 TCP sockets send errors
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>recverr</term>
+                          <listitem><simpara>
+                           IPv4 or IPv6 TCP sockets receive errors
+                          </simpara></listitem>
+                        </varlistentry>
+
+                      </variablelist>
+                    </listitem>
+                  </varlistentry><!-- end of tcp -->
+
+                </variablelist>
+              </listitem>
+            </varlistentry><!-- end of ipv4 | ipv6 -->
+
+          </variablelist>
+        </listitem>
+      </varlistentry><!-- end of socket -->
+
     </variablelist>
 
     <para>
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index 0a10311..4158638 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -300,7 +300,8 @@ class MockXfrinConnection(XfrinConnection):
     def __init__(self, sock_map, zone_name, rrclass, datasrc_client,
                  shutdown_event, master_addr, tsig_key=None):
         super().__init__(sock_map, zone_name, rrclass, MockDataSourceClient(),
-                         shutdown_event, master_addr, begin_soa_rrset)
+                         shutdown_event, master_addr, begin_soa_rrset,
+                         xfrin.Counters(xfrin.SPECFILE_LOCATION))
         self.query_data = b''
         self.reply_data = b''
         self.force_time_out = False
@@ -1104,12 +1105,10 @@ class TestAXFR(TestXfrinConnection):
         for (info, ver) in addrs:
             c = MockXfrinConnection({}, TEST_ZONE_NAME, RRClass.CH, None,
                                     threading.Event(), info)
-            c.init_socket()
             if ver is not None:
                 self.assertEqual(ver, c._get_ipver_str())
             else:
                 self.assertRaises(ValueError, c._get_ipver_str)
-            c.close()
 
     def test_soacheck(self):
         # we need to defer the creation until we know the QID, which is
@@ -2129,8 +2128,6 @@ class TestStatisticsXfrinConn(TestXfrinConnection):
     and methods related to statistics tests'''
     def setUp(self):
         super().setUp()
-        # clear all statistics counters before each test
-        self.conn._counters.clear_all()
         # fake datetime
         self.__orig_datetime = isc.statistics.counters.datetime
         self.__orig_start_timer = isc.statistics.counters._start_timer
@@ -2145,16 +2142,19 @@ class TestStatisticsXfrinConn(TestXfrinConnection):
         self._const_sec = round(delta.days * 86400 + delta.seconds +
                                 delta.microseconds * 1E-6, 6)
         # List of statistics counter names and expected initial values
-        self.__name_to_counter = (('axfrreqv4', 0),
-                                 ('axfrreqv6', 0),
-                                 ('ixfrreqv4', 0),
-                                 ('ixfrreqv6', 0),
-                                 ('last_axfr_duration', 0.0),
-                                 ('last_ixfr_duration', 0.0),
-                                 ('soaoutv4', 0),
-                                 ('soaoutv6', 0),
-                                 ('xfrfail', 0),
-                                 ('xfrsuccess', 0))
+        self.__name_to_perzone_counter = (('axfrreqv4', 0),
+                                          ('axfrreqv6', 0),
+                                          ('ixfrreqv4', 0),
+                                          ('ixfrreqv6', 0),
+                                          ('last_axfr_duration', 0.0),
+                                          ('last_ixfr_duration', 0.0),
+                                          ('soaoutv4', 0),
+                                          ('soaoutv6', 0),
+                                          ('xfrfail', 0),
+                                          ('xfrsuccess', 0))
+        self.__name_to_counter = ('soa_in_progress',
+                                  'ixfr_running',
+                                  'axfr_running')
         self.__zones = 'zones'
 
     def tearDown(self):
@@ -2169,25 +2169,41 @@ class TestStatisticsXfrinConn(TestXfrinConnection):
     def _check_init_statistics(self):
         '''checks exception being raised if not incremented statistics
         counter gotten'''
-        for (name, exp) in self.__name_to_counter:
+        for (name, exp) in self.__name_to_perzone_counter:
             self.assertRaises(isc.cc.data.DataNotFoundError,
                               self.conn._counters.get, self.__zones,
                               TEST_ZONE_NAME_STR, name)
+        for name in self.__name_to_counter:
+            self.assertRaises(isc.cc.data.DataNotFoundError,
+                              self.conn._counters.get, name)
 
-    def _check_updated_statistics(self, overwrite):
+    def _check_updated_perzone_statistics(self, overwrite):
         '''checks getting expect values after updating the pairs of
-        statistics counter name and value on to the "overwrite"
+        per-zone statistics counter name and value on to the "overwrite"
         dictionary'''
-        name2count = dict(self.__name_to_counter)
+        name2count = dict(self.__name_to_perzone_counter)
         name2count.update(overwrite)
         for (name, exp) in name2count.items():
             act = self.conn._counters.get(self.__zones,
                                           TEST_RRCLASS_STR,
                                           TEST_ZONE_NAME_STR,
                                           name)
-            msg = '%s is expected %s but actually %s' % (name, exp, act)
+            msg = '%s: expected %s but actually got %s' % (name, exp, act)
             self.assertEqual(exp, act, msg=msg)
 
+    def _check_updated_statistics(self, expects):
+        '''checks counters in expects are incremented. also checks other
+        counters which are not in expects are not incremented.'''
+        for name in self.__name_to_counter:
+            if name in expects:
+                exp = expects[name]
+                act = self.conn._counters.get(name)
+                msg = '%s: expected %s but actually got %s' % (name, exp, act)
+                self.assertEqual(exp, act, msg=msg)
+            else:
+                self.assertRaises(isc.cc.data.DataNotFoundError,
+                                  self.conn._counters.get, name)
+
 class TestStatisticsXfrinAXFRv4(TestStatisticsXfrinConn):
     '''Xfrin AXFR tests for IPv4 to check statistics counters'''
     def test_soaout(self):
@@ -2196,7 +2212,7 @@ class TestStatisticsXfrinAXFRv4(TestStatisticsXfrinConn):
         self.conn.response_generator = self._create_soa_response_data
         self._check_init_statistics()
         self.assertEqual(self.conn._check_soa_serial(), XFRIN_OK)
-        self._check_updated_statistics({'soaout' + self._ipver: 1})
+        self._check_updated_perzone_statistics({'soaout' + self._ipver: 1})
 
     def test_axfrreq_xfrsuccess_last_axfr_duration(self):
         '''tests that axfrreqv4 or axfrreqv6 and xfrsuccess counters
@@ -2204,9 +2220,10 @@ class TestStatisticsXfrinAXFRv4(TestStatisticsXfrinConn):
         self.conn.response_generator = self._create_normal_response_data
         self._check_init_statistics()
         self.assertEqual(self.conn.do_xfrin(False), XFRIN_OK)
-        self._check_updated_statistics({'axfrreq' + self._ipver: 1,
-                                        'xfrsuccess': 1,
-                                        'last_axfr_duration': self._const_sec})
+        self._check_updated_perzone_statistics({'axfrreq' + self._ipver: 1,
+                                                'xfrsuccess': 1,
+                                                'last_axfr_duration':
+                                                    self._const_sec})
 
     def test_axfrreq_xfrsuccess_last_axfr_duration2(self):
         '''tests that axfrreqv4 or axfrreqv6 and xfrsuccess counters
@@ -2217,10 +2234,10 @@ class TestStatisticsXfrinAXFRv4(TestStatisticsXfrinConn):
         self.conn._handle_xfrin_responses = exception_raiser
         self._check_init_statistics()
         self.assertEqual(self.conn.do_xfrin(False), XFRIN_OK)
-        self._check_updated_statistics({'axfrreq' + self._ipver: 1,
-                                        'xfrsuccess': 1,
-                                        'last_axfr_duration':
-                                            self._const_sec})
+        self._check_updated_perzone_statistics({'axfrreq' + self._ipver: 1,
+                                                'xfrsuccess': 1,
+                                                'last_axfr_duration':
+                                                    self._const_sec})
 
     def test_axfrreq_xfrfail(self):
         '''tests that axfrreqv4 or axfrreqv6 and xfrfail counters are
@@ -2236,8 +2253,47 @@ class TestStatisticsXfrinAXFRv4(TestStatisticsXfrinConn):
             self.conn._handle_xfrin_responses = exception_raiser
             self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
             count += 1
-            self._check_updated_statistics({'axfrreq' + self._ipver: count,
-                                            'xfrfail': count})
+            self._check_updated_perzone_statistics({'axfrreq' + self._ipver:
+                                                        count,
+                                                    'xfrfail': count})
+
+    def test_soa_in_progress1(self):
+        '''tests that an soa_in_progress counter is incremented and decremented
+        when an soa query succeeds'''
+        self.conn.response_generator = self._create_soa_response_data
+        self._check_init_statistics()
+        self.assertEqual(self.conn._check_soa_serial(), XFRIN_OK)
+        self._check_updated_statistics({'soa_in_progress': 0})
+
+    def test_soa_in_progress2(self):
+        '''tests that an soa_in_progress counter is incremented and decremented
+        even if socket.error is raised from XfrinConnection._send_query()'''
+        def exception_raiser(x):
+            raise socket.error()
+        self.conn._send_query = exception_raiser
+        self._check_init_statistics()
+        self.assertRaises(socket.error, self.conn._check_soa_serial)
+        self._check_updated_statistics({'soa_in_progress': 0})
+
+    def test_axfr_running1(self):
+        '''tests that axfr_running counter is incremented and decremented'''
+        self.conn.response_generator = self._create_normal_response_data
+        self._check_init_statistics()
+        self.assertEqual(self.conn.do_xfrin(False), XFRIN_OK)
+        self._check_updated_statistics({'axfr_running': 0})
+
+    def test_axfr_running2(self):
+        '''tests that axfr_running counter is incremented and decremented even
+        if some failure exceptions are expected to be raised inside do_xfrin():
+        XfrinZoneError, XfrinProtocolError, XfrinException, and Exception'''
+        self._check_init_statistics()
+        for ex in [XfrinZoneError, XfrinProtocolError, XfrinException,
+                   Exception]:
+            def exception_raiser():
+                raise ex()
+            self.conn._handle_xfrin_responses = exception_raiser
+            self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+            self._check_updated_statistics({'axfr_running': 0})
 
 class TestStatisticsXfrinIXFRv4(TestStatisticsXfrinConn):
     '''Xfrin IXFR tests for IPv4 to check statistics counters'''
@@ -2252,10 +2308,10 @@ class TestStatisticsXfrinIXFRv4(TestStatisticsXfrinConn):
         self.conn.response_generator = create_ixfr_response
         self._check_init_statistics()
         self.assertEqual(XFRIN_OK, self.conn.do_xfrin(False, RRType.IXFR))
-        self._check_updated_statistics({'ixfrreq' + self._ipver: 1,
-                                        'xfrsuccess': 1,
-                                        'last_ixfr_duration':
-                                            self._const_sec})
+        self._check_updated_perzone_statistics({'ixfrreq' + self._ipver: 1,
+                                                'xfrsuccess': 1,
+                                                'last_ixfr_duration':
+                                                    self._const_sec})
 
     def test_ixfrreq_xfrsuccess_last_ixfr_duration2(self):
         '''tests that ixfrreqv4 or ixfrreqv6 and xfrsuccess counters
@@ -2266,10 +2322,10 @@ class TestStatisticsXfrinIXFRv4(TestStatisticsXfrinConn):
         self.conn._handle_xfrin_responses = exception_raiser
         self._check_init_statistics()
         self.assertEqual(self.conn.do_xfrin(False, RRType.IXFR), XFRIN_OK)
-        self._check_updated_statistics({'ixfrreq' + self._ipver: 1,
-                                        'xfrsuccess': 1,
-                                        'last_ixfr_duration':
-                                            self._const_sec})
+        self._check_updated_perzone_statistics({'ixfrreq' + self._ipver: 1,
+                                                'xfrsuccess': 1,
+                                                'last_ixfr_duration':
+                                                    self._const_sec})
 
     def test_ixfrreq_xfrfail(self):
         '''tests that ixfrreqv4 or ixfrreqv6 and xfrfail counters are
@@ -2285,8 +2341,36 @@ class TestStatisticsXfrinIXFRv4(TestStatisticsXfrinConn):
             self.conn._handle_xfrin_responses = exception_raiser
             self.assertEqual(self.conn.do_xfrin(False, RRType.IXFR), XFRIN_FAIL)
             count += 1
-            self._check_updated_statistics({'ixfrreq' + self._ipver: count,
-                                            'xfrfail': count})
+            self._check_updated_perzone_statistics({'ixfrreq' + self._ipver:
+                                                        count,
+                                                    'xfrfail': count})
+
+    def test_ixfr_running1(self):
+        '''tests that ixfr_running counter is incremented and decremented'''
+        def create_ixfr_response():
+            self.conn.reply_data = self.conn.create_response_data(
+                questions=[Question(TEST_ZONE_NAME, TEST_RRCLASS,
+                                    RRType.IXFR)],
+                answers=[soa_rrset, begin_soa_rrset, soa_rrset, soa_rrset])
+        self.conn.response_generator = create_ixfr_response
+        self._check_init_statistics()
+        self.assertEqual(XFRIN_OK, self.conn.do_xfrin(False, RRType.IXFR))
+        self._check_updated_statistics({'ixfr_running': 0})
+
+    def test_ixfr_running2(self):
+        '''tests that ixfr_running counter is incremented and decremented even
+        if some failure exceptions are expected to be raised inside do_xfrin():
+        XfrinZoneError, XfrinProtocolError, XfrinException, and Exception'''
+        self._check_init_statistics()
+        count = 0
+        for ex in [XfrinZoneError, XfrinProtocolError, XfrinException,
+                   Exception]:
+            def exception_raiser():
+                raise ex()
+            self.conn._handle_xfrin_responses = exception_raiser
+            self.assertEqual(self.conn.do_xfrin(False, RRType.IXFR), XFRIN_FAIL)
+            count += 1
+            self._check_updated_statistics({'ixfr_running': 0})
 
 class TestStatisticsXfrinAXFRv6(TestStatisticsXfrinAXFRv4):
     '''Same tests as TestStatisticsXfrinAXFRv4 for IPv6'''
@@ -3198,7 +3282,9 @@ class TestXfrinProcess(unittest.TestCase):
         xfrin.process_xfrin(self, XfrinRecorder(), Name("example.org."),
                             RRClass.IN, None, zone_soa, None,
                             TEST_MASTER_IPV4_ADDRINFO, True, None,
-                            request_ixfr, self.__get_connection)
+                            request_ixfr,
+                            xfrin.Counters(xfrin.SPECFILE_LOCATION),
+                            self.__get_connection)
         self.assertEqual([], self.__rets)
         self.assertEqual(transfers, self.__transfers)
         # Create a connection for each attempt
@@ -3409,6 +3495,125 @@ class TestXfrinTransferStats(unittest.TestCase):
         zbps = self.stats.get_bytes_per_second()
         self.assertEqual(0, zbps)
 
+class TestXfrinConnectionSocketCounter(unittest.TestCase):
+
+    @property
+    def _master_addrinfo(self):
+        return TEST_MASTER_IPV4_ADDRINFO
+    @property
+    def _ipver(self):
+        return 'ipv4'
+
+    def setUp(self):
+        self.conn = XfrinConnection(
+            None, TEST_ZONE_NAME, None, MockDataSourceClient(), None,
+            self._master_addrinfo, None,
+            xfrin.Counters(xfrin.SPECFILE_LOCATION))
+        self.expception = socket.error
+
+    def raise_expception(self, *args):
+        raise self.expception
+
+    def test_open(self):
+        self.assertRaises(isc.cc.data.DataNotFoundError,
+                          self.conn._counters.get,
+                          'socket', self._ipver, 'tcp', 'open')
+        self.conn.create_socket(self._master_addrinfo[0],
+                                self._master_addrinfo[1])
+        self.assertEqual(1, self.conn._counters.get(
+                'socket', self._ipver, 'tcp', 'open'))
+
+    def test_openfail(self):
+        self.assertRaises(isc.cc.data.DataNotFoundError,
+                          self.conn._counters.get,
+                          'socket', self._ipver, 'tcp', 'openfail')
+        orig_create_socket = xfrin.asyncore.dispatcher.create_socket
+        xfrin.asyncore.dispatcher.create_socket = self.raise_expception
+        try:
+            self.assertRaises(self.expception, self.conn.create_socket,
+                              self._master_addrinfo[0],
+                              self._master_addrinfo[1])
+            self.assertEqual(1, self.conn._counters.get(
+                    'socket', self._ipver, 'tcp', 'openfail'))
+        finally:
+            xfrin.asyncore.dispatcher.create_socket = orig_create_socket
+
+    def test_close(self):
+        self.assertRaises(isc.cc.data.DataNotFoundError,
+                          self.conn._counters.get,
+                          'socket', self._ipver, 'tcp', 'close')
+        orig_socket_close = xfrin.asyncore.dispatcher.close
+        xfrin.asyncore.dispatcher.close = lambda x: None
+        try:
+            self.conn.close()
+            self.assertEqual(1, self.conn._counters.get(
+                    'socket', self._ipver, 'tcp', 'close'))
+        finally:
+            xfrin.asyncore.dispatcher.close = orig_socket_close
+
+    def test_conn(self):
+        self.assertRaises(isc.cc.data.DataNotFoundError,
+                          self.conn._counters.get,
+                          'socket', self._ipver, 'tcp', 'conn')
+        orig_socket_connect = xfrin.asyncore.dispatcher.connect
+        xfrin.asyncore.dispatcher.connect = lambda *a: None
+        try:
+            self.conn.connect(self._master_addrinfo[2])
+            self.assertEqual(1, self.conn._counters.get(
+                    'socket', self._ipver, 'tcp', 'conn'))
+        finally:
+            xfrin.asyncore.dispatcher.connect = orig_socket_connect
+
+    def test_connfail(self):
+        self.assertRaises(isc.cc.data.DataNotFoundError,
+                          self.conn._counters.get,
+                          'socket', self._ipver, 'tcp', 'connfail')
+        orig_socket_connect = xfrin.asyncore.dispatcher.connect
+        xfrin.asyncore.dispatcher.connect = self.raise_expception
+        try:
+            self.assertRaises(self.expception, self.conn.connect,
+                              self._master_addrinfo[2])
+            self.assertFalse(self.conn.connect_to_master())
+            self.assertEqual(2, self.conn._counters.get(
+                    'socket', self._ipver, 'tcp', 'connfail'))
+        finally:
+            xfrin.asyncore.dispatcher.connect = orig_socket_connect
+
+    def test_senderr(self):
+        self.assertRaises(isc.cc.data.DataNotFoundError,
+                          self.conn._counters.get,
+                          'socket', self._ipver, 'tcp', 'senderr')
+        orig_socket_send = xfrin.asyncore.dispatcher.send
+        xfrin.asyncore.dispatcher.send = self.raise_expception
+        try:
+            self.assertRaises(self.expception, self.conn.send, None)
+            self.assertEqual(1, self.conn._counters.get(
+                    'socket', self._ipver, 'tcp', 'senderr'))
+        finally:
+            xfrin.asyncore.dispatcher.send = orig_socket_send
+
+    def test_recverr(self):
+        self.assertRaises(isc.cc.data.DataNotFoundError,
+                          self.conn._counters.get,
+                          'socket', self._ipver, 'tcp', 'recverr')
+        orig_socket_recv = xfrin.asyncore.dispatcher.recv
+        xfrin.asyncore.dispatcher.recv = self.raise_expception
+        try:
+            self.assertRaises(self.expception, self.conn.recv, None)
+            self.assertEqual(1, self.conn._counters.get(
+                    'socket', self._ipver, 'tcp', 'recverr'))
+        finally:
+            xfrin.asyncore.dispatcher.recv = orig_socket_recv
+
+class TestXfrinConnectionSocketCounterV6(TestXfrinConnectionSocketCounter):
+
+    @property
+    def _master_addrinfo(self):
+        return TEST_MASTER_IPV6_ADDRINFO
+    @property
+    def _ipver(self):
+        return 'ipv6'
+
 if __name__== "__main__":
     try:
         isc.log.resetUnitTestRootLogger()
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index 16c8532..a894d55 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -31,6 +31,7 @@ from isc.config.ccsession import *
 from isc.statistics.dns import Counters
 from isc.notify import notify_out
 import isc.util.process
+import isc.util.traceback_handler
 from isc.util.address_formatter import AddressFormatter
 from isc.datasrc import DataSourceClient, ZoneFinder
 import isc.net.parse
@@ -563,8 +564,8 @@ class XfrinConnection(asyncore.dispatcher):
 
     def __init__(self,
                  sock_map, zone_name, rrclass, datasrc_client,
-                 shutdown_event, master_addrinfo, zone_soa, tsig_key=None,
-                 idle_timeout=60):
+                 shutdown_event, master_addrinfo, zone_soa, counters,
+                 tsig_key=None, idle_timeout=60):
         """Constructor of the XfirnConnection class.
 
         Parameters:
@@ -579,6 +580,7 @@ class XfrinConnection(asyncore.dispatcher):
             address and port of the master server.
           zone_soa (RRset or None): SOA RRset of zone's current SOA or None
             if it's not available.
+          counters (Counters): used for statistics counters
           idle_timeout (int): max idle time for read data from socket.
 
         """
@@ -609,6 +611,11 @@ class XfrinConnection(asyncore.dispatcher):
         self._shutdown_event = shutdown_event
         self._master_addrinfo = master_addrinfo
         self._tsig_key = tsig_key
+        # self.tsig_key_name is used for outputting an error massage in
+        # connect_to_master().
+        self.tsig_key_name = None
+        if tsig_key:
+            self.tsig_key_name = self._tsig_key.get_key_name()
         self._tsig_ctx = None
         # tsig_ctx_creator is introduced to allow tests to use a mock class for
         # easier tests (in normal case we always use the default)
@@ -617,7 +624,75 @@ class XfrinConnection(asyncore.dispatcher):
         # keep a record of this specific transfer to log on success
         # (time, rr/s, etc)
         self._transfer_stats = XfrinTransferStats()
-        self._counters = Counters(SPECFILE_LOCATION)
+        self._counters = counters
+
+    def create_socket(self, family, type):
+        """create_socket() overridden from the super class for
+        statistics counter open and openfail"""
+        try:
+            ret = super().create_socket(family, type)
+            # count open
+            self._counters.inc('socket',
+                               'ip' + self._get_ipver_str(),
+                               'tcp', 'open')
+            return ret
+        except:
+            # count openfail
+            self._counters.inc('socket',
+                               'ip' + self._get_ipver_str(),
+                               'tcp', 'openfail')
+            raise
+
+    def close(self):
+        """close() overridden from the super class for
+        statistics counter close"""
+        ret = super().close()
+        # count close
+        self._counters.inc('socket',
+                           'ip' + self._get_ipver_str(),
+                           'tcp', 'close')
+        return ret
+
+    def connect(self, address):
+        """connect() overridden from the super class for
+        statistics counter conn and connfail"""
+        try:
+            ret = super().connect(address)
+            # count conn
+            self._counters.inc('socket',
+                               'ip' + self._get_ipver_str(),
+                               'tcp', 'conn')
+            return ret
+        except:
+            # count connfail
+            self._counters.inc('socket',
+                               'ip' + self._get_ipver_str(),
+                               'tcp', 'connfail')
+            raise
+
+    def send(self, data):
+        """send() overridden from the super class for
+        statistics counter senderr"""
+        try:
+            return super().send(data)
+        except:
+            # count senderr
+            self._counters.inc('socket',
+                               'ip' + self._get_ipver_str(),
+                               'tcp', 'senderr')
+            raise
+
+    def recv(self, buffer_size):
+        """recv() overridden from the super class for
+        statistics counter senderr"""
+        try:
+            return super().recv(buffer_size)
+        except:
+            # count recverr
+            self._counters.inc('socket',
+                               'ip' + self._get_ipver_str(),
+                               'tcp', 'recverr')
+            raise
 
     def init_socket(self):
         '''Initialize the underlyig socket.
@@ -859,9 +934,9 @@ class XfrinConnection(asyncore.dispatcher):
         It raises a ValueError exception on other address families.
 
         """
-        if self.socket.family == socket.AF_INET:
+        if self._master_addrinfo[0] == socket.AF_INET:
             return 'v4'
-        elif self.socket.family == socket.AF_INET6:
+        elif self._master_addrinfo[0] == socket.AF_INET6:
             return 'v6'
         raise ValueError("Invalid address family. "
                          "This is supported only for IP sockets")
@@ -875,37 +950,44 @@ class XfrinConnection(asyncore.dispatcher):
 
         '''
 
-        self._send_query(RRType.SOA)
-        # count soaoutv4 or soaoutv6 requests
-        self._counters.inc('zones', self._rrclass.to_text(),
-                           self._zone_name.to_text(), 'soaout' +
-                           self._get_ipver_str())
-        data_len = self._get_request_response(2)
-        msg_len = socket.htons(struct.unpack('H', data_len)[0])
-        soa_response = self._get_request_response(msg_len)
-        msg = Message(Message.PARSE)
-        msg.from_wire(soa_response, Message.PRESERVE_ORDER)
-
-        # Validate/parse the rest of the response, and extract the SOA
-        # from the answer section
-        soa = self.__parse_soa_response(msg, soa_response)
-
-        # Compare the two serials.  If ours is 'new', abort with ZoneUptodate.
-        primary_serial = get_soa_serial(soa.get_rdata()[0])
-        if self._request_serial is not None and \
-                self._request_serial >= primary_serial:
-            if self._request_serial != primary_serial:
-                logger.info(XFRIN_ZONE_SERIAL_AHEAD, primary_serial,
-                            self.zone_str(),
-                            format_addrinfo(self._master_addrinfo),
-                            self._request_serial)
-            raise XfrinZoneUptodate
-
-        return XFRIN_OK
+        # increment SOA query in progress
+        self._counters.inc('soa_in_progress')
+        try:
+            self._send_query(RRType.SOA)
+            # count soaoutv4 or soaoutv6 requests
+            self._counters.inc('zones', self._rrclass.to_text(),
+                               self._zone_name.to_text(), 'soaout' +
+                               self._get_ipver_str())
+            data_len = self._get_request_response(2)
+            msg_len = socket.htons(struct.unpack('H', data_len)[0])
+            soa_response = self._get_request_response(msg_len)
+            msg = Message(Message.PARSE)
+            msg.from_wire(soa_response, Message.PRESERVE_ORDER)
+
+            # Validate/parse the rest of the response, and extract the SOA
+            # from the answer section
+            soa = self.__parse_soa_response(msg, soa_response)
+
+            # Compare the two serials.  If ours is 'new', abort with ZoneUptodate.
+            primary_serial = get_soa_serial(soa.get_rdata()[0])
+            if self._request_serial is not None and \
+                    self._request_serial >= primary_serial:
+                if self._request_serial != primary_serial:
+                    logger.info(XFRIN_ZONE_SERIAL_AHEAD, primary_serial,
+                                self.zone_str(),
+                                format_addrinfo(self._master_addrinfo),
+                                self._request_serial)
+                raise XfrinZoneUptodate
+
+            return XFRIN_OK
+        finally:
+            # decrement SOA query in progress
+            self._counters.dec('soa_in_progress')
 
     def do_xfrin(self, check_soa, request_type=RRType.AXFR):
         '''Do an xfr session by sending xfr request and parsing responses.'''
 
+        xfer_started = False # Don't set True until xfer is started
         try:
             ret = XFRIN_OK
             self._request_type = request_type
@@ -917,6 +999,9 @@ class XfrinConnection(asyncore.dispatcher):
                 if not self.connect_to_master():
                     raise XfrinException('Unable to reconnect to master')
 
+            xfer_started = True
+            # increment xfer running
+            self._counters.inc(req_str.lower() + '_running')
             # start statistics timer
             # Note: If the timer for the zone is already started but
             # not yet stopped due to some error, the last start time
@@ -1007,6 +1092,9 @@ class XfrinConnection(asyncore.dispatcher):
                                           self._zone_name.to_text(),
                                           'last_' + req_str.lower() +
                                           '_duration')
+            # decrement xfer running only if started
+            if xfer_started:
+                self._counters.dec(req_str.lower() + '_running')
             # Make sure any remaining transaction in the diff is closed
             # (if not yet - possible in case of xfr-level exception) as soon
             # as possible
@@ -1107,7 +1195,7 @@ def __get_initial_xfr_type(zone_soa, request_ixfr, zname, zclass, master_addr):
 
 def __process_xfrin(server, zone_name, rrclass, datasrc_client, zone_soa,
                     shutdown_event, master_addrinfo, check_soa, tsig_key,
-                    request_ixfr, conn_class):
+                    request_ixfr, counters, conn_class):
     conn = None
     exception = None
     ret = XFRIN_FAIL
@@ -1131,7 +1219,7 @@ def __process_xfrin(server, zone_name, rrclass, datasrc_client, zone_soa,
             retry = False
             conn = conn_class(sock_map, zone_name, rrclass, datasrc_client,
                               shutdown_event, master_addrinfo, zone_soa,
-                              tsig_key)
+                              counters, tsig_key)
             conn.init_socket()
             ret = XFRIN_FAIL
             if conn.connect_to_master():
@@ -1178,7 +1266,7 @@ def __process_xfrin(server, zone_name, rrclass, datasrc_client, zone_soa,
 
 def process_xfrin(server, xfrin_recorder, zone_name, rrclass, datasrc_client,
                   zone_soa, shutdown_event, master_addrinfo, check_soa,
-                  tsig_key, request_ixfr, conn_class=XfrinConnection):
+                  tsig_key, request_ixfr, counters, conn_class=XfrinConnection):
     # Even if it should be rare, the main process of xfrin session can
     # raise an exception.  In order to make sure the lock in xfrin_recorder
     # is released in any cases, we delegate the main part to the helper
@@ -1188,7 +1276,7 @@ def process_xfrin(server, xfrin_recorder, zone_name, rrclass, datasrc_client,
     try:
         __process_xfrin(server, zone_name, rrclass, datasrc_client, zone_soa,
                         shutdown_event, master_addrinfo, check_soa, tsig_key,
-                        request_ixfr, conn_class)
+                        request_ixfr, counters, conn_class)
     except Exception as ex:
         # don't log it until we complete decrement().
         exception = ex
@@ -1753,7 +1841,8 @@ class Xfrin:
                                               datasrc_client, zone_soa,
                                               self._shutdown_event,
                                               master_addrinfo, check_soa,
-                                              tsig_key, request_ixfr))
+                                              tsig_key, request_ixfr,
+                                              self._counters))
 
         xfrin_thread.start()
         return (0, 'zone xfrin is started')
@@ -1847,4 +1936,4 @@ def main(xfrin_class, use_signal=True):
     logger.info(XFRIN_EXITING)
 
 if __name__ == '__main__':
-    main(Xfrin)
+    isc.util.traceback_handler.traceback_handler(lambda: main(Xfrin))
diff --git a/src/bin/xfrin/xfrin.spec b/src/bin/xfrin/xfrin.spec
index d09685b..a662f75 100644
--- a/src/bin/xfrin/xfrin.spec
+++ b/src/bin/xfrin/xfrin.spec
@@ -250,6 +250,251 @@
             ]
           }
         }
+      },
+      {
+        "item_name": "ixfr_running",
+        "item_type": "integer",
+        "item_optional": false,
+        "item_default": 0,
+        "item_title": "IXFRs running",
+        "item_description": "Number of IXFRs in progress"
+      },
+      {
+        "item_name": "axfr_running",
+        "item_type": "integer",
+        "item_optional": false,
+        "item_default": 0,
+        "item_title": "AXFRs running",
+        "item_description": "Number of AXFRs in progress"
+      },
+      {
+        "item_name": "soa_in_progress",
+        "item_type": "integer",
+        "item_optional": false,
+        "item_default": 0,
+        "item_title": "SOA queries",
+        "item_description": "Number of SOA queries in progress"
+      },
+      {
+        "item_name": "socket",
+        "item_type": "map",
+        "item_optional": false,
+        "item_default": {
+          "ipv4": {
+            "tcp": {
+              "open": 0,
+              "openfail": 0,
+              "close": 0,
+              "connfail": 0,
+              "conn": 0,
+              "senderr": 0,
+              "recverr": 0
+            }
+          },
+          "ipv6": {
+            "tcp": {
+              "open": 0,
+              "openfail": 0,
+              "close": 0,
+              "connfail": 0,
+              "conn": 0,
+              "senderr": 0,
+              "recverr": 0
+            }
+          }
+        },
+        "item_title": "Socket",
+        "item_description": "A directory name of socket statistics",
+        "map_item_spec": [
+          {
+            "item_name": "ipv4",
+            "item_type": "map",
+            "item_optional": false,
+            "item_default": {
+              "tcp": {
+                "open": 0,
+                "openfail": 0,
+                "close": 0,
+                "connfail": 0,
+                "conn": 0,
+                "senderr": 0,
+                "recverr": 0
+              }
+            },
+            "item_title": "IPv4",
+            "item_description": "A directory name of IPv4",
+            "map_item_spec": [
+              {
+                "item_name": "tcp",
+                "item_type": "map",
+                "item_optional": false,
+                "item_default": {
+                  "open": 0,
+                  "openfail": 0,
+                  "close": 0,
+                  "connfail": 0,
+                  "conn": 0,
+                  "senderr": 0,
+                  "recverr": 0
+                },
+                "item_title": "TCP",
+                "item_description": "A directory name of TCP statistics",
+                "map_item_spec": [
+                  {
+                    "item_name": "open",
+                    "item_type": "integer",
+                    "item_optional": false,
+                    "item_default": 0,
+                    "item_title": "Open",
+                    "item_description": "IPv4 TCP sockets opened successfully"
+                  },
+                  {
+                    "item_name": "openfail",
+                    "item_type": "integer",
+                    "item_optional": false,
+                    "item_default": 0,
+                    "item_title": "Open failures",
+                    "item_description": "IPv4 TCP sockets open failures"
+                  },
+                  {
+                    "item_name": "close",
+                    "item_type": "integer",
+                    "item_optional": false,
+                    "item_default": 0,
+                    "item_title": "Close",
+                    "item_description": "IPv4 TCP sockets closed"
+                  },
+                  {
+                    "item_name": "connfail",
+                    "item_type": "integer",
+                    "item_optional": false,
+                    "item_default": 0,
+                    "item_title": "Connect failures",
+                    "item_description": "IPv4 TCP sockets connection failures"
+                  },
+                  {
+                    "item_name": "conn",
+                    "item_type": "integer",
+                    "item_optional": false,
+                    "item_default": 0,
+                    "item_title": "Connect",
+                    "item_description": "IPv4 TCP connections established successfully"
+                  },
+                  {
+                    "item_name": "senderr",
+                    "item_type": "integer",
+                    "item_optional": false,
+                    "item_default": 0,
+                    "item_title": "Send errors",
+                    "item_description": "IPv4 TCP sockets send errors"
+                  },
+                  {
+                    "item_name": "recverr",
+                    "item_type": "integer",
+                    "item_optional": false,
+                    "item_default": 0,
+                    "item_title": "Receive errors",
+                    "item_description": "IPv4 TCP sockets receive errors"
+                  }
+                ]
+              }
+            ]
+          },
+          {
+            "item_name": "ipv6",
+            "item_type": "map",
+            "item_optional": false,
+            "item_default": {
+              "tcp": {
+                "open": 0,
+                "openfail": 0,
+                "close": 0,
+                "connfail": 0,
+                "conn": 0,
+                "senderr": 0,
+                "recverr": 0
+              }
+            },
+            "item_title": "IPv6",
+            "item_description": "A directory name of IPv6",
+            "map_item_spec": [
+              {
+                "item_name": "tcp",
+                "item_type": "map",
+                "item_optional": false,
+                "item_default": {
+                  "open": 0,
+                  "openfail": 0,
+                  "close": 0,
+                  "connfail": 0,
+                  "conn": 0,
+                  "senderr": 0,
+                  "recverr": 0
+                },
+                "item_title": "TCP",
+                "item_description": "A directory name of TCP statistics",
+                "map_item_spec": [
+                  {
+                    "item_name": "open",
+                    "item_type": "integer",
+                    "item_optional": false,
+                    "item_default": 0,
+                    "item_title": "Open",
+                    "item_description": "IPv6 TCP sockets opened successfully"
+                  },
+                  {
+                    "item_name": "openfail",
+                    "item_type": "integer",
+                    "item_optional": false,
+                    "item_default": 0,
+                    "item_title": "Open failures",
+                    "item_description": "IPv6 TCP sockets open failures"
+                  },
+                  {
+                    "item_name": "close",
+                    "item_type": "integer",
+                    "item_optional": false,
+                    "item_default": 0,
+                    "item_title": "Close",
+                    "item_description": "IPv6 TCP sockets closed"
+                  },
+                  {
+                    "item_name": "connfail",
+                    "item_type": "integer",
+                    "item_optional": false,
+                    "item_default": 0,
+                    "item_title": "Connect failures",
+                    "item_description": "IPv6 TCP sockets connection failures"
+                  },
+                  {
+                    "item_name": "conn",
+                    "item_type": "integer",
+                    "item_optional": false,
+                    "item_default": 0,
+                    "item_title": "Connect",
+                    "item_description": "IPv6 TCP connections established successfully"
+                  },
+                  {
+                    "item_name": "senderr",
+                    "item_type": "integer",
+                    "item_optional": false,
+                    "item_default": 0,
+                    "item_title": "Send errors",
+                    "item_description": "IPv6 TCP sockets send errors"
+                  },
+                  {
+                    "item_name": "recverr",
+                    "item_type": "integer",
+                    "item_optional": false,
+                    "item_default": 0,
+                    "item_title": "Receive errors",
+                    "item_description": "IPv6 TCP sockets receive errors"
+                  }
+                ]
+              }
+            ]
+          }
+        ]
       }
     ]
   }
diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in
index d20ebe8..f7ed1e1 100644
--- a/src/bin/xfrout/tests/xfrout_test.py.in
+++ b/src/bin/xfrout/tests/xfrout_test.py.in
@@ -309,7 +309,8 @@ class TestXfroutSessionBase(unittest.TestCase):
                                        # When not testing ACLs, simply accept
                                        isc.acl.dns.REQUEST_LOADER.load(
                                            [{"action": "ACCEPT"}]),
-                                       {})
+                                       {},
+                                       xfrout.Counters(xfrout.SPECFILE_LOCATION))
         self.set_request_type(RRType.AXFR) # test AXFR by default
         self.mdata = self.create_request_data()
         self.soa_rrset = create_soa(SOA_CURRENT_VERSION)
@@ -1323,7 +1324,8 @@ class TestUnixSockServer(unittest.TestCase):
             # This would be the handler class, but we just check it is passed
             # the right parametes, so function is enough for that.
             keys = isc.server_common.tsig_keyring.get_keyring()
-            def handler(sock, data, server, keyring, address, acl, config):
+            def handler(sock, data, server, keyring, address, acl, config,
+                        counters):
                 self.assertEqual("sock", sock)
                 self.assertEqual("data", data)
                 self.assertEqual(self.unix, server)
@@ -1331,6 +1333,7 @@ class TestUnixSockServer(unittest.TestCase):
                 self.assertEqual("Address", address)
                 self.assertEqual("acl", acl)
                 self.assertEqual("Zone config", config)
+                self.assertIs(self.unix._counters, counters)
             self.unix.RequestHandlerClass = handler
             self.unix.finish_request("sock", "data")
         finally:
@@ -1629,7 +1632,9 @@ class TestUnixSockServerForCounter(unittest.TestCase):
         xfrout.ThreadingUnixStreamServer = DummySocketserver
         xfrout.super = lambda : DummySocketserver()
         xfrout.select.select = lambda x,y,z: ([None],[None],[None])
-        self.unix = UnixSockServer(None, None, threading.Event(), None, None)
+        self._counters = xfrout.Counters(xfrout.SPECFILE_LOCATION)
+        self.unix = UnixSockServer(None, None, threading.Event(), None, None,
+                                   self._counters)
 
     def tearDown(self):
         ( UnixSockServer._remove_unused_sock_file,
@@ -1659,7 +1664,8 @@ class TestUnixSockServerForCounter(unittest.TestCase):
                           'socket', 'unixdomain', 'openfail')
         xfrout.ThreadingUnixStreamServer = DummySocketserverException
         try:
-            self.unix = UnixSockServer(None, None, None, None, None)
+            self.unix = UnixSockServer(None, None, None, None, None,
+                                       self._counters)
         except Exception:
             pass
         else:
@@ -1700,7 +1706,7 @@ class TestUnixSockServerForCounter(unittest.TestCase):
                           self.unix._counters.get,
                           'socket', 'unixdomain', 'acceptfail')
         xfrout.super = lambda : DummyClassException()
-        self.unix = UnixSockServer(None, None, None, None, None)
+        self.unix = UnixSockServer(None, None, None, None, None, self._counters)
         self.assertRaises(Exception, self.unix.get_request)
         self.assertEqual(
             self.unix._counters.get('socket', 'unixdomain', 'acceptfail'), 1)
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index bcb0d53..aa84587 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -30,6 +30,7 @@ from isc.cc import SessionError, SessionTimeout
 from isc.statistics.dns import Counters
 from isc.notify import notify_out
 import isc.util.process
+import isc.util.traceback_handler
 import fcntl
 import socket
 import select
@@ -176,7 +177,8 @@ def make_blocking(filenum, on):
 
 class XfroutSession():
     def __init__(self, sock_fd, request_data, server, tsig_key_ring, remote,
-                 default_acl, zone_config, client_class=DataSourceClient):
+                 default_acl, zone_config, counters,
+                 client_class=DataSourceClient):
         self._sock_fd = sock_fd
         self._request_data = request_data
         self._server = server
@@ -193,7 +195,7 @@ class XfroutSession():
         self._jnl_reader = None # will be set to a reader for IXFR
         # Creation of self.counters should be done before of
         # invoking self._handle()
-        self._counters = Counters(SPECFILE_LOCATION)
+        self._counters = counters
         self._handle()
 
     def create_tsig_ctx(self, tsig_record, tsig_key_ring):
@@ -683,11 +685,11 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
     '''The unix domain socket server which accept xfr query sent from auth server.'''
 
     def __init__(self, sock_file, handle_class, shutdown_event, config_data,
-                 cc):
+                 cc, counters):
         self._remove_unused_sock_file(sock_file)
         self._sock_file = sock_file
         socketserver_mixin.NoPollMixIn.__init__(self)
-        self._counters = Counters(SPECFILE_LOCATION)
+        self._counters = counters
         try:
             ThreadingUnixStreamServer.__init__(self, sock_file, \
                                                    handle_class)
@@ -886,7 +888,8 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
         self._lock.release()
         self.RequestHandlerClass(sock_fd, request_data, self,
                                  isc.server_common.tsig_keyring.get_keyring(),
-                                 self._guess_remote(sock_fd), acl, zone_config)
+                                 self._guess_remote(sock_fd), acl, zone_config,
+                                 self._counters)
 
     def _remove_unused_sock_file(self, sock_file):
         '''Try to remove the socket file. If the file is being used
@@ -1025,12 +1028,12 @@ class XfroutServer:
         self._shutdown_event = threading.Event()
         self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler)
         self._config_data = self._cc.get_full_config()
+        self._counters = Counters(SPECFILE_LOCATION)
         self._cc.start()
         self._cc.add_remote_config(AUTH_SPECFILE_LOCATION)
         isc.server_common.tsig_keyring.init_keyring(self._cc)
         self._start_xfr_query_listener()
         self._start_notifier()
-        self._counters = Counters(SPECFILE_LOCATION)
 
     def _start_xfr_query_listener(self):
         '''Start a new thread to accept xfr query. '''
@@ -1039,13 +1042,13 @@ class XfroutServer:
             XfroutSession,
             self._shutdown_event,
             self._config_data,
-            self._cc)
+            self._cc, self._counters)
         listener = threading.Thread(target=self._unix_socket_server.serve_forever)
         listener.start()
 
     def _start_notifier(self):
         datasrc = self._unix_socket_server.get_db_file()
-        self._notifier = notify_out.NotifyOut(datasrc)
+        self._notifier = notify_out.NotifyOut(datasrc, counters=self._counters)
         if 'also_notify' in self._config_data:
             for slave in self._config_data['also_notify']:
                 address = self._default_notify_address
@@ -1164,7 +1167,7 @@ def set_cmd_options(parser):
     parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
             help="display more about what is going on")
 
-if '__main__' == __name__:
+def main():
     try:
         parser = OptionParser()
         set_cmd_options(parser)
@@ -1189,3 +1192,6 @@ if '__main__' == __name__:
         xfrout_server.shutdown()
 
     logger.info(XFROUT_EXITING)
+
+if '__main__' == __name__:
+    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in
index 2d5167b..622336d 100755
--- a/src/bin/zonemgr/zonemgr.py.in
+++ b/src/bin/zonemgr/zonemgr.py.in
@@ -37,6 +37,7 @@ import errno
 from optparse import OptionParser, OptionValueError
 from isc.config.ccsession import *
 import isc.util.process
+import isc.util.traceback_handler
 from isc.log_messages.zonemgr_messages import *
 from isc.notify import notify_out
 from isc.server_common.datasrc_clients_mgr import DataSrcClientsMgr, ConfigError
@@ -802,7 +803,7 @@ def set_cmd_options(parser):
     parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
             help="display more about what is going on")
 
-if '__main__' == __name__:
+def main():
     try:
         logger.debug(DBG_START_SHUT, ZONEMGR_STARTING)
         parser = OptionParser()
@@ -830,3 +831,6 @@ if '__main__' == __name__:
         zonemgrd.shutdown()
 
     logger.info(ZONEMGR_SHUTDOWN)
+
+if '__main__' == __name__:
+    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/hooks/Makefile.am b/src/hooks/Makefile.am
new file mode 100644
index 0000000..815beed
--- /dev/null
+++ b/src/hooks/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = dhcp
diff --git a/src/hooks/dhcp/Makefile.am b/src/hooks/dhcp/Makefile.am
new file mode 100644
index 0000000..6785617
--- /dev/null
+++ b/src/hooks/dhcp/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = user_chk
diff --git a/src/hooks/dhcp/user_chk/Makefile.am b/src/hooks/dhcp/user_chk/Makefile.am
new file mode 100644
index 0000000..874719b
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/Makefile.am
@@ -0,0 +1,65 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS  = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS  = $(B10_CXXFLAGS)
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+# Until logging in dynamically loaded libraries is fixed,
+# Define rule to build logging source files from message file
+# user_chk_messages.h user_chk_messages.cc: s-messages
+
+# Until logging in dynamically loaded libraries is fixed, exclude these.
+# s-messages: user_chk_messages.mes
+#	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/hooks/dhcp/user_chk/user_chk_messages.mes
+#	touch $@
+
+# Tell automake that the message files are built as part of the build process
+# (so that they are built before the main library is built).
+# BUILT_SOURCES = user_chk_messages.h user_chk_messages.cc
+BUILT_SOURCES =
+
+# Ensure that the message file is included in the distribution
+EXTRA_DIST =
+
+# Get rid of generated message files on a clean
+#CLEANFILES = *.gcno *.gcda user_chk_messages.h user_chk_messages.cc s-messages
+CLEANFILES = *.gcno *.gcda
+
+lib_LTLIBRARIES = libdhcp_user_chk.la
+libdhcp_user_chk_la_SOURCES  =
+libdhcp_user_chk_la_SOURCES += load_unload.cc
+libdhcp_user_chk_la_SOURCES += subnet_select_co.cc
+libdhcp_user_chk_la_SOURCES += user.cc user.h
+# Until logging in dynamically loaded libraries is fixed, exclude these.
+#libdhcp_user_chk_la_SOURCES += user_chk_log.cc user_chk_log.h
+libdhcp_user_chk_la_SOURCES += user_data_source.h
+libdhcp_user_chk_la_SOURCES += user_file.cc user_file.h
+libdhcp_user_chk_la_SOURCES += user_registry.cc user_registry.h
+libdhcp_user_chk_la_SOURCES += version.cc
+
+# Until logging in dynamically loaded libraries is fixed, exclude these.
+#nodist_libdhcp_user_chk_la_SOURCES = user_chk_messages.cc user_chk_messages.h
+
+libdhcp_user_chk_la_CXXFLAGS = $(AM_CXXFLAGS)
+libdhcp_user_chk_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libdhcp_user_chk_la_LDFLAGS  = $(AM_LDFLAGS)
+libdhcp_user_chk_la_LDFLAGS  += -avoid-version -export-dynamic -module
+libdhcp_user_chk_la_LIBADD  =
+libdhcp_user_chk_la_LIBADD  += $(top_builddir)/src/lib/hooks/libb10-hooks.la
+libdhcp_user_chk_la_LIBADD  += $(top_builddir)/src/lib/log/libb10-log.la
+libdhcp_user_chk_la_LIBADD  += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+libdhcp_user_chk_la_LIBADD  += $(top_builddir)/src/lib/util/libb10-util.la
+libdhcp_user_chk_la_LIBADD  += $(top_builddir)/src/lib/util/threads/libb10-threads.la
+
+
+if USE_CLANGPP
+# Disable unused parameter warning caused by some of the
+# Boost headers when compiling with clang.
+libdhcp_user_chk_la_CXXFLAGS += -Wno-unused-parameter
+endif
diff --git a/src/hooks/dhcp/user_chk/load_unload.cc b/src/hooks/dhcp/user_chk/load_unload.cc
new file mode 100644
index 0000000..79afb82
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/load_unload.cc
@@ -0,0 +1,113 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @file This file defines the load and unload hooks library functions.
+
+#include <hooks/hooks.h>
+#include <user_registry.h>
+#include <user_file.h>
+
+#include <iostream>
+#include <fstream>
+#include <errno.h>
+
+using namespace isc::hooks;
+
+/// @brief Pointer to the registry instance.
+UserRegistryPtr user_registry;
+
+/// @brief Output filestream for recording user check outcomes.
+std::fstream user_chk_output;
+
+/// @brief User registry input file name.
+/// @todo Hard-coded for now, this should be configurable.
+const char* registry_fname = "/tmp/user_chk_registry.txt";
+
+/// @brief User check outcome file name.
+/// @todo Hard-coded for now, this should be configurable.
+const char* user_chk_output_fname = "/tmp/user_chk_outcome.txt";
+
+// Functions accessed by the hooks framework use C linkage to avoid the name
+// mangling that accompanies use of the C++ compiler as well as to avoid
+// issues related to namespaces.
+extern "C" {
+
+/// @brief Called by the Hooks library manager when the library is loaded.
+///
+/// Instantiates the UserRegistry and opens the outcome file. Failure in
+/// either results in a failed return code.
+///
+/// @param unused library handle parameter required by Hooks API.
+///
+/// @return Returns 0 upon success, non-zero upon failure.
+int load(LibraryHandle&) {
+    // non-zero indicates an error.
+    int ret_val = 0;
+    try {
+        // Instantiate the registry.
+        user_registry.reset(new UserRegistry());
+
+        // Create the data source.
+        UserDataSourcePtr user_file(new UserFile(registry_fname));
+
+        // Set the registry's data source
+        user_registry->setSource(user_file);
+
+        // Do an initial load of the registry.
+        user_registry->refresh();
+
+        // Open up the output file for user_chk results.
+        user_chk_output.open(user_chk_output_fname,
+                     std::fstream::out | std::fstream::app);
+
+        if (!user_chk_output) {
+            // Grab the system error message.
+            const char* errmsg = strerror(errno);
+            isc_throw(isc::Unexpected, "Cannot open output file: "
+                                       << user_chk_output_fname
+                                       << " reason: " << errmsg);
+        }
+    }
+    catch (const std::exception& ex) {
+        // Log the error and return failure.
+        std::cout << "DHCP UserCheckHook could not be loaded: "
+                  << ex.what() << std::endl;
+        ret_val = 1;
+    }
+
+    return (ret_val);
+}
+
+/// @brief Called by the Hooks library manager when the library is unloaded.
+///
+/// Destroys the UserRegistry and closes the outcome file.
+///
+/// @return Always returns 0.
+int unload() {
+    try {
+        user_registry.reset();
+        if (user_chk_output.is_open()) {
+            user_chk_output.close();
+        }
+    } catch (const std::exception& ex) {
+        // On the off chance something goes awry, catch it and log it.
+        // @todo Not sure if we should return a non-zero result or not.
+        std::cout << "DHCP UserCheckHook could not be unloaded: "
+                  << ex.what() << std::endl;
+    }
+
+    return (0);
+}
+
+}
diff --git a/src/hooks/dhcp/user_chk/subnet_select_co.cc b/src/hooks/dhcp/user_chk/subnet_select_co.cc
new file mode 100644
index 0000000..3e08f13
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/subnet_select_co.cc
@@ -0,0 +1,237 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @file Defines the subnet4_select and subnet6_select callout functions.
+
+#include <hooks/hooks.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+#include <dhcpsrv/subnet.h>
+#include <user_registry.h>
+
+#include <fstream>
+#include <string>
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace std;
+
+extern UserRegistryPtr user_registry;
+extern std::fstream user_chk_output;
+extern const char* registry_fname;
+extern const char* user_chk_output_fname;
+
+// Functions accessed by the hooks framework use C linkage to avoid the name
+// mangling that accompanies use of the C++ compiler as well as to avoid
+// issues related to namespaces.
+extern "C" {
+
+/// @brief Adds an entry to the end of the user check outcome file.
+///
+/// Each user entry is written in an ini-like format, with one name-value pair
+/// per line as follows:
+///
+/// id_type=<id type>
+/// client=<id str>
+/// subnet=<subnet str>
+/// registered=<is registered>"
+///
+/// where:
+/// <id type> text label of the id type: "HW_ADDR" or "DUID"
+/// <id str> user's id formatted as either isc::dhcp::Hwaddr.toText() or
+/// isc::dhcp::DUID.toText()
+/// <subnet str> selected subnet formatted as isc::dhcp::Subnet4::toText() or
+/// isc::dhcp::Subnet6::toText() as appropriate.
+/// <is registered> "yes" or "no"
+///
+/// Sample IPv4 entry would like this:
+///
+/// @code
+/// id_type=DUID
+/// client=00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:04
+/// subnet=2001:db8:2::/64
+/// registered=yes
+/// id_type=duid
+/// @endcode
+///
+/// Sample IPv4 entry would like this:
+///
+/// @code
+/// id_type=DUID
+/// id_type=HW_ADDR
+/// client=hwtype=1 00:0c:01:02:03:05
+/// subnet=152.77.5.0/24
+/// registered=no
+/// @endcode
+///
+/// @param id_type_str text label identify the id type
+/// @param id_val_str text representation of the user id
+/// @param subnet_str text representation  of the selected subnet
+/// @param registered boolean indicating if the user is registered or not
+void generate_output_record(const std::string& id_type_str,
+                            const std::string& id_val_str,
+                            const std::string& subnet_str,
+                            const bool& registered)
+{
+    user_chk_output << "id_type=" << id_type_str << std::endl
+                    << "client=" << id_val_str << std::endl
+                    << "subnet=" << subnet_str << std::endl
+                    << "registered=" << (registered ? "yes" : "no")
+                    << std::endl;
+
+    // @todo Flush is here to ensure output is immediate for demo purposes.
+    // Performance would generally dictate not using it.
+    flush(user_chk_output);
+}
+
+/// @brief  This callout is called at the "subnet4_select" hook.
+///
+/// This function searches the UserRegistry for the client indicated by the
+/// inbound IPv4 DHCP packet. If the client is found in the registry output
+/// the generate outcome record and return.
+///
+/// If the client is not found in the registry replace the selected subnet
+/// with the restricted access subnet, then generate the outcome record and
+/// return.  By convention, it is assumed that last subnet in the list of
+/// available subnets is the restricted access subnet.
+///
+/// @param handle CalloutHandle which provides access to context.
+///
+/// @return 0 upon success, non-zero otherwise.
+int subnet4_select(CalloutHandle& handle) {
+    if (!user_registry) {
+        std::cout << "DHCP UserCheckHook : subnet4_select UserRegistry is null"
+                  << std::endl;
+        return (1);
+    }
+
+    try {
+        // Get subnet collection. If it's empty just bail nothing to do.
+        const isc::dhcp::Subnet4Collection *subnets = NULL;
+        handle.getArgument("subnet4collection", subnets);
+        if (subnets->empty()) {
+            return 0;
+        }
+
+        // Refresh the registry.
+        user_registry->refresh();
+
+        // Get the HWAddress as the user identifier.
+        Pkt4Ptr query;
+        handle.getArgument("query4", query);
+        HWAddrPtr hwaddr = query->getHWAddr();
+
+        // Look for the user.
+        UserPtr registered_user = user_registry->findUser(*hwaddr);
+        if (registered_user) {
+            // User is in the registry, so leave the pre-selected subnet alone.
+            Subnet4Ptr subnet;
+            handle.getArgument("subnet4", subnet);
+            // Add the outcome entry to the output file.
+            generate_output_record(UserId::HW_ADDRESS_STR, hwaddr->toText(),
+                                   subnet->toText(), true);
+        } else {
+            // User is not in the registry, so assign them to the last subnet
+            // in the collection.  By convention we are assuming this is the
+            // restricted subnet.
+            Subnet4Ptr subnet = subnets->back();
+            handle.setArgument("subnet4", subnet);
+            // Add the outcome entry to the output file.
+            generate_output_record(UserId::HW_ADDRESS_STR, hwaddr->toText(),
+                                   subnet->toText(), false);
+        }
+    } catch (const std::exception& ex) {
+        std::cout << "DHCP UserCheckHook : subnet6_select unexpected error: "
+                  << ex.what() << std::endl;
+        return (1);
+    }
+
+    return (0);
+}
+
+/// @brief  This callout is called at the "subnet6_select" hook.
+///
+/// This function searches the UserRegistry for the client indicated by the
+/// inbound IPv6 DHCP packet. If the client is found in the registry generate
+/// the outcome record and return.
+///
+/// If the client is not found in the registry replace the selected subnet
+/// with the restricted access subnet, then generate the outcome record and
+/// return.  By convention, it is assumed that last subnet in the list of
+/// available subnets is the restricted access subnet.
+///
+/// @param handle CalloutHandle which provides access to context.
+///
+/// @return 0 upon success, non-zero otherwise.
+int subnet6_select(CalloutHandle& handle) {
+    if (!user_registry) {
+        std::cout << "DHCP UserCheckHook : subnet6_select UserRegistry is null"
+                  << std::endl;
+        return (1);
+    }
+
+    try {
+        // Get subnet collection. If it's empty just bail nothing to do.
+        const isc::dhcp::Subnet6Collection *subnets = NULL;
+        handle.getArgument("subnet6collection", subnets);
+        if (subnets->empty()) {
+            return 0;
+        }
+
+        // Refresh the registry.
+        user_registry->refresh();
+
+        // Get the HWAddress as the user identifier.
+        Pkt6Ptr query;
+        handle.getArgument("query6", query);
+
+        DuidPtr duid;
+        OptionPtr opt_duid = query->getOption(D6O_CLIENTID);
+        if (!opt_duid) {
+            std::cout << "DHCP6 query is missing DUID" << std::endl;
+            return (1);
+        }
+
+        duid = DuidPtr(new DUID(opt_duid->getData()));
+
+        // Look for the user.
+        UserPtr registered_user = user_registry->findUser(*duid);
+        if (registered_user) {
+            // User is in the registry, so leave the pre-selected subnet alone.
+            Subnet6Ptr subnet;
+            handle.getArgument("subnet6", subnet);
+            // Add the outcome entry to the output file.
+            generate_output_record(UserId::DUID_STR, duid->toText(),
+                                   subnet->toText(), true);
+        } else {
+            // User is not in the registry, so assign them to the last subnet
+            // in the collection.  By convention we are assuming this is the
+            // restricted subnet.
+            Subnet6Ptr subnet = subnets->back();
+            handle.setArgument("subnet6", subnet);
+            // Add the outcome entry to the output file.
+            generate_output_record(UserId::DUID_STR, duid->toText(),
+                                   subnet->toText(), false);
+        }
+    } catch (const std::exception& ex) {
+        std::cout << "DHCP UserCheckHook : subnet6_select unexpected error: "
+                  << ex.what() << std::endl;
+        return (1);
+    }
+
+    return (0);
+}
+
+}
diff --git a/src/hooks/dhcp/user_chk/tests/Makefile.am b/src/hooks/dhcp/user_chk/tests/Makefile.am
new file mode 100644
index 0000000..d76f215
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/tests/Makefile.am
@@ -0,0 +1,73 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -I$(top_builddir)/src/hooks/dhcp/user_chk -I$(top_srcdir)/src/hooks/dhcp/user_chk
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(BOTAN_INCLUDES)
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_srcdir)/src/hooks/dhcp/user_chk/tests\"
+AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+USER_CHK_LIB = $(top_builddir)/src/hooks/dhcp/user_chk/libdhcp_user_chk.la
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+# Unit test data files need to get installed.
+EXTRA_DIST = test_users_1.txt test_users_err.txt
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS_ENVIRONMENT = \
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+TESTS += libdhcp_user_chk_unittests
+
+libdhcp_user_chk_unittests_SOURCES  = 
+libdhcp_user_chk_unittests_SOURCES += ../load_unload.cc
+libdhcp_user_chk_unittests_SOURCES += ../subnet_select_co.cc
+libdhcp_user_chk_unittests_SOURCES += ../version.cc
+libdhcp_user_chk_unittests_SOURCES += ../user.cc ../user.h
+# Until logging in dynamically loaded libraries is fixed, exclude these.
+#libdhcp_user_chk_unittests_SOURCES += ../user_chk_log.cc ../user_chk_log.h
+#libdhcp_user_chk_unittests_SOURCES += ../user_chk_messages.cc ../user_chk_messages.h
+libdhcp_user_chk_unittests_SOURCES += ../user_data_source.h
+libdhcp_user_chk_unittests_SOURCES += ../user_file.cc ../user_file.h
+libdhcp_user_chk_unittests_SOURCES += ../user_registry.cc ../user_registry.h
+libdhcp_user_chk_unittests_SOURCES += run_unittests.cc
+libdhcp_user_chk_unittests_SOURCES += userid_unittests.cc
+libdhcp_user_chk_unittests_SOURCES += user_unittests.cc
+libdhcp_user_chk_unittests_SOURCES += user_registry_unittests.cc
+libdhcp_user_chk_unittests_SOURCES += user_file_unittests.cc
+
+libdhcp_user_chk_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
+
+libdhcp_user_chk_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
+
+libdhcp_user_chk_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_CLANGPP
+# This is to workaround unused variables tcout and tcerr in
+# log4cplus's streams.h and unused parameters from some of the
+# Boost headers.
+libdhcp_user_chk_unittests_CXXFLAGS += -Wno-unused-parameter
+endif
+
+libdhcp_user_chk_unittests_LDADD = $(top_builddir)/src/lib/log/libb10-log.la
+libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
+libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+libdhcp_user_chk_unittests_LDADD += ${BOTAN_LIBS} ${BOTAN_RPATH}
+libdhcp_user_chk_unittests_LDADD += $(GTEST_LDADD)
+endif
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/hooks/dhcp/user_chk/tests/run_unittests.cc b/src/hooks/dhcp/user_chk/tests/run_unittests.cc
new file mode 100644
index 0000000..185f888
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/tests/run_unittests.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <log/logger_support.h>
+
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+    isc::log::initLogger();
+
+    int result = RUN_ALL_TESTS();
+
+    return (result);
+}
diff --git a/src/hooks/dhcp/user_chk/tests/test_data_files_config.h.in b/src/hooks/dhcp/user_chk/tests/test_data_files_config.h.in
new file mode 100644
index 0000000..9abdbc6
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/tests/test_data_files_config.h.in
@@ -0,0 +1,16 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+//
+/// @brief Path to local dir so tests can locate test data files 
+#define USER_CHK_TEST_DIR "@abs_top_srcdir@/src/hooks/dhcp/user_chk/tests"
diff --git a/src/hooks/dhcp/user_chk/tests/test_users_1.txt b/src/hooks/dhcp/user_chk/tests/test_users_1.txt
new file mode 100644
index 0000000..5fa718f
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/tests/test_users_1.txt
@@ -0,0 +1,2 @@
+{ "type" : "HW_ADDR", "id" : "01AC00F03344", "opt1" : "true" }
+{ "type" : "DUID", "id" : "225060de0a0b", "opt1" : "true" }
diff --git a/src/hooks/dhcp/user_chk/tests/test_users_err.txt b/src/hooks/dhcp/user_chk/tests/test_users_err.txt
new file mode 100644
index 0000000..3204006
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/tests/test_users_err.txt
@@ -0,0 +1,2 @@
+{ "type" : "DUID", "id" : "777777777777", "opt1" : "true" }
+{ "type" : "BOGUS", "id" : "01AC00F03344", "opt1" : "true" }
diff --git a/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc b/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc
new file mode 100644
index 0000000..9507fab
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc
@@ -0,0 +1,158 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+#include <test_data_files_config.h>
+#include <user_file.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+
+namespace {
+
+/// @brief Convenience method for reliably building test file path names.
+///
+/// Function prefixes the given file name with a path to unit tests directory
+/// so we can reliably find test data files.
+///
+/// @param name base file name of the test file
+std::string testFilePath(const std::string& name) {
+    return (std::string(USER_CHK_TEST_DIR) + "/" + name);
+}
+
+/// @brief Tests the UserFile constructor.
+TEST(UserFile, construction) {
+    // Verify that a UserFile with no file name is rejected.
+    ASSERT_THROW(UserFile(""), UserFileError);
+
+    // Verify that a UserFile with a non-blank file name is accepted.
+    ASSERT_NO_THROW(UserFile("someName"));
+}
+
+/// @brief Tests opening and closing UserFile
+TEST(UserFile, openFile) {
+    UserFilePtr user_file;
+
+    // Construct a user file that refers to a non existant file.
+    ASSERT_NO_THROW(user_file.reset(new UserFile("NoSuchFile")));
+    EXPECT_FALSE(user_file->isOpen());
+
+    // Verify a non-existant file fails to open
+    ASSERT_THROW(user_file->open(), UserFileError);
+    EXPECT_FALSE(user_file->isOpen());
+
+    // Construct a user file that should exist.
+    ASSERT_NO_THROW(user_file.reset(new UserFile
+                                   (testFilePath("test_users_1.txt"))));
+    // File should not be open.
+    EXPECT_FALSE(user_file->isOpen());
+
+    // Verify that we can open it.
+    ASSERT_NO_THROW(user_file->open());
+    EXPECT_TRUE(user_file->isOpen());
+
+    // Verify that we cannot open an already open file.
+    ASSERT_THROW(user_file->open(), UserFileError);
+
+    // Verifyt we can close it.
+    ASSERT_NO_THROW(user_file->close());
+    EXPECT_FALSE(user_file->isOpen());
+
+    // Verify that we can reopen it.
+    ASSERT_NO_THROW(user_file->open());
+    EXPECT_TRUE(user_file->isOpen());
+}
+
+
+/// @brief Tests makeUser with invalid user strings
+TEST(UserFile, makeUser) {
+    const char* invalid_strs[]= {
+        // Missinge type element.
+        "{ \"id\" : \"01AC00F03344\" }",
+        // Invalid id type string value.
+        "{ \"type\" : \"BOGUS\", \"id\" : \"01AC00F03344\"}",
+        // Non-string id type
+        "{ \"type\" : 1, \"id\" : \"01AC00F03344\"}",
+        // Missing id element.
+        "{ \"type\" : \"HW_ADDR\" }",
+        // Odd number of digits in id value.
+        "{ \"type\" : \"HW_ADDR\", \"id\" : \"1AC00F03344\"}",
+        // Invalid characters in id value.
+        "{ \"type\" : \"HW_ADDR\", \"id\" : \"THIS IS BAD!\"}",
+        // Empty id value.
+        "{ \"type\" : \"HW_ADDR\", \"id\" : \"\"}",
+        // Non-string id.
+        "{ \"type\" : \"HW_ADDR\", \"id\" : 01AC00F03344 }",
+        // Option with non-string value
+        "{ \"type\" : \"HW_ADDR\", \"id\" : \"01AC00F03344\", \"opt\" : 4 }",
+        NULL
+        };
+
+    // Create a UseFile to work with.
+    UserFilePtr user_file;
+    ASSERT_NO_THROW(user_file.reset(new UserFile("noFile")));
+
+    // Iterate over the list of invalid user strings and verify
+    // each one fails.
+    const char** tmp = invalid_strs;;
+    while (*tmp) {
+        EXPECT_THROW(user_file->makeUser(*tmp), UserFileError)
+                     << "Invalid str not caught: ["
+                     <<  *tmp << "]" << std::endl;
+        ++tmp;
+    }
+}
+
+/// @brief Test reading from UserFile
+TEST(UserFile, readFile) {
+    UserFilePtr user_file;
+
+    // Construct and then open a known file.
+    ASSERT_NO_THROW(user_file.reset(new UserFile
+                                    (testFilePath("test_users_1.txt"))));
+    ASSERT_NO_THROW(user_file->open());
+    EXPECT_TRUE(user_file->isOpen());
+
+    // File should contain two valid users. Read and verify each.
+    UserPtr user;
+    int i = 0;
+    do {
+        ASSERT_NO_THROW(user = user_file->readNextUser());
+        switch (i++) {
+            case 0:
+                EXPECT_EQ(UserId::HW_ADDRESS, user->getUserId().getType());
+                EXPECT_EQ("01ac00f03344", user->getUserId().toText());
+                EXPECT_EQ("true", user->getProperty("opt1"));
+                break;
+            case 1:
+                EXPECT_EQ(UserId::DUID, user->getUserId().getType());
+                EXPECT_EQ("225060de0a0b", user->getUserId().toText());
+                EXPECT_EQ("true", user->getProperty("opt1"));
+                break;
+            default:
+                // Third time around, we are at EOF User should be null.
+                ASSERT_FALSE(user);
+                break;
+        }
+    } while (user);
+
+
+    ASSERT_NO_THROW(user_file->close());
+}
+
+} // end of anonymous namespace
diff --git a/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc b/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc
new file mode 100644
index 0000000..1f250c8
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc
@@ -0,0 +1,216 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcp/hwaddr.h>
+#include <exceptions/exceptions.h>
+#include <user_registry.h>
+#include <user_file.h>
+#include <test_data_files_config.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+
+namespace {
+
+/// @brief Convenience method for reliably building test file path names.
+///
+/// Function prefixes the given file name with a path to unit tests directory
+/// so we can reliably find test data files.
+///
+/// @param name base file name of the test file
+std::string testFilePath(const std::string& name) {
+    return (std::string(USER_CHK_TEST_DIR) + "/" + name);
+}
+
+/// @brief Tests UserRegistry construction.
+TEST(UserRegistry, constructor) {
+    // Currently there is only the default constructor which does not throw.
+    UserRegistryPtr reg;
+    ASSERT_NO_THROW(reg.reset(new UserRegistry()));
+}
+
+/// @brief Tests mechanics of adding, finding, removing Users.
+TEST(UserRegistry, userBasics) {
+    // Create an empty registry.
+    UserRegistryPtr reg;
+    ASSERT_NO_THROW(reg.reset(new UserRegistry()));
+
+    // Verify that a blank user cannot be added.
+    UserPtr user;
+    ASSERT_THROW(reg->addUser(user), UserRegistryError);
+
+    // Make a new id and user.
+    UserIdPtr id;
+    ASSERT_NO_THROW(id.reset(new UserId(UserId::HW_ADDRESS, "01010101")));
+    ASSERT_NO_THROW(user.reset(new User(*id)));
+
+    // Verify that we can add a user.
+    ASSERT_NO_THROW(reg->addUser(user));
+
+    // Verify that the user can be found.
+    UserPtr found_user;
+    ASSERT_NO_THROW(found_user = reg->findUser(*id));
+    EXPECT_TRUE(found_user);
+    EXPECT_EQ(found_user->getUserId(), *id);
+
+    // Verify that searching for a non-existant user returns empty user pointer.
+    UserIdPtr id2;
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS, "02020202")));
+    ASSERT_NO_THROW(found_user = reg->findUser(*id2));
+    EXPECT_FALSE(found_user);
+
+    // Verify that the user can be deleted.
+    ASSERT_NO_THROW(reg->removeUser(*id));
+    ASSERT_NO_THROW(found_user = reg->findUser(*id));
+    EXPECT_FALSE(found_user);
+}
+
+/// @brief Tests finding users by isc::dhcp::HWaddr instance.
+TEST(UserRegistry, findByHWAddr) {
+    // Create the registry.
+    UserRegistryPtr reg;
+    ASSERT_NO_THROW(reg.reset(new UserRegistry()));
+
+    // Make a new user and add it.
+    UserPtr user;
+    ASSERT_NO_THROW(user.reset(new User(UserId::HW_ADDRESS, "01010101")));
+    ASSERT_NO_THROW(reg->addUser(user));
+
+    // Make a HWAddr instance using the same id value.
+    isc::dhcp::HWAddr hwaddr(user->getUserId().getId(), isc::dhcp::HTYPE_ETHER);
+
+    // Verify user can be found by HWAddr.
+    UserPtr found_user;
+    ASSERT_NO_THROW(found_user = reg->findUser(hwaddr));
+    EXPECT_TRUE(found_user);
+    EXPECT_EQ(found_user->getUserId(), user->getUserId());
+}
+
+/// @brief Tests finding users by isc::dhcp::DUID instance.
+TEST(UserRegistry, findByDUID) {
+    // Create the registry.
+    UserRegistryPtr reg;
+    ASSERT_NO_THROW(reg.reset(new UserRegistry()));
+
+    // Make a new user and add it.
+    UserPtr user;
+    ASSERT_NO_THROW(user.reset(new User(UserId::DUID, "01010101")));
+    ASSERT_NO_THROW(reg->addUser(user));
+
+    // Make a DUID instance using the same id value.
+    isc::dhcp::DUID duid(user->getUserId().getId());
+
+    // Verify user can be found by DUID.
+    UserPtr found_user;
+    ASSERT_NO_THROW(found_user = reg->findUser(duid));
+    EXPECT_TRUE(found_user);
+    EXPECT_EQ(found_user->getUserId(), user->getUserId());
+}
+
+/// @brief Tests mixing users of different types.
+TEST(UserRegistry, oneOfEach) {
+    // Create the registry.
+    UserRegistryPtr reg;
+    ASSERT_NO_THROW(reg.reset(new UserRegistry()));
+
+    // Make user ids.
+    UserIdPtr idh, idd;
+    ASSERT_NO_THROW(idh.reset(new UserId(UserId::HW_ADDRESS, "01010101")));
+    ASSERT_NO_THROW(idd.reset(new UserId(UserId::DUID, "03030303")));
+
+    // Make and add HW_ADDRESS user.
+    UserPtr user;
+    user.reset(new User(*idh));
+    ASSERT_NO_THROW(reg->addUser(user));
+
+    // Make and add DUID user.
+    user.reset(new User(*idd));
+    ASSERT_NO_THROW(reg->addUser(user));
+
+    // Verify we can find both.
+    UserPtr found_user;
+    ASSERT_NO_THROW(found_user = reg->findUser(*idh));
+    ASSERT_NO_THROW(found_user = reg->findUser(*idd));
+}
+
+/// @brief Tests loading the registry from a file.
+TEST(UserRegistry, refreshFromFile) {
+    // Create the registry.
+    UserRegistryPtr reg;
+    ASSERT_NO_THROW(reg.reset(new UserRegistry()));
+
+    UserDataSourcePtr user_file;
+
+    // Verify that data source cannot be set to null source.
+    ASSERT_THROW(reg->setSource(user_file), UserRegistryError);
+
+    // Create the data source.
+    ASSERT_NO_THROW(user_file.reset(new UserFile
+                                    (testFilePath("test_users_1.txt"))));
+
+    // Set the registry's data source and refresh the registry.
+    ASSERT_NO_THROW(reg->setSource(user_file));
+    ASSERT_NO_THROW(reg->refresh());
+
+    // Verify we can find all the expected users.
+    UserIdPtr id;
+    ASSERT_NO_THROW(id.reset(new UserId(UserId::HW_ADDRESS, "01ac00f03344")));
+    EXPECT_TRUE(reg->findUser(*id));
+
+    ASSERT_NO_THROW(id.reset(new UserId(UserId::DUID, "225060de0a0b")));
+    EXPECT_TRUE(reg->findUser(*id));
+}
+
+/// @brief Tests preservation of registry upon refresh failure.
+TEST(UserRegistry, refreshFail) {
+    // Create the registry.
+    UserRegistryPtr reg;
+    ASSERT_NO_THROW(reg.reset(new UserRegistry()));
+
+    // Create the data source.
+    UserDataSourcePtr user_file;
+    ASSERT_NO_THROW(user_file.reset(new UserFile
+                                    (testFilePath("test_users_1.txt"))));
+
+    // Set the registry's data source and refresh the registry.
+    ASSERT_NO_THROW(reg->setSource(user_file));
+    ASSERT_NO_THROW(reg->refresh());
+
+    // Make user ids of expected users.
+    UserIdPtr id1, id2;
+    ASSERT_NO_THROW(id1.reset(new UserId(UserId::HW_ADDRESS, "01ac00f03344")));
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::DUID, "225060de0a0b")));
+
+    // Verify we can find all the expected users.
+    EXPECT_TRUE(reg->findUser(*id1));
+    EXPECT_TRUE(reg->findUser(*id2));
+
+    // Replace original data source with a new one containing an invalid entry.
+    ASSERT_NO_THROW(user_file.reset(new UserFile
+                                    (testFilePath("test_users_err.txt"))));
+    ASSERT_NO_THROW(reg->setSource(user_file));
+
+    // Refresh should throw due to invalid data.
+    EXPECT_THROW(reg->refresh(), UserRegistryError);
+
+    // Verify we can still find all the original users.
+    EXPECT_TRUE(reg->findUser(*id1));
+    EXPECT_TRUE(reg->findUser(*id2));
+}
+
+} // end of anonymous namespace
diff --git a/src/hooks/dhcp/user_chk/tests/user_unittests.cc b/src/hooks/dhcp/user_chk/tests/user_unittests.cc
new file mode 100644
index 0000000..2ca156c
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/tests/user_unittests.cc
@@ -0,0 +1,96 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+#include <user.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+
+namespace {
+
+/// @brief Tests User construction variants.
+/// Note, since all constructors accept or rely on UserId, invalid id
+/// variants are tested under UserId not here.
+TEST(UserTest, construction) {
+    std::string test_address("01FF02AC030B0709");
+
+    // Create a user id.
+    UserIdPtr id;
+    ASSERT_NO_THROW(id.reset(new UserId(UserId::HW_ADDRESS, test_address)));
+
+    // Verify construction from a UserId.
+    UserPtr user;
+    ASSERT_NO_THROW(user.reset(new User(*id)));
+
+    // Verify construction from an id type and an address vector.
+    ASSERT_NO_THROW(user.reset(new User(UserId::HW_ADDRESS, id->getId())));
+    ASSERT_NO_THROW(user.reset(new User(UserId::DUID, id->getId())));
+
+    // Verify construction from an id type and an address string.
+    ASSERT_NO_THROW(user.reset(new User(UserId::HW_ADDRESS, test_address)));
+    ASSERT_NO_THROW(user.reset(new User(UserId::DUID, test_address)));
+}
+
+/// @brief Tests property map fundamentals.
+TEST(UserTest, properties) {
+    // Create a user.
+    UserPtr user;
+    ASSERT_NO_THROW(user.reset(new User(UserId::DUID, "01020304050607")));
+
+    // Verify that we can add and retrieve a property.
+    std::string value = "";
+    EXPECT_NO_THROW(user->setProperty("one","1"));
+    EXPECT_NO_THROW(value = user->getProperty("one"));
+    EXPECT_EQ("1", value);
+
+    // Verify that we can update and then retrieve the property.
+    EXPECT_NO_THROW(user->setProperty("one","1.0"));
+    EXPECT_NO_THROW(value = user->getProperty("one"));
+    EXPECT_EQ("1.0", value);
+
+    // Verify that we can remove and then NOT find the property.
+    EXPECT_NO_THROW(user->delProperty("one"));
+    EXPECT_NO_THROW(value = user->getProperty("one"));
+    EXPECT_TRUE(value.empty());
+
+    // Verify that a blank property name is NOT permitted.
+    EXPECT_THROW(user->setProperty("", "blah"), isc::BadValue);
+
+    // Verify that a blank property value IS permitted.
+    EXPECT_NO_THROW(user->setProperty("one", ""));
+    EXPECT_NO_THROW(value = user->getProperty("one"));
+    EXPECT_TRUE(value.empty());
+
+    PropertyMap map;
+    map["one"]="1.0";
+    map["two"]="2.0";
+
+    ASSERT_NO_THROW(user->setProperties(map));
+
+    EXPECT_NO_THROW(value = user->getProperty("one"));
+    EXPECT_EQ("1.0", value);
+
+    EXPECT_NO_THROW(value = user->getProperty("two"));
+    EXPECT_EQ("2.0", value);
+
+    const PropertyMap& map2 = user->getProperties();
+    EXPECT_TRUE(map2 == map);
+}
+
+} // end of anonymous namespace
diff --git a/src/hooks/dhcp/user_chk/tests/userid_unittests.cc b/src/hooks/dhcp/user_chk/tests/userid_unittests.cc
new file mode 100644
index 0000000..c31c4ae
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/tests/userid_unittests.cc
@@ -0,0 +1,139 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+#include <user.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+
+namespace {
+
+/// @brief Test invalid constructors.
+TEST(UserIdTest, invalidConstructors) {
+    // Verify that constructor does not allow empty id vector.
+    std::vector<uint8_t> empty_bytes;
+    ASSERT_THROW(UserId(UserId::HW_ADDRESS, empty_bytes), isc::BadValue);
+    ASSERT_THROW(UserId(UserId::DUID, empty_bytes), isc::BadValue);
+
+    // Verify that constructor does not allow empty id string.
+    ASSERT_THROW(UserId(UserId::HW_ADDRESS, ""), isc::BadValue);
+    ASSERT_THROW(UserId(UserId::DUID, ""), isc::BadValue);
+}
+
+/// @brief Test making and using HW_ADDRESS type UserIds
+TEST(UserIdTest, hwAddress_type) {
+    // Verify text label look up for HW_ADDRESS enum.
+    EXPECT_EQ(std::string(UserId::HW_ADDRESS_STR),
+              UserId::lookupTypeStr(UserId::HW_ADDRESS));
+
+    // Verify enum look up for HW_ADDRESS text label.
+    EXPECT_EQ(UserId::HW_ADDRESS,
+              UserId::lookupType(UserId::HW_ADDRESS_STR));
+
+    // Build a test address vector.
+    uint8_t tmp[] = { 0x01, 0xFF, 0x02, 0xAC, 0x03, 0x0B, 0x07, 0x08 };
+    std::vector<uint8_t> bytes(tmp, tmp + (sizeof(tmp)/sizeof(uint8_t)));
+
+    // Verify construction from an HW_ADDRESS id type and address vector.
+    UserIdPtr id;
+    ASSERT_NO_THROW(id.reset(new UserId(UserId::HW_ADDRESS, bytes)));
+    // Verify that the id can be fetched.
+    EXPECT_EQ(id->getType(), UserId::HW_ADDRESS);
+    EXPECT_TRUE(bytes == id->getId());
+
+    // Check relational oeprators when a == b.
+    UserIdPtr id2;
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS, id->toText())));
+    EXPECT_TRUE(*id == *id2);
+    EXPECT_FALSE(*id != *id2);
+    EXPECT_FALSE(*id < *id2);
+
+    // Check relational oeprators when a < b.
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS,
+                                         "01FF02AC030B0709")));
+    EXPECT_FALSE(*id == *id2);
+    EXPECT_TRUE(*id != *id2);
+    EXPECT_TRUE(*id < *id2);
+
+    // Check relational oeprators when a > b.
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS,
+                                         "01FF02AC030B0707")));
+    EXPECT_FALSE(*id == *id2);
+    EXPECT_TRUE(*id != *id2);
+    EXPECT_FALSE(*id < *id2);
+}
+
+/// @brief Test making and using DUID type UserIds
+TEST(UserIdTest, duid_type) {
+    // Verify text label look up for DUID enum.
+    EXPECT_EQ(std::string(UserId::DUID_STR),
+              UserId::lookupTypeStr(UserId::DUID));
+
+    // Verify enum look up for DUID text label.
+    EXPECT_EQ(UserId::DUID,
+              UserId::lookupType(UserId::DUID_STR));
+
+    // Build a test DUID vector.
+    uint8_t tmp[] = { 0x01, 0xFF, 0x02, 0xAC, 0x03, 0x0B, 0x07, 0x08 };
+    std::vector<uint8_t> bytes(tmp, tmp + (sizeof(tmp)/sizeof(uint8_t)));
+
+    // Verify construction from an DUID id type and address vector.
+    UserIdPtr id;
+    ASSERT_NO_THROW(id.reset(new UserId(UserId::DUID, bytes)));
+    // Verify that the id can be fetched.
+    EXPECT_EQ(id->getType(), UserId::DUID);
+    EXPECT_TRUE(bytes == id->getId());
+
+    // Check relational oeprators when a == b.
+    UserIdPtr id2;
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::DUID, id->toText())));
+    EXPECT_TRUE(*id == *id2);
+    EXPECT_FALSE(*id != *id2);
+    EXPECT_FALSE(*id < *id2);
+
+    // Check relational oeprators when a < b.
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::DUID, "01FF02AC030B0709")));
+    EXPECT_FALSE(*id == *id2);
+    EXPECT_TRUE(*id != *id2);
+    EXPECT_TRUE(*id < *id2);
+
+    // Check relational oeprators when a > b.
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::DUID, "01FF02AC030B0707")));
+    EXPECT_FALSE(*id == *id2);
+    EXPECT_TRUE(*id != *id2);
+    EXPECT_FALSE(*id < *id2);
+}
+
+/// @brief Tests that UserIds of different types compare correctly.
+TEST(UserIdTest, mixed_type_compare) {
+    UserIdPtr hw, duid;
+    // Create UserIds with different types, but same id data.
+    ASSERT_NO_THROW(hw.reset(new UserId(UserId::HW_ADDRESS,
+                                        "01FF02AC030B0709")));
+    ASSERT_NO_THROW(duid.reset(new UserId(UserId::DUID,
+                                          "01FF02AC030B0709")));
+
+    // Verify that UserIdType influences logical comparators.
+    EXPECT_FALSE(*hw == *duid);
+    EXPECT_TRUE(*hw != *duid);
+    EXPECT_TRUE(*hw < *duid);
+}
+
+
+} // end of anonymous namespace
diff --git a/src/hooks/dhcp/user_chk/user.cc b/src/hooks/dhcp/user_chk/user.cc
new file mode 100644
index 0000000..c7c18fa
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/user.cc
@@ -0,0 +1,210 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcp/hwaddr.h>
+#include <dhcp/duid.h>
+#include <exceptions/exceptions.h>
+#include <util/encode/hex.h>
+
+#include <user.h>
+
+#include <iomanip>
+#include <sstream>
+
+//********************************* UserId ******************************
+
+const char* UserId::HW_ADDRESS_STR = "HW_ADDR";
+const char* UserId::DUID_STR = "DUID";
+
+UserId::UserId(UserIdType id_type, const std::vector<uint8_t>& id)
+    : id_type_(id_type), id_(id) {
+    if (id.size() == 0) {
+        isc_throw(isc::BadValue, "UserId id may not be blank");
+    }
+}
+
+UserId::UserId(UserIdType id_type, const std::string & id_str) :
+    id_type_(id_type) {
+    if (id_str.empty()) {
+        isc_throw(isc::BadValue, "UserId id string may not be blank");
+    }
+
+    // Convert the id string to vector.
+    // Input is expected to be 2-digits per bytes, no delimiters.
+    std::vector<uint8_t> addr_bytes;
+    isc::util::encode::decodeHex(id_str, addr_bytes);
+
+    // Attempt to instantiate the appropriate id class to leverage validation.
+    switch (id_type) {
+        case HW_ADDRESS: {
+            isc::dhcp::HWAddr hwaddr(addr_bytes, isc::dhcp::HTYPE_ETHER);
+            break;
+            }
+        case DUID: {
+            isc::dhcp::DUID duid(addr_bytes);
+            break;
+            }
+        default:
+            isc_throw (isc::BadValue, "Invalid id_type: " << id_type);
+            break;
+    }
+
+    // It's a valid id.
+    id_ = addr_bytes;
+}
+
+UserId::~UserId() {
+}
+
+const std::vector<uint8_t>&
+UserId::getId() const {
+    return (id_);
+}
+
+UserId::UserIdType
+UserId::getType() const {
+    return (id_type_);
+}
+
+std::string
+UserId::toText(char delim_char) const {
+    std::stringstream tmp;
+    tmp << std::hex;
+    bool delim = false;
+    for (std::vector<uint8_t>::const_iterator it = id_.begin();
+         it != id_.end(); ++it) {
+        if (delim_char && delim) {
+            tmp << delim_char;
+        }
+
+        tmp << std::setw(2) << std::setfill('0')
+            << static_cast<unsigned int>(*it);
+        delim = true;
+    }
+
+    return (tmp.str());
+}
+
+bool
+UserId::operator ==(const UserId & other) const {
+    return ((this->id_type_ == other.id_type_) && (this->id_ == other.id_));
+}
+
+bool
+UserId::operator !=(const UserId & other) const {
+    return (!(*this == other));
+}
+
+bool
+UserId::operator <(const UserId & other) const {
+    return ((this->id_type_ < other.id_type_) ||
+            ((this->id_type_ == other.id_type_) && (this->id_ < other.id_)));
+}
+
+std::string
+UserId::lookupTypeStr(UserIdType type) {
+    const char* tmp = NULL;
+    switch (type) {
+        case HW_ADDRESS:
+            tmp = HW_ADDRESS_STR;
+            break;
+        case DUID:
+            tmp = DUID_STR;
+            break;
+        default:
+            isc_throw(isc::BadValue, "Invalid UserIdType:" << type);
+            break;
+    }
+
+    return (std::string(tmp));
+}
+
+UserId::UserIdType
+UserId::lookupType(const std::string& type_str) {
+    if (type_str.compare(HW_ADDRESS_STR) == 0) {
+        return (HW_ADDRESS);
+    } else if (type_str.compare(DUID_STR) == 0) {
+        return (DUID);
+    }
+
+    isc_throw(isc::BadValue, "Invalid UserIdType string:" << type_str);
+}
+
+std::ostream&
+operator<<(std::ostream& os, const UserId& user_id) {
+    std::string tmp = UserId::lookupTypeStr(user_id.getType());
+    os << tmp << "=" << user_id.toText();
+    return (os);
+}
+
+//********************************* User ******************************
+
+User::User(const UserId& user_id) : user_id_(user_id) {
+}
+
+User::User(UserId::UserIdType id_type, const std::vector<uint8_t>& id)
+    : user_id_(id_type, id) {
+}
+
+User::User(UserId::UserIdType id_type, const std::string& id_str)
+    : user_id_(id_type, id_str) {
+}
+
+User::~User() {
+}
+
+const PropertyMap&
+User::getProperties() const {
+    return (properties_);
+}
+
+void
+User::setProperties(const PropertyMap& properties) {
+    properties_ = properties;
+}
+
+void User::setProperty(const std::string& name, const std::string& value) {
+    if (name.empty()) {
+        isc_throw (isc::BadValue, "User property name cannot be blank");
+    }
+
+    // Note that if the property exists its value will be updated.
+    properties_[name]=value;
+}
+
+std::string
+User::getProperty(const std::string& name) const {
+    PropertyMap::const_iterator it = properties_.find(name);
+    if (it != properties_.end()) {
+        return ((*it).second);
+    }
+
+    // By returning an empty string rather than throwing, we allow the
+    // flexibility of defaulting to blank if not specified.  Let the caller
+    // decide if that is valid or not.
+    return ("");
+}
+
+void
+User::delProperty(const std::string & name) {
+    PropertyMap::iterator it = properties_.find(name);
+    if (it != properties_.end()) {
+        properties_.erase(it);
+    }
+}
+
+const UserId&
+User::getUserId() const {
+    return (user_id_);
+}
diff --git a/src/hooks/dhcp/user_chk/user.h b/src/hooks/dhcp/user_chk/user.h
new file mode 100644
index 0000000..20167b6
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/user.h
@@ -0,0 +1,249 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef USER_H
+#define USER_H
+
+#include <boost/shared_ptr.hpp>
+
+#include <map>
+#include <stdint.h>
+#include <vector>
+
+/// @file user.h This file defines classes: UserId and User.
+/// @brief These classes are used to describe and recognize DHCP lease
+/// clients.
+
+/// @brief Encapsulates a unique identifier for a DHCP client.
+/// This class provides a generic wrapper around the information used to
+/// uniquely identify the requester in a DHCP request packet.  It provides
+/// the necessary operators such that it can be used as a key within STL
+/// containers such as maps.  It supports both IPv4 and IPv6 clients.
+class UserId {
+public:
+    /// @brief Defines the supported types of user ids.
+    // Use explicit values to ensure consistent numeric ordering for key
+    // comparisons.
+    enum UserIdType {
+        /// @brief Hardware addresses (MAC) are used for IPv4 clients.
+        HW_ADDRESS = 0,
+        /// @brief DUIDs are used for IPv6 clients.
+        DUID = 1
+    };
+
+    /// @brief Defines the text label hardware address id type.
+    static const char* HW_ADDRESS_STR;
+    /// @brief Define the text label DUID id type.
+    static const char* DUID_STR;
+
+    /// @brief Constructor
+    ///
+    /// Constructs a UserId from an id type and id vector.
+    ///
+    /// @param id_type The type of user id contained in vector
+    /// @param id a vector of unsigned bytes containing the id
+    ///
+    /// @throw isc::BadValue if the vector is empty.
+    UserId(UserIdType id_type, const std::vector<uint8_t>& id);
+
+    /// @brief Constructor
+    ///
+    /// Constructs a UserId from an id type and id string.
+    ///
+    /// @param id_type The type of user id contained in string.
+    /// The string is expected to contain an even number of hex digits
+    /// without delimiters.
+    ///
+    /// @param id a vector of unsigned bytes containing the id
+    ///
+    /// @throw isc::BadValue if the string is empty, contains non
+    /// valid hex digits, or an odd number of hex digits.
+    UserId(UserIdType id_type, const std::string& id_str);
+
+    /// @brief Destructor.
+    ~UserId();
+
+    /// @brief Returns a const reference to the actual id value
+    const std::vector<uint8_t>& getId() const;
+
+    /// @brief Returns the user id type
+    UserIdType getType() const;
+
+    /// @brief Returns textual representation of the id
+    ///
+    /// Outputs a string of hex digits representing the id with an
+    /// optional delimiter between digit pairs (i.e. bytes).
+    ///
+    /// Without a delimiter:
+    ///   "0c220F"
+    ///
+    /// with colon as a delimiter:
+    ///   "0c:22:0F"
+    ///
+    /// @param delim_char The delimiter to place in between
+    /// "bytes". It defaults to none.
+    ///  (e.g. 00010203ff)
+    std::string toText(char delim_char=0x0) const;
+
+    /// @brief Compares two UserIds for equality
+    bool operator ==(const UserId & other) const;
+
+    /// @brief Compares two UserIds for inequality
+    bool operator !=(const UserId & other) const;
+
+    /// @brief Performs less than comparison of two UserIds
+    bool operator <(const UserId & other) const;
+
+    /// @brief Returns the text label for a given id type
+    ///
+    /// @param type The id type value for which the label is desired
+    ///
+    /// @throw isc::BadValue if type is not valid.
+    static std::string lookupTypeStr(UserIdType type);
+
+    /// @brief Returns the id type for a given text label
+    ///
+    /// @param type_str The text label for which the id value is desired
+    ///
+    /// @throw isc::BadValue if type_str is not a valid text label.
+    static UserIdType lookupType(const std::string& type_str);
+
+private:
+    /// @brief The type of id value
+    UserIdType id_type_;
+
+    /// @brief The id value
+    std::vector<uint8_t> id_;
+
+};
+
+/// @brief Outputs the UserId contents in a string to the given stream.
+///
+/// The output string has the form "<type>=<id>" where:
+///
+/// <type> is the text label returned by UserId::lookupTypeStr()
+/// <id> is the output of UserId::toText() without a delimiter.
+///
+/// Examples:
+///       HW_ADDR=0c0e0a01ff06
+///       DUID=0001000119efe63b000c01020306
+///
+/// @param os output stream to which to write
+/// @param user_id source object to output
+std::ostream&
+operator<<(std::ostream& os, const UserId& user_id);
+
+/// @brief Defines a smart pointer to UserId
+typedef boost::shared_ptr<UserId> UserIdPtr;
+
+/// @brief Defines a map of string values keyed by string labels.
+typedef std::map<std::string, std::string> PropertyMap;
+
+/// @brief Represents a unique DHCP user
+/// This class is used to represent a specific DHCP user who is identified by a
+/// unique id and who possesses a set of properties.
+class User {
+public:
+    /// @brief Constructor
+    ///
+    /// Constructs a new User from a given id with an empty set of properties.
+    ///
+    /// @param user_id Id to assign to the user
+    ///
+    /// @throw isc::BadValue if user id is blank.
+    User(const UserId & user_id);
+
+    /// @brief Constructor
+    ///
+    /// Constructs a new User from a given id type and vector containing the
+    /// id data with an empty set of properties.
+    ///
+    /// @param user_id Type of id contained in the id vector
+    /// @param id Vector of data representing the user's id
+    ///
+    /// @throw isc::BadValue if user id vector is empty.
+    User(UserId::UserIdType id_type, const std::vector<uint8_t>& id);
+
+    /// @brief Constructor
+    ///
+    /// Constructs a new User from a given id type and string containing the
+    /// id data with an empty set of properties.
+    ///
+    /// @param user_id Type of id contained in the id vector
+    /// @param id string of hex digits representing the user's id
+    ///
+    /// @throw isc::BadValue if user id string is empty or invalid
+    User(UserId::UserIdType id_type, const std::string& id_str);
+
+    /// @brief Destructor
+    ~User();
+
+    /// @brief Returns a reference to the map of properties.
+    ///
+    /// Note that this reference can go out of scope and should not be
+    /// relied upon other than for momentary use.
+    const PropertyMap& getProperties() const;
+
+    /// @brief Sets the user's properties from a given property map
+    ///
+    /// Replaces the contents of the user's property map with the given
+    /// property map.
+    ///
+    /// @param properties property map to assign to the user
+    void setProperties(const PropertyMap& properties);
+
+    /// @brief Sets a given property to the given value
+    ///
+    /// Adds or updates the given property to the given value.
+    ///
+    /// @param name string by which the property is identified (keyed)
+    /// @param value string data to associate with the property
+    ///
+    /// @throw isc::BadValue if name is blank.
+    void setProperty(const std::string& name, const std::string& value);
+
+    /// @brief Fetches the string value for a given property name.
+    ///
+    /// @param name property name to fetch
+    ///
+    /// @return Returns the string value for the given name or an empty string
+    /// if the property is not found in the property map.
+    std::string getProperty(const std::string& name) const;
+
+    /// @brief Removes the given property from the property map.
+    ///
+    /// Removes the given property from the map if found. If not, no harm no
+    /// foul.
+    ///
+    /// @param name property name to remove
+    void delProperty(const std::string& name);
+
+    /// @brief Returns the user's id.
+    ///
+    /// Note that this reference can go out of scope and should not be
+    /// relied upon other than for momentary use.
+    const UserId& getUserId() const;
+
+private:
+    /// @brief The user's id.
+    UserId user_id_;
+
+    /// @brief The user's property map.
+    PropertyMap properties_;
+};
+
+/// @brief Defines a smart pointer to a User.
+typedef boost::shared_ptr<User> UserPtr;
+
+#endif
diff --git a/src/hooks/dhcp/user_chk/user_chk_log.cc b/src/hooks/dhcp/user_chk/user_chk_log.cc
new file mode 100644
index 0000000..f104efb
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/user_chk_log.cc
@@ -0,0 +1,18 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// Defines the logger used by the user check hooks library.
+#include <user_chk_log.h>
+
+isc::log::Logger user_chk_logger("user_chk");
diff --git a/src/hooks/dhcp/user_chk/user_chk_log.h b/src/hooks/dhcp/user_chk/user_chk_log.h
new file mode 100644
index 0000000..43fb364
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/user_chk_log.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef USER_CHK_LOG_H
+#define USER_CHK_LOG_H
+
+#include <log/message_initializer.h>
+#include <log/macros.h>
+#include <user_chk_messages.h>
+
+/// @brief User Check Logger
+///
+/// Define the logger used to log messages.  We could define it in multiple
+/// modules, but defining in a single module and linking to it saves time and
+/// space.
+extern isc::log::Logger user_chk_logger;
+
+#endif // USER_CHK_LOG_H
diff --git a/src/hooks/dhcp/user_chk/user_chk_messages.mes b/src/hooks/dhcp/user_chk/user_chk_messages.mes
new file mode 100644
index 0000000..23e3023
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/user_chk_messages.mes
@@ -0,0 +1,43 @@
+# Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+% USER_CHK_HOOK_LOAD_ERROR DHCP UserCheckHook could not be loaded: %1
+This is an error message issued when the DHCP UserCheckHook could not be loaded.
+The exact cause should be explained in the log message.  User subnet selection
+will revert to default processing.
+
+% USER_CHK_HOOK_UNLOAD_ERROR DHCP UserCheckHook an error occurred unloading the library: %1
+This is an error message issued when an error occurs while unloading the
+UserCheckHook library.  This is unlikely to occur and normal operations of the
+library will likely resume when it is next loaded.
+
+% USER_CHK_SUBNET4_SELECT_ERROR DHCP UserCheckHook an unexpected error occured in subnet4_select callout: %1
+This is an error message issued when the DHCP UserCheckHook subnet4_select hook
+encounters an unexpected error.  The message should contain a more detailed
+explanation.
+
+% USER_CHK_SUBNET4_SELECT_REGISTRY_NULL DHCP UserCheckHook UserRegistry has not been created.
+This is an error message issued when the DHCP UserCheckHook subnet4_select hook
+has been invoked but the UserRegistry has not been created.  This is a
+programmatic error and should not occur.
+
+% USER_CHK_SUBNET6_SELECT_ERROR DHCP UserCheckHook an unexpected error occured in subnet6_select callout: %1
+This is an error message issued when the DHCP UserCheckHook subnet6_select hook
+encounters an unexpected error.  The message should contain a more detailed
+explanation.
+
+% USER_CHK_SUBNET6_SELECT_REGISTRY_NULL DHCP UserCheckHook UserRegistry has not been created.
+This is an error message issued when the DHCP UserCheckHook subnet6_select hook
+has been invoked but the UserRegistry has not been created.  This is a
+programmatic error and should not occur.
diff --git a/src/hooks/dhcp/user_chk/user_data_source.h b/src/hooks/dhcp/user_chk/user_data_source.h
new file mode 100644
index 0000000..a1842cf
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/user_data_source.h
@@ -0,0 +1,75 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+#ifndef _USER_DATA_SOURCE_H
+#define _USER_DATA_SOURCE_H
+
+/// @file user_data_source.h Defines the base class, UserDataSource.
+#include <exceptions/exceptions.h>
+#include <user.h>
+
+/// @brief Thrown if UserDataSource encounters an error
+class UserDataSourceError : public isc::Exception {
+public:
+    UserDataSourceError(const char* file, size_t line,
+                               const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Defines an interface for reading user data into a registry.
+/// This is an abstract class which defines the interface for reading Users
+/// from an IO source such as a file.
+class UserDataSource {
+public:
+    /// @brief Constructor.
+    UserDataSource() {};
+
+    /// @brief Virtual Destructor.
+    virtual ~UserDataSource() {};
+
+    /// @brief Opens the data source.
+    ///
+    /// Prepares the data source for reading.  Upon successful completion the
+    /// data source is ready to read from the beginning of its content.
+    ///
+    /// @throw UserDataSourceError if the source fails to open.
+    virtual void open() = 0;
+
+    /// @brief Fetches the next user from the data source.
+    ///
+    /// Reads the next User from the data source and returns it.  If no more
+    /// data is available it should return an empty (null) user.
+    ///
+    /// @throw UserDataSourceError if an error occurs.
+    virtual UserPtr readNextUser() = 0;
+
+    /// @brief Closes that data source.
+    ///
+    /// Closes the data source.
+    ///
+    /// This method must not throw exceptions.
+    virtual void close() = 0;
+
+    /// @brief Returns true if the data source is open.
+    ///
+    /// This method should return true once the data source has been
+    /// successfully opened and until it has been closed.
+    ///
+    /// It is assumed to be exception safe.
+    virtual bool isOpen() const = 0;
+};
+
+/// @brief Defines a smart pointer to a UserDataSource.
+typedef boost::shared_ptr<UserDataSource> UserDataSourcePtr;
+
+#endif
diff --git a/src/hooks/dhcp/user_chk/user_file.cc b/src/hooks/dhcp/user_chk/user_file.cc
new file mode 100644
index 0000000..4181dc4
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/user_file.cc
@@ -0,0 +1,159 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cc/data.h>
+#include <user.h>
+#include <user_file.h>
+
+#include <boost/foreach.hpp>
+#include <errno.h>
+#include <iostream>
+
+UserFile::UserFile(const std::string& fname) : fname_(fname), file_() {
+    if (fname_.empty()) {
+        isc_throw(UserFileError, "file name cannot be blank");
+    }
+}
+
+UserFile::~UserFile(){
+    close();
+};
+
+void
+UserFile::open() {
+    if (isOpen()) {
+        isc_throw (UserFileError, "file is already open");
+    }
+
+    file_.open(fname_.c_str(), std::ifstream::in);
+    int sav_error = errno;
+    if (!file_.is_open()) {
+        isc_throw(UserFileError, "cannot open file:" << fname_
+                                 << " reason: " << strerror(sav_error));
+    }
+}
+
+UserPtr
+UserFile::readNextUser() {
+    if (!isOpen()) {
+        isc_throw (UserFileError, "cannot read, file is not open");
+    }
+
+    if (file_.good()) {
+        char buf[USER_ENTRY_MAX_LEN];
+
+        // Get the next line.
+        file_.getline(buf, sizeof(buf));
+
+        // We got something, try to make a user out of it.
+        if (file_.gcount() > 0) {
+            return(makeUser(buf));
+        }
+    }
+
+    // Returns an empty user on EOF.
+    return (UserPtr());
+}
+
+UserPtr
+UserFile::makeUser(const std::string& user_string) {
+    // This method leverages the existing JSON parsing provided by isc::data
+    // library.  Should this prove to be a performance issue, it may be that
+    // lighter weight solution would be appropriate.
+
+    // Turn the string of JSON text into an Element set.
+    isc::data::ElementPtr elements;
+    try {
+        elements = isc::data::Element::fromJSON(user_string);
+    } catch (isc::data::JSONError& ex) {
+        isc_throw(UserFileError,
+                  "UserFile entry is malformed JSON: " << ex.what());
+    }
+
+    // Get a map of the Elements, keyed by element name.
+    isc::data::ConstElementPtr element;
+    PropertyMap properties;
+    std::string id_type_str;
+    std::string id_str;
+
+    // Iterate over the elements, saving of "type" and "id" to their
+    // respective locals.  Anything else is assumed to be an option so
+    // add it to the local property map.
+    std::pair<std::string, isc::data::ConstElementPtr> element_pair;
+    BOOST_FOREACH (element_pair, elements->mapValue()) {
+        // Get the element's label.
+        std::string label = element_pair.first;
+
+        // Currently everything must be a string.
+        if (element_pair.second->getType() != isc::data::Element::string) {
+            isc_throw (UserFileError, "UserFile entry: " << user_string
+                       << "has non-string value for : " << label);
+        }
+
+        std::string value = element_pair.second->stringValue();
+
+        if (label == "type") {
+            id_type_str = value;
+        } else if (label == "id") {
+            id_str = value;
+        } else {
+            // JSON parsing reduces any duplicates to the last value parsed,
+            // so we will never see duplicates here.
+            properties[label]=value;
+        }
+    }
+
+    // First we attempt to translate the id type.
+    UserId::UserIdType id_type;
+    try {
+        id_type = UserId::lookupType(id_type_str);
+    } catch (const std::exception& ex) {
+        isc_throw (UserFileError, "UserFile entry has invalid type: "
+                                  << user_string << " " << ex.what());
+    }
+
+    // Id type is valid, so attempt to make the user based on that and
+    // the value we have for "id".
+    UserPtr user;
+    try {
+        user.reset(new User(id_type, id_str));
+    } catch (const std::exception& ex) {
+        isc_throw (UserFileError, "UserFile cannot create user form entry: "
+                                  << user_string << " " << ex.what());
+    }
+
+    // We have a new User, so add in the properties and return it.
+    user->setProperties(properties);
+    return (user);
+}
+
+bool
+UserFile::isOpen() const {
+    return (file_.is_open());
+}
+
+void
+UserFile::close() {
+    try {
+        if (file_.is_open()) {
+            file_.close();
+        }
+    } catch (const std::exception& ex) {
+        // Highly unlikely to occur but let's at least spit out an error.
+        // Beyond that we swallow it for tidiness.
+        std::cout << "UserFile unexpected error closing the file: "
+                  << fname_ << " : " << ex.what() << std::endl;
+    }
+}
+
diff --git a/src/hooks/dhcp/user_chk/user_file.h b/src/hooks/dhcp/user_chk/user_file.h
new file mode 100644
index 0000000..b65862e
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/user_file.h
@@ -0,0 +1,135 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+#ifndef _USER_FILE_H
+#define _USER_FILE_H
+
+/// @file user_file.h Defines the class, UserFile, which implements the UserDataSource interface for text files.
+
+#include <user_data_source.h>
+#include <user.h>
+
+#include <boost/shared_ptr.hpp>
+#include <fstream>
+#include <string>
+
+using namespace std;
+
+/// @brief Thrown a UserFile encounters an error.
+/// Note that it derives from UserDataSourceError to comply with the interface.
+class UserFileError : public UserDataSourceError {
+public:
+    UserFileError(const char* file, size_t line,
+                               const char* what) :
+        UserDataSourceError(file, line, what) { };
+};
+
+/// @brief Provides a UserDataSource implementation for JSON text files.
+/// This class allows a text file of JSON entries to be treated as a source of
+/// User entries.  The format of the file is one user entry per line, where
+/// each line contains a JSON string as follows:
+///
+/// { "type" : "<user type>", "id" : "<user_id>" (options)  }
+///
+/// where:
+///
+/// <user_type>  text label of the id type: "HW_ADDR" or "DUID"
+/// <user_id>  the user's id as a string of hex digits without delimiters
+/// (options) zero or more string elements as name-value pairs, separated by
+/// commas: "opt1" : "val1",  "other_opt", "77" ...
+///
+/// Each entry must have a valid entry for "type" and a valid entry or "id".
+///
+/// If an entry contains duplicate option names, that option will be assigend
+/// the last value found. This is typical JSON behavior.
+/// Currently, only string option values (i.e. enclosed in quotes) are
+/// supported.
+///
+/// Example file entries might look like this:
+/// @code
+///
+/// { "type" : "HW_ADDR", "id" : "01AC00F03344", "opt1" : "true" }
+/// { "type" : "DUID", "id" : "225060de0a0b", "opt1" : "false" }
+///
+/// @endcode
+class UserFile : public UserDataSource {
+public:
+    /// @brief Maximum length of a single user entry.
+    /// This value is somewhat arbitrary. 4K seems reasonably large.  If it
+    /// goes beyond this, then a flat file is not likely the way to go.
+    static const size_t USER_ENTRY_MAX_LEN = 4096;
+
+    /// @brief Constructor
+    ///
+    /// Create a UserFile for the given file name without opening the file.
+    /// @param fname pathname to the input file.
+    ///
+    /// @throw UserFileError if given file name is empty.
+    UserFile(const std::string& fname);
+
+    /// @brief Destructor.
+    ////
+    /// The destructor does call the close method.
+    virtual ~UserFile();
+
+    /// @brief Opens the input file for reading.
+    ///
+    /// Upon successful completion, the file is opened and positioned to start
+    /// reading from the beginning of the file.
+    ///
+    /// @throw UserFileError if the file cannot be opened.
+    virtual void open();
+
+    /// @brief Fetches the next user from the file.
+    ///
+    /// Reads the next user entry from the file and attempts to create a
+    /// new User from the text therein.  If there is no more data to be read
+    /// it returns an empty UserPtr.
+    ///
+    /// @return A UserPtr pointing to the new User or an empty pointer on EOF.
+    ///
+    /// @throw UserFileError if an error occurs while reading.
+    virtual UserPtr readNextUser();
+
+    /// @brief Closes the underlying file.
+    ///
+    /// Method is exception safe.
+    virtual void close();
+
+    /// @brief Returns true if the file is open.
+    ///
+    /// @return True if the underlying file is open, false otherwise.
+    virtual bool isOpen() const;
+
+    /// @brief Creates a new User instance from JSON text.
+    ///
+    /// @param user_string string the JSON text for a user entry.
+    ///
+    /// @return A pointer to the newly created User instance.
+    ///
+    /// @throw UserFileError if the entry is invalid.
+    UserPtr makeUser(const std::string& user_string);
+
+private:
+    /// @brief Pathname of the input text file.
+    string fname_;
+
+    /// @brief Input file stream.
+    std::ifstream file_;
+
+};
+
+/// @brief Defines a smart pointer to a UserFile.
+typedef boost::shared_ptr<UserFile> UserFilePtr;
+
+#endif
diff --git a/src/hooks/dhcp/user_chk/user_registry.cc b/src/hooks/dhcp/user_chk/user_registry.cc
new file mode 100644
index 0000000..44b98a5
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/user_registry.cc
@@ -0,0 +1,122 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <user_registry.h>
+#include <user.h>
+
+UserRegistry::UserRegistry() {
+}
+
+UserRegistry::~UserRegistry(){
+}
+
+void
+UserRegistry::addUser(UserPtr& user) {
+    if (!user) {
+        isc_throw (UserRegistryError, "UserRegistry cannot add blank user");
+    }
+
+    UserPtr found_user;
+    if ((found_user = findUser(user->getUserId()))) {
+        isc_throw (UserRegistryError, "UserRegistry duplicate user: "
+                   << user->getUserId());
+    }
+
+    users_[user->getUserId()] = user;
+}
+
+const UserPtr&
+UserRegistry::findUser(const UserId& id) const {
+    static UserPtr empty;
+    UserMap::const_iterator it = users_.find(id);
+    if (it != users_.end()) {
+        const UserPtr tmp = (*it).second;
+        return ((*it).second);
+    }
+
+    return empty;
+}
+
+void
+UserRegistry::removeUser(const UserId& id) {
+    static UserPtr empty;
+    UserMap::iterator it = users_.find(id);
+    if (it != users_.end()) {
+        users_.erase(it);
+    }
+}
+
+const UserPtr&
+UserRegistry::findUser(const isc::dhcp::HWAddr& hwaddr) const {
+    UserId id(UserId::HW_ADDRESS, hwaddr.hwaddr_);
+    return (findUser(id));
+}
+
+const UserPtr&
+UserRegistry::findUser(const isc::dhcp::DUID& duid) const {
+    UserId id(UserId::DUID, duid.getDuid());
+    return (findUser(id));
+}
+
+void UserRegistry::refresh() {
+    if (!source_) {
+        isc_throw(UserRegistryError,
+                  "UserRegistry: cannot refresh, no data source");
+    }
+
+    // If the source isn't open, open it.
+    if (!source_->isOpen()) {
+        source_->open();
+    }
+
+    // Make a copy in case something goes wrong midstream.
+    UserMap backup(users_);
+
+    // Empty the registry then read users from source until source is empty.
+    clearall();
+    try {
+        UserPtr user;
+        while ((user = source_->readNextUser())) {
+            addUser(user);
+        }
+    } catch (const std::exception& ex) {
+        // Source was compromsised so restore registry from backup.
+        users_ = backup;
+        // Close the source.
+        source_->close();
+        isc_throw (UserRegistryError, "UserRegistry: refresh failed during read"
+                   << ex.what());
+    }
+
+    // Close the source.
+    source_->close();
+}
+
+void UserRegistry::clearall() {
+    users_.clear();
+}
+
+void UserRegistry::setSource(UserDataSourcePtr& source) {
+    if (!source) {
+        isc_throw (UserRegistryError,
+                   "UserRegistry: data source cannot be set to null");
+    }
+
+    source_ = source;
+}
+
+const UserDataSourcePtr& UserRegistry::getSource() {
+    return (source_);
+}
+
diff --git a/src/hooks/dhcp/user_chk/user_registry.h b/src/hooks/dhcp/user_chk/user_registry.h
new file mode 100644
index 0000000..a0fb7dc
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/user_registry.h
@@ -0,0 +1,128 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+#ifndef _USER_REGISTRY_H
+#define _USER_REGISTRY_H
+
+/// @file user_registry.h Defines the class, UserRegistry.
+
+#include <dhcp/hwaddr.h>
+#include <dhcp/duid.h>
+#include <exceptions/exceptions.h>
+#include <user.h>
+#include <user_data_source.h>
+
+#include <string>
+
+using namespace std;
+
+/// @brief Thrown UserRegistry encounters an error
+class UserRegistryError : public isc::Exception {
+public:
+    UserRegistryError(const char* file, size_t line,
+                               const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Defines a map of unique Users keyed by UserId.
+typedef std::map<UserId,UserPtr> UserMap;
+
+/// @brief Embodies an update-able, searchable list of unique users
+/// This class provides the means to create and maintain a searchable list
+/// of unique users. List entries are pointers to instances of User, keyed
+/// by their UserIds.
+/// Users may be added and removed from the list individually or the list
+/// may be updated by loading it from a data source, such as a file.
+class UserRegistry {
+public:
+    /// @brief Constructor
+    ///
+    /// Creates a new registry with an empty list of users and no data source.
+    UserRegistry();
+
+    /// @brief Destructor
+    ~UserRegistry();
+
+    /// @brief Adds a given user to the registry.
+    ///
+    /// @param user A pointer to the user to add
+    ///
+    /// @throw UserRegistryError if the user is null or if the user already
+    /// exists in the registry.
+    void addUser(UserPtr& user);
+
+    /// @brief Finds a user in the registry by user id
+    ///
+    /// @param id The user id for which to search
+    ///
+    /// @return A pointer to the user if found or an null pointer if not.
+    const UserPtr& findUser(const UserId& id) const;
+
+    /// @brief Removes a user from the registry by user id
+    ///
+    /// Removes the user entry if found, if not simply return.
+    ///
+    /// @param id The user id of the user to remove
+    void removeUser(const UserId&  id);
+
+    /// @brief Finds a user in the registry by hardware address
+    ///
+    /// @param hwaddr The hardware address for which to search
+    ///
+    /// @return A pointer to the user if found or an null pointer if not.
+    const UserPtr& findUser(const isc::dhcp::HWAddr& hwaddr) const;
+
+    /// @brief Finds a user in the registry by DUID
+    ///
+    /// @param duid The DUID for which to search
+    ///
+    /// @return A pointer to the user if found or an null pointer if not.
+    const UserPtr& findUser(const isc::dhcp::DUID& duid) const;
+
+    /// @brief Updates the registry from its data source.
+    ///
+    /// This method will replace the contents of the registry with new content
+    /// read from its data source.  It will attempt to open the source and
+    /// then add users from the source to the registry until the source is
+    /// exhausted.  If an error occurs accessing the source the registry
+    /// contents will be restored to that of before the call to refresh.
+    ///
+    /// @throw UserRegistryError if the data source has not been set (is null)
+    /// or if an error occurs accessing the data source.
+    void refresh();
+
+    /// @brief Removes all entries from the registry.
+    void clearall();
+
+    /// @brief Returns a reference to the data source.
+    const UserDataSourcePtr& getSource();
+
+    /// @brief Sets the data source to the given value.
+    ///
+    /// @param source reference to the data source to use.
+    ///
+    /// @throw UserRegistryError if new source value is null.
+    void setSource(UserDataSourcePtr& source);
+
+private:
+    /// @brief The registry of users.
+    UserMap users_;
+
+    /// @brief The current data source of users.
+    UserDataSourcePtr source_;
+};
+
+/// @brief Define a smart pointer to a UserRegistry.
+typedef boost::shared_ptr<UserRegistry> UserRegistryPtr;
+
+#endif
diff --git a/src/hooks/dhcp/user_chk/version.cc b/src/hooks/dhcp/user_chk/version.cc
new file mode 100644
index 0000000..eaa9d50
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/version.cc
@@ -0,0 +1,25 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+// version.cc
+
+#include <hooks/hooks.h>
+
+extern "C" {
+
+/// @brief Version function required by Hooks API for compatibility checks.
+int version() {
+    return (BIND10_HOOKS_VERSION);
+}
+
+}
diff --git a/src/lib/asiodns/Makefile.am b/src/lib/asiodns/Makefile.am
index 321de8b..dab1a32 100644
--- a/src/lib/asiodns/Makefile.am
+++ b/src/lib/asiodns/Makefile.am
@@ -8,11 +8,14 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
-CLEANFILES = *.gcno *.gcda asiodns_messages.h asiodns_messages.cc
+CLEANFILES = *.gcno *.gcda asiodns_messages.h asiodns_messages.cc s-messages
 
 # Define rule to build logging source files from message file
-asiodns_messages.h asiodns_messages.cc: asiodns_messages.mes
+asiodns_messages.h asiodns_messages.cc: s-messages
+
+s-messages: asiodns_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/asiodns/asiodns_messages.mes
+	touch $@
 
 BUILT_SOURCES = asiodns_messages.h asiodns_messages.cc
 
@@ -35,9 +38,6 @@ EXTRA_DIST = asiodns_messages.mes
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
 libb10_asiodns_la_CXXFLAGS = $(AM_CXXFLAGS)
-if USE_CLANGPP
-# Same for clang++, but we need to turn off -Werror completely.
-libb10_asiodns_la_CXXFLAGS += -Wno-error
-endif
 libb10_asiodns_la_CPPFLAGS = $(AM_CPPFLAGS)
-libb10_asiodns_la_LIBADD = $(top_builddir)/src/lib/log/libb10-log.la
+libb10_asiodns_la_LIBADD  = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+libb10_asiodns_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
diff --git a/src/lib/asiodns/tests/Makefile.am b/src/lib/asiodns/tests/Makefile.am
index 25f2ea8..25b524e 100644
--- a/src/lib/asiodns/tests/Makefile.am
+++ b/src/lib/asiodns/tests/Makefile.am
@@ -44,10 +44,6 @@ run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
 if USE_GXX
 run_unittests_CXXFLAGS += -Wno-unused-parameter
 endif
-if USE_CLANGPP
-# Same for clang++, but we need to turn off -Werror completely.
-run_unittests_CXXFLAGS += -Wno-error
-endif
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/asiolink/Makefile.am b/src/lib/asiolink/Makefile.am
index 8a5bc76..504dd78 100644
--- a/src/lib/asiolink/Makefile.am
+++ b/src/lib/asiolink/Makefile.am
@@ -37,10 +37,6 @@ libb10_asiolink_la_SOURCES += local_socket.h local_socket.cc
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
 libb10_asiolink_la_CXXFLAGS = $(AM_CXXFLAGS)
-if USE_CLANGPP
-# Same for clang++, but we need to turn off -Werror completely.
-libb10_asiolink_la_CXXFLAGS += -Wno-error
-endif
 libb10_asiolink_la_CPPFLAGS = $(AM_CPPFLAGS)
 libb10_asiolink_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 
diff --git a/src/lib/asiolink/io_asio_socket.h b/src/lib/asiolink/io_asio_socket.h
index a3f3f97..afd2884 100644
--- a/src/lib/asiolink/io_asio_socket.h
+++ b/src/lib/asiolink/io_asio_socket.h
@@ -344,7 +344,7 @@ public:
     ///
     /// Must be supplied as it is abstract in the base class.
     /// The parameters are unused.
-    virtual void asyncReceive(void* data, size_t, size_t, IOEndpoint*, C&) {
+    virtual void asyncReceive(void*, size_t, size_t, IOEndpoint*, C&) {
     }
 
     /// \brief Checks if the data received is complete.
@@ -357,10 +357,8 @@ public:
     /// \param outbuff Unused.
     ///
     /// \return Always true
-    virtual bool receiveComplete(const void* staging, size_t length,
-                                 size_t& cumulative, size_t& offset,
-                                 size_t& expected,
-                                 isc::util::OutputBufferPtr& outbuff)
+    virtual bool receiveComplete(const void*, size_t, size_t&, size_t&,
+                                 size_t&, isc::util::OutputBufferPtr&)
     {
         return (true);
     }
diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am
index 530fe0b..8525c2a 100644
--- a/src/lib/asiolink/tests/Makefile.am
+++ b/src/lib/asiolink/tests/Makefile.am
@@ -52,10 +52,6 @@ run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
 if USE_GXX
 run_unittests_CXXFLAGS += -Wno-unused-parameter
 endif
-if USE_CLANGPP
-# Same for clang++, but we need to turn off -Werror completely.
-run_unittests_CXXFLAGS += -Wno-error
-endif
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/asiolink/tests/io_endpoint_unittest.cc b/src/lib/asiolink/tests/io_endpoint_unittest.cc
index 462a2fb..c953974 100644
--- a/src/lib/asiolink/tests/io_endpoint_unittest.cc
+++ b/src/lib/asiolink/tests/io_endpoint_unittest.cc
@@ -41,7 +41,7 @@ TEST(IOEndpointTest, createUDPv4) {
     EXPECT_EQ(53210, ep->getPort());
     EXPECT_EQ(AF_INET, ep->getFamily());
     EXPECT_EQ(AF_INET, ep->getAddress().getFamily());
-    EXPECT_EQ(IPPROTO_UDP, ep->getProtocol());
+    EXPECT_EQ(static_cast<short>(IPPROTO_UDP), ep->getProtocol());
 }
 
 TEST(IOEndpointTest, createTCPv4) {
@@ -51,7 +51,7 @@ TEST(IOEndpointTest, createTCPv4) {
     EXPECT_EQ(5301, ep->getPort());
     EXPECT_EQ(AF_INET, ep->getFamily());
     EXPECT_EQ(AF_INET, ep->getAddress().getFamily());
-    EXPECT_EQ(IPPROTO_TCP, ep->getProtocol());
+    EXPECT_EQ(static_cast<short>(IPPROTO_TCP), ep->getProtocol());
 }
 
 TEST(IOEndpointTest, createUDPv6) {
@@ -62,7 +62,7 @@ TEST(IOEndpointTest, createUDPv6) {
     EXPECT_EQ(5302, ep->getPort());
     EXPECT_EQ(AF_INET6, ep->getFamily());
     EXPECT_EQ(AF_INET6, ep->getAddress().getFamily());
-    EXPECT_EQ(IPPROTO_UDP, ep->getProtocol());
+    EXPECT_EQ(static_cast<short>(IPPROTO_UDP), ep->getProtocol());
 }
 
 TEST(IOEndpointTest, createTCPv6) {
@@ -73,7 +73,7 @@ TEST(IOEndpointTest, createTCPv6) {
     EXPECT_EQ(5303, ep->getPort());
     EXPECT_EQ(AF_INET6, ep->getFamily());
     EXPECT_EQ(AF_INET6, ep->getAddress().getFamily());
-    EXPECT_EQ(IPPROTO_TCP, ep->getProtocol());
+    EXPECT_EQ(static_cast<short>(IPPROTO_TCP), ep->getProtocol());
 }
 
 TEST(IOEndpointTest, equality) {
diff --git a/src/lib/asiolink/tests/io_socket_unittest.cc b/src/lib/asiolink/tests/io_socket_unittest.cc
index 15afc17..44e3630 100644
--- a/src/lib/asiolink/tests/io_socket_unittest.cc
+++ b/src/lib/asiolink/tests/io_socket_unittest.cc
@@ -23,8 +23,10 @@
 using namespace isc::asiolink;
 
 TEST(IOSocketTest, dummySockets) {
-    EXPECT_EQ(IPPROTO_UDP, IOSocket::getDummyUDPSocket().getProtocol());
-    EXPECT_EQ(IPPROTO_TCP, IOSocket::getDummyTCPSocket().getProtocol());
+    EXPECT_EQ(static_cast<short>(IPPROTO_UDP),
+              IOSocket::getDummyUDPSocket().getProtocol());
+    EXPECT_EQ(static_cast<short>(IPPROTO_TCP),
+              IOSocket::getDummyTCPSocket().getProtocol());
     EXPECT_EQ(-1, IOSocket::getDummyUDPSocket().getNative());
     EXPECT_EQ(-1, IOSocket::getDummyTCPSocket().getNative());
 }
diff --git a/src/lib/asiolink/tests/tcp_endpoint_unittest.cc b/src/lib/asiolink/tests/tcp_endpoint_unittest.cc
index 6988082..79f330f 100644
--- a/src/lib/asiolink/tests/tcp_endpoint_unittest.cc
+++ b/src/lib/asiolink/tests/tcp_endpoint_unittest.cc
@@ -37,7 +37,7 @@ TEST(TCPEndpointTest, v4Address) {
 
     EXPECT_TRUE(address == endpoint.getAddress());
     EXPECT_EQ(test_port, endpoint.getPort());
-    EXPECT_EQ(IPPROTO_TCP, endpoint.getProtocol());
+    EXPECT_EQ(static_cast<short>(IPPROTO_TCP), endpoint.getProtocol());
     EXPECT_EQ(AF_INET, endpoint.getFamily());
 }
 
@@ -50,6 +50,6 @@ TEST(TCPEndpointTest, v6Address) {
 
     EXPECT_TRUE(address == endpoint.getAddress());
     EXPECT_EQ(test_port, endpoint.getPort());
-    EXPECT_EQ(IPPROTO_TCP, endpoint.getProtocol());
+    EXPECT_EQ(static_cast<short>(IPPROTO_TCP), endpoint.getProtocol());
     EXPECT_EQ(AF_INET6, endpoint.getFamily());
 }
diff --git a/src/lib/asiolink/tests/udp_endpoint_unittest.cc b/src/lib/asiolink/tests/udp_endpoint_unittest.cc
index 03de6b8..507103c 100644
--- a/src/lib/asiolink/tests/udp_endpoint_unittest.cc
+++ b/src/lib/asiolink/tests/udp_endpoint_unittest.cc
@@ -37,7 +37,7 @@ TEST(UDPEndpointTest, v4Address) {
 
     EXPECT_TRUE(address == endpoint.getAddress());
     EXPECT_EQ(test_port, endpoint.getPort());
-    EXPECT_EQ(IPPROTO_UDP, endpoint.getProtocol());
+    EXPECT_EQ(static_cast<short>(IPPROTO_UDP), endpoint.getProtocol());
     EXPECT_EQ(AF_INET, endpoint.getFamily());
 }
 
@@ -50,6 +50,6 @@ TEST(UDPEndpointTest, v6Address) {
 
     EXPECT_TRUE(address == endpoint.getAddress());
     EXPECT_EQ(test_port, endpoint.getPort());
-    EXPECT_EQ(IPPROTO_UDP, endpoint.getProtocol());
+    EXPECT_EQ(static_cast<short>(IPPROTO_UDP), endpoint.getProtocol());
     EXPECT_EQ(AF_INET6, endpoint.getFamily());
 }
diff --git a/src/lib/cache/Makefile.am b/src/lib/cache/Makefile.am
index 00ca16e..7a84dd6 100644
--- a/src/lib/cache/Makefile.am
+++ b/src/lib/cache/Makefile.am
@@ -36,9 +36,12 @@ nodist_libb10_cache_la_SOURCES = cache_messages.cc cache_messages.h
 
 BUILT_SOURCES = cache_messages.cc cache_messages.h
 
-cache_messages.cc cache_messages.h: cache_messages.mes
+cache_messages.cc cache_messages.h: s-messages
+
+s-messages: cache_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/cache/cache_messages.mes
+	touch $@
 
-CLEANFILES = *.gcno *.gcda cache_messages.cc cache_messages.h
+CLEANFILES = *.gcno *.gcda cache_messages.cc cache_messages.h s-messages
 
 EXTRA_DIST = cache_messages.mes
diff --git a/src/lib/cc/Makefile.am b/src/lib/cc/Makefile.am
index 06e9309..55c14c8 100644
--- a/src/lib/cc/Makefile.am
+++ b/src/lib/cc/Makefile.am
@@ -13,12 +13,6 @@ if USE_GXX
 AM_CXXFLAGS += -Wno-unused-parameter
 AM_CXXFLAGS += -fno-strict-aliasing
 endif
-if USE_CLANGPP
-# Likewise, ASIO header files will trigger various warnings with clang++.
-# Worse, there doesn't seem to be any option to disable one of the warnings
-# in any way, so we need to turn off -Werror.
-AM_CXXFLAGS += -Wno-error
-endif
 
 lib_LTLIBRARIES = libb10-cc.la
 libb10_cc_la_SOURCES = data.cc data.h session.cc session.h
@@ -29,13 +23,16 @@ nodist_libb10_cc_la_SOURCES += proto_defs.h
 libb10_cc_la_LIBADD = $(top_builddir)/src/lib/log/libb10-log.la
 
 CLEANFILES = *.gcno *.gcda session_config.h cc_messages.cc cc_messages.h \
-	proto_defs.h
+	proto_defs.h s-messages
 
 session_config.h: session_config.h.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" session_config.h.pre >$@
 
-cc_messages.cc cc_messages.h: cc_messages.mes
+cc_messages.cc cc_messages.h: s-messages
+
+s-messages: cc_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/cc/cc_messages.mes
+	touch $@
 
 BUILT_SOURCES = session_config.h cc_messages.cc cc_messages.h proto_defs.h
 
diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc
index 3518939..5f87d7c 100644
--- a/src/lib/cc/data.cc
+++ b/src/lib/cc/data.cc
@@ -145,6 +145,11 @@ Element::size() const {
     isc_throw(TypeError, "size() called on a non-list Element");
 }
 
+bool
+Element::empty() const {
+    isc_throw(TypeError, "empty() called on a non-list Element");
+}
+
 ConstElementPtr
 Element::get(const std::string&) const {
     isc_throw(TypeError, "get(string) called on a non-map Element");
diff --git a/src/lib/cc/data.h b/src/lib/cc/data.h
index 8050607..bea9dbb 100644
--- a/src/lib/cc/data.h
+++ b/src/lib/cc/data.h
@@ -210,6 +210,9 @@ public:
 
     /// Returns the number of elements in the list.
     virtual size_t size() const;
+
+    /// Return true if there are no elements in the list.
+    virtual bool empty() const;
     //@}
 
 
@@ -479,6 +482,7 @@ public:
     void remove(int i) { l.erase(l.begin() + i); };
     void toJSON(std::ostream& ss) const;
     size_t size() const { return (l.size()); }
+    bool empty() const { return (l.empty()); }
     bool equals(const Element& other) const;
 };
 
diff --git a/src/lib/cc/session.cc b/src/lib/cc/session.cc
index cb1ca39..0a2f11d 100644
--- a/src/lib/cc/session.cc
+++ b/src/lib/cc/session.cc
@@ -535,7 +535,7 @@ Session::reply(ConstElementPtr envelope, ConstElementPtr newmsg) {
 
 bool
 Session::hasQueuedMsgs() const {
-    return (impl_->queue_->size() > 0);
+    return (!impl_->queue_->empty());
 }
 
 void
diff --git a/src/lib/cc/tests/Makefile.am b/src/lib/cc/tests/Makefile.am
index 1c2b4b8..2afcf14 100644
--- a/src/lib/cc/tests/Makefile.am
+++ b/src/lib/cc/tests/Makefile.am
@@ -6,9 +6,6 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
 if USE_GXX			#XXX: see ../Makefile.am
 AM_CXXFLAGS += -Wno-unused-parameter
 endif
-if USE_CLANGPP
-AM_CXXFLAGS += -Wno-error
-endif
 
 if USE_STATIC_LINK
 AM_LDFLAGS = -static
diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc
index a60c994..9568884 100644
--- a/src/lib/cc/tests/data_unittests.cc
+++ b/src/lib/cc/tests/data_unittests.cc
@@ -418,6 +418,7 @@ TEST(Element, create_and_value_throws) {
     EXPECT_THROW(el->add(el), TypeError);
     EXPECT_THROW(el->remove(1), TypeError);
     EXPECT_THROW(el->size(), TypeError);
+    EXPECT_THROW(el->empty(), TypeError);
     EXPECT_THROW(el->get("foo"), TypeError);
     EXPECT_THROW(el->set("foo", el), TypeError);
     EXPECT_THROW(el->remove("foo"), TypeError);
@@ -441,6 +442,7 @@ TEST(Element, create_and_value_throws) {
     EXPECT_THROW(el->add(el), TypeError);
     EXPECT_THROW(el->remove(1), TypeError);
     EXPECT_THROW(el->size(), TypeError);
+    EXPECT_THROW(el->empty(), TypeError);
     EXPECT_THROW(el->get("foo"), TypeError);
     EXPECT_THROW(el->set("foo", el), TypeError);
     EXPECT_THROW(el->remove("foo"), TypeError);
@@ -464,6 +466,7 @@ TEST(Element, create_and_value_throws) {
     EXPECT_THROW(el->add(el), TypeError);
     EXPECT_THROW(el->remove(1), TypeError);
     EXPECT_THROW(el->size(), TypeError);
+    EXPECT_THROW(el->empty(), TypeError);
     EXPECT_THROW(el->get("foo"), TypeError);
     EXPECT_THROW(el->set("foo", el), TypeError);
     EXPECT_THROW(el->remove("foo"), TypeError);
@@ -487,6 +490,7 @@ TEST(Element, create_and_value_throws) {
     EXPECT_THROW(el->add(el), TypeError);
     EXPECT_THROW(el->remove(1), TypeError);
     EXPECT_THROW(el->size(), TypeError);
+    EXPECT_THROW(el->empty(), TypeError);
     EXPECT_THROW(el->get("foo"), TypeError);
     EXPECT_THROW(el->set("foo", el), TypeError);
     EXPECT_THROW(el->remove("foo"), TypeError);
@@ -497,8 +501,10 @@ TEST(Element, create_and_value_throws) {
     testGetValueList<ConstElementPtr>();
 
     el = Element::createList();
+    EXPECT_TRUE(el->empty());
     v.push_back(Element::create(1));
     EXPECT_TRUE(el->setValue(v));
+    EXPECT_FALSE(el->empty());
     EXPECT_EQ("[ 1 ]", el->str());
 
     testGetValueMap<ElementPtr>();
diff --git a/src/lib/config/Makefile.am b/src/lib/config/Makefile.am
index b4fc2e0..9820f08 100644
--- a/src/lib/config/Makefile.am
+++ b/src/lib/config/Makefile.am
@@ -6,8 +6,11 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 
 # Define rule to build logging source files from message file
-config_messages.h config_messages.cc: config_messages.mes
+config_messages.h config_messages.cc: s-messages
+
+s-messages: config_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/config/config_messages.mes
+	touch $@
 
 BUILT_SOURCES = config_messages.h config_messages.cc
 
@@ -27,4 +30,4 @@ nodist_libb10_cfgclient_la_SOURCES  = config_messages.h config_messages.cc
 # The message file should be in the distribution.
 EXTRA_DIST = config_messages.mes
 
-CLEANFILES = *.gcno *.gcda config_messages.h config_messages.cc
+CLEANFILES = *.gcno *.gcda config_messages.h config_messages.cc s-messages
diff --git a/src/lib/config/ccsession.cc b/src/lib/config/ccsession.cc
index 780fe95..10bc728 100644
--- a/src/lib/config/ccsession.cc
+++ b/src/lib/config/ccsession.cc
@@ -144,7 +144,7 @@ parseCommand(ConstElementPtr& arg, ConstElementPtr command) {
         command->contains(isc::cc::CC_PAYLOAD_COMMAND)) {
         ConstElementPtr cmd = command->get(isc::cc::CC_PAYLOAD_COMMAND);
         if (cmd->getType() == Element::list &&
-            cmd->size() > 0 &&
+            !cmd->empty() &&
             cmd->get(0)->getType() == Element::string) {
             if (cmd->size() > 1) {
                 arg = cmd->get(1);
@@ -611,6 +611,11 @@ ModuleCCSession::checkCommand() {
             return (0);
         }
 
+        // In case it is notification, eat it.
+        if (checkNotification(routing, data)) {
+            return (0);
+        }
+
         /* ignore result messages (in case we're out of sync, to prevent
          * pingpongs */
         if (data->getType() != Element::map ||
@@ -902,5 +907,84 @@ ModuleCCSession::notify(const std::string& group, const std::string& name,
                  isc::cc::CC_TO_WILDCARD, false);
 }
 
+ModuleCCSession::NotificationID
+ModuleCCSession::subscribeNotification(const std::string& notification_group,
+                                       const NotificationCallback& callback)
+{
+    // Either insert a new empty list of callbacks or get an existing one.
+    // Either way, get the iterator for its position.
+    const std::pair<SubscribedNotifications::iterator, bool>& inserted =
+        notifications_.insert(
+            std::pair<std::string, NotificationCallbacks>(notification_group,
+                NotificationCallbacks()));
+    if (inserted.second) {
+        // It was newly inserted. In that case, we need to subscribe to the
+        // group.
+        session_.subscribe(isc::cc::CC_GROUP_NOTIFICATION_PREFIX +
+                           notification_group);
+    }
+    // Insert the callback to the chain
+    NotificationCallbacks& callbacks = inserted.first->second;
+    const NotificationCallbacks::iterator& callback_id =
+        callbacks.insert(callbacks.end(), callback);
+    // Just pack the iterators to form the ID
+    return (NotificationID(inserted.first, callback_id));
+}
+
+void
+ModuleCCSession::unsubscribeNotification(const NotificationID& notification) {
+    NotificationCallbacks& callbacks = notification.first->second;
+    // Remove the callback
+    callbacks.erase(notification.second);
+    // If it became empty, remove it from the map and unsubscribe
+    if (callbacks.empty()) {
+        session_.unsubscribe(isc::cc::CC_GROUP_NOTIFICATION_PREFIX +
+                             notification.first->first);
+        notifications_.erase(notification.first);
+    }
+}
+
+bool
+ModuleCCSession::checkNotification(const data::ConstElementPtr& envelope,
+                                   const data::ConstElementPtr& msg)
+{
+    if (msg->getType() != data::Element::map) {
+        // If it's not a map, then it's not a notification
+        return (false);
+    }
+    if (msg->contains(isc::cc::CC_PAYLOAD_NOTIFICATION)) {
+        // There's a notification inside. Extract its parameters.
+        const std::string& group =
+            envelope->get(isc::cc::CC_HEADER_GROUP)->stringValue();
+        const std::string& notification_group =
+            group.substr(std::string(isc::cc::CC_GROUP_NOTIFICATION_PREFIX).
+                         size());
+        const data::ConstElementPtr& notification =
+            msg->get(isc::cc::CC_PAYLOAD_NOTIFICATION);
+        // The first one is the event that happened
+        const std::string& event = notification->get(0)->stringValue();
+        // Any other params are second. But they may be missing
+        const data::ConstElementPtr params =
+            notification->size() == 1 ? data::ConstElementPtr() :
+            notification->get(1);
+        // Find the chain of notification callbacks
+        const SubscribedNotifications::iterator& chain_iter =
+            notifications_.find(notification_group);
+        if (chain_iter == notifications_.end()) {
+            // This means we no longer have any notifications for this group.
+            // This can happen legally as a race condition - if msgq sends
+            // us a notification, but we unsubscribe before we get to it
+            // in the input stream.
+            return (false);
+        }
+        BOOST_FOREACH(const NotificationCallback& callback,
+                      chain_iter->second) {
+            callback(event, params);
+        }
+        return (true);
+    }
+    return (false); // Not a notification
+}
+
 }
 }
diff --git a/src/lib/config/ccsession.h b/src/lib/config/ccsession.h
index c536861..75c3ee6 100644
--- a/src/lib/config/ccsession.h
+++ b/src/lib/config/ccsession.h
@@ -575,6 +575,57 @@ public:
     /// \param id The id of request as returned by groupRecvMsgAsync.
     void cancelAsyncRecv(const AsyncRecvRequestID& id);
 
+    /// \brief Called when a notification comes
+    ///
+    /// The callback should be exception-free. If it raises an exception,
+    /// it'll leak through the event loop up and probably terminate the
+    /// application.
+    ///
+    /// \param event_name The identification of event type.
+    /// \param params The parameters of the event. This may be NULL
+    ///     pointer in case no parameters were sent with the event.
+    typedef boost::function<void (const std::string& event_name,
+                                  const data::ConstElementPtr& params)>
+        NotificationCallback;
+
+    /// \brief Multiple notification callbacks for the same notification
+    typedef std::list<NotificationCallback> NotificationCallbacks;
+
+    /// \brief Mapping from groups to callbacks
+    typedef std::map<std::string, NotificationCallbacks>
+        SubscribedNotifications;
+
+    /// \brief Identification of single callback
+    typedef std::pair<SubscribedNotifications::iterator,
+                      NotificationCallbacks::iterator>
+        NotificationID;
+
+    /// \brief Subscribe to a notification group
+    ///
+    /// From now on, every notification that is sent to the given group
+    /// triggers the passed callback.
+    ///
+    /// There may be multiple (independent) callbacks for the same channel.
+    /// This one adds a new one, to the end of the chain (the callbacks
+    /// are called in the same order as they were registered).
+    ///
+    /// \param notification_group The channel of notifications.
+    /// \param callback The callback to be added.
+    /// \return ID of the notification callback. It is an opaque ID and can
+    ///     be used to remove this callback.
+    NotificationID subscribeNotification(const std::string& notification_group,
+                                         const NotificationCallback& callback);
+
+    /// \brief Unsubscribe the callback from its notification group.
+    ///
+    /// Express that the desire for this callback to be executed is no longer
+    /// relevant. All the other callbacks (even for the same notification
+    /// group) are left intact.
+    ///
+    /// \param notification The ID of notification callback returned by
+    ///     subscribeNotification.
+    void unsubscribeNotification(const NotificationID& notification);
+
     /// \brief Subscribe to a group
     ///
     /// Wrapper around the CCSession::subscribe.
@@ -634,6 +685,8 @@ private:
     ///     otherwise.
     bool checkAsyncRecv(const data::ConstElementPtr& envelope,
                         const data::ConstElementPtr& msg);
+    bool checkNotification(const data::ConstElementPtr& envelope,
+                           const data::ConstElementPtr& msg);
     /// \brief Checks if a message with this envelope matches the request
     bool requestMatch(const AsyncRecvRequest& request,
                       const data::ConstElementPtr& envelope) const;
@@ -643,6 +696,8 @@ private:
     isc::cc::AbstractSession& session_;
     ModuleSpec module_specification_;
     AsyncRecvRequests async_recv_requests_;
+    SubscribedNotifications notifications_;
+
     isc::data::ConstElementPtr handleConfigUpdate(
         isc::data::ConstElementPtr new_config);
 
diff --git a/src/lib/config/tests/ccsession_unittests.cc b/src/lib/config/tests/ccsession_unittests.cc
index 4a04412..b9c70d0 100644
--- a/src/lib/config/tests/ccsession_unittests.cc
+++ b/src/lib/config/tests/ccsession_unittests.cc
@@ -87,6 +87,65 @@ protected:
     const std::string root_name;
 };
 
+void
+notificationCallback(std::vector<std::string>* called,
+                     const std::string& id, const std::string& notification,
+                     const ConstElementPtr& params)
+{
+    called->push_back(id);
+    EXPECT_EQ("event", notification);
+    EXPECT_TRUE(el("{\"param\": true}")->equals(*params));
+}
+
+TEST_F(CCSessionTest, receiveNotification) {
+    // Not subscribed to the group yet
+    ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL,
+                         false, false);
+    EXPECT_FALSE(session.haveSubscription("notifications/group", "*"));
+    std::vector<std::string> called;
+    // Subscribe to the notification. Twice.
+    const ModuleCCSession::NotificationID& first =
+        mccs.subscribeNotification("group", boost::bind(&notificationCallback,
+                                                        &called, "first",
+                                                        _1, _2));
+    const ModuleCCSession::NotificationID& second =
+        mccs.subscribeNotification("group", boost::bind(&notificationCallback,
+                                                        &called, "second",
+                                                        _1, _2));
+    EXPECT_TRUE(session.haveSubscription("notifications/group", "*"));
+    EXPECT_TRUE(called.empty());
+    // Send the notification
+    const isc::data::ConstElementPtr msg = el("{"
+        "       \"notification\": ["
+        "           \"event\", {"
+        "               \"param\": true"
+        "           }"
+        "       ]"
+        "   }");
+    session.addMessage(msg, "notifications/group", "*");
+    mccs.checkCommand();
+    ASSERT_EQ(2, called.size());
+    EXPECT_EQ("first", called[0]);
+    EXPECT_EQ("second", called[1]);
+    called.clear();
+    // Unsubscribe one of them
+    mccs.unsubscribeNotification(first);
+    // We are still subscribed to the group and handle the requests
+    EXPECT_TRUE(session.haveSubscription("notifications/group", "*"));
+    // Send the notification
+    session.addMessage(msg, "notifications/group", "*");
+    mccs.checkCommand();
+    ASSERT_EQ(1, called.size());
+    EXPECT_EQ("second", called[0]);
+    // Unsubscribe the other one too. That should cancel the upstream
+    // subscription
+    mccs.unsubscribeNotification(second);
+    EXPECT_FALSE(session.haveSubscription("notifications/group", "*"));
+    // Nothing crashes if out of sync notification comes unexpected
+    session.addMessage(msg, "notifications/group", "*");
+    EXPECT_NO_THROW(mccs.checkCommand());
+}
+
 // Test we can send an RPC (command) and get an answer. The answer is success
 // in this case.
 TEST_F(CCSessionTest, rpcCallSuccess) {
diff --git a/src/lib/config/tests/fake_session.cc b/src/lib/config/tests/fake_session.cc
index e6d569e..c2fba57 100644
--- a/src/lib/config/tests/fake_session.cc
+++ b/src/lib/config/tests/fake_session.cc
@@ -104,7 +104,7 @@ FakeSession::recvmsg(ConstElementPtr& msg, bool nonblock, int) {
     //cout << "[XX] client asks for message " << endl;
     if (messages_ &&
         messages_->getType() == Element::list &&
-        messages_->size() > 0) {
+        !messages_->empty()) {
         msg = messages_->get(0);
         messages_->remove(0);
     } else {
@@ -127,7 +127,7 @@ FakeSession::recvmsg(ConstElementPtr& env, ConstElementPtr& msg, bool nonblock,
     env = ElementPtr();
     if (messages_ &&
         messages_->getType() == Element::list &&
-        messages_->size() > 0) {
+        !messages_->empty()) {
         // do we need initial message to have env[group] and [to] too?
         msg = messages_->get(0);
         messages_->remove(0);
@@ -210,13 +210,13 @@ FakeSession::reply(ConstElementPtr envelope, ConstElementPtr newmsg) {
 
 bool
 FakeSession::hasQueuedMsgs() const {
-    return (msg_queue_ && msg_queue_->size() > 0);
+    return (msg_queue_ && !msg_queue_->empty());
 }
 
 ConstElementPtr
 FakeSession::getFirstMessage(std::string& group, std::string& to) const {
     ConstElementPtr el;
-    if (msg_queue_ && msg_queue_->size() > 0) {
+    if (msg_queue_ && !msg_queue_->empty()) {
         el = msg_queue_->get(0);
         msg_queue_->remove(0);
         group = el->get(0)->stringValue();
diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am
index 5422f7d..e358c05 100644
--- a/src/lib/datasrc/Makefile.am
+++ b/src/lib/datasrc/Makefile.am
@@ -22,6 +22,7 @@ CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
 CLEANFILES += sqlite3_datasrc_messages.h sqlite3_datasrc_messages.cc
 CLEANFILES += datasrc_config.h
 CLEANFILES += static.zone
+CLEANFILES += s-messages1 s-messages2
 
 lib_LTLIBRARIES = libb10-datasrc.la
 libb10_datasrc_la_SOURCES = exceptions.h
@@ -65,10 +66,17 @@ libb10_datasrc_la_LIBADD += $(SQLITE_LIBS)
 
 BUILT_SOURCES = datasrc_config.h datasrc_messages.h datasrc_messages.cc
 BUILT_SOURCES += sqlite3_datasrc_messages.h sqlite3_datasrc_messages.cc
-datasrc_messages.h datasrc_messages.cc: Makefile datasrc_messages.mes
+datasrc_messages.h datasrc_messages.cc: s-messages1
+
+s-messages1: Makefile datasrc_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/datasrc/datasrc_messages.mes
-sqlite3_datasrc_messages.h sqlite3_datasrc_messages.cc: Makefile sqlite3_datasrc_messages.mes
+	touch $@
+
+sqlite3_datasrc_messages.h sqlite3_datasrc_messages.cc: s-messages2
+
+s-messages2: Makefile sqlite3_datasrc_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/datasrc/sqlite3_datasrc_messages.mes
+	touch $@
 
 EXTRA_DIST = datasrc_messages.mes sqlite3_datasrc_messages.mes static.zone.pre
 
diff --git a/src/lib/datasrc/client_list.cc b/src/lib/datasrc/client_list.cc
index 531821c..94afe96 100644
--- a/src/lib/datasrc/client_list.cc
+++ b/src/lib/datasrc/client_list.cc
@@ -83,6 +83,7 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
     if (!config) {
         isc_throw(isc::BadValue, "NULL configuration passed");
     }
+
     // TODO: Implement recycling from the old configuration.
     size_t i(0); // Outside of the try to be able to access it in the catch
     try {
diff --git a/src/lib/datasrc/memory/Makefile.am b/src/lib/datasrc/memory/Makefile.am
index 434eaf2..197f3fa 100644
--- a/src/lib/datasrc/memory/Makefile.am
+++ b/src/lib/datasrc/memory/Makefile.am
@@ -40,8 +40,11 @@ nodist_libdatasrc_memory_la_SOURCES = memory_messages.h memory_messages.cc
 EXTRA_DIST  = rdata_serialization_priv.cc
 
 BUILT_SOURCES = memory_messages.h memory_messages.cc
-memory_messages.h memory_messages.cc: Makefile memory_messages.mes
+memory_messages.h memory_messages.cc: s-messages
+
+s-messages: Makefile memory_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/datasrc/memory/memory_messages.mes
+	touch $@
 
 EXTRA_DIST += memory_messages.mes
-CLEANFILES += memory_messages.h memory_messages.cc
+CLEANFILES += memory_messages.h memory_messages.cc s-messages
diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h
index 5f41371..05d9795 100644
--- a/src/lib/datasrc/memory/domaintree.h
+++ b/src/lib/datasrc/memory/domaintree.h
@@ -361,11 +361,26 @@ private:
         return (getColor() == BLACK);
     }
 
+    /// \brief Static variant of isBlack() that also allows NULL nodes.
+    static bool isBlack(const DomainTreeNode<T>* node) {
+        if (!node) {
+            // NULL nodes are black.
+            return (true);
+        }
+
+        return (node->isBlack());
+    }
+
     /// \brief Returns if the node color is red
     bool isRed() const {
         return (!isBlack());
     }
 
+    /// \brief Static variant of isRed() that also allows NULL nodes.
+    static bool isRed(const DomainTreeNode<T>* node) {
+        return (!isBlack(node));
+    }
+
     /// \brief Sets the color of this node
     void setColor(const DomainTreeNodeColor color) {
         if (color == RED) {
@@ -393,11 +408,20 @@ private:
         return ((flags_ & FLAG_SUBTREE_ROOT) != 0);
     }
 
+    /// \brief Static helper function used by const and non-const
+    /// variants of getSubTreeRoot()
+    template <typename TT>
+    static TT*
+    getSubTreeRootImpl(TT* node);
+
     /// \brief returns the root of its subtree
     ///
     /// This method takes a node and returns the root of its subtree.
     ///
     /// This method never throws an exception.
+    DomainTreeNode<T>* getSubTreeRoot();
+
+    /// \brief returns the root of its subtree (const variant)
     const DomainTreeNode<T>* getSubTreeRoot() const;
 
 public:
@@ -409,6 +433,10 @@ public:
     /// (which should be absolute), it will return \c NULL.
     ///
     /// This method never throws an exception.
+    DomainTreeNode<T>* getUpperNode();
+
+    /// \brief returns the parent of the root of its subtree (const
+    /// variant)
     const DomainTreeNode<T>* getUpperNode() const;
 
     /// \brief return the next node which is bigger than current node
@@ -426,6 +454,10 @@ public:
     /// returns \c NULL.
     ///
     /// This method never throws an exception.
+    DomainTreeNode<T>* successor();
+
+    /// \brief return the next node which is bigger than current node
+    /// in the same subtree (const variant)
     const DomainTreeNode<T>* successor() const;
 
     /// \brief return the next node which is smaller than current node
@@ -443,6 +475,10 @@ public:
     /// returns \c NULL.
     ///
     /// This method never throws an exception.
+    DomainTreeNode<T>* predecessor();
+
+    /// \brief return the next node which is smaller than current node
+    /// in the same subtree (const variant)
     const DomainTreeNode<T>* predecessor() const;
 
     /// \brief returns the node distance from the root of its subtree
@@ -472,12 +508,13 @@ private:
     /// The overhead of the member pointers should be optimised out, as this
     /// will probably get completely inlined into predecessor and successor
     /// methods.
-    const DomainTreeNode<T>*
-        abstractSuccessor(typename DomainTreeNode<T>::DomainTreeNodePtr
+    template <typename TT>
+    static TT*
+    abstractSuccessor(TT* node,
+                      typename DomainTreeNode<T>::DomainTreeNodePtr
                           DomainTreeNode<T>::*left,
-                          typename DomainTreeNode<T>::DomainTreeNodePtr
-                          DomainTreeNode<T>::*right)
-        const;
+                      typename DomainTreeNode<T>::DomainTreeNodePtr
+                          DomainTreeNode<T>::*right);
 
     /// \name Data to maintain the rbtree structure.
     ///
@@ -530,6 +567,24 @@ private:
         }
     }
 
+    /// \brief Access sibling node as bare pointer.
+    ///
+    /// A sibling node is defined as the parent's other child. It exists
+    /// at the same level as child. Note that child can be NULL here,
+    /// which is why this is a static function.
+    ///
+    /// \return the sibling node if one exists, NULL otherwise.
+    static DomainTreeNode<T>* getSibling(DomainTreeNode<T>* parent,
+                                         DomainTreeNode<T>* child)
+    {
+        if (!parent) {
+            return (NULL);
+        }
+
+        return ((parent->getLeft() == child) ?
+                parent->getRight() : parent->getLeft());
+    }
+
     /// \brief Access uncle node as bare pointer.
     ///
     /// An uncle node is defined as the parent node's sibling. It exists
@@ -570,6 +625,96 @@ private:
         return (down_.get());
     }
 
+    /// \brief Helper method used in many places in code to set parent's
+    /// child links.
+    void connectChild(DomainTreeNode<T>* oldnode,
+                      DomainTreeNode<T>* newnode,
+                      DomainTreeNodePtr* root_ptr,
+                      DomainTreeNode<T>* thisnode = NULL)
+    {
+        if (!thisnode) {
+            thisnode = this;
+        }
+
+        if (getParent() != NULL) {
+            if (getParent()->getLeft() == oldnode) {
+                thisnode->getParent()->left_ = newnode;
+            } else if (getParent()->getRight() == oldnode) {
+                thisnode->getParent()->right_ = newnode;
+            } else {
+                thisnode->getParent()->down_ = newnode;
+            }
+        } else {
+            *root_ptr = newnode;
+        }
+    }
+
+    /// \brief Exchanges the location of two nodes (this and
+    /// lower). Their data remain the same, but their location in the
+    /// tree, colors and sub-tree root status may change. Note that this
+    /// is different from std::swap()-like behavior.
+    ///
+    /// IMPORTANT: A necessary pre-condition is that lower node must be
+    /// at a lower level in the tree than this node. This method is
+    /// primarily used in remove() and this pre-condition is followed
+    /// there.
+    ///
+    /// This method doesn't throw any exceptions.
+    void exchange(DomainTreeNode<T>* lower, DomainTreeNodePtr* root_ptr) {
+        // Swap the pointers first. down should not be swapped as it
+        // belongs to the node's data, and not to its position in the
+        // tree.
+
+        // NOTE: The conditions following the swaps below are
+        // asymmetric. We only need to check this for the lower node, as
+        // it can be a direct child of this node. The reverse is not
+        // possible.
+
+        std::swap(left_, lower->left_);
+        if (lower->getLeft() == lower) {
+            lower->left_ = this;
+        }
+
+        std::swap(right_, lower->right_);
+        if (lower->getRight() == lower) {
+            lower->right_ = this;
+        }
+
+        std::swap(parent_, lower->parent_);
+        if (getParent() == this) {
+            parent_ = lower;
+        }
+
+        // Update FLAG_RED and FLAG_SUBTREE_ROOT as these two are
+        // associated with the node's position.
+        const DomainTreeNodeColor this_color = getColor();
+        const bool this_is_subtree_root = isSubTreeRoot();
+        const DomainTreeNodeColor lower_color = lower->getColor();
+        const bool lower_is_subtree_root = lower->isSubTreeRoot();
+
+        lower->setColor(this_color);
+        setColor(lower_color);
+
+        setSubTreeRoot(lower_is_subtree_root);
+        lower->setSubTreeRoot(this_is_subtree_root);
+
+        lower->connectChild(this, lower, root_ptr);
+
+        if (getParent()->getLeft() == lower) {
+            getParent()->left_ = this;
+        } else if (getParent()->getRight() == lower) {
+            getParent()->right_ = this;
+        }
+
+        if (lower->getRight()) {
+            lower->getRight()->parent_ = lower;
+        }
+
+        if (lower->getLeft()) {
+            lower->getLeft()->parent_ = lower;
+        }
+    }
+
     /// \brief Data stored here.
     boost::interprocess::offset_ptr<T> data_;
 
@@ -610,17 +755,36 @@ DomainTreeNode<T>::~DomainTreeNode() {
 }
 
 template <typename T>
-const DomainTreeNode<T>*
-DomainTreeNode<T>::getSubTreeRoot() const {
-    const DomainTreeNode<T>* current = this;
-
-    // current would never be equal to NULL here (in a correct tree
+template <typename TT>
+TT*
+DomainTreeNode<T>::getSubTreeRootImpl(TT* node) {
+    // node would never be equal to NULL here (in a correct tree
     // implementation)
-    while (!current->isSubTreeRoot()) {
-        current = current->getParent();
+    assert(node != NULL);
+
+    while (!node->isSubTreeRoot()) {
+        node = node->getParent();
     }
 
-    return (current);
+    return (node);
+}
+
+template <typename T>
+DomainTreeNode<T>*
+DomainTreeNode<T>::getSubTreeRoot() {
+    return (getSubTreeRootImpl<DomainTreeNode<T> >(this));
+}
+
+template <typename T>
+const DomainTreeNode<T>*
+DomainTreeNode<T>::getSubTreeRoot() const {
+    return (getSubTreeRootImpl<const DomainTreeNode<T> >(this));
+}
+
+template <typename T>
+DomainTreeNode<T>*
+DomainTreeNode<T>::getUpperNode() {
+    return (getSubTreeRoot()->getParent());
 }
 
 template <typename T>
@@ -655,40 +819,39 @@ DomainTreeNode<T>::getAbsoluteLabels(
 }
 
 template <typename T>
-const DomainTreeNode<T>*
-DomainTreeNode<T>::abstractSuccessor(
+template <typename TT>
+TT*
+DomainTreeNode<T>::abstractSuccessor(TT* node,
     typename DomainTreeNode<T>::DomainTreeNodePtr DomainTreeNode<T>::*left,
     typename DomainTreeNode<T>::DomainTreeNodePtr DomainTreeNode<T>::*right)
-    const
 {
     // This function is written as a successor. It becomes predecessor if
     // the left and right pointers are swapped. So in case of predecessor,
     // the left pointer points to right and vice versa. Don't get confused
     // by the idea, just imagine the pointers look into a mirror.
 
-    const DomainTreeNode<T>* current = this;
     // If it has right node, the successor is the left-most node of the right
     // subtree.
-    if ((current->*right).get() != NULL) {
-        current = (current->*right).get();
+    if ((node->*right).get() != NULL) {
+        node = (node->*right).get();
         const DomainTreeNode<T>* left_n;
-        while ((left_n = (current->*left).get()) != NULL) {
-            current = left_n;
+        while ((left_n = (node->*left).get()) != NULL) {
+            node = left_n;
         }
-        return (current);
+        return (node);
     }
 
     // Otherwise go up until we find the first left branch on our path to
     // root.  If found, the parent of the branch is the successor.
     // Otherwise, we return the null node
-    const DomainTreeNode<T>* parent = current->getParent();
-    while ((!current->isSubTreeRoot()) &&
-           (current == (parent->*right).get())) {
-        current = parent;
+    const DomainTreeNode<T>* parent = node->getParent();
+    while ((!node->isSubTreeRoot()) &&
+           (node == (parent->*right).get())) {
+        node = parent;
         parent = parent->getParent();
     }
 
-    if (!current->isSubTreeRoot()) {
+    if (!node->isSubTreeRoot()) {
         return (parent);
     } else {
         return (NULL);
@@ -696,18 +859,33 @@ DomainTreeNode<T>::abstractSuccessor(
 }
 
 template <typename T>
+DomainTreeNode<T>*
+DomainTreeNode<T>::successor() {
+    return (abstractSuccessor<DomainTreeNode<T> >
+            (this, &DomainTreeNode<T>::left_, &DomainTreeNode<T>::right_));
+}
+
+template <typename T>
 const DomainTreeNode<T>*
 DomainTreeNode<T>::successor() const {
-    return (abstractSuccessor(&DomainTreeNode<T>::left_,
-                              &DomainTreeNode<T>::right_));
+    return (abstractSuccessor<const DomainTreeNode<T> >
+            (this, &DomainTreeNode<T>::left_, &DomainTreeNode<T>::right_));
+}
+
+template <typename T>
+DomainTreeNode<T>*
+DomainTreeNode<T>::predecessor() {
+    // Swap the left and right pointers for the abstractSuccessor
+    return (abstractSuccessor<DomainTreeNode<T> >
+            (this, &DomainTreeNode<T>::right_, &DomainTreeNode<T>::left_));
 }
 
 template <typename T>
 const DomainTreeNode<T>*
 DomainTreeNode<T>::predecessor() const {
     // Swap the left and right pointers for the abstractSuccessor
-    return (abstractSuccessor(&DomainTreeNode<T>::right_,
-                              &DomainTreeNode<T>::left_));
+    return (abstractSuccessor<const DomainTreeNode<T> >
+            (this, &DomainTreeNode<T>::right_, &DomainTreeNode<T>::left_));
 }
 
 /// \brief DomainTreeNodeChain stores detailed information of \c
@@ -1072,7 +1250,7 @@ public:
                         DomainTree<T>* tree,
                         DataDeleter deleter)
     {
-        tree->deleteAllNodes(mem_sgmt, deleter);
+        tree->removeAllNodes(mem_sgmt, deleter);
         tree->~DomainTree<T>();
         mem_sgmt.deallocate(tree, sizeof(DomainTree<T>));
     }
@@ -1148,45 +1326,19 @@ public:
     ///    of it. In that case, node parameter is left intact.
     //@{
 
-    /// \brief Simple find
-    ///
-    /// Acts as described in the \ref find section.
-    Result find(const isc::dns::Name& name,
-                const DomainTreeNode<T>** node) const {
-        DomainTreeNodeChain<T> node_path;
-        const isc::dns::LabelSequence ls(name);
-        Result ret = (find<void*>(ls, node, node_path, NULL, NULL));
-        return (ret);
-    }
-
-    /// \brief Simple find, with node_path tracking
-    ///
-    /// Acts as described in the \ref find section.
-    Result find(const isc::dns::Name& name, const DomainTreeNode<T>** node,
-                DomainTreeNodeChain<T>& node_path) const
-    {
-        const isc::dns::LabelSequence ls(name);
-        Result ret = (find<void*>(ls, node, node_path, NULL, NULL));
-        return (ret);
-    }
-
-    /// \brief Simple find returning immutable node.
-    ///
-    /// Acts as described in the \ref find section, but returns immutable
-    /// node pointer.
-    template <typename CBARG>
-    Result find(const isc::dns::Name& name,
-                const DomainTreeNode<T>** node,
-                DomainTreeNodeChain<T>& node_path,
-                bool (*callback)(const DomainTreeNode<T>&, CBARG),
-                CBARG callback_arg) const
-    {
-        const isc::dns::LabelSequence ls(name);
-        Result ret = find(ls, node, node_path, callback,
-                          callback_arg);
-        return (ret);
-    }
+private:
+    /// \brief Static helper function used by const and non-const
+    /// variants of find() below
+    template <typename TT, typename TTN, typename CBARG>
+    static Result findImpl(TT* tree,
+                           const isc::dns::LabelSequence& target_labels_orig,
+                           TTN** target,
+                           TTN* node,
+                           DomainTreeNodeChain<T>& node_path,
+                           bool (*callback)(const DomainTreeNode<T>&, CBARG),
+                           CBARG callback_arg);
 
+public:
     /// \brief Find with callback and node chain
     /// \anchor callback
     ///
@@ -1263,10 +1415,79 @@ public:
     ///     \c true, it returns immediately with the current node.
     template <typename CBARG>
     Result find(const isc::dns::LabelSequence& target_labels_orig,
+                DomainTreeNode<T>** node,
+                DomainTreeNodeChain<T>& node_path,
+                bool (*callback)(const DomainTreeNode<T>&, CBARG),
+                CBARG callback_arg);
+
+    /// \brief Find with callback and node chain (const variant)
+    template <typename CBARG>
+    Result find(const isc::dns::LabelSequence& target_labels_orig,
                 const DomainTreeNode<T>** node,
                 DomainTreeNodeChain<T>& node_path,
                 bool (*callback)(const DomainTreeNode<T>&, CBARG),
                 CBARG callback_arg) const;
+
+    /// \brief Simple find
+    ///
+    /// Acts as described in the \ref find section.
+    Result find(const isc::dns::Name& name,
+                DomainTreeNode<T>** node) {
+        const isc::dns::LabelSequence ls(name);
+        DomainTreeNodeChain<T> node_path;
+        return (find<void*>(ls, node, node_path, NULL, NULL));
+    }
+
+    /// \brief Simple find (const variant)
+    Result find(const isc::dns::Name& name,
+                const DomainTreeNode<T>** node) const {
+        const isc::dns::LabelSequence ls(name);
+        DomainTreeNodeChain<T> node_path;
+        return (find<void*>(ls, node, node_path, NULL, NULL));
+    }
+
+    /// \brief Simple find, with node_path tracking
+    ///
+    /// Acts as described in the \ref find section.
+    Result find(const isc::dns::Name& name, DomainTreeNode<T>** node,
+                DomainTreeNodeChain<T>& node_path)
+    {
+        const isc::dns::LabelSequence ls(name);
+        return (find<void*>(ls, node, node_path, NULL, NULL));
+    }
+
+    /// \brief Simple find, with node_path tracking (const variant)
+    Result find(const isc::dns::Name& name, const DomainTreeNode<T>** node,
+                DomainTreeNodeChain<T>& node_path) const
+    {
+        const isc::dns::LabelSequence ls(name);
+        return (find<void*>(ls, node, node_path, NULL, NULL));
+    }
+
+    /// \brief Simple find with callback
+    template <typename CBARG>
+    Result find(const isc::dns::Name& name,
+                DomainTreeNode<T>** node,
+                DomainTreeNodeChain<T>& node_path,
+                bool (*callback)(const DomainTreeNode<T>&, CBARG),
+                CBARG callback_arg)
+    {
+        const isc::dns::LabelSequence ls(name);
+        return (find<CBARG>(ls, node, node_path, callback, callback_arg));
+    }
+
+    /// \brief Simple find with callback (const variant)
+    template <typename CBARG>
+    Result find(const isc::dns::Name& name,
+                const DomainTreeNode<T>** node,
+                DomainTreeNodeChain<T>& node_path,
+                bool (*callback)(const DomainTreeNode<T>&, CBARG),
+                CBARG callback_arg) const
+    {
+        const isc::dns::LabelSequence ls(name);
+        return (find<CBARG>(ls, node, node_path, callback, callback_arg));
+    }
+
     //@}
 
     /// \brief return the next bigger node in DNSSEC order from a given node
@@ -1320,12 +1541,24 @@ public:
     const DomainTreeNode<T>*
     previousNode(DomainTreeNodeChain<T>& node_path) const;
 
+private:
+    /// \brief Static helper function used by const and non-const
+    /// variants of largestNode()
+    template <typename TT, typename TTN>
+    static TTN*
+    largestNodeImpl(TT* node);
+
+public:
     /// \brief return the largest node in the tree of trees.
     ///
     /// \throw none
     ///
     /// \return A \c DomainTreeNode that is the largest node in the
     /// tree. If there are no nodes, then \c NULL is returned.
+    DomainTreeNode<T>* largestNode();
+
+    /// \brief return the largest node in the tree of trees (const
+    /// variant).
     const DomainTreeNode<T>* largestNode() const;
 
     /// \brief Get the total number of nodes in the tree
@@ -1335,6 +1568,39 @@ public:
     /// This function is mainly intended to be used for debugging.
     uint32_t getNodeCount() const { return (node_count_); }
 
+private:
+    /// \brief Helper method for getHeight()
+    size_t getHeightHelper(const DomainTreeNode<T>* node) const;
+
+public:
+    /// \brief Return the maximum height of sub-root nodes found in the
+    /// DomainTree forest.
+    ///
+    /// The height of a node is defined as the number of nodes in the
+    /// longest path from the node to a leaf. For each subtree in the
+    /// DomainTree forest, this method determines the height of its root
+    /// node. Then it returns the maximum such height in the forest.
+    ///
+    /// Note: This method exists for testing purposes. Non-test code
+    /// must not use it.
+    size_t getHeight() const;
+
+private:
+    /// \brief Helper method for checkProperties()
+    bool checkPropertiesHelper(const DomainTreeNode<T>* node) const;
+
+    /// \brief Helper for checkProperties()
+    bool checkBlackDistanceHelper(const DomainTreeNode<T>* node,
+                                  size_t* distance)
+        const;
+
+public:
+    /// \brief Check red-black properties of the DomainTree.
+    ///
+    /// Note: This method exists for testing purposes. Non-test code
+    /// must not use it.
+    bool checkProperties() const;
+
     /// \name Debug function
     //@{
     /// \brief Print the nodes in the trees.
@@ -1402,15 +1668,31 @@ public:
     Result insert(util::MemorySegment& mem_sgmt, const isc::dns::Name& name,
                   DomainTreeNode<T>** inserted_node);
 
+    /// \brief Delete a tree node.
+    ///
+    /// \throw none.
+    ///
+    /// \param mem_sgmt The \c MemorySegment object used to insert the nodes
+    /// (which was also used for creating the tree due to the requirement of
+    /// \c insert()).
+    /// \param node The node to delete.
+    /// \param deleter The \c DataDeleter used to destroy data stored in
+    /// the tree nodes.
+    template <typename DataDeleter>
+    void remove(util::MemorySegment& mem_sgmt, DomainTreeNode<T>* node,
+                DataDeleter deleter);
+
     /// \brief Delete all tree nodes.
     ///
     /// \throw none.
     ///
     /// \param mem_sgmt The \c MemorySegment object used to insert the nodes
     /// (which was also used for creating the tree due to the requirement of
-    /// \c inert()).
+    /// \c insert()).
+    /// \param deleter The \c DataDeleter used to destroy data stored in
+    /// the tree nodes.
     template <typename DataDeleter>
-    void deleteAllNodes(util::MemorySegment& mem_sgmt, DataDeleter deleter);
+    void removeAllNodes(util::MemorySegment& mem_sgmt, DataDeleter deleter);
 
     /// \brief Swaps two tree's contents.
     ///
@@ -1433,6 +1715,10 @@ private:
     insertRebalance(typename DomainTreeNode<T>::DomainTreeNodePtr* root,
                     DomainTreeNode<T>* node);
 
+    void
+    removeRebalance(typename DomainTreeNode<T>::DomainTreeNodePtr* root_ptr,
+                    DomainTreeNode<T>* child, DomainTreeNode<T>* parent);
+
     DomainTreeNode<T>*
     rightRotate(typename DomainTreeNode<T>::DomainTreeNodePtr* root,
                 DomainTreeNode<T>* node);
@@ -1471,6 +1757,7 @@ private:
     void nodeFission(util::MemorySegment& mem_sgmt, DomainTreeNode<T>& node,
                      const isc::dns::LabelSequence& new_prefix,
                      const isc::dns::LabelSequence& new_suffix);
+
     //@}
 
     typename DomainTreeNode<T>::DomainTreeNodePtr root_;
@@ -1535,13 +1822,15 @@ DomainTree<T>::deleteHelper(util::MemorySegment& mem_sgmt,
 }
 
 template <typename T>
-template <typename CBARG>
+template <typename TT, typename TTN, typename CBARG>
 typename DomainTree<T>::Result
-DomainTree<T>::find(const isc::dns::LabelSequence& target_labels_orig,
-                    const DomainTreeNode<T>** target,
-                    DomainTreeNodeChain<T>& node_path,
-                    bool (*callback)(const DomainTreeNode<T>&, CBARG),
-                    CBARG callback_arg) const
+DomainTree<T>::findImpl(TT* tree,
+                        const isc::dns::LabelSequence& target_labels_orig,
+                        TTN** target,
+                        TTN* node,
+                        DomainTreeNodeChain<T>& node_path,
+                        bool (*callback)(const DomainTreeNode<T>&, CBARG),
+                        CBARG callback_arg)
 {
     if (node_path.isEmpty() ^ target_labels_orig.isAbsolute()) {
         isc_throw(isc::BadValue,
@@ -1549,17 +1838,6 @@ DomainTree<T>::find(const isc::dns::LabelSequence& target_labels_orig,
                   " and label sequence");
     }
 
-    const DomainTreeNode<T>* node;
-
-    if (!node_path.isEmpty()) {
-        // Get the top node in the node chain
-        node = node_path.top();
-        // Start searching from its down pointer
-        node = node->getDown();
-    } else {
-        node = root_.get();
-    }
-
     Result ret = NOTFOUND;
     dns::LabelSequence target_labels(target_labels_orig);
 
@@ -1570,7 +1848,7 @@ DomainTree<T>::find(const isc::dns::LabelSequence& target_labels_orig,
             node_path.last_comparison_.getRelation();
 
         if (relation == isc::dns::NameComparisonResult::EQUAL) {
-            if (needsReturnEmptyNode_ || !node->isEmpty()) {
+            if (tree->needsReturnEmptyNode_ || !node->isEmpty()) {
                 node_path.push(node);
                 *target = node;
                 ret = EXACTMATCH;
@@ -1583,7 +1861,7 @@ DomainTree<T>::find(const isc::dns::LabelSequence& target_labels_orig,
                 node->getLeft() : node->getRight();
         } else {
             if (relation == isc::dns::NameComparisonResult::SUBDOMAIN) {
-                if (needsReturnEmptyNode_ || !node->isEmpty()) {
+                if (tree->needsReturnEmptyNode_ || !node->isEmpty()) {
                     ret = PARTIALMATCH;
                     *target = node;
                     if (callback != NULL &&
@@ -1607,6 +1885,53 @@ DomainTree<T>::find(const isc::dns::LabelSequence& target_labels_orig,
 }
 
 template <typename T>
+template <typename CBARG>
+typename DomainTree<T>::Result
+DomainTree<T>::find(const isc::dns::LabelSequence& target_labels_orig,
+                    DomainTreeNode<T>** target,
+                    DomainTreeNodeChain<T>& node_path,
+                    bool (*callback)(const DomainTreeNode<T>&, CBARG),
+                    CBARG callback_arg)
+{
+    if (!node_path.isEmpty()) {
+        isc_throw(isc::BadValue,
+                  "DomainTree::find() non-const method is given "
+                  "non-empty node chain");
+    }
+
+    DomainTreeNode<T>* node = root_.get();
+
+    return (findImpl<DomainTree<T>, DomainTreeNode<T>, CBARG >
+            (this, target_labels_orig, target, node, node_path,
+             callback, callback_arg));
+}
+
+template <typename T>
+template <typename CBARG>
+typename DomainTree<T>::Result
+DomainTree<T>::find(const isc::dns::LabelSequence& target_labels_orig,
+                    const DomainTreeNode<T>** target,
+                    DomainTreeNodeChain<T>& node_path,
+                    bool (*callback)(const DomainTreeNode<T>&, CBARG),
+                    CBARG callback_arg) const
+{
+    const DomainTreeNode<T>* node;
+
+    if (!node_path.isEmpty()) {
+        // Get the top node in the node chain
+        node = node_path.top();
+        // Start searching from its down pointer
+        node = node->getDown();
+    } else {
+        node = root_.get();
+    }
+
+    return (findImpl<const DomainTree<T>, const DomainTreeNode<T>, CBARG >
+            (this, target_labels_orig, target, node, node_path,
+             callback, callback_arg));
+}
+
+template <typename T>
 const DomainTreeNode<T>*
 DomainTree<T>::nextNode(DomainTreeNodeChain<T>& node_path) const {
     if (node_path.isEmpty()) {
@@ -1789,9 +2114,10 @@ DomainTree<T>::previousNode(DomainTreeNodeChain<T>& node_path) const {
 }
 
 template <typename T>
-const DomainTreeNode<T>*
-DomainTree<T>::largestNode() const {
-    const DomainTreeNode<T>* node = root_.get();
+template <typename TT, typename TTN>
+TTN*
+DomainTree<T>::largestNodeImpl(TT* tree) {
+    TTN* node = tree->root_.get();
     while (node != NULL) {
         // We go right first, then down.
         if (node->getRight() != NULL) {
@@ -1807,6 +2133,19 @@ DomainTree<T>::largestNode() const {
 }
 
 template <typename T>
+DomainTreeNode<T>*
+DomainTree<T>::largestNode() {
+    return (largestNodeImpl<DomainTree<T>, DomainTreeNode<T> >(this));
+}
+
+template <typename T>
+const DomainTreeNode<T>*
+DomainTree<T>::largestNode() const {
+    return (largestNodeImpl<const DomainTree<T>, const DomainTreeNode<T> >
+            (this));
+}
+
+template <typename T>
 typename DomainTree<T>::Result
 DomainTree<T>::insert(util::MemorySegment& mem_sgmt,
                       const isc::dns::Name& target_name,
@@ -1892,7 +2231,127 @@ DomainTree<T>::insert(util::MemorySegment& mem_sgmt,
 template <typename T>
 template <typename DataDeleter>
 void
-DomainTree<T>::deleteAllNodes(util::MemorySegment& mem_sgmt,
+DomainTree<T>::remove(util::MemorySegment& mem_sgmt, DomainTreeNode<T>* node,
+                      DataDeleter deleter)
+{
+    // If node has a down pointer, we cannot remove this node from the
+    // DomainTree forest. We merely clear its data (destroying the data)
+    // and return.
+    if (node->getDown()) {
+        T* data = node->setData(NULL);
+        if (data) {
+            deleter(data);
+        }
+        return;
+    }
+
+    while (true) {
+        // Save subtree root's parent for use later.
+        DomainTreeNode<T>* upper_node = node->getUpperNode();
+
+        // node points to the node to be deleted in the BST. It first
+        // has to be exchanged with the right-most node in the left
+        // sub-tree or the left-most node in the right sub-tree. (Here,
+        // sub-tree is inside this RB tree itself, not in the
+        // tree-of-trees forest.) The node then ends up having a maximum
+        // of 1 child. Note that this is not an in-place value swap of
+        // node data, but the actual node locations are swapped in
+        // exchange(). Unlike normal BSTs, we have to do this as our
+        // label data is at address (this + 1).
+        if (node->getLeft() && node->getRight()) {
+            DomainTreeNode<T>* rightmost = node->getLeft();
+            while (rightmost->getRight() != NULL) {
+                rightmost = rightmost->getRight();
+            }
+
+            node->exchange(rightmost, &root_);
+        }
+
+        // Now, node has 0 or 1 children, as from above, either its
+        // right or left child definitely doesn't exist (and is NULL).
+        // Pick the child node, or if no children exist, just use NULL.
+        DomainTreeNode<T>* child;
+        if (node->getRight()) {
+            child = node->getRight();
+        } else {
+            child = node->getLeft();
+        }
+
+        // Set it as the node's parent's child, effectively removing
+        // node from the tree.
+        node->connectChild(node, child, &root_);
+
+        // Child can be NULL here if node was a leaf.
+        if (child) {
+            child->parent_ = node->getParent();
+            // Even if node is not a leaf node, we don't always do an
+            // exchange() with another node, so we have to set the
+            // child's FLAG_SUBTREE_ROOT explicitly.
+            if ((!child->getParent()) ||
+                (child->getParent()->getDown() == child))
+            {
+                child->setSubTreeRoot(node->isSubTreeRoot());
+            }
+        }
+
+        // If node is RED, it is a valid red-black tree already as
+        // (node's) child must be BLACK or NULL (which is
+        // BLACK). Deleting (the RED) node will not have any effect on
+        // the number of BLACK nodes through this path (involving node's
+        // parent and its new child). In this case, we can skip the
+        // following block.
+        if (node->isBlack()) {
+            if (child && child->isRed()) {
+                // If node is BLACK and child is RED, removing node
+                // would decrease the number of BLACK nodes through this
+                // path (involving node's parent and its new child). So
+                // we color child to be BLACK to restore the old count
+                // of black nodes through this path. It is now a valid
+                // red-black tree.
+                child->setColor(DomainTreeNode<T>::BLACK);
+            } else {
+                // If node is BLACK and child is also BLACK or NULL
+                // (which is BLACK), we need to do re-balancing to make
+                // it a valid red-black tree again.
+                typename DomainTreeNode<T>::DomainTreeNodePtr* root_ptr =
+                    upper_node ? &(upper_node->down_) : &root_;
+                removeRebalance(root_ptr, child, node->getParent());
+            }
+        }
+
+        // Finally, destroy the node.
+        if (node->data_.get()) {
+            deleter(node->data_.get());
+        }
+        DomainTreeNode<T>::destroy(mem_sgmt, node);
+        --node_count_;
+
+        // If the node deletion did not cause the subtree to disappear
+        // completely, return early.
+        if (upper_node->getDown()) {
+            break;
+        }
+
+        // If the upper node is not empty, it cannot be deleted.
+        if (!upper_node->isEmpty()) {
+            break;
+        }
+
+        // If upper node is the root node (.), don't attempt to delete
+        // it. The root node must always exist.
+        if (upper_node == root_.get()) {
+            break;
+        }
+
+        // Ascend up the tree and delete the upper node.
+        node = upper_node;
+    }
+}
+
+template <typename T>
+template <typename DataDeleter>
+void
+DomainTree<T>::removeAllNodes(util::MemorySegment& mem_sgmt,
                               DataDeleter deleter)
 {
     deleteHelper(mem_sgmt, root_.get(), deleter);
@@ -1916,17 +2375,8 @@ DomainTree<T>::nodeFission(util::MemorySegment& mem_sgmt,
     node.resetLabels(new_prefix);
 
     up_node->parent_ = node.getParent();
-    if (node.getParent() != NULL) {
-        if (node.getParent()->getLeft() == &node) {
-            node.getParent()->left_ = up_node;
-        } else if (node.getParent()->getRight() == &node) {
-            node.getParent()->right_ = up_node;
-        } else {
-            node.getParent()->down_ = up_node;
-        }
-    } else {
-        root_ = up_node;
-    }
+
+    node.connectChild(&node, up_node, &root_);
 
     up_node->down_ = &node;
     node.parent_ = up_node;
@@ -2095,6 +2545,283 @@ DomainTree<T>::insertRebalance
     (*subtree_root)->setColor(DomainTreeNode<T>::BLACK);
 }
 
+template <typename T>
+void
+DomainTree<T>::removeRebalance
+    (typename DomainTreeNode<T>::DomainTreeNodePtr* root_ptr,
+     DomainTreeNode<T>* child,  DomainTreeNode<T>* parent)
+{
+    // Case 1. Repeat until we reach the root node of this subtree in
+    // the forest. Note that child can be NULL here, so we can only test
+    // the parent pointer and see if it has escaped to the upper tree.
+    while (&(parent->down_) != root_ptr) {
+        // A sibling node is defined as the parent's other child. It
+        // exists at the same level as child. Note that child can be
+        // NULL here.
+        DomainTreeNode<T>* sibling =
+            DomainTreeNode<T>::getSibling(parent, child);
+
+        // NOTE #1: Understand this clearly. We are here only because in
+        // the path through parent--child, a BLACK node was removed,
+        // i.e., the sibling's side in the path through parent--sibling
+        // is heavier by 1 extra BLACK node in its path. Because this
+        // can be an iterative process up the tree, the key is to
+        // understand this point when entering the block here.
+
+        // NOTE #2: sibling cannot be NULL here as parent--child has
+        // fewer BLACK nodes than parent--sibling.
+        assert(sibling);
+
+        // If sibling is RED, convert the tree to a form where sibling
+        // is BLACK.
+        if (sibling->isRed()) {
+            // Case 2. Here, the sibling is RED. We do a tree rotation
+            // at the parent such that sibling is the new parent, and
+            // the old parent is sibling's child. We also invert the
+            // colors of the two nodes.
+            //
+            // This step is done to convert the tree to a form for
+            // further cases below.
+
+            /* Parent (P) has to be BLACK here as its child sibling (S)
+             * is RED.
+             *
+             *       P(B)                   S(B)
+             *      /   \                  /   \
+             *    C(?)   S(R)     =>     P(R)  y(B)
+             *    /  \   /  \            /  \
+             *          x(B) y(B)     C(?)  x(B)
+             *                       /   \
+             */
+
+            parent->setColor(DomainTreeNode<T>::RED);
+            sibling->setColor(DomainTreeNode<T>::BLACK);
+
+            if (parent->getLeft() == child) {
+                leftRotate(root_ptr, parent);
+            } else {
+                rightRotate(root_ptr, parent);
+            }
+
+            // Re-compute child's sibling due to the tree adjustment
+            // above.
+            sibling = DomainTreeNode<T>::getSibling(parent, child);
+        }
+
+        // NOTE #3: sibling still cannot be NULL here as parent--child
+        // has fewer BLACK nodes than parent--sibling.
+        assert(sibling);
+
+        // NOTE #4: From above, sibling must be BLACK here.
+        assert(sibling->isBlack());
+
+        // NOTE #5: If a tree rotation happened above, the new sibling's
+        // side through parent--sibling [x(B)] above is still heavier
+        // than parent--child by 1 extra BLACK node in its path.
+
+        // Case 3. If both of sibling's children are BLACK, then set the
+        // sibling's color to RED. This reduces the number of BLACK
+        // nodes in parent--sibling path by 1 and balances the BLACK
+        // nodes count on both sides of parent. But this introduces
+        // another issue which is that the path through one child
+        // (=parent) of parent's parent (child's grandparent) has fewer
+        // BLACK nodes now than the other child (parent's sibling).
+        //
+        // To fix this: (a) if parent is colored RED, we can change its
+        // color to BLACK (to increment the number of black nodes in
+        // grandparent--parent-->path) and we're done with the
+        // rebalancing; (b) if parent is colored BLACK, then we set
+        // child=parent and go back to the beginning of the loop to
+        // repeat the original rebalancing problem 1 node higher up the
+        // tree (see NOTE #1 above).
+
+        /* (a):
+         *
+         *         G(?)                   G(?)
+         *         /  \                   /  \
+         *       P(R)        =>         P(B)      (Rebalancing is complete)
+         *      /   \                  /   \
+         *   C(?)    S(B)           C(?)    S(R)
+         *   / \     /   \           / \    /   \
+         *        ss1(B)  ss2(B)         ss1(B)  ss2(B)
+         *
+         *
+         * (b):
+         *
+         *         G(?)                   G(?) <----------(New parent)
+         *         /   \                  /   \
+         *       P(B)        =>         P(B) <------------(New child)
+         *      /   \                  /   \
+         *   C(?)    S(B)           C(?)    S(R)
+         *   / \     /   \           / \    /   \
+         *        ss1(B)  ss2(B)         ss1(B)  ss2(B)
+         */
+
+        if ((DomainTreeNode<T>::isBlack(sibling->getLeft()) &&
+             DomainTreeNode<T>::isBlack(sibling->getRight())))
+        {
+            sibling->setColor(DomainTreeNode<T>::RED);
+
+            if (parent->isBlack()) {
+                child = parent;
+                parent = parent->getParent();
+                continue;
+            } else {
+                parent->setColor(DomainTreeNode<T>::BLACK);
+                break;
+            }
+        }
+
+        // NOTE #3 and NOTE #4 asserted above still hold here.
+        assert(sibling);
+        assert(sibling->isBlack());
+
+        // NOTE #6: The path through parent--sibling is still heavier
+        // than parent--child by 1 extra BLACK node in its path. This is
+        // the key point, and this is why we are still doing the
+        // rebalancing.
+
+        // Case 4. Now, one or both of sibling's children are not
+        // BLACK. (a) We consider the case where child is the left-child
+        // of parent, and the left-child of sibling is RED and the
+        // right-child of sibling is BLACK. (b) We also consider its
+        // mirror, arrangement, i.e., the case where child is the
+        // right-child of parent, and the right-child of sibling is RED
+        // and the left-child of sibling is BLACK.
+        //
+        // In both cases, we change sibling's color to RED, the color of
+        // the RED child of sibling to BLACK (so both children of
+        // sibling are BLACK), and we do a tree rotation around sibling
+        // node in the opposite direction of the old RED child of
+        // sibling.
+        //
+        // This step is done to convert the tree to a form for further
+        // cases below.
+
+        /* (a):
+         *
+         *       P(?)        =>         P(?)
+         *      /   \                  /   \
+         *   C(?)    S(B)           C(?)    ss1(B)
+         *   / \     /   \           / \    /    \
+         *        ss1(R)  ss2(B)          x(B)   S(R)
+         *        /  \                           /  \
+         *      x(B) y(B)                     y(B)   ss2(B)
+         *
+         *
+         * (b):
+         *
+         *           P(?)        =>          P(?)
+         *          /    \                  /    \
+         *       S(B)     C(?)          ss1(B)   C(?)
+         *       /  \      / \           /  \     / \
+         *  ss2(B) ss1(R)             S(R)  x(B)
+         *          /  \              /  \
+         *        y(B) x(B)       ss2(B) y(B)
+         */
+
+        DomainTreeNode<T>* ss1 = sibling->getLeft();
+        DomainTreeNode<T>* ss2 = sibling->getRight();
+        if (parent->getLeft() != child) {
+            // Swap for the mirror arrangement described in case 4 (b)
+            // above.
+            std::swap(ss1, ss2);
+        }
+
+        if (DomainTreeNode<T>::isBlack(ss2)) {
+            // It is implied by ss2 being BLACK that ss1 is RED.
+
+            sibling->setColor(DomainTreeNode<T>::RED);
+            // ss1 cannot be NULL here as it is a RED node.
+            ss1->setColor(DomainTreeNode<T>::BLACK);
+
+            if (parent->getLeft() == child) {
+                rightRotate(root_ptr, sibling);
+            } else {
+                leftRotate(root_ptr, sibling);
+            }
+            // Re-compute child's sibling due to the tree adjustment
+            // above.
+            sibling = DomainTreeNode<T>::getSibling(parent, child);
+        }
+
+        // NOTE #7: sibling cannot be NULL here as even if the sibling
+        // variable was assigned in the node above, it was set to ss1
+        // which was a RED node before (non-NULL).
+        assert(sibling);
+
+        // NOTE #8: sibling is still BLACK, even if the sibling variable
+        // was assigned in the node above, it was set to ss1 which is
+        // now a BLACK node.
+        assert(sibling->isBlack());
+
+        // NOTE #9: The path through parent--sibling is still heavier
+        // than parent--child by 1 extra BLACK node in its path. This is
+        // the key point, and this is why we are still doing the
+        // rebalancing.
+
+        // Case 5. After case 4 above, we are in a canonical form now
+        // where sibling is BLACK, and either (a) if child is the
+        // left-child of parent, the right-child (ss2) of sibling is
+        // definitely RED, or (b) if child is the right-child of parent,
+        // the left-child (ss2) of sibling is definitely RED.
+        //
+        // Here, we set sibling's color to that of the parent, and set
+        // the parent's color to BLACK. We set ss2's color to BLACK (it
+        // was previously RED). Then we do a tree-rotation around parent
+        // in the direction of child to pull in the BLACK parent into
+        // its sub-tree. These steps effectively balances the tree as
+        // you can see from the graph below. Before starting, the graph
+        // on the child's side is lighter by 1 BLACK node than the graph
+        // on the sibling's side. After these steps, both sides of S(x)
+        // have the same number of BLACK nodes in any path through it.
+
+        /* (a):
+         *
+         *          P(x)                            S(x)
+         *         /    \                         /     \
+         *      C(?)     S(B)         =>      P(B)      ss2(B)
+         *       / \    /    \               /   \        /  \
+         *            ss1(?) ss2(R)       C(?) ss1(?)   (B)  (B)
+         *                    /  \        / \
+         *                  (B)  (B)
+         *
+         *     (Here, 'x' is the parent's color. In the resulting tree,
+         *      it is set as S node's color.)
+         *
+         * (b):
+         *      This is just the mirror of above.
+         */
+
+        sibling->setColor(parent->getColor());
+        parent->setColor(DomainTreeNode<T>::BLACK);
+
+        ss1 = sibling->getLeft();
+        ss2 = sibling->getRight();
+        if (parent->getLeft() != child) {
+            // Swap for the mirror arrangement described in case 4 (b)
+            // above.
+            std::swap(ss1, ss2);
+        }
+
+        // ss2 cannot be NULL here as it is a RED node.
+        ss2->setColor(DomainTreeNode<T>::BLACK);
+
+        if (parent->getLeft() == child) {
+            leftRotate(root_ptr, parent);
+        } else {
+            rightRotate(root_ptr, parent);
+        }
+
+        // NOTE #10: Now, sibling is parent's parent. All paths through
+        // sibling have the same number of BLACK nodes.
+
+        // We are completely finished and the tree is now
+        // balanced. Phew. Now let's take a coffee-break.
+
+        break;
+    }
+}
 
 template <typename T>
 DomainTreeNode<T>*
@@ -2164,6 +2891,112 @@ DomainTree<T>::rightRotate
     return (node);
 }
 
+template <typename T>
+size_t
+DomainTree<T>::getHeightHelper(const DomainTreeNode<T>* node) const {
+    if (node == NULL) {
+        return (0);
+    }
+
+    const size_t dl = getHeightHelper(node->getLeft());
+    const size_t dr = getHeightHelper(node->getRight());
+
+    const size_t this_height = std::max(dl + 1, dr + 1);
+    const size_t down_height = getHeightHelper(node->getDown());
+
+    return (std::max(this_height, down_height));
+}
+
+template <typename T>
+size_t
+DomainTree<T>::getHeight() const {
+    return (getHeightHelper(root_.get()));
+}
+
+template <typename T>
+bool
+DomainTree<T>::checkPropertiesHelper(const DomainTreeNode<T>* node) const {
+    if (node == NULL) {
+        return (true);
+    }
+
+    if (node->isRed()) {
+        // Root nodes must be BLACK.
+        if (node->isSubTreeRoot()) {
+            return (false);
+        }
+
+        // Both children of RED nodes must be BLACK.
+        if (DomainTreeNode<T>::isRed(node->getLeft()) ||
+            DomainTreeNode<T>::isRed(node->getRight()))
+        {
+            return (false);
+        }
+    }
+
+    // If node is assigned to the down_ pointer of its parent, it is a
+    // subtree root and must have the flag set.
+    if (((!node->getParent()) ||
+         (node->getParent()->getDown() == node)) &&
+        (!node->isSubTreeRoot()))
+    {
+        return (false);
+    }
+
+    // Repeat tests with this node's children.
+    return (checkPropertiesHelper(node->getLeft()) &&
+            checkPropertiesHelper(node->getRight()) &&
+            checkPropertiesHelper(node->getDown()));
+}
+
+template <typename T>
+bool
+DomainTree<T>::checkBlackDistanceHelper(const DomainTreeNode<T>* node,
+                                        size_t* distance) const
+{
+    if (node == NULL) {
+        *distance = 1;
+        return (true);
+    }
+
+    size_t dl, dr, dd;
+    if (!checkBlackDistanceHelper(node->getLeft(), &dl)) {
+        return (false);
+    }
+    if (!checkBlackDistanceHelper(node->getRight(), &dr)) {
+        return (false);
+    }
+    if (!checkBlackDistanceHelper(node->getDown(), &dd)) {
+        return (false);
+    }
+
+    if (dl != dr) {
+        return (false);
+    }
+
+    if (node->isBlack()) {
+        ++dl;
+    }
+
+    *distance = dl;
+
+    return (true);
+}
+
+template <typename T>
+bool
+DomainTree<T>::checkProperties() const {
+    if (!checkPropertiesHelper(root_.get())) {
+        return (false);
+    }
+
+    // Path from a given node to all its leaves must contain the same
+    // number of BLACK child nodes. This is done separately here instead
+    // of inside checkPropertiesHelper() as it would take (n log n)
+    // complexity otherwise.
+    size_t dd;
+    return (checkBlackDistanceHelper(root_.get(), &dd));
+}
 
 template <typename T>
 void
diff --git a/src/lib/datasrc/memory/rdataset.cc b/src/lib/datasrc/memory/rdataset.cc
index 7f37f51..b60316c 100644
--- a/src/lib/datasrc/memory/rdataset.cc
+++ b/src/lib/datasrc/memory/rdataset.cc
@@ -12,6 +12,9 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include "rdataset.h"
+#include "rdata_serialization.h"
+
 #include <exceptions/exceptions.h>
 
 #include <dns/rdata.h>
@@ -19,11 +22,10 @@
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
 #include <dns/rrset.h>
-
-#include "rdataset.h"
-#include "rdata_serialization.h"
+#include <util/buffer.h>
 
 #include <boost/static_assert.hpp>
+#include <boost/bind.hpp>
 
 #include <stdint.h>
 #include <algorithm>
@@ -74,12 +76,16 @@ lowestTTL(const RdataSet* rdataset, ConstRRsetPtr& rrset,
                 sig_rrset->getTTL());
     }
 }
-}
 
-RdataSet*
-RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
-                 ConstRRsetPtr rrset, ConstRRsetPtr sig_rrset,
-                 const RdataSet* old_rdataset)
+// Do some sanity checks on params of create and substract. Return the
+// target rrclass and rrtype (as they are produced as side effect of the
+// checks).
+//
+// The only reason for this function is to reuse common code of the
+// methods.
+std::pair<RRClass, RRType>
+sanityChecks(const ConstRRsetPtr& rrset, const ConstRRsetPtr &sig_rrset,
+             const RdataSet *old_rdataset)
 {
     // Check basic validity
     if (!rrset && !sig_rrset) {
@@ -91,6 +97,9 @@ RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
     if (sig_rrset && sig_rrset->getRdataCount() == 0) {
         isc_throw(BadValue, "Empty SIG RRset");
     }
+    if (sig_rrset && sig_rrset->getType() != RRType::RRSIG()) {
+        isc_throw(BadValue, "SIG RRset doesn't have type RRSIG");
+    }
     if (rrset && sig_rrset && rrset->getClass() != sig_rrset->getClass()) {
         isc_throw(BadValue, "RR class doesn't match between RRset and RRSIG");
     }
@@ -98,9 +107,45 @@ RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
     const RRClass rrclass = rrset ? rrset->getClass() : sig_rrset->getClass();
     const RRType rrtype = rrset ? rrset->getType() :
         getCoveredType(sig_rrset->getRdataIterator()->getCurrent());
+
     if (old_rdataset && old_rdataset->type != rrtype) {
-        isc_throw(BadValue, "RR type doesn't match for merging RdataSet");
+        isc_throw(BadValue, "RR type doesn't match between RdataSets");
     }
+
+    return (std::pair<RRClass, RRType>(rrclass, rrtype));
+}
+
+} // Anonymous namespace
+
+RdataSet*
+RdataSet::packSet(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
+                  size_t rdata_count, size_t rrsig_count, const RRType& rrtype,
+                  const RRTTL& rrttl)
+{
+    const size_t ext_rrsig_count_len =
+        rrsig_count >= MANY_RRSIG_COUNT ? sizeof(uint16_t) : 0;
+    const size_t data_len = encoder.getStorageLength();
+    void* p = mem_sgmt.allocate(sizeof(RdataSet) + ext_rrsig_count_len +
+                                data_len);
+    RdataSet* rdataset = new(p) RdataSet(rrtype, rdata_count, rrsig_count,
+                                         rrttl);
+    if (rrsig_count >= RdataSet::MANY_RRSIG_COUNT) {
+        *rdataset->getExtSIGCountBuf() = rrsig_count;
+    }
+    encoder.encode(rdataset->getDataBuf(), data_len);
+    return (rdataset);
+}
+
+RdataSet*
+RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
+                 ConstRRsetPtr rrset, ConstRRsetPtr sig_rrset,
+                 const RdataSet* old_rdataset)
+{
+    const std::pair<RRClass, RRType>& rrparams =
+        sanityChecks(rrset, sig_rrset, old_rdataset);
+    const RRClass& rrclass = rrparams.first;
+    const RRType& rrtype = rrparams.second;
+
     const RRTTL rrttl = lowestTTL(old_rdataset, rrset, sig_rrset);
     if (old_rdataset) {
         encoder.start(rrclass, rrtype, old_rdataset->getDataBuf(),
@@ -148,18 +193,99 @@ RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
                   << MAX_RRSIG_COUNT);
     }
 
-    const size_t ext_rrsig_count_len =
-        rrsig_count >= MANY_RRSIG_COUNT ? sizeof(uint16_t) : 0;
-    const size_t data_len = encoder.getStorageLength();
-    void* p = mem_sgmt.allocate(sizeof(RdataSet) + ext_rrsig_count_len +
-                                data_len);
-    RdataSet* rdataset = new(p) RdataSet(rrtype, rdata_count, rrsig_count,
-                                         rrttl);
-    if (rrsig_count >= MANY_RRSIG_COUNT) {
-        *rdataset->getExtSIGCountBuf() = rrsig_count;
+    return (packSet(mem_sgmt, encoder, rdata_count, rrsig_count, rrtype,
+                    rrttl));
+}
+
+namespace {
+
+void writeName(util::OutputBuffer* buffer, const LabelSequence& name,
+               RdataNameAttributes)
+{
+    size_t len;
+    const uint8_t* data = name.getData(&len);
+    buffer->writeData(data, len);
+}
+
+void writeData(util::OutputBuffer* buffer, const void* data, size_t len) {
+    buffer->writeData(data, len);
+}
+
+size_t subtractIterate(const dns::ConstRRsetPtr& subtract,
+                       const RRClass& rrclass, const RRType& rrtype,
+                       boost::function<bool ()> iterator,
+                       boost::function<void (const Rdata& rdata)> inserter,
+                       util::OutputBuffer& buffer)
+{
+    size_t count = 0;
+    while (iterator()) {
+        util::InputBuffer input(buffer.getData(), buffer.getLength());
+        const RdataPtr& rdata(createRdata(rrtype, rrclass, input,
+                                          buffer.getLength()));
+        buffer.clear();
+
+        bool insert = true;
+        if (subtract) {
+            for (RdataIteratorPtr it = subtract->getRdataIterator();
+                 !it->isLast(); it->next()) {
+                if (rdata->compare(it->getCurrent()) == 0) {
+                    insert = false;
+                    break;
+                }
+            }
+        }
+
+        if (insert) {
+            inserter(*rdata);
+            ++count;
+        }
     }
-    encoder.encode(rdataset->getDataBuf(), data_len);
-    return (rdataset);
+    return (count);
+}
+
+} // Anonymous namespace
+
+RdataSet*
+RdataSet::subtract(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
+                   const dns::ConstRRsetPtr& rrset,
+                   const dns::ConstRRsetPtr& sig_rrset,
+                   const RdataSet& old_rdataset)
+{
+    const std::pair<RRClass, RRType>& rrparams =
+        sanityChecks(rrset, sig_rrset, &old_rdataset);
+    const RRClass& rrclass = rrparams.first;
+    const RRType& rrtype = rrparams.second;
+
+    // Do the encoding
+    encoder.start(rrclass, rrtype);
+    util::OutputBuffer buffer(1024);
+    RdataReader reader(rrclass, rrtype, old_rdataset.getDataBuf(),
+                       old_rdataset.getRdataCount(),
+                       old_rdataset.getSigRdataCount(),
+                       boost::bind(writeName, &buffer, _1, _2),
+                       boost::bind(writeData, &buffer, _1, _2));
+
+    // Copy over the Rdata (except for the subtracted)
+    const size_t rdata_count =
+        subtractIterate(rrset, rrclass, rrtype,
+                        boost::bind(&RdataReader::iterateRdata, &reader),
+                        boost::bind(&RdataEncoder::addRdata, &encoder, _1),
+                        buffer);
+    // Copy over the signatures (except for the subtracted)
+    const size_t rrsig_count =
+        subtractIterate(sig_rrset, rrclass, RRType::RRSIG(),
+                        boost::bind(&RdataReader::iterateSingleSig, &reader),
+                        boost::bind(&RdataEncoder::addSIGRdata, &encoder, _1),
+                        buffer);
+
+    // Note that we don't need to check for overflow, if it fitted before, it
+    // fits after removal of something too.
+
+    if (rdata_count == 0 && rrsig_count == 0) {
+        return (NULL); // It is left empty
+    }
+    return (packSet(mem_sgmt, encoder, rdata_count, rrsig_count, rrtype,
+                    restoreTTL(old_rdataset.getTTLData())));
 }
 
 void
diff --git a/src/lib/datasrc/memory/rdataset.h b/src/lib/datasrc/memory/rdataset.h
index caef551..bd93781 100644
--- a/src/lib/datasrc/memory/rdataset.h
+++ b/src/lib/datasrc/memory/rdataset.h
@@ -207,6 +207,53 @@ public:
                             dns::ConstRRsetPtr sig_rrset,
                             const RdataSet* old_rdataset = NULL);
 
+    /// \brief Subtract some RDATAs and RRSIGs from an RdataSet
+    ///
+    /// Allocate and construct a new RdataSet that contains all the
+    /// data from the \c old_rdataset except for the ones in rrset
+    /// and sig_rrset.
+    ///
+    /// The interface is almost the same as with \c create, as well
+    /// as the restrictions and internal format. The most significant
+    /// difference in the interface is old_rdataset is mandatory here.
+    ///
+    /// This ignores RDATAs present in rrset and not in old_rdataset
+    /// (similarly for RRSIGs). If an RDATA in rrset and not in
+    /// old_rdataset is an error condition or needs other special
+    /// handling, it is up to the caller to check the old_rdataset
+    /// first.
+    ///
+    /// There'll be no memory leak on exception. However, the memory
+    /// allocated from the mem_sgmt may move when
+    /// \c util::MemorySegmentGrown is thrown. Note that it may happen
+    /// even when subtracting data from the old_rdataset, since a new
+    /// copy is being created.
+    ///
+    /// The old_rdataset is not destroyed and it is up to the caller to
+    /// manage its allocation.
+    ///
+    /// \throw util::MemorySegmentGrown The memory segment has grown, possibly
+    ///     relocating data.
+    /// \throw isc::BadValue Given RRset(s) are invalid.
+    /// \throw std::bad_alloc Memory allocation fails.
+    ///
+    /// \param mem_sgmt A \c MemorySegment from which memory for the new
+    /// \c RdataSet is allocated.
+    /// \param encoder The RDATA encoder to encode \c rrset and \c sig_rrset
+    /// with the \c RdataSet is to be created.
+    /// \param rrset A (non-RRSIG) RRset containing the RDATA that are not
+    /// to be present in the result. Can be NULL if sig_rrset is not.
+    /// \param sig_rrset An RRSIG RRset containing the RRSIGs that are not
+    /// to be present in the result. Can be NULL if rrset is not.
+    /// \param old_rdataset The data from which to subtract.
+    ///
+    /// \return A pointer to the created \c RdataSet.
+    static RdataSet* subtract(util::MemorySegment& mem_sgmt,
+                              RdataEncoder& encoder,
+                              const dns::ConstRRsetPtr& rrset,
+                              const dns::ConstRRsetPtr& sig_rrset,
+                              const RdataSet& old_rdataset);
+
     /// \brief Destruct and deallocate \c RdataSet
     ///
     /// Note that this method needs to know the expected RR class of the
@@ -311,6 +358,12 @@ private:
     // field for the real number of RRSIGs.  It's 2^3 - 1 = 7.
     static const size_t MANY_RRSIG_COUNT = (1 << 3) - 1;
 
+    // Common code for packing the result in create and subtract.
+    static RdataSet* packSet(util::MemorySegment& mem_sgmt,
+                             RdataEncoder& encoder, size_t rdata_count,
+                             size_t rrsig_count, const dns::RRType& rrtype,
+                             const dns::RRTTL& rrttl);
+
 public:
     /// \brief Return the bare pointer to the next node.
     ///
diff --git a/src/lib/datasrc/memory/zone_finder.cc b/src/lib/datasrc/memory/zone_finder.cc
index 3f61b89..a6c601b 100644
--- a/src/lib/datasrc/memory/zone_finder.cc
+++ b/src/lib/datasrc/memory/zone_finder.cc
@@ -772,7 +772,7 @@ InMemoryZoneFinder::findAll(const isc::dns::Name& name,
 // the case of CNAME can be eliminated (these should be guaranteed at the load
 // or update time, but even if they miss a corner case and allows a CNAME to
 // be added at origin, the zone is broken anyway, so we'd just let this
-// method return garbage, too).  As a result, there can be only too cases
+// method return garbage, too).  As a result, there can be only two cases
 // for the result codes: SUCCESS if the requested type of RR exists; NXRRSET
 // otherwise.  Due to its simplicity we implement it separately, rather than
 // sharing the code with findInternal.
diff --git a/src/lib/datasrc/tests/memory/domaintree_unittest.cc b/src/lib/datasrc/tests/memory/domaintree_unittest.cc
index 3e6bceb..7568f8d 100644
--- a/src/lib/datasrc/tests/memory/domaintree_unittest.cc
+++ b/src/lib/datasrc/tests/memory/domaintree_unittest.cc
@@ -31,11 +31,17 @@
 
 #include <boost/format.hpp>
 
+#include <stdlib.h>
+
+#include <set>
+#include <algorithm>
+
 using namespace std;
 using namespace isc;
 using namespace isc::dns;
 using isc::UnitTestUtil;
 using namespace isc::datasrc::memory;
+using isc::util::random::UniformRandomIntegerGenerator;
 
 // XXX: some compilers cannot find class static constants used in
 // EXPECT_xxx macros, for which we need an explicit empty definition.
@@ -62,6 +68,53 @@ const size_t Name::MAX_LABELS;
 
 namespace {
 
+// The full absolute names of the nodes in the tree (the tree also
+// contains "." which is not included in this list).
+const char* const domain_names[] = {
+    "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
+    "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"
+};
+
+// These are set as the node data.
+const int node_distances[] = {
+    3, 1, 2, 2, 2, 3, 1, 2, 1, 1, 2, 2
+};
+
+const int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
+
+/*
+ * The domain order should be:
+ * ., a, b, c, d.e.f, x.d.e.f, w.y.d.e.f, o.w.y.d.e.f, p.w.y.d.e.f,
+ * q.w.y.d.e.f, z.d.e.f, j.z.d.e.f, g.h, i.g.h, k.g.h
+ *             . (no data, can't be found)
+ *             |
+ *             b
+ *           /   \
+ *          a    d.e.f
+ *              /  |   \
+ *             c   |    g.h
+ *                 |     |
+ *                w.y    i
+ *              /  |  \   \
+ *             x   |   z   k
+ *                 |   |
+ *                 p   j
+ *               /   \
+ *              o     q
+ */
+
+const char* const ordered_names[] = {
+    "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
+    "p.w.y.d.e.f", "q.w.y.d.e.f", "z.d.e.f", "j.z.d.e.f",
+    "g.h", "i.g.h", "k.g.h"};
+const size_t ordered_names_count(sizeof(ordered_names) /
+                                 sizeof(*ordered_names));
+
+const char* const upper_node_names[] = {
+    ".", ".", ".", ".", "d.e.f", "d.e.f", "w.y.d.e.f",
+    "w.y.d.e.f", "w.y.d.e.f", "d.e.f", "z.d.e.f",
+    ".", "g.h", "g.h"};
+
 void deleteData(int* i) {
     delete i;
 }
@@ -88,20 +141,13 @@ class DomainTreeTest : public::testing::Test {
 protected:
     DomainTreeTest() :
         dtree_holder_(mem_sgmt_, TestDomainTree::create(mem_sgmt_)),
-        dtree_expose_empty_node_holder_(mem_sgmt_,
-                                         TestDomainTree::create(mem_sgmt_, true)),
+        dtree_expose_empty_node_holder_
+            (mem_sgmt_, TestDomainTree::create(mem_sgmt_, true)),
         dtree(*dtree_holder_.get()),
         dtree_expose_empty_node(*dtree_expose_empty_node_holder_.get()),
-        cdtnode(NULL)
+        cdtnode(NULL),
+        name_gen_('a', 'z')
     {
-        const char* const domain_names[] = {
-            "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
-            "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
-        const int node_distances[] = {
-            3, 1, 2, 2, 2, 3, 1, 2, 1, 1, 2, 2
-        };
-
-        int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
         for (int i = 0; i < name_count; ++i) {
             dtree.insert(mem_sgmt_, Name(domain_names[i]), &dtnode);
             // Check the node doesn't have any data initially.
@@ -113,6 +159,8 @@ protected:
             EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(
                           new int(node_distances[i])));
         }
+
+        srandom(time(NULL));
     }
 
     util::MemorySegmentLocal mem_sgmt_;
@@ -122,6 +170,7 @@ protected:
     TestDomainTree& dtree_expose_empty_node;
     TestDomainTreeNode* dtnode;
     const TestDomainTreeNode* cdtnode;
+    UniformRandomIntegerGenerator name_gen_;
     uint8_t buf[LabelSequence::MAX_SERIALIZED_LENGTH];
 };
 
@@ -129,8 +178,8 @@ TEST_F(DomainTreeTest, nodeCount) {
     EXPECT_EQ(15, dtree.getNodeCount());
 
     // Delete all nodes, then the count should be set to 0.  This also tests
-    // the behavior of deleteAllNodes().
-    dtree.deleteAllNodes(mem_sgmt_, deleteData);
+    // the behavior of removeAllNodes().
+    dtree.removeAllNodes(mem_sgmt_, deleteData);
     EXPECT_EQ(0, dtree.getNodeCount());
 }
 
@@ -150,22 +199,6 @@ TEST_F(DomainTreeTest, getDistance) {
     }
 }
 
-void
-checkDistances(const TestDomainTree& tree, int distance) {
-    TestDomainTreeNodeChain node_path;
-    const TestDomainTreeNode* node = NULL;
-
-    // Try to find a node left of the left-most node, and start from its
-    // next node (which is the left-most node in its subtree).
-    EXPECT_EQ(TestDomainTree::NOTFOUND,
-              tree.find<void*>(Name("0"), &node, node_path, NULL, NULL));
-    while ((node = tree.nextNode(node_path)) != NULL) {
-        // The distance from each node to its sub-tree root must be less
-        // than 2 * log(n).
-        EXPECT_GE(2 * distance, node->getDistance());
-    }
-}
-
 TEST_F(DomainTreeTest, checkDistanceRandom) {
     // This test checks an important performance-related property of the
     // DomainTree (a red-black tree), which is important for us: the
@@ -176,7 +209,6 @@ TEST_F(DomainTreeTest, checkDistanceRandom) {
 
     TreeHolder mytree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
     TestDomainTree& mytree = *mytree_holder.get();
-    isc::util::random::UniformRandomIntegerGenerator gen('a', 'z');
     const int log_num_nodes = 20;
 
     // Make a large million+ node top-level domain tree, i.e., the
@@ -191,7 +223,7 @@ TEST_F(DomainTreeTest, checkDistanceRandom) {
         string namestr;
         while (true) {
             for (int j = 0; j < 32; j++) {
-                namestr += gen();
+                namestr += name_gen_();
             }
             namestr += '.';
 
@@ -206,7 +238,12 @@ TEST_F(DomainTreeTest, checkDistanceRandom) {
         EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(i + 1)));
     }
 
-    checkDistances(mytree, log_num_nodes);
+    // The distance from each node to its sub-tree root must be less
+    // than 2 * log(n).
+    EXPECT_GE(2 * log_num_nodes, mytree.getHeight());
+
+    // Also check RB tree properties
+    EXPECT_TRUE(mytree.checkProperties());
 }
 
 TEST_F(DomainTreeTest, checkDistanceSorted) {
@@ -235,7 +272,12 @@ TEST_F(DomainTreeTest, checkDistanceSorted) {
         EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(i + 1)));
     }
 
-    checkDistances(mytree, log_num_nodes);
+    // The distance from each node to its sub-tree root must be less
+    // than 2 * log(n).
+    EXPECT_GE(2 * log_num_nodes, mytree.getHeight());
+
+    // Also check RB tree properties
+    EXPECT_TRUE(mytree.checkProperties());
 }
 
 TEST_F(DomainTreeTest, setGetData) {
@@ -344,6 +386,329 @@ TEST_F(DomainTreeTest, insertNames) {
                                                   &dtnode));
 }
 
+TEST_F(DomainTreeTest, remove) {
+    // This testcase checks that after node removal, the binary-search
+    // tree is valid and all nodes that are supposed to exist are
+    // present in the correct order. It mainly tests DomainTree as a
+    // BST, and not particularly as a red-black tree. This test checks
+    // node deletion when upper nodes have data.
+
+    // Delete single nodes and check if the rest of the nodes exist
+    for (int j = 0; j < ordered_names_count; ++j) {
+        TreeHolder holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_, true));
+        TestDomainTree& tree(*holder.get());
+        TestDomainTreeNode* node;
+
+        for (int i = 0; i < name_count; ++i) {
+            tree.insert(mem_sgmt_, Name(domain_names[i]), NULL);
+        }
+
+        for (int i = 0; i < ordered_names_count; ++i) {
+            EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                      tree.find(Name(ordered_names[i]), &node));
+            // Check nodes are not empty.
+            EXPECT_EQ(static_cast<int*>(NULL), node->setData(new int(i)));
+            EXPECT_FALSE(node->isEmpty());
+        }
+
+        // Now, delete the j'th node from the tree.
+        EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                  tree.find(Name(ordered_names[j]), &node));
+        tree.remove(mem_sgmt_, node, deleteData);
+
+        // Check RB tree properties
+        ASSERT_TRUE(tree.checkProperties());
+
+        // Now, walk through nodes in order.
+        TestDomainTreeNodeChain node_path;
+        const TestDomainTreeNode* cnode;
+        int start_node;
+
+        if (j == 0) {
+            EXPECT_NE(TestDomainTree::EXACTMATCH,
+                      tree.find(Name(ordered_names[0]),
+                                &cnode));
+            EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                      tree.find(Name(ordered_names[1]),
+                                &cnode, node_path));
+            start_node = 1;
+        } else {
+            EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                      tree.find(Name(ordered_names[0]),
+                                &cnode, node_path));
+            start_node = 0;
+        }
+
+        for (int i = start_node; i < ordered_names_count; ++i) {
+            const Name nj(ordered_names[j]);
+            const Name ni(ordered_names[i]);
+
+            if (ni == nj) {
+                // This may be true for the last node if we seek ahead
+                // in the loop using nextNode() below.
+                if (!cnode) {
+                    break;
+                }
+                // All ordered nodes have data initially. If any node is
+                // empty, it means it was remove()d, but an empty node
+                // exists because it is a super-domain. Just skip it.
+                if (cnode->isEmpty()) {
+                     cnode = tree.nextNode(node_path);
+                }
+                continue;
+            }
+
+            ASSERT_NE(static_cast<void*>(NULL), cnode);
+            const int* data = cnode->getData();
+
+            if (data) {
+                 EXPECT_EQ(i, *data);
+            }
+
+            uint8_t buf[isc::dns::LabelSequence::MAX_SERIALIZED_LENGTH];
+            const LabelSequence ls(cnode->getAbsoluteLabels(buf));
+            EXPECT_EQ(LabelSequence(ni), ls);
+
+            cnode = tree.nextNode(node_path);
+        }
+
+        // We should have reached the end of the tree.
+        ASSERT_EQ(static_cast<void*>(NULL), cnode);
+    }
+}
+
+TEST_F(DomainTreeTest, removeEmpty) {
+    // This test is similar to the .remove test. But it checks node
+    // deletion when upper nodes are empty.
+
+    // Delete single nodes and check if the rest of the nodes exist
+    for (int j = 0; j < ordered_names_count; ++j) {
+        TreeHolder holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_, true));
+        TestDomainTree& tree(*holder.get());
+        TestDomainTreeNode* node;
+
+        for (int i = 0; i < name_count; ++i) {
+            tree.insert(mem_sgmt_, Name(domain_names[i]), NULL);
+        }
+
+        for (int i = 0; i < ordered_names_count; ++i) {
+            EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                      tree.find(Name(ordered_names[i]), &node));
+            // Check nodes are empty.
+            EXPECT_TRUE(node->isEmpty());
+        }
+
+        // Now, delete the j'th node from the tree.
+        EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                  tree.find(Name(ordered_names[j]), &node));
+        tree.remove(mem_sgmt_, node, deleteData);
+
+        // Check RB tree properties
+        ASSERT_TRUE(tree.checkProperties());
+
+        // Now, walk through nodes in order.
+        TestDomainTreeNodeChain node_path;
+        const TestDomainTreeNode* cnode;
+        int start_node;
+
+        if (j == 0) {
+            EXPECT_NE(TestDomainTree::EXACTMATCH,
+                      tree.find(Name(ordered_names[0]),
+                                &cnode));
+            EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                      tree.find(Name(ordered_names[1]),
+                                &cnode, node_path));
+            start_node = 1;
+        } else {
+            EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                      tree.find(Name(ordered_names[0]),
+                                &cnode, node_path));
+            start_node = 0;
+        }
+
+        for (int i = start_node; i < ordered_names_count; ++i) {
+            const Name nj(ordered_names[j]);
+            const Name ni(ordered_names[i]);
+
+            if ((nj == Name("j.z.d.e.f")) &&
+                (ni == Name("z.d.e.f")))
+            {
+                // The only special case in the tree. Here, "z.d.e.f"
+                // will not exist as it would have been removed during
+                // removal of "j.z.d.e.f".
+                continue;
+            }
+
+            if (ni == nj) {
+                // This may be true for the last node if we seek ahead
+                // in the loop using nextNode() below.
+                if (!cnode) {
+                    break;
+                }
+                // All ordered nodes are empty initially. If an empty
+                // removed node exists because it is a super-domain,
+                // just skip it.
+                if ((nj == Name("d.e.f")) ||
+                    (nj == Name("w.y.d.e.f")) ||
+                    (nj == Name("z.d.e.f")) ||
+                    (nj == Name("g.h")))
+                {
+                     cnode = tree.nextNode(node_path);
+                }
+                continue;
+            }
+
+            ASSERT_NE(static_cast<void*>(NULL), cnode);
+
+            uint8_t buf[isc::dns::LabelSequence::MAX_SERIALIZED_LENGTH];
+            const LabelSequence ls(cnode->getAbsoluteLabels(buf));
+            EXPECT_EQ(LabelSequence(ni), ls);
+
+            cnode = tree.nextNode(node_path);
+        }
+
+        // We should have reached the end of the tree.
+        ASSERT_EQ(static_cast<void*>(NULL), cnode);
+    }
+}
+
+void
+insertNodes(util::MemorySegment& mem_sgmt, TestDomainTree& tree,
+            std::set<std::string>& names, size_t num_nodes,
+            UniformRandomIntegerGenerator& name_gen)
+{
+    for (size_t i = 0; i < num_nodes; ++i) {
+        std::string namestr;
+        while (true) {
+            for (int j = 0; j < 32; j++) {
+                namestr += name_gen();
+            }
+            namestr += '.';
+
+            TestDomainTreeNode* cnode;
+            if (tree.insert(mem_sgmt, Name(namestr), &cnode) ==
+                TestDomainTree::SUCCESS) {
+                names.insert(namestr);
+                break;
+            }
+
+            namestr.clear();
+        }
+    }
+}
+
+void
+removeNodes(util::MemorySegment& mem_sgmt, TestDomainTree& tree,
+            std::set<std::string>& names, size_t num_nodes)
+{
+    size_t set_size = names.size();
+
+    for (size_t i = 0; i < num_nodes; ++i) {
+        // Here, UniformRandomIntegerGenerator is not a great RNG as
+        // it'll likely get seeded with the same seed throughout this
+        // testcase, and the size of the names set keeps changing.
+
+        std::set<std::string>::iterator it(names.begin());
+        // This is rather inefficient, but it's a test...
+        std::advance(it, random() % set_size);
+        std::string nstr(*it);
+
+        TestDomainTreeNode* node;
+        EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                  tree.find(Name(nstr), &node));
+
+        tree.remove(mem_sgmt, node, deleteData);
+
+        names.erase(*it);
+        --set_size;
+    }
+}
+
+void
+checkTree(const TestDomainTree& tree,
+          const std::set<std::string>& names)
+{
+    // The distance from each node to its sub-tree root must be less
+    // than 2 * log_2(1024).
+    EXPECT_GE(2 * 10, tree.getHeight());
+
+    // Also check RB tree properties
+    EXPECT_TRUE(tree.checkProperties());
+
+    // Now, walk through nodes in order.
+    TestDomainTreeNodeChain node_path;
+    const TestDomainTreeNode* cnode;
+
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              tree.find(Name("."), &cnode, node_path));
+
+    // Skip to the next node after "."
+    cnode = tree.nextNode(node_path);
+    if (names.empty()) {
+        // Empty tree.
+        EXPECT_EQ(static_cast<void*>(NULL), cnode);
+        return;
+    }
+
+    for (std::set<std::string>::const_iterator it = names.begin();
+         it != names.end(); ++it)
+    {
+        const Name n1(*it);
+        const Name n2(cnode->getName());
+
+        const NameComparisonResult result = n1.compare(n2);
+        EXPECT_EQ(NameComparisonResult::EQUAL, result.getRelation());
+
+        cnode = tree.nextNode(node_path);
+    }
+
+    // We should have reached the end of the tree.
+    EXPECT_EQ(static_cast<void*>(NULL), cnode);
+}
+
+TEST_F(DomainTreeTest, insertAndRemove) {
+    // What is the best way to test our red-black tree code? It is not a
+    // good method to test every case handled in the actual code itself
+    // (such as in insertRebalance() and removeRebalance()). This is
+    // because our approach itself may be incorrect.
+    //
+    // We test our code at the interface level here by exercising the
+    // tree randomly multiple times, checking that red-black tree
+    // properties are valid, and all the nodes that are supposed to be
+    // in the tree exist and are in order.
+
+    // NOTE: These tests are run within a single tree in the
+    // forest. Fusion, etc. are tested elsewhere. The number of nodes in
+    // the tree doesn't grow over 1024.
+
+    TreeHolder holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_, true));
+    TestDomainTree& tree(*holder.get());
+    std::set<std::string> names;
+
+    size_t node_count = 0;
+
+    // Repeat the insert/remove test some 4096 times
+    for (int i = 0; i < 4096; ++i) {
+         UniformRandomIntegerGenerator gen(1, 1024 - node_count);
+         size_t num_nodes = gen();
+         node_count += num_nodes;
+
+         insertNodes(mem_sgmt_, tree, names, num_nodes, name_gen_);
+         checkTree(tree, names);
+
+         UniformRandomIntegerGenerator gen2(1, node_count);
+         num_nodes = gen2();
+         node_count -= num_nodes;
+
+         removeNodes(mem_sgmt_, tree, names, num_nodes);
+         checkTree(tree, names);
+    }
+
+    // Remove the rest of the nodes.
+    removeNodes(mem_sgmt_, tree, names, node_count);
+    checkTree(tree, names);
+}
+
 TEST_F(DomainTreeTest, subTreeRoot) {
     // This is a testcase for a particular issue that went unchecked in
     // #2089's implementation, but was fixed in #2092. The issue was
@@ -778,45 +1143,14 @@ TEST_F(DomainTreeTest, getAbsoluteNameError) {
     EXPECT_THROW(chain.getAbsoluteName(), BadValue);
 }
 
-/*
- * The domain order should be:
- * ., a, b, c, d.e.f, x.d.e.f, w.y.d.e.f, o.w.y.d.e.f, p.w.y.d.e.f,
- * q.w.y.d.e.f, z.d.e.f, j.z.d.e.f, g.h, i.g.h, k.g.h
- *             . (no data, can't be found)
- *             |
- *             b
- *           /   \
- *          a    d.e.f
- *              /  |   \
- *             c   |    g.h
- *                 |     |
- *                w.y    i
- *              /  |  \   \
- *             x   |   z   k
- *                 |   |
- *                 p   j
- *               /   \
- *              o     q
- */
-const char* const names[] = {
-    "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
-    "p.w.y.d.e.f", "q.w.y.d.e.f", "z.d.e.f", "j.z.d.e.f",
-    "g.h", "i.g.h", "k.g.h"};
-const size_t name_count(sizeof(names) / sizeof(*names));
-
-const char* const upper_node_names[] = {
-    ".", ".", ".", ".", "d.e.f", "d.e.f", "w.y.d.e.f",
-    "w.y.d.e.f", "w.y.d.e.f", "d.e.f", "z.d.e.f",
-    ".", "g.h", "g.h"};
-
 TEST_F(DomainTreeTest, getUpperNode) {
     TestDomainTreeNodeChain node_path;
     const TestDomainTreeNode* node = NULL;
     EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              dtree_expose_empty_node.find(Name(names[0]),
-                                            &node,
-                                            node_path));
-    for (int i = 0; i < name_count; ++i) {
+              dtree_expose_empty_node.find(Name(ordered_names[0]),
+                                           &node,
+                                           node_path));
+    for (int i = 0; i < ordered_names_count; ++i) {
         EXPECT_NE(static_cast<void*>(NULL), node);
 
         const TestDomainTreeNode* upper_node = node->getUpperNode();
@@ -852,7 +1186,7 @@ TEST_F(DomainTreeTest, getSubTreeRoot) {
     TestDomainTreeNodeChain node_path;
     const TestDomainTreeNode* node = NULL;
     EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              dtree_expose_empty_node.find(Name(names[0]),
+              dtree_expose_empty_node.find(Name(ordered_names[0]),
                                             &node,
                                             node_path));
     for (int i = 0; i < name_count; ++i) {
@@ -884,10 +1218,10 @@ TEST_F(DomainTreeTest, nextNode) {
     TestDomainTreeNodeChain node_path;
     const TestDomainTreeNode* node = NULL;
     EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              dtree.find(Name(names[0]), &node, node_path));
-    for (int i = 0; i < name_count; ++i) {
+              dtree.find(Name(ordered_names[0]), &node, node_path));
+    for (int i = 0; i < ordered_names_count; ++i) {
         EXPECT_NE(static_cast<void*>(NULL), node);
-        EXPECT_EQ(Name(names[i]), node_path.getAbsoluteName());
+        EXPECT_EQ(Name(ordered_names[i]), node_path.getAbsoluteName());
         node = dtree.nextNode(node_path);
     }
 
@@ -903,7 +1237,7 @@ TEST_F(DomainTreeTest, nextNode) {
 // node_path - the path from the previous call to find(), will be modified
 // chain_length - the number of names that should be in the chain to be walked
 //   (0 means it should be empty, 3 means 'a', 'b' and 'c' should be there -
-//   this is always from the beginning of the names[] list).
+//   this is always from the beginning of the ordered_names[] list).
 // skip_first - if this is false, the node should already contain the node with
 //   the first name of the chain. If it is true, the node should be NULL
 //   (true is for finds that return no match, false for the ones that return
@@ -921,7 +1255,7 @@ previousWalk(TestDomainTree& dtree, const TestDomainTreeNode* node,
     }
     for (size_t i(chain_length); i > 0; --i) {
         EXPECT_NE(static_cast<void*>(NULL), node);
-        EXPECT_EQ(Name(names[i - 1]), node_path.getAbsoluteName());
+        EXPECT_EQ(Name(ordered_names[i - 1]), node_path.getAbsoluteName());
         // Find the node at the path and check the value is the same
         // (that it really returns the correct corresponding node)
         //
@@ -930,7 +1264,8 @@ previousWalk(TestDomainTree& dtree, const TestDomainTreeNode* node,
             const TestDomainTreeNode* node2(NULL);
             TestDomainTreeNodeChain node_path2;
             EXPECT_EQ(TestDomainTree::EXACTMATCH,
-                      dtree.find(Name(names[i - 1]), &node2, node_path2));
+                      dtree.find(Name(ordered_names[i - 1]),
+                                 &node2, node_path2));
             EXPECT_EQ(node, node2);
         }
         node = dtree.previousNode(node_path);
@@ -960,8 +1295,9 @@ TEST_F(DomainTreeTest, previousNode) {
     {
         SCOPED_TRACE("Iterate through");
         EXPECT_EQ(TestDomainTree::EXACTMATCH,
-                  dtree.find(Name(names[name_count - 1]), &node, node_path));
-        previousWalk(dtree, node, node_path, name_count, false);
+                  dtree.find(Name(ordered_names[ordered_names_count - 1]),
+                             &node, node_path));
+        previousWalk(dtree, node, node_path, ordered_names_count, false);
         node = NULL;
         node_path.clear();
     }
@@ -970,7 +1306,7 @@ TEST_F(DomainTreeTest, previousNode) {
         SCOPED_TRACE("Iterate from the middle");
         // Now, start somewhere in the middle, but within the real node.
         EXPECT_EQ(TestDomainTree::EXACTMATCH,
-                  dtree.find(Name(names[4]), &node, node_path));
+                  dtree.find(Name(ordered_names[4]), &node, node_path));
         previousWalk(dtree, node, node_path, 5, false);
         node = NULL;
         node_path.clear();
@@ -981,7 +1317,7 @@ TEST_F(DomainTreeTest, previousNode) {
         // If we start at the lowest (which is "a"), we get to the beginning
         // right away.
         EXPECT_EQ(TestDomainTree::EXACTMATCH,
-                  dtree.find(Name(names[0]), &node, node_path));
+                  dtree.find(Name(ordered_names[0]), &node, node_path));
         EXPECT_NE(static_cast<void*>(NULL), node);
         node = dtree.previousNode(node_path);
         ASSERT_NE(static_cast<void*>(NULL), node);
@@ -1008,7 +1344,7 @@ TEST_F(DomainTreeTest, previousNode) {
         SCOPED_TRACE("Start after the last");
         EXPECT_EQ(TestDomainTree::NOTFOUND,
                   dtree.find(Name("z"), &node, node_path));
-        previousWalk(dtree, node, node_path, name_count, true);
+        previousWalk(dtree, node, node_path, ordered_names_count, true);
         node = NULL;
         node_path.clear();
     }
@@ -1070,7 +1406,7 @@ TEST_F(DomainTreeTest, previousNode) {
         EXPECT_GT(node_path.getLastComparisonResult().getOrder(), 0);
         // We then descend into 'i.g.h' and walk all the nodes in the
         // tree.
-        previousWalk(dtree, node, node_path, name_count, true);
+        previousWalk(dtree, node, node_path, ordered_names_count, true);
         node = NULL;
         node_path.clear();
     }
@@ -1364,17 +1700,11 @@ TEST_F(DomainTreeTest, root) {
 }
 
 TEST_F(DomainTreeTest, getAbsoluteLabels) {
-    // The full absolute names of the nodes in the tree
-    // with the addition of the explicit root node
-    const char* const domain_names[] = {
-        "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
-        "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
     // The names of the nodes themselves, as they end up in the tree
     const char* const first_labels[] = {
         "c", "b", "a", "x", "z", "g.h", "i", "o",
         "j", "p", "q", "k"};
 
-    const int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
     for (int i = 0; i < name_count; ++i) {
         EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name(domain_names[i]),
                   &cdtnode));
diff --git a/src/lib/datasrc/tests/memory/rdataset_unittest.cc b/src/lib/datasrc/tests/memory/rdataset_unittest.cc
index 31e5be2..e6fd5cc 100644
--- a/src/lib/datasrc/tests/memory/rdataset_unittest.cc
+++ b/src/lib/datasrc/tests/memory/rdataset_unittest.cc
@@ -103,8 +103,10 @@ checkData(const void* data, size_t size, const RRType* rrtype,
     ASSERT_TRUE(*it != it_end); // shouldn't reach the end yet
 
     isc::util::InputBuffer b(data, size);
-    EXPECT_EQ(0, createRdata(*rrtype, RRClass::IN(), b, size)->compare(
-                  *createRdata(*rrtype, RRClass::IN(), **it)));
+    const RdataPtr& actual(createRdata(*rrtype, RRClass::IN(), b, size));
+    const RdataPtr& expected(createRdata(*rrtype, RRClass::IN(), **it));
+    EXPECT_EQ(0, actual->compare(*expected)) << actual->toText() <<
+        " vs. " << expected->toText();
     ++(*it);                    // move to the next expected data
 }
 
@@ -224,6 +226,91 @@ TEST_F(RdataSetTest, mergeCreate) {
     }
 }
 
+TEST_F(RdataSetTest, subtract) {
+    // Prepare test data
+    const char* const a_rdatas[] = { "192.0.2.1", "192.0.2.2" };
+    const char* const sig_rdatas[] = {
+        "A 5 2 3600 20120814220826 20120715220826 1234 example.com. FAKE",
+        "A 5 2 3600 20120814220826 20120715220826 4321 example.com. FAKE" };
+    ConstRRsetPtr a_rrsets = textToRRset("www.example.com. 1076895760 IN A "
+                                         + string(a_rdatas[0]) + "\n"
+                                         + "www.example.com. 1076895760 IN A "
+                                         + string(a_rdatas[1]));
+    ConstRRsetPtr rrsig_rrsets =
+        textToRRset("www.example.com. 1076895760 IN RRSIG "
+                    + string(sig_rdatas[0]) + "\n"
+                    + "www.example.com. 1076895760 IN RRSIG "
+                    + string(sig_rdatas[1]));
+    ConstRRsetPtr null_rrset;   // convenience shortcut
+    // Prepare the data to subtract (they have one common and one differing
+    // element each).
+    const char* const a_rdatas_rm[] = { "192.0.2.1", "192.0.2.3" };
+    const char* const sig_rdatas_rm[] = {
+        "A 5 2 3600 20120814220826 20120715220826 1234 example.com. FAKE",
+        "A 5 2 3600 20120814220826 20120715220826 5678 example.com. FAKE" };
+    ConstRRsetPtr a_rrsets_rm =
+        textToRRset("www.example.com. 1076895760 IN A "
+                    + string(a_rdatas_rm[0]) + "\n"
+                    + "www.example.com. 1076895760 IN A "
+                    + string(a_rdatas_rm[1]));
+    ConstRRsetPtr rrsig_rrsets_rm =
+        textToRRset("www.example.com. 1076895760 IN RRSIG "
+                    + string(sig_rdatas_rm[0]) + "\n"
+                    + "www.example.com. 1076895760 IN RRSIG "
+                    + string(sig_rdatas_rm[1]));
+
+    // A similar cycle as in the mergeCreate test.
+    for (int i = 1; i < 4; ++i) {
+        for (int j = 1; j < 4; ++j) {
+            SCOPED_TRACE("creating subtract case " + lexical_cast<string>(i) +
+                         ", " + lexical_cast<string>(j));
+            // Create old rdataset
+            SegmentObjectHolder<RdataSet, RRClass> holder1(mem_sgmt_, rrclass);
+            holder1.set(RdataSet::create(mem_sgmt_, encoder_,
+                                 (i & 1) ? a_rrsets : null_rrset,
+                                 (i & 2) ? rrsig_rrsets : null_rrset));
+            // Create subtracted rdataset, from the old one and RRsets
+            SegmentObjectHolder<RdataSet, RRClass> holder2(mem_sgmt_, rrclass);
+            holder2.set(RdataSet::subtract(mem_sgmt_, encoder_,
+                                 (j & 1) ? a_rrsets_rm : null_rrset,
+                                 (j & 2) ? rrsig_rrsets_rm : null_rrset,
+                                 *holder1.get()));
+
+            // Set up the expected data for the case.
+            vector<string> expected_rdata;
+            if (i & 1) {
+                if (!(j & 1)) { // Not removed the other
+                    expected_rdata.push_back(a_rdatas[0]);
+                }
+                expected_rdata.push_back(a_rdatas[1]);
+            }
+            vector<string> expected_sigs;
+            if (i & 2) {
+                if (!(j & 2)) { // Not removed the other
+                    expected_sigs.push_back(sig_rdatas[0]);
+                }
+                expected_sigs.push_back(sig_rdatas[1]);
+            }
+
+            // Then perform the check
+            checkRdataSet(*holder2.get(), expected_rdata, expected_sigs);
+        }
+    }
+    // Reusing the data we have, test some corner cases.
+    SegmentObjectHolder<RdataSet, RRClass> holder_old(mem_sgmt_, rrclass);
+    holder_old.set(RdataSet::create(mem_sgmt_, encoder_, a_rrsets,
+                                    rrsig_rrsets));
+
+    // It throws if no Rdata passed.
+    EXPECT_THROW(RdataSet::subtract(mem_sgmt_, encoder_, null_rrset,
+                                    null_rrset, *holder_old.get()),
+                 isc::BadValue);
+
+    // If we remove everything, it returns NULL
+    EXPECT_EQ(NULL, RdataSet::subtract(mem_sgmt_, encoder_, a_rrsets,
+                                       rrsig_rrsets, *holder_old.get()));
+}
+
 TEST_F(RdataSetTest, duplicate) {
     // Create RRset and RRSIG containing duplicate RDATA.
     ConstRRsetPtr dup_rrset =
@@ -610,4 +697,58 @@ TEST_F(RdataSetTest, varyingTTL) {
     EXPECT_EQ(RRTTL(5), restoreTTL(rdataset->getTTLData()));
     RdataSet::destroy(mem_sgmt_, rdataset, rrclass);
 }
+
+// Creation of rdataset with bad params, with create and subtract
+TEST_F(RdataSetTest, badParams) {
+    const ConstRRsetPtr empty_rrset(new RRset(Name("www.example.com"),
+                                             RRClass::IN(), RRType::A(),
+                                             RRTTL(3600)));
+    const ConstRRsetPtr empty_rrsig(new RRset(Name("www.example.com"),
+                                             RRClass::IN(), RRType::RRSIG(),
+                                             RRTTL(3600)));
+    const ConstRRsetPtr a_rrset = textToRRset("www.example.com. 3600 IN A "
+                                              "192.0.2.1");
+    const ConstRRsetPtr aaaa_rrset = textToRRset("www.example.com. 3600 IN AAAA "
+                                                 "2001:db8::1");
+    const ConstRRsetPtr sig_rrset = textToRRset("www.example.com. 3600 IN RRSIG "
+                                                "A 5 2 3600 20120814220826 "
+                                                "20120715220826 1234 "
+                                                "example.com. FAKE");
+    const ConstRRsetPtr sig_rrset_ch = textToRRset("www.example.com. 3600 CH RRSIG "
+                                                   "A 5 2 3600 20120814220826 "
+                                                   "20120715220826 1234 "
+                                                   "example.com. FAKE",
+                                                   RRClass::CH());
+    SegmentObjectHolder<RdataSet, RRClass> holder(mem_sgmt_, rrclass);
+    holder.set(RdataSet::create(mem_sgmt_, encoder_, a_rrset, sig_rrset));
+    // Empty RRset as rdata
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, empty_rrset, sig_rrset),
+                 isc::BadValue);
+    // The same for rrsig
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset, empty_rrsig),
+                 isc::BadValue);
+    // Mismatched type
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, empty_rrset, a_rrset),
+                 isc::BadValue);
+    // Similar for subtract
+    EXPECT_THROW(RdataSet::subtract(mem_sgmt_, encoder_, empty_rrset,
+                                    sig_rrset, *holder.get()),
+                 isc::BadValue);
+    EXPECT_THROW(RdataSet::subtract(mem_sgmt_, encoder_, a_rrset, empty_rrset,
+                                    *holder.get()),
+                 isc::BadValue);
+    // Class mismatch
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset, sig_rrset_ch),
+                 isc::BadValue);
+    EXPECT_THROW(RdataSet::subtract(mem_sgmt_, encoder_, a_rrset,
+                                    sig_rrset_ch, *holder.get()),
+                 isc::BadValue);
+    // Bad rrtype
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset,
+                                  ConstRRsetPtr(), holder.get()),
+                 isc::BadValue);
+    EXPECT_THROW(RdataSet::subtract(mem_sgmt_, encoder_, aaaa_rrset,
+                                    ConstRRsetPtr(), *holder.get()),
+                 isc::BadValue);
+}
 }
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index 31ecdee..7a3a559 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -26,8 +26,10 @@ libb10_dhcp___la_SOURCES += option4_addrlst.cc option4_addrlst.h
 libb10_dhcp___la_SOURCES += option4_client_fqdn.cc option4_client_fqdn.h
 libb10_dhcp___la_SOURCES += option6_ia.cc option6_ia.h
 libb10_dhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
+libb10_dhcp___la_SOURCES += option6_iaprefix.cc option6_iaprefix.h
 libb10_dhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h
 libb10_dhcp___la_SOURCES += option6_client_fqdn.cc option6_client_fqdn.h
+libb10_dhcp___la_SOURCES += option_vendor.cc option_vendor.h
 libb10_dhcp___la_SOURCES += option_int.h
 libb10_dhcp___la_SOURCES += option_int_array.h
 libb10_dhcp___la_SOURCES += option.cc option.h
@@ -47,6 +49,7 @@ libb10_dhcp___la_SOURCES += pkt_filter_lpf.cc pkt_filter_lpf.h
 endif
 
 libb10_dhcp___la_SOURCES += std_option_defs.h
+libb10_dhcp___la_SOURCES += docsis3_option_defs.h
 
 libb10_dhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
 libb10_dhcp___la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
diff --git a/src/lib/dhcp/dhcp4.h b/src/lib/dhcp/dhcp4.h
index 002e74f..392478a 100644
--- a/src/lib/dhcp/dhcp4.h
+++ b/src/lib/dhcp/dhcp4.h
@@ -125,6 +125,8 @@ enum DHCPOptionType {
     DHO_DHCP_CLIENT_IDENTIFIER       = 61,
     DHO_NWIP_DOMAIN_NAME             = 62,
     DHO_NWIP_SUBOPTIONS              = 63,
+    DHO_TFTP_SERVER_NAME             = 66,
+    DHO_BOOT_FILE_NAME               = 67,
     DHO_USER_CLASS                   = 77,
     DHO_FQDN                         = 81,
     DHO_DHCP_AGENT_OPTIONS           = 82,
@@ -162,16 +164,27 @@ static const uint16_t DHCP4_SERVER_PORT = 67;
 /// extensions field).
 static const uint32_t DHCP_OPTIONS_COOKIE = 0x63825363;
 
+/* Relay Agent Information option subtypes: */
+
+static const uint16_t RAI_OPTION_AGENT_CIRCUIT_ID = 1; // RFC3046
+static const uint16_t RAI_OPTION_REMOTE_ID = 2; // RFC3046
+/* option 3 is reserved and will never be assigned */
+static const uint16_t RAI_OPTION_DOCSIS_DEVICE_CLASS = 4; // RFC3256
+static const uint16_t RAI_OPTION_LINK_SELECTION = 5; // RFC3527
+static const uint16_t RAI_OPTION_SUBSCRIBER_ID = 6; //RFC3993
+static const uint16_t RAI_OPTION_RADIUS = 7; //RFC4014
+static const uint16_t RAI_OPTION_AUTH = 8; //RFC4030
+static const uint16_t RAI_OPTION_VSI = 9; // RFC4243
+static const uint16_t RAI_OPTION_RELAY_FLAGS = 10; // RFC5010
+static const uint16_t RAI_OPTION_SERVER_ID_OVERRIDE = 11; // RFC5107
+static const uint16_t RAI_OPTION_RELAY_ID = 12; //RFC6925
+static const uint16_t RAI_OPTION_VIRTUAL_SUBNET_SELECT = 151; //RFC6607
+static const uint16_t RAI_OPTION_VIRTUAL_SUBNET_SELECT_CTRL = 152; //RFC6607
+
 // TODO: Following are leftovers from dhcp.h import from ISC DHCP
 // They will be converted to C++-style defines once they will start
 // to be used.
 #if 0
-/* Relay Agent Information option subtypes: */
-#define RAI_CIRCUIT_ID  1
-#define RAI_REMOTE_ID   2
-#define RAI_AGENT_ID    3
-#define RAI_LINK_SELECT 5
-
 /* FQDN suboptions: */
 #define FQDN_NO_CLIENT_UPDATE           1
 #define FQDN_SERVER_UPDATE              2
diff --git a/src/lib/dhcp/docsis3_option_defs.h b/src/lib/dhcp/docsis3_option_defs.h
new file mode 100644
index 0000000..19bdaa0
--- /dev/null
+++ b/src/lib/dhcp/docsis3_option_defs.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DOCSIS3_OPTION_DEFS_H
+#define DOCSIS3_OPTION_DEFS_H
+
+#include <dhcp/std_option_defs.h>
+#include <dhcp/option_data_types.h>
+
+
+namespace {
+
+#define VENDOR_ID_CABLE_LABS 4491
+
+#define DOCSIS3_V4_ORO 1
+#define DOCSIS3_V4_TFTP_SERVERS 2
+
+/// @brief Definitions of standard DHCPv4 options.
+const OptionDefParams DOCSIS3_V4_DEFS[] = {
+    { "oro", DOCSIS3_V4_ORO, OPT_UINT8_TYPE, true, NO_RECORD_DEF, "" },
+    { "tftp-servers", DOCSIS3_V4_TFTP_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" }
+};
+
+/// Number of option definitions defined.
+const int DOCSIS3_V4_DEFS_SIZE  = sizeof(DOCSIS3_V4_DEFS) / sizeof(OptionDefParams);
+
+/// @todo define remaining docsis3 v6 codes
+#define DOCSIS3_V6_ORO 1
+#define DOCSIS3_V6_DEVICE_TYPE 2
+#define DOCSIS3_V6_VENDOR_NAME 10
+#define DOCSIS3_V6_TFTP_SERVERS 32
+#define DOCSIS3_V6_CONFIG_FILE 33
+#define DOCSIS3_V6_SYSLOG_SERVERS 34
+#define DOCSIS3_V6_TIME_SERVERS 37
+#define DOCSIS3_V6_TIME_OFFSET 38
+
+/// @brief Definitions of standard DHCPv6 options.
+const OptionDefParams DOCSIS3_V6_DEFS[] = {
+    { "oro",            DOCSIS3_V6_ORO, OPT_UINT16_TYPE, true, NO_RECORD_DEF, "" },
+    { "device-type",    DOCSIS3_V6_DEVICE_TYPE, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
+    { "vendor-type",    DOCSIS3_V6_VENDOR_NAME, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
+    { "tftp-servers",   DOCSIS3_V6_TFTP_SERVERS, OPT_IPV6_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
+    { "time-servers",   DOCSIS3_V6_TIME_SERVERS, OPT_IPV6_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
+    { "config-file",    DOCSIS3_V6_CONFIG_FILE, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
+    { "syslog-servers", DOCSIS3_V6_SYSLOG_SERVERS, OPT_IPV6_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
+    { "time-offset",    DOCSIS3_V6_TIME_OFFSET, OPT_INT32_TYPE, false, NO_RECORD_DEF, "" }
+    // @todo add definitions for all remaning options.
+};
+
+/// Number of option definitions defined.
+const int DOCSIS3_V6_DEFS_SIZE  = sizeof(DOCSIS3_V6_DEFS) / sizeof(OptionDefParams);
+
+}; // anonymous namespace
+
+#endif // DOCSIS3_OPTION_DEFS_H
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index 128aafe..0ad35a5 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -177,6 +177,17 @@ IfaceMgr::IfaceMgr()
     }
 }
 
+void Iface::addUnicast(const isc::asiolink::IOAddress& addr) {
+    for (Iface::AddressCollection::const_iterator i = unicasts_.begin();
+         i != unicasts_.end(); ++i) {
+        if (*i == addr) {
+            isc_throw(BadValue, "Address " << addr.toText()
+                      << " already defined on the " << name_ << " interface.");
+        }
+    }
+    unicasts_.push_back(addr);
+}
+
 void IfaceMgr::closeSockets() {
     for (IfaceCollection::iterator iface = ifaces_.begin();
          iface != ifaces_.end(); ++iface) {
@@ -343,8 +354,10 @@ bool IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast) {
 
             }
             if (sock < 0) {
+                const char* errstr = strerror(errno);
                 isc_throw(SocketConfigError, "failed to open IPv4 socket"
-                          << " supporting broadcast traffic");
+                          << " supporting broadcast traffic, reason:"
+                          << errstr);
             }
 
             count++;
@@ -368,6 +381,23 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
             continue;
         }
 
+        // Open unicast sockets if there are any unicast addresses defined
+        Iface::AddressCollection unicasts = iface->getUnicasts();
+        for (Iface::AddressCollection::iterator addr = unicasts.begin();
+             addr != unicasts.end(); ++addr) {
+
+            sock = openSocket(iface->getName(), *addr, port);
+            if (sock < 0) {
+                const char* errstr = strerror(errno);
+                isc_throw(SocketConfigError, "failed to open unicast socket on "
+                          << addr->toText() << " on interface " << iface->getName()
+                          << ", reason: " << errstr);
+            }
+
+            count++;
+
+        }
+
         Iface::AddressCollection addrs = iface->getAddresses();
         for (Iface::AddressCollection::iterator addr = addrs.begin();
              addr != addrs.end();
@@ -389,7 +419,10 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
 
             sock = openSocket(iface->getName(), *addr, port);
             if (sock < 0) {
-                isc_throw(SocketConfigError, "failed to open unicast socket");
+                const char* errstr = strerror(errno);
+                isc_throw(SocketConfigError, "failed to open link-local socket on "
+                          << addr->toText() << " on interface "
+                          << iface->getName() << ", reason: " << errstr);
             }
 
             // Binding socket to unicast address and then joining multicast group
@@ -414,8 +447,10 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
                                    IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
                                    port);
             if (sock2 < 0) {
+                const char* errstr = strerror(errno);
                 isc_throw(SocketConfigError, "Failed to open multicast socket on "
-                          << " interface " << iface->getFullName());
+                          << " interface " << iface->getFullName() << ", reason:"
+                          << errstr);
                 iface->delSocket(sock); // delete previously opened socket
             }
 #endif
@@ -608,7 +643,9 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
         // interface.
         sock.open(asio::ip::udp::v4(), err_code);
         if (err_code) {
-            isc_throw(Unexpected, "failed to open UDPv4 socket");
+            const char* errstr = strerror(errno);
+            isc_throw(Unexpected, "failed to open UDPv4 socket, reason:"
+                      << errstr);
         }
         sock.set_option(asio::socket_base::broadcast(true), err_code);
         if (err_code) {
@@ -1137,16 +1174,50 @@ uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6& pkt) {
                   << pkt.getIface());
     }
 
+
     const Iface::SocketCollection& socket_collection = iface->getSockets();
+
+    Iface::SocketCollection::const_iterator candidate = socket_collection.end();
+
     Iface::SocketCollection::const_iterator s;
     for (s = socket_collection.begin(); s != socket_collection.end(); ++s) {
-        if ((s->family_ == AF_INET6) &&
-            (!s->addr_.getAddress().to_v6().is_multicast())) {
+
+        // We should not merge those conditions for debugging reasons.
+
+        // V4 sockets are useless for sending v6 packets.
+        if (s->family_ != AF_INET6) {
+            continue;
+        }
+
+        // Sockets bound to multicast address are useless for sending anything.
+        if (s->addr_.getAddress().to_v6().is_multicast()) {
+            continue;
+        }
+
+        if (s->addr_ == pkt.getLocalAddr()) {
+            // This socket is bound to the source address. This is perfect
+            // match, no need to look any further.
             return (s->sockfd_);
         }
-        /// @todo: Add more checks here later. If remote address is
-        /// not link-local, we can't use link local bound socket
-        /// to send data.
+
+        // If we don't have any other candidate, this one will do
+        if (candidate == socket_collection.end()) {
+            candidate = s;
+        } else {
+            // If we want to send something to link-local and the socket is
+            // bound to link-local or we want to send to global and the socket
+            // is bound to global, then use it as candidate
+            if ( (pkt.getRemoteAddr().getAddress().to_v6().is_link_local() &&
+                s->addr_.getAddress().to_v6().is_link_local()) ||
+                 (!pkt.getRemoteAddr().getAddress().to_v6().is_link_local() &&
+                  !s->addr_.getAddress().to_v6().is_link_local()) ) {
+                candidate = s;
+            }
+        }
+    }
+
+    if (candidate != socket_collection.end()) {
+        return (candidate->sockfd_);
     }
 
     isc_throw(Unexpected, "Interface " << iface->getFullName()
@@ -1175,5 +1246,6 @@ uint16_t IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
               << " does not have any suitable IPv4 sockets open.");
 }
 
+
 } // end of namespace isc::dhcp
 } // end of namespace isc
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
index 217524d..2f48813 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -264,6 +264,27 @@ public:
     /// @return collection of sockets added to interface
     const SocketCollection& getSockets() const { return sockets_; }
 
+    /// @brief Removes any unicast addresses
+    ///
+    /// Removes any unicast addresses that the server was configured to
+    /// listen on
+    void clearUnicasts() {
+        unicasts_.clear();
+    }
+
+    /// @brief Adds unicast the server should listen on
+    ///
+    /// @throw BadValue if specified address is already defined on interface
+    /// @param addr unicast address to listen on
+    void addUnicast(const isc::asiolink::IOAddress& addr);
+
+    /// @brief Returns a container of addresses the server should listen on
+    ///
+    /// @return address collection (may be empty)
+    const AddressCollection& getUnicasts() const {
+        return unicasts_;
+    }
+
 protected:
     /// Socket used to send data.
     SocketCollection sockets_;
@@ -277,6 +298,9 @@ protected:
     /// List of assigned addresses.
     AddressCollection addrs_;
 
+    /// List of unicast addresses the server should listen on
+    AddressCollection unicasts_;
+
     /// Link-layer address.
     uint8_t mac_[MAX_MAC_LEN];
 
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index 697c33e..4bc4a92 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -18,13 +18,16 @@
 #include <dhcp/dhcp6.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcp/option.h>
+#include <dhcp/option_vendor.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option_definition.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/std_option_defs.h>
+#include <dhcp/docsis3_option_defs.h>
 #include <exceptions/exceptions.h>
 #include <util/buffer.h>
+#include <dhcp/option_definition.h>
 
 #include <boost/shared_array.hpp>
 #include <boost/shared_ptr.hpp>
@@ -45,17 +48,29 @@ OptionDefContainer LibDHCP::v4option_defs_;
 // Static container with DHCPv6 option definitions.
 OptionDefContainer LibDHCP::v6option_defs_;
 
+VendorOptionDefContainers LibDHCP::vendor4_defs_;
+
+VendorOptionDefContainers LibDHCP::vendor6_defs_;
+
+// Let's keep it in .cc file. Moving it to .h would require including optionDefParams
+// definitions there
+void initOptionSpace(OptionDefContainer& defs,
+                     const OptionDefParams* params,
+                     size_t params_size);
+
 const OptionDefContainer&
 LibDHCP::getOptionDefs(const Option::Universe u) {
     switch (u) {
     case Option::V4:
         if (v4option_defs_.empty()) {
             initStdOptionDefs4();
+            initVendorOptsDocsis4();
         }
         return (v4option_defs_);
     case Option::V6:
         if (v6option_defs_.empty()) {
             initStdOptionDefs6();
+            initVendorOptsDocsis6();
         }
         return (v6option_defs_);
     default:
@@ -63,6 +78,38 @@ LibDHCP::getOptionDefs(const Option::Universe u) {
     }
 }
 
+const OptionDefContainer*
+LibDHCP::getVendorOption4Defs(const uint32_t vendor_id) {
+
+    if (vendor_id == VENDOR_ID_CABLE_LABS &&
+        vendor4_defs_.find(VENDOR_ID_CABLE_LABS) == vendor4_defs_.end()) {
+        initVendorOptsDocsis4();
+    }
+
+    VendorOptionDefContainers::const_iterator def = vendor4_defs_.find(vendor_id);
+    if (def == vendor4_defs_.end()) {
+        // No such vendor-id space
+        return (NULL);
+    }
+    return (&(def->second));
+}
+
+const OptionDefContainer*
+LibDHCP::getVendorOption6Defs(const uint32_t vendor_id) {
+
+    if (vendor_id == VENDOR_ID_CABLE_LABS &&
+        vendor6_defs_.find(VENDOR_ID_CABLE_LABS) == vendor6_defs_.end()) {
+        initVendorOptsDocsis6();
+    }
+
+    VendorOptionDefContainers::const_iterator def = vendor6_defs_.find(vendor_id);
+    if (def == vendor6_defs_.end()) {
+        // No such vendor-id space
+        return (NULL);
+    }
+    return (&(def->second));
+}
+
 OptionDefinitionPtr
 LibDHCP::getOptionDef(const Option::Universe u, const uint16_t code) {
     const OptionDefContainer& defs = getOptionDefs(u);
@@ -74,6 +121,31 @@ LibDHCP::getOptionDef(const Option::Universe u, const uint16_t code) {
     return (OptionDefinitionPtr());
 }
 
+OptionDefinitionPtr
+LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
+                            const uint16_t code) {
+    const OptionDefContainer* defs = NULL;
+    if (u == Option::V4) {
+        defs = getVendorOption4Defs(vendor_id);
+    } else if (u == Option::V6) {
+        defs = getVendorOption6Defs(vendor_id);
+    }
+
+    if (!defs) {
+        // Weird universe or unknown vendor_id. We don't care. No definitions
+        // one way or another
+        // What is it anyway?
+        return (OptionDefinitionPtr());
+    }
+
+    const OptionDefContainerTypeIndex& idx = defs->get<1>();
+    const OptionDefContainerTypeRange& range = idx.equal_range(code);
+    if (range.first != range.second) {
+        return (*range.first);
+    }
+    return (OptionDefinitionPtr());
+}
+
 bool
 LibDHCP::isStandardOption(const Option::Universe u, const uint16_t code) {
     if (u == Option::V6) {
@@ -128,14 +200,22 @@ LibDHCP::optionFactory(Option::Universe u,
 
 
 size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
-                               isc::dhcp::Option::OptionCollection& options,
+                               const std::string& option_space,
+                               isc::dhcp::OptionCollection& options,
                                size_t* relay_msg_offset /* = 0 */,
                                size_t* relay_msg_len /* = 0 */) {
     size_t offset = 0;
     size_t length = buf.size();
 
-    // Get the list of stdandard option definitions.
-    const OptionDefContainer& option_defs = LibDHCP::getOptionDefs(Option::V6);
+    // Get the list of standard option definitions.
+    OptionDefContainer option_defs;
+    if (option_space == "dhcp6") {
+        option_defs = LibDHCP::getOptionDefs(Option::V6);
+    }
+    // @todo Once we implement other option spaces we should add else clause
+    // here and gather option definitions for them. For now leaving option_defs
+    // empty will imply creation of generic Option.
+
     // Get the search index #1. It allows to search for option definitions
     // using option code.
     const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
@@ -164,6 +244,23 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
             continue;
         }
 
+        if (opt_type == D6O_VENDOR_OPTS) {
+            if (offset + 4 > length) {
+                // Truncated vendor-option. There is expected at least 4 bytes
+                // long enterprise-id field
+                return (offset);
+            }
+
+            // Parse this as vendor option
+            OptionPtr vendor_opt(new OptionVendor(Option::V6, buf.begin() + offset,
+                                                  buf.begin() + offset + opt_len));
+            options.insert(std::make_pair(opt_type, vendor_opt));
+
+            offset += opt_len;
+            continue;
+        }
+
+
         // Get all definitions with the particular option code. Note that option
         // code is non-unique within this container however at this point we
         // expect to get one option definition with the particular code. If more
@@ -206,11 +303,19 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
 }
 
 size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
-                               isc::dhcp::Option::OptionCollection& options) {
+                               const std::string& option_space,
+                               isc::dhcp::OptionCollection& options) {
     size_t offset = 0;
 
     // Get the list of stdandard option definitions.
-    const OptionDefContainer& option_defs = LibDHCP::getOptionDefs(Option::V4);
+    OptionDefContainer option_defs;
+    if (option_space == "dhcp4") {
+        option_defs = LibDHCP::getOptionDefs(Option::V4);
+    }
+    // @todo Once we implement other option spaces we should add else clause
+    // here and gather option definitions for them. For now leaving option_defs
+    // empty will imply creation of generic Option.
+
     // Get the search index #1. It allows to search for option definitions
     // using option code.
     const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
@@ -280,10 +385,195 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
     return (offset);
 }
 
+size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
+                                     const OptionBuffer& buf,
+                                     isc::dhcp::OptionCollection& options) {
+    size_t offset = 0;
+    size_t length = buf.size();
+
+    // Get the list of option definitions for this particular vendor-id
+    const OptionDefContainer* option_defs = LibDHCP::getVendorOption6Defs(vendor_id);
+
+    // Get the search index #1. It allows to search for option definitions
+    // using option code. If there's no such vendor-id space, we're out of luck
+    // anyway.
+    const OptionDefContainerTypeIndex* idx = NULL;
+    if (option_defs) {
+        idx = &(option_defs->get<1>());
+    }
+
+    // The buffer being read comprises a set of options, each starting with
+    // a two-byte type code and a two-byte length field.
+    while (offset + 4 <= length) {
+        uint16_t opt_type = isc::util::readUint16(&buf[offset]);
+        offset += 2;
+
+        uint16_t opt_len = isc::util::readUint16(&buf[offset]);
+        offset += 2;
+
+        if (offset + opt_len > length) {
+            // @todo: consider throwing exception here.
+            return (offset);
+        }
+
+        OptionPtr opt;
+        opt.reset();
+
+        // If there is a definition for such a vendor option...
+        if (idx) {
+            // Get all definitions with the particular option code. Note that option
+            // code is non-unique within this container however at this point we
+            // expect to get one option definition with the particular code. If more
+            // are returned we report an error.
+            const OptionDefContainerTypeRange& range = idx->equal_range(opt_type);
+            // Get the number of returned option definitions for the option code.
+            size_t num_defs = distance(range.first, range.second);
+
+            if (num_defs > 1) {
+                // Multiple options of the same code are not supported right now!
+                isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
+                          " for option type " << opt_type << " returned. Currently it is not"
+                          " supported to initialize multiple option definitions"
+                          " for the same option code. This will be supported once"
+                          " support for option spaces is implemented");
+            } else if (num_defs == 1) {
+                // The option definition has been found. Use it to create
+                // the option instance from the provided buffer chunk.
+                const OptionDefinitionPtr& def = *(range.first);
+                assert(def);
+                opt = def->optionFactory(Option::V6, opt_type,
+                                         buf.begin() + offset,
+                                         buf.begin() + offset + opt_len);
+            }
+        }
+
+        // This can happen in one of 2 cases:
+        // 1. we do not have definitions for that vendor-space
+        // 2. we do have definitions, but that particular option was not defined
+        if (!opt) {
+            opt = OptionPtr(new Option(Option::V6, opt_type,
+                                       buf.begin() + offset,
+                                       buf.begin() + offset + opt_len));
+        }
+
+        // add option to options
+        if (opt) {
+            options.insert(std::make_pair(opt_type, opt));
+        }
+        offset += opt_len;
+    }
+
+    return (offset);
+}
+
+size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffer& buf,
+                                     isc::dhcp::OptionCollection& options) {
+    size_t offset = 0;
+
+    // Get the list of stdandard option definitions.
+    const OptionDefContainer* option_defs = LibDHCP::getVendorOption4Defs(vendor_id);
+    // Get the search index #1. It allows to search for option definitions
+    // using option code.
+    const OptionDefContainerTypeIndex* idx = NULL;
+    if (option_defs) {
+        idx = &(option_defs->get<1>());
+    }
+
+    // The buffer being read comprises a set of options, each starting with
+    // a one-byte type code and a one-byte length field.
+    while (offset + 1 <= buf.size()) {
+
+        // Note that Vendor-Specific info option (RFC3925) has a different option
+        // format than Vendor-Spec info for DHCPv6. (there's additional layer of
+        // data-length
+        uint8_t data_len = buf[offset++];
+
+        if (offset + data_len > buf.size()) {
+            // Truncated data-option
+            return (offset);
+        }
+
+        uint8_t offset_end = offset + data_len;
+
+        // beginning of data-chunk parser
+        while (offset + 1 <= offset_end) {
+            uint8_t opt_type = buf[offset++];
+
+            // DHO_END is a special, one octet long option
+            if (opt_type == DHO_END)
+                return (offset); // just return. Don't need to add DHO_END option
+
+            // DHO_PAD is just a padding after DHO_END. Let's continue parsing
+            // in case we receive a message without DHO_END.
+            if (opt_type == DHO_PAD)
+                continue;
+
+            if (offset + 1 >= buf.size()) {
+                // opt_type must be cast to integer so as it is not treated as
+                // unsigned char value (a number is presented in error message).
+                isc_throw(OutOfRange, "Attempt to parse truncated option "
+                          << static_cast<int>(opt_type));
+            }
+
+            uint8_t opt_len =  buf[offset++];
+            if (offset + opt_len > buf.size()) {
+                isc_throw(OutOfRange, "Option parse failed. Tried to parse "
+                          << offset + opt_len << " bytes from " << buf.size()
+                          << "-byte long buffer.");
+            }
+
+            OptionPtr opt;
+            opt.reset();
+
+            if (idx) {
+                // Get all definitions with the particular option code. Note that option code
+                // is non-unique within this container however at this point we expect
+                // to get one option definition with the particular code. If more are
+                // returned we report an error.
+                const OptionDefContainerTypeRange& range = idx->equal_range(opt_type);
+            // Get the number of returned option definitions for the option code.
+                size_t num_defs = distance(range.first, range.second);
+
+                if (num_defs > 1) {
+                    // Multiple options of the same code are not supported right now!
+                    isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
+                              " for option type " << static_cast<int>(opt_type)
+                              << " returned. Currently it is not supported to initialize"
+                              << " multiple option definitions for the same option code."
+                              << " This will be supported once support for option spaces"
+                              << " is implemented");
+                } else if (num_defs == 1) {
+                    // The option definition has been found. Use it to create
+                    // the option instance from the provided buffer chunk.
+                    const OptionDefinitionPtr& def = *(range.first);
+                    assert(def);
+                    opt = def->optionFactory(Option::V4, opt_type,
+                                             buf.begin() + offset,
+                                             buf.begin() + offset + opt_len);
+                }
+            }
+
+            if (!opt) {
+                opt = OptionPtr(new Option(Option::V4, opt_type,
+                                           buf.begin() + offset,
+                                           buf.begin() + offset + opt_len));
+            }
+
+            options.insert(std::make_pair(opt_type, opt));
+            offset += opt_len;
+
+        } // end of data-chunk
+
+    }
+    return (offset);
+}
+
+
+
 void
 LibDHCP::packOptions(isc::util::OutputBuffer& buf,
-                     const Option::OptionCollection& options) {
-    for (Option::OptionCollection::const_iterator it = options.begin();
+                     const OptionCollection& options) {
+    for (OptionCollection::const_iterator it = options.begin();
          it != options.end(); ++it) {
         it->second->pack(buf);
     }
@@ -330,68 +620,35 @@ void LibDHCP::OptionFactoryRegister(Option::Universe u,
 
 void
 LibDHCP::initStdOptionDefs4() {
-    v4option_defs_.clear();
-
-    // Now let's add all option definitions.
-    for (int i = 0; i < OPTION_DEF_PARAMS_SIZE4; ++i) {
-        std::string encapsulates(OPTION_DEF_PARAMS4[i].encapsulates);
-        if (!encapsulates.empty() && OPTION_DEF_PARAMS4[i].array) {
-            isc_throw(isc::BadValue, "invalid standard option definition: "
-                      << "option with code '" << OPTION_DEF_PARAMS4[i].code
-                      << "' may not encapsulate option space '"
-                      << encapsulates << "' because the definition"
-                      << " indicates that this option comprises an array"
-                      << " of values");
-        }
-
-        // Depending whether the option encapsulates an option space or not
-        // we pick different constructor to create an instance of the option
-        // definition.
-        OptionDefinitionPtr definition;
-        if (encapsulates.empty()) {
-            // Option does not encapsulate any option space.
-            definition.reset(new OptionDefinition(OPTION_DEF_PARAMS4[i].name,
-                                                  OPTION_DEF_PARAMS4[i].code,
-                                                  OPTION_DEF_PARAMS4[i].type,
-                                                  OPTION_DEF_PARAMS4[i].array));
-
-        } else {
-            // Option does encapsulate an option space.
-            definition.reset(new OptionDefinition(OPTION_DEF_PARAMS4[i].name,
-                                                  OPTION_DEF_PARAMS4[i].code,
-                                                  OPTION_DEF_PARAMS4[i].type,
-                                                  OPTION_DEF_PARAMS4[i].encapsulates));
-
-        }
+    initOptionSpace(v4option_defs_, OPTION_DEF_PARAMS4, OPTION_DEF_PARAMS_SIZE4);
+}
 
-        for (int rec = 0; rec < OPTION_DEF_PARAMS4[i].records_size; ++rec) {
-            definition->addRecordField(OPTION_DEF_PARAMS4[i].records[rec]);
-        }
+void
+LibDHCP::initStdOptionDefs6() {
+    initOptionSpace(v6option_defs_, OPTION_DEF_PARAMS6, OPTION_DEF_PARAMS_SIZE6);
+}
 
-        // Sanity check if the option is valid.
-        try {
-            definition->validate();
-        } catch (const Exception& ex) {
-            // This is unlikely event that validation fails and may
-            // be only caused by programming error. To guarantee the
-            // data consistency we clear all option definitions that
-            // have been added so far and pass the exception forward.
-            v4option_defs_.clear();
-            throw;
-        }
-        v4option_defs_.push_back(definition);
-    }
+void
+LibDHCP::initVendorOptsDocsis4() {
+    initOptionSpace(vendor4_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V4_DEFS, DOCSIS3_V4_DEFS_SIZE);
 }
 
 void
-LibDHCP::initStdOptionDefs6() {
-    v6option_defs_.clear();
+LibDHCP::initVendorOptsDocsis6() {
+    vendor6_defs_[VENDOR_ID_CABLE_LABS] = OptionDefContainer();
+    initOptionSpace(vendor6_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V6_DEFS, DOCSIS3_V6_DEFS_SIZE);
+}
+
+void initOptionSpace(OptionDefContainer& defs,
+                     const OptionDefParams* params,
+                     size_t params_size) {
+    defs.clear();
 
-    for (int i = 0; i < OPTION_DEF_PARAMS_SIZE6; ++i) {
-        std::string encapsulates(OPTION_DEF_PARAMS6[i].encapsulates);
-        if (!encapsulates.empty() && OPTION_DEF_PARAMS6[i].array) {
+    for (int i = 0; i < params_size; ++i) {
+        std::string encapsulates(params[i].encapsulates);
+        if (!encapsulates.empty() && params[i].array) {
             isc_throw(isc::BadValue, "invalid standard option definition: "
-                      << "option with code '" << OPTION_DEF_PARAMS6[i].code
+                      << "option with code '" << params[i].code
                       << "' may not encapsulate option space '"
                       << encapsulates << "' because the definition"
                       << " indicates that this option comprises an array"
@@ -404,33 +661,33 @@ LibDHCP::initStdOptionDefs6() {
         OptionDefinitionPtr definition;
         if (encapsulates.empty()) {
             // Option does not encapsulate any option space.
-            definition.reset(new OptionDefinition(OPTION_DEF_PARAMS6[i].name,
-                                                  OPTION_DEF_PARAMS6[i].code,
-                                                  OPTION_DEF_PARAMS6[i].type,
-                                                  OPTION_DEF_PARAMS6[i].array));
+            definition.reset(new OptionDefinition(params[i].name,
+                                                  params[i].code,
+                                                  params[i].type,
+                                                  params[i].array));
         } else {
             // Option does encapsulate an option space.
-            definition.reset(new OptionDefinition(OPTION_DEF_PARAMS6[i].name,
-                                                  OPTION_DEF_PARAMS6[i].code,
-                                                  OPTION_DEF_PARAMS6[i].type,
-                                                  OPTION_DEF_PARAMS6[i].encapsulates));
+            definition.reset(new OptionDefinition(params[i].name,
+                                                  params[i].code,
+                                                  params[i].type,
+                                                  params[i].encapsulates));
 
         }
 
-        for (int rec = 0; rec < OPTION_DEF_PARAMS6[i].records_size; ++rec) {
-            definition->addRecordField(OPTION_DEF_PARAMS6[i].records[rec]);
+        for (int rec = 0; rec < params[i].records_size; ++rec) {
+            definition->addRecordField(params[i].records[rec]);
         }
 
         try {
             definition->validate();
-        } catch (const Exception& ex) {
+        } catch (const isc::Exception& ex) {
             // This is unlikely event that validation fails and may
             // be only caused by programming error. To guarantee the
             // data consistency we clear all option definitions that
             // have been added so far and pass the exception forward.
-            v6option_defs_.clear();
+            defs.clear();
             throw;
         }
-        v6option_defs_.push_back(definition);
+        defs.push_back(definition);
     }
 }
diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h
index 9d8bcab..230ba94 100644
--- a/src/lib/dhcp/libdhcp++.h
+++ b/src/lib/dhcp/libdhcp++.h
@@ -55,6 +55,17 @@ public:
     static OptionDefinitionPtr getOptionDef(const Option::Universe u,
                                             const uint16_t code);
 
+    /// @brief Returns vendor option definition for a given vendor-id and code
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param vendor_id enterprise-id for a given vendor
+    /// @param code option code
+    /// @return reference to an option definition being requested
+    /// or NULL pointer if option definition has not been found.
+    static OptionDefinitionPtr getVendorOptionDef(const Option::Universe u,
+                                                  const uint32_t vendor_id,
+                                                  const uint16_t code);
+
     /// @brief Check if the specified option is a standard option.
     ///
     /// @param u universe (V4 or V6)
@@ -100,7 +111,7 @@ public:
     /// @param buf output buffer (assembled options will be stored here)
     /// @param options collection of options to store to
     static void packOptions(isc::util::OutputBuffer& buf,
-                            const isc::dhcp::Option::OptionCollection& options);
+                            const isc::dhcp::OptionCollection& options);
 
     /// @brief Parses provided buffer as DHCPv4 options and creates Option objects.
     ///
@@ -108,10 +119,13 @@ public:
     /// in options container.
     ///
     /// @param buf Buffer to be parsed.
+    /// @param option_space A name of the option space which holds definitions
+    /// of to be used to parse options in the packets.
     /// @param options Reference to option container. Options will be
     ///        put here.
     static size_t unpackOptions4(const OptionBuffer& buf,
-                                 isc::dhcp::Option::OptionCollection& options);
+                                 const std::string& option_space,
+                                 isc::dhcp::OptionCollection& options);
 
     /// @brief Parses provided buffer as DHCPv6 options and creates Option objects.
     ///
@@ -125,6 +139,8 @@ public:
     /// iteration its content will be treated as buffer to be parsed.
     ///
     /// @param buf Buffer to be parsed.
+    /// @param option_space A name of the option space which holds definitions
+    /// of to be used to parse options in the packets.
     /// @param options Reference to option container. Options will be
     ///        put here.
     /// @param relay_msg_offset reference to a size_t structure. If specified,
@@ -133,7 +149,8 @@ public:
     ///        length of the relay_msg option will be stored in it.
     /// @return offset to the first byte after last parsed option
     static size_t unpackOptions6(const OptionBuffer& buf,
-                                 isc::dhcp::Option::OptionCollection& options,
+                                 const std::string& option_space,
+                                 isc::dhcp::OptionCollection& options,
                                  size_t* relay_msg_offset = 0,
                                  size_t* relay_msg_len = 0);
 
@@ -149,6 +166,49 @@ public:
                                       uint16_t type,
                                       Option::Factory * factory);
 
+    /// @brief Returns v4 option definitions for a given vendor
+    ///
+    /// @param vendor_id enterprise-id of a given vendor
+    /// @return a container for a given vendor (or NULL if not option
+    ///         definitions are defined)
+    static const OptionDefContainer*
+    getVendorOption4Defs(const uint32_t vendor_id);
+
+    /// @brief Returns v6 option definitions for a given vendor
+    ///
+    /// @param vendor_id enterprise-id of a given vendor
+    /// @return a container for a given vendor (or NULL if not option
+    ///         definitions are defined)
+    static const OptionDefContainer*
+    getVendorOption6Defs(const uint32_t vendor_id);
+
+    /// @brief Parses provided buffer as DHCPv6 vendor options and creates
+    ///        Option objects.
+    ///
+    /// Parses provided buffer and stores created Option objects
+    /// in options container.
+    ///
+    /// @param vendor_id enterprise-id of the vendor
+    /// @param buf Buffer to be parsed.
+    /// @param options Reference to option container. Options will be
+    ///        put here.
+    static size_t unpackVendorOptions6(const uint32_t vendor_id,
+                                       const OptionBuffer& buf,
+                                       isc::dhcp::OptionCollection& options);
+
+    /// @brief Parses provided buffer as DHCPv4 vendor options and creates
+    ///        Option objects.
+    ///
+    /// Parses provided buffer and stores created Option objects
+    /// in options container.
+    ///
+    /// @param vendor_id enterprise-id of the vendor
+    /// @param buf Buffer to be parsed.
+    /// @param options Reference to option container. Options will be
+    ///        put here.
+    static size_t unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffer& buf,
+                                       isc::dhcp::OptionCollection& options);
+
 private:
 
     /// Initialize standard DHCPv4 option definitions.
@@ -170,6 +230,10 @@ private:
     /// is incorrect. This is a programming error.
     static void initStdOptionDefs6();
 
+    static void initVendorOptsDocsis4();
+
+    static void initVendorOptsDocsis6();
+
     /// pointers to factories that produce DHCPv6 options
     static FactoryMap v4factories_;
 
@@ -181,6 +245,12 @@ private:
 
     /// Container with DHCPv6 option definitions.
     static OptionDefContainer v6option_defs_;
+
+    /// Container for v4 vendor option definitions
+    static VendorOptionDefContainers vendor4_defs_;
+
+    /// Container for v6 vendor option definitions
+    static VendorOptionDefContainers vendor6_defs_;
 };
 
 }
diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc
index cd6e313..f5ab75e 100644
--- a/src/lib/dhcp/option.cc
+++ b/src/lib/dhcp/option.cc
@@ -126,12 +126,19 @@ void Option::unpack(OptionBufferConstIter begin,
 
 void
 Option::unpackOptions(const OptionBuffer& buf) {
+    // If custom option parsing function has been set, use this function
+    // to parse options. Otherwise, use standard function from libdhcp++.
+    if (!callback_.empty()) {
+        callback_(buf, getEncapsulatedSpace(), options_, 0, 0);
+        return;
+    }
+
     switch (universe_) {
     case V4:
-        LibDHCP::unpackOptions4(buf, options_);
+        LibDHCP::unpackOptions4(buf, getEncapsulatedSpace(), options_);
         return;
     case V6:
-        LibDHCP::unpackOptions6(buf, options_);
+        LibDHCP::unpackOptions6(buf, getEncapsulatedSpace(), options_);
         return;
     default:
         isc_throw(isc::BadValue, "Invalid universe type " << universe_);
@@ -146,7 +153,7 @@ uint16_t Option::len() {
     int length = getHeaderLen() + data_.size();
 
     // ... and sum of lengths of all suboptions
-    for (Option::OptionCollection::iterator it = options_.begin();
+    for (OptionCollection::iterator it = options_.begin();
          it != options_.end();
          ++it) {
         length += (*it).second->len();
@@ -169,7 +176,7 @@ Option::valid() {
 }
 
 OptionPtr Option::getOption(uint16_t opt_type) {
-    isc::dhcp::Option::OptionCollection::const_iterator x =
+    isc::dhcp::OptionCollection::const_iterator x =
         options_.find(opt_type);
     if ( x != options_.end() ) {
         return (*x).second;
@@ -178,7 +185,7 @@ OptionPtr Option::getOption(uint16_t opt_type) {
 }
 
 bool Option::delOption(uint16_t opt_type) {
-    isc::dhcp::Option::OptionCollection::iterator x = options_.find(opt_type);
+    isc::dhcp::OptionCollection::iterator x = options_.find(opt_type);
     if ( x != options_.end() ) {
         options_.erase(x);
         return true; // delete successful
diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h
index 9abb4b3..dbdb3af 100644
--- a/src/lib/dhcp/option.h
+++ b/src/lib/dhcp/option.h
@@ -17,6 +17,7 @@
 
 #include <util/buffer.h>
 
+#include <boost/function.hpp>
 #include <boost/shared_ptr.hpp>
 
 #include <map>
@@ -44,6 +45,34 @@ typedef boost::shared_ptr<OptionBuffer> OptionBufferPtr;
 class Option;
 typedef boost::shared_ptr<Option> OptionPtr;
 
+/// A collection of DHCP (v4 or v6) options
+typedef std::multimap<unsigned int, OptionPtr> OptionCollection;
+
+/// @brief This type describes a callback function to parse options from buffer.
+///
+/// @note The last two parameters should be specified in the callback function
+/// parameters list only if DHCPv6 options are parsed. Exclude these parameters
+/// from the callback function defined to parse DHCPv4 options.
+///
+/// @param buffer A buffer holding options to be parsed.
+/// @param encapsulated_space A name of the option space to which options being
+/// parsed belong.
+/// @param [out] options A container to which parsed options should be appended.
+/// @param relay_msg_offset A pointer to a size_t value. It indicates the
+/// offset to beginning of relay_msg option. This parameter should be specified
+/// for DHCPv6 options only.
+/// @param relay_msg_len A pointer to a size_t value. It holds the length of
+/// of the relay_msg option. This parameter should be specified for DHCPv6
+/// options only.
+///
+/// @return An offset to the first byte after last parsed option.
+typedef boost::function< size_t(const OptionBuffer& buffer,
+                                const std::string encapsulated_space,
+                                OptionCollection& options,
+                                size_t* relay_msg_offset,
+                                size_t* relay_msg_len)
+                         > UnpackOptionsCallback;
+
 
 class Option {
 public:
@@ -56,8 +85,6 @@ public:
     /// defines option universe DHCPv4 or DHCPv6
     enum Universe { V4, V6 };
 
-    /// a collection of DHCPv6 options
-    typedef std::multimap<unsigned int, OptionPtr> OptionCollection;
 
     /// @brief a factory function prototype
     ///
@@ -290,6 +317,29 @@ public:
         data_.assign(first, last);
     }
 
+    /// @brief Sets the name of the option space encapsulated by this option.
+    ///
+    /// @param encapsulated_space name of the option space encapsulated by
+    /// this option.
+    void setEncapsulatedSpace(const std::string& encapsulated_space) {
+        encapsulated_space_ = encapsulated_space;
+    }
+
+    /// @brief Returns the name of the option space encapsulated by this option.
+    ///
+    /// @return name of the option space encapsulated by this option.
+    std::string getEncapsulatedSpace() const {
+        return (encapsulated_space_);
+    }
+
+    /// @brief Set callback function to be used to parse options.
+    ///
+    /// @param callback An instance of the callback function or NULL to
+    /// uninstall callback.
+    void setCallback(UnpackOptionsCallback callback) {
+        callback_ = callback;
+    }
+
     /// just to force that every option has virtual dtor
     virtual ~Option();
 
@@ -372,6 +422,12 @@ protected:
     /// collection for storing suboptions
     OptionCollection options_;
 
+    /// Name of the option space being encapsulated by this option.
+    std::string encapsulated_space_;
+
+    /// A callback to be called to unpack options from the packet.
+    UnpackOptionsCallback callback_;
+
     /// @todo probably 2 different containers have to be used for v4 (unique
     /// options) and v6 (options with the same type can repeat)
 };
diff --git a/src/lib/dhcp/option6_ia.cc b/src/lib/dhcp/option6_ia.cc
index 64c2936..825a5bf 100644
--- a/src/lib/dhcp/option6_ia.cc
+++ b/src/lib/dhcp/option6_ia.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -30,10 +30,28 @@ namespace dhcp {
 
 Option6IA::Option6IA(uint16_t type, uint32_t iaid)
     :Option(Option::V6, type), iaid_(iaid), t1_(0), t2_(0) {
+
+    // IA_TA has different layout than IA_NA and IA_PD. We can't sue this class
+    if (type == D6O_IA_TA) {
+        isc_throw(BadValue, "Can't use Option6IA for IA_TA as it has "
+                  "a different layout");
+    }
+
+    setEncapsulatedSpace("dhcp6");
 }
 
-Option6IA::Option6IA(uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
+Option6IA::Option6IA(uint16_t type, OptionBufferConstIter begin,
+                     OptionBufferConstIter end)
     :Option(Option::V6, type) {
+
+    // IA_TA has different layout than IA_NA and IA_PD. We can't use this class
+    if (type == D6O_IA_TA) {
+        isc_throw(BadValue, "Can't use Option6IA for IA_TA as it has "
+                  "a different layout");
+    }
+
+    setEncapsulatedSpace("dhcp6");
+
     unpack(begin, end);
 }
 
@@ -99,7 +117,7 @@ uint16_t Option6IA::len() {
         OPTION6_IA_LEN  /* option content (12) */;
 
     // length of all suboptions
-    for (Option::OptionCollection::iterator it = options_.begin();
+    for (OptionCollection::iterator it = options_.begin();
          it != options_.end();
          ++it) {
         length += (*it).second->len();
diff --git a/src/lib/dhcp/option6_iaaddr.cc b/src/lib/dhcp/option6_iaaddr.cc
index d0ba087..39efa61 100644
--- a/src/lib/dhcp/option6_iaaddr.cc
+++ b/src/lib/dhcp/option6_iaaddr.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -35,11 +35,16 @@ Option6IAAddr::Option6IAAddr(uint16_t type, const isc::asiolink::IOAddress& addr
                              uint32_t pref, uint32_t valid)
     :Option(V6, type), addr_(addr), preferred_(pref),
      valid_(valid) {
+    setEncapsulatedSpace("dhcp6");
+    if (!addr.isV6()) {
+        isc_throw(isc::BadValue, addr_.toText() << " is not an IPv6 address");
+    }
 }
 
 Option6IAAddr::Option6IAAddr(uint32_t type, OptionBuffer::const_iterator begin,
                              OptionBuffer::const_iterator end)
     :Option(V6, type), addr_("::") {
+    setEncapsulatedSpace("dhcp6");
     unpack(begin, end);
 }
 
@@ -107,7 +112,7 @@ uint16_t Option6IAAddr::len() {
     // length of all suboptions
     // TODO implement:
     // protected: unsigned short Option::lenHelper(int header_size);
-    for (Option::OptionCollection::iterator it = options_.begin();
+    for (OptionCollection::iterator it = options_.begin();
          it != options_.end();
          ++it) {
         length += (*it).second->len();
diff --git a/src/lib/dhcp/option6_iaaddr.h b/src/lib/dhcp/option6_iaaddr.h
index cb85bed..c188f88 100644
--- a/src/lib/dhcp/option6_iaaddr.h
+++ b/src/lib/dhcp/option6_iaaddr.h
@@ -33,7 +33,9 @@ public:
     /// length of the fixed part of the IAADDR option
     static const size_t OPTION6_IAADDR_LEN = 24;
 
-    /// @brief Ctor, used for options constructed (during transmission).
+    /// @brief Constructor, used for options constructed (during transmission).
+    ///
+    /// @throw BadValue if specified addr is not IPv6
     ///
     /// @param type option type
     /// @param addr reference to an address
@@ -42,7 +44,9 @@ public:
     Option6IAAddr(uint16_t type, const isc::asiolink::IOAddress& addr,
                   uint32_t preferred, uint32_t valid);
 
-    /// @brief ctor, used for received options.
+    /// @brief Constructor, used for received options.
+    ///
+    /// @throw OutOfRange if specified option is truncated
     ///
     /// @param type option type
     /// @param begin iterator to first byte of option data
diff --git a/src/lib/dhcp/option6_iaprefix.cc b/src/lib/dhcp/option6_iaprefix.cc
new file mode 100644
index 0000000..a7776d7
--- /dev/null
+++ b/src/lib/dhcp/option6_iaprefix.cc
@@ -0,0 +1,126 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <asiolink/io_address.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option6_iaprefix.h>
+#include <exceptions/exceptions.h>
+#include <util/io_utilities.h>
+
+#include <sstream>
+
+#include <stdint.h>
+#include <arpa/inet.h>
+
+using namespace std;
+using namespace isc::asiolink;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+Option6IAPrefix::Option6IAPrefix(uint16_t type, const isc::asiolink::IOAddress& prefix,
+                                 uint8_t prefix_len, uint32_t pref, uint32_t valid)
+    :Option6IAAddr(type, prefix, pref, valid), prefix_len_(prefix_len) {
+    setEncapsulatedSpace("dhcp6");
+    // Option6IAAddr will check if prefix is IPv6 and will throw if it is not
+    if (prefix_len > 128) {
+        isc_throw(BadValue, prefix_len << " is not a valid prefix length. "
+                  << "Allowed range is 0..128");
+    }
+}
+
+Option6IAPrefix::Option6IAPrefix(uint32_t type, OptionBuffer::const_iterator begin,
+                             OptionBuffer::const_iterator end)
+    :Option6IAAddr(type, begin, end) {
+    setEncapsulatedSpace("dhcp6");
+    unpack(begin, end);
+}
+
+void Option6IAPrefix::pack(isc::util::OutputBuffer& buf) {
+    if (!addr_.isV6()) {
+        isc_throw(isc::BadValue, addr_.toText() << " is not an IPv6 address");
+    }
+
+    buf.writeUint16(type_);
+
+    // len() returns complete option length. len field contains
+    // length without 4-byte option header
+    buf.writeUint16(len() - getHeaderLen());
+
+    buf.writeUint32(preferred_);
+    buf.writeUint32(valid_);
+    buf.writeUint8(prefix_len_);
+
+    buf.writeData(&addr_.toBytes()[0], isc::asiolink::V6ADDRESS_LEN);
+
+    // store encapsulated options (the only defined so far is PD_EXCLUDE)
+    packOptions(buf);
+}
+
+void Option6IAPrefix::unpack(OptionBuffer::const_iterator begin,
+                      OptionBuffer::const_iterator end) {
+    if ( distance(begin, end) < OPTION6_IAPREFIX_LEN) {
+        isc_throw(OutOfRange, "Option " << type_ << " truncated");
+    }
+
+    preferred_ = readUint32( &(*begin) );
+    begin += sizeof(uint32_t);
+
+    valid_ = readUint32( &(*begin) );
+    begin += sizeof(uint32_t);
+
+    prefix_len_ = *begin;
+    begin += sizeof(uint8_t);
+
+    // 16 bytes: IPv6 address
+    addr_ = IOAddress::fromBytes(AF_INET6, &(*begin));
+    begin += V6ADDRESS_LEN;
+
+    // unpack encapsulated options (the only defined so far is PD_EXCLUDE)
+    unpackOptions(OptionBuffer(begin, end));
+}
+
+std::string Option6IAPrefix::toText(int indent /* =0 */) {
+    stringstream tmp;
+    for (int i=0; i<indent; i++)
+        tmp << " ";
+
+    tmp << "type=" << type_ << "(IAPREFIX) prefix=" << addr_.toText() << "/"
+        << prefix_len_ << ", preferred-lft=" << preferred_ << ", valid-lft="
+        << valid_ << endl;
+
+    for (OptionCollection::const_iterator opt=options_.begin();
+         opt!=options_.end();
+         ++opt) {
+        tmp << (*opt).second->toText(indent + 2);
+    }
+    return tmp.str();
+}
+
+uint16_t Option6IAPrefix::len() {
+
+    uint16_t length = OPTION6_HDR_LEN + OPTION6_IAPREFIX_LEN;
+
+    // length of all suboptions
+    for (OptionCollection::const_iterator it = options_.begin();
+         it != options_.end(); ++it) {
+        length += (*it).second->len();
+    }
+    return (length);
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcp/option6_iaprefix.h b/src/lib/dhcp/option6_iaprefix.h
new file mode 100644
index 0000000..0a790be
--- /dev/null
+++ b/src/lib/dhcp/option6_iaprefix.h
@@ -0,0 +1,109 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef OPTION6_IAPREFIX_H
+#define OPTION6_IAPREFIX_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option.h>
+
+namespace isc {
+namespace dhcp {
+
+
+/// @brief Class that represents IAPREFIX option in DHCPv6
+///
+/// It is based on a similar class that handles addresses. There are major
+/// differences, though. The fields are in different order. There is also
+/// additional prefix length field.
+///
+/// It should be noted that to get a full prefix (2 values: base prefix, and
+/// a prefix length) 2 methods are used: getAddress() and getLength(). Although
+/// using getAddress() to obtain base prefix is somewhat counter-intuitive at
+/// first, it becomes obvious when one realizes that an address is a special
+/// case of a prefix with /128. It makes everyone's like much easier, because
+/// the base prefix doubles as a regular address in many cases, e.g. when
+/// searching for a lease.
+class Option6IAPrefix : public Option6IAAddr {
+
+public:
+    /// length of the fixed part of the IAPREFIX option
+    static const size_t OPTION6_IAPREFIX_LEN = 25;
+
+    /// @brief Constructor, used for options constructed (during transmission).
+    ///
+    /// @param type option type
+    /// @param addr reference to an address
+    /// @param prefix_length length (1-128)
+    /// @param preferred address preferred lifetime (in seconds)
+    /// @param valid address valid lifetime (in seconds)
+    Option6IAPrefix(uint16_t type, const isc::asiolink::IOAddress& addr,
+                    uint8_t prefix_length, uint32_t preferred, uint32_t valid);
+
+    /// @brief Constructor, used for received options.
+    ///
+    /// @throw OutOfRange if buffer is too short
+    ///
+    /// @param type option type
+    /// @param begin iterator to first byte of option data
+    /// @param end iterator to end of option data (first byte after option end)
+    Option6IAPrefix(uint32_t type, OptionBuffer::const_iterator begin,
+                    OptionBuffer::const_iterator end);
+
+    /// @brief Writes option in wire-format.
+    ///
+    /// Writes option in wire-format to buf, returns pointer to first unused
+    /// byte after stored option.
+    ///
+    /// @throw BadValue if the address is not IPv6
+    ///
+    /// @param buf pointer to a buffer
+    void pack(isc::util::OutputBuffer& buf);
+
+    /// @brief Parses received buffer.
+    ///
+    /// @throw OutOfRange when buffer is shorter than 25 bytes
+    ///
+    /// @param begin iterator to first byte of option data
+    /// @param end iterator to end of option data (first byte after option end)
+    virtual void unpack(OptionBufferConstIter begin,
+                        OptionBufferConstIter end);
+
+    /// Returns string representation of the option.
+    ///
+    /// @param indent number of spaces before printing text
+    ///
+    /// @return string with text representation.
+    virtual std::string toText(int indent = 0);
+
+    /// sets address in this option.
+    ///
+    /// @param addr address to be sent in this option
+    void setPrefix(const isc::asiolink::IOAddress& prefix,
+                   uint8_t length) { addr_ = prefix; prefix_len_ = length; }
+
+    uint8_t getLength() const { return prefix_len_; }
+
+    /// returns data length (data length + DHCPv4/DHCPv6 option header)
+    virtual uint16_t len();
+
+protected:
+    uint8_t prefix_len_;
+};
+
+} // isc::dhcp namespace
+} // isc namespace
+
+#endif // OPTION_IA_H
diff --git a/src/lib/dhcp/option_custom.cc b/src/lib/dhcp/option_custom.cc
index 12145fc..278c0c9 100644
--- a/src/lib/dhcp/option_custom.cc
+++ b/src/lib/dhcp/option_custom.cc
@@ -24,6 +24,7 @@ OptionCustom::OptionCustom(const OptionDefinition& def,
                            Universe u)
     : Option(u, def.getCode(), OptionBuffer()),
       definition_(def) {
+    setEncapsulatedSpace(def.getEncapsulatedSpace());
     createBuffers();
 }
 
@@ -32,6 +33,7 @@ OptionCustom::OptionCustom(const OptionDefinition& def,
                            const OptionBuffer& data)
     : Option(u, def.getCode(), data.begin(), data.end()),
       definition_(def) {
+    setEncapsulatedSpace(def.getEncapsulatedSpace());
     createBuffers(getData());
 }
 
@@ -41,6 +43,7 @@ OptionCustom::OptionCustom(const OptionDefinition& def,
                            OptionBufferConstIter last)
     : Option(u, def.getCode(), first, last),
       definition_(def) {
+    setEncapsulatedSpace(def.getEncapsulatedSpace());
     createBuffers(getData());
 }
 
@@ -246,6 +249,12 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
             // Proceed to the next data field.
             data += data_size;
         }
+
+        // Unpack suboptions if any.
+        if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
+            unpackOptions(OptionBuffer(data, data_buf.end()));
+        }
+
     } else if (data_type != OPT_EMPTY_TYPE) {
         // If data_type value is other than OPT_RECORD_TYPE, our option is
         // empty (have no data at all) or it comprises one or more
@@ -313,9 +322,20 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
             }
             if (data_size > 0) {
                 buffers.push_back(OptionBuffer(data, data + data_size));
+                data += data_size;
             } else {
                 isc_throw(OutOfRange, "option buffer truncated");
             }
+
+            // Unpack suboptions if any.
+            if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
+                unpackOptions(OptionBuffer(data, data_buf.end()));
+            }
+        }
+    } else if (data_type == OPT_EMPTY_TYPE) {
+        // Unpack suboptions if any.
+        if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
+            unpackOptions(OptionBuffer(data, data_buf.end()));
         }
     }
     // If everything went ok we can replace old buffer set with new ones.
@@ -522,7 +542,7 @@ OptionCustom::len() {
     }
 
     // ... and lengths of all suboptions
-    for (OptionCustom::OptionCollection::iterator it = options_.begin();
+    for (OptionCollection::iterator it = options_.begin();
          it != options_.end();
          ++it) {
         length += (*it).second->len();
diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc
index be24021..be1a5e7 100644
--- a/src/lib/dhcp/option_definition.cc
+++ b/src/lib/dhcp/option_definition.cc
@@ -19,6 +19,7 @@
 #include <dhcp/option6_addrlst.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
 #include <dhcp/option6_client_fqdn.h>
 #include <dhcp/option_custom.h>
 #include <dhcp/option_definition.h>
@@ -26,6 +27,7 @@
 #include <dhcp/option_int_array.h>
 #include <dhcp/option_space.h>
 #include <dhcp/option_string.h>
+#include <dhcp/option_vendor.h>
 #include <util/encode/hex.h>
 #include <util/strutil.h>
 #include <boost/algorithm/string/classification.hpp>
@@ -113,42 +115,67 @@ OptionDefinition::addRecordField(const OptionDataType data_type) {
 OptionPtr
 OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
                                 OptionBufferConstIter begin,
-                                OptionBufferConstIter end) const {
+                                OptionBufferConstIter end,
+                                UnpackOptionsCallback callback) const {
+
     try {
+        // Some of the options are represented by the specialized classes derived
+        // from Option class (e.g. IA_NA, IAADDR). Although, they can be also
+        // represented by the generic classes, we want the object of the specialized
+        // type to be returned. Therefore, we first check that if we are dealing
+        // with such an option. If the instance is returned we just exit at this
+        // point. If not, we will search for a generic option type to return.
+        OptionPtr option = factorySpecialFormatOption(u, begin, end, callback);
+        if (option) {
+            return (option);
+        }
+
         switch(type_) {
         case OPT_EMPTY_TYPE:
-            return (factoryEmpty(u, type));
+            if (getEncapsulatedSpace().empty()) {
+                    return (factoryEmpty(u, type));
+            } else {
+                return (OptionPtr(new OptionCustom(*this, u, begin, end)));
+            }
 
         case OPT_BINARY_TYPE:
             return (factoryGeneric(u, type, begin, end));
 
         case OPT_UINT8_TYPE:
-            return (array_type_ ? factoryGeneric(u, type, begin, end) :
-                    factoryInteger<uint8_t>(u, type, begin, end));
+            return (array_type_ ?
+                    factoryIntegerArray<uint8_t>(u, type, begin, end) :
+                    factoryInteger<uint8_t>(u, type, getEncapsulatedSpace(),
+                                            begin, end, callback));
 
         case OPT_INT8_TYPE:
-            return (array_type_ ? factoryGeneric(u, type, begin, end) :
-                    factoryInteger<int8_t>(u, type, begin, end));
+            return (array_type_ ?
+                    factoryIntegerArray<int8_t>(u, type, begin, end) :
+                    factoryInteger<int8_t>(u, type, getEncapsulatedSpace(),
+                                           begin, end, callback));
 
         case OPT_UINT16_TYPE:
             return (array_type_ ?
                     factoryIntegerArray<uint16_t>(u, type, begin, end) :
-                    factoryInteger<uint16_t>(u, type, begin, end));
+                    factoryInteger<uint16_t>(u, type, getEncapsulatedSpace(),
+                                             begin, end, callback));
 
         case OPT_INT16_TYPE:
             return (array_type_ ?
                     factoryIntegerArray<uint16_t>(u, type, begin, end) :
-                    factoryInteger<int16_t>(u, type, begin, end));
+                    factoryInteger<int16_t>(u, type, getEncapsulatedSpace(),
+                                            begin, end, callback));
 
         case OPT_UINT32_TYPE:
             return (array_type_ ?
                     factoryIntegerArray<uint32_t>(u, type, begin, end) :
-                    factoryInteger<uint32_t>(u, type, begin, end));
+                    factoryInteger<uint32_t>(u, type, getEncapsulatedSpace(),
+                                             begin, end, callback));
 
         case OPT_INT32_TYPE:
             return (array_type_ ?
                     factoryIntegerArray<uint32_t>(u, type, begin, end) :
-                    factoryInteger<int32_t>(u, type, begin, end));
+                    factoryInteger<int32_t>(u, type, getEncapsulatedSpace(),
+                                            begin, end, callback));
 
         case OPT_IPV4_ADDRESS_TYPE:
             // If definition specifies that an option is an array
@@ -173,37 +200,10 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
             return (OptionPtr(new OptionString(u, type, begin, end)));
 
         default:
-            if (u == Option::V6) {
-                if ((code_ == D6O_IA_NA || code_ == D6O_IA_PD) &&
-                    haveIA6Format()) {
-                    // Return Option6IA instance for IA_PD and IA_NA option
-                    // types only. We don't want to return Option6IA for other
-                    // options that comprise 3 UINT32 data fields because
-                    // Option6IA accessors' and modifiers' names are derived
-                    // from the IA_NA and IA_PD options' field names: IAID,
-                    // T1, T2. Using functions such as getIAID, getT1 etc. for
-                    // options other than IA_NA and IA_PD would be bad practice
-                    // and cause confusion.
-                    return (factoryIA6(type, begin, end));
-
-                } else if (code_ == D6O_IAADDR && haveIAAddr6Format()) {
-                    // Rerurn Option6IAAddr option instance for the IAADDR
-                    // option only for the same reasons as described in
-                    // for IA_NA and IA_PD above.
-                    return (factoryIAAddr6(type, begin, end));
-                } else if (code_ == D6O_CLIENT_FQDN && haveClientFqdnFormat()) {
-                    // FQDN option requires special processing. Thus, there is
-                    // a specialized class to handle it.
-                    return (OptionPtr(new Option6ClientFqdn(begin, end)));
-                }
-            } else {
-                if ((code_ == DHO_FQDN) && haveFqdn4Format()) {
-                    return (OptionPtr(new Option4ClientFqdn(begin, end)));
-                }
-            }
+            // Do nothing. We will return generic option a few lines down.
+            ;
         }
         return (OptionPtr(new OptionCustom(*this, u, begin, end)));
-
     } catch (const Exception& ex) {
         isc_throw(InvalidOptionValue, ex.what());
     }
@@ -211,8 +211,9 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
 
 OptionPtr
 OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
-                                const OptionBuffer& buf) const {
-    return (optionFactory(u, type, buf.begin(), buf.end()));
+                                const OptionBuffer& buf,
+                                UnpackOptionsCallback callback) const {
+    return (optionFactory(u, type, buf.begin(), buf.end(), callback));
 }
 
 OptionPtr
@@ -361,6 +362,16 @@ OptionDefinition::haveIAAddr6Format() const {
 }
 
 bool
+OptionDefinition::haveIAPrefix6Format() const {
+    return (haveType(OPT_RECORD_TYPE) &&
+            record_fields_.size() == 4 &&
+            record_fields_[0] == OPT_UINT32_TYPE &&
+            record_fields_[1] == OPT_UINT32_TYPE &&
+            record_fields_[2] == OPT_UINT8_TYPE &&
+            record_fields_[3] == OPT_IPV6_ADDRESS_TYPE);
+}
+
+bool
 OptionDefinition::haveFqdn4Format() const {
     return (haveType(OPT_RECORD_TYPE) &&
             record_fields_.size() == 4 &&
@@ -378,6 +389,16 @@ OptionDefinition::haveClientFqdnFormat() const {
             (record_fields_[1] == OPT_FQDN_TYPE));
 }
 
+bool
+OptionDefinition::haveVendor4Format() const {
+    return (true);
+}
+
+bool
+OptionDefinition::haveVendor6Format() const {
+    return  (getType() == OPT_UINT32_TYPE && !getEncapsulatedSpace().empty());
+}
+
 template<typename T>
 T
 OptionDefinition::lexicalCastWithRangeCheck(const std::string& value_str)
@@ -560,6 +581,65 @@ OptionDefinition::factoryIAAddr6(uint16_t type,
     return (option);
 }
 
+OptionPtr
+OptionDefinition::factoryIAPrefix6(uint16_t type,
+                                 OptionBufferConstIter begin,
+                                 OptionBufferConstIter end) {
+    if (std::distance(begin, end) < Option6IAPrefix::OPTION6_IAPREFIX_LEN) {
+        isc_throw(isc::OutOfRange,
+                  "input option buffer has invalid size, expected at least "
+                  << Option6IAPrefix::OPTION6_IAPREFIX_LEN << " bytes");
+    }
+    boost::shared_ptr<Option6IAPrefix> option(new Option6IAPrefix(type, begin,
+                                                                  end));
+    return (option);
+}
+
+OptionPtr
+OptionDefinition::factorySpecialFormatOption(Option::Universe u,
+                                             OptionBufferConstIter begin,
+                                             OptionBufferConstIter end,
+                                             UnpackOptionsCallback) const {
+    if (u == Option::V6) {
+        if ((getCode() == D6O_IA_NA || getCode() == D6O_IA_PD) &&
+            haveIA6Format()) {
+            // Return Option6IA instance for IA_PD and IA_NA option
+            // types only. We don't want to return Option6IA for other
+            // options that comprise 3 UINT32 data fields because
+            // Option6IA accessors' and modifiers' names are derived
+            // from the IA_NA and IA_PD options' field names: IAID,
+            // T1, T2. Using functions such as getIAID, getT1 etc. for
+            // options other than IA_NA and IA_PD would be bad practice
+            // and cause confusion.
+            return (factoryIA6(getCode(), begin, end));
+
+        } else if (getCode() == D6O_IAADDR && haveIAAddr6Format()) {
+            // Rerurn Option6IAAddr option instance for the IAADDR
+            // option only for the same reasons as described in
+            // for IA_NA and IA_PD above.
+            return (factoryIAAddr6(getCode(), begin, end));
+        } else if (getCode() == D6O_IAPREFIX && haveIAPrefix6Format()) {
+            return (factoryIAPrefix6(getCode(), begin, end));
+        } else if (getCode() == D6O_CLIENT_FQDN && haveClientFqdnFormat()) {
+            // FQDN option requires special processing. Thus, there is
+            // a specialized class to handle it.
+            return (OptionPtr(new Option6ClientFqdn(begin, end)));
+        } else if (getCode() == D6O_VENDOR_OPTS && haveVendor6Format()) {
+            // Vendor-Specific Information.
+            return (OptionPtr(new OptionVendor(Option::V6, begin, end)));
+        }
+    } else {
+        if ((getCode() == DHO_FQDN) && haveFqdn4Format()) {
+            return (OptionPtr(new Option4ClientFqdn(begin, end)));
+
+        } else if (getCode() == DHO_VIVSO_SUBOPTIONS && haveVendor4Format()) {
+            // Vendor-Specific Information.
+            return (OptionPtr(new OptionVendor(Option::V4, begin, end)));
+
+        }
+    }
+    return (OptionPtr());
+}
 
 } // end of isc::dhcp namespace
 } // end of isc namespace
diff --git a/src/lib/dhcp/option_definition.h b/src/lib/dhcp/option_definition.h
index 884a4ad..25ce5e2 100644
--- a/src/lib/dhcp/option_definition.h
+++ b/src/lib/dhcp/option_definition.h
@@ -23,6 +23,7 @@
 #include <boost/multi_index/sequenced_index.hpp>
 #include <boost/multi_index_container.hpp>
 #include <boost/shared_ptr.hpp>
+#include <map>
 
 namespace isc {
 namespace dhcp {
@@ -275,6 +276,11 @@ public:
     /// @return true if specified format is IAADDR option format.
     bool haveIAAddr6Format() const;
 
+    /// @brief Check if specified format is IAPREFIX option format.
+    ///
+    /// @return true if specified format is IAPREFIX option format.
+    bool haveIAPrefix6Format() const;
+
     /// @brief Check if specified format is OPTION_CLIENT_FQDN option format.
     ///
     /// @return true of specified format is OPTION_CLIENT_FQDN option format,
@@ -296,6 +302,28 @@ public:
     /// %Option.
     bool haveFqdn4Format() const;
 
+    /// @brief Check if the option has format of Vendor-Identifying Vendor
+    /// Specific Options.
+    ///
+    /// @return Always true.
+    /// @todo The Vendor-Identifying Vendor-Specific Option has a complex format
+    /// which we do not support here. Therefore it is not really possible to
+    /// check that the current definition is valid. We may need to add support
+    /// for such option format or simply do not check the format for certain
+    /// options, e.g. vendor options, IA_NA, IAADDR and always return objects
+    /// of the certain type.
+    bool haveVendor4Format() const;
+
+    /// @brief Check if option has a format of the Vendor-Specific Information
+    /// %Option.
+    ///
+    /// The Vendor-Specific Information %Option comprises 32-bit enterprise id
+    /// and the suboptions.
+    ///
+    /// @return true if option definition conforms to the format of the
+    /// Vendor-Specific Information %Option.
+    bool haveVendor6Format() const;
+
     /// @brief Option factory.
     ///
     /// This function creates an instance of DHCP option using
@@ -310,12 +338,17 @@ public:
     /// @param type option type.
     /// @param begin beginning of the option buffer.
     /// @param end end of the option buffer.
+    /// @param callback An instance of the function which parses packet options.
+    /// If this is set to non NULL value this function will be used instead of
+    /// @c isc::dhcp::LibDHCP::unpackOptions6 and
+    /// isc::dhcp::LibDHCP::unpackOptions4.
     ///
     /// @return instance of the DHCP option.
     /// @throw InvalidOptionValue if data for the option is invalid.
     OptionPtr optionFactory(Option::Universe u, uint16_t type,
                             OptionBufferConstIter begin,
-                            OptionBufferConstIter end) const;
+                            OptionBufferConstIter end,
+                            UnpackOptionsCallback callback = NULL) const;
 
     /// @brief Option factory.
     ///
@@ -330,11 +363,16 @@ public:
     /// @param u option universe (V4 or V6).
     /// @param type option type.
     /// @param buf option buffer.
+    /// @param callback An instance of the function which parses packet options.
+    /// If this is set to non NULL value this function will be used instead of
+    /// @c isc::dhcp::LibDHCP::unpackOptions6 and
+    /// isc::dhcp::LibDHCP::unpackOptions4.
     ///
     /// @return instance of the DHCP option.
     /// @throw InvalidOptionValue if data for the option is invalid.
     OptionPtr optionFactory(Option::Universe u, uint16_t type,
-                            const OptionBuffer& buf = OptionBuffer()) const;
+                            const OptionBuffer& buf = OptionBuffer(),
+                            UnpackOptionsCallback callback = NULL) const;
 
     /// @brief Option factory.
     ///
@@ -415,7 +453,6 @@ public:
     ///
     /// @throw isc::OutOfRange if provided option buffer is too short or
     /// too long. Expected size is 12 bytes.
-    /// @throw isc::BadValue if specified universe value is not V6.
     static OptionPtr factoryIA6(uint16_t type,
                                 OptionBufferConstIter begin,
                                 OptionBufferConstIter end);
@@ -428,25 +465,48 @@ public:
     ///
     /// @throw isc::OutOfRange if provided option buffer is too short or
     /// too long. Expected size is 24 bytes.
-    /// @throw isc::BadValue if specified universe value is not V6.
     static OptionPtr factoryIAAddr6(uint16_t type,
                                     OptionBufferConstIter begin,
                                     OptionBufferConstIter end);
 
+    /// @brief Factory for IAPREFIX-type of option.
+    ///
+    /// @param type option type.
+    /// @param begin iterator pointing to the beginning of the buffer.
+    /// @param end iterator pointing to the end of the buffer.
+    ///
+    /// @throw isc::OutOfRange if provided option buffer is too short.
+    /// Expected minimum size is 25 bytes.
+    static OptionPtr factoryIAPrefix6(uint16_t type,
+                                      OptionBufferConstIter begin,
+                                      OptionBufferConstIter end);
+
     /// @brief Factory function to create option with integer value.
     ///
     /// @param u universe (V4 or V6).
     /// @param type option type.
+    /// @param encapsulated_space An option space being encapsulated by the
+    /// options created by this factory function. The options which belong to
+    /// encapsulated option space are sub options of this option.
     /// @param begin iterator pointing to the beginning of the buffer.
     /// @param end iterator pointing to the end of the buffer.
+    /// @param callback An instance of the function which parses packet options.
+    /// If this is set to non NULL value this function will be used instead of
+    /// @c isc::dhcp::LibDHCP::unpackOptions6 and
+    /// isc::dhcp::LibDHCP::unpackOptions4.
     /// @tparam T type of the data field (must be one of the uintX_t or intX_t).
     ///
     /// @throw isc::OutOfRange if provided option buffer length is invalid.
     template<typename T>
     static OptionPtr factoryInteger(Option::Universe u, uint16_t type,
+                                    const std::string& encapsulated_space,
                                     OptionBufferConstIter begin,
-                                    OptionBufferConstIter end) {
-        OptionPtr option(new OptionInt<T>(u, type, begin, end));
+                                    OptionBufferConstIter end,
+                                    UnpackOptionsCallback callback) {
+        OptionPtr option(new OptionInt<T>(u, type, 0));
+        option->setEncapsulatedSpace(encapsulated_space);
+        option->setCallback(callback);
+        option->unpack(begin, end);
         return (option);
     }
 
@@ -470,6 +530,31 @@ public:
 
 private:
 
+    /// @brief Creates an instance of an option having special format.
+    ///
+    /// The option with special formats are encapsulated by the dedicated
+    /// classes derived from @c Option class. In particular these are:
+    /// - IA_NA
+    /// - IAADDR
+    /// - FQDN
+    /// - VIVSO.
+    ///
+    /// @param u A universe (V4 or V6).
+    /// @param begin beginning of the option buffer.
+    /// @param end end of the option buffer.
+    /// @param callback An instance of the function which parses packet options.
+    /// If this is set to non NULL value this function will be used instead of
+    /// @c isc::dhcp::LibDHCP::unpackOptions6 and
+    /// isc::dhcp::LibDHCP::unpackOptions4.
+    ///
+    /// @return An instance of the option having special format or NULL if
+    /// such an option can't be created because an option with the given
+    /// option code hasn't got the special format.
+    OptionPtr factorySpecialFormatOption(Option::Universe u,
+                                         OptionBufferConstIter begin,
+                                         OptionBufferConstIter end,
+                                         UnpackOptionsCallback callback) const;
+
     /// @brief Check if specified option format is a record with 3 fields
     /// where first one is custom, and two others are uint32.
     ///
@@ -579,6 +664,9 @@ typedef boost::multi_index_container<
 /// Pointer to an option definition container.
 typedef boost::shared_ptr<OptionDefContainer> OptionDefContainerPtr;
 
+/// Container that holds various vendor option containers
+typedef std::map<uint32_t, OptionDefContainer> VendorOptionDefContainers;
+
 /// Type of the index #1 - option type.
 typedef OptionDefContainer::nth_index<1>::type OptionDefContainerTypeIndex;
 /// Pair of iterators to represent the range of options definitions
diff --git a/src/lib/dhcp/option_int.h b/src/lib/dhcp/option_int.h
index 9a4cfb1..cbdbcb0 100644
--- a/src/lib/dhcp/option_int.h
+++ b/src/lib/dhcp/option_int.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -47,11 +47,13 @@ public:
     ///
     /// @throw isc::dhcp::InvalidDataType if data field type provided
     /// as template parameter is not a supported integer type.
+    /// @todo Extend constructor to set encapsulated option space name.
     OptionInt(Option::Universe u, uint16_t type, T value)
         : Option(u, type), value_(value) {
         if (!OptionDataTypeTraits<T>::integer_type) {
             isc_throw(dhcp::InvalidDataType, "non-integer type");
         }
+        setEncapsulatedSpace(u == Option::V4 ? "dhcp4" : "dhcp6");
     }
 
     /// @brief Constructor.
@@ -68,12 +70,14 @@ public:
     /// @throw isc::OutOfRange if provided buffer is shorter than data size.
     /// @throw isc::dhcp::InvalidDataType if data field type provided
     /// as template parameter is not a supported integer type.
+    /// @todo Extend constructor to set encapsulated option space name.
     OptionInt(Option::Universe u, uint16_t type, OptionBufferConstIter begin,
                OptionBufferConstIter end)
         : Option(u, type) {
         if (!OptionDataTypeTraits<T>::integer_type) {
             isc_throw(dhcp::InvalidDataType, "non-integer type");
         }
+        setEncapsulatedSpace(u == Option::V4 ? "dhcp4" : "dhcp6");
         unpack(begin, end);
     }
 
@@ -175,7 +179,7 @@ public:
         // The data length is equal to size of T.
         length += sizeof(T);;
         // length of all suboptions
-        for (Option::OptionCollection::iterator it = options_.begin();
+        for (OptionCollection::iterator it = options_.begin();
              it != options_.end();
              ++it) {
             length += (*it).second->len();
diff --git a/src/lib/dhcp/option_int_array.h b/src/lib/dhcp/option_int_array.h
index a87e9b5..e0e9356 100644
--- a/src/lib/dhcp/option_int_array.h
+++ b/src/lib/dhcp/option_int_array.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -239,7 +239,7 @@ public:
         uint16_t length = (getUniverse() == Option::V4) ? OPTION4_HDR_LEN : OPTION6_HDR_LEN;
         length += values_.size() * sizeof(T);
         // length of all suboptions
-        for (Option::OptionCollection::iterator it = options_.begin();
+        for (OptionCollection::iterator it = options_.begin();
              it != options_.end();
              ++it) {
             length += (*it).second->len();
diff --git a/src/lib/dhcp/option_vendor.cc b/src/lib/dhcp/option_vendor.cc
new file mode 100644
index 0000000..878b534
--- /dev/null
+++ b/src/lib/dhcp/option_vendor.cc
@@ -0,0 +1,85 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/option_vendor.h>
+
+using namespace isc::dhcp;
+
+OptionVendor::OptionVendor(Option::Universe u, const uint32_t vendor_id)
+    :Option(u, u==Option::V4?DHO_VIVSO_SUBOPTIONS:D6O_VENDOR_OPTS), vendor_id_(vendor_id) {
+}
+
+OptionVendor::OptionVendor(Option::Universe u, OptionBufferConstIter begin,
+                           OptionBufferConstIter end)
+    :Option(u, u==Option::V4?DHO_VIVSO_SUBOPTIONS:D6O_VENDOR_OPTS), vendor_id_(0) {
+    unpack(begin, end);
+}
+
+
+void OptionVendor::pack(isc::util::OutputBuffer& buf) {
+    packHeader(buf);
+
+    // Store vendor-id
+    buf.writeUint32(vendor_id_);
+
+    // The format is slightly different for v4
+    if (universe_ == Option::V4) {
+        // Calculate and store data-len as follows:
+        // data-len = total option length - header length
+        //            - enterprise id field length - data-len field size
+        buf.writeUint8(len() - getHeaderLen() -
+                       sizeof(uint32_t) - sizeof(uint8_t));
+    }
+
+    packOptions(buf);
+}
+
+void OptionVendor::unpack(OptionBufferConstIter begin,
+                          OptionBufferConstIter end) {
+    if (distance(begin, end) < sizeof(uint32_t)) {
+        isc_throw(OutOfRange, "Truncated vendor-specific information option"
+                  << ", length=" << distance(begin, end));
+    }
+
+    vendor_id_ = isc::util::readUint32(&(*begin));
+
+    OptionBuffer vendor_buffer(begin +4, end);
+
+    if (universe_ == Option::V6) {
+        LibDHCP::unpackVendorOptions6(vendor_id_, vendor_buffer, options_);
+    } else {
+        LibDHCP::unpackVendorOptions4(vendor_id_, vendor_buffer, options_);
+    }
+}
+
+uint16_t OptionVendor::len() {
+    uint16_t length = getHeaderLen();
+
+    length += sizeof(uint32_t); // Vendor-id field
+
+    // Data-len field exists in DHCPv4 vendor options only
+    if (universe_ == Option::V4) {
+        length += sizeof(uint8_t);  // data-len
+    }
+
+    // length of all suboptions
+    for (OptionCollection::iterator it = options_.begin();
+         it != options_.end();
+         ++it) {
+        length += (*it).second->len();
+    }
+    return (length);
+}
diff --git a/src/lib/dhcp/option_vendor.h b/src/lib/dhcp/option_vendor.h
new file mode 100644
index 0000000..5b43508
--- /dev/null
+++ b/src/lib/dhcp/option_vendor.h
@@ -0,0 +1,101 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef OPTION_VENDOR_H
+#define OPTION_VENDOR_H
+
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option.h>
+#include <dhcp/option_data_types.h>
+#include <util/io_utilities.h>
+
+#include <stdint.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief This class represents vendor-specific information option.
+///
+/// As specified in RFC3925, the option formatting is slightly different
+/// for DHCPv4 than DHCPv6. The DHCPv4 Option includes additional field
+/// holding vendor data length.
+class OptionVendor: public Option {
+public:
+    /// @brief Constructor.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param vendor_id vendor enterprise-id (unique 32 bit integer)
+    OptionVendor(Option::Universe u, const uint32_t vendor_id);
+
+    /// @brief Constructor.
+    ///
+    /// This constructor creates option from a buffer. This constructor
+    /// may throw exception if \ref unpack function throws during buffer
+    /// parsing.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param begin iterator to first byte of option data.
+    /// @param end iterator to end of option data (first byte after option end).
+    ///
+    /// @throw isc::OutOfRange if provided buffer is shorter than data size.
+    /// @todo Extend constructor to set encapsulated option space name.
+    OptionVendor(Option::Universe u, OptionBufferConstIter begin,
+                 OptionBufferConstIter end);
+
+    /// @brief Writes option in wire-format to buf, returns pointer to first
+    /// unused byte after stored option.
+    ///
+    /// @param [out] buf buffer (option will be stored here)
+    virtual void pack(isc::util::OutputBuffer& buf);
+
+    /// @brief Parses received buffer
+    ///
+    /// Parses received buffer and returns offset to the first unused byte after
+    /// parsed option.
+    ///
+    /// @param begin iterator to first byte of option data
+    /// @param end iterator to end of option data (first byte after option end)
+    ///
+    /// @throw isc::OutOfRange if provided buffer is shorter than data size.
+    virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end);
+
+    /// @brief Sets enterprise identifier
+    ///
+    /// @param value vendor identifier
+    void setVendorId(const uint32_t vendor_id) { vendor_id_ = vendor_id; }
+
+    /// @brief Returns enterprise identifier
+    ///
+    /// @return enterprise identifier
+    uint32_t getVendorId() const { return (vendor_id_); }
+
+    /// @brief returns complete length of option
+    ///
+    /// Returns length of this option, including option header and suboptions
+    ///
+    /// @return length of this option
+    virtual uint16_t len();
+
+private:
+
+    uint32_t vendor_id_;  ///< Enterprise-id
+};
+
+/// Pointer to a vendor option
+typedef boost::shared_ptr<OptionVendor> OptionVendorPtr;
+
+} // isc::dhcp namespace
+} // isc namespace
+
+#endif // OPTION_VENDOR_H
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index 67c0ae5..8a5b8ab 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -93,7 +93,7 @@ Pkt4::len() {
     size_t length = DHCPV4_PKT_HDR_LEN; // DHCPv4 header
 
     // ... and sum of lengths of all options
-    for (Option::OptionCollection::const_iterator it = options_.begin();
+    for (OptionCollection::const_iterator it = options_.begin();
          it != options_.end();
          ++it) {
         length += (*it).second->len();
@@ -108,6 +108,10 @@ Pkt4::pack() {
         isc_throw(InvalidOperation, "Can't build Pkt4 packet. HWAddr not set.");
     }
 
+    // Clear the output buffer to make sure that consecutive calls to pack()
+    // will not result in concatenation of multiple packet copies.
+    buffer_out_.clear();
+
     try {
         size_t hw_len = hwaddr_->hwaddr_.size();
 
@@ -162,58 +166,67 @@ void
 Pkt4::unpack() {
 
     // input buffer (used during message reception)
-    isc::util::InputBuffer bufferIn(&data_[0], data_.size());
+    isc::util::InputBuffer buffer_in(&data_[0], data_.size());
 
-    if (bufferIn.getLength() < DHCPV4_PKT_HDR_LEN) {
+    if (buffer_in.getLength() < DHCPV4_PKT_HDR_LEN) {
         isc_throw(OutOfRange, "Received truncated DHCPv4 packet (len="
-                  << bufferIn.getLength() << " received, at least "
+                  << buffer_in.getLength() << " received, at least "
                   << DHCPV4_PKT_HDR_LEN << "is expected");
     }
 
-    op_ = bufferIn.readUint8();
-    uint8_t htype = bufferIn.readUint8();
-    uint8_t hlen = bufferIn.readUint8();
-    hops_ = bufferIn.readUint8();
-    transid_ = bufferIn.readUint32();
-    secs_ = bufferIn.readUint16();
-    flags_ = bufferIn.readUint16();
-    ciaddr_ = IOAddress(bufferIn.readUint32());
-    yiaddr_ = IOAddress(bufferIn.readUint32());
-    siaddr_ = IOAddress(bufferIn.readUint32());
-    giaddr_ = IOAddress(bufferIn.readUint32());
+    op_ = buffer_in.readUint8();
+    uint8_t htype = buffer_in.readUint8();
+    uint8_t hlen = buffer_in.readUint8();
+    hops_ = buffer_in.readUint8();
+    transid_ = buffer_in.readUint32();
+    secs_ = buffer_in.readUint16();
+    flags_ = buffer_in.readUint16();
+    ciaddr_ = IOAddress(buffer_in.readUint32());
+    yiaddr_ = IOAddress(buffer_in.readUint32());
+    siaddr_ = IOAddress(buffer_in.readUint32());
+    giaddr_ = IOAddress(buffer_in.readUint32());
 
     vector<uint8_t> hw_addr(MAX_CHADDR_LEN, 0);
-    bufferIn.readVector(hw_addr, MAX_CHADDR_LEN);
-    bufferIn.readData(sname_, MAX_SNAME_LEN);
-    bufferIn.readData(file_, MAX_FILE_LEN);
+    buffer_in.readVector(hw_addr, MAX_CHADDR_LEN);
+    buffer_in.readData(sname_, MAX_SNAME_LEN);
+    buffer_in.readData(file_, MAX_FILE_LEN);
 
     hw_addr.resize(hlen);
 
     hwaddr_ = HWAddrPtr(new HWAddr(hw_addr, htype));
 
-    if (bufferIn.getLength() == bufferIn.getPosition()) {
+    if (buffer_in.getLength() == buffer_in.getPosition()) {
         // this is *NOT* DHCP packet. It does not have any DHCPv4 options. In
         // particular, it does not have magic cookie, a 4 byte sequence that
         // differentiates between DHCP and BOOTP packets.
         isc_throw(InvalidOperation, "Received BOOTP packet. BOOTP is not supported.");
     }
 
-    if (bufferIn.getLength() - bufferIn.getPosition() < 4) {
+    if (buffer_in.getLength() - buffer_in.getPosition() < 4) {
       // there is not enough data to hold magic DHCP cookie
       isc_throw(Unexpected, "Truncated or no DHCP packet.");
     }
 
-    uint32_t magic = bufferIn.readUint32();
+    uint32_t magic = buffer_in.readUint32();
     if (magic != DHCP_OPTIONS_COOKIE) {
       isc_throw(Unexpected, "Invalid or missing DHCP magic cookie");
     }
 
-    size_t opts_len = bufferIn.getLength() - bufferIn.getPosition();
-    vector<uint8_t> optsBuffer;
+    size_t opts_len = buffer_in.getLength() - buffer_in.getPosition();
+    vector<uint8_t> opts_buffer;
 
-    // First use of readVector.
-    bufferIn.readVector(optsBuffer, opts_len);
-    LibDHCP::unpackOptions4(optsBuffer, options_);
+    // Use readVector because a function which parses option requires
+    // a vector as an input.
+    buffer_in.readVector(opts_buffer, opts_len);
+    if (callback_.empty()) {
+        LibDHCP::unpackOptions4(opts_buffer, "dhcp4", options_);
+    } else {
+        // The last two arguments are set to NULL because they are
+        // specific to DHCPv6 options parsing. They are unused for
+        // DHCPv4 case. In DHCPv6 case they hold are the relay message
+        // offset and length.
+        callback_(opts_buffer, "dhcp4", options_, NULL, NULL);
+    }
 
     // @todo check will need to be called separately, so hooks can be called
     // after the packet is parsed, but before its content is verified
@@ -270,7 +283,7 @@ Pkt4::toText() {
         << ":" << remote_port_ << ", msgtype=" << static_cast<int>(getType())
         << ", transid=0x" << hex << transid_ << dec << endl;
 
-    for (isc::dhcp::Option::OptionCollection::iterator opt=options_.begin();
+    for (isc::dhcp::OptionCollection::iterator opt=options_.begin();
          opt != options_.end();
          ++opt) {
         tmp << "  " << opt->second->toText() << std::endl;
@@ -428,7 +441,7 @@ Pkt4::addOption(boost::shared_ptr<Option> opt) {
 
 boost::shared_ptr<isc::dhcp::Option>
 Pkt4::getOption(uint8_t type) const {
-    Option::OptionCollection::const_iterator x = options_.find(type);
+    OptionCollection::const_iterator x = options_.find(type);
     if (x != options_.end()) {
         return (*x).second;
     }
@@ -437,7 +450,7 @@ Pkt4::getOption(uint8_t type) const {
 
 bool
 Pkt4::delOption(uint8_t type) {
-    isc::dhcp::Option::OptionCollection::iterator x = options_.find(type);
+    isc::dhcp::OptionCollection::iterator x = options_.find(type);
     if (x != options_.end()) {
         options_.erase(x);
         return (true); // delete successful
diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
index a64734b..c8015cd 100644
--- a/src/lib/dhcp/pkt4.h
+++ b/src/lib/dhcp/pkt4.h
@@ -16,6 +16,7 @@
 #define PKT4_H
 
 #include <asiolink/io_address.h>
+#include <dhcp/option.h>
 #include <util/buffer.h>
 #include <dhcp/option.h>
 #include <dhcp/hwaddr.h>
@@ -71,6 +72,7 @@ public:
     /// Prepares on-wire format of message and all its options.
     /// Options must be stored in options_ field.
     /// Output buffer will be stored in buffer_out_.
+    /// The buffer_out_ is cleared before writting to the buffer.
     ///
     /// @throw InvalidOperation if packing fails
     void
@@ -482,6 +484,14 @@ public:
     /// @return remote port
     uint16_t getRemotePort() const { return (remote_port_); }
 
+    /// @brief Set callback function to be used to parse options.
+    ///
+    /// @param callback An instance of the callback function or NULL to
+    /// uninstall callback.
+    void setCallback(UnpackOptionsCallback callback) {
+        callback_ = callback;
+    }
+
     /// @brief Update packet timestamp.
     ///
     /// Updates packet timestamp. This method is invoked
@@ -632,11 +642,14 @@ protected:
     /// behavior must be taken into consideration before making
     /// changes to this member such as access scope restriction or
     /// data format change etc.
-    isc::dhcp::Option::OptionCollection options_;
+    isc::dhcp::OptionCollection options_;
 
     /// packet timestamp
     boost::posix_time::ptime timestamp_;
 
+    /// A callback to be called to unpack options from the packet.
+    UnpackOptionsCallback callback_;
+
 }; // Pkt4 class
 
 typedef boost::shared_ptr<Pkt4> Pkt4Ptr;
diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc
index 9ace94a..308880e 100644
--- a/src/lib/dhcp/pkt6.cc
+++ b/src/lib/dhcp/pkt6.cc
@@ -14,6 +14,7 @@
 
 #include <dhcp/dhcp6.h>
 #include <dhcp/libdhcp++.h>
+#include <dhcp/option.h>
 #include <dhcp/pkt6.h>
 #include <exceptions/exceptions.h>
 
@@ -42,7 +43,7 @@ Pkt6::Pkt6(const uint8_t* buf, uint32_t buf_len, DHCPv6Proto proto /* = UDP */)
     remote_addr_("::"),
     local_port_(0),
     remote_port_(0),
-    bufferOut_(0) {
+    buffer_out_(0) {
     data_.resize(buf_len);
     memcpy(&data_[0], buf, buf_len);
 }
@@ -57,7 +58,7 @@ Pkt6::Pkt6(uint8_t msg_type, uint32_t transid, DHCPv6Proto proto /*= UDP*/) :
     remote_addr_("::"),
     local_port_(0),
     remote_port_(0),
-    bufferOut_(0) {
+    buffer_out_(0) {
 }
 
 uint16_t Pkt6::len() {
@@ -134,7 +135,7 @@ OptionPtr Pkt6::getRelayOption(uint16_t opt_type, uint8_t relay_level) {
                   << " There is no info about " << relay_level + 1 << " relay.");
     }
 
-    for (Option::OptionCollection::iterator it = relay_info_[relay_level].options_.begin();
+    for (OptionCollection::iterator it = relay_info_[relay_level].options_.begin();
          it != relay_info_[relay_level].options_.end(); ++it) {
         if ((*it).second->getType() == opt_type) {
             return (it->second);
@@ -148,7 +149,7 @@ uint16_t Pkt6::getRelayOverhead(const RelayInfo& relay) const {
     uint16_t len = DHCPV6_RELAY_HDR_LEN // fixed header
         + Option::OPTION6_HDR_LEN; // header of the relay-msg option
 
-    for (Option::OptionCollection::const_iterator opt = relay.options_.begin();
+    for (OptionCollection::const_iterator opt = relay.options_.begin();
          opt != relay.options_.end(); ++opt) {
         len += (opt->second)->len();
     }
@@ -171,7 +172,7 @@ uint16_t Pkt6::calculateRelaySizes() {
 uint16_t Pkt6::directLen() const {
     uint16_t length = DHCPV6_PKT_HDR_LEN; // DHCPv6 header
 
-    for (Option::OptionCollection::const_iterator it = options_.begin();
+    for (OptionCollection::const_iterator it = options_.begin();
          it != options_.end();
          ++it) {
         length += (*it).second->len();
@@ -198,6 +199,8 @@ Pkt6::pack() {
 void
 Pkt6::packUDP() {
     try {
+        // Make sure that the buffer is empty before we start writting to it.
+        buffer_out_.clear();
 
         // is this a relayed packet?
         if (!relay_info_.empty()) {
@@ -214,11 +217,11 @@ Pkt6::packUDP() {
                  relay != relay_info_.end(); ++relay) {
 
                 // build relay-forw/relay-repl header (see RFC3315, section 7)
-                bufferOut_.writeUint8(relay->msg_type_);
-                bufferOut_.writeUint8(relay->hop_count_);
-                bufferOut_.writeData(&(relay->linkaddr_.toBytes()[0]),
+                buffer_out_.writeUint8(relay->msg_type_);
+                buffer_out_.writeUint8(relay->hop_count_);
+                buffer_out_.writeData(&(relay->linkaddr_.toBytes()[0]),
                                      isc::asiolink::V6ADDRESS_LEN);
-                bufferOut_.writeData(&relay->peeraddr_.toBytes()[0],
+                buffer_out_.writeData(&relay->peeraddr_.toBytes()[0],
                                      isc::asiolink::V6ADDRESS_LEN);
 
                 // store every option in this relay scope. Usually that will be
@@ -226,31 +229,31 @@ Pkt6::packUDP() {
                 // present here as well (vendor-opts for Cable modems,
                 // subscriber-id, remote-id, options echoed back from Echo
                 // Request Option, etc.)
-                for (Option::OptionCollection::const_iterator opt =
+                for (OptionCollection::const_iterator opt =
                          relay->options_.begin();
                      opt != relay->options_.end(); ++opt) {
-                    (opt->second)->pack(bufferOut_);
+                    (opt->second)->pack(buffer_out_);
                 }
 
                 // and include header relay-msg option. Its payload will be
                 // generated in the next iteration (if there are more relays)
                 // or outside the loop (if there are no more relays and the
                 // payload is a direct message)
-                bufferOut_.writeUint16(D6O_RELAY_MSG);
-                bufferOut_.writeUint16(relay->relay_msg_len_);
+                buffer_out_.writeUint16(D6O_RELAY_MSG);
+                buffer_out_.writeUint16(relay->relay_msg_len_);
             }
 
         }
 
         // DHCPv6 header: message-type (1 octect) + transaction id (3 octets)
-        bufferOut_.writeUint8(msg_type_);
+        buffer_out_.writeUint8(msg_type_);
         // store 3-octet transaction-id
-        bufferOut_.writeUint8( (transid_ >> 16) & 0xff );
-        bufferOut_.writeUint8( (transid_ >> 8) & 0xff );
-        bufferOut_.writeUint8( (transid_) & 0xff );
+        buffer_out_.writeUint8( (transid_ >> 16) & 0xff );
+        buffer_out_.writeUint8( (transid_ >> 8) & 0xff );
+        buffer_out_.writeUint8( (transid_) & 0xff );
 
         // the rest are options
-        LibDHCP::packOptions(bufferOut_, options_);
+        LibDHCP::packOptions(buffer_out_, options_);
     }
     catch (const Exception& e) {
        // An exception is thrown and message will be written to Logger
@@ -324,7 +327,16 @@ Pkt6::unpackMsg(OptionBuffer::const_iterator begin,
     try {
         OptionBuffer opt_buffer(begin, end);
 
-        LibDHCP::unpackOptions6(opt_buffer, options_);
+        // If custom option parsing function has been set, use this function
+        // to parse options. Otherwise, use standard function from libdhcp.
+        if (callback_.empty()) {
+            LibDHCP::unpackOptions6(opt_buffer, "dhcp6", options_);
+        } else {
+            // The last two arguments hold the DHCPv6 Relay message offset and
+            // length. Setting them to NULL because we are dealing with the
+            // not-relayed message.
+            callback_(opt_buffer, "dhcp6", options_, NULL, NULL);
+        }
     } catch (const Exception& e) {
         // @todo: throw exception here once we turn this function to void.
         return (false);
@@ -361,8 +373,16 @@ Pkt6::unpackRelayMsg() {
         try {
             // parse the rest as options
             OptionBuffer opt_buffer(&data_[offset], &data_[offset+bufsize]);
-            LibDHCP::unpackOptions6(opt_buffer, relay.options_, &relay_msg_offset,
-                                    &relay_msg_len);
+
+            // If custom option parsing function has been set, use this function
+            // to parse options. Otherwise, use standard function from libdhcp.
+            if (callback_.empty()) {
+                LibDHCP::unpackOptions6(opt_buffer, "dhcp6", relay.options_,
+                                        &relay_msg_offset, &relay_msg_len);
+            } else {
+                callback_(opt_buffer, "dhcp6", relay.options_,
+                          &relay_msg_offset, &relay_msg_len);
+            }
 
             /// @todo: check that each option appears at most once
             //relay.interface_id_ = options->getOption(D6O_INTERFACE_ID);
@@ -438,7 +458,7 @@ Pkt6::toText() {
         << "]:" << remote_port_ << endl;
     tmp << "msgtype=" << static_cast<int>(msg_type_) << ", transid=0x" <<
         hex << transid_ << dec << endl;
-    for (isc::dhcp::Option::OptionCollection::iterator opt=options_.begin();
+    for (isc::dhcp::OptionCollection::iterator opt=options_.begin();
          opt != options_.end();
          ++opt) {
         tmp << opt->second->toText() << std::endl;
@@ -448,18 +468,18 @@ Pkt6::toText() {
 
 OptionPtr
 Pkt6::getOption(uint16_t opt_type) {
-    isc::dhcp::Option::OptionCollection::const_iterator x = options_.find(opt_type);
+    isc::dhcp::OptionCollection::const_iterator x = options_.find(opt_type);
     if (x!=options_.end()) {
         return (*x).second;
     }
     return OptionPtr(); // NULL
 }
 
-isc::dhcp::Option::OptionCollection
+isc::dhcp::OptionCollection
 Pkt6::getOptions(uint16_t opt_type) {
-    isc::dhcp::Option::OptionCollection found;
+    isc::dhcp::OptionCollection found;
 
-    for (Option::OptionCollection::const_iterator x = options_.begin();
+    for (OptionCollection::const_iterator x = options_.begin();
          x != options_.end(); ++x) {
         if (x->first == opt_type) {
             found.insert(make_pair(opt_type, x->second));
@@ -475,7 +495,7 @@ Pkt6::addOption(const OptionPtr& opt) {
 
 bool
 Pkt6::delOption(uint16_t type) {
-    isc::dhcp::Option::OptionCollection::iterator x = options_.find(type);
+    isc::dhcp::OptionCollection::iterator x = options_.find(type);
     if (x!=options_.end()) {
         options_.erase(x);
         return (true); // delete successful
@@ -484,7 +504,7 @@ Pkt6::delOption(uint16_t type) {
 }
 
 void Pkt6::repack() {
-    bufferOut_.writeData(&data_[0], data_.size());
+    buffer_out_.writeData(&data_[0], data_.size());
 }
 
 void
diff --git a/src/lib/dhcp/pkt6.h b/src/lib/dhcp/pkt6.h
index a7f95c6..702c424 100644
--- a/src/lib/dhcp/pkt6.h
+++ b/src/lib/dhcp/pkt6.h
@@ -88,7 +88,7 @@ public:
         uint16_t  relay_msg_len_;
 
         /// options received from a specified relay, except relay-msg option
-        isc::dhcp::Option::OptionCollection options_;
+        isc::dhcp::OptionCollection options_;
     };
 
     /// Constructor, used in replying to a message
@@ -115,6 +115,7 @@ public:
     /// Options must be stored in options_ field.
     /// Output buffer will be stored in data_. Length
     /// will be set in data_len_.
+    /// The output buffer is cleared before new data is written to it.
     ///
     /// @throw BadValue if packet protocol is invalid, InvalidOperation
     /// if packing fails, or NotImplemented if protocol is TCP (IPv6 over TCP is
@@ -139,7 +140,7 @@ public:
     /// zero length
     ///
     /// @return reference to output buffer
-    const isc::util::OutputBuffer& getBuffer() const { return (bufferOut_); };
+    const isc::util::OutputBuffer& getBuffer() const { return (buffer_out_); };
 
     /// @brief Returns protocol of this packet (UDP or TCP).
     ///
@@ -242,7 +243,7 @@ public:
     ///
     /// @param type option type we are looking for
     /// @return instance of option collection with requested options
-    isc::dhcp::Option::OptionCollection getOptions(uint16_t type);
+    isc::dhcp::OptionCollection getOptions(uint16_t type);
 
     /// Attempts to delete first suboption of requested type
     ///
@@ -350,7 +351,7 @@ public:
     /// behavior must be taken into consideration before making
     /// changes to this member such as access scope restriction or
     /// data format change etc.
-    isc::dhcp::Option::OptionCollection options_;
+    isc::dhcp::OptionCollection options_;
 
     /// @brief Update packet timestamp.
     ///
@@ -388,6 +389,14 @@ public:
     ///         be freed by the caller.
     const char* getName() const;
 
+    /// @brief Set callback function to be used to parse options.
+    ///
+    /// @param callback An instance of the callback function or NULL to
+    /// uninstall callback.
+    void setCallback(UnpackOptionsCallback callback) {
+        callback_ = callback;
+    }
+
     /// @brief copies relay information from client's packet to server's response
     ///
     /// This information is not simply copied over. Some parameter are
@@ -522,7 +531,7 @@ protected:
     /// remote TCP or UDP port
     uint16_t remote_port_;
 
-    /// output buffer (used during message transmission)
+    /// Output buffer (used during message transmission)
     ///
     /// @warning This protected member is accessed by derived
     /// classes directly. One of such derived classes is
@@ -530,10 +539,14 @@ protected:
     /// behavior must be taken into consideration before making
     /// changes to this member such as access scope restriction or
     /// data format change etc.
-    isc::util::OutputBuffer bufferOut_;
+    isc::util::OutputBuffer buffer_out_;
 
     /// packet timestamp
     boost::posix_time::ptime timestamp_;
+
+    /// A callback to be called to unpack options from the packet.
+    UnpackOptionsCallback callback_;
+
 }; // Pkt6 class
 
 } // isc::dhcp namespace
diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h
index 00acdab..9f64553 100644
--- a/src/lib/dhcp/std_option_defs.h
+++ b/src/lib/dhcp/std_option_defs.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -16,6 +16,8 @@
 #define STD_OPTION_DEFS_H
 
 #include <dhcp/option_data_types.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
 
 namespace {
 
@@ -68,7 +70,7 @@ RECORD_DECL(FQDN_RECORDS, OPT_UINT8_TYPE, OPT_UINT8_TYPE, OPT_UINT8_TYPE,
 /// @brief Definitions of standard DHCPv4 options.
 const OptionDefParams OPTION_DEF_PARAMS4[] = {
     { "subnet-mask", DHO_SUBNET_MASK, OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF, "" },
-    { "time-offset", DHO_TIME_OFFSET, OPT_UINT32_TYPE, false, NO_RECORD_DEF, "" },
+    { "time-offset", DHO_TIME_OFFSET, OPT_INT32_TYPE, false, NO_RECORD_DEF, "" },
     { "routers", DHO_ROUTERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
     { "time-servers", DHO_TIME_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
     { "name-servers", DHO_NAME_SERVERS, OPT_IPV4_ADDRESS_TYPE,
@@ -163,6 +165,10 @@ const OptionDefParams OPTION_DEF_PARAMS4[] = {
       OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
     { "nwip-domain-name", DHO_NWIP_DOMAIN_NAME, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
     { "nwip-suboptions", DHO_NWIP_SUBOPTIONS, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
+    { "tftp-server-name", DHO_TFTP_SERVER_NAME, OPT_STRING_TYPE, false,
+      NO_RECORD_DEF, "" },
+    { "boot-file-name", DHO_BOOT_FILE_NAME, OPT_STRING_TYPE, false,
+      NO_RECORD_DEF, "" },
     { "user-class", DHO_USER_CLASS, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
     { "fqdn", DHO_FQDN, OPT_RECORD_TYPE, false, RECORD_DEF(FQDN_RECORDS), "" },
     { "dhcp-agent-options", DHO_DHCP_AGENT_OPTIONS,
@@ -214,7 +220,7 @@ RECORD_DECL(IA_NA_RECORDS, OPT_UINT32_TYPE, OPT_UINT32_TYPE, OPT_UINT32_TYPE);
 RECORD_DECL(IA_PD_RECORDS, OPT_UINT32_TYPE, OPT_UINT32_TYPE, OPT_UINT32_TYPE);
 // ia-prefix
 RECORD_DECL(IA_PREFIX_RECORDS, OPT_UINT32_TYPE, OPT_UINT32_TYPE,
-            OPT_UINT8_TYPE, OPT_BINARY_TYPE);
+            OPT_UINT8_TYPE, OPT_IPV6_ADDRESS_TYPE);
 // lq-query
 RECORD_DECL(LQ_QUERY_RECORDS, OPT_UINT8_TYPE, OPT_IPV6_ADDRESS_TYPE);
 // lq-relay-data
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index 3baac04..79bfde7 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -36,6 +36,7 @@ libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc
 libdhcp___unittests_SOURCES += option6_client_fqdn_unittest.cc
 libdhcp___unittests_SOURCES += option6_ia_unittest.cc
 libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
+libdhcp___unittests_SOURCES += option6_iaprefix_unittest.cc
 libdhcp___unittests_SOURCES += option_int_unittest.cc
 libdhcp___unittests_SOURCES += option_int_array_unittest.cc
 libdhcp___unittests_SOURCES += option_data_types_unittest.cc
@@ -44,6 +45,7 @@ libdhcp___unittests_SOURCES += option_custom_unittest.cc
 libdhcp___unittests_SOURCES += option_unittest.cc
 libdhcp___unittests_SOURCES += option_space_unittest.cc
 libdhcp___unittests_SOURCES += option_string_unittest.cc
+libdhcp___unittests_SOURCES += option_vendor_unittest.cc
 libdhcp___unittests_SOURCES += pkt4_unittest.cc
 libdhcp___unittests_SOURCES += pkt6_unittest.cc
 libdhcp___unittests_SOURCES += pkt_filter_inet_unittest.cc
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index 488ecb3..b936b5c 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -145,6 +145,27 @@ public:
         return (sockets_count);
     }
 
+    /// @brief returns socket bound to a specific address (or NULL)
+    ///
+    /// A helper function, used to pick a socketinfo that is bound to a given
+    /// address.
+    ///
+    /// @param sockets sockets collection
+    /// @param addr address the socket is bound to
+    ///
+    /// @return socket info structure (or NULL)
+    const isc::dhcp::SocketInfo*
+    getSocketByAddr(const isc::dhcp::Iface::SocketCollection& sockets,
+                    const IOAddress& addr) {
+        for (isc::dhcp::Iface::SocketCollection::const_iterator s =
+                 sockets.begin(); s != sockets.end(); ++s) {
+            if (s->addr_ == addr) {
+                return (&(*s));
+            }
+        }
+        return (NULL);
+    }
+
 };
 
 // We need some known interface to work reliably. Loopback interface is named
@@ -781,6 +802,7 @@ TEST_F(IfaceMgrTest, sendReceive6) {
     // try to send/receive data over the closed socket. Closed socket's descriptor is
     // still being hold by IfaceMgr which will try to use it to receive data.
     close(socket1);
+    close(socket2);
     EXPECT_THROW(ifacemgr->receive6(10), SocketReadError);
     EXPECT_THROW(ifacemgr->send(sendPkt), SocketWriteError);
 }
@@ -1520,4 +1542,116 @@ TEST_F(IfaceMgrTest, controlSession) {
     close(pipefd[0]);
 }
 
+// Test checks if the unicast sockets can be opened.
+// This test is now disabled, because there is no reliable way to test it. We
+// can't even use loopback, beacuse openSockets() skips loopback interface
+// (as it should be, because DHCP server is not supposed to listen on loopback).
+TEST_F(IfaceMgrTest, DISABLED_openUnicastSockets) {
+    /// @todo Need to implement a test that is able to check whether we can open
+    /// unicast sockets. There are 2 problems with it:
+    /// 1. We need to have a non-link-local address on an interface that is
+    ///    up, running, IPv6 and multicast capable
+    /// 2. We need that information on every OS that we run tests on. So far
+    ///    we are only supporting interface detection in Linux.
+    ///
+    /// To achieve this, we will probably need a pre-test setup, similar to what
+    /// BIND9 is doing (i.e. configuring well known addresses on loopback).
+
+    scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+    // Get the interface (todo: which interface)
+    Iface* iface = ifacemgr->getIface("eth0");
+    ASSERT_TRUE(iface);
+    iface->inactive6_ = false;
+
+    // Tell the interface that it should bind to this global interface
+    EXPECT_NO_THROW(iface->addUnicast(IOAddress("2001:db8::1")));
+
+    // Tell IfaceMgr to open sockets. This should trigger at least 2 sockets
+    // to open on eth0: link-local and global. On some systems (Linux), an
+    // additional socket for multicast may be opened.
+    EXPECT_TRUE(ifacemgr->openSockets6(PORT1));
+
+    const Iface::SocketCollection& sockets = iface->getSockets();
+    ASSERT_GE(2, sockets.size());
+
+    // Global unicast should be first
+    EXPECT_TRUE(getSocketByAddr(sockets, IOAddress("2001:db8::1")));
+    EXPECT_TRUE(getSocketByAddr(sockets, IOAddress("figure-out-link-local-addr")));
+}
+
+// Checks if there is a protection against unicast duplicates.
+TEST_F(IfaceMgrTest, unicastDuplicates) {
+    NakedIfaceMgr ifacemgr;
+
+    Iface* iface = ifacemgr.getIface(LOOPBACK);
+    if (iface == NULL) {
+        cout << "Local loopback interface not found. Skipping test. " << endl;
+        return;
+    }
+
+    // Tell the interface that it should bind to this global interface
+    EXPECT_NO_THROW(iface->addUnicast(IOAddress("2001:db8::1")));
+
+    // Tell the interface that it should bind to this global interface
+    EXPECT_THROW(iface->addUnicast(IOAddress("2001:db8::1")), BadValue);
+}
+
+// This test requires addresses 2001:db8:15c::1/128 and fe80::1/64 to be
+// configured on loopback interface
+//
+// Useful commands:
+// ip a a 2001:db8:15c::1/128 dev lo
+// ip a a fe80::1/64 dev lo
+//
+// If you do not issue those commands before running this test, it will fail.
+TEST_F(IfaceMgrTest, DISABLED_getSocket) {
+    // Testing socket operation in a portable way is tricky
+    // without interface detection implemented.
+
+    scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+    IOAddress lo_addr("::1");
+    IOAddress link_local("fe80::1");
+    IOAddress global("2001:db8:15c::1");
+
+    IOAddress dst_link_local("fe80::dead:beef");
+    IOAddress dst_global("2001:db8:15c::dead:beef");
+
+    // Bind loopback address
+    int socket1 = ifacemgr->openSocket(LOOPBACK, lo_addr, 10547);
+    EXPECT_GE(socket1, 0); // socket >= 0
+
+    // Bind link-local address
+    int socket2 = ifacemgr->openSocket(LOOPBACK, link_local, 10547);
+    EXPECT_GE(socket2, 0);
+
+    int socket3 = ifacemgr->openSocket(LOOPBACK, global, 10547);
+    EXPECT_GE(socket3, 0);
+
+    // Let's make sure those sockets are unique
+    EXPECT_NE(socket1, socket2);
+    EXPECT_NE(socket2, socket3);
+    EXPECT_NE(socket3, socket1);
+
+    // Create a packet
+    Pkt6 pkt6(DHCPV6_SOLICIT, 123);
+    pkt6.setIface(LOOPBACK);
+
+    // Check that packets sent to link-local will get socket bound to link local
+    pkt6.setLocalAddr(global);
+    pkt6.setRemoteAddr(dst_global);
+    EXPECT_EQ(socket3, ifacemgr->getSocket(pkt6));
+
+    // Check that packets sent to link-local will get socket bound to link local
+    pkt6.setLocalAddr(link_local);
+    pkt6.setRemoteAddr(dst_link_local);
+    EXPECT_EQ(socket2, ifacemgr->getSocket(pkt6));
+
+    // Close sockets here because the following tests will want to
+    // open sockets on the same ports.
+    ifacemgr->closeSockets();
+}
+
+
 }
diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc
index f7f5ffc..92f7baf 100644
--- a/src/lib/dhcp/tests/libdhcp++_unittest.cc
+++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc
@@ -23,10 +23,12 @@
 #include <dhcp/option6_client_fqdn.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
 #include <dhcp/option_custom.h>
 #include <dhcp/option_int.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/option_string.h>
+#include <dhcp/option_vendor.h>
 #include <util/buffer.h>
 
 #include <gtest/gtest.h>
@@ -42,6 +44,12 @@ using namespace isc::dhcp;
 using namespace isc::util;
 
 namespace {
+
+// DHCPv6 suboptions of Vendor Options Option.
+/// @todo move to src/lib/dhcp/docsis3_option_defs.h once #3194 is merged.
+const uint16_t OPTION_CMTS_CAPS = 1025;
+const uint16_t OPTION_CM_MAC = 1026;
+
 class LibDhcpTest : public ::testing::Test {
 public:
     LibDhcpTest() { }
@@ -105,6 +113,33 @@ public:
         testStdOptionDefs(Option::V6, code, begin, end, expected_type,
                           encapsulates);
     }
+
+    /// @brief Create a sample DHCPv4 option 43 with suboptions.
+    static OptionBuffer createVendorOption() {
+        const uint8_t opt_data[] = {
+            0x2B, 0x0D,  // Vendor-Specific Information (CableLabs)
+            // Suboptions start here...
+            0x02, 0x05,  // Device Type Option (length = 5)
+            'D', 'u', 'm', 'm', 'y',
+            0x04, 0x04,   // Serial Number Option (length = 4)
+            0x42, 0x52, 0x32, 0x32 // Serial number
+        };
+        return (OptionBuffer(opt_data, opt_data + sizeof(opt_data)));
+    }
+
+    /// @brief Create a sample DHCPv4 option 82 with suboptions.
+    static OptionBuffer createAgentInformationOption() {
+        const uint8_t opt_data[] = {
+            0x52, 0x0E, // Agent Information Option (length = 14)
+            // Suboptions start here...
+            0x01, 0x04, // Agent Circuit ID (length = 4)
+            0x20, 0x00, 0x00, 0x02, // ID
+            0x02, 0x06, // Agent Remote ID
+            0x20, 0xE5, 0x2A, 0xB8, 0x15, 0x14 // ID
+        };
+        return (OptionBuffer(opt_data, opt_data + sizeof(opt_data)));
+    }
+
 private:
 
     /// @brief Test DHCPv4 or DHCPv6 option definition.
@@ -177,7 +212,19 @@ const uint8_t v6packed[] = {
     0, 2, 0, 3, 105, 106, 107, // SERVER_ID (7 bytes)
     0, 14, 0, 0, // RAPID_COMMIT (0 bytes)
     0,  6, 0, 4, 108, 109, 110, 111, // ORO (8 bytes)
-    0,  8, 0, 2, 112, 113 // ELAPSED_TIME (6 bytes)
+    0,  8, 0, 2, 112, 113, // ELAPSED_TIME (6 bytes)
+    // Vendor Specific Information Option starts here
+    0x00, 0x11,  // VSI Option Code
+    0x00, 0x16,  // VSI Option Length
+    0x00, 0x00, 0x11, 0x8B, // Enterprise ID
+    0x04, 0x01,  // CMTS Capabilities Option
+    0x00, 0x04,  // Length
+    0x01, 0x02,
+    0x03, 0x00,  // DOCSIS Version Number
+    0x04, 0x02,  // CM MAC Address Suboption
+    0x00, 0x06,  // Length
+    0x74, 0x56, 0x12, 0x29, 0x97, 0xD0, // Actual MAC Address
+
 };
 
 TEST_F(LibDhcpTest, optionFactory) {
@@ -254,7 +301,7 @@ TEST_F(LibDhcpTest, optionFactory) {
 
 TEST_F(LibDhcpTest, packOptions6) {
     OptionBuffer buf(512);
-    isc::dhcp::Option::OptionCollection opts; // list of options
+    isc::dhcp::OptionCollection opts; // list of options
 
     // generate content for options
     for (int i = 0; i < 64; i++) {
@@ -267,11 +314,23 @@ TEST_F(LibDhcpTest, packOptions6) {
     OptionPtr opt4(new Option(Option::V6, 6, buf.begin() + 8, buf.begin() + 12));
     OptionPtr opt5(new Option(Option::V6, 8, buf.begin() + 12, buf.begin() + 14));
 
+    OptionPtr cm_mac(new Option(Option::V6, OPTION_CM_MAC,
+                                OptionBuffer(v6packed + 54, v6packed  + 60)));
+
+    OptionPtr cmts_caps(new Option(Option::V6, OPTION_CMTS_CAPS,
+                                   OptionBuffer(v6packed + 46, v6packed + 50)));
+
+    boost::shared_ptr<OptionInt<uint32_t> >
+        vsi(new OptionInt<uint32_t>(Option::V6, D6O_VENDOR_OPTS, 4491));
+    vsi->addOption(cm_mac);
+    vsi->addOption(cmts_caps);
+
     opts.insert(make_pair(opt1->getType(), opt1));
     opts.insert(make_pair(opt1->getType(), opt2));
     opts.insert(make_pair(opt1->getType(), opt3));
     opts.insert(make_pair(opt1->getType(), opt4));
     opts.insert(make_pair(opt1->getType(), opt5));
+    opts.insert(make_pair(opt1->getType(), vsi));
 
     OutputBuffer assembled(512);
 
@@ -286,19 +345,19 @@ TEST_F(LibDhcpTest, unpackOptions6) {
     // Option is used as a simple option implementation
     // More advanced uses are validated in tests dedicated for
     // specific derived classes.
-    isc::dhcp::Option::OptionCollection options; // list of options
+    isc::dhcp::OptionCollection options; // list of options
 
     OptionBuffer buf(512);
     memcpy(&buf[0], v6packed, sizeof(v6packed));
 
     EXPECT_NO_THROW ({
             LibDHCP::unpackOptions6(OptionBuffer(buf.begin(), buf.begin() + sizeof(v6packed)),
-                                    options);
+                                    "dhcp6", options);
     });
 
-    EXPECT_EQ(options.size(), 5); // there should be 5 options
+    EXPECT_EQ(options.size(), 6); // there should be 5 options
 
-    isc::dhcp::Option::OptionCollection::const_iterator x = options.find(1);
+    isc::dhcp::OptionCollection::const_iterator x = options.find(1);
     ASSERT_FALSE(x == options.end()); // option 1 should exist
     EXPECT_EQ(1, x->second->getType());  // this should be option 1
     ASSERT_EQ(9, x->second->len()); // it should be of length 9
@@ -357,6 +416,27 @@ TEST_F(LibDhcpTest, unpackOptions6) {
     // Returned value should be equivalent to two byte values: 112, 113
     EXPECT_EQ(0x7071, opt_elapsed_time->getValue());
 
+    // Check if Vendor Specific Information Option along with suboptions
+    // have been parsed correctly.
+    x = options.find(D6O_VENDOR_OPTS);
+    EXPECT_FALSE(x == options.end());
+    EXPECT_EQ(D6O_VENDOR_OPTS, x->second->getType());
+    EXPECT_EQ(26, x->second->len());
+
+    // CM MAC Address Option
+    OptionPtr cm_mac = x->second->getOption(OPTION_CM_MAC);
+    ASSERT_TRUE(cm_mac);
+    EXPECT_EQ(OPTION_CM_MAC, cm_mac->getType());
+    ASSERT_EQ(10, cm_mac->len());
+    EXPECT_EQ(0, memcmp(&cm_mac->getData()[0], v6packed + 54, 6));
+
+    // CMTS Capabilities
+    OptionPtr cmts_caps = x->second->getOption(OPTION_CMTS_CAPS);
+    ASSERT_TRUE(cmts_caps);
+    EXPECT_EQ(OPTION_CMTS_CAPS, cmts_caps->getType());
+    ASSERT_EQ(8, cmts_caps->len());
+    EXPECT_EQ(0, memcmp(&cmts_caps->getData()[0], v6packed + 46, 4));
+
     x = options.find(0);
     EXPECT_TRUE(x == options.end()); // option 0 not found
 
@@ -380,7 +460,12 @@ static uint8_t v4_opts[] = {
     60,  3, 10, 11, 12, // Class Id
     14,  3, 20, 21, 22, // Merit Dump File
     254, 3, 30, 31, 32, // Reserved
-    128, 3, 40, 41, 42  // Vendor specific
+    128, 3, 40, 41, 42, // Vendor specific
+    0x52, 0x19,         // RAI
+    0x01, 0x04, 0x20, 0x00, 0x00, 0x02, // Agent Circuit ID
+    0x02, 0x06, 0x20, 0xE5, 0x2A, 0xB8, 0x15, 0x14, // Agent Remote ID
+    0x09, 0x09, 0x00, 0x00, 0x11, 0x8B, 0x04, // Vendor Specific Information
+    0x01, 0x02, 0x03, 0x00  // Vendor Specific Information continued
 };
 
 TEST_F(LibDhcpTest, packOptions4) {
@@ -399,32 +484,65 @@ TEST_F(LibDhcpTest, packOptions4) {
     OptionPtr opt4(new Option(Option::V4,254, payload[3]));
     OptionPtr opt5(new Option(Option::V4,128, payload[4]));
 
-    isc::dhcp::Option::OptionCollection opts; // list of options
+    // Add RAI option, which comprises 3 sub-options.
+
+    // Get the option definition for RAI option. This option is represented
+    // by OptionCustom which requires a definition to be passed to
+    // the constructor.
+    OptionDefinitionPtr rai_def = LibDHCP::getOptionDef(Option::V4,
+                                                        DHO_DHCP_AGENT_OPTIONS);
+    ASSERT_TRUE(rai_def);
+    // Create RAI option.
+    OptionCustomPtr rai(new OptionCustom(*rai_def, Option::V4));
+
+    // The sub-options are created using the bits of v4_opts buffer because
+    // we want to use this buffer as a reference to verify that produced
+    // option in on-wire format is correct.
+
+    // Create Ciruit ID sub-option and add to RAI.
+    OptionPtr circuit_id(new Option(Option::V4, RAI_OPTION_AGENT_CIRCUIT_ID,
+                                    OptionBuffer(v4_opts + 29,
+                                                 v4_opts + 33)));
+    rai->addOption(circuit_id);
+
+    // Create Remote ID option and add to RAI.
+    OptionPtr remote_id(new Option(Option::V4, RAI_OPTION_REMOTE_ID,
+                                   OptionBuffer(v4_opts + 35, v4_opts + 41)));
+    rai->addOption(remote_id);
+
+    // Create Vendor Specific Information and add to RAI.
+    OptionPtr vsi(new Option(Option::V4, RAI_OPTION_VSI,
+                             OptionBuffer(v4_opts + 43, v4_opts + 52)));
+    rai->addOption(vsi);
+
+    isc::dhcp::OptionCollection opts; // list of options
+    // Note that we insert each option under the same option code into
+    // the map. This gurantees that options are packed in the same order
+    // they were added. Otherwise, options would get sorted by code and
+    // the resulting buffer wouldn't match with the reference buffer.
     opts.insert(make_pair(opt1->getType(), opt1));
     opts.insert(make_pair(opt1->getType(), opt2));
     opts.insert(make_pair(opt1->getType(), opt3));
     opts.insert(make_pair(opt1->getType(), opt4));
     opts.insert(make_pair(opt1->getType(), opt5));
-
-    vector<uint8_t> expVect(v4_opts, v4_opts + sizeof(v4_opts));
+    opts.insert(make_pair(opt1->getType(), rai));
 
     OutputBuffer buf(100);
     EXPECT_NO_THROW(LibDHCP::packOptions(buf, opts));
     ASSERT_EQ(buf.getLength(), sizeof(v4_opts));
     EXPECT_EQ(0, memcmp(v4_opts, buf.getData(), sizeof(v4_opts)));
-
 }
 
 TEST_F(LibDhcpTest, unpackOptions4) {
 
     vector<uint8_t> v4packed(v4_opts, v4_opts + sizeof(v4_opts));
-    isc::dhcp::Option::OptionCollection options; // list of options
+    isc::dhcp::OptionCollection options; // list of options
 
     ASSERT_NO_THROW(
-        LibDHCP::unpackOptions4(v4packed, options);
+        LibDHCP::unpackOptions4(v4packed, "dhcp4", options);
     );
 
-    isc::dhcp::Option::OptionCollection::const_iterator x = options.find(12);
+    isc::dhcp::OptionCollection::const_iterator x = options.find(12);
     ASSERT_FALSE(x == options.end()); // option 1 should exist
     // Option 12 holds a string so let's cast it to an appropriate type.
     OptionStringPtr option12 = boost::static_pointer_cast<OptionString>(x->second);
@@ -464,6 +582,48 @@ TEST_F(LibDhcpTest, unpackOptions4) {
     EXPECT_EQ(5, x->second->len()); // total option length 5
     EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4_opts + 22, 3)); // data len=3
 
+    // Checking DHCP Relay Agent Information Option.
+    x = options.find(DHO_DHCP_AGENT_OPTIONS);
+    ASSERT_FALSE(x == options.end());
+    EXPECT_EQ(DHO_DHCP_AGENT_OPTIONS, x->second->getType());
+    // RAI is represented by OptionCustom.
+    OptionCustomPtr rai = boost::dynamic_pointer_cast<OptionCustom>(x->second);
+    ASSERT_TRUE(rai);
+    // RAI should have 3 sub-options: Circuit ID, Agent Remote ID, Vendor
+    // Specific Information option. Note that by parsing these suboptions we
+    // are checking that unpackOptions4 differentiates between standard option
+    // space called "dhcp4" and other option spaces. These sub-options do not
+    // belong to standard option space and should be parsed using different
+    // option definitions.
+    // @todo Currently, definitions for option space "dhcp-agent-options-space"
+    // are not defined. Therefore all suboptions will be represented here by
+    // the generic Option class.
+
+    // Check that Circuit ID option is among parsed options.
+    OptionPtr rai_option = rai->getOption(RAI_OPTION_AGENT_CIRCUIT_ID);
+    ASSERT_TRUE(rai_option);
+    EXPECT_EQ(RAI_OPTION_AGENT_CIRCUIT_ID, rai_option->getType());
+    ASSERT_EQ(6, rai_option->len());
+    EXPECT_EQ(0, memcmp(&rai_option->getData()[0], v4_opts + 29, 4));
+
+    // Check that Remote ID option is among parsed options.
+    rai_option = rai->getOption(RAI_OPTION_REMOTE_ID);
+    ASSERT_TRUE(rai_option);
+    EXPECT_EQ(RAI_OPTION_REMOTE_ID, rai_option->getType());
+    ASSERT_EQ(8, rai_option->len());
+    EXPECT_EQ(0, memcmp(&rai_option->getData()[0], v4_opts + 35, 6));
+
+    // Check that Vendor Specific Information option is among parsed options.
+    rai_option = rai->getOption(RAI_OPTION_VSI);
+    ASSERT_TRUE(rai_option);
+    EXPECT_EQ(RAI_OPTION_VSI, rai_option->getType());
+    ASSERT_EQ(11, rai_option->len());
+    EXPECT_EQ(0, memcmp(&rai_option->getData()[0], v4_opts + 43, 9));
+
+    // Make sure, that option other than those above is not present.
+    EXPECT_FALSE(rai->getOption(10));
+
+    // Check the same for the global option space.
     x = options.find(0);
     EXPECT_TRUE(x == options.end()); // option 0 not found
 
@@ -472,6 +632,7 @@ TEST_F(LibDhcpTest, unpackOptions4) {
 
     x = options.find(2);
     EXPECT_TRUE(x == options.end()); // option 2 not found
+
 }
 
 TEST_F(LibDhcpTest, isStandardOption4) {
@@ -546,7 +707,7 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
                                     typeid(OptionCustom));
 
     LibDhcpTest::testStdOptionDefs4(DHO_TIME_OFFSET, begin, begin + 4,
-                                    typeid(OptionInt<uint32_t>));
+                                    typeid(OptionInt<int32_t>));
 
     LibDhcpTest::testStdOptionDefs4(DHO_ROUTERS, begin, end,
                                     typeid(Option4AddrLst));
@@ -653,8 +814,8 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
     LibDhcpTest::testStdOptionDefs4(DHO_DEFAULT_TCP_TTL, begin, begin + 1,
                                     typeid(OptionInt<uint8_t>));
 
-    LibDhcpTest::testStdOptionDefs4(DHO_TCP_KEEPALIVE_INTERVAL, begin, begin + 4,
-                                    typeid(OptionInt<uint32_t>));
+    LibDhcpTest::testStdOptionDefs4(DHO_TCP_KEEPALIVE_INTERVAL, begin,
+                                    begin + 4, typeid(OptionInt<uint32_t>));
 
     LibDhcpTest::testStdOptionDefs4(DHO_TCP_KEEPALIVE_GARBAGE, begin, begin + 1,
                                     typeid(OptionCustom));
@@ -668,8 +829,13 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
     LibDhcpTest::testStdOptionDefs4(DHO_NTP_SERVERS, begin, end,
                                     typeid(Option4AddrLst));
 
-    LibDhcpTest::testStdOptionDefs4(DHO_VENDOR_ENCAPSULATED_OPTIONS, begin, end,
-                                    typeid(Option),
+    // The following option requires well formed buffer to be created from.
+    // Not just a dummy one. This buffer includes some suboptions.
+    OptionBuffer vendor_opts_buf = createVendorOption();
+    LibDhcpTest::testStdOptionDefs4(DHO_VENDOR_ENCAPSULATED_OPTIONS,
+                                    vendor_opts_buf.begin(),
+                                    vendor_opts_buf.end(),
+                                    typeid(OptionCustom),
                                     "vendor-encapsulated-options-space");
 
     LibDhcpTest::testStdOptionDefs4(DHO_NETBIOS_NAME_SERVERS, begin, end,
@@ -706,7 +872,7 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
                                     typeid(OptionCustom));
 
     LibDhcpTest::testStdOptionDefs4(DHO_DHCP_PARAMETER_REQUEST_LIST, begin, end,
-                                    typeid(Option));
+                                    typeid(OptionUint8Array));
 
     LibDhcpTest::testStdOptionDefs4(DHO_DHCP_MESSAGE, begin, end,
                                     typeid(OptionString));
@@ -732,14 +898,26 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
     LibDhcpTest::testStdOptionDefs4(DHO_NWIP_SUBOPTIONS, begin, end,
                                     typeid(Option));
 
+    LibDhcpTest::testStdOptionDefs4(DHO_TFTP_SERVER_NAME, begin, end,
+                                    typeid(OptionString));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_BOOT_FILE_NAME, begin, end,
+                                    typeid(OptionString));
+
     LibDhcpTest::testStdOptionDefs4(DHO_USER_CLASS, begin, end,
                                     typeid(Option));
 
     LibDhcpTest::testStdOptionDefs4(DHO_FQDN, begin, begin + 3,
                                     typeid(Option4ClientFqdn));
 
-    LibDhcpTest::testStdOptionDefs4(DHO_DHCP_AGENT_OPTIONS, begin, end,
-                                    typeid(Option), "dhcp-agent-options-space");
+    // The following option requires well formed buffer to be created from.
+    // Not just a dummy one. This buffer includes some suboptions.
+    OptionBuffer agent_info_buf = createAgentInformationOption();
+    LibDhcpTest::testStdOptionDefs4(DHO_DHCP_AGENT_OPTIONS,
+                                    agent_info_buf.begin(),
+                                    agent_info_buf.end(),
+                                    typeid(OptionCustom),
+                                    "dhcp-agent-options-space");
 
     LibDhcpTest::testStdOptionDefs4(DHO_AUTHENTICATE, begin, end,
                                     typeid(Option));
@@ -761,7 +939,7 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
                                     typeid(Option));
 
     LibDhcpTest::testStdOptionDefs4(DHO_VIVSO_SUBOPTIONS, begin, end,
-                                    typeid(Option));
+                                    typeid(OptionVendor));
 }
 
 // Test that definitions of standard options have been initialized
@@ -838,7 +1016,7 @@ TEST_F(LibDhcpTest, stdOptionDefs6) {
                                     typeid(OptionCustom));
 
     LibDhcpTest::testStdOptionDefs6(D6O_VENDOR_OPTS, begin, end,
-                                    typeid(OptionInt<uint32_t>),
+                                    typeid(OptionVendor),
                                     "vendor-opts-space");
 
     LibDhcpTest::testStdOptionDefs6(D6O_INTERFACE_ID, begin, end,
@@ -866,8 +1044,8 @@ TEST_F(LibDhcpTest, stdOptionDefs6) {
     LibDhcpTest::testStdOptionDefs6(D6O_IA_PD, begin, end,
                                     typeid(Option6IA));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_IAPREFIX, begin, end,
-                                    typeid(OptionCustom));
+    LibDhcpTest::testStdOptionDefs6(D6O_IAPREFIX, begin, begin + 25,
+                                    typeid(Option6IAPrefix));
 
     LibDhcpTest::testStdOptionDefs6(D6O_NIS_SERVERS, begin, end,
                                     typeid(Option6AddrLst));
diff --git a/src/lib/dhcp/tests/option6_ia_unittest.cc b/src/lib/dhcp/tests/option6_ia_unittest.cc
index 071edb9..2a91505 100644
--- a/src/lib/dhcp/tests/option6_ia_unittest.cc
+++ b/src/lib/dhcp/tests/option6_ia_unittest.cc
@@ -18,6 +18,7 @@
 #include <dhcp/option.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
 #include <util/buffer.h>
 
 #include <boost/scoped_ptr.hpp>
@@ -43,71 +44,96 @@ public:
             buf_[i] = 255 - i;
         }
     }
-    OptionBuffer buf_;
-    OutputBuffer outBuf_;
-};
 
-TEST_F(Option6IATest, basic) {
-    buf_[0] = 0xa1; // iaid
-    buf_[1] = 0xa2;
-    buf_[2] = 0xa3;
-    buf_[3] = 0xa4;
+    /// @brief performs basic checks on IA option
+    ///
+    /// Check that an option can be built based on incoming buffer and that
+    /// the option contains expected values.
+    /// @param type specifies option type (IA_NA or IA_PD)
+    void checkIA(uint16_t type) {
+        buf_[0] = 0xa1; // iaid
+        buf_[1] = 0xa2;
+        buf_[2] = 0xa3;
+        buf_[3] = 0xa4;
+
+        buf_[4] = 0x81; // T1
+        buf_[5] = 0x02;
+        buf_[6] = 0x03;
+        buf_[7] = 0x04;
+
+        buf_[8] = 0x84; // T2
+        buf_[9] = 0x03;
+        buf_[10] = 0x02;
+        buf_[11] = 0x01;
+
+        // Create an option
+        // unpack() is called from constructor
+        scoped_ptr<Option6IA> opt;
+        ASSERT_NO_THROW(opt.reset(new Option6IA(type, buf_.begin(),
+                                                buf_.begin() + 12)));
 
-    buf_[4] = 0x81; // T1
-    buf_[5] = 0x02;
-    buf_[6] = 0x03;
-    buf_[7] = 0x04;
+        EXPECT_EQ(Option::V6, opt->getUniverse());
+        EXPECT_EQ(type, opt->getType());
+        EXPECT_EQ(0xa1a2a3a4, opt->getIAID());
+        EXPECT_EQ(0x81020304, opt->getT1());
+        EXPECT_EQ(0x84030201, opt->getT2());
 
-    buf_[8] = 0x84; // T2
-    buf_[9] = 0x03;
-    buf_[10] = 0x02;
-    buf_[11] = 0x01;
+        // Pack this option again in the same buffer, but in
+        // different place
 
-    // Create an option
-    // unpack() is called from constructor
-    scoped_ptr<Option6IA> opt(new Option6IA(D6O_IA_NA,
-                                            buf_.begin(),
-                                            buf_.begin() + 12));
+        // Test for pack()
+        ASSERT_NO_THROW(opt->pack(outBuf_));
 
-    EXPECT_EQ(Option::V6, opt->getUniverse());
-    EXPECT_EQ(D6O_IA_NA, opt->getType());
-    EXPECT_EQ(0xa1a2a3a4, opt->getIAID());
-    EXPECT_EQ(0x81020304, opt->getT1());
-    EXPECT_EQ(0x84030201, opt->getT2());
+        // 12 bytes header + 4 bytes content
+        EXPECT_EQ(12, opt->len() - opt->getHeaderLen());
+        EXPECT_EQ(type, opt->getType());
 
-    // Pack this option again in the same buffer, but in
-    // different place
+        EXPECT_EQ(16, outBuf_.getLength()); // lenght(IA_NA) = 16
 
-    // Test for pack()
-    opt->pack(outBuf_);
+        // Check if pack worked properly:
+        InputBuffer out(outBuf_.getData(), outBuf_.getLength());
 
-    // 12 bytes header + 4 bytes content
-    EXPECT_EQ(12, opt->len() - opt->getHeaderLen());
-    EXPECT_EQ(D6O_IA_NA, opt->getType());
+        // - if option type is correct
+        EXPECT_EQ(type, out.readUint16());
 
-    EXPECT_EQ(16, outBuf_.getLength()); // lenght(IA_NA) = 16
+        // - if option length is correct
+        EXPECT_EQ(12, out.readUint16());
 
-    // Check if pack worked properly:
-    InputBuffer out(outBuf_.getData(), outBuf_.getLength());
+        // - if iaid is correct
+        EXPECT_EQ(0xa1a2a3a4, out.readUint32() );
 
-    // - if option type is correct
-    EXPECT_EQ(D6O_IA_NA, out.readUint16());
+        // - if T1 is correct
+        EXPECT_EQ(0x81020304, out.readUint32() );
 
-    // - if option length is correct
-    EXPECT_EQ(12, out.readUint16());
+        // - if T1 is correct
+        EXPECT_EQ(0x84030201, out.readUint32() );
 
-    // - if iaid is correct
-    EXPECT_EQ(0xa1a2a3a4, out.readUint32() );
+        EXPECT_NO_THROW(opt.reset());
+    }
 
-   // - if T1 is correct
-    EXPECT_EQ(0x81020304, out.readUint32() );
+    OptionBuffer buf_;
+    OutputBuffer outBuf_;
+};
 
-    // - if T1 is correct
-    EXPECT_EQ(0x84030201, out.readUint32() );
+TEST_F(Option6IATest, basic) {
+    checkIA(D6O_IA_NA);
+}
 
-    EXPECT_NO_THROW(opt.reset());
+TEST_F(Option6IATest, pdBasic) {
+    checkIA(D6O_IA_PD);
 }
 
+// Check that this class cannot be used for IA_TA (IA_TA has no T1, T2 fields
+// and people tend to think that if it's good for IA_NA and IA_PD, it can
+// be used for IA_TA as well and that is not true)
+TEST_F(Option6IATest, taForbidden) {
+    EXPECT_THROW(Option6IA(D6O_IA_TA, buf_.begin(), buf_.begin() + 50),
+                 BadValue);
+
+    EXPECT_THROW(Option6IA(D6O_IA_TA, 123), BadValue);
+}
+
+// Check that getters/setters are working as expected.
 TEST_F(Option6IATest, simple) {
     scoped_ptr<Option6IA> ia(new Option6IA(D6O_IA_NA, 1234));
 
@@ -131,12 +157,8 @@ TEST_F(Option6IATest, simple) {
     EXPECT_NO_THROW(ia.reset());
 }
 
-
-// test if option can build suboptions
-TEST_F(Option6IATest, suboptions_pack) {
-    buf_[0] = 0xff;
-    buf_[1] = 0xfe;
-    buf_[2] = 0xfc;
+// test if the option can build suboptions
+TEST_F(Option6IATest, suboptionsPack) {
 
     scoped_ptr<Option6IA> ia(new Option6IA(D6O_IA_NA, 0x13579ace));
     ia->setT1(0x2345);
@@ -154,6 +176,7 @@ TEST_F(Option6IATest, suboptions_pack) {
     ASSERT_EQ(4, sub1->len());
     ASSERT_EQ(48, ia->len());
 
+    // This contains expected on-wire format
     uint8_t expected[] = {
         D6O_IA_NA/256, D6O_IA_NA%256, // type
         0, 44, // length
@@ -175,18 +198,69 @@ TEST_F(Option6IATest, suboptions_pack) {
     };
 
     ia->pack(outBuf_);
-    ASSERT_EQ(48, outBuf_.getLength());
 
+    ASSERT_EQ(48, outBuf_.getLength());
     EXPECT_EQ(0, memcmp(outBuf_.getData(), expected, 48));
-
     EXPECT_NO_THROW(ia.reset());
 }
 
+// test if IA_PD option can build IAPREFIX suboptions
+TEST_F(Option6IATest, pdSuboptionsPack) {
+
+    // Let's build IA_PD
+    scoped_ptr<Option6IA> ia;
+    ASSERT_NO_THROW(ia.reset(new Option6IA(D6O_IA_PD, 0x13579ace)));
+    ia->setT1(0x2345);
+    ia->setT2(0x3456);
+
+    // Put some dummy option in it
+    OptionPtr sub1(new Option(Option::V6, 0xcafe));
+
+    // Put a valid IAPREFIX option in it
+    boost::shared_ptr<Option6IAPrefix> addr1(
+        new Option6IAPrefix(D6O_IAPREFIX, IOAddress("2001:db8:1234:5678::abcd"),
+                            91, 0x5000, 0x7000));
+
+    ia->addOption(sub1);
+    ia->addOption(addr1);
+
+    ASSERT_EQ(29, addr1->len());
+    ASSERT_EQ(4, sub1->len());
+    ASSERT_EQ(49, ia->len());
+
+    uint8_t expected[] = {
+        D6O_IA_PD/256, D6O_IA_PD%256, // type
+        0, 45, // length
+        0x13, 0x57, 0x9a, 0xce, // iaid
+        0, 0, 0x23, 0x45,  // T1
+        0, 0, 0x34, 0x56,  // T2
+
+        // iaprefix suboption
+        D6O_IAPREFIX/256, D6O_IAPREFIX%256, // type
+        0, 25, // len
+        0, 0, 0x50, 0, // preferred-lifetime
+        0, 0, 0x70, 0, // valid-lifetime
+        91, // prefix length
+        0x20, 0x01, 0xd, 0xb8, 0x12,0x34, 0x56, 0x78,
+        0, 0, 0, 0, 0, 0, 0xab, 0xcd, // IP address
+
+        // suboption
+        0xca, 0xfe, // type
+        0, 0 // len
+    };
+
+    ia->pack(outBuf_);
+    ASSERT_EQ(49, outBuf_.getLength());
+
+    EXPECT_EQ(0, memcmp(outBuf_.getData(), expected, 49));
+
+    EXPECT_NO_THROW(ia.reset());
+}
 
 // test if option can parse suboptions
 TEST_F(Option6IATest, suboptions_unpack) {
     // sizeof (expected) = 48 bytes
-    uint8_t expected[] = {
+    const uint8_t expected[] = {
         D6O_IA_NA / 256, D6O_IA_NA % 256, // type
         0, 28, // length
         0x13, 0x57, 0x9a, 0xce, // iaid
diff --git a/src/lib/dhcp/tests/option6_iaaddr_unittest.cc b/src/lib/dhcp/tests/option6_iaaddr_unittest.cc
index e28e2e0..d2e6a15 100644
--- a/src/lib/dhcp/tests/option6_iaaddr_unittest.cc
+++ b/src/lib/dhcp/tests/option6_iaaddr_unittest.cc
@@ -106,4 +106,19 @@ TEST_F(Option6IAAddrTest, basic) {
     EXPECT_NO_THROW(opt.reset());
 }
 
+/// @todo: Write test for (type, addr, pref, valid) constructor
+/// See option6_iaprefix_unittest.cc for similar test
+
+// Tests if broken usage causes exception to be thrown
+TEST_F(Option6IAAddrTest, negative) {
+
+    // Too short. Minimum length is 24
+    EXPECT_THROW(Option6IAAddr(D6O_IAADDR, buf_.begin(), buf_.begin() + 23),
+                 OutOfRange);
+
+    // This option is for IPv6 addresses only
+    EXPECT_THROW(Option6IAAddr(D6O_IAADDR, isc::asiolink::IOAddress("192.0.2.1"),
+                               1000, 2000), BadValue);
+}
+
 }
diff --git a/src/lib/dhcp/tests/option6_iaprefix_unittest.cc b/src/lib/dhcp/tests/option6_iaprefix_unittest.cc
new file mode 100644
index 0000000..e5dd05e
--- /dev/null
+++ b/src/lib/dhcp/tests/option6_iaprefix_unittest.cc
@@ -0,0 +1,188 @@
+// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/option6_iaprefix.h>
+#include <util/buffer.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <iostream>
+#include <sstream>
+
+#include <arpa/inet.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::util;
+using namespace isc::asiolink;
+
+namespace {
+class Option6IAPrefixTest : public ::testing::Test {
+public:
+    Option6IAPrefixTest() : buf_(255), outBuf_(255) {
+        for (int i = 0; i < 255; i++) {
+            buf_[i] = 255 - i;
+        }
+    }
+
+    /// @brief creates on-wire representation of IAPREFIX option
+    ///
+    /// buf_ field is set up to have IAPREFIX with preferred=1000,
+    /// valid=3000000000 and prefix beign 2001:db8:1::dead:beef/77
+    void setExampleBuffer() {
+        for (int i = 0; i < 255; i++) {
+            buf_[i] = 0;
+        }
+
+        buf_[ 0] = 0x00;
+        buf_[ 1] = 0x00;
+        buf_[ 2] = 0x03;
+        buf_[ 3] = 0xe8; // preferred lifetime = 1000
+
+        buf_[ 4]  = 0xb2;
+        buf_[ 5] = 0xd0;
+        buf_[ 6] = 0x5e;
+        buf_[ 7] = 0x00; // valid lifetime = 3,000,000,000
+
+        buf_[ 8] = 77; // Prefix length = 77
+
+        buf_[ 9] = 0x20;
+        buf_[10] = 0x01;
+        buf_[11] = 0x0d;
+        buf_[12] = 0xb8;
+        buf_[13] = 0x00;
+        buf_[14] = 0x01;
+        buf_[21] = 0xde;
+        buf_[22] = 0xad;
+        buf_[23] = 0xbe;
+        buf_[24] = 0xef; // 2001:db8:1::dead:beef
+    }
+
+
+    /// @brief Checks whether specified IAPREFIX option meets expected values
+    ///
+    /// To be used with option generated by setExampleBuffer
+    ///
+    /// @param opt IAPREFIX option being tested
+    /// @param expected_type expected option type
+    void checkOption(Option6IAPrefix& opt, uint16_t expected_type) {
+
+        // Check if all fields have expected values
+        EXPECT_EQ(Option::V6, opt.getUniverse());
+        EXPECT_EQ(expected_type, opt.getType());
+        EXPECT_EQ("2001:db8:1::dead:beef", opt.getAddress().toText());
+        EXPECT_EQ(1000, opt.getPreferred());
+        EXPECT_EQ(3000000000U, opt.getValid());
+        EXPECT_EQ(77, opt.getLength());
+
+        // 4 bytes header + 25 bytes content
+        EXPECT_EQ(Option::OPTION6_HDR_LEN + Option6IAPrefix::OPTION6_IAPREFIX_LEN,
+                  opt.len());
+    }
+
+    /// @brief Checks whether content of output buffer is correct
+    ///
+    /// Output buffer is expected to be filled with an option matchin
+    /// buf_ content as defined in setExampleBuffer().
+    ///
+    /// @param expected_type expected option type
+    void checkOutputBuffer(uint16_t expected_type) {
+        // Check if pack worked properly:
+        const uint8_t* out = (const uint8_t*)outBuf_.getData();
+
+        // - if option type is correct
+        EXPECT_EQ(expected_type, out[0]*256 + out[1]);
+
+        // - if option length is correct
+        EXPECT_EQ(25, out[2]*256 + out[3]);
+
+        // - if option content is correct
+        EXPECT_EQ(0, memcmp(out + 4, &buf_[0], 25));
+    }
+
+    OptionBuffer buf_;
+    OutputBuffer outBuf_;
+};
+
+// Tests if receiving option can be parsed correctly
+TEST_F(Option6IAPrefixTest, basic) {
+
+    setExampleBuffer();
+
+    // Create an option (unpack content)
+    boost::scoped_ptr<Option6IAPrefix> opt;
+    ASSERT_NO_THROW(opt.reset(new Option6IAPrefix(D6O_IAPREFIX, buf_.begin(),
+                                                  buf_.begin() + 25)));
+    ASSERT_TRUE(opt);
+
+    // Pack this option
+    opt->pack(outBuf_);
+    EXPECT_EQ(29, outBuf_.getLength());
+
+    checkOption(*opt, D6O_IAPREFIX);
+
+    checkOutputBuffer(D6O_IAPREFIX);
+
+    // Check that option can be disposed safely
+    EXPECT_NO_THROW(opt.reset());
+}
+
+// Checks whether a new option can be built correctly
+TEST_F(Option6IAPrefixTest, build) {
+
+    boost::scoped_ptr<Option6IAPrefix> opt;
+    setExampleBuffer();
+
+    ASSERT_NO_THROW(opt.reset(new Option6IAPrefix(12345,
+                    IOAddress("2001:db8:1::dead:beef"), 77, 1000, 3000000000u)));
+    ASSERT_TRUE(opt);
+
+    checkOption(*opt, 12345);
+
+    // Check if we can build it properly
+    EXPECT_NO_THROW(opt->pack(outBuf_));
+    EXPECT_EQ(29, outBuf_.getLength());
+    checkOutputBuffer(12345);
+
+    // Check that option can be disposed safely
+    EXPECT_NO_THROW(opt.reset());
+}
+
+// Checks negative cases
+TEST_F(Option6IAPrefixTest, negative) {
+
+    // Truncated option (at least 25 bytes is needed)
+    EXPECT_THROW(Option6IAPrefix(D6O_IAPREFIX, buf_.begin(), buf_.begin() + 24),
+                 OutOfRange);
+
+    // Empty option
+    EXPECT_THROW(Option6IAPrefix(D6O_IAPREFIX, buf_.begin(), buf_.begin()),
+                 OutOfRange);
+
+    // This is for IPv6 prefixes only
+    EXPECT_THROW(Option6IAPrefix(12345, IOAddress("192.0.2.1"), 77, 1000, 2000),
+                 BadValue);
+
+    // Prefix length can't be larger than 128
+    EXPECT_THROW(Option6IAPrefix(12345, IOAddress("192.0.2.1"), 255, 1000, 2000),
+                 BadValue);
+}
+
+}
diff --git a/src/lib/dhcp/tests/option_custom_unittest.cc b/src/lib/dhcp/tests/option_custom_unittest.cc
index c04bbc2..4add2d8 100644
--- a/src/lib/dhcp/tests/option_custom_unittest.cc
+++ b/src/lib/dhcp/tests/option_custom_unittest.cc
@@ -32,6 +32,59 @@ public:
     /// @brief Constructor.
     OptionCustomTest() { }
 
+    /// @brief Appends DHCPv4 suboption in the on-wire format to the buffer.
+    ///
+    /// @param buf A buffer to which suboption is appended.
+    void appendV4Suboption(OptionBuffer& buf) {
+        const uint8_t subopt_data[] = {
+            0x01, 0x02, // Option type = 1, length = 2
+            0x01, 0x02  // Two bytes of data
+        };
+        buf.insert(buf.end(), subopt_data, subopt_data + sizeof(subopt_data));
+    }
+
+    /// @brief Check if the parsed option has a suboption.
+    ///
+    /// @param opt An option in which suboption is expected.
+    /// @return Assertion result indicating that the suboption is
+    /// present (success) or missing (failure).
+    ::testing::AssertionResult hasV4Suboption(OptionCustom* opt) {
+        OptionPtr subopt = opt->getOption(1);
+        if (!subopt) {
+            return (::testing::AssertionFailure(::testing::Message()
+                                                << "Suboption of OptionCustom"
+                                                " is missing"));
+        }
+        return (::testing::AssertionSuccess());
+    }
+
+    /// @brief Appends DHCPv6 suboption in the on-wire format to the buffer.
+    ///
+    /// @param buf A buffer to which suboption is appended.
+    void appendV6Suboption(OptionBuffer& buf) {
+        const uint8_t subopt_data[] = {
+            0x00, 0x01, // Option type = 1
+            0x00, 0x04, // Option length = 4
+            0x01, 0x02, 0x03, 0x04 // Four bytes of data
+        };
+        buf.insert(buf.end(), subopt_data, subopt_data + sizeof(subopt_data));
+    }
+
+    /// @brief Check if the parsed option has a suboption.
+    ///
+    /// @param opt An option in which suboption is expected.
+    /// @return Assertion result indicating that the suboption is
+    /// present (success) or missing (failure).
+    ::testing::AssertionResult hasV6Suboption(OptionCustom* opt) {
+        OptionPtr subopt = opt->getOption(1);
+        if (!subopt) {
+            return (::testing::AssertionFailure(::testing::Message()
+                                                << "Suboption of OptionCustom"
+                                                " is missing"));
+        }
+        return (::testing::AssertionSuccess());
+    }
+
     /// @brief Write IP address into a buffer.
     ///
     /// @param address address to be written.
@@ -114,23 +167,30 @@ TEST_F(OptionCustomTest, constructor) {
 // The purpose of this test is to verify that 'empty' option definition can
 // be used to create an instance of custom option.
 TEST_F(OptionCustomTest, emptyData) {
-    OptionDefinition opt_def("OPTION_FOO", 232, "empty");
+    OptionDefinition opt_def("option-foo", 232, "empty", "option-foo-space");
 
+    // Create a buffer holding 1 suboption.
     OptionBuffer buf;
+    appendV4Suboption(buf);
+
     boost::scoped_ptr<OptionCustom> option;
     ASSERT_NO_THROW(
-        option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(), buf.end()));
+        option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(),
+                                      buf.end()));
     );
     ASSERT_TRUE(option);
 
     // Option is 'empty' so no data fields are expected.
     EXPECT_EQ(0, option->getDataFieldsNum());
+
+    // Check that suboption has been parsed.
+    EXPECT_TRUE(hasV4Suboption(option.get()));
 }
 
 // The purpose of this test is to verify that the option definition comprising
 // a binary value can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, binaryData) {
-    OptionDefinition opt_def("OPTION_FOO", 231, "binary");
+    OptionDefinition opt_def("option-foo", 231, "binary", "option-foo-space");
 
     // Create a buffer holding some binary data. This data will be
     // used as reference when we read back the data from a created
@@ -139,6 +199,11 @@ TEST_F(OptionCustomTest, binaryData) {
     for (int i = 0; i < 14; ++i) {
         buf_in[i] = i;
     }
+
+    // Append suboption data. This data should NOT be recognized when
+    // option has a binary format.
+    appendV4Suboption(buf_in);
+
     // Use scoped pointer because it allows to declare the option
     // in the function scope and initialize it under ASSERT.
     boost::scoped_ptr<OptionCustom> option;
@@ -169,20 +234,24 @@ TEST_F(OptionCustomTest, binaryData) {
                                       buf_in.end())),
         isc::OutOfRange
     );
+
+    // Suboptions are not recognized for the binary formats because as it is
+    // a variable length format. Therefore, we expect that there are no
+    // suboptions in the parsed option.
+    EXPECT_FALSE(option->getOption(1));
 }
 
 // The purpose of this test is to verify that an option definition comprising
 // a single boolean value can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, booleanData) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "boolean");
+    OptionDefinition opt_def("option-foo", 1000, "boolean", "option-foo-space");
 
     OptionBuffer buf;
     // Push back the value that represents 'false'.
     buf.push_back(0);
-    // Push back the 'true' value. Note that this value should
-    // be ignored by custom option because it holds single boolean
-    // value (according to option definition).
-    buf.push_back(1);
+
+    // Append suboption. It should be present in the parsed packet.
+    appendV6Suboption(buf);
 
     boost::scoped_ptr<OptionCustom> option;
     ASSERT_NO_THROW(
@@ -202,10 +271,14 @@ TEST_F(OptionCustomTest, booleanData) {
     ASSERT_NO_THROW(value = option->readBoolean(0));
     EXPECT_FALSE(value);
 
+    // There should be one suboption present.
+    EXPECT_TRUE(hasV6Suboption(option.get()));
+
     // Check that the option with "no data" is rejected.
     buf.clear();
     EXPECT_THROW(
-        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end())),
+        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(),
+                                      buf.end())),
         isc::OutOfRange
     );
 }
@@ -213,7 +286,7 @@ TEST_F(OptionCustomTest, booleanData) {
 // The purpose of this test is to verify that the data from a buffer
 // can be read as FQDN.
 TEST_F(OptionCustomTest, fqdnData) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "fqdn");
+    OptionDefinition opt_def("option-foo", 1000, "fqdn", "option-foo-space");
 
     const char data[] = {
         8, 109, 121, 100, 111, 109, 97, 105, 110, // "mydomain"
@@ -224,6 +297,10 @@ TEST_F(OptionCustomTest, fqdnData) {
 
     std::vector<uint8_t> buf(data, data + sizeof(data));
 
+    // The FQDN has a certain boundary. Right after FQDN it should be
+    // possible to append suboption and parse it correctly.
+    appendV6Suboption(buf);
+
     boost::scoped_ptr<OptionCustom> option;
     ASSERT_NO_THROW(
         option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end()));
@@ -235,6 +312,9 @@ TEST_F(OptionCustomTest, fqdnData) {
     std::string domain0 = option->readFqdn(0);
     EXPECT_EQ("mydomain.example.com.", domain0);
 
+    // This option should have one suboption.
+    EXPECT_TRUE(hasV6Suboption(option.get()));
+
     // Check that the option with truncated data can't be created.
     EXPECT_THROW(
         option.reset(new OptionCustom(opt_def, Option::V6,
@@ -246,12 +326,15 @@ TEST_F(OptionCustomTest, fqdnData) {
 // The purpose of this test is to verify that the option definition comprising
 // 16-bit signed integer value can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, int16Data) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "int16");
+    OptionDefinition opt_def("option-foo", 1000, "int16", "option-foo-space");
 
     OptionBuffer buf;
     // Store signed integer value in the input buffer.
     writeInt<int16_t>(-234, buf);
 
+    // Append suboption.
+    appendV6Suboption(buf);
+
     // Create custom option.
     boost::scoped_ptr<OptionCustom> option;
     ASSERT_NO_THROW(
@@ -268,6 +351,9 @@ TEST_F(OptionCustomTest, int16Data) {
     ASSERT_NO_THROW(value = option->readInteger<int16_t>(0));
     EXPECT_EQ(-234, value);
 
+    // Parsed option should have one suboption.
+    EXPECT_TRUE(hasV6Suboption(option.get()));
+
     // Check that the option is not created when a buffer is
     // too short (1 byte instead of 2 bytes).
     EXPECT_THROW(
@@ -279,11 +365,13 @@ TEST_F(OptionCustomTest, int16Data) {
 // The purpose of this test is to verify that the option definition comprising
 // 32-bit signed integer value can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, int32Data) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "int32");
+    OptionDefinition opt_def("option-foo", 1000, "int32", "option-foo-space");
 
     OptionBuffer buf;
     writeInt<int32_t>(-234, buf);
-    writeInt<int32_t>(100, buf);
+
+    // Append one suboption.
+    appendV6Suboption(buf);
 
     // Create custom option.
     boost::scoped_ptr<OptionCustom> option;
@@ -301,6 +389,9 @@ TEST_F(OptionCustomTest, int32Data) {
     ASSERT_NO_THROW(value = option->readInteger<int32_t>(0));
     EXPECT_EQ(-234, value);
 
+    // The parsed option should have one suboption.
+    EXPECT_TRUE(hasV6Suboption(option.get()));
+
     // Check that the option is not created when a buffer is
     // too short (3 bytes instead of 4 bytes).
     EXPECT_THROW(
@@ -312,12 +403,16 @@ TEST_F(OptionCustomTest, int32Data) {
 // The purpose of this test is to verify that the option definition comprising
 // single IPv4 address can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, ipv4AddressData) {
-    OptionDefinition opt_def("OPTION_FOO", 231, "ipv4-address");
+    OptionDefinition opt_def("OPTION_FOO", 231, "ipv4-address",
+                             "option-foo-space");
 
     // Create input buffer.
     OptionBuffer buf;
     writeAddress(IOAddress("192.168.100.50"), buf);
 
+    // Append one suboption.
+    appendV4Suboption(buf);
+
     // Create custom option.
     boost::scoped_ptr<OptionCustom> option;
     ASSERT_NO_THROW(
@@ -334,6 +429,9 @@ TEST_F(OptionCustomTest, ipv4AddressData) {
 
     EXPECT_EQ("192.168.100.50", address.toText());
 
+    // Parsed option should have one suboption.
+    EXPECT_TRUE(hasV4Suboption(option.get()));
+
     // Check that option is not created if the provided buffer is
     // too short (use 3 bytes instead of 4).
     EXPECT_THROW(
@@ -345,12 +443,16 @@ TEST_F(OptionCustomTest, ipv4AddressData) {
 // The purpose of this test is to verify that the option definition comprising
 // single IPv6 address can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, ipv6AddressData) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "ipv6-address");
+    OptionDefinition opt_def("option-foo", 1000, "ipv6-address",
+                             "option-foo-space");
 
     // Initialize input buffer.
     OptionBuffer buf;
     writeAddress(IOAddress("2001:db8:1::100"), buf);
 
+    // Append suboption.
+    appendV6Suboption(buf);
+
     // Create custom option using input buffer.
     boost::scoped_ptr<OptionCustom> option;
     ASSERT_NO_THROW(
@@ -369,6 +471,9 @@ TEST_F(OptionCustomTest, ipv6AddressData) {
 
     EXPECT_EQ("2001:db8:1::100", address.toText());
 
+    // Parsed option should have one suboption.
+    EXPECT_TRUE(hasV6Suboption(option.get()));
+
     // Check that option is not created if the provided buffer is
     // too short (use 15 bytes instead of 16).
     EXPECT_THROW(
@@ -382,12 +487,19 @@ TEST_F(OptionCustomTest, ipv6AddressData) {
 // The purpose of this test is to verify that the option definition comprising
 // string value can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, stringData) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "string");
+    OptionDefinition opt_def("option-foo", 1000, "string", "option-foo-space");
 
     // Create an input buffer holding some string value.
     OptionBuffer buf;
     writeString("hello world!", buf);
 
+    // Append suboption. It should not be detected because the string field
+    //  has variable length.
+    appendV6Suboption(buf);
+
+    // Append suboption. Since the option has variable length string field,
+    // the suboption should not be recognized.
+
     // Create custom option using input buffer.
     boost::scoped_ptr<OptionCustom> option;
     ASSERT_NO_THROW(
@@ -403,7 +515,14 @@ TEST_F(OptionCustomTest, stringData) {
     std::string value;
     ASSERT_NO_THROW(value = option->readString(0));
 
-    EXPECT_EQ("hello world!", value);
+    // The initial part of the string should contain the actual string.
+    // The rest of it is a garbage from an attempt to decode suboption
+    // as a string.
+    ASSERT_EQ(20, value.size());
+    EXPECT_EQ("hello world!", value.substr(0, 12));
+
+    // No suboption should be present.
+    EXPECT_FALSE(option->getOption(1));
 
     // Check that option will not be created if empty buffer is provided.
     buf.clear();
@@ -416,7 +535,7 @@ TEST_F(OptionCustomTest, stringData) {
 // The purpose of this test is to verify that the option definition comprising
 // an array of boolean values can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, booleanDataArray) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "boolean", true);
+    OptionDefinition opt_def("option-foo", 1000, "boolean", true);
 
     // Create a buffer with 5 values that represent array of
     // booleans.
@@ -472,7 +591,7 @@ TEST_F(OptionCustomTest, booleanDataArray) {
 // an array of 32-bit signed integer values can be used to create an instance
 // of custom option.
 TEST_F(OptionCustomTest, uint32DataArray) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "uint32", true);
+    OptionDefinition opt_def("option-foo", 1000, "uint32", true);
 
     // Create an input buffer that holds 4 uint32 values that
     // represent an array.
@@ -656,6 +775,47 @@ TEST_F(OptionCustomTest, fqdnDataArray) {
     EXPECT_EQ("example.com.", domain1);
 }
 
+// The purpose of this test is to verify that the opton definition comprising
+// a record of fixed-size fields can be used to create an option with a
+// suboption.
+TEST_F(OptionCustomTest, recordDataWithSuboption) {
+    OptionDefinition opt_def("option-foo", 1000, "record", "option-foo-space");
+    ASSERT_NO_THROW(opt_def.addRecordField("uint32"));
+    ASSERT_NO_THROW(opt_def.addRecordField("ipv4-address"));
+
+    // Create a buffer with two fields: 4-byte number and IPv4 address.
+    OptionBuffer buf;
+    writeInt<uint32_t>(0x01020304, buf);
+    writeAddress(IOAddress("192.168.0.1"), buf);
+
+    // Append a suboption. It should be correctly parsed because option fields
+    // preceding this option have fixed (known) size.
+    appendV6Suboption(buf);
+
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+         option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(),
+                                       buf.end()));
+    );
+    ASSERT_TRUE(option);
+
+    // We should have two data fields parsed.
+    ASSERT_EQ(2, option->getDataFieldsNum());
+
+    // Validate values in fields.
+    uint32_t value0 = 0;
+    ASSERT_NO_THROW(value0 = option->readInteger<uint32_t>(0));
+    EXPECT_EQ(0x01020304, value0);
+
+    IOAddress value1 = 0;
+    ASSERT_NO_THROW(value1 = option->readAddress(1));
+    EXPECT_EQ("192.168.0.1", value1.toText());
+
+    // Parsed option should have one suboption.
+    EXPECT_TRUE(hasV6Suboption(option.get()));
+
+}
+
 // The purpose of this test is to verify that the option definition comprising
 // a record of various data fields can be used to create an instance of
 // custom option.
diff --git a/src/lib/dhcp/tests/option_definition_unittest.cc b/src/lib/dhcp/tests/option_definition_unittest.cc
index d3b766d..357ed56 100644
--- a/src/lib/dhcp/tests/option_definition_unittest.cc
+++ b/src/lib/dhcp/tests/option_definition_unittest.cc
@@ -439,7 +439,7 @@ TEST_F(OptionDefinitionTest, ipv4AddressArrayTokenized) {
     EXPECT_TRUE(std::equal(addrs.begin(), addrs.end(), addrs_returned.begin()));
 }
 
-// The purpose of thie test is to verify that option definition for
+// The purpose of this test is to verify that option definition for
 // 'empty' option can be created and that it returns 'empty' option.
 TEST_F(OptionDefinitionTest, empty) {
     OptionDefinition opt_def("OPTION_RAPID_COMMIT", D6O_RAPID_COMMIT, "empty");
@@ -464,6 +464,49 @@ TEST_F(OptionDefinitionTest, empty) {
     EXPECT_EQ(0, option_v4->getData().size());
 }
 
+// The purpose of this test is to verify that when the empty option encapsulates
+// some option space, an instance of the OptionCustom is returned and its
+// suboptions are decoded.
+TEST_F(OptionDefinitionTest, emptyWithSuboptions) {
+    // Create an instance of the 'empty' option definition. This option
+    // encapsulates 'option-foo-space' so when we create a new option
+    // with this definition the OptionCustom should be returned. The
+    // Option Custom is generic option which support variety of formats
+    // and supports decoding suboptions.
+    OptionDefinition opt_def("option-foo", 1024, "empty", "option-foo-space");
+
+    // Define a suboption.
+    const uint8_t subopt_data[] = {
+        0x04, 0x01,  // Option code 1025
+        0x00, 0x04,  // Option len = 4
+        0x01, 0x02, 0x03, 0x04 // Option data
+    };
+
+    // Create an option, having option code 1024 from the definition. Pass
+    // the option buffer containing suboption.
+    OptionPtr option_v6;
+    ASSERT_NO_THROW(
+        option_v6 = opt_def.optionFactory(Option::V6, 1024,
+                                          OptionBuffer(subopt_data,
+                                                       subopt_data +
+                                                       sizeof(subopt_data)))
+    );
+    // Returned option should be of the OptionCustom type.
+    ASSERT_TRUE(typeid(*option_v6) == typeid(OptionCustom));
+    // Sanity-check length, universe etc.
+    EXPECT_EQ(Option::V6, option_v6->getUniverse());
+    EXPECT_EQ(4, option_v6->getHeaderLen());
+    // This option should have one suboption with the code of 1025.
+    OptionPtr subopt_v6 = option_v6->getOption(1025);
+    EXPECT_TRUE(subopt_v6);
+    // Check that this suboption holds valid data.
+    EXPECT_EQ(1025, subopt_v6->getType());
+    EXPECT_EQ(Option::V6, subopt_v6->getUniverse());
+    EXPECT_EQ(0, memcmp(&subopt_v6->getData()[0], subopt_data + 4, 4));
+
+    // @todo consider having a similar test for V4.
+}
+
 // The purpose of this test is to verify that definition can be
 // creates for the option that holds binary data.
 TEST_F(OptionDefinitionTest, binary) {
@@ -951,8 +994,8 @@ TEST_F(OptionDefinitionTest, integerInvalidType) {
     // see if it rejects it.
     OptionBuffer buf(1);
     EXPECT_THROW(
-        OptionDefinition::factoryInteger<bool>(Option::V6, D6O_PREFERENCE,
-                                               buf.begin(), buf.end()),
+        OptionDefinition::factoryInteger<bool>(Option::V6, D6O_PREFERENCE, "dhcp6",
+                                               buf.begin(), buf.end(), NULL),
         isc::dhcp::InvalidDataType
     );
 }
diff --git a/src/lib/dhcp/tests/option_unittest.cc b/src/lib/dhcp/tests/option_unittest.cc
index 237e73b..a3aea9f 100644
--- a/src/lib/dhcp/tests/option_unittest.cc
+++ b/src/lib/dhcp/tests/option_unittest.cc
@@ -15,10 +15,12 @@
 #include <config.h>
 
 #include <dhcp/dhcp6.h>
+#include <dhcp/libdhcp++.h>
 #include <dhcp/option.h>
 #include <exceptions/exceptions.h>
 #include <util/buffer.h>
 
+#include <boost/bind.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
@@ -35,6 +37,68 @@ using namespace isc::util;
 using boost::scoped_ptr;
 
 namespace {
+
+/// @brief A class which contains a custom callback function to unpack options.
+///
+/// This is a class used by the tests which verify that the custom callback
+/// functions can be installed to unpack options from a message. When the
+/// callback function is called, the executed_ member is set to true to allow
+/// verification that the callback was really called. Internally, this class
+/// uses libdhcp++ to unpack options so the options parsing algorithm remains
+/// unchanged after installation of the callback.
+class CustomUnpackCallback {
+public:
+
+    /// @brief Constructor
+    ///
+    /// Marks that callback hasn't been called.
+    CustomUnpackCallback()
+        : executed_(false) {
+    }
+
+    /// @brief A callback
+    ///
+    /// Contains custom implementation of the callback.
+    ///
+    /// @param buf a A buffer holding options in on-wire format.
+    /// @param option_space A name of the option space being encapsulated by
+    /// the option being parsed.
+    /// @param [out] options A reference to the collection where parsed options
+    /// will be stored.
+    /// @param relay_msg_offset Reference to a size_t structure. If specified,
+    /// offset to beginning of relay_msg option will be stored in it.
+    /// @param relay_msg_len reference to a size_t structure. If specified,
+    /// length of the relay_msg option will be stored in it.
+    /// @return An offset to the first byte after last parsed option.
+    size_t execute(const OptionBuffer& buf,
+                   const std::string& option_space,
+                   isc::dhcp::OptionCollection& options,
+                   size_t* relay_msg_offset,
+                   size_t* relay_msg_len) {
+        // Set the executed_ member to true to allow verification that the
+        // callback has been actually called.
+        executed_ = true;
+        // Use default implementation of the unpack algorithm to parse options.
+        return (LibDHCP::unpackOptions6(buf, option_space, options, relay_msg_offset,
+                                        relay_msg_len));
+    }
+
+    /// A flag which indicates if callback function has been called.
+    bool executed_;
+};
+
+/// @brief A class which derives from option and exposes protected members.
+class NakedOption : public Option {
+public:
+    /// @brief Constructor
+    ///
+    /// Sets the universe and option type to arbitrary test values.
+    NakedOption() : Option(Option::V6, 258) {
+    }
+
+    using Option::unpackOptions;
+};
+
 class OptionTest : public ::testing::Test {
 public:
     OptionTest(): buf_(255), outBuf_(255) {
@@ -505,4 +569,73 @@ TEST_F(OptionTest, equal) {
 
     EXPECT_TRUE(opt2->equal(opt5));
 }
+
+// This test verifies that the name of the option space being encapsulated by
+// the particular option can be set.
+TEST_F(OptionTest, setEncapsulatedSpace) {
+    Option optv6(Option::V6, 258);
+    EXPECT_TRUE(optv6.getEncapsulatedSpace().empty());
+
+    optv6.setEncapsulatedSpace("dhcp6");
+    EXPECT_EQ("dhcp6", optv6.getEncapsulatedSpace());
+
+    Option optv4(Option::V4, 125);
+    EXPECT_TRUE(optv4.getEncapsulatedSpace().empty());
+
+    optv4.setEncapsulatedSpace("dhcp4");
+    EXPECT_EQ("dhcp4", optv4.getEncapsulatedSpace());
+
+}
+
+// This test verifies that it is possible to specify custom implementation of
+// the option parsing algorithm by installing a callback function.
+TEST_F(OptionTest, unpackCallback) {
+    // Create a buffer which holds two sub options.
+    const char opt_data[] = {
+        0x00, 0x01,  // sub option code  = 1
+        0x00, 0x02,  // sub option length = 2
+        0x00, 0x01,  // sub option data (2 bytes)
+        0x00, 0x02,  // sub option code = 2
+        0x00, 0x02,  // sub option length = 2
+        0x01, 0x01   // sub option data (2 bytes)
+    };
+    OptionBuffer opt_buf(opt_data, opt_data + sizeof(opt_data));
+
+    // Make sure that the flag which indicates if the callback function has
+    // been called is not set. Otherwise, our test doesn't make sense.
+    CustomUnpackCallback cb;
+    ASSERT_FALSE(cb.executed_);
+    // Create an option and install a callback.
+    NakedOption option;
+    // Parameters from _1 to _5 are placeholders for the actual values
+    // to be passed to the callback function. See: boost::bind documentation
+    // at http://www.boost.org/doc/libs/1_54_0/libs/bind/bind.html.
+    // Also, see UnpackOptionsCallback in option.h for description of the
+    // parameter values.
+    option.setCallback(boost::bind(&CustomUnpackCallback::execute, &cb,
+                                   _1, _2, _3, _4, _5));
+    // Parse options. It should result in a call to our callback function.
+    // This function uses LibDHCP to parse options so they should be parsed
+    // correctly.
+    ASSERT_NO_THROW(option.unpackOptions(opt_buf));
+    EXPECT_TRUE(option.getOption(1));
+    EXPECT_TRUE(option.getOption(2));
+    EXPECT_FALSE(option.getOption(3));
+    // The callback should have been registered.
+    EXPECT_TRUE(cb.executed_);
+    // Reset the flag because now we are going to uninstall the callback and
+    // verify that it was NOT called.
+    cb.executed_ = false;
+    // Uninstall the callback.
+    option.setCallback(NULL);
+    ASSERT_NO_THROW(option.unpackOptions(opt_buf));
+    // Options should still get unpacked...
+    EXPECT_TRUE(option.getOption(1));
+    EXPECT_TRUE(option.getOption(2));
+    EXPECT_FALSE(option.getOption(3));
+    // ... but not via callback.
+    EXPECT_FALSE(cb.executed_);
+}
+
+
 }
diff --git a/src/lib/dhcp/tests/option_vendor_unittest.cc b/src/lib/dhcp/tests/option_vendor_unittest.cc
new file mode 100644
index 0000000..9b0f5fa
--- /dev/null
+++ b/src/lib/dhcp/tests/option_vendor_unittest.cc
@@ -0,0 +1,240 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/docsis3_option_defs.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option_vendor.h>
+#include <dhcp/option_int_array.h>
+#include <exceptions/exceptions.h>
+#include <util/buffer.h>
+#include <util/encode/hex.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <iostream>
+#include <sstream>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::util;
+using boost::scoped_ptr;
+
+namespace {
+
+class OptionVendorTest : public ::testing::Test {
+public:
+    OptionVendorTest() {
+    }
+
+    OptionBuffer createV4VendorOptions() {
+
+        // Copied from wireshark, file docsis-*-CG3000DCR-Registration-Filtered.cap
+        // packet #1
+        /* V-I Vendor-specific Information (125)
+           Length: 127
+           Enterprise ID: Cable Television Laboratories, Inc. (4491)
+           Suboption 1: Option Request
+           Suboption 5: Modem capabilties */
+        string from_wireshark = "7d7f0000118b7a01010205750101010201030301010401"
+            "0105010106010107010f0801100901030a01010b01180c01010d0200400e020010"
+            "0f010110040000000211010014010015013f1601011701011801041901041a0104"
+            "1b01201c01021d01081e01201f0110200110210102220101230100240100250101"
+            "260200ff270101";
+
+        OptionBuffer bin;
+        // Decode the hex string and store it in bin (which happens
+        // to be OptionBuffer format)
+        isc::util::encode::decodeHex(from_wireshark, bin);
+
+        return (bin);
+    }
+
+    OptionBuffer createV6VendorOption() {
+
+        // Copied from wireshark, docsis-CG3000DCR-Registration-v6CMM-Filtered.cap
+        // packet #1 (v6 vendor option with lots of cable modem specific data)
+        string from_wireshark = "001100ff0000118b0001000a0020002100220025002600"
+            "02000345434d0003000b45434d3a45524f555445520004000d3242523232395534"
+            "303034344300050004312e30340006000856312e33332e303300070007322e332e"
+            "3052320008000630303039354200090009434733303030444352000a00074e6574"
+            "6765617200230077057501010102010303010104010105010106010107010f0801"
+            "100901030a01010b01180c01010d0200400e0200100f0101100400000002110100"
+            "14010015013f1601011701011801041901041a01041b01201c01021d01081e0120"
+            "1f0110200110210102220101230100240100250101260200ff2701010024000620"
+            "e52ab81514";
+        /* Vendor-specific Information
+                Option: Vendor-specific Information (17)
+                Length: 255
+                Value: 0000118b0001000a00200021002200250026000200034543...
+                Enterprise ID: Cable Television Laboratories, Inc. (4491)
+                Suboption 1: Option Request =  32 33 34 37 38
+                Suboption 2: Device Type = "ECM"
+                Suboption 3: Embedded Components = "ECM:EROUTER"
+                Suboption 4: Serial Number = "2BR229U40044C"
+                Suboption 5: Hardware Version = "1.04"
+                Suboption 6: Software Version = "V1.33.03"
+                Suboption 7: Boot ROM Version = "2.3.0R2"
+                Suboption 8: Organization Unique Identifier = "00095B"
+                Suboption 9: Model Number = "CG3000DCR"
+                Suboption 10: Vendor Name = "Netgear"
+                Suboption 35: TLV5 = 057501010102010303010104010105010106010107010f08...
+                Suboption 36: Device Identifier = 20e52ab81514 */
+
+        OptionBuffer bin;
+        // Decode the hex string and store it in bin (which happens
+        // to be OptionBuffer format)
+        isc::util::encode::decodeHex(from_wireshark, bin);
+
+        return (bin);
+    }
+};
+
+// Basic test for v4 vendor option functionality
+TEST_F(OptionVendorTest, v4Basic) {
+
+    uint32_t vendor_id = 1234;
+
+    scoped_ptr<Option> opt;
+    EXPECT_NO_THROW(opt.reset(new OptionVendor(Option::V4, vendor_id)));
+
+    EXPECT_EQ(Option::V4, opt->getUniverse());
+    EXPECT_EQ(DHO_VIVSO_SUBOPTIONS, opt->getType());
+
+    // Minimal length is 7: 1(type) + 1(length) + 4(vendor-id) + datalen(1)
+    EXPECT_EQ(7, opt->len());
+
+    // Check destructor
+    EXPECT_NO_THROW(opt.reset());
+}
+
+// Basic test for v6 vendor option functionality
+TEST_F(OptionVendorTest, v6Basic) {
+
+    uint32_t vendor_id = 1234;
+
+    scoped_ptr<Option> opt;
+    EXPECT_NO_THROW(opt.reset(new OptionVendor(Option::V6, vendor_id)));
+
+    EXPECT_EQ(Option::V6, opt->getUniverse());
+    EXPECT_EQ(D6O_VENDOR_OPTS, opt->getType());
+
+    // Minimal length is 8: 2(type) + 2(length) + 4(vendor-id)
+    EXPECT_EQ(8, opt->len());
+
+    // Check destructor
+    EXPECT_NO_THROW(opt.reset());
+}
+
+// Tests whether we can parse v4 vendor options properly
+TEST_F(OptionVendorTest, v4Parse) {
+    OptionBuffer binary = createV4VendorOptions();
+
+    // Let's create vendor option based on incoming buffer
+    OptionVendorPtr vendor;
+    ASSERT_NO_THROW(vendor.reset(new OptionVendor(Option::V4, binary.begin() + 2,
+                                                  binary.end())));
+
+    // We know that there are supposed to be 2 options inside
+    EXPECT_TRUE(vendor->getOption(DOCSIS3_V4_ORO));
+    EXPECT_TRUE(vendor->getOption(5));
+}
+
+// Tests whether we can parse and then pack a v4 option.
+TEST_F(OptionVendorTest, packUnpack4) {
+    OptionBuffer binary = createV4VendorOptions();
+
+    OptionVendorPtr vendor;
+
+    // Create vendor option (ignore the first 2 bytes, these are option code
+    // and option length
+    ASSERT_NO_THROW(vendor.reset(new OptionVendor(Option::V4, binary.begin() + 2,
+                                                  binary.end())));
+
+    OutputBuffer output(0);
+
+    EXPECT_NO_THROW(vendor->pack(output));
+
+    ASSERT_EQ(binary.size(), output.getLength());
+
+    // We're lucky, because the packet capture we have happens to have options
+    // with monotonically increasing values (1 and 5), so our pack() method
+    // will pack them in exactly the same order as in the original.
+    EXPECT_FALSE(memcmp(&binary[0], output.getData(), output.getLength()));
+}
+
+// Tests whether we can parse v6 vendor options properly
+TEST_F(OptionVendorTest, v6Parse) {
+    OptionBuffer binary = createV6VendorOption();
+
+    OptionVendorPtr vendor;
+    // Create vendor option (ignore the first 4 bytes. These are option code
+    // (2 bytes) and option length (2 bytes).
+    ASSERT_NO_THROW(vendor.reset(new OptionVendor(Option::V6, binary.begin() + 4,
+                                                  binary.end())));
+
+    OptionPtr opt;
+    opt = vendor->getOption(DOCSIS3_V6_ORO);
+    ASSERT_TRUE(opt);
+    OptionUint16ArrayPtr oro =
+        boost::dynamic_pointer_cast<OptionUint16Array>(opt);
+
+    // Check that all remaining expected options are there
+    EXPECT_TRUE(vendor->getOption(2));
+    EXPECT_TRUE(vendor->getOption(3));
+    EXPECT_TRUE(vendor->getOption(4));
+    EXPECT_TRUE(vendor->getOption(5));
+    EXPECT_TRUE(vendor->getOption(6));
+    EXPECT_TRUE(vendor->getOption(7));
+    EXPECT_TRUE(vendor->getOption(8));
+    EXPECT_TRUE(vendor->getOption(9));
+    EXPECT_TRUE(vendor->getOption(10));
+    EXPECT_TRUE(vendor->getOption(35));
+    EXPECT_TRUE(vendor->getOption(36));
+
+    // Check that there are no other options there
+    for (uint16_t i = 11; i < 35; ++i) {
+        EXPECT_FALSE(vendor->getOption(i));
+    }
+
+    for (uint16_t i = 37; i < 65535; ++i) {
+        EXPECT_FALSE(vendor->getOption(i));
+    }
+}
+
+// Tests whether we can parse and then pack a v6 option.
+TEST_F(OptionVendorTest, packUnpack6) {
+    OptionBuffer binary = createV6VendorOption();
+
+    OptionVendorPtr vendor;
+
+    // Create vendor option (ignore the first 4 bytes. These are option code
+    // (2 bytes) and option length (2 bytes).
+    ASSERT_NO_THROW(vendor.reset(new OptionVendor(Option::V6, binary.begin() + 4,
+                                                  binary.end())));
+
+    OutputBuffer output(0);
+
+    EXPECT_NO_THROW(vendor->pack(output));
+
+    ASSERT_EQ(binary.size(), output.getLength());
+    EXPECT_FALSE(memcmp(&binary[0], output.getData(), output.getLength()));
+}
+
+}
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index a108930..72ffff7 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -16,6 +16,7 @@
 
 #include <asiolink/io_address.h>
 #include <dhcp/dhcp4.h>
+#include <dhcp/libdhcp++.h>
 #include <dhcp/option_string.h>
 #include <dhcp/pkt4.h>
 #include <exceptions/exceptions.h>
@@ -42,40 +43,60 @@ using boost::scoped_ptr;
 
 namespace {
 
-TEST(Pkt4Test, constructor) {
-
-    ASSERT_EQ(236U, static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) );
-    scoped_ptr<Pkt4> pkt;
-
-    // Just some dummy payload.
-    uint8_t testData[250];
-    for (int i = 0; i < 250; i++) {
-        testData[i] = i;
+/// @brief A class which contains a custom callback function to unpack options.
+///
+/// This is a class used by the tests which verify that the custom callback
+/// functions can be installed to unpack options from a message. When the
+/// callback function is called, the executed_ member is set to true to allow
+/// verification that the callback was really called. Internally, this class
+/// uses libdhcp++ to unpack options so the options parsing algorithm remains
+/// unchanged after installation of the callback.
+class CustomUnpackCallback {
+public:
+
+    /// @brief Constructor
+    ///
+    /// Marks that callback hasn't been called.
+    CustomUnpackCallback()
+        : executed_(false) {
     }
 
-    // Positive case1. Normal received packet.
-    EXPECT_NO_THROW(pkt.reset(new Pkt4(testData, Pkt4::DHCPV4_PKT_HDR_LEN)));
-
-    EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), pkt->len());
-
-    EXPECT_NO_THROW(pkt.reset());
-
-    // Positive case2. Normal outgoing packet.
-    EXPECT_NO_THROW(pkt.reset(new Pkt4(DHCPDISCOVER, 0xffffffff)));
+    /// @brief A callback
+    ///
+    /// Contains custom implementation of the callback.
+    ///
+    /// @param buf a A buffer holding options in on-wire format.
+    /// @param option_space A name of the option space being encapsulated by
+    /// the option being parsed.
+    /// @param [out] options A reference to the collection where parsed options
+    /// will be stored.
+    /// @return An offset to the first byte after last parsed option.
+    size_t execute(const OptionBuffer& buf,
+                   const std::string& option_space,
+                   isc::dhcp::OptionCollection& options) {
+        // Set the executed_ member to true to allow verification that the
+        // callback has been actually called.
+        executed_ = true;
+        // Use default implementation of the unpack algorithm to parse options.
+        return (LibDHCP::unpackOptions4(buf, option_space, options));
+    }
 
-    // DHCPv4 packet must be at least 236 bytes long, with Message Type
-    // Option taking extra 3 bytes it is 239
-    EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) + 3, pkt->len());
-    EXPECT_EQ(DHCPDISCOVER, pkt->getType());
-    EXPECT_EQ(0xffffffff, pkt->getTransid());
-    EXPECT_NO_THROW(pkt.reset());
+    /// A flag which indicates if callback function has been called.
+    bool executed_;
+};
 
-    // Negative case. Should drop truncated messages.
-    EXPECT_THROW(
-        pkt.reset(new Pkt4(testData, Pkt4::DHCPV4_PKT_HDR_LEN - 1)),
-        OutOfRange
-    );
-}
+/// V4 Options being used for pack/unpack testing.
+/// For test simplicity, all selected options have
+/// variable length data so as there are no restrictions
+/// on a length of their data.
+static uint8_t v4_opts[] = {
+    12,  3, 0,   1,  2, // Hostname
+    14,  3, 10, 11, 12, // Merit Dump File
+    53, 1, 2, // Message Type (required to not throw exception during unpack)
+    60,  3, 20, 21, 22, // Class Id
+    128, 3, 30, 31, 32, // Vendor specific
+    254, 3, 40, 41, 42, // Reserved
+};
 
 // Sample data
 const uint8_t dummyOp = BOOTREQUEST;
@@ -110,82 +131,179 @@ const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur "
 BOOST_STATIC_ASSERT(sizeof(dummyFile)  == Pkt4::MAX_FILE_LEN + 1);
 BOOST_STATIC_ASSERT(sizeof(dummySname) == Pkt4::MAX_SNAME_LEN + 1);
 
-/// @brief Generates test packet.
-///
-/// Allocates and generates test packet, with all fixed fields set to non-zero
-/// values. Content is not always reasonable.
-///
-/// See generateTestPacket2() function that returns exactly the same packet in
-/// on-wire format.
-///
-/// @return pointer to allocated Pkt4 object.
-boost::shared_ptr<Pkt4>
-generateTestPacket1() {
-
-    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDISCOVER, dummyTransid));
-
-    vector<uint8_t> vectorMacAddr(dummyMacAddr, dummyMacAddr
-                                  +sizeof(dummyMacAddr));
-
-    // hwType = 6(ETHERNET), hlen = 6(MAC address len)
-    pkt->setHWAddr(dummyHtype, dummyHlen, vectorMacAddr);
-    pkt->setHops(dummyHops); // 13 relays. Wow!
-    // Transaction-id is already set.
-    pkt->setSecs(dummySecs);
-    pkt->setFlags(dummyFlags); // all flags set
-    pkt->setCiaddr(dummyCiaddr);
-    pkt->setYiaddr(dummyYiaddr);
-    pkt->setSiaddr(dummySiaddr);
-    pkt->setGiaddr(dummyGiaddr);
-    // Chaddr already set with setHWAddr().
-    pkt->setSname(dummySname, 64);
-    pkt->setFile(dummyFile, 128);
-
-    return (pkt);
-}
 
-/// @brief Generates test packet.
-///
-/// Allocates and generates on-wire buffer that represents test packet, with all
-/// fixed fields set to non-zero values.  Content is not always reasonable.
-///
-/// See generateTestPacket1() function that returns exactly the same packet as
-/// Pkt4 object.
-///
-/// @return pointer to allocated Pkt4 object
-// Returns a vector containing a DHCPv4 packet header.
-vector<uint8_t>
-generateTestPacket2() {
-
-    // That is only part of the header. It contains all "short" fields,
-    // larger fields are constructed separately.
-    uint8_t hdr[] = {
-        1, 6, 6, 13,            // op, htype, hlen, hops,
-        0x12, 0x34, 0x56, 0x78, // transaction-id
-        0, 42, 0x80, 0x00,      // 42 secs, BROADCAST flags
-        192, 0, 2, 1,           // ciaddr
-        1, 2, 3, 4,             // yiaddr
-        192, 0, 2, 255,         // siaddr
-        255, 255, 255, 255,     // giaddr
-    };
+class Pkt4Test : public ::testing::Test {
+public:
+    Pkt4Test() {
+    }
+
+    /// @brief Generates test packet.
+    ///
+    /// Allocates and generates test packet, with all fixed fields set to non-zero
+    /// values. Content is not always reasonable.
+    ///
+    /// See generateTestPacket2() function that returns exactly the same packet in
+    /// on-wire format.
+    ///
+    /// @return pointer to allocated Pkt4 object.
+    Pkt4Ptr generateTestPacket1() {
+
+        boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDISCOVER, dummyTransid));
+
+        vector<uint8_t> vectorMacAddr(dummyMacAddr, dummyMacAddr
+                                      + sizeof(dummyMacAddr));
+
+        // hwType = 6(ETHERNET), hlen = 6(MAC address len)
+        pkt->setHWAddr(dummyHtype, dummyHlen, vectorMacAddr);
+        pkt->setHops(dummyHops); // 13 relays. Wow!
+        // Transaction-id is already set.
+        pkt->setSecs(dummySecs);
+        pkt->setFlags(dummyFlags); // all flags set
+        pkt->setCiaddr(dummyCiaddr);
+        pkt->setYiaddr(dummyYiaddr);
+        pkt->setSiaddr(dummySiaddr);
+        pkt->setGiaddr(dummyGiaddr);
+        // Chaddr already set with setHWAddr().
+        pkt->setSname(dummySname, 64);
+        pkt->setFile(dummyFile, 128);
+
+        return (pkt);
+    }
+
+    /// @brief Generates test packet.
+    ///
+    /// Allocates and generates on-wire buffer that represents test packet, with all
+    /// fixed fields set to non-zero values.  Content is not always reasonable.
+    ///
+    /// See generateTestPacket1() function that returns exactly the same packet as
+    /// Pkt4 object.
+    ///
+    /// @return pointer to allocated Pkt4 object
+    // Returns a vector containing a DHCPv4 packet header.
+    vector<uint8_t> generateTestPacket2() {
+
+        // That is only part of the header. It contains all "short" fields,
+        // larger fields are constructed separately.
+        uint8_t hdr[] = {
+            1, 6, 6, 13,            // op, htype, hlen, hops,
+            0x12, 0x34, 0x56, 0x78, // transaction-id
+            0, 42, 0x80, 0x00,      // 42 secs, BROADCAST flags
+            192, 0, 2, 1,           // ciaddr
+            1, 2, 3, 4,             // yiaddr
+            192, 0, 2, 255,         // siaddr
+            255, 255, 255, 255,     // giaddr
+        };
+
+        // Initialize the vector with the header fields defined above.
+        vector<uint8_t> buf(hdr, hdr + sizeof(hdr));
+
+        // Append the large header fields.
+        copy(dummyChaddr, dummyChaddr + Pkt4::MAX_CHADDR_LEN, back_inserter(buf));
+        copy(dummySname, dummySname + Pkt4::MAX_SNAME_LEN, back_inserter(buf));
+        copy(dummyFile, dummyFile + Pkt4::MAX_FILE_LEN, back_inserter(buf));
+
+        // Should now have all the header, so check.  The "static_cast" is used
+        // to get round an odd bug whereby the linker appears not to find the
+        // definition of DHCPV4_PKT_HDR_LEN if it appears within an EXPECT_EQ().
+        EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), buf.size());
+
+        return (buf);
+    }
+
+    /// @brief Verify that the options are correct after parsing.
+    ///
+    /// @param pkt A packet holding parsed options.
+    void verifyParsedOptions(const Pkt4Ptr& pkt) {
+        EXPECT_TRUE(pkt->getOption(12));
+        EXPECT_TRUE(pkt->getOption(60));
+        EXPECT_TRUE(pkt->getOption(14));
+        EXPECT_TRUE(pkt->getOption(128));
+        EXPECT_TRUE(pkt->getOption(254));
+
+        boost::shared_ptr<Option> x = pkt->getOption(12);
+        ASSERT_TRUE(x); // option 1 should exist
+        // Option 12 is represented by the OptionString class so let's do
+        // the appropriate conversion.
+        OptionStringPtr option12 = boost::static_pointer_cast<OptionString>(x);
+        ASSERT_TRUE(option12);
+        EXPECT_EQ(12, option12->getType());  // this should be option 12
+        ASSERT_EQ(3, option12->getValue().length()); // it should be of length 3
+        EXPECT_EQ(5, option12->len()); // total option length 5
+        EXPECT_EQ(0, memcmp(&option12->getValue()[0], v4_opts + 2, 3)); // data len=3
+
+        x = pkt->getOption(14);
+        ASSERT_TRUE(x); // option 14 should exist
+        // Option 14 is represented by the OptionString class so let's do
+        // the appropriate conversion.
+        OptionStringPtr option14 = boost::static_pointer_cast<OptionString>(x);
+        ASSERT_TRUE(option14);
+        EXPECT_EQ(14, option14->getType());  // this should be option 14
+        ASSERT_EQ(3, option14->getValue().length()); // it should be of length 3
+        EXPECT_EQ(5, option14->len()); // total option length 5
+        EXPECT_EQ(0, memcmp(&option14->getValue()[0], v4_opts + 7, 3)); // data len=3
+
+        x = pkt->getOption(60);
+        ASSERT_TRUE(x); // option 60 should exist
+        EXPECT_EQ(60, x->getType());  // this should be option 60
+        ASSERT_EQ(3, x->getData().size()); // it should be of length 3
+        EXPECT_EQ(5, x->len()); // total option length 5
+        EXPECT_EQ(0, memcmp(&x->getData()[0], v4_opts + 15, 3)); // data len=3
+
+        x = pkt->getOption(128);
+        ASSERT_TRUE(x); // option 3 should exist
+        EXPECT_EQ(128, x->getType());  // this should be option 254
+        ASSERT_EQ(3, x->getData().size()); // it should be of length 3
+        EXPECT_EQ(5, x->len()); // total option length 5
+        EXPECT_EQ(0, memcmp(&x->getData()[0], v4_opts + 20, 3)); // data len=3
+
+        x = pkt->getOption(254);
+        ASSERT_TRUE(x); // option 3 should exist
+        EXPECT_EQ(254, x->getType());  // this should be option 254
+        ASSERT_EQ(3, x->getData().size()); // it should be of length 3
+        EXPECT_EQ(5, x->len()); // total option length 5
+        EXPECT_EQ(0, memcmp(&x->getData()[0], v4_opts + 25, 3)); // data len=3
+    }
+
+};
+
+
+TEST_F(Pkt4Test, constructor) {
+
+    ASSERT_EQ(236U, static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) );
+    scoped_ptr<Pkt4> pkt;
+
+    // Just some dummy payload.
+    uint8_t testData[250];
+    for (int i = 0; i < 250; i++) {
+        testData[i] = i;
+    }
+
+    // Positive case1. Normal received packet.
+    EXPECT_NO_THROW(pkt.reset(new Pkt4(testData, Pkt4::DHCPV4_PKT_HDR_LEN)));
+
+    EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), pkt->len());
 
-    // Initialize the vector with the header fields defined above.
-    vector<uint8_t> buf(hdr, hdr + sizeof(hdr));
+    EXPECT_NO_THROW(pkt.reset());
 
-    // Append the large header fields.
-    copy(dummyChaddr, dummyChaddr + Pkt4::MAX_CHADDR_LEN, back_inserter(buf));
-    copy(dummySname, dummySname + Pkt4::MAX_SNAME_LEN, back_inserter(buf));
-    copy(dummyFile, dummyFile + Pkt4::MAX_FILE_LEN, back_inserter(buf));
+    // Positive case2. Normal outgoing packet.
+    EXPECT_NO_THROW(pkt.reset(new Pkt4(DHCPDISCOVER, 0xffffffff)));
 
-    // Should now have all the header, so check.  The "static_cast" is used
-    // to get round an odd bug whereby the linker appears not to find the
-    // definition of DHCPV4_PKT_HDR_LEN if it appears within an EXPECT_EQ().
-    EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), buf.size());
+    // DHCPv4 packet must be at least 236 bytes long, with Message Type
+    // Option taking extra 3 bytes it is 239
+    EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) + 3, pkt->len());
+    EXPECT_EQ(DHCPDISCOVER, pkt->getType());
+    EXPECT_EQ(0xffffffff, pkt->getTransid());
+    EXPECT_NO_THROW(pkt.reset());
 
-    return (buf);
+    // Negative case. Should drop truncated messages.
+    EXPECT_THROW(
+        pkt.reset(new Pkt4(testData, Pkt4::DHCPV4_PKT_HDR_LEN - 1)),
+        OutOfRange
+    );
 }
 
-TEST(Pkt4Test, fixedFields) {
+
+TEST_F(Pkt4Test, fixedFields) {
 
     boost::shared_ptr<Pkt4> pkt = generateTestPacket1();
 
@@ -215,7 +333,7 @@ TEST(Pkt4Test, fixedFields) {
     EXPECT_EQ(DHCPDISCOVER, pkt->getType());
 }
 
-TEST(Pkt4Test, fixedFieldsPack) {
+TEST_F(Pkt4Test, fixedFieldsPack) {
     boost::shared_ptr<Pkt4> pkt = generateTestPacket1();
     vector<uint8_t> expectedFormat = generateTestPacket2();
 
@@ -235,7 +353,7 @@ TEST(Pkt4Test, fixedFieldsPack) {
 }
 
 /// TODO Uncomment when ticket #1226 is implemented
-TEST(Pkt4Test, fixedFieldsUnpack) {
+TEST_F(Pkt4Test, fixedFieldsUnpack) {
     vector<uint8_t> expectedFormat = generateTestPacket2();
 
     expectedFormat.push_back(0x63); // magic cookie
@@ -282,7 +400,7 @@ TEST(Pkt4Test, fixedFieldsUnpack) {
 }
 
 // This test is for hardware addresses (htype, hlen and chaddr fields)
-TEST(Pkt4Test, hwAddr) {
+TEST_F(Pkt4Test, hwAddr) {
 
     vector<uint8_t> mac;
     uint8_t expectedChaddr[Pkt4::MAX_CHADDR_LEN];
@@ -329,7 +447,7 @@ TEST(Pkt4Test, hwAddr) {
     /// longer than 16 bytes should be stored in client-identifier option
 }
 
-TEST(Pkt4Test, msgTypes) {
+TEST_F(Pkt4Test, msgTypes) {
 
     struct msgType {
         uint8_t dhcp;
@@ -366,7 +484,7 @@ TEST(Pkt4Test, msgTypes) {
 }
 
 // This test verifies handling of sname field
-TEST(Pkt4Test, sname) {
+TEST_F(Pkt4Test, sname) {
 
     uint8_t sname[Pkt4::MAX_SNAME_LEN];
 
@@ -404,7 +522,7 @@ TEST(Pkt4Test, sname) {
     EXPECT_THROW(pkt4.setSname(NULL, 0), InvalidParameter);
 }
 
-TEST(Pkt4Test, file) {
+TEST_F(Pkt4Test, file) {
 
     uint8_t file[Pkt4::MAX_FILE_LEN];
 
@@ -442,20 +560,7 @@ TEST(Pkt4Test, file) {
     EXPECT_THROW(pkt4.setFile(NULL, 0), InvalidParameter);
 }
 
-/// V4 Options being used for pack/unpack testing.
-/// For test simplicity, all selected options have
-/// variable length data so as there are no restrictions
-/// on a length of their data.
-static uint8_t v4Opts[] = {
-    12,  3, 0,   1,  2, // Hostname
-    14,  3, 10, 11, 12, // Merit Dump File
-    53, 1, 2, // Message Type (required to not throw exception during unpack)
-    60,  3, 20, 21, 22, // Class Id
-    128, 3, 30, 31, 32, // Vendor specific
-    254, 3, 40, 41, 42, // Reserved
-};
-
-TEST(Pkt4Test, options) {
+TEST_F(Pkt4Test, options) {
     scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 0));
 
     vector<uint8_t> payload[5];
@@ -496,10 +601,10 @@ TEST(Pkt4Test, options) {
     );
 
     const OutputBuffer& buf = pkt->getBuffer();
-    // Check that all options are stored, they should take sizeof(v4Opts),
+    // Check that all options are stored, they should take sizeof(v4_opts),
     // DHCP magic cookie (4 bytes), and OPTION_END added (just one byte)
     ASSERT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) +
-              sizeof(DHCP_OPTIONS_COOKIE) + sizeof(v4Opts) + 1,
+              sizeof(DHCP_OPTIONS_COOKIE) + sizeof(v4_opts) + 1,
               buf.getLength());
 
     // That that this extra data actually contain our options
@@ -508,8 +613,8 @@ TEST(Pkt4Test, options) {
     // Rewind to end of fixed part.
     ptr += Pkt4::DHCPV4_PKT_HDR_LEN + sizeof(DHCP_OPTIONS_COOKIE);
 
-    EXPECT_EQ(0, memcmp(ptr, v4Opts, sizeof(v4Opts)));
-    EXPECT_EQ(DHO_END, static_cast<uint8_t>(*(ptr + sizeof(v4Opts))));
+    EXPECT_EQ(0, memcmp(ptr, v4_opts, sizeof(v4_opts)));
+    EXPECT_EQ(DHO_END, static_cast<uint8_t>(*(ptr + sizeof(v4_opts))));
 
     // delOption() checks
     EXPECT_TRUE(pkt->getOption(12));  // Sanity check: option 12 is still there
@@ -520,7 +625,8 @@ TEST(Pkt4Test, options) {
     EXPECT_NO_THROW(pkt.reset());
 }
 
-TEST(Pkt4Test, unpackOptions) {
+// This test verifies that the options are unpacked from the packet correctly.
+TEST_F(Pkt4Test, unpackOptions) {
 
     vector<uint8_t> expectedFormat = generateTestPacket2();
 
@@ -529,8 +635,8 @@ TEST(Pkt4Test, unpackOptions) {
     expectedFormat.push_back(0x53);
     expectedFormat.push_back(0x63);
 
-    for (int i = 0; i < sizeof(v4Opts); i++) {
-        expectedFormat.push_back(v4Opts[i]);
+    for (int i = 0; i < sizeof(v4_opts); i++) {
+        expectedFormat.push_back(v4_opts[i]);
     }
 
     // now expectedFormat contains fixed format and 5 options
@@ -542,59 +648,53 @@ TEST(Pkt4Test, unpackOptions) {
         pkt->unpack()
     );
 
-    EXPECT_TRUE(pkt->getOption(12));
-    EXPECT_TRUE(pkt->getOption(60));
-    EXPECT_TRUE(pkt->getOption(14));
-    EXPECT_TRUE(pkt->getOption(128));
-    EXPECT_TRUE(pkt->getOption(254));
+    verifyParsedOptions(pkt);
+}
+
+// This test verifies that it is possible to specify custom implementation of
+// the option parsing algorithm by installing a callback function.
+TEST_F(Pkt4Test, unpackOptionsWithCallback) {
+    vector<uint8_t> expectedFormat = generateTestPacket2();
+
+    expectedFormat.push_back(0x63);
+    expectedFormat.push_back(0x82);
+    expectedFormat.push_back(0x53);
+    expectedFormat.push_back(0x63);
+
+    for (int i = 0; i < sizeof(v4_opts); i++) {
+        expectedFormat.push_back(v4_opts[i]);
+    }
+
+    // now expectedFormat contains fixed format and 5 options
+
+    boost::shared_ptr<Pkt4> pkt(new Pkt4(&expectedFormat[0],
+                                expectedFormat.size()));
+
+    CustomUnpackCallback cb;
+    pkt->setCallback(boost::bind(&CustomUnpackCallback::execute, &cb,
+                                 _1, _2, _3));
+
+    ASSERT_FALSE(cb.executed_);
+
+    EXPECT_NO_THROW(pkt->unpack());
+
+    EXPECT_TRUE(cb.executed_);
+    verifyParsedOptions(pkt);
+
+    // Reset the indicator to perform another check: uninstall the callback.
+    cb.executed_ = false;
+    // By setting the callback to NULL we effectively uninstall the callback.
+    pkt->setCallback(NULL);
+    // Do another unpack.
+    EXPECT_NO_THROW(pkt->unpack());
+    // Callback should not be executed.
+    EXPECT_FALSE(cb.executed_);
 
-    boost::shared_ptr<Option> x = pkt->getOption(12);
-    ASSERT_TRUE(x); // option 1 should exist
-    // Option 12 is represented by the OptionString class so let's do
-    // the appropriate conversion.
-    OptionStringPtr option12 = boost::static_pointer_cast<OptionString>(x);
-    ASSERT_TRUE(option12);
-    EXPECT_EQ(12, option12->getType());  // this should be option 12
-    ASSERT_EQ(3, option12->getValue().length()); // it should be of length 3
-    EXPECT_EQ(5, option12->len()); // total option length 5
-    EXPECT_EQ(0, memcmp(&option12->getValue()[0], v4Opts + 2, 3)); // data len=3
-
-    x = pkt->getOption(14);
-    ASSERT_TRUE(x); // option 14 should exist
-    // Option 14 is represented by the OptionString class so let's do
-    // the appropriate conversion.
-    OptionStringPtr option14 = boost::static_pointer_cast<OptionString>(x);
-    ASSERT_TRUE(option14);
-    EXPECT_EQ(14, option14->getType());  // this should be option 14
-    ASSERT_EQ(3, option14->getValue().length()); // it should be of length 3
-    EXPECT_EQ(5, option14->len()); // total option length 5
-    EXPECT_EQ(0, memcmp(&option14->getValue()[0], v4Opts + 7, 3)); // data len=3
-
-    x = pkt->getOption(60);
-    ASSERT_TRUE(x); // option 60 should exist
-    EXPECT_EQ(60, x->getType());  // this should be option 60
-    ASSERT_EQ(3, x->getData().size()); // it should be of length 3
-    EXPECT_EQ(5, x->len()); // total option length 5
-    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts + 15, 3)); // data len=3
-
-    x = pkt->getOption(128);
-    ASSERT_TRUE(x); // option 3 should exist
-    EXPECT_EQ(128, x->getType());  // this should be option 254
-    ASSERT_EQ(3, x->getData().size()); // it should be of length 3
-    EXPECT_EQ(5, x->len()); // total option length 5
-    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts + 20, 3)); // data len=3
-
-    x = pkt->getOption(254);
-    ASSERT_TRUE(x); // option 3 should exist
-    EXPECT_EQ(254, x->getType());  // this should be option 254
-    ASSERT_EQ(3, x->getData().size()); // it should be of length 3
-    EXPECT_EQ(5, x->len()); // total option length 5
-    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts + 25, 3)); // data len=3
 }
 
 // This test verifies methods that are used for manipulating meta fields
 // i.e. fields that are not part of DHCPv4 (e.g. interface name).
-TEST(Pkt4Test, metaFields) {
+TEST_F(Pkt4Test, metaFields) {
 
     scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
     pkt->setIface("loooopback");
@@ -608,7 +708,7 @@ TEST(Pkt4Test, metaFields) {
     EXPECT_EQ("4.3.2.1", pkt->getLocalAddr().toText());
 }
 
-TEST(Pkt4Test, Timestamp) {
+TEST_F(Pkt4Test, Timestamp) {
     scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
 
     // Just after construction timestamp is invalid
@@ -634,7 +734,7 @@ TEST(Pkt4Test, Timestamp) {
     EXPECT_TRUE(ts_period.length().total_microseconds() >= 0);
 }
 
-TEST(Pkt4Test, hwaddr) {
+TEST_F(Pkt4Test, hwaddr) {
     scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
     const uint8_t hw[] = { 2, 4, 6, 8, 10, 12 }; // MAC
     const uint8_t hw_type = 123; // hardware type
@@ -655,7 +755,7 @@ TEST(Pkt4Test, hwaddr) {
 
 // This test verifies that the packet remte and local HW address can
 // be set and returned.
-TEST(Pkt4Test, hwaddrSrcRemote) {
+TEST_F(Pkt4Test, hwaddrSrcRemote) {
     scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
     const uint8_t src_hw[] = { 1, 2, 3, 4, 5, 6 };
     const uint8_t dst_hw[] = { 7, 8, 9, 10, 11, 12 };
diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc
index 138148a..e18b545 100644
--- a/src/lib/dhcp/tests/pkt6_unittest.cc
+++ b/src/lib/dhcp/tests/pkt6_unittest.cc
@@ -24,6 +24,7 @@
 #include <dhcp/pkt6.h>
 #include <util/range_utilities.h>
 
+#include <boost/bind.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <util/encode/hex.h>
@@ -41,7 +42,56 @@ using namespace isc::dhcp;
 using boost::scoped_ptr;
 
 namespace {
-// empty class for now, but may be extended once Addr6 becomes bigger
+
+/// @brief A class which contains a custom callback function to unpack options.
+///
+/// This is a class used by the tests which verify that the custom callback
+/// functions can be installed to unpack options from a message. When the
+/// callback function is called, the executed_ member is set to true to allow
+/// verification that the callback was really called. Internally, this class
+/// uses libdhcp++ to unpack options so the options parsing algorithm remains
+/// unchanged after installation of the callback.
+class CustomUnpackCallback {
+public:
+
+    /// @brief Constructor
+    ///
+    /// Marks that callback hasn't been called.
+    CustomUnpackCallback()
+        : executed_(false) {
+    }
+
+    /// @brief A callback
+    ///
+    /// Contains custom implementation of the callback.
+    ///
+    /// @param buf a A buffer holding options in on-wire format.
+    /// @param option_space A name of the option space encapsulated by the
+    /// option being parsed.
+    /// @param [out] options A reference to the collection where parsed options
+    /// will be stored.
+    /// @param relay_msg_offset Reference to a size_t structure. If specified,
+    /// offset to beginning of relay_msg option will be stored in it.
+    /// @param relay_msg_len reference to a size_t structure. If specified,
+    /// length of the relay_msg option will be stored in it.
+    /// @return An offset to the first byte after last parsed option.
+    size_t execute(const OptionBuffer& buf,
+                   const std::string& option_space,
+                   isc::dhcp::OptionCollection& options,
+                   size_t* relay_msg_offset,
+                   size_t* relay_msg_len) {
+        // Set the executed_ member to true to allow verification that the
+        // callback has been actually called.
+        executed_ = true;
+        // Use default implementation of the unpack algorithm to parse options.
+        return (LibDHCP::unpackOptions6(buf, option_space, options, relay_msg_offset,
+                                        relay_msg_len));
+    }
+
+    /// A flag which indicates if callback function has been called.
+    bool executed_;
+};
+
 class Pkt6Test : public ::testing::Test {
 public:
     Pkt6Test() {
@@ -58,6 +108,51 @@ public:
         util::fillRandom(data.begin(), data.end());
         return OptionPtr(new Option(Option::V6, code, data));
     }
+
+    /// @brief Create a wire representation of the test packet and clone it.
+    ///
+    /// The purpose of this function is to create a packet to be used to
+    /// check that packet parsing works correctly. The unpack() function
+    /// requires that the data_ field of the object holds the data to be
+    /// parsed. This function creates an on-wire representation of the
+    /// packet by calling pack(). But, the pack() function stores the
+    /// on-wire representation into the output buffer (not the data_ field).
+    /// For this reason, it is not enough to return the packet on which
+    /// pack() is called. This function returns a clone of this packet
+    /// which is created using a constructor taking a buffer and buffer
+    /// length as an input. This constructor is normally used to parse
+    /// received packets. It stores the packet in a data_ field and
+    /// therefore unpack() can be called to parse it.
+    Pkt6Ptr packAndClone() {
+        Pkt6Ptr parent(new Pkt6(DHCPV6_SOLICIT, 0x020304));
+
+        OptionPtr opt1(new Option(Option::V6, 1));
+        OptionPtr opt2(new Option(Option::V6, 2));
+        OptionPtr opt3(new Option(Option::V6, 100));
+        // Let's not use zero-length option type 3 as it is IA_NA
+
+        parent->addOption(opt1);
+        parent->addOption(opt2);
+        parent->addOption(opt3);
+
+        EXPECT_EQ(DHCPV6_SOLICIT, parent->getType());
+
+        // Calculated length should be 16
+        EXPECT_EQ(Pkt6::DHCPV6_PKT_HDR_LEN + 3 * Option::OPTION6_HDR_LEN,
+                  parent->len());
+
+        EXPECT_NO_THROW(parent->pack());
+
+        EXPECT_EQ(Pkt6::DHCPV6_PKT_HDR_LEN + 3 * Option::OPTION6_HDR_LEN,
+                  parent->len());
+
+        // Create second packet,based on assembled data from the first one
+        Pkt6Ptr clone(new Pkt6(static_cast<const uint8_t*>
+                               (parent->getBuffer().getData()),
+                               parent->getBuffer().getLength()));
+        return (clone);
+
+    }
 };
 
 TEST_F(Pkt6Test, constructor) {
@@ -204,44 +299,62 @@ TEST_F(Pkt6Test, unpack_solicit1) {
 }
 
 TEST_F(Pkt6Test, packUnpack) {
-    scoped_ptr<Pkt6> parent(new Pkt6(DHCPV6_SOLICIT, 0x020304));
+    // Create an on-wire representation of the test packet and clone it.
+    Pkt6Ptr clone = packAndClone();
 
-    OptionPtr opt1(new Option(Option::V6, 1));
-    OptionPtr opt2(new Option(Option::V6, 2));
-    OptionPtr opt3(new Option(Option::V6, 100));
-    // Let's not use zero-length option type 3 as it is IA_NA
-
-    parent->addOption(opt1);
-    parent->addOption(opt2);
-    parent->addOption(opt3);
-
-    EXPECT_EQ(DHCPV6_SOLICIT, parent->getType());
-
-    // Calculated length should be 16
-    EXPECT_EQ(Pkt6::DHCPV6_PKT_HDR_LEN + 3 * Option::OPTION6_HDR_LEN,
-              parent->len());
+    // Now recreate options list
+    EXPECT_TRUE(clone->unpack());
 
-    EXPECT_NO_THROW(parent->pack());
+    // transid, message-type should be the same as before
+    EXPECT_EQ(0x020304, clone->getTransid());
+    EXPECT_EQ(DHCPV6_SOLICIT, clone->getType());
 
-    EXPECT_EQ(Pkt6::DHCPV6_PKT_HDR_LEN + 3 * Option::OPTION6_HDR_LEN,
-              parent->len());
+    EXPECT_TRUE(clone->getOption(1));
+    EXPECT_TRUE(clone->getOption(2));
+    EXPECT_TRUE(clone->getOption(100));
+    EXPECT_FALSE(clone->getOption(4));
+}
 
-    // Create second packet,based on assembled data from the first one
-    scoped_ptr<Pkt6> clone(new Pkt6(
-        static_cast<const uint8_t*>(parent->getBuffer().getData()),
-                                    parent->getBuffer().getLength()));
+// This test verifies that it is possible to specify custom implementation of
+// the option parsing algorithm by installing a callback function.
+TEST_F(Pkt6Test, packUnpackWithCallback) {
+    // Create an on-wire representation of the test packet and clone it.
+    Pkt6Ptr clone = packAndClone();
+
+    // Install the custom callback function. We expect that this function
+    // will be called to parse options in the packet instead of
+    // LibDHCP::unpackOptions6.
+    CustomUnpackCallback cb;
+    clone->setCallback(boost::bind(&CustomUnpackCallback::execute, &cb,
+                                   _1, _2, _3, _4, _5));
+    // Make sure that the flag which indicates if the callback function has
+    // been called is not set. Otherwise, our test doesn't make sense.
+    ASSERT_FALSE(cb.executed_);
 
     // Now recreate options list
-    EXPECT_TRUE( clone->unpack() );
+    EXPECT_TRUE(clone->unpack());
+
+    // An object which holds a callback should now have a flag set which
+    // indicates that callback has been called.
+    EXPECT_TRUE(cb.executed_);
 
     // transid, message-type should be the same as before
-    EXPECT_EQ(parent->getTransid(), parent->getTransid());
+    EXPECT_EQ(0x020304, clone->getTransid());
     EXPECT_EQ(DHCPV6_SOLICIT, clone->getType());
 
     EXPECT_TRUE(clone->getOption(1));
     EXPECT_TRUE(clone->getOption(2));
     EXPECT_TRUE(clone->getOption(100));
     EXPECT_FALSE(clone->getOption(4));
+
+    // Reset the indicator to perform another check: uninstall the callback.
+    cb.executed_ = false;
+    // By setting the callback to NULL we effectively uninstall the callback.
+    clone->setCallback(NULL);
+    // Do another unpack.
+    EXPECT_TRUE(clone->unpack());
+    // Callback should not be executed.
+    EXPECT_FALSE(cb.executed_);
 }
 
 // This test verifies that options can be added (addOption()), retrieved
@@ -266,12 +379,12 @@ TEST_F(Pkt6Test, addGetDelOptions) {
     // Now there are 2 options of type 2
     parent->addOption(opt3);
 
-    Option::OptionCollection options = parent->getOptions(2);
+    OptionCollection options = parent->getOptions(2);
     EXPECT_EQ(2, options.size()); // there should be 2 instances
 
     // Both options must be of type 2 and there must not be
     // any other type returned
-    for (Option::OptionCollection::const_iterator x= options.begin();
+    for (OptionCollection::const_iterator x= options.begin();
          x != options.end(); ++x) {
         EXPECT_EQ(2, x->second->getType());
     }
diff --git a/src/lib/dhcp/tests/protocol_util_unittest.cc b/src/lib/dhcp/tests/protocol_util_unittest.cc
index 644dbf7..199ca27 100644
--- a/src/lib/dhcp/tests/protocol_util_unittest.cc
+++ b/src/lib/dhcp/tests/protocol_util_unittest.cc
@@ -340,7 +340,7 @@ TEST(ProtocolUtilTest, writeIpUdpHeader) {
 
     // Protocol type is UDP.
     uint8_t proto = in_buf.readUint8();
-    EXPECT_EQ(IPPROTO_UDP, proto);
+    EXPECT_EQ(static_cast<short>(IPPROTO_UDP), proto);
 
     // Check that the checksum is correct. The reference checksum value
     // has been calculated manually.
diff --git a/src/lib/dhcp_ddns/Makefile.am b/src/lib/dhcp_ddns/Makefile.am
index c0d8a7d..552f6d9 100644
--- a/src/lib/dhcp_ddns/Makefile.am
+++ b/src/lib/dhcp_ddns/Makefile.am
@@ -12,8 +12,11 @@ AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 
 
 # Define rule to build logging source files from message file
-dhcp_ddns_messages.h dhcp_ddns_messages.cc: dhcp_ddns_messages.mes
+dhcp_ddns_messages.h dhcp_ddns_messages.cc: s-messages
+
+s-messages: dhcp_ddns_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/dhcp_ddns/dhcp_ddns_messages.mes
+	touch $@
 
 # Tell automake that the message files are built as part of the build process
 # (so that they are built before the main library is built).
@@ -23,7 +26,7 @@ BUILT_SOURCES = dhcp_ddns_messages.h dhcp_ddns_messages.cc
 EXTRA_DIST = dhcp_ddns_messages.mes libdhcp_ddns.dox
 
 # Get rid of generated message files on a clean
-CLEANFILES = *.gcno *.gcda dhcp_ddns_messages.h dhcp_ddns_messages.cc
+CLEANFILES = *.gcno *.gcda dhcp_ddns_messages.h dhcp_ddns_messages.cc s-messages
 
 lib_LTLIBRARIES = libb10-dhcp_ddns.la
 libb10_dhcp_ddns_la_SOURCES  =
@@ -39,6 +42,7 @@ libb10_dhcp_ddns_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
 libb10_dhcp_ddns_la_LDFLAGS  = $(AM_LDFLAGS)
 libb10_dhcp_ddns_la_LDFLAGS += ${BOTAN_LDFLAGS}
 libb10_dhcp_ddns_la_LIBADD  =
+libb10_dhcp_ddns_la_LIBADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 libb10_dhcp_ddns_la_LIBADD += $(top_builddir)/src/lib/cryptolink/libb10-cryptolink.la
 libb10_dhcp_ddns_la_LIBADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
 libb10_dhcp_ddns_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
diff --git a/src/lib/dhcp_ddns/ncr_msg.cc b/src/lib/dhcp_ddns/ncr_msg.cc
index 4726850..7b076e1 100644
--- a/src/lib/dhcp_ddns/ncr_msg.cc
+++ b/src/lib/dhcp_ddns/ncr_msg.cc
@@ -177,6 +177,11 @@ D2Dhcid::createDigest(const uint8_t identifier_type,
     bytes_.insert(bytes_.end(), secure.begin(), secure.end());
 }
 
+std::ostream&
+operator<<(std::ostream& os, const D2Dhcid& dhcid) {
+    os << dhcid.toStr();
+    return (os);
+}
 
 
 
diff --git a/src/lib/dhcp_ddns/ncr_msg.h b/src/lib/dhcp_ddns/ncr_msg.h
index f27407f..50b0a74 100644
--- a/src/lib/dhcp_ddns/ncr_msg.h
+++ b/src/lib/dhcp_ddns/ncr_msg.h
@@ -195,6 +195,9 @@ private:
     std::vector<uint8_t> bytes_;
 };
 
+std::ostream&
+operator<<(std::ostream& os, const D2Dhcid& dhcid);
+
 class NameChangeRequest;
 /// @brief Defines a pointer to a NameChangeRequest.
 typedef boost::shared_ptr<NameChangeRequest> NameChangeRequestPtr;
@@ -212,6 +215,14 @@ typedef std::map<std::string, isc::data::ConstElementPtr> ElementMap;
 class NameChangeRequest {
 public:
     /// @brief Default Constructor.
+    ///
+    /// @todo Currently, fromWire makes use of the ability to create an empty
+    /// NameChangeRequest and then builds it bit by bit.  This means that it
+    /// is technically possible to create one and attempt to use in ways
+    /// other than intended and its invalid content may or may not be handled
+    /// gracefully by consuming code.  It might be wise to revisit this
+    /// structuring such that we do not use a default constructor and only
+    /// allow valid instantiations.
     NameChangeRequest();
 
     /// @brief Constructor.  Full constructor, which provides parameters for
diff --git a/src/lib/dhcp_ddns/tests/ncr_unittests.cc b/src/lib/dhcp_ddns/tests/ncr_unittests.cc
index 33ec288..1298b66 100644
--- a/src/lib/dhcp_ddns/tests/ncr_unittests.cc
+++ b/src/lib/dhcp_ddns/tests/ncr_unittests.cc
@@ -237,129 +237,6 @@ TEST(NameChangeRequestTest, constructionTests) {
 
 }
 
-/// @brief Verifies the fundamentals of converting from and to JSON.
-/// It verifies that:
-/// 1. A NameChangeRequest can be created from a valid JSON string.
-/// 2. A valid JSON string can be created from a NameChangeRequest
-TEST(NameChangeRequestTest, basicJsonTest) {
-    // Define valid JSON rendition of a request.
-    std::string msg_str = "{"
-                            "\"change_type\":1,"
-                            "\"forward_change\":true,"
-                            "\"reverse_change\":false,"
-                            "\"fqdn\":\"walah.walah.com\","
-                            "\"ip_address\":\"192.168.2.1\","
-                            "\"dhcid\":\"010203040A7F8E3D\","
-                            "\"lease_expires_on\":\"20130121132405\","
-                            "\"lease_length\":1300"
-                          "}";
-
-    // Verify that a NameChangeRequests can be instantiated from the
-    // a valid JSON rendition.
-    NameChangeRequestPtr ncr;
-    ASSERT_NO_THROW(ncr  = NameChangeRequest::fromJSON(msg_str));
-    ASSERT_TRUE(ncr);
-
-    // Verify that the JSON string created by the new request equals the
-    // original input string.
-    std::string json_str = ncr->toJSON();
-    EXPECT_EQ(msg_str, json_str);
-}
-
-/// @brief Tests a variety of invalid JSON message strings.
-/// This test iterates over a list of JSON messages, each containing a single
-/// content error. The list of messages is defined by the global array,
-/// invalid_messages. Currently that list contains the following invalid
-/// conditions:
-///  1. Invalid change type
-///  2. Invalid forward change
-///  3. Invalid reverse change
-///  4. Forward and reverse change both false
-///  5. Invalid forward change
-///  6. Blank FQDN
-///  7. Bad IP address
-///  8. Blank DHCID
-///  9. Odd number of digits in DHCID
-/// 10. Text in DHCID
-/// 11. Invalid lease expiration string
-/// 12. Non-integer for lease length.
-/// If more permutations arise they can easily be added to the list.
-TEST(NameChangeRequestTest, invalidMsgChecks) {
-    // Iterate over the list of JSON strings, attempting to create a
-    // NameChangeRequest. The attempt should throw a NcrMessageError.
-    int num_msgs = sizeof(invalid_msgs)/sizeof(char*);
-    for (int i = 0; i < num_msgs; i++) {
-        EXPECT_THROW(NameChangeRequest::fromJSON(invalid_msgs[i]),
-                     NcrMessageError) << "Invalid message not caught idx: "
-                     << i << std::endl << " text:[" << invalid_msgs[i] << "]"
-                     << std::endl;
-    }
-}
-
-/// @brief Tests a variety of valid JSON message strings.
-/// This test iterates over a list of JSON messages, each containing a single
-/// valid request rendition. The list of messages is defined by the global
-/// array, valid_messages. Currently that list contains the following valid
-/// messages:
-///  1. Valid, IPv4 Add
-///  2. Valid, IPv4 Remove
-///  3. Valid, IPv6 Add
-/// If more permutations arise they can easily be added to the list.
-TEST(NameChangeRequestTest, validMsgChecks) {
-    // Iterate over the list of JSON strings, attempting to create a
-    // NameChangeRequest. The attempt should succeed.
-    int num_msgs = sizeof(valid_msgs)/sizeof(char*);
-    for (int i = 0; i < num_msgs; i++) {
-        EXPECT_NO_THROW(NameChangeRequest::fromJSON(valid_msgs[i]))
-                        << "Valid message failed,  message idx: " << i
-                        << std::endl << " text:[" << valid_msgs[i] << "]"
-                        << std::endl;
-    }
-}
-
-/// @brief Tests converting to and from JSON via isc::util buffer classes.
-/// This test verifies that:
-/// 1. A NameChangeRequest can be rendered in JSON written to an OutputBuffer
-/// 2. A InputBuffer containing a valid JSON request rendition can be used
-/// to create a NameChangeRequest.
-TEST(NameChangeRequestTest, toFromBufferTest) {
-    // Define a string containing a valid JSON NameChangeRequest rendition.
-    std::string msg_str = "{"
-                            "\"change_type\":1,"
-                            "\"forward_change\":true,"
-                            "\"reverse_change\":false,"
-                            "\"fqdn\":\"walah.walah.com\","
-                            "\"ip_address\":\"192.168.2.1\","
-                            "\"dhcid\":\"010203040A7F8E3D\","
-                            "\"lease_expires_on\":\"20130121132405\","
-                            "\"lease_length\":1300"
-                          "}";
-
-    // Create a request from JSON directly.
-    NameChangeRequestPtr ncr;
-    ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(msg_str));
-
-    // Verify that we output the request as JSON text to a buffer
-    // without error.
-    isc::util::OutputBuffer output_buffer(1024);
-    ASSERT_NO_THROW(ncr->toFormat(FMT_JSON, output_buffer));
-
-    // Make an InputBuffer from the OutputBuffer.
-    isc::util::InputBuffer input_buffer(output_buffer.getData(),
-                                        output_buffer.getLength());
-
-    // Verify that we can create a new request from the InputBuffer.
-    NameChangeRequestPtr ncr2;
-    ASSERT_NO_THROW(ncr2 =
-                    NameChangeRequest::fromFormat(FMT_JSON, input_buffer));
-
-    // Convert the new request to JSON directly.
-    std::string final_str = ncr2->toJSON();
-
-    // Verify that the final string matches the original.
-    ASSERT_EQ(final_str, msg_str);
-}
-
 /// @brief Tests the basic workings of D2Dhcid to and from string conversions.
 /// It verifies that:
 /// 1. DHCID input strings must contain an even number of characters
@@ -566,5 +443,137 @@ TEST_F(DhcidTest, fromHWAddr) {
                  isc::dhcp_ddns::DhcidRdataComputeError);
 }
 
+// test operator<< on D2Dhcid
+TEST(NameChangeRequestTest, leftShiftOperation) {
+    const D2Dhcid dhcid("010203040A7F8E3D");
+
+    ostringstream oss;
+    oss << dhcid;
+    EXPECT_EQ(dhcid.toStr(), oss.str());
+}
+
+/// @brief Verifies the fundamentals of converting from and to JSON.
+/// It verifies that:
+/// 1. A NameChangeRequest can be created from a valid JSON string.
+/// 2. A valid JSON string can be created from a NameChangeRequest
+TEST(NameChangeRequestTest, basicJsonTest) {
+    // Define valid JSON rendition of a request.
+    std::string msg_str = "{"
+                            "\"change_type\":1,"
+                            "\"forward_change\":true,"
+                            "\"reverse_change\":false,"
+                            "\"fqdn\":\"walah.walah.com\","
+                            "\"ip_address\":\"192.168.2.1\","
+                            "\"dhcid\":\"010203040A7F8E3D\","
+                            "\"lease_expires_on\":\"20130121132405\","
+                            "\"lease_length\":1300"
+                          "}";
+
+    // Verify that a NameChangeRequests can be instantiated from the
+    // a valid JSON rendition.
+    NameChangeRequestPtr ncr;
+    ASSERT_NO_THROW(ncr  = NameChangeRequest::fromJSON(msg_str));
+    ASSERT_TRUE(ncr);
+
+    // Verify that the JSON string created by the new request equals the
+    // original input string.
+    std::string json_str = ncr->toJSON();
+    EXPECT_EQ(msg_str, json_str);
+}
+
+/// @brief Tests a variety of invalid JSON message strings.
+/// This test iterates over a list of JSON messages, each containing a single
+/// content error. The list of messages is defined by the global array,
+/// invalid_messages. Currently that list contains the following invalid
+/// conditions:
+///  1. Invalid change type
+///  2. Invalid forward change
+///  3. Invalid reverse change
+///  4. Forward and reverse change both false
+///  5. Invalid forward change
+///  6. Blank FQDN
+///  7. Bad IP address
+///  8. Blank DHCID
+///  9. Odd number of digits in DHCID
+/// 10. Text in DHCID
+/// 11. Invalid lease expiration string
+/// 12. Non-integer for lease length.
+/// If more permutations arise they can easily be added to the list.
+TEST(NameChangeRequestTest, invalidMsgChecks) {
+    // Iterate over the list of JSON strings, attempting to create a
+    // NameChangeRequest. The attempt should throw a NcrMessageError.
+    int num_msgs = sizeof(invalid_msgs)/sizeof(char*);
+    for (int i = 0; i < num_msgs; i++) {
+        EXPECT_THROW(NameChangeRequest::fromJSON(invalid_msgs[i]),
+                     NcrMessageError) << "Invalid message not caught idx: "
+                     << i << std::endl << " text:[" << invalid_msgs[i] << "]"
+                     << std::endl;
+    }
+}
+
+/// @brief Tests a variety of valid JSON message strings.
+/// This test iterates over a list of JSON messages, each containing a single
+/// valid request rendition. The list of messages is defined by the global
+/// array, valid_messages. Currently that list contains the following valid
+/// messages:
+///  1. Valid, IPv4 Add
+///  2. Valid, IPv4 Remove
+///  3. Valid, IPv6 Add
+/// If more permutations arise they can easily be added to the list.
+TEST(NameChangeRequestTest, validMsgChecks) {
+    // Iterate over the list of JSON strings, attempting to create a
+    // NameChangeRequest. The attempt should succeed.
+    int num_msgs = sizeof(valid_msgs)/sizeof(char*);
+    for (int i = 0; i < num_msgs; i++) {
+        EXPECT_NO_THROW(NameChangeRequest::fromJSON(valid_msgs[i]))
+                        << "Valid message failed,  message idx: " << i
+                        << std::endl << " text:[" << valid_msgs[i] << "]"
+                        << std::endl;
+    }
+}
+
+/// @brief Tests converting to and from JSON via isc::util buffer classes.
+/// This test verifies that:
+/// 1. A NameChangeRequest can be rendered in JSON written to an OutputBuffer
+/// 2. A InputBuffer containing a valid JSON request rendition can be used
+/// to create a NameChangeRequest.
+TEST(NameChangeRequestTest, toFromBufferTest) {
+    // Define a string containing a valid JSON NameChangeRequest rendition.
+    std::string msg_str = "{"
+                            "\"change_type\":1,"
+                            "\"forward_change\":true,"
+                            "\"reverse_change\":false,"
+                            "\"fqdn\":\"walah.walah.com\","
+                            "\"ip_address\":\"192.168.2.1\","
+                            "\"dhcid\":\"010203040A7F8E3D\","
+                            "\"lease_expires_on\":\"20130121132405\","
+                            "\"lease_length\":1300"
+                          "}";
+
+    // Create a request from JSON directly.
+    NameChangeRequestPtr ncr;
+    ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(msg_str));
+
+    // Verify that we output the request as JSON text to a buffer
+    // without error.
+    isc::util::OutputBuffer output_buffer(1024);
+    ASSERT_NO_THROW(ncr->toFormat(FMT_JSON, output_buffer));
+
+    // Make an InputBuffer from the OutputBuffer.
+    isc::util::InputBuffer input_buffer(output_buffer.getData(),
+                                        output_buffer.getLength());
+
+    // Verify that we can create a new request from the InputBuffer.
+    NameChangeRequestPtr ncr2;
+    ASSERT_NO_THROW(ncr2 =
+                    NameChangeRequest::fromFormat(FMT_JSON, input_buffer));
+
+    // Convert the new request to JSON directly.
+    std::string final_str = ncr2->toJSON();
+
+    // Verify that the final string matches the original.
+    ASSERT_EQ(final_str, msg_str);
+}
+
 } // end of anonymous namespace
 
diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am
index 5390a01..b3c4206 100644
--- a/src/lib/dhcpsrv/Makefile.am
+++ b/src/lib/dhcpsrv/Makefile.am
@@ -11,8 +11,11 @@ endif
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 # Define rule to build logging source files from message file
-dhcpsrv_messages.h dhcpsrv_messages.cc: dhcpsrv_messages.mes
+dhcpsrv_messages.h dhcpsrv_messages.cc: s-messages
+
+s-messages: dhcpsrv_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/dhcpsrv/dhcpsrv_messages.mes
+	touch $@
 
 # Tell Automake that the dhcpsrv_messages.{cc,h} source files are created in the
 # build process, so it must create these before doing anything else.  Although
@@ -29,7 +32,7 @@ BUILT_SOURCES = dhcpsrv_messages.h dhcpsrv_messages.cc
 AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 
 # Make sure the generated files are deleted in a "clean" operation
-CLEANFILES = *.gcno *.gcda dhcpsrv_messages.h dhcpsrv_messages.cc
+CLEANFILES = *.gcno *.gcda dhcpsrv_messages.h dhcpsrv_messages.cc s-messages
 
 lib_LTLIBRARIES = libb10-dhcpsrv.la
 libb10_dhcpsrv_la_SOURCES  =
@@ -42,6 +45,7 @@ libb10_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
 libb10_dhcpsrv_la_SOURCES += dhcp_config_parser.h
 libb10_dhcpsrv_la_SOURCES += dhcp_parsers.cc dhcp_parsers.h 
 libb10_dhcpsrv_la_SOURCES += key_from_key.h
+libb10_dhcpsrv_la_SOURCES += lease.cc lease.h
 libb10_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
 libb10_dhcpsrv_la_SOURCES += lease_mgr_factory.cc lease_mgr_factory.h
 libb10_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h
diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc
index 2bef8f8..dd1481e 100644
--- a/src/lib/dhcpsrv/alloc_engine.cc
+++ b/src/lib/dhcpsrv/alloc_engine.cc
@@ -53,8 +53,8 @@ AllocEngineHooks Hooks;
 namespace isc {
 namespace dhcp {
 
-AllocEngine::IterativeAllocator::IterativeAllocator()
-    :Allocator() {
+AllocEngine::IterativeAllocator::IterativeAllocator(Lease::Type lease_type)
+    :Allocator(lease_type) {
 }
 
 isc::asiolink::IOAddress
@@ -85,18 +85,77 @@ AllocEngine::IterativeAllocator::increaseAddress(const isc::asiolink::IOAddress&
     return (IOAddress::fromBytes(addr.getFamily(), packed));
 }
 
+isc::asiolink::IOAddress
+AllocEngine::IterativeAllocator::increasePrefix(const isc::asiolink::IOAddress& prefix,
+                                                const uint8_t prefix_len) {
+    if (!prefix.isV6()) {
+        isc_throw(BadValue, "Prefix operations are for IPv6 only (attempted to "
+                  "increase prefix " << prefix.toText() << ")");
+    }
+
+    // Get a buffer holding an address.
+    const std::vector<uint8_t>& vec = prefix.toBytes();
+
+    if (prefix_len < 1 || prefix_len > 128) {
+        isc_throw(BadValue, "Cannot increase prefix: invalid prefix length: "
+                  << prefix_len);
+    }
+
+    // Brief explanation what happens here:
+    // http://www.youtube.com/watch?v=NFQCYpIHLNQ
+
+    uint8_t n_bytes = (prefix_len - 1)/8;
+    uint8_t n_bits = 8 - (prefix_len - n_bytes*8);
+    uint8_t mask = 1 << n_bits;
+
+    // Longer explanation: n_bytes specifies number of full bytes that are
+    // in-prefix. They can also be used as an offset for the first byte that
+    // is not in prefix. n_bits specifies number of bits on the last byte that
+    // is (often partially) in prefix. For example for a /125 prefix, the values
+    // are 15 and 3, respectively. Mask is a bitmask that has the least
+    // significant bit from the prefix set.
+
+    uint8_t packed[V6ADDRESS_LEN];
+
+    // Copy the address. It must be V6, but we already checked that.
+    std::memcpy(packed, &vec[0], V6ADDRESS_LEN);
+
+    // Can we safely increase only the last byte in prefix without overflow?
+    if (packed[n_bytes] + uint16_t(mask) < 256u) {
+        packed[n_bytes] += mask;
+        return (IOAddress::fromBytes(AF_INET6, packed));
+    }
+
+    // Overflow (done on uint8_t, but the sum is greater than 255)
+    packed[n_bytes] += mask;
+
+    // Deal with the overflow. Start increasing the least significant byte
+    for (int i = n_bytes - 1; i >= 0; --i) {
+        ++packed[i];
+        // If we haven't overflowed (0xff->0x0) the next byte, then we are done
+        if (packed[i] != 0) {
+            break;
+        }
+    }
+
+    return (IOAddress::fromBytes(AF_INET6, packed));
+}
+
 
 isc::asiolink::IOAddress
 AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
                                              const DuidPtr&,
                                              const IOAddress&) {
 
+    // Is this prefix allocation?
+    bool prefix = pool_type_ == Lease::TYPE_PD;
+
     // Let's get the last allocated address. It is usually set correctly,
     // but there are times when it won't be (like after removing a pool or
     // perhaps restarting the server).
-    IOAddress last = subnet->getLastAllocated();
+    IOAddress last = subnet->getLastAllocated(pool_type_);
 
-    const PoolCollection& pools = subnet->getPools();
+    const PoolCollection& pools = subnet->getPools(pool_type_);
 
     if (pools.empty()) {
         isc_throw(AllocFailed, "No pools defined in selected subnet");
@@ -117,16 +176,28 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
     if (it == pools.end()) {
         // ok to access first element directly. We checked that pools is non-empty
         IOAddress next = pools[0]->getFirstAddress();
-        subnet->setLastAllocated(next);
+        subnet->setLastAllocated(pool_type_, next);
         return (next);
     }
 
     // Ok, we have a pool that the last address belonged to, let's use it.
 
-    IOAddress next = increaseAddress(last); // basically addr++
+    IOAddress next("::");
+    if (!prefix) {
+        next = increaseAddress(last); // basically addr++
+    } else {
+        Pool6Ptr pool6 = boost::dynamic_pointer_cast<Pool6>(*it);
+        if (!pool6) {
+            // Something is gravely wrong here
+            isc_throw(Unexpected, "Wrong type of pool: " << (*it)->toText()
+                      << " is not Pool6");
+        }
+        // Get the next prefix
+        next = increasePrefix(last, pool6->getLength());
+    }
     if ((*it)->inRange(next)) {
         // the next one is in the pool as well, so we haven't hit pool boundary yet
-        subnet->setLastAllocated(next);
+        subnet->setLastAllocated(pool_type_, next);
         return (next);
     }
 
@@ -136,18 +207,18 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
         // Really out of luck today. That was the last pool. Let's rewind
         // to the beginning.
         next = pools[0]->getFirstAddress();
-        subnet->setLastAllocated(next);
+        subnet->setLastAllocated(pool_type_, next);
         return (next);
     }
 
     // there is a next pool, let's try first address from it
     next = (*it)->getFirstAddress();
-    subnet->setLastAllocated(next);
+    subnet->setLastAllocated(pool_type_, next);
     return (next);
 }
 
-AllocEngine::HashedAllocator::HashedAllocator()
-    :Allocator() {
+AllocEngine::HashedAllocator::HashedAllocator(Lease::Type lease_type)
+    :Allocator(lease_type) {
     isc_throw(NotImplemented, "Hashed allocator is not implemented");
 }
 
@@ -159,8 +230,8 @@ AllocEngine::HashedAllocator::pickAddress(const SubnetPtr&,
     isc_throw(NotImplemented, "Hashed allocator is not implemented");
 }
 
-AllocEngine::RandomAllocator::RandomAllocator()
-    :Allocator() {
+AllocEngine::RandomAllocator::RandomAllocator(Lease::Type lease_type)
+    :Allocator(lease_type) {
     isc_throw(NotImplemented, "Random allocator is not implemented");
 }
 
@@ -173,44 +244,68 @@ AllocEngine::RandomAllocator::pickAddress(const SubnetPtr&,
 }
 
 
-AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts)
+AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts,
+                         bool ipv6)
     :attempts_(attempts) {
+
+    // Choose the basic (normal address) lease type
+    Lease::Type basic_type = ipv6 ? Lease::TYPE_NA : Lease::TYPE_V4;
+
+    // Initalize normal address allocators
     switch (engine_type) {
     case ALLOC_ITERATIVE:
-        allocator_ = boost::shared_ptr<Allocator>(new IterativeAllocator());
+        allocators_[basic_type] = AllocatorPtr(new IterativeAllocator(basic_type));
         break;
     case ALLOC_HASHED:
-        allocator_ = boost::shared_ptr<Allocator>(new HashedAllocator());
+        allocators_[basic_type] = AllocatorPtr(new HashedAllocator(basic_type));
         break;
     case ALLOC_RANDOM:
-        allocator_ = boost::shared_ptr<Allocator>(new RandomAllocator());
+        allocators_[basic_type] = AllocatorPtr(new RandomAllocator(basic_type));
         break;
-
     default:
         isc_throw(BadValue, "Invalid/unsupported allocation algorithm");
     }
 
+    // If this is IPv6 allocation engine, initalize also temporary addrs
+    // and prefixes
+    if (ipv6) {
+        switch (engine_type) {
+        case ALLOC_ITERATIVE:
+            allocators_[Lease::TYPE_TA] = AllocatorPtr(new IterativeAllocator(Lease::TYPE_TA));
+            allocators_[Lease::TYPE_PD] = AllocatorPtr(new IterativeAllocator(Lease::TYPE_PD));
+            break;
+        case ALLOC_HASHED:
+            allocators_[Lease::TYPE_TA] = AllocatorPtr(new HashedAllocator(Lease::TYPE_TA));
+            allocators_[Lease::TYPE_PD] = AllocatorPtr(new HashedAllocator(Lease::TYPE_PD));
+            break;
+        case ALLOC_RANDOM:
+            allocators_[Lease::TYPE_TA] = AllocatorPtr(new RandomAllocator(Lease::TYPE_TA));
+            allocators_[Lease::TYPE_PD] = AllocatorPtr(new RandomAllocator(Lease::TYPE_PD));
+            break;
+        default:
+            isc_throw(BadValue, "Invalid/unsupported allocation algorithm");
+        }
+    }
+
     // Register hook points
     hook_index_lease4_select_ = Hooks.hook_index_lease4_select_;
     hook_index_lease6_select_ = Hooks.hook_index_lease6_select_;
 }
 
-Lease6Ptr
-AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
-                              const DuidPtr& duid,
-                              uint32_t iaid,
-                              const IOAddress& hint,
-                              const bool fwd_dns_update,
-                              const bool rev_dns_update,
-                              const std::string& hostname,
-                              bool fake_allocation,
-                              const isc::hooks::CalloutHandlePtr& callout_handle) {
+Lease6Collection
+AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
+                             uint32_t iaid, const IOAddress& hint,
+                             Lease::Type type, const bool fwd_dns_update,
+                             const bool rev_dns_update,
+                             const std::string& hostname, bool fake_allocation,
+                             const isc::hooks::CalloutHandlePtr& callout_handle) {
 
     try {
-        // That check is not necessary. We create allocator in AllocEngine
-        // constructor
-        if (!allocator_) {
-            isc_throw(InvalidOperation, "No allocator selected");
+        AllocatorPtr allocator = getAllocator(type);
+
+        if (!allocator) {
+            isc_throw(InvalidOperation, "No allocator specified for "
+                      << Lease6::typeToText(type));
         }
 
         if (!subnet) {
@@ -222,40 +317,54 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
         }
 
         // check if there's existing lease for that subnet/duid/iaid combination.
-        Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(*duid, iaid, subnet->getID());
-        if (existing) {
-            // we have a lease already. This is a returning client, probably after
-            // his reboot.
+        /// @todo: Make this generic (cover temp. addrs and prefixes)
+        Lease6Collection existing = LeaseMgrFactory::instance().getLeases6(type,
+                                    *duid, iaid, subnet->getID());
+
+        if (!existing.empty()) {
+            // we have at least one lease already. This is a returning client,
+            // probably after his reboot.
             return (existing);
         }
 
         // check if the hint is in pool and is available
-        if (subnet->inPool(hint)) {
-            existing = LeaseMgrFactory::instance().getLease6(hint);
-            if (!existing) {
+        // This is equivalent of subnet->inPool(hint), but returns the pool
+        Pool6Ptr pool = boost::dynamic_pointer_cast<Pool6>(subnet->getPool(type, hint, false));
+
+        if (pool) {
+            /// @todo: We support only one hint for now
+            Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(type, hint);
+            if (!lease) {
                 /// @todo: check if the hint is reserved once we have host support
                 /// implemented
 
                 // the hint is valid and not currently used, let's create a lease for it
-                Lease6Ptr lease = createLease6(subnet, duid, iaid,
-                                               hint,
-                                               fwd_dns_update,
-                                               rev_dns_update, hostname,
-                                               callout_handle,
-                                               fake_allocation);
+                lease = createLease6(subnet, duid, iaid, hint, pool->getLength(),
+                                     type, fwd_dns_update, rev_dns_update,
+                                     hostname, callout_handle, fake_allocation);
 
                 // It can happen that the lease allocation failed (we could have lost
                 // the race condition. That means that the hint is lo longer usable and
                 // we need to continue the regular allocation path.
                 if (lease) {
-                    return (lease);
+                    /// @todo: We support only one lease per ia for now
+                    Lease6Collection collection;
+                    collection.push_back(lease);
+                    return (collection);
                 }
             } else {
-                if (existing->expired()) {
-                    return (reuseExpiredLease(existing, subnet, duid, iaid,
+                if (lease->expired()) {
+                    /// We found a lease and it is expired, so we can reuse it
+                    lease = reuseExpiredLease(lease, subnet, duid, iaid,
+                                              pool->getLength(),
                                               fwd_dns_update, rev_dns_update,
                                               hostname, callout_handle,
-                                              fake_allocation));
+                                              fake_allocation);
+
+                    /// @todo: We support only one lease per ia for now
+                    Lease6Collection collection;
+                    collection.push_back(lease);
+                    return (collection);
                 }
 
             }
@@ -279,21 +388,35 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
 
         unsigned int i = attempts_;
         do {
-            IOAddress candidate = allocator_->pickAddress(subnet, duid, hint);
+            IOAddress candidate = allocator->pickAddress(subnet, duid, hint);
 
             /// @todo: check if the address is reserved once we have host support
             /// implemented
 
-            Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(candidate);
+            // The first step is to find out prefix length. It is 128 for
+            // non-PD leases.
+            uint8_t prefix_len = 128;
+            if (type == Lease::TYPE_PD) {
+                Pool6Ptr pool = boost::dynamic_pointer_cast<Pool6>(
+                    subnet->getPool(type, candidate, false));
+                prefix_len = pool->getLength();
+            }
+
+            Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(type,
+                                 candidate);
             if (!existing) {
+
                 // there's no existing lease for selected candidate, so it is
                 // free. Let's allocate it.
+
                 Lease6Ptr lease = createLease6(subnet, duid, iaid, candidate,
-                                               fwd_dns_update, rev_dns_update,
-                                               hostname,
+                                               prefix_len, type, fwd_dns_update,
+                                               rev_dns_update, hostname,
                                                callout_handle, fake_allocation);
                 if (lease) {
-                    return (lease);
+                    Lease6Collection collection;
+                    collection.push_back(lease);
+                    return (collection);
                 }
 
                 // Although the address was free just microseconds ago, it may have
@@ -301,10 +424,13 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
                 // allocation attempts.
             } else {
                 if (existing->expired()) {
-                    return (reuseExpiredLease(existing, subnet, duid, iaid,
-                                              fwd_dns_update, rev_dns_update,
-                                              hostname, callout_handle,
-                                              fake_allocation));
+                    existing = reuseExpiredLease(existing, subnet, duid, iaid,
+                                                 prefix_len, fwd_dns_update,
+                                                 rev_dns_update, hostname,
+                                                 callout_handle, fake_allocation);
+                    Lease6Collection collection;
+                    collection.push_back(existing);
+                    return (collection);
                 }
             }
 
@@ -322,20 +448,16 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
         LOG_ERROR(dhcpsrv_logger, DHCPSRV_ADDRESS6_ALLOC_ERROR).arg(e.what());
     }
 
-    return (Lease6Ptr());
+    return (Lease6Collection());
 }
 
 Lease4Ptr
-AllocEngine::allocateAddress4(const SubnetPtr& subnet,
-                              const ClientIdPtr& clientid,
-                              const HWAddrPtr& hwaddr,
-                              const IOAddress& hint,
-                              const bool fwd_dns_update,
-                              const bool rev_dns_update,
-                              const std::string& hostname,
-                              bool fake_allocation,
-                              const isc::hooks::CalloutHandlePtr& callout_handle,
-                              Lease4Ptr& old_lease) {
+AllocEngine::allocateLease4(const SubnetPtr& subnet, const ClientIdPtr& clientid,
+                            const HWAddrPtr& hwaddr, const IOAddress& hint,
+                            const bool fwd_dns_update, const bool rev_dns_update,
+                            const std::string& hostname, bool fake_allocation,
+                            const isc::hooks::CalloutHandlePtr& callout_handle,
+                            Lease4Ptr& old_lease) {
 
     // The NULL pointer indicates that the old lease didn't exist. It may
     // be later set to non NULL value if existing lease is found in the
@@ -343,9 +465,12 @@ AllocEngine::allocateAddress4(const SubnetPtr& subnet,
     old_lease.reset();
 
     try {
+
+        AllocatorPtr allocator = getAllocator(Lease::TYPE_V4);
+
         // Allocator is always created in AllocEngine constructor and there is
         // currently no other way to set it, so that check is not really necessary.
-        if (!allocator_) {
+        if (!allocator) {
             isc_throw(InvalidOperation, "No allocator selected");
         }
 
@@ -395,7 +520,7 @@ AllocEngine::allocateAddress4(const SubnetPtr& subnet,
         }
 
         // check if the hint is in pool and is available
-        if (subnet->inPool(hint)) {
+        if (subnet->inPool(Lease::TYPE_V4, hint)) {
             existing = LeaseMgrFactory::instance().getLease4(hint);
             if (!existing) {
                 /// @todo: Check if the hint is reserved once we have host support
@@ -444,7 +569,7 @@ AllocEngine::allocateAddress4(const SubnetPtr& subnet,
 
         unsigned int i = attempts_;
         do {
-            IOAddress candidate = allocator_->pickAddress(subnet, clientid, hint);
+            IOAddress candidate = allocator->pickAddress(subnet, clientid, hint);
 
             /// @todo: check if the address is reserved once we have host support
             /// implemented
@@ -571,7 +696,8 @@ Lease4Ptr AllocEngine::renewLease4(const SubnetPtr& subnet,
 Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
                                          const Subnet6Ptr& subnet,
                                          const DuidPtr& duid,
-                                         uint32_t iaid,
+                                         const uint32_t iaid,
+                                         uint8_t prefix_len,
                                          const bool fwd_dns_update,
                                          const bool rev_dns_update,
                                          const std::string& hostname,
@@ -582,6 +708,10 @@ Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
         isc_throw(BadValue, "Attempt to recycle lease that is still valid");
     }
 
+    if (expired->type_ != Lease::TYPE_PD) {
+        prefix_len = 128; // non-PD lease types must be always /128
+    }
+
     // address, lease type and prefixlen (0) stay the same
     expired->iaid_ = iaid;
     expired->duid_ = duid;
@@ -595,6 +725,7 @@ Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
     expired->hostname_ = hostname;
     expired->fqdn_fwd_ = fwd_dns_update;
     expired->fqdn_rev_ = rev_dns_update;
+    expired->prefixlen_ = prefix_len;
 
     /// @todo: log here that the lease was reused (there's ticket #2524 for
     /// logging in libdhcpsrv)
@@ -728,17 +859,24 @@ Lease4Ptr AllocEngine::reuseExpiredLease(Lease4Ptr& expired,
 
 Lease6Ptr AllocEngine::createLease6(const Subnet6Ptr& subnet,
                                     const DuidPtr& duid,
-                                    uint32_t iaid,
+                                    const uint32_t iaid,
                                     const IOAddress& addr,
+                                    uint8_t prefix_len,
+                                    const Lease::Type type,
                                     const bool fwd_dns_update,
                                     const bool rev_dns_update,
                                     const std::string& hostname,
                                     const isc::hooks::CalloutHandlePtr& callout_handle,
                                     bool fake_allocation /*= false */ ) {
 
-    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid, iaid,
+    if (type != Lease::TYPE_PD) {
+        prefix_len = 128; // non-PD lease types must be always /128
+    }
+
+    Lease6Ptr lease(new Lease6(type, addr, duid, iaid,
                                subnet->getPreferred(), subnet->getValid(),
-                               subnet->getT1(), subnet->getT2(), subnet->getID()));
+                               subnet->getT1(), subnet->getT2(), subnet->getID(),
+                               prefix_len));
 
     lease->fqdn_fwd_ = fwd_dns_update;
     lease->fqdn_rev_ = rev_dns_update;
@@ -795,7 +933,8 @@ Lease6Ptr AllocEngine::createLease6(const Subnet6Ptr& subnet,
 
         // It is for advertise only. We should not insert the lease into LeaseMgr,
         // but rather check that we could have inserted it.
-        Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(addr);
+        Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(
+                             Lease::TYPE_NA, addr);
         if (!existing) {
             return (lease);
         } else {
@@ -898,6 +1037,16 @@ Lease4Ptr AllocEngine::createLease4(const SubnetPtr& subnet,
     }
 }
 
+AllocEngine::AllocatorPtr AllocEngine::getAllocator(Lease::Type type) {
+    std::map<Lease::Type, AllocatorPtr>::const_iterator alloc = allocators_.find(type);
+
+    if (alloc == allocators_.end()) {
+        isc_throw(BadValue, "No allocator initialized for pool type "
+                  << Lease::typeToText(type));
+    }
+    return (alloc->second);
+}
+
 AllocEngine::~AllocEngine() {
     // no need to delete allocator. smart_ptr will do the trick for us
 }
diff --git a/src/lib/dhcpsrv/alloc_engine.h b/src/lib/dhcpsrv/alloc_engine.h
index ad18b83..8299bb8 100644
--- a/src/lib/dhcpsrv/alloc_engine.h
+++ b/src/lib/dhcpsrv/alloc_engine.h
@@ -25,6 +25,8 @@
 #include <boost/shared_ptr.hpp>
 #include <boost/noncopyable.hpp>
 
+#include <map>
+
 namespace isc {
 namespace dhcp {
 
@@ -68,6 +70,14 @@ protected:
         /// again if necessary. The number of times this method is called will
         /// increase as the number of available leases will decrease.
         ///
+        /// This method can also be used to pick a prefix. We should not rename
+        /// it to pickLease(), because at this early stage there is no concept
+        /// of a lease yet. Here it is a matter of selecting one address or
+        /// prefix from the defined pool, without going into details who it is
+        /// for or who uses it. I thought that pickAddress() is less confusing
+        /// than pickResource(), because nobody would immediately know what the
+        /// resource means in this context.
+        ///
         /// @param subnet next address will be returned from pool of that subnet
         /// @param duid Client's DUID
         /// @param hint client's hint
@@ -77,12 +87,26 @@ protected:
         pickAddress(const SubnetPtr& subnet, const DuidPtr& duid,
                     const isc::asiolink::IOAddress& hint) = 0;
 
+        /// @brief Default constructor.
+        ///
+        /// Specifies which type of leases this allocator will assign
+        /// @param pool_type specifies pool type (addresses, temp. addr or prefixes)
+        Allocator(Lease::Type pool_type)
+            :pool_type_(pool_type) {
+        }
+
         /// @brief virtual destructor
         virtual ~Allocator() {
         }
     protected:
+
+        /// @brief defines pool type allocation
+        Lease::Type pool_type_;
     };
 
+    /// defines a pointer to allocator
+    typedef boost::shared_ptr<Allocator> AllocatorPtr;
+
     /// @brief Address/prefix allocator that iterates over all addresses
     ///
     /// This class implements iterative algorithm that returns all addresses in
@@ -95,7 +119,8 @@ protected:
         /// @brief default constructor
         ///
         /// Does not do anything
-        IterativeAllocator();
+        /// @param type - specifies allocation type
+        IterativeAllocator(Lease::Type type);
 
         /// @brief returns the next address from pools in a subnet
         ///
@@ -107,13 +132,31 @@ protected:
             pickAddress(const SubnetPtr& subnet,
                         const DuidPtr& duid,
                         const isc::asiolink::IOAddress& hint);
-    private:
+    protected:
 
-        /// @brief returns an address by one
+        /// @brief Returns an address increased by one
+        ///
+        /// This method works for both IPv4 and IPv6 addresses. For example,
+        /// increase 192.0.2.255 will become 192.0.3.0.
+        ///
         /// @param addr address to be increased
         /// @return address increased by one
-        isc::asiolink::IOAddress increaseAddress(const isc::asiolink::IOAddress& addr);
+        static isc::asiolink::IOAddress
+        increaseAddress(const isc::asiolink::IOAddress& addr);
 
+        /// @brief Returns the next prefix
+        ///
+        /// This method works for IPv6 addresses only. It increases
+        /// specified prefix by a given prefix_len. For example, 2001:db8::
+        /// increased by prefix length /32 will become 2001:db9::. This method
+        /// is used to iterate over IPv6 prefix pools
+        ///
+        /// @param prefix prefix to be increased
+        /// @param prefix_len length of the prefix to be increased
+        /// @return result prefix
+        static isc::asiolink::IOAddress
+        increasePrefix(const isc::asiolink::IOAddress& prefix,
+                       const uint8_t prefix_len);
     };
 
     /// @brief Address/prefix allocator that gets an address based on a hash
@@ -123,7 +166,8 @@ protected:
     public:
 
         /// @brief default constructor (does nothing)
-        HashedAllocator();
+        /// @param type - specifies allocation type
+        HashedAllocator(Lease::Type type);
 
         /// @brief returns an address based on hash calculated from client's DUID.
         ///
@@ -145,7 +189,8 @@ protected:
     public:
 
         /// @brief default constructor (does nothing)
-        RandomAllocator();
+        /// @param type - specifies allocation type
+        RandomAllocator(Lease::Type type);
 
         /// @brief returns an random address from pool of specified subnet
         ///
@@ -180,7 +225,8 @@ protected:
     /// @param engine_type selects allocation algorithm
     /// @param attempts number of attempts for each lease allocation before
     ///        we give up (0 means unlimited)
-    AllocEngine(AllocType engine_type, unsigned int attempts);
+    /// @param ipv6 specifies if the engine should work for IPv4 or IPv6
+    AllocEngine(AllocType engine_type, unsigned int attempts, bool ipv6 = true);
 
     /// @brief Returns IPv4 lease.
     ///
@@ -229,16 +275,13 @@ protected:
     ///
     /// @return Allocated IPv4 lease (or NULL if allocation failed)
     Lease4Ptr
-    allocateAddress4(const SubnetPtr& subnet,
-                     const ClientIdPtr& clientid,
-                     const HWAddrPtr& hwaddr,
-                     const isc::asiolink::IOAddress& hint,
-                     const bool fwd_dns_update,
-                     const bool rev_dns_update,
-                     const std::string& hostname,
-                     bool fake_allocation,
-                     const isc::hooks::CalloutHandlePtr& callout_handle,
-                     Lease4Ptr& old_lease);
+    allocateLease4(const SubnetPtr& subnet, const ClientIdPtr& clientid,
+                   const HWAddrPtr& hwaddr,
+                   const isc::asiolink::IOAddress& hint,
+                   const bool fwd_dns_update, const bool rev_dns_update,
+                   const std::string& hostname, bool fake_allocation,
+                   const isc::hooks::CalloutHandlePtr& callout_handle,
+                   Lease4Ptr& old_lease);
 
     /// @brief Renews a IPv4 lease
     ///
@@ -283,6 +326,7 @@ protected:
     /// @param duid Client's DUID
     /// @param iaid iaid field from the IA_NA container that client sent
     /// @param hint a hint that the client provided
+    /// @param type lease type (IA, TA or PD)
     /// @param fwd_dns_update A boolean value which indicates that server takes
     ///        responsibility for the forward DNS Update for this lease
     ///        (if true).
@@ -295,17 +339,19 @@ protected:
     /// @param callout_handle a callout handle (used in hooks). A lease callouts
     ///        will be executed if this parameter is passed.
     ///
-    /// @return Allocated IPv6 lease (or NULL if allocation failed)
-    Lease6Ptr
-    allocateAddress6(const Subnet6Ptr& subnet,
-                     const DuidPtr& duid,
-                     uint32_t iaid,
-                     const isc::asiolink::IOAddress& hint,
-                     const bool fwd_dns_update,
-                     const bool rev_dns_update,
-                     const std::string& hostname,
-                     bool fake_allocation,
-                     const isc::hooks::CalloutHandlePtr& callout_handle);
+    /// @return Allocated IPv6 leases (may be empty if allocation failed)
+    Lease6Collection
+    allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid, uint32_t iaid,
+                    const isc::asiolink::IOAddress& hint, Lease::Type type,
+                    const bool fwd_dns_update, const bool rev_dns_update,
+                    const std::string& hostname, bool fake_allocation,
+                    const isc::hooks::CalloutHandlePtr& callout_handle);
+
+    /// @brief returns allocator for a given pool type
+    /// @param type type of pool (V4, IA, TA or PD)
+    /// @throw BadValue if allocator for a given type is missing
+    /// @return pointer to allocator handing a given resource types
+    AllocatorPtr getAllocator(Lease::Type type);
 
     /// @brief Destructor. Used during DHCPv6 service shutdown.
     virtual ~AllocEngine();
@@ -352,7 +398,10 @@ private:
     /// @param duid client's DUID
     /// @param iaid IAID from the IA_NA container the client sent to us
     /// @param addr an address that was selected and is confirmed to be
-    /// available
+    ///        available
+    /// @param prefix_len length of the prefix (for PD only)
+    ///        should be 128 for other lease types
+    /// @param type lease type (IA, TA or PD)
     /// @param fwd_dns_update A boolean value which indicates that server takes
     ///        responsibility for the forward DNS Update for this lease
     ///        (if true).
@@ -368,7 +417,8 @@ private:
     /// @return allocated lease (or NULL in the unlikely case of the lease just
     ///         became unavailable)
     Lease6Ptr createLease6(const Subnet6Ptr& subnet, const DuidPtr& duid,
-                           uint32_t iaid, const isc::asiolink::IOAddress& addr,
+                           const uint32_t iaid, const isc::asiolink::IOAddress& addr,
+                           const uint8_t prefix_len, const Lease::Type type,
                            const bool fwd_dns_update, const bool rev_dns_update,
                            const std::string& hostname,
                            const isc::hooks::CalloutHandlePtr& callout_handle,
@@ -415,6 +465,8 @@ private:
     /// @param subnet subnet the lease is allocated from
     /// @param duid client's DUID
     /// @param iaid IAID from the IA_NA container the client sent to us
+    /// @param prefix_len prefix length (for PD leases)
+    ///        Should be 128 for other lease types
     /// @param fwd_dns_update A boolean value which indicates that server takes
     ///        responsibility for the forward DNS Update for this lease
     ///        (if true).
@@ -429,7 +481,8 @@ private:
     /// @return refreshed lease
     /// @throw BadValue if trying to recycle lease that is still valid
     Lease6Ptr reuseExpiredLease(Lease6Ptr& expired, const Subnet6Ptr& subnet,
-                                const DuidPtr& duid, uint32_t iaid,
+                                const DuidPtr& duid, const uint32_t iaid,
+                                uint8_t prefix_len,
                                 const bool fwd_dns_update,
                                 const bool rev_dns_update,
                                 const std::string& hostname,
@@ -437,7 +490,10 @@ private:
                                 bool fake_allocation = false);
 
     /// @brief a pointer to currently used allocator
-    boost::shared_ptr<Allocator> allocator_;
+    ///
+    /// For IPv4, there will be only one allocator: TYPE_V4
+    /// For IPv6, there will be 3 allocators: TYPE_NA, TYPE_TA, TYPE_PD
+    std::map<Lease::Type, AllocatorPtr> allocators_;
 
     /// @brief number of attempts before we give up lease allocation (0=unlimited)
     unsigned int attempts_;
diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc
index 56e4c8e..b4fb11d 100644
--- a/src/lib/dhcpsrv/cfgmgr.cc
+++ b/src/lib/dhcpsrv/cfgmgr.cc
@@ -16,6 +16,7 @@
 #include <dhcp/libdhcp++.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/dhcpsrv_log.h>
+#include <string>
 
 using namespace isc::asiolink;
 using namespace isc::util;
@@ -269,14 +270,31 @@ std::string CfgMgr::getDataDir() {
 
 void
 CfgMgr::addActiveIface(const std::string& iface) {
-    if (isIfaceListedActive(iface)) {
+
+    size_t pos = iface.find("/");
+    std::string iface_copy = iface;
+
+    if (pos != std::string::npos) {
+        std::string addr_string = iface.substr(pos + 1);
+        try {
+            IOAddress addr(addr_string);
+            iface_copy = iface.substr(0,pos);
+            unicast_addrs_.insert(make_pair(iface_copy, addr));
+        } catch (...) {
+            isc_throw(BadValue, "Can't convert '" << addr_string
+                      << "' into address in interface defition ('"
+                      << iface << "')");
+        }
+    }
+
+    if (isIfaceListedActive(iface_copy)) {
         isc_throw(DuplicateListeningIface,
-                  "attempt to add duplicate interface '" << iface << "'"
+                  "attempt to add duplicate interface '" << iface_copy << "'"
                   " to the set of interfaces on which server listens");
     }
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_IFACE)
-        .arg(iface);
-    active_ifaces_.push_back(iface);
+        .arg(iface_copy);
+    active_ifaces_.push_back(iface_copy);
 }
 
 void
@@ -292,6 +310,8 @@ CfgMgr::deleteActiveIfaces() {
               DHCPSRV_CFGMGR_CLEAR_ACTIVE_IFACES);
     active_ifaces_.clear();
     all_ifaces_active_ = false;
+
+    unicast_addrs_.clear();
 }
 
 bool
@@ -319,6 +339,15 @@ CfgMgr::isIfaceListedActive(const std::string& iface) const {
     return (false);
 }
 
+const isc::asiolink::IOAddress*
+CfgMgr::getUnicast(const std::string& iface) const {
+    UnicastIfacesCollection::const_iterator addr = unicast_addrs_.find(iface);
+    if (addr == unicast_addrs_.end()) {
+        return (NULL);
+    }
+    return (&(*addr).second);
+}
+
 CfgMgr::CfgMgr()
     : datadir_(DHCP_DATA_DIR),
       all_ifaces_active_(false) {
diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h
index 0ec51d0..7b5ead3 100644
--- a/src/lib/dhcpsrv/cfgmgr.h
+++ b/src/lib/dhcpsrv/cfgmgr.h
@@ -305,6 +305,17 @@ public:
     /// interfaces on which server is configured to listen.
     bool isActiveIface(const std::string& iface) const;
 
+    /// @brief returns unicast a given interface should listen on (or NULL)
+    ///
+    /// This method will return an address for a specified interface, if the
+    /// server is supposed to listen on unicast address. This address is
+    /// intended to be used immediately. This pointer is valid only until
+    /// the next configuration change.
+    ///
+    /// @return IOAddress pointer (or NULL if none)
+    const isc::asiolink::IOAddress*
+    getUnicast(const std::string& iface) const;
+
 protected:
 
     /// @brief Protected constructor.
@@ -355,7 +366,7 @@ private:
     /// A collection of option definitions that can be accessed
     /// using option space name they belong to.
     OptionSpaceContainer<OptionDefContainer,
-                         OptionDefinitionPtr> option_def_spaces_;
+        OptionDefinitionPtr, std::string> option_def_spaces_;
 
     /// @brief Container for defined DHCPv6 option spaces.
     OptionSpaceCollection spaces6_;
@@ -372,6 +383,12 @@ private:
     std::list<std::string> active_ifaces_;
     //@}
 
+    /// @name a collection of unicast addresses and the interfaces names the
+    //        server is supposed to listen on
+    //@{
+    typedef std::map<std::string, isc::asiolink::IOAddress> UnicastIfacesCollection;
+    UnicastIfacesCollection unicast_addrs_;
+
     /// A flag which indicates that server should listen on all available
     /// interfaces.
     bool all_ifaces_active_;
diff --git a/src/lib/dhcpsrv/dhcp_parsers.cc b/src/lib/dhcpsrv/dhcp_parsers.cc
index e03d13f..ffd49d3 100644
--- a/src/lib/dhcpsrv/dhcp_parsers.cc
+++ b/src/lib/dhcpsrv/dhcp_parsers.cc
@@ -231,6 +231,7 @@ InterfaceListConfigParser::commit() {
 
 bool
 InterfaceListConfigParser::isIfaceAdded(const std::string& iface) const {
+
     for (IfaceListStorage::const_iterator it = interfaces_.begin();
          it != interfaces_.end(); ++it) {
         if (iface == *it) {
@@ -429,6 +430,8 @@ OptionDataParser::createOption() {
                 << "')");
     }
 
+    const bool csv_format = boolean_values_->getParam("csv-format");
+
     // Find the Option Definition for the option by its option code.
     // findOptionDefinition will throw if not found, no need to test.
     OptionDefinitionPtr def;
@@ -449,7 +452,10 @@ OptionDataParser::createOption() {
         if (std::distance(range.first, range.second) > 0) {
             def = *range.first;
         }
-        if (!def) {
+
+        // It's ok if we don't have option format if the option is
+        // specified as hex
+        if (!def && csv_format) {
             isc_throw(DhcpConfigError, "definition for the option '"
                       << option_space << "." << option_name
                       << "' having code '" <<  option_code
@@ -459,7 +465,6 @@ OptionDataParser::createOption() {
 
     // Get option data from the configuration database ('data' field).
     const std::string option_data = string_values_->getParam("data");
-    const bool csv_format = boolean_values_->getParam("csv-format");
 
     // Transform string of hexadecimal digits into binary format.
     std::vector<uint8_t> binary;
@@ -1047,8 +1052,16 @@ SubnetConfigParser::createSubnet() {
             }
             // Add sub-options (if any).
             appendSubOptions(option_space, desc.option);
-            // In any case, we add the option to the subnet.
-            subnet_->addOption(desc.option, false, option_space);
+
+            // Check if the option space defines a vendor-option
+            uint32_t vendor_id = optionSpaceToVendorId(option_space);
+            if (vendor_id) {
+                // This is a vendor option
+                subnet_->addVendorOption(desc.option, false, vendor_id);
+            } else {
+                // This is a normal option
+                subnet_->addOption(desc.option, false, option_space);
+            }
         }
     }
 
@@ -1077,12 +1090,59 @@ SubnetConfigParser::createSubnet() {
             if (!existing_desc.option) {
                 // Add sub-options (if any).
                 appendSubOptions(option_space, desc.option);
-                subnet_->addOption(desc.option, false, option_space);
+
+                uint32_t vendor_id = optionSpaceToVendorId(option_space);
+                if (vendor_id) {
+                    // This is a vendor option
+                    subnet_->addVendorOption(desc.option, false, vendor_id);
+                } else {
+                    // This is a normal option
+                    subnet_->addOption(desc.option, false, option_space);
+                }
             }
         }
     }
 }
 
+uint32_t
+SubnetConfigParser::optionSpaceToVendorId(const std::string& option_space) {
+    if (option_space.size() < 8) {
+        // 8 is a minimal length of "vendor-X" format
+        return (0);
+    }
+    if (option_space.substr(0,7) != "vendor-") {
+        return (0);
+    }
+
+    // text after "vendor-", supposedly numbers only
+    string x = option_space.substr(7);
+
+    int64_t check;
+    try {
+        check = boost::lexical_cast<int64_t>(x);
+    } catch (const boost::bad_lexical_cast &) {
+        /// @todo: Should we throw here?
+        // isc_throw(BadValue, "Failed to parse vendor-X value (" << x
+        //           << ") as unsigned 32-bit integer.");
+        return (0);
+    }
+    if (check > std::numeric_limits<uint32_t>::max()) {
+        /// @todo: Should we throw here?
+        //isc_throw(BadValue, "Value " << x << "is too large"
+        //          << " for unsigned 32-bit integer.");
+        return (0);
+    }
+    if (check < 0) {
+        /// @todo: Should we throw here?
+        // isc_throw(BadValue, "Value " << x << "is negative."
+        //       << " Only 0 or larger are allowed for unsigned 32-bit integer.");
+        return (0);
+    }
+
+    // value is small enough to fit
+    return (static_cast<uint32_t>(check));
+}
+
 isc::dhcp::Triplet<uint32_t>
 SubnetConfigParser::getParam(const std::string& name) {
     uint32_t value = 0;
diff --git a/src/lib/dhcpsrv/dhcp_parsers.h b/src/lib/dhcpsrv/dhcp_parsers.h
index 0829b21..28c57b8 100644
--- a/src/lib/dhcpsrv/dhcp_parsers.h
+++ b/src/lib/dhcpsrv/dhcp_parsers.h
@@ -34,14 +34,14 @@ namespace dhcp {
 
 /// @brief Storage for option definitions.
 typedef OptionSpaceContainer<OptionDefContainer,
-                             OptionDefinitionPtr> OptionDefStorage;
+    OptionDefinitionPtr, std::string> OptionDefStorage;
 /// @brief Shared pointer to option definitions storage.
 typedef boost::shared_ptr<OptionDefStorage> OptionDefStoragePtr;
 
 /// Collection of containers holding option spaces. Each container within
 /// a particular option space holds so-called option descriptors.
 typedef OptionSpaceContainer<Subnet::OptionContainer,
-                             Subnet::OptionDescriptor> OptionStorage;
+    Subnet::OptionDescriptor, std::string> OptionStorage;
 /// @brief Shared pointer to option storage.
 typedef boost::shared_ptr<OptionStorage> OptionStoragePtr;
 
@@ -773,6 +773,15 @@ public:
     /// @brief Adds the created subnet to a server's configuration.
     virtual void commit() = 0;
 
+    /// @brief tries to convert option_space string to numeric vendor_id
+    ///
+    /// This will work if the option_space has format "vendor-X", where
+    /// X can be any value between 1 and MAX_UINT32.
+    /// This is used to detect whether a given option-space is a vendor
+    /// space or not. Returns 0 if the format is different.
+    /// @return numeric vendor-id (or 0 if the format does not match)
+    static uint32_t optionSpaceToVendorId(const std::string& option_space);
+
 protected:
     /// @brief creates parsers for entries in subnet definition
     ///
diff --git a/src/lib/dhcpsrv/dhcpdb_create.mysql b/src/lib/dhcpsrv/dhcpdb_create.mysql
index f0da337..10531e3 100644
--- a/src/lib/dhcpsrv/dhcpdb_create.mysql
+++ b/src/lib/dhcpsrv/dhcpdb_create.mysql
@@ -36,7 +36,10 @@ CREATE TABLE lease4 (
     client_id VARBINARY(128),                   # Client ID
     valid_lifetime INT UNSIGNED,                # Length of the lease (seconds)
     expire TIMESTAMP,                           # Expiration time of the lease
-    subnet_id INT UNSIGNED                      # Subnet identification
+    subnet_id INT UNSIGNED,                     # Subnet identification
+    fqdn_fwd BOOL,                              # Has forward DNS update been performed by a server
+    fqdn_rev BOOL,                              # Has reverse DNS update been performed by a server
+    hostname VARCHAR(255)                       # The FQDN of the client
     ) ENGINE = INNODB;
 
 
@@ -60,7 +63,11 @@ CREATE TABLE lease6 (
     lease_type TINYINT,                         # Lease type (see lease6_types
                                                 #    table for possible values)
     iaid INT UNSIGNED,                          # See Section 10 of RFC 3315
-    prefix_len TINYINT UNSIGNED                 # For IA_PD only
+    prefix_len TINYINT UNSIGNED,                # For IA_PD only
+    fqdn_fwd BOOL,                              # Has forward DNS update been performed by a server
+    fqdn_rev BOOL,                              # Has reverse DNS update been performed by a server
+    hostname VARCHAR(255)                       # The FQDN of the client
+
     ) ENGINE = INNODB;
 
 # Create search indexes for lease4 table 
@@ -69,7 +76,9 @@ CREATE INDEX lease6_by_iaid_subnet_id_duid ON lease6 (iaid, subnet_id, duid);
 
 # ... and a definition of lease6 types.  This table is a convenience for
 # users of the database - if they want to view the lease table and use the
-# type names, they can join this table with the lease6 table
+# type names, they can join this table with the lease6 table.
+# Make sure those values match Lease6::LeaseType enum (see src/bin/dhcpsrv/
+# lease_mgr.h)
 CREATE TABLE lease6_types (
     lease_type TINYINT PRIMARY KEY NOT NULL,    # Lease type code.
     name VARCHAR(5)                             # Name of the lease type
diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes
index a915b02..6ee3e87 100644
--- a/src/lib/dhcpsrv/dhcpsrv_messages.mes
+++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes
@@ -210,6 +210,11 @@ A debug message issued when the server is attempting to obtain an IPv6
 lease from the memory file database for a client with the specified IAID
 (Identity Association ID), Subnet ID and DUID (DHCP Unique Identifier).
 
+% DHCPSRV_MEMFILE_GET_CLIENTID_HWADDR_SUBID obtaining IPv4 lease for client ID %1, hardware address %2 and subnet ID %3
+A debug message issued when the server is attempting to obtain an IPv4
+lease from the memory file database for a client with the specified
+client ID, hardware address and subnet ID.
+
 % DHCPSRV_MEMFILE_GET_SUBID_CLIENTID obtaining IPv4 lease for subnet ID %1 and client ID %2
 A debug message issued when the server is attempting to obtain an IPv4
 lease from the memory file database for a client with the specified
@@ -247,7 +252,7 @@ recommended.
 A debug message issued when the server is about to add an IPv4 lease
 with the specified address to the MySQL backend database.
 
-% DHCPSRV_MYSQL_ADD_ADDR6 adding IPv6 lease with address %1
+% DHCPSRV_MYSQL_ADD_ADDR6 adding IPv6 lease with address %1, lease type %2
 A debug message issued when the server is about to add an IPv6 lease
 with the specified address to the MySQL backend database.
 
@@ -270,7 +275,7 @@ the specified address from the MySQL database for the specified address.
 A debug message issued when the server is attempting to obtain an IPv4
 lease from the MySQL database for the specified address.
 
-% DHCPSRV_MYSQL_GET_ADDR6 obtaining IPv6 lease for address %1
+% DHCPSRV_MYSQL_GET_ADDR6 obtaining IPv6 lease for address %1, lease type %2
 A debug message issued when the server is attempting to obtain an IPv6
 lease from the MySQL database for the specified address.
 
@@ -284,12 +289,12 @@ A debug message issued when the server is attempting to obtain a set
 of IPv4 leases from the MySQL database for a client with the specified
 hardware address.
 
-% DHCPSRV_MYSQL_GET_IAID_DUID obtaining IPv4 leases for IAID %1 and DUID %2
+% DHCPSRV_MYSQL_GET_IAID_DUID obtaining IPv6 leases for IAID %1, DUID %2, lease type %3
 A debug message issued when the server is attempting to obtain a set of
 IPv6 lease from the MySQL database for a client with the specified IAID
 (Identity Association ID) and DUID (DHCP Unique Identifier).
 
-% DHCPSRV_MYSQL_GET_IAID_SUBID_DUID obtaining IPv4 leases for IAID %1, Subnet ID %2 and DUID %3
+% DHCPSRV_MYSQL_GET_IAID_SUBID_DUID obtaining IPv6 leases for IAID %1, Subnet ID %2, DUID %3, lease type %4
 A debug message issued when the server is attempting to obtain an IPv6
 lease from the MySQL database for a client with the specified IAID
 (Identity Association ID), Subnet ID and DUID (DHCP Unique Identifier).
@@ -316,7 +321,7 @@ be rolled back and not committed to the database.
 A debug message issued when the server is attempting to update IPv4
 lease from the MySQL database for the specified address.
 
-% DHCPSRV_MYSQL_UPDATE_ADDR6 updating IPv6 lease for address %1
+% DHCPSRV_MYSQL_UPDATE_ADDR6 updating IPv6 lease for address %1, lease type %2
 A debug message issued when the server is attempting to update IPv6
 lease from the MySQL database for the specified address.
 
diff --git a/src/lib/dhcpsrv/lease.cc b/src/lib/dhcpsrv/lease.cc
new file mode 100644
index 0000000..2e4bf2e
--- /dev/null
+++ b/src/lib/dhcpsrv/lease.cc
@@ -0,0 +1,237 @@
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcpsrv/lease.h>
+#include <sstream>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+Lease::Lease(const isc::asiolink::IOAddress& addr, uint32_t t1, uint32_t t2,
+             uint32_t valid_lft, SubnetID subnet_id, time_t cltt,
+             const bool fqdn_fwd, const bool fqdn_rev,
+             const std::string& hostname)
+    :addr_(addr), t1_(t1), t2_(t2), valid_lft_(valid_lft), cltt_(cltt),
+     subnet_id_(subnet_id), fixed_(false), hostname_(hostname),
+     fqdn_fwd_(fqdn_fwd), fqdn_rev_(fqdn_rev) {
+}
+
+std::string
+Lease::typeToText(Lease::Type type) {
+   switch (type) {
+   case Lease::TYPE_V4:
+       return string("V4");
+   case Lease::TYPE_NA:
+       return string("IA_NA");
+   case Lease::TYPE_TA:
+       return string("IA_TA");
+   case Lease::TYPE_PD:
+       return string("IA_PD");
+       break;
+   default: {
+       stringstream tmp;
+       tmp << "unknown (" << type << ")";
+       return (tmp.str());
+   }
+   }
+}
+
+bool Lease::expired() const {
+
+    // Let's use int64 to avoid problems with negative/large uint32 values
+    int64_t expire_time = cltt_ + valid_lft_;
+    return (expire_time < time(NULL));
+}
+
+Lease4::Lease4(const Lease4& other)
+    : Lease(other.addr_, other.t1_, other.t2_, other.valid_lft_,
+            other.subnet_id_, other.cltt_, other.fqdn_fwd_,
+            other.fqdn_rev_, other.hostname_), ext_(other.ext_),
+      hwaddr_(other.hwaddr_) {
+
+    fixed_ = other.fixed_;
+    comments_ = other.comments_;
+
+    if (other.client_id_) {
+        client_id_.reset(new ClientId(other.client_id_->getClientId()));
+
+    } else {
+        client_id_.reset();
+
+    }
+}
+
+bool
+Lease4::matches(const Lease4& other) const {
+    if ((client_id_ && !other.client_id_) ||
+        (!client_id_ && other.client_id_)) {
+        // One lease has client-id, but the other doesn't
+        return false;
+    }
+
+    if (client_id_ && other.client_id_ &&
+        *client_id_ != *other.client_id_) {
+        // Different client-ids
+        return false;
+    }
+
+    return (addr_ == other.addr_ &&
+            ext_ == other.ext_ &&
+            hwaddr_ == other.hwaddr_);
+
+}
+
+Lease4&
+Lease4::operator=(const Lease4& other) {
+    if (this != &other) {
+        addr_ = other.addr_;
+        t1_ = other.t1_;
+        t2_ = other.t2_;
+        valid_lft_ = other.valid_lft_;
+        cltt_ = other.cltt_;
+        subnet_id_ = other.subnet_id_;
+        fixed_ = other.fixed_;
+        hostname_ = other.hostname_;
+        fqdn_fwd_ = other.fqdn_fwd_;
+        fqdn_rev_ = other.fqdn_rev_;
+        comments_ = other.comments_;
+        ext_ = other.ext_;
+        hwaddr_ = other.hwaddr_;
+
+        if (other.client_id_) {
+            client_id_.reset(new ClientId(other.client_id_->getClientId()));
+        } else {
+            client_id_.reset();
+        }
+    }
+    return (*this);
+}
+
+Lease6::Lease6(Type type, const isc::asiolink::IOAddress& addr,
+               DuidPtr duid, uint32_t iaid, uint32_t preferred, uint32_t valid,
+               uint32_t t1, uint32_t t2, SubnetID subnet_id, uint8_t prefixlen)
+    : Lease(addr, t1, t2, valid, subnet_id, 0/*cltt*/, false, false, ""),
+      type_(type), prefixlen_(prefixlen), iaid_(iaid), duid_(duid),
+      preferred_lft_(preferred) {
+    if (!duid) {
+        isc_throw(InvalidOperation, "DUID must be specified for a lease");
+    }
+
+    cltt_ = time(NULL);
+}
+
+Lease6::Lease6(Type type, const isc::asiolink::IOAddress& addr,
+               DuidPtr duid, uint32_t iaid, uint32_t preferred, uint32_t valid,
+               uint32_t t1, uint32_t t2, SubnetID subnet_id,
+               const bool fqdn_fwd, const bool fqdn_rev,
+               const std::string& hostname, uint8_t prefixlen)
+    : Lease(addr, t1, t2, valid, subnet_id, 0/*cltt*/,
+            fqdn_fwd, fqdn_rev, hostname),
+      type_(type), prefixlen_(prefixlen), iaid_(iaid), duid_(duid),
+      preferred_lft_(preferred) {
+    if (!duid) {
+        isc_throw(InvalidOperation, "DUID must be specified for a lease");
+    }
+
+    cltt_ = time(NULL);
+}
+
+std::string
+Lease6::toText() const {
+    ostringstream stream;
+
+    stream << "Type:          " << typeToText(type_) << "(" 
+           << static_cast<int>(type_) << ") ";
+    stream << "Address:       " << addr_.toText() << "\n"
+           << "Prefix length: " << static_cast<int>(prefixlen_) << "\n"
+           << "IAID:          " << iaid_ << "\n"
+           << "Pref life:     " << preferred_lft_ << "\n"
+           << "Valid life:    " << valid_lft_ << "\n"
+           << "Cltt:          " << cltt_ << "\n"
+           << "Subnet ID:     " << subnet_id_ << "\n";
+
+    return (stream.str());
+}
+
+std::string
+Lease4::toText() const {
+    ostringstream stream;
+
+    stream << "Address:       " << addr_.toText() << "\n"
+           << "Valid life:    " << valid_lft_ << "\n"
+           << "T1:            " << t1_ << "\n"
+           << "T2:            " << t2_ << "\n"
+           << "Cltt:          " << cltt_ << "\n"
+           << "Subnet ID:     " << subnet_id_ << "\n";
+
+    return (stream.str());
+}
+
+
+bool
+Lease4::operator==(const Lease4& other) const {
+    if ( (client_id_ && !other.client_id_) ||
+         (!client_id_ && other.client_id_) ) {
+        // One lease has client-id, but the other doesn't
+        return false;
+    }
+
+    if (client_id_ && other.client_id_ &&
+        *client_id_ != *other.client_id_) {
+        // Different client-ids
+        return false;
+    }
+
+    return (matches(other) &&
+            subnet_id_ == other.subnet_id_ &&
+            t1_ == other.t1_ &&
+            t2_ == other.t2_ &&
+            valid_lft_ == other.valid_lft_ &&
+            cltt_ == other.cltt_ &&
+            fixed_ == other.fixed_ &&
+            hostname_ == other.hostname_ &&
+            fqdn_fwd_ == other.fqdn_fwd_ &&
+            fqdn_rev_ == other.fqdn_rev_ &&
+            comments_ == other.comments_);
+}
+
+bool
+Lease6::matches(const Lease6& other) const {
+    return (addr_ == other.addr_ &&
+            type_ == other.type_ &&
+            prefixlen_ == other.prefixlen_ &&
+            iaid_ == other.iaid_ &&
+            *duid_ == *other.duid_);
+}
+
+bool
+Lease6::operator==(const Lease6& other) const {
+    return (matches(other) &&
+            preferred_lft_ == other.preferred_lft_ &&
+            valid_lft_ == other.valid_lft_ &&
+            t1_ == other.t1_ &&
+            t2_ == other.t2_ &&
+            cltt_ == other.cltt_ &&
+            subnet_id_ == other.subnet_id_ &&
+            fixed_ == other.fixed_ &&
+            hostname_ == other.hostname_ &&
+            fqdn_fwd_ == other.fqdn_fwd_ &&
+            fqdn_rev_ == other.fqdn_rev_ &&
+            comments_ == other.comments_);
+}
+
+} // namespace isc::dhcp
+} // namespace isc
diff --git a/src/lib/dhcpsrv/lease.h b/src/lib/dhcpsrv/lease.h
new file mode 100644
index 0000000..0f0cb3c
--- /dev/null
+++ b/src/lib/dhcpsrv/lease.h
@@ -0,0 +1,381 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef LEASE_H
+#define LEASE_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/duid.h>
+#include <dhcp/option.h>
+#include <dhcp/hwaddr.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Unique identifier for a subnet (both v4 and v6)
+///
+/// Let's copy SubnetID definition from subnet.h. We can't include it directly,
+/// because subnet.h needs Lease::Type, so it includes lease.h
+typedef uint32_t SubnetID;
+
+/// @brief a common structure for IPv4 and IPv6 leases
+///
+/// This structure holds all information that is common between IPv4 and IPv6
+/// leases.
+struct Lease {
+
+    /// @brief Type of lease or pool
+    typedef enum {
+        TYPE_NA = 0, /// the lease contains non-temporary IPv6 address
+        TYPE_TA = 1, /// the lease contains temporary IPv6 address
+        TYPE_PD = 2, /// the lease contains IPv6 prefix (for prefix delegation)
+        TYPE_V4 = 3  /// IPv4 lease
+    } Type;
+
+    /// @brief returns text representation of a lease type
+    /// @param type lease or pool type to be converted
+    /// @return text decription
+    static std::string typeToText(Type type);
+
+    /// @brief Constructor
+    ///
+    /// @param addr IP address
+    /// @param t1 renewal time
+    /// @param t2 rebinding time
+    /// @param valid_lft Lifetime of the lease
+    /// @param subnet_id Subnet identification
+    /// @param cltt Client last transmission time
+    /// @param fqdn_fwd If true, forward DNS update is performed for a lease.
+    /// @param fqdn_rev If true, reverse DNS update is performed for a lease.
+    /// @param hostname FQDN of the client which gets the lease.
+    Lease(const isc::asiolink::IOAddress& addr, uint32_t t1, uint32_t t2,
+          uint32_t valid_lft, SubnetID subnet_id, time_t cltt,
+          const bool fqdn_fwd, const bool fqdn_rev,
+          const std::string& hostname);
+
+    /// @brief Destructor
+    virtual ~Lease() {}
+
+    /// @brief IPv4 ot IPv6 address
+    ///
+    /// IPv4, IPv6 address or, in the case of a prefix delegation, the prefix.
+    isc::asiolink::IOAddress addr_;
+
+    /// @brief Renewal timer
+    ///
+    /// Specifies renewal time. Although technically it is a property of the
+    /// IA container and not the address itself, since our data model does not
+    /// define a separate IA entity, we are keeping it in the lease. In the
+    /// case of multiple addresses/prefixes for the same IA, each must have
+    /// consistent T1 and T2 values. This is specified in seconds since cltt.
+    uint32_t t1_;
+
+    /// @brief Rebinding timer
+    ///
+    /// Specifies rebinding time. Although technically it is a property of the
+    /// IA container and not the address itself, since our data model does not
+    /// define a separate IA entity, we are keeping it in the lease. In the
+    /// case of multiple addresses/prefixes for the same IA, each must have
+    /// consistent T1 and T2 values. This is specified in seconds since cltt.
+    uint32_t t2_;
+
+    /// @brief Valid lifetime
+    ///
+    /// Expressed as number of seconds since cltt.
+    uint32_t valid_lft_;
+
+    /// @brief Client last transmission time
+    ///
+    /// Specifies a timestamp giving the time when the last transmission from a
+    /// client was received.
+    time_t cltt_;
+
+    /// @brief Subnet identifier
+    ///
+    /// Specifies the identification of the subnet to which the lease belongs.
+    SubnetID subnet_id_;
+
+    /// @brief Fixed lease?
+    ///
+    /// Fixed leases are kept after they are released/expired.
+    bool fixed_;
+
+    /// @brief Client hostname
+    ///
+    /// This field may be empty
+    std::string hostname_;
+
+    /// @brief Forward zone updated?
+    ///
+    /// Set true if the DNS AAAA record for this lease has been updated.
+    bool fqdn_fwd_;
+
+    /// @brief Reverse zone updated?
+    ///
+    /// Set true if the DNS PTR record for this lease has been updated.
+    bool fqdn_rev_;
+
+    /// @brief Lease comments
+    ///
+    /// Currently not used. It may be used for keeping comments made by the
+    /// system administrator.
+    std::string comments_;
+
+    /// @brief Convert Lease to Printable Form
+    ///
+    /// @return String form of the lease
+    virtual std::string toText() const = 0;
+
+    /// @brief returns true if the lease is expired
+    /// @return true if the lease is expired
+    bool expired() const;
+
+};
+
+/// @brief Structure that holds a lease for IPv4 address
+///
+/// For performance reasons it is a simple structure, not a class. If we chose
+/// make it a class, all fields would have to made private and getters/setters
+/// would be required. As this is a critical part of the code that will be used
+/// extensively, direct access is warranted.
+struct Lease4 : public Lease {
+
+    /// @brief Address extension
+    ///
+    /// It is envisaged that in some cases IPv4 address will be accompanied
+    /// with some additional data. One example of such use are Address + Port
+    /// solutions (or Port-restricted Addresses), where several clients may get
+    /// the same address, but different port ranges. This feature is not
+    /// expected to be widely used.  Under normal circumstances, the value
+    /// should be 0.
+    uint32_t ext_;
+
+    /// @brief Hardware address
+    std::vector<uint8_t> hwaddr_;
+
+    /// @brief Client identifier
+    ///
+    /// @todo Should this be a pointer to a client ID or the ID itself?
+    ///       Compare with the DUID in the Lease6 structure.
+    ClientIdPtr client_id_;
+
+    /// @brief Constructor
+    ///
+    /// @param addr IPv4 address.
+    /// @param hwaddr Hardware address buffer
+    /// @param hwaddr_len Length of hardware address buffer
+    /// @param clientid Client identification buffer
+    /// @param clientid_len Length of client identification buffer
+    /// @param valid_lft Lifetime of the lease
+    /// @param t1 renewal time
+    /// @param t2 rebinding time
+    /// @param cltt Client last transmission time
+    /// @param subnet_id Subnet identification
+    /// @param fqdn_fwd If true, forward DNS update is performed for a lease.
+    /// @param fqdn_rev If true, reverse DNS update is performed for a lease.
+    /// @param hostname FQDN of the client which gets the lease.
+    Lease4(const isc::asiolink::IOAddress& addr, const uint8_t* hwaddr, size_t hwaddr_len,
+           const uint8_t* clientid, size_t clientid_len, uint32_t valid_lft,
+           uint32_t t1, uint32_t t2, time_t cltt, uint32_t subnet_id,
+           const bool fqdn_fwd = false, const bool fqdn_rev = false,
+           const std::string& hostname = "")
+        : Lease(addr, t1, t2, valid_lft, subnet_id, cltt, fqdn_fwd, fqdn_rev,
+                hostname),
+        ext_(0), hwaddr_(hwaddr, hwaddr + hwaddr_len) {
+        if (clientid_len) {
+            client_id_.reset(new ClientId(clientid, clientid_len));
+        }
+    }
+
+    /// @brief Default constructor
+    ///
+    /// Initialize fields that don't have a default constructor.
+    Lease4() : Lease(0, 0, 0, 0, 0, 0, false, false, "") {
+    }
+
+    /// @brief Copy constructor
+    ///
+    /// @param other the @c Lease4 object to be copied.
+    Lease4(const Lease4& other);
+
+    /// @brief Check if two objects encapsulate the lease for the same
+    /// client.
+    ///
+    /// Checks if two @c Lease4 objects have the same address, client id,
+    /// HW address and ext_ value.  If these parameters match it is an
+    /// indication that both objects describe the lease for the same
+    /// client but apparently one is a result of renewal of the other. The
+    /// special case of the matching lease is the one that is equal to another.
+    ///
+    /// @param other A lease to compare with.
+    ///
+    /// @return true if the selected parameters of the two leases match.
+    bool matches(const Lease4& other) const;
+
+    /// @brief Assignment operator.
+    ///
+    /// @param other the @c Lease4 object to be assigned.
+    Lease4& operator=(const Lease4& other);
+
+    /// @brief Compare two leases for equality
+    ///
+    /// @param other lease6 object with which to compare
+    bool operator==(const Lease4& other) const;
+
+    /// @brief Compare two leases for inequality
+    ///
+    /// @param other lease6 object with which to compare
+    bool operator!=(const Lease4& other) const {
+        return (!operator==(other));
+    }
+
+    /// @brief Convert lease to printable form
+    ///
+    /// @return Textual represenation of lease data
+    virtual std::string toText() const;
+
+    /// @todo: Add DHCPv4 failover related fields here
+};
+
+/// @brief Pointer to a Lease4 structure.
+typedef boost::shared_ptr<Lease4> Lease4Ptr;
+
+/// @brief A collection of IPv4 leases.
+typedef std::vector<Lease4Ptr> Lease4Collection;
+
+/// @brief Structure that holds a lease for IPv6 address and/or prefix
+///
+/// For performance reasons it is a simple structure, not a class. If we chose
+/// make it a class, all fields would have to made private and getters/setters
+/// would be required. As this is a critical part of the code that will be used
+/// extensively, direct access is warranted.
+struct Lease6 : public Lease {
+
+    /// @brief Lease type
+    ///
+    /// One of normal address, temporary address, or prefix.
+    Type type_;
+
+    /// @brief IPv6 prefix length
+    ///
+    /// This is used only for prefix delegations and is ignored otherwise.
+    uint8_t prefixlen_;
+
+    /// @brief Identity Association Identifier (IAID)
+    ///
+    /// DHCPv6 stores all addresses and prefixes in IA containers (IA_NA,
+    /// IA_TA, IA_PD). All containers may appear more than once in a message.
+    /// To differentiate between them, the IAID field is present
+    uint32_t iaid_;
+
+    /// @brief Client identifier
+    DuidPtr duid_;
+
+    /// @brief preferred lifetime
+    ///
+    /// This parameter specifies the preferred lifetime since the lease was
+    /// assigned or renewed (cltt), expressed in seconds.
+    uint32_t preferred_lft_;
+
+    /// @todo: Add DHCPv6 failover related fields here
+
+    /// @brief Constructor
+    /// @param type Lease type.
+    /// @param addr Assigned address.
+    /// @param duid A pointer to an object representing DUID.
+    /// @param iaid IAID.
+    /// @param preferred Preferred lifetime.
+    /// @param valid Valid lifetime.
+    /// @param t1 A value of the T1 timer.
+    /// @param t2 A value of the T2 timer.
+    /// @param subnet_id A Subnet identifier.
+    /// @param prefixlen An address prefix length.
+    Lease6(Type type, const isc::asiolink::IOAddress& addr, DuidPtr duid,
+           uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
+           uint32_t t2, SubnetID subnet_id, uint8_t prefixlen = 128);
+
+    /// @brief Constructor, including FQDN data.
+    ///
+    /// @param type Lease type.
+    /// @param addr Assigned address.
+    /// @param duid A pointer to an object representing DUID.
+    /// @param iaid IAID.
+    /// @param preferred Preferred lifetime.
+    /// @param valid Valid lifetime.
+    /// @param t1 A value of the T1 timer.
+    /// @param t2 A value of the T2 timer.
+    /// @param subnet_id A Subnet identifier.
+    /// @param fqdn_fwd If true, forward DNS update is performed for a lease.
+    /// @param fqdn_rev If true, reverse DNS update is performed for a lease.
+    /// @param hostname FQDN of the client which gets the lease.
+    /// @param prefixlen An address prefix length.
+    Lease6(Type type, const isc::asiolink::IOAddress& addr, DuidPtr duid,
+           uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
+           uint32_t t2, SubnetID subnet_id, const bool fqdn_fwd,
+           const bool fqdn_rev, const std::string& hostname,
+           uint8_t prefixlen = 0);
+
+    /// @brief Constructor
+    ///
+    /// Initialize fields that don't have a default constructor.
+    Lease6() : Lease(isc::asiolink::IOAddress("::"), 0, 0, 0, 0, 0,
+                     false, false, ""),
+        type_(TYPE_NA) {
+    }
+
+    /// @brief Checks if two lease objects encapsulate the lease for the same
+    /// client.
+    ///
+    /// This function compares address, type, prefix length, IAID and DUID
+    /// parameters between two @c Lease6 objects. If these parameters match
+    /// it is an indication that both objects describe the lease for the same
+    /// client but apparently one is a result of renewal of the other. The
+    /// special case of the matching lease is the one that is equal to another.
+    ///
+    /// @param other A lease to compare to.
+    ///
+    /// @return true if selected parameters of the two leases match.
+    bool matches(const Lease6& other) const;
+
+    /// @brief Compare two leases for equality
+    ///
+    /// @param other lease6 object with which to compare
+    bool operator==(const Lease6& other) const;
+
+    /// @brief Compare two leases for inequality
+    ///
+    /// @param other lease6 object with which to compare
+    bool operator!=(const Lease6& other) const {
+        return (!operator==(other));
+    }
+
+    /// @brief Convert Lease to Printable Form
+    ///
+    /// @return String form of the lease
+    virtual std::string toText() const;
+};
+
+/// @brief Pointer to a Lease6 structure.
+typedef boost::shared_ptr<Lease6> Lease6Ptr;
+
+/// @brief Pointer to a const Lease6 structure.
+typedef boost::shared_ptr<const Lease6> ConstLease6Ptr;
+
+/// @brief A collection of IPv6 leases.
+typedef std::vector<Lease6Ptr> Lease6Collection;
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // LEASE_H
diff --git a/src/lib/dhcpsrv/lease_mgr.cc b/src/lib/dhcpsrv/lease_mgr.cc
index f226afe..e4fa07f 100644
--- a/src/lib/dhcpsrv/lease_mgr.cc
+++ b/src/lib/dhcpsrv/lease_mgr.cc
@@ -32,79 +32,6 @@ using namespace std;
 namespace isc {
 namespace dhcp {
 
-Lease::Lease(const isc::asiolink::IOAddress& addr, uint32_t t1, uint32_t t2,
-             uint32_t valid_lft, SubnetID subnet_id, time_t cltt)
-    :addr_(addr), t1_(t1), t2_(t2), valid_lft_(valid_lft), cltt_(cltt),
-     subnet_id_(subnet_id), fixed_(false), fqdn_fwd_(false), fqdn_rev_(false) {
-}
-
-Lease4::Lease4(const Lease4& other)
-    : Lease(other.addr_, other.t1_, other.t2_, other.valid_lft_,
-            other.subnet_id_, other.cltt_), ext_(other.ext_),
-      hwaddr_(other.hwaddr_) {
-
-    fixed_ = other.fixed_;
-    fqdn_fwd_ = other.fqdn_fwd_;
-    fqdn_rev_ = other.fqdn_rev_;
-    hostname_ = other.hostname_;
-    comments_ = other.comments_;
-
-    if (other.client_id_) {
-        client_id_.reset(new ClientId(other.client_id_->getClientId()));
-
-    } else {
-        client_id_.reset();
-
-    }
-}
-
-Lease4&
-Lease4::operator=(const Lease4& other) {
-    if (this != &other) {
-        addr_ = other.addr_;
-        t1_ = other.t1_;
-        t2_ = other.t2_;
-        valid_lft_ = other.valid_lft_;
-        cltt_ = other.cltt_;
-        subnet_id_ = other.subnet_id_;
-        fixed_ = other.fixed_;
-        hostname_ = other.hostname_;
-        fqdn_fwd_ = other.fqdn_fwd_;
-        fqdn_rev_ = other.fqdn_rev_;
-        comments_ = other.comments_;
-        ext_ = other.ext_;
-        hwaddr_ = other.hwaddr_;
-
-        if (other.client_id_) {
-            client_id_.reset(new ClientId(other.client_id_->getClientId()));
-        } else {
-            client_id_.reset();
-        }
-    }
-    return (*this);
-}
-
-Lease6::Lease6(LeaseType type, const isc::asiolink::IOAddress& addr,
-               DuidPtr duid, uint32_t iaid, uint32_t preferred, uint32_t valid,
-               uint32_t t1, uint32_t t2, SubnetID subnet_id, uint8_t prefixlen)
-    : Lease(addr, t1, t2, valid, subnet_id, 0/*cltt*/),
-      type_(type), prefixlen_(prefixlen), iaid_(iaid), duid_(duid),
-      preferred_lft_(preferred) {
-    if (!duid) {
-        isc_throw(InvalidOperation, "DUID must be specified for a lease");
-    }
-
-    cltt_ = time(NULL);
-}
-
-bool Lease::expired() const {
-
-    // Let's use int64 to avoid problems with negative/large uint32 values
-    int64_t expire_time = cltt_ + valid_lft_;
-    return (expire_time < time(NULL));
-}
-
-
 std::string LeaseMgr::getParameter(const std::string& name) const {
     ParameterMap::const_iterator param = parameters_.find(name);
     if (param == parameters_.end()) {
@@ -113,107 +40,21 @@ std::string LeaseMgr::getParameter(const std::string& name) const {
     return (param->second);
 }
 
-std::string
-Lease6::toText() const {
-    ostringstream stream;
+Lease6Ptr
+LeaseMgr::getLease6(Lease::Type type, const DUID& duid,
+                    uint32_t iaid, SubnetID subnet_id) const {
+    Lease6Collection col = getLeases6(type, duid, iaid, subnet_id);
 
-    stream << "Type:          " << static_cast<int>(type_) << " (";
-    switch (type_) {
-        case Lease6::LEASE_IA_NA:
-            stream << "IA_NA)\n";
-            break;
-        case Lease6::LEASE_IA_TA:
-            stream << "IA_TA)\n";
-            break;
-        case Lease6::LEASE_IA_PD:
-            stream << "IA_PD)\n";
-            break;
-        default:
-            stream << "unknown)\n";
+    if (col.size() > 1) {
+        isc_throw(MultipleRecords, "More than one lease found for type "
+                  << static_cast<int>(type) << ", duid "
+                  << duid.toText() << ", iaid " << iaid
+                  << " and subnet-id " << subnet_id);
     }
-    stream << "Address:       " << addr_.toText() << "\n"
-           << "Prefix length: " << static_cast<int>(prefixlen_) << "\n"
-           << "IAID:          " << iaid_ << "\n"
-           << "Pref life:     " << preferred_lft_ << "\n"
-           << "Valid life:    " << valid_lft_ << "\n"
-           << "Cltt:          " << cltt_ << "\n"
-           << "Subnet ID:     " << subnet_id_ << "\n";
-
-    return (stream.str());
-}
-
-std::string
-Lease4::toText() const {
-    ostringstream stream;
-
-    stream << "Address:       " << addr_.toText() << "\n"
-           << "Valid life:    " << valid_lft_ << "\n"
-           << "T1:            " << t1_ << "\n"
-           << "T2:            " << t2_ << "\n"
-           << "Cltt:          " << cltt_ << "\n"
-           << "Subnet ID:     " << subnet_id_ << "\n";
-
-    return (stream.str());
-}
-
-bool
-Lease4::matches(const Lease4& other) const {
-    if ((client_id_ && !other.client_id_) ||
-        (!client_id_ && other.client_id_)) {
-        // One lease has client-id, but the other doesn't
-        return false;
+    if (col.empty()) {
+        return (Lease6Ptr());
     }
-
-    if (client_id_ && other.client_id_ &&
-        *client_id_ != *other.client_id_) {
-        // Different client-ids
-        return false;
-    }
-
-    return (addr_ == other.addr_ &&
-            ext_ == other.ext_ &&
-            hwaddr_ == other.hwaddr_);
-
-}
-
-bool
-Lease4::operator==(const Lease4& other) const {
-    return (matches(other) &&
-            subnet_id_ == other.subnet_id_ &&
-            t1_ == other.t1_ &&
-            t2_ == other.t2_ &&
-            valid_lft_ == other.valid_lft_ &&
-            cltt_ == other.cltt_ &&
-            fixed_ == other.fixed_ &&
-            hostname_ == other.hostname_ &&
-            fqdn_fwd_ == other.fqdn_fwd_ &&
-            fqdn_rev_ == other.fqdn_rev_ &&
-            comments_ == other.comments_);
-}
-
-bool
-Lease6::matches(const Lease6& other) const {
-    return (addr_ == other.addr_ &&
-            type_ == other.type_ &&
-            prefixlen_ == other.prefixlen_ &&
-            iaid_ == other.iaid_ &&
-            *duid_ == *other.duid_);
-}
-
-bool
-Lease6::operator==(const Lease6& other) const {
-    return (matches(other) &&
-            preferred_lft_ == other.preferred_lft_ &&
-            valid_lft_ == other.valid_lft_ &&
-            t1_ == other.t1_ &&
-            t2_ == other.t2_ &&
-            cltt_ == other.cltt_ &&
-            subnet_id_ == other.subnet_id_ &&
-            fixed_ == other.fixed_ &&
-            hostname_ == other.hostname_ &&
-            fqdn_fwd_ == other.fqdn_fwd_ &&
-            fqdn_rev_ == other.fqdn_rev_ &&
-            comments_ == other.comments_);
+    return (*col.begin());
 }
 
 } // namespace isc::dhcp
diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h
index d85651a..a4579b8 100644
--- a/src/lib/dhcpsrv/lease_mgr.h
+++ b/src/lib/dhcpsrv/lease_mgr.h
@@ -19,6 +19,7 @@
 #include <dhcp/duid.h>
 #include <dhcp/option.h>
 #include <dhcp/hwaddr.h>
+#include <dhcpsrv/lease.h>
 #include <dhcpsrv/subnet.h>
 #include <exceptions/exceptions.h>
 
@@ -108,304 +109,6 @@ public:
         isc::Exception(file, line, what) {}
 };
 
-/// @brief a common structure for IPv4 and IPv6 leases
-///
-/// This structure holds all information that is common between IPv4 and IPv6
-/// leases.
-struct Lease {
-
-    /// @brief Constructor
-    ///
-    /// @param addr IP address
-    /// @param t1 renewal time
-    /// @param t2 rebinding time
-    /// @param valid_lft Lifetime of the lease
-    /// @param subnet_id Subnet identification
-    /// @param cltt Client last transmission time
-    Lease(const isc::asiolink::IOAddress& addr, uint32_t t1, uint32_t t2,
-          uint32_t valid_lft, SubnetID subnet_id, time_t cltt);
-
-    /// @brief Destructor
-    virtual ~Lease() {}
-
-    /// @brief IPv4 ot IPv6 address
-    ///
-    /// IPv4, IPv6 address or, in the case of a prefix delegation, the prefix.
-    isc::asiolink::IOAddress addr_;
-
-    /// @brief Renewal timer
-    ///
-    /// Specifies renewal time. Although technically it is a property of the
-    /// IA container and not the address itself, since our data model does not
-    /// define a separate IA entity, we are keeping it in the lease. In the
-    /// case of multiple addresses/prefixes for the same IA, each must have
-    /// consistent T1 and T2 values. This is specified in seconds since cltt.
-    uint32_t t1_;
-
-    /// @brief Rebinding timer
-    ///
-    /// Specifies rebinding time. Although technically it is a property of the
-    /// IA container and not the address itself, since our data model does not
-    /// define a separate IA entity, we are keeping it in the lease. In the
-    /// case of multiple addresses/prefixes for the same IA, each must have
-    /// consistent T1 and T2 values. This is specified in seconds since cltt.
-    uint32_t t2_;
-
-    /// @brief Valid lifetime
-    ///
-    /// Expressed as number of seconds since cltt.
-    uint32_t valid_lft_;
-
-    /// @brief Client last transmission time
-    ///
-    /// Specifies a timestamp giving the time when the last transmission from a
-    /// client was received.
-    time_t cltt_;
-
-    /// @brief Subnet identifier
-    ///
-    /// Specifies the identification of the subnet to which the lease belongs.
-    SubnetID subnet_id_;
-
-    /// @brief Fixed lease?
-    ///
-    /// Fixed leases are kept after they are released/expired.
-    bool fixed_;
-
-    /// @brief Client hostname
-    ///
-    /// This field may be empty
-    std::string hostname_;
-
-    /// @brief Forward zone updated?
-    ///
-    /// Set true if the DNS AAAA record for this lease has been updated.
-    bool fqdn_fwd_;
-
-    /// @brief Reverse zone updated?
-    ///
-    /// Set true if the DNS PTR record for this lease has been updated.
-    bool fqdn_rev_;
-
-    /// @brief Lease comments
-    ///
-    /// Currently not used. It may be used for keeping comments made by the
-    /// system administrator.
-    std::string comments_;
-
-    /// @brief Convert Lease to Printable Form
-    ///
-    /// @return String form of the lease
-    virtual std::string toText() const = 0;
-
-    /// @brief returns true if the lease is expired
-    /// @return true if the lease is expired
-    bool expired() const;
-
-};
-
-/// @brief Structure that holds a lease for IPv4 address
-///
-/// For performance reasons it is a simple structure, not a class. If we chose
-/// make it a class, all fields would have to made private and getters/setters
-/// would be required. As this is a critical part of the code that will be used
-/// extensively, direct access is warranted.
-struct Lease4 : public Lease {
-
-    /// @brief Address extension
-    ///
-    /// It is envisaged that in some cases IPv4 address will be accompanied
-    /// with some additional data. One example of such use are Address + Port
-    /// solutions (or Port-restricted Addresses), where several clients may get
-    /// the same address, but different port ranges. This feature is not
-    /// expected to be widely used.  Under normal circumstances, the value
-    /// should be 0.
-    uint32_t ext_;
-
-    /// @brief Hardware address
-    std::vector<uint8_t> hwaddr_;
-
-    /// @brief Client identifier
-    ///
-    /// @todo Should this be a pointer to a client ID or the ID itself?
-    ///       Compare with the DUID in the Lease6 structure.
-    ClientIdPtr client_id_;
-
-    /// @brief Constructor
-    ///
-    /// @param addr IPv4 address.
-    /// @param hwaddr Hardware address buffer
-    /// @param hwaddr_len Length of hardware address buffer
-    /// @param clientid Client identification buffer
-    /// @param clientid_len Length of client identification buffer
-    /// @param valid_lft Lifetime of the lease
-    /// @param t1 renewal time
-    /// @param t2 rebinding time
-    /// @param cltt Client last transmission time
-    /// @param subnet_id Subnet identification
-    Lease4(const isc::asiolink::IOAddress& addr, const uint8_t* hwaddr, size_t hwaddr_len,
-           const uint8_t* clientid, size_t clientid_len, uint32_t valid_lft,
-           uint32_t t1, uint32_t t2, time_t cltt, uint32_t subnet_id)
-        : Lease(addr, t1, t2, valid_lft, subnet_id, cltt),
-        ext_(0), hwaddr_(hwaddr, hwaddr + hwaddr_len) {
-        if (clientid_len) {
-            client_id_.reset(new ClientId(clientid, clientid_len));
-        }
-    }
-
-    /// @brief Default constructor
-    ///
-    /// Initialize fields that don't have a default constructor.
-    Lease4() : Lease(0, 0, 0, 0, 0, 0) {
-    }
-
-    /// @brief Copy constructor
-    ///
-    /// @param other the @c Lease4 object to be copied.
-    Lease4(const Lease4& other);
-
-    /// @brief Check if two objects encapsulate the lease for the same
-    /// client.
-    ///
-    /// Checks if two @c Lease4 objects have the same address, client id,
-    /// HW address and ext_ value.  If these parameters match it is an
-    /// indication that both objects describe the lease for the same
-    /// client but apparently one is a result of renewal of the other. The
-    /// special case of the matching lease is the one that is equal to another.
-    ///
-    /// @param other A lease to compare with.
-    ///
-    /// @return true if the selected parameters of the two leases match.
-    bool matches(const Lease4& other) const;
-
-    /// @brief Assignment operator.
-    ///
-    /// @param other the @c Lease4 object to be assigned.
-    Lease4& operator=(const Lease4& other);
-
-    /// @brief Compare two leases for equality
-    ///
-    /// @param other lease6 object with which to compare
-    bool operator==(const Lease4& other) const;
-
-    /// @brief Compare two leases for inequality
-    ///
-    /// @param other lease6 object with which to compare
-    bool operator!=(const Lease4& other) const {
-        return (!operator==(other));
-    }
-
-    /// @brief Convert lease to printable form
-    ///
-    /// @return Textual represenation of lease data
-    virtual std::string toText() const;
-
-    /// @todo: Add DHCPv4 failover related fields here
-};
-
-/// @brief Pointer to a Lease4 structure.
-typedef boost::shared_ptr<Lease4> Lease4Ptr;
-
-/// @brief A collection of IPv4 leases.
-typedef std::vector<Lease4Ptr> Lease4Collection;
-
-
-
-/// @brief Structure that holds a lease for IPv6 address and/or prefix
-///
-/// For performance reasons it is a simple structure, not a class. If we chose
-/// make it a class, all fields would have to made private and getters/setters
-/// would be required. As this is a critical part of the code that will be used
-/// extensively, direct access is warranted.
-struct Lease6 : public Lease {
-
-    /// @brief Type of lease contents
-    typedef enum {
-        LEASE_IA_NA, /// the lease contains non-temporary IPv6 address
-        LEASE_IA_TA, /// the lease contains temporary IPv6 address
-        LEASE_IA_PD  /// the lease contains IPv6 prefix (for prefix delegation)
-    } LeaseType;
-
-    /// @brief Lease type
-    ///
-    /// One of normal address, temporary address, or prefix.
-    LeaseType type_;
-
-    /// @brief IPv6 prefix length
-    ///
-    /// This is used only for prefix delegations and is ignored otherwise.
-    uint8_t prefixlen_;
-
-    /// @brief Identity Association Identifier (IAID)
-    ///
-    /// DHCPv6 stores all addresses and prefixes in IA containers (IA_NA,
-    /// IA_TA, IA_PD). All containers may appear more than once in a message.
-    /// To differentiate between them, the IAID field is present
-    uint32_t iaid_;
-
-    /// @brief Client identifier
-    DuidPtr duid_;
-
-    /// @brief preferred lifetime
-    ///
-    /// This parameter specifies the preferred lifetime since the lease was
-    /// assigned or renewed (cltt), expressed in seconds.
-    uint32_t preferred_lft_;
-
-    /// @todo: Add DHCPv6 failover related fields here
-
-    /// @brief Constructor
-    Lease6(LeaseType type, const isc::asiolink::IOAddress& addr, DuidPtr duid,
-           uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
-           uint32_t t2, SubnetID subnet_id, uint8_t prefixlen_ = 0);
-
-    /// @brief Constructor
-    ///
-    /// Initialize fields that don't have a default constructor.
-    Lease6() : Lease(isc::asiolink::IOAddress("::"), 0, 0, 0, 0, 0),
-        type_(LEASE_IA_NA) {
-    }
-
-    /// @brief Checks if two lease objects encapsulate the lease for the same
-    /// client.
-    ///
-    /// This function compares address, type, prefix length, IAID and DUID
-    /// parameters between two @c Lease6 objects. If these parameters match
-    /// it is an indication that both objects describe the lease for the same
-    /// client but apparently one is a result of renewal of the other. The
-    /// special case of the matching lease is the one that is equal to another.
-    ///
-    /// @param other A lease to compare to.
-    ///
-    /// @return true if selected parameters of the two leases match.
-    bool matches(const Lease6& other) const;
-
-    /// @brief Compare two leases for equality
-    ///
-    /// @param other lease6 object with which to compare
-    bool operator==(const Lease6& other) const;
-
-    /// @brief Compare two leases for inequality
-    ///
-    /// @param other lease6 object with which to compare
-    bool operator!=(const Lease6& other) const {
-        return (!operator==(other));
-    }
-
-    /// @brief Convert Lease to Printable Form
-    ///
-    /// @return String form of the lease
-    virtual std::string toText() const;
-};
-
-/// @brief Pointer to a Lease6 structure.
-typedef boost::shared_ptr<Lease6> Lease6Ptr;
-
-/// @brief Pointer to a const Lease6 structure.
-typedef boost::shared_ptr<const Lease6> ConstLease6Ptr;
-
-/// @brief A collection of IPv6 leases.
-typedef std::vector<Lease6Ptr> Lease6Collection;
 
 /// @brief Abstract Lease Manager
 ///
@@ -439,7 +142,7 @@ public:
     ///
     /// @result true if the lease was added, false if not (because a lease
     ///         with the same address was already there).
-    virtual bool addLease(const Lease4Ptr& lease) = 0;
+    virtual bool addLease(const isc::dhcp::Lease4Ptr& lease) = 0;
 
     /// @brief Adds an IPv6 lease.
     ///
@@ -518,10 +221,12 @@ public:
     /// The assumption here is that there will not be site or link-local
     /// addresses used, so there is no way of having address duplication.
     ///
+    /// @param type specifies lease type: (NA, TA or PD)
     /// @param addr address of the searched lease
     ///
     /// @return smart pointer to the lease (or NULL if a lease is not found)
-    virtual Lease6Ptr getLease6(const isc::asiolink::IOAddress& addr) const = 0;
+    virtual Lease6Ptr getLease6(Lease::Type type,
+                                const isc::asiolink::IOAddress& addr) const = 0;
 
     /// @brief Returns existing IPv6 leases for a given DUID+IA combination
     ///
@@ -530,22 +235,54 @@ public:
     /// can be more than one. Thus return type is a container, not a single
     /// pointer.
     ///
+    /// @param type specifies lease type: (NA, TA or PD)
     /// @param duid client DUID
     /// @param iaid IA identifier
     ///
-    /// @return smart pointer to the lease (or NULL if a lease is not found)
-    virtual Lease6Collection getLease6(const DUID& duid,
-                                       uint32_t iaid) const = 0;
+    /// @return Lease collection (may be empty if no lease is found)
+    virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
+                                        uint32_t iaid) const = 0;
 
     /// @brief Returns existing IPv6 lease for a given DUID+IA combination
     ///
+    /// There may be more than one address, temp. address or prefix
+    /// for specified duid/iaid/subnet-id tuple.
+    ///
+    /// @param type specifies lease type: (NA, TA or PD)
     /// @param duid client DUID
     /// @param iaid IA identifier
     /// @param subnet_id subnet id of the subnet the lease belongs to
     ///
-    /// @return smart pointer to the lease (or NULL if a lease is not found)
-    virtual Lease6Ptr getLease6(const DUID& duid, uint32_t iaid,
-                                SubnetID subnet_id) const = 0;
+    /// @return Lease collection (may be empty if no lease is found)
+    virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
+                                        uint32_t iaid, SubnetID subnet_id) const = 0;
+
+
+    /// @brief returns zero or one IPv6 lease for a given duid+iaid+subnet_id
+    ///
+    /// This function is mostly intended to be used in unit-tests during the
+    /// transition from single to multi address per IA. It may also be used
+    /// in other cases where at most one lease is expected in the database.
+    ///
+    /// It is a wrapper around getLease6(), which returns a collection of
+    /// leases. That collection can be converted into a single pointer if
+    /// there are no leases (NULL pointer) or one lease (use that single lease).
+    /// If there are more leases in the collection, the function will
+    /// throw MultipleRecords exception.
+    ///
+    /// Note: This method is not virtual on purpose. It is common for all
+    /// backends.
+    ///
+    /// @param type specifies lease type: (NA, TA or PD)
+    /// @param duid client DUID
+    /// @param iaid IA identifier
+    /// @param subnet_id subnet id of the subnet the lease belongs to
+    ///
+    /// @throw MultipleRecords if there is more than one lease matching
+    ///
+    /// @return Lease pointer (or NULL if none is found)
+    Lease6Ptr getLease6(Lease::Type type, const DUID& duid,
+                        uint32_t iaid, SubnetID subnet_id) const;
 
     /// @brief Updates IPv4 lease.
     ///
diff --git a/src/lib/dhcpsrv/libdhcpsrv.dox b/src/lib/dhcpsrv/libdhcpsrv.dox
index e3d3429..d56029a 100644
--- a/src/lib/dhcpsrv/libdhcpsrv.dox
+++ b/src/lib/dhcpsrv/libdhcpsrv.dox
@@ -61,19 +61,21 @@ separate module, called allocator. Its sole purpose is to pick an address from
 a pool. Allocation engine will then check if the picked address is free and if
 it is not, then will ask allocator to pick again.
 
-At lease 3 allocators will be implemented:
-
-- Iterative - it iterates over all addresses in available pools, one
-by one. The advantages of this approach are speed (typically it only needs to
-increase last address), the guarantee to cover all addresses and predictability.
-This allocator behaves very good in case of nearing depletion. Even when pools
-are almost completely allocated, it still will be able to allocate outstanding
-leases efficiently. Predictability can also be considered a serious flaw in
-some environments, as prediction of the next address is trivial and can be
-leveraged by an attacker. Another drawback of this allocator is that it does
-not attempt to give the same address to returning clients (clients that released
-or expired their leases and are requesting a new lease will likely to get a 
-different lease). This allocator is implemented in \ref isc::dhcp::AllocEngine::IterativeAllocator.
+At least 3 allocators will be implemented:
+
+- Iterative - it iterates over all resources (addresses or prefixes) in
+available pools, one by one. The advantages of this approach are: speed
+(typically it only needs to increase address just one), the guarantee to cover
+all addresses and predictability.  This allocator behaves reasonably good in
+case of nearing depletion. Even when pools are almost completely allocated, it
+still will be able to allocate outstanding leases efficiently. Predictability
+can also be considered a serious flaw in some environments, as prediction of the
+next address is trivial and can be leveraged by an attacker. Another drawback of
+this allocator is that it does not attempt to give the same address to returning
+clients (clients that released or expired their leases and are requesting a new
+lease will likely to get a different lease). This allocator is not suitable for
+temporary addresses, which must be randomized. This allocator is implemented
+in \ref isc::dhcp::AllocEngine::IterativeAllocator.
 
 - Hashed - ISC-DHCP uses hash of the client-id or DUID to determine, which
 address is tried first. If that address is not available, the result is hashed
@@ -81,13 +83,13 @@ again. That procedure is repeated until available address is found or there
 are no more addresses left. The benefit of that approach is that it provides
 a relative lease stability, so returning old clients are likely to get the same
 address again. The drawbacks are increased computation cost, as each iteration
-requires use of a hashing function. That is especially difficult when the 
+requires use of a hashing function. That is especially difficult when the
 pools are almost depleted. It also may be difficult to guarantee that the
 repeated hashing will iterate over all available addresses in all pools. Flawed
 hash algorithm can go into cycles that iterate over only part of the addresses.
 It is difficult to detect such issues as only some initial seed (client-id
 or DUID) values may trigger short cycles. This allocator is currently not
-implemented.
+implemented. This will be the only allocator allowed for temporary addresses.
 
 - Random - Another possible approach to address selection is randomization. This
 allocator can pick an address randomly from the configured pool. The benefit
@@ -97,4 +99,26 @@ returning clients are almost guaranteed to get a different address. Another
 drawback is that with almost depleted pools it is increasingly difficult to
 "guess" an address that is free. This allocator is currently not implemented.
 
+ at subsection allocEngineTypes Different lease types support
+
+Allocation Engine has been extended to support different types of leases. Four
+types are supported: TYPE_V4 (IPv4 addresses), TYPE_NA (normal IPv6 addresses),
+TYPE_TA (temporary IPv6 addresses) and TYPE_PD (delegated prefixes). Support for
+TYPE_TA is partial. Some routines are able to handle it, while other are
+not. The major missing piece is the RandomAllocator, so there is no way to randomly
+generate an address. This defeats the purpose of using temporary addresses for now.
+
+ at subsection allocEnginePD Prefix Delegation support in AllocEngine
+
+The Allocation Engine supports allocation of the IPv6 addresses and prefixes.
+For a prefix pool, the iterative allocator "walks over"
+every available pool. It is similar to how it iterates over address pool,
+but instead of increasing address by just one, it walks over the whole delegated
+prefix length in one step. This is implemented in
+isc::dhcp::AllocEngine::IterativeAllocator::increasePrefix(). Functionally the
+increaseAddress(addr) call is equivalent to increasePrefix(addr, 128)
+(increasing by a /128 prefix, i.e. a single address).  However, both methods are
+kept, because increaseAddress() is faster and this is a routine that may be
+called many hundred thousands times per second.
+
 */
diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.cc b/src/lib/dhcpsrv/memfile_lease_mgr.cc
index 804eef8..4f73278 100644
--- a/src/lib/dhcpsrv/memfile_lease_mgr.cc
+++ b/src/lib/dhcpsrv/memfile_lease_mgr.cc
@@ -28,7 +28,8 @@ Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters)
 Memfile_LeaseMgr::~Memfile_LeaseMgr() {
 }
 
-bool Memfile_LeaseMgr::addLease(const Lease4Ptr& lease) {
+bool
+Memfile_LeaseMgr::addLease(const Lease4Ptr& lease) {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_ADD_ADDR4).arg(lease->addr_.toText());
 
@@ -40,11 +41,12 @@ bool Memfile_LeaseMgr::addLease(const Lease4Ptr& lease) {
     return (true);
 }
 
-bool Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) {
+bool
+Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_ADD_ADDR6).arg(lease->addr_.toText());
 
-    if (getLease6(lease->addr_)) {
+    if (getLease6(lease->type_, lease->addr_)) {
         // there is a lease with specified address already
         return (false);
     }
@@ -52,7 +54,8 @@ bool Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) {
     return (true);
 }
 
-Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
+Lease4Ptr
+Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_GET_ADDR4).arg(addr.toText());
 
@@ -66,15 +69,27 @@ Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) cons
     }
 }
 
-Lease4Collection Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr) const {
+Lease4Collection
+Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_GET_HWADDR).arg(hwaddr.toText());
+    typedef Lease4Storage::nth_index<0>::type SearchIndex;
+    Lease4Collection collection;
+    const SearchIndex& idx = storage4_.get<0>();
+    for(SearchIndex::const_iterator lease = idx.begin();
+        lease != idx.end(); ++lease) {
+
+        // Every Lease4 has a hardware address, so we can compare it
+        if((* lease)->hwaddr_ == hwaddr.hwaddr_) {
+            collection.push_back((* lease));
+        }
+    }
 
-    isc_throw(NotImplemented, "getLease4(HWaddr x) method not implemented yet");
+    return (collection);
 }
 
-Lease4Ptr Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr,
-                                      SubnetID subnet_id) const {
+Lease4Ptr
+Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_GET_SUBID_HWADDR).arg(subnet_id)
         .arg(hwaddr.toText());
@@ -90,21 +105,65 @@ Lease4Ptr Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr,
         idx.find(boost::make_tuple(hwaddr.hwaddr_, subnet_id));
     // Lease was not found. Return empty pointer to the caller.
     if (lease == idx.end()) {
-        return Lease4Ptr();
+        return (Lease4Ptr());
     }
 
     // Lease was found. Return it to the caller.
     return (Lease4Ptr(new Lease4(**lease)));
 }
 
-Lease4Collection Memfile_LeaseMgr::getLease4(const ClientId& clientid) const {
+Lease4Collection
+Memfile_LeaseMgr::getLease4(const ClientId& clientid) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_GET_CLIENTID).arg(clientid.toText());
-    isc_throw(NotImplemented, "getLease4(ClientId) not implemented");
+    typedef Memfile_LeaseMgr::Lease4Storage::nth_index<0>::type SearchIndex;
+    Lease4Collection collection;
+    const SearchIndex& idx = storage4_.get<0>();
+    for(SearchIndex::const_iterator lease = idx.begin();
+        lease != idx.end(); ++ lease) {
+
+        // client-id is not mandatory in DHCPv4. There can be a lease that does
+        // not have a client-id. Dereferencing null pointer would be a bad thing
+        if((*lease)->client_id_ && *(*lease)->client_id_ == clientid) {
+            collection.push_back((* lease));
+        }
+    }
+
+    return (collection);
 }
 
-Lease4Ptr Memfile_LeaseMgr::getLease4(const ClientId& client_id,
-                                      SubnetID subnet_id) const {
+Lease4Ptr
+Memfile_LeaseMgr::getLease4(const ClientId& client_id,
+                            const HWAddr& hwaddr,
+                            SubnetID subnet_id) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_MEMFILE_GET_CLIENTID_HWADDR_SUBID).arg(client_id.toText())
+                                                        .arg(hwaddr.toText())
+                                                        .arg(subnet_id);
+
+    // We are going to use index #3 of the multi index container.
+    // We define SearchIndex locally in this function because
+    // currently only this function uses this index.
+    typedef Lease4Storage::nth_index<3>::type SearchIndex;
+    // Get the index.
+    const SearchIndex& idx = storage4_.get<3>();
+    // Try to get the lease using client id, hardware address and subnet id.
+    SearchIndex::const_iterator lease =
+        idx.find(boost::make_tuple(client_id.getClientId(), hwaddr.hwaddr_,
+                                   subnet_id));
+
+    if (lease == idx.end()) {
+        // Lease was not found. Return empty pointer to the caller.
+        return (Lease4Ptr());
+    }
+
+    // Lease was found. Return it to the caller.
+    return (*lease);
+}
+
+Lease4Ptr
+Memfile_LeaseMgr::getLease4(const ClientId& client_id,
+                            SubnetID subnet_id) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_GET_SUBID_CLIENTID).arg(subnet_id)
               .arg(client_id.toText());
@@ -120,14 +179,15 @@ Lease4Ptr Memfile_LeaseMgr::getLease4(const ClientId& client_id,
         idx.find(boost::make_tuple(client_id.getClientId(), subnet_id));
     // Lease was not found. Return empty pointer to the caller.
     if (lease == idx.end()) {
-        return Lease4Ptr();
+        return (Lease4Ptr());
     }
     // Lease was found. Return it to the caller.
     return (Lease4Ptr(new Lease4(**lease)));
 }
 
 Lease6Ptr
-Memfile_LeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const {
+Memfile_LeaseMgr::getLease6(Lease::Type /* not used yet */,
+                            const isc::asiolink::IOAddress& addr) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_GET_ADDR6).arg(addr.toText());
 
@@ -139,16 +199,21 @@ Memfile_LeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const {
     }
 }
 
-Lease6Collection Memfile_LeaseMgr::getLease6(const DUID& duid,
-                                             uint32_t iaid) const {
+Lease6Collection
+Memfile_LeaseMgr::getLeases6(Lease::Type /* not used yet */,
+                            const DUID& duid, uint32_t iaid) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_GET_IAID_DUID).arg(iaid).arg(duid.toText());
 
+    /// @todo Not implemented.
+
     return (Lease6Collection());
 }
 
-Lease6Ptr Memfile_LeaseMgr::getLease6(const DUID& duid, uint32_t iaid,
-                                      SubnetID subnet_id) const {
+Lease6Collection
+Memfile_LeaseMgr::getLeases6(Lease::Type /* not used yet */,
+                             const DUID& duid, uint32_t iaid,
+                             SubnetID subnet_id) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_GET_IAID_SUBID_DUID)
               .arg(iaid).arg(subnet_id).arg(duid.toText());
@@ -164,13 +229,18 @@ Lease6Ptr Memfile_LeaseMgr::getLease6(const DUID& duid, uint32_t iaid,
         idx.find(boost::make_tuple(duid.getDuid(), iaid, subnet_id));
     // Lease was not found. Return empty pointer.
     if (lease == idx.end()) {
-        return (Lease6Ptr());
+        return (Lease6Collection());
     }
+
     // Lease was found, return it to the caller.
-    return (Lease6Ptr(new Lease6(**lease)));
+    /// @todo: allow multiple leases for a single duid+iaid+subnet_id tuple
+    Lease6Collection collection;
+    collection.push_back(Lease6Ptr(new Lease6(**lease)));
+    return (collection);
 }
 
-void Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) {
+void
+Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_UPDATE_ADDR4).arg(lease->addr_.toText());
 
@@ -182,7 +252,8 @@ void Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) {
     **lease_it = *lease;
 }
 
-void Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) {
+void
+Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_UPDATE_ADDR6).arg(lease->addr_.toText());
 
@@ -194,7 +265,8 @@ void Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) {
     **lease_it = *lease;
 }
 
-bool Memfile_LeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
+bool
+Memfile_LeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_DELETE_ADDR).arg(addr.toText());
     if (addr.isV4()) {
@@ -221,7 +293,8 @@ bool Memfile_LeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
     }
 }
 
-std::string Memfile_LeaseMgr::getDescription() const {
+std::string
+Memfile_LeaseMgr::getDescription() const {
     return (std::string("This is a dummy memfile backend implementation.\n"
                         "It does not offer any useful lease management and its only\n"
                         "purpose is to test abstract lease manager API."));
diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.h b/src/lib/dhcpsrv/memfile_lease_mgr.h
index 2f75a98..aa81f00 100644
--- a/src/lib/dhcpsrv/memfile_lease_mgr.h
+++ b/src/lib/dhcpsrv/memfile_lease_mgr.h
@@ -54,7 +54,6 @@ public:
 
     /// @brief Adds an IPv4 lease.
     ///
-    /// @todo Not implemented yet
     /// @param lease lease to be added
     virtual bool addLease(const Lease4Ptr& lease);
 
@@ -75,8 +74,6 @@ public:
 
     /// @brief Returns existing IPv4 leases for specified hardware address.
     ///
-    /// @todo Not implemented yet
-    ///
     /// Although in the usual case there will be only one lease, for mobile
     /// clients or clients with multiple static/fixed/reserved leases there
     /// can be more than one. Thus return type is a container, not a single
@@ -105,11 +102,24 @@ public:
 
     /// @brief Returns existing IPv4 lease for specified client-id
     ///
-    /// @todo Not implemented yet
-    ///
     /// @param clientid client identifier
     virtual Lease4Collection getLease4(const ClientId& clientid) const;
 
+    /// @brief Returns IPv4 lease for specified client-id/hwaddr/subnet-id tuple
+    ///
+    /// There can be at most one lease for a given client-id/hwaddr tuple
+    /// in a single pool, so this method with either return a single lease
+    /// or NULL.
+    ///
+    /// @param clientid client identifier
+    /// @param hwaddr hardware address of the client
+    /// @param subnet_id identifier of the subnet that lease must belong to
+    ///
+    /// @return a pointer to the lease (or NULL if a lease is not found)
+    virtual Lease4Ptr getLease4(const ClientId& clientid,
+                                const HWAddr& hwaddr,
+                                SubnetID subnet_id) const;
+
     /// @brief Returns existing IPv4 lease for specified client-id
     ///
     /// This function returns a copy of the lease. The modification in the
@@ -130,33 +140,38 @@ public:
     /// This function returns a copy of the lease. The modification in the
     /// return lease does not affect the instance held in the lease storage.
     ///
+    /// @param type specifies lease type: (NA, TA or PD)
     /// @param addr An address of the searched lease.
     ///
     /// @return smart pointer to the lease (or NULL if a lease is not found)
-    virtual Lease6Ptr getLease6(const isc::asiolink::IOAddress& addr) const;
+    virtual Lease6Ptr getLease6(Lease::Type type,
+                                const isc::asiolink::IOAddress& addr) const;
 
     /// @brief Returns existing IPv6 lease for a given DUID+IA combination
     ///
     /// @todo Not implemented yet
     ///
+    /// @param type specifies lease type: (NA, TA or PD)
     /// @param duid client DUID
     /// @param iaid IA identifier
     ///
     /// @return collection of IPv6 leases
-    virtual Lease6Collection getLease6(const DUID& duid, uint32_t iaid) const;
+    virtual Lease6Collection getLeases6(Lease::Type type,
+                                        const DUID& duid, uint32_t iaid) const;
 
-    /// @brief Returns existing IPv6 lease for a given DUID+IA combination
+    /// @brief Returns existing IPv6 lease for a given DUID/IA/subnet-id tuple
     ///
     /// This function returns a copy of the lease. The modification in the
     /// return lease does not affect the instance held in the lease storage.
     ///
+    /// @param type specifies lease type: (NA, TA or PD)
     /// @param duid client DUID
     /// @param iaid IA identifier
     /// @param subnet_id identifier of the subnet the lease must belong to
     ///
-    /// @return smart pointer to the lease (or NULL if a lease is not found)
-    virtual Lease6Ptr getLease6(const DUID& duid, uint32_t iaid,
-                                SubnetID subnet_id) const;
+    /// @return lease collection (may be empty if no lease is found)
+    virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
+                                        uint32_t iaid, SubnetID subnet_id) const;
 
     /// @brief Updates IPv4 lease.
     ///
@@ -332,6 +347,35 @@ protected:
                     // The subnet id is accessed through the subnet_id_ member.
                     boost::multi_index::member<Lease, uint32_t, &Lease::subnet_id_>
                 >
+            >,
+
+            // Specification of the fourth index starts here.
+            boost::multi_index::ordered_unique<
+                // This is a composite index that uses two values to search for a
+                // lease: client id and subnet id.
+                boost::multi_index::composite_key<
+                    Lease4,
+                    // The client id value is not directly accessible through the
+                    // Lease4 object as it is wrapped with the ClientIdPtr object.
+                    // Therefore we use the KeyFromKeyExtractor class to access
+                    // client id through this cascaded structure. The client id
+                    // is used as an index value.
+                    KeyFromKeyExtractor<
+                        // Specify that the vector holding client id value can be obtained
+                        // from the ClientId object.
+                        boost::multi_index::const_mem_fun<ClientId, std::vector<uint8_t>,
+                                                          &ClientId::getClientId>,
+                        // Specify that the ClientId object (actually pointer to it) can
+                        // be accessed by the client_id_ member of Lease4 class.
+                        boost::multi_index::member<Lease4, ClientIdPtr, &Lease4::client_id_>
+                    >,
+                    // The hardware address is held in the hwaddr_ member of the
+                    // Lease4 object.
+                    boost::multi_index::member<Lease4, std::vector<uint8_t>,
+                                               &Lease4::hwaddr_>,
+                    // The subnet id is accessed through the subnet_id_ member.
+                    boost::multi_index::member<Lease, SubnetID, &Lease::subnet_id_>
+                >
             >
         >
     > Lease4Storage; // Specify the type name for this container.
diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc
index 9828085..e695584 100644
--- a/src/lib/dhcpsrv/mysql_lease_mgr.cc
+++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -104,6 +104,12 @@ const size_t ADDRESS6_TEXT_MAX_LEN = 39;
 const my_bool MLM_FALSE = 0;                ///< False value
 const my_bool MLM_TRUE = 1;                 ///< True value
 
+/// @brief Maximum length of the hostname stored in DNS.
+///
+/// This length is restricted by the length of the domain-name carried
+/// in the Client FQDN %Option (see RFC4702 and RFC4704).
+const size_t HOSTNAME_MAX_LEN = 255;
+
 ///@}
 
 /// @brief MySQL Selection Statements
@@ -123,68 +129,81 @@ TaggedStatement tagged_statements[] = {
                     "DELETE FROM lease6 WHERE address = ?"},
     {MySqlLeaseMgr::GET_LEASE4_ADDR,
                     "SELECT address, hwaddr, client_id, "
-                        "valid_lifetime, expire, subnet_id "
+                        "valid_lifetime, expire, subnet_id, "
+                        "fqdn_fwd, fqdn_rev, hostname "
                             "FROM lease4 "
                             "WHERE address = ?"},
     {MySqlLeaseMgr::GET_LEASE4_CLIENTID,
                     "SELECT address, hwaddr, client_id, "
-                        "valid_lifetime, expire, subnet_id "
+                        "valid_lifetime, expire, subnet_id, "
+                        "fqdn_fwd, fqdn_rev, hostname "
                             "FROM lease4 "
                             "WHERE client_id = ?"},
     {MySqlLeaseMgr::GET_LEASE4_CLIENTID_SUBID,
                     "SELECT address, hwaddr, client_id, "
-                        "valid_lifetime, expire, subnet_id "
+                        "valid_lifetime, expire, subnet_id, "
+                        "fqdn_fwd, fqdn_rev, hostname "
                             "FROM lease4 "
                             "WHERE client_id = ? AND subnet_id = ?"},
     {MySqlLeaseMgr::GET_LEASE4_HWADDR,
                     "SELECT address, hwaddr, client_id, "
-                        "valid_lifetime, expire, subnet_id "
+                        "valid_lifetime, expire, subnet_id, "
+                        "fqdn_fwd, fqdn_rev, hostname "
                             "FROM lease4 "
                             "WHERE hwaddr = ?"},
     {MySqlLeaseMgr::GET_LEASE4_HWADDR_SUBID,
                     "SELECT address, hwaddr, client_id, "
-                        "valid_lifetime, expire, subnet_id "
+                        "valid_lifetime, expire, subnet_id, "
+                        "fqdn_fwd, fqdn_rev, hostname "
                             "FROM lease4 "
                             "WHERE hwaddr = ? AND subnet_id = ?"},
     {MySqlLeaseMgr::GET_LEASE6_ADDR,
                     "SELECT address, duid, valid_lifetime, "
                         "expire, subnet_id, pref_lifetime, "
-                        "lease_type, iaid, prefix_len "
+                        "lease_type, iaid, prefix_len, "
+                        "fqdn_fwd, fqdn_rev, hostname "
                             "FROM lease6 "
-                            "WHERE address = ?"},
+                            "WHERE address = ? AND lease_type = ?"},
     {MySqlLeaseMgr::GET_LEASE6_DUID_IAID,
                     "SELECT address, duid, valid_lifetime, "
                         "expire, subnet_id, pref_lifetime, "
-                        "lease_type, iaid, prefix_len "
+                        "lease_type, iaid, prefix_len, "
+                        "fqdn_fwd, fqdn_rev, hostname "
                             "FROM lease6 "
-                            "WHERE duid = ? AND iaid = ?"},
+                            "WHERE duid = ? AND iaid = ? AND lease_type = ?"},
     {MySqlLeaseMgr::GET_LEASE6_DUID_IAID_SUBID,
                     "SELECT address, duid, valid_lifetime, "
                         "expire, subnet_id, pref_lifetime, "
-                        "lease_type, iaid, prefix_len "
+                        "lease_type, iaid, prefix_len, "
+                        "fqdn_fwd, fqdn_rev, hostname "
                             "FROM lease6 "
-                            "WHERE duid = ? AND iaid = ? AND subnet_id = ?"},
+                            "WHERE duid = ? AND iaid = ? AND subnet_id = ? "
+                            "AND lease_type = ?"},
     {MySqlLeaseMgr::GET_VERSION,
                     "SELECT version, minor FROM schema_version"},
     {MySqlLeaseMgr::INSERT_LEASE4,
                     "INSERT INTO lease4(address, hwaddr, client_id, "
-                        "valid_lifetime, expire, subnet_id) "
-                            "VALUES (?, ?, ?, ?, ?, ?)"},
+                        "valid_lifetime, expire, subnet_id, "
+                        "fqdn_fwd, fqdn_rev, hostname) "
+                            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
     {MySqlLeaseMgr::INSERT_LEASE6,
                     "INSERT INTO lease6(address, duid, valid_lifetime, "
                         "expire, subnet_id, pref_lifetime, "
-                        "lease_type, iaid, prefix_len) "
-                            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
+                        "lease_type, iaid, prefix_len, "
+                        "fqdn_fwd, fqdn_rev, hostname) "
+                            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
     {MySqlLeaseMgr::UPDATE_LEASE4,
                     "UPDATE lease4 SET address = ?, hwaddr = ?, "
                         "client_id = ?, valid_lifetime = ?, expire = ?, "
-                        "subnet_id = ? "
+                        "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, "
+                        "hostname = ? "
                             "WHERE address = ?"},
     {MySqlLeaseMgr::UPDATE_LEASE6,
                     "UPDATE lease6 SET address = ?, duid = ?, "
                         "valid_lifetime = ?, expire = ?, subnet_id = ?, "
                         "pref_lifetime = ?, lease_type = ?, iaid = ?, "
-                        "prefix_len = ? "
+                        "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, "
+                        "hostname = ? "
                             "WHERE address = ?"},
     // End of list sentinel
     {MySqlLeaseMgr::NUM_STATEMENTS, NULL}
@@ -275,16 +294,18 @@ public:
 
 class MySqlLease4Exchange : public MySqlLeaseExchange {
     /// @brief Set number of database columns for this lease structure
-    static const size_t LEASE_COLUMNS = 6;
+    static const size_t LEASE_COLUMNS = 9;
 
 public:
     /// @brief Constructor
     ///
     /// The initialization of the variables here is only to satisfy cppcheck -
     /// all variables are initialized/set in the methods before they are used.
-    MySqlLease4Exchange() : addr4_(0), hwaddr_length_(0), client_id_length_(0) {
+    MySqlLease4Exchange() : addr4_(0), hwaddr_length_(0), client_id_length_(0),
+                            fqdn_fwd_(false), fqdn_rev_(false), hostname_length_(0) {
         memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
         memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
+        memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
         std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
 
         // Set the column names (for error messages)
@@ -294,7 +315,10 @@ public:
         columns_[3] = "valid_lifetime";
         columns_[4] = "expire";
         columns_[5] = "subnet_id";
-        BOOST_STATIC_ASSERT(5 < LEASE_COLUMNS);
+        columns_[6] = "fqdn_fwd";
+        columns_[7] = "fqdn_rev";
+        columns_[8] = "hostname";
+        BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
     }
 
     /// @brief Create MYSQL_BIND objects for Lease4 Pointer
@@ -397,11 +421,32 @@ public:
         // bind_[5].is_null = &MLM_FALSE; // commented out for performance
                                           // reasons, see memset() above
 
+        // fqdn_fwd: boolean
+        bind_[6].buffer_type = MYSQL_TYPE_TINY;
+        bind_[6].buffer = reinterpret_cast<char*>(&lease_->fqdn_fwd_);
+        bind_[6].is_unsigned = MLM_TRUE;
+        // bind_[6].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
+
+        // fqdn_rev: boolean
+        bind_[7].buffer_type = MYSQL_TYPE_TINY;
+        bind_[7].buffer = reinterpret_cast<char*>(&lease_->fqdn_rev_);
+        bind_[7].is_unsigned = MLM_TRUE;
+        // bind_[7].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
+
+        // hostname: varchar(255)
+        bind_[8].buffer_type = MYSQL_TYPE_VARCHAR;
+        bind_[8].buffer = const_cast<char*>(lease_->hostname_.c_str());
+        bind_[8].buffer_length = lease_->hostname_.length();
+        // bind_[8].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
+
         // Add the error flags
         setErrorIndicators(bind_, error_, LEASE_COLUMNS);
 
         // .. and check that we have the numbers correct at compile time.
-        BOOST_STATIC_ASSERT(5 < LEASE_COLUMNS);
+        BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
 
         // Add the data to the vector.  Note the end element is one after the
         // end of the array.
@@ -470,11 +515,34 @@ public:
         // bind_[5].is_null = &MLM_FALSE; // commented out for performance
                                           // reasons, see memset() above
 
+        // fqdn_fwd: boolean
+        bind_[6].buffer_type = MYSQL_TYPE_TINY;
+        bind_[6].buffer = reinterpret_cast<char*>(&fqdn_fwd_);
+        bind_[6].is_unsigned = MLM_TRUE;
+        // bind_[6].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
+
+        // fqdn_rev: boolean
+        bind_[7].buffer_type = MYSQL_TYPE_TINY;
+        bind_[7].buffer = reinterpret_cast<char*>(&fqdn_rev_);
+        bind_[7].is_unsigned = MLM_TRUE;
+        // bind_[7].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
+
+        // hostname: varchar(255)
+        hostname_length_ = sizeof(hostname_buffer_);
+        bind_[8].buffer_type = MYSQL_TYPE_STRING;
+        bind_[8].buffer = reinterpret_cast<char*>(hostname_buffer_);
+        bind_[8].buffer_length = hostname_length_;
+        bind_[8].length = &hostname_length_;
+        // bind_[8].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
+
         // Add the error flags
         setErrorIndicators(bind_, error_, LEASE_COLUMNS);
 
         // .. and check that we have the numbers correct at compile time.
-        BOOST_STATIC_ASSERT(5 < LEASE_COLUMNS);
+        BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
 
         // Add the data to the vector.  Note the end element is one after the
         // end of the array.
@@ -500,10 +568,16 @@ public:
             client_id_length_ = 0;
         }
 
+        // Hostname is passed to Lease4 as a string object. We have to create
+        // it from the buffer holding hostname and the buffer length.
+        std::string hostname(hostname_buffer_,
+                             hostname_buffer_ + hostname_length_);
+
         // note that T1 and T2 are not stored
         return (Lease4Ptr(new Lease4(addr4_, hwaddr_buffer_, hwaddr_length_,
                                      client_id_buffer_, client_id_length_,
-                                     valid_lifetime_, 0, 0, cltt, subnet_id_)));
+                                     valid_lifetime_, 0, 0, cltt, subnet_id_,
+                                     fqdn_fwd_, fqdn_rev_, hostname)));
     }
 
     /// @brief Return columns in error
@@ -543,6 +617,15 @@ private:
     Lease4Ptr       lease_;             ///< Pointer to lease object
     uint32_t        subnet_id_;         ///< Subnet identification
     uint32_t        valid_lifetime_;    ///< Lease time
+
+    my_bool         fqdn_fwd_;          ///< Has forward DNS update been
+                                        ///< performed
+    my_bool         fqdn_rev_;          ///< Has reverse DNS update been
+                                        ///< performed
+    char            hostname_buffer_[HOSTNAME_MAX_LEN];
+                                        ///< Client hostname
+    unsigned long   hostname_length_;   ///< Client hostname length
+
 };
 
 
@@ -562,28 +645,34 @@ private:
 
 class MySqlLease6Exchange : public MySqlLeaseExchange {
     /// @brief Set number of database columns for this lease structure
-    static const size_t LEASE_COLUMNS = 9;
+    static const size_t LEASE_COLUMNS = 12;
 
 public:
     /// @brief Constructor
     ///
     /// The initialization of the variables here is nonly to satisfy cppcheck -
     /// all variables are initialized/set in the methods before they are used.
-    MySqlLease6Exchange() : addr6_length_(0), duid_length_(0) {
+    MySqlLease6Exchange() : addr6_length_(0), duid_length_(0),
+                            fqdn_fwd_(false), fqdn_rev_(false),
+                            hostname_length_(0) {
         memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
         memset(duid_buffer_, 0, sizeof(duid_buffer_));
+        memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
         std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
 
         // Set the column names (for error messages)
-        columns_[0] = "address";
-        columns_[1] = "duid";
-        columns_[2] = "valid_lifetime";
-        columns_[3] = "expire";
-        columns_[4] = "subnet_id";
-        columns_[5] = "pref_lifetime";
-        columns_[6] = "lease_type";
-        columns_[7] = "iaid";
-        columns_[8] = "prefix_len";
+        columns_[0]  = "address";
+        columns_[1]  = "duid";
+        columns_[2]  = "valid_lifetime";
+        columns_[3]  = "expire";
+        columns_[4]  = "subnet_id";
+        columns_[5]  = "pref_lifetime";
+        columns_[6]  = "lease_type";
+        columns_[7]  = "iaid";
+        columns_[8]  = "prefix_len";
+        columns_[9]  = "fqdn_fwd";
+        columns_[10] = "fqdn_rev";
+        columns_[11] = "hostname";
         BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
     }
 
@@ -707,11 +796,32 @@ public:
         // bind_[8].is_null = &MLM_FALSE; // commented out for performance
                                           // reasons, see memset() above
 
+        // fqdn_fwd: boolean
+        bind_[9].buffer_type = MYSQL_TYPE_TINY;
+        bind_[9].buffer = reinterpret_cast<char*>(&lease_->fqdn_fwd_);
+        bind_[9].is_unsigned = MLM_TRUE;
+        // bind_[7].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
+
+        // fqdn_rev: boolean
+        bind_[10].buffer_type = MYSQL_TYPE_TINY;
+        bind_[10].buffer = reinterpret_cast<char*>(&lease_->fqdn_rev_);
+        bind_[10].is_unsigned = MLM_TRUE;
+        // bind_[10].is_null = &MLM_FALSE; // commented out for performance
+                                           // reasons, see memset() above
+
+        // hostname: varchar(255)
+        bind_[11].buffer_type = MYSQL_TYPE_VARCHAR;
+        bind_[11].buffer = const_cast<char*>(lease_->hostname_.c_str());
+        bind_[11].buffer_length = lease_->hostname_.length();
+        // bind_[11].is_null = &MLM_FALSE; // commented out for performance
+                                           // reasons, see memset() above
+
         // Add the error flags
         setErrorIndicators(bind_, error_, LEASE_COLUMNS);
 
         // .. and check that we have the numbers correct at compile time.
-        BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
+        BOOST_STATIC_ASSERT(11 < LEASE_COLUMNS);
 
         // Add the data to the vector.  Note the end element is one after the
         // end of the array.
@@ -805,11 +915,34 @@ public:
         // bind_[8].is_null = &MLM_FALSE; // commented out for performance
                                           // reasons, see memset() above
 
+        // fqdn_fwd: boolean
+        bind_[9].buffer_type = MYSQL_TYPE_TINY;
+        bind_[9].buffer = reinterpret_cast<char*>(&fqdn_fwd_);
+        bind_[9].is_unsigned = MLM_TRUE;
+        // bind_[9].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
+
+        // fqdn_rev: boolean
+        bind_[10].buffer_type = MYSQL_TYPE_TINY;
+        bind_[10].buffer = reinterpret_cast<char*>(&fqdn_rev_);
+        bind_[10].is_unsigned = MLM_TRUE;
+        // bind_[10].is_null = &MLM_FALSE; // commented out for performance
+                                           // reasons, see memset() above
+
+        // hostname: varchar(255)
+        hostname_length_ = sizeof(hostname_buffer_);
+        bind_[11].buffer_type = MYSQL_TYPE_STRING;
+        bind_[11].buffer = reinterpret_cast<char*>(hostname_buffer_);
+        bind_[11].buffer_length = hostname_length_;
+        bind_[11].length = &hostname_length_;
+        // bind_[11].is_null = &MLM_FALSE; // commented out for performance
+                                           // reasons, see memset() above
+
         // Add the error flags
         setErrorIndicators(bind_, error_, LEASE_COLUMNS);
 
         // .. and check that we have the numbers correct at compile time.
-        BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
+        BOOST_STATIC_ASSERT(11 < LEASE_COLUMNS);
 
         // Add the data to the vector.  Note the end element is one after the
         // end of the array.
@@ -836,34 +969,41 @@ public:
 
         // Set the lease type in a variable of the appropriate data type, which
         // has been initialized with an arbitrary (but valid) value.
-        Lease6::LeaseType type = Lease6::LEASE_IA_NA;
+        Lease::Type type = Lease::TYPE_NA;
         switch (lease_type_) {
-            case Lease6::LEASE_IA_NA:
-                type = Lease6::LEASE_IA_NA;
+            case Lease::TYPE_NA:
+                type = Lease::TYPE_NA;
                 break;
 
-            case Lease6::LEASE_IA_TA:
-                type = Lease6::LEASE_IA_TA;
+            case Lease::TYPE_TA:
+                type = Lease::TYPE_TA;
                 break;
 
-            case Lease6::LEASE_IA_PD:
-                type = Lease6::LEASE_IA_PD;
+            case Lease::TYPE_PD:
+                type = Lease::TYPE_PD;
                 break;
 
             default:
                 isc_throw(BadValue, "invalid lease type returned (" <<
-                          lease_type_ << ") for lease with address " <<
-                          address << ". Only 0, 1, or 2 are allowed.");
+                          static_cast<int>(lease_type_) << ") for lease with "
+                          << "address " << address << ". Only 0, 1, or 2 are "
+                          << "allowed.");
         }
 
         // Set up DUID,
         DuidPtr duid_ptr(new DUID(duid_buffer_, duid_length_));
 
+        // Hostname is passed to Lease6 as a string object, so we have to
+        // create it from the hostname buffer and length.
+        std::string hostname(hostname_buffer_,
+                             hostname_buffer_ + hostname_length_);
+
         // Create the lease and set the cltt (after converting from the
         // expire time retrieved from the database).
         Lease6Ptr result(new Lease6(type, addr, duid_ptr, iaid_,
                                     pref_lifetime_, valid_lifetime_, 0, 0,
-                                    subnet_id_, prefixlen_));
+                                    subnet_id_, fqdn_fwd_, fqdn_rev_,
+                                    hostname, prefixlen_));
         time_t cltt = 0;
         MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
         result->cltt_ = cltt;
@@ -907,6 +1047,14 @@ private:
     uint32_t        pref_lifetime_;     ///< Preferred lifetime
     uint32_t        subnet_id_;         ///< Subnet identification
     uint32_t        valid_lifetime_;    ///< Lease time
+    my_bool         fqdn_fwd_;          ///< Has forward DNS update been
+                                        ///< performed
+    my_bool         fqdn_rev_;          ///< Has reverse DNS update been
+                                        ///< performed
+    char            hostname_buffer_[HOSTNAME_MAX_LEN];
+                                        ///< Client hostname
+    unsigned long   hostname_length_;   ///< Client hostname length
+
 };
 
 
@@ -1106,7 +1254,7 @@ MySqlLeaseMgr::openDatabase() {
                   mysql_error(mysql_));
     }
 
-    // Set SQL mode options for the connection:  SQL mode governs how what 
+    // Set SQL mode options for the connection:  SQL mode governs how what
     // constitutes insertable data for a given column, and how to handle
     // invalid data.  We want to ensure we get the strictest behavior and
     // to reject invalid data with an error.
@@ -1227,7 +1375,8 @@ MySqlLeaseMgr::addLease(const Lease4Ptr& lease) {
 bool
 MySqlLeaseMgr::addLease(const Lease6Ptr& lease) {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_MYSQL_ADD_ADDR6).arg(lease->addr_.toText());
+              DHCPSRV_MYSQL_ADD_ADDR6).arg(lease->addr_.toText())
+              .arg(lease->type_);
 
     // Create the MYSQL_BIND array for the lease
     std::vector<MYSQL_BIND> bind = exchange6_->createBindForSend(lease);
@@ -1504,12 +1653,14 @@ MySqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const {
 
 
 Lease6Ptr
-MySqlLeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const {
+MySqlLeaseMgr::getLease6(Lease::Type lease_type,
+                         const isc::asiolink::IOAddress& addr) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_MYSQL_GET_ADDR6).arg(addr.toText());
+              DHCPSRV_MYSQL_GET_ADDR6).arg(addr.toText())
+              .arg(lease_type);
 
     // Set up the WHERE clause value
-    MYSQL_BIND inbind[1];
+    MYSQL_BIND inbind[2];
     memset(inbind, 0, sizeof(inbind));
 
     std::string addr6 = addr.toText();
@@ -1522,6 +1673,11 @@ MySqlLeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const {
     inbind[0].buffer_length = addr6_length;
     inbind[0].length = &addr6_length;
 
+    // LEASE_TYPE
+    inbind[1].buffer_type = MYSQL_TYPE_TINY;
+    inbind[1].buffer = reinterpret_cast<char*>(&lease_type);
+    inbind[1].is_unsigned = MLM_TRUE;
+
     Lease6Ptr result;
     getLease(GET_LEASE6_ADDR, inbind, result);
 
@@ -1530,12 +1686,14 @@ MySqlLeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const {
 
 
 Lease6Collection
-MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid) const {
+MySqlLeaseMgr::getLeases6(Lease::Type lease_type,
+                          const DUID& duid, uint32_t iaid) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_MYSQL_GET_IAID_DUID).arg(iaid).arg(duid.toText());
+              DHCPSRV_MYSQL_GET_IAID_DUID).arg(iaid).arg(duid.toText())
+              .arg(lease_type);
 
     // Set up the WHERE clause value
-    MYSQL_BIND inbind[2];
+    MYSQL_BIND inbind[3];
     memset(inbind, 0, sizeof(inbind));
 
     // In the following statement, the DUID is being read.  However, the
@@ -1563,6 +1721,11 @@ MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid) const {
     inbind[1].buffer = reinterpret_cast<char*>(&iaid);
     inbind[1].is_unsigned = MLM_TRUE;
 
+    // LEASE_TYPE
+    inbind[2].buffer_type = MYSQL_TYPE_TINY;
+    inbind[2].buffer = reinterpret_cast<char*>(&lease_type);
+    inbind[2].is_unsigned = MLM_TRUE;
+
     // ... and get the data
     Lease6Collection result;
     getLeaseCollection(GET_LEASE6_DUID_IAID, inbind, result);
@@ -1570,16 +1733,17 @@ MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid) const {
     return (result);
 }
 
-
-Lease6Ptr
-MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid,
-                         SubnetID subnet_id) const {
+Lease6Collection
+MySqlLeaseMgr::getLeases6(Lease::Type lease_type,
+                          const DUID& duid, uint32_t iaid,
+                          SubnetID subnet_id) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MYSQL_GET_IAID_SUBID_DUID)
-              .arg(iaid).arg(subnet_id).arg(duid.toText());
+              .arg(iaid).arg(subnet_id).arg(duid.toText())
+              .arg(lease_type);
 
     // Set up the WHERE clause value
-    MYSQL_BIND inbind[3];
+    MYSQL_BIND inbind[4];
     memset(inbind, 0, sizeof(inbind));
 
     // See the earlier description of the use of "const_cast" when accessing
@@ -1602,8 +1766,14 @@ MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid,
     inbind[2].buffer = reinterpret_cast<char*>(&subnet_id);
     inbind[2].is_unsigned = MLM_TRUE;
 
-    Lease6Ptr result;
-    getLease(GET_LEASE6_DUID_IAID_SUBID, inbind, result);
+    // LEASE_TYPE
+    inbind[3].buffer_type = MYSQL_TYPE_TINY;
+    inbind[3].buffer = reinterpret_cast<char*>(&lease_type);
+    inbind[3].is_unsigned = MLM_TRUE;
+
+    // ... and get the data
+    Lease6Collection result;
+    getLeaseCollection(GET_LEASE6_DUID_IAID_SUBID, inbind, result);
 
     return (result);
 }
@@ -1670,7 +1840,8 @@ MySqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
     const StatementIndex stindex = UPDATE_LEASE6;
 
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_MYSQL_UPDATE_ADDR6).arg(lease->addr_.toText());
+              DHCPSRV_MYSQL_UPDATE_ADDR6).arg(lease->addr_.toText())
+              .arg(lease->type_);
 
     // Create the MYSQL_BIND array for the data being updated
     std::vector<MYSQL_BIND> bind = exchange6_->createBindForSend(lease);
diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.h b/src/lib/dhcpsrv/mysql_lease_mgr.h
index 6d8eb8c..6fae5e2 100644
--- a/src/lib/dhcpsrv/mysql_lease_mgr.h
+++ b/src/lib/dhcpsrv/mysql_lease_mgr.h
@@ -244,6 +244,7 @@ public:
     /// The assumption here is that there will not be site or link-local
     /// addresses used, so there is no way of having address duplication.
     ///
+    /// @param type specifies lease type: (NA, TA or PD)
     /// @param addr address of the searched lease
     ///
     /// @return smart pointer to the lease (or NULL if a lease is not found)
@@ -255,7 +256,8 @@ public:
     ///        programming error.
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual Lease6Ptr getLease6(const isc::asiolink::IOAddress& addr) const;
+    virtual Lease6Ptr getLease6(Lease::Type type,
+                                const isc::asiolink::IOAddress& addr) const;
 
     /// @brief Returns existing IPv6 leases for a given DUID+IA combination
     ///
@@ -264,6 +266,7 @@ public:
     /// can be more than one. Thus return type is a container, not a single
     /// pointer.
     ///
+    /// @param type specifies lease type: (NA, TA or PD)
     /// @param duid client DUID
     /// @param iaid IA identifier
     ///
@@ -276,16 +279,17 @@ public:
     ///        programming error.
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual Lease6Collection getLease6(const DUID& duid,
-                                       uint32_t iaid) const;
+    virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
+                                        uint32_t iaid) const;
 
     /// @brief Returns existing IPv6 lease for a given DUID+IA combination
     ///
+    /// @param type specifies lease type: (NA, TA or PD)
     /// @param duid client DUID
     /// @param iaid IA identifier
     /// @param subnet_id subnet id of the subnet the lease belongs to
     ///
-    /// @return smart pointer to the lease (or NULL if a lease is not found)
+    /// @return lease collection (may be empty if no lease is found)
     ///
     /// @throw isc::BadValue record retrieved from database had an invalid
     ///        lease type field.
@@ -294,8 +298,8 @@ public:
     ///        programming error.
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual Lease6Ptr getLease6(const DUID& duid, uint32_t iaid,
-                                SubnetID subnet_id) const;
+    virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
+                                        uint32_t iaid, SubnetID subnet_id) const;
 
     /// @brief Updates IPv4 lease.
     ///
diff --git a/src/lib/dhcpsrv/option_space_container.h b/src/lib/dhcpsrv/option_space_container.h
index ba16fbb..7d258d6 100644
--- a/src/lib/dhcpsrv/option_space_container.h
+++ b/src/lib/dhcpsrv/option_space_container.h
@@ -31,7 +31,8 @@ namespace dhcp {
 /// @tparam ContainerType of the container holding items within
 /// option space.
 /// @tparam ItemType type of the item being held by the container.
-template<typename ContainerType, typename ItemType>
+/// @tparam Selector a string (for option spaces) or uint32_t (for vendor options)
+template<typename ContainerType, typename ItemType, typename Selector>
 class OptionSpaceContainer {
 public:
 
@@ -41,8 +42,8 @@ public:
     /// @brief Adds a new item to the option_space.
     ///
     /// @param item reference to the item being added.
-    /// @param option_space name of the option space.
-    void addItem(const ItemType& item, const std::string& option_space) {
+    /// @param option_space name or vendor-id of the option space
+    void addItem(const ItemType& item, const Selector& option_space) {
         ItemsContainerPtr items = getItems(option_space);
         items->push_back(item);
         option_space_map_[option_space] = items;
@@ -54,10 +55,10 @@ public:
     /// space an empty container is created and returned. However
     /// this container is not added to the list of option spaces.
     ///
-    /// @param option_space name of the option space.
+    /// @param option_space name or vendor-id of the option space.
     ///
     /// @return pointer to the container holding items.
-    ItemsContainerPtr getItems(const std::string& option_space) const {
+    ItemsContainerPtr getItems(const Selector& option_space) const {
         const typename OptionSpaceMap::const_iterator& items =
             option_space_map_.find(option_space);
         if (items == option_space_map_.end()) {
@@ -73,8 +74,8 @@ public:
     /// @todo This function is likely to be removed once
     /// we create a structore of OptionSpaces defined
     /// through the configuration manager.
-    std::list<std::string> getOptionSpaceNames() {
-        std::list<std::string> names;
+    std::list<Selector> getOptionSpaceNames() {
+        std::list<Selector> names;
         for (typename OptionSpaceMap::const_iterator space =
                  option_space_map_.begin();
              space != option_space_map_.end(); ++space) {
@@ -90,8 +91,8 @@ public:
 
 private:
 
-    /// A map holding container (option space name is the key).
-    typedef std::map<std::string, ItemsContainerPtr> OptionSpaceMap;
+    /// A map holding container (option space name or vendor-id is the key).
+    typedef std::map<Selector, ItemsContainerPtr> OptionSpaceMap;
     OptionSpaceMap option_space_map_;
 };
 
diff --git a/src/lib/dhcpsrv/pool.cc b/src/lib/dhcpsrv/pool.cc
index 7104c61..f1ba871 100644
--- a/src/lib/dhcpsrv/pool.cc
+++ b/src/lib/dhcpsrv/pool.cc
@@ -15,24 +15,33 @@
 #include <asiolink/io_address.h>
 #include <dhcpsrv/addr_utilities.h>
 #include <dhcpsrv/pool.h>
+#include <sstream>
 
 using namespace isc::asiolink;
 
 namespace isc {
 namespace dhcp {
 
-Pool::Pool(const isc::asiolink::IOAddress& first,
+Pool::Pool(Lease::Type type, const isc::asiolink::IOAddress& first,
            const isc::asiolink::IOAddress& last)
-    :id_(getNextID()), first_(first), last_(last) {
+    :id_(getNextID()), first_(first), last_(last), type_(type) {
 }
 
 bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
     return (first_.smallerEqual(addr) && addr.smallerEqual(last_));
 }
 
+std::string
+Pool::toText() const {
+    std::stringstream tmp;
+    tmp << "type=" << Lease::typeToText(type_) << ", " << first_.toText()
+        << "-" << last_.toText();
+    return (tmp.str());
+}
+
 Pool4::Pool4(const isc::asiolink::IOAddress& first,
              const isc::asiolink::IOAddress& last)
-    :Pool(first, last) {
+:Pool(Lease::TYPE_V4, first, last) {
     // check if specified address boundaries are sane
     if (!first.isV4() || !last.isV4()) {
         isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
@@ -43,9 +52,8 @@ Pool4::Pool4(const isc::asiolink::IOAddress& first,
     }
 }
 
-Pool4::Pool4(const isc::asiolink::IOAddress& prefix,
-             uint8_t prefix_len)
-    :Pool(prefix, IOAddress("0.0.0.0")) {
+Pool4::Pool4( const isc::asiolink::IOAddress& prefix, uint8_t prefix_len)
+:Pool(Lease::TYPE_V4, prefix, IOAddress("0.0.0.0")) {
 
     // check if the prefix is sane
     if (!prefix.isV4()) {
@@ -62,15 +70,21 @@ Pool4::Pool4(const isc::asiolink::IOAddress& prefix,
 }
 
 
-Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
+Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& first,
              const isc::asiolink::IOAddress& last)
-    :Pool(first, last), type_(type) {
+    :Pool(type, first, last), prefix_len_(128) {
 
     // check if specified address boundaries are sane
     if (!first.isV6() || !last.isV6()) {
         isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
     }
 
+    if ( (type != Lease::TYPE_NA) && (type != Lease::TYPE_TA) &&
+         (type != Lease::TYPE_PD)) {
+        isc_throw(BadValue, "Invalid Pool6 type: " << static_cast<int>(type)
+                  << ", must be TYPE_IA, TYPE_TA or TYPE_PD");
+    }
+
     if (last < first) {
         isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
         // This check is a bit strict. If we decide that it is too strict,
@@ -87,24 +101,36 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
     // TYPE_PD is not supported by this constructor. first-last style
     // parameters are for IA and TA only. There is another dedicated
     // constructor for that (it uses prefix/length)
-    if ((type != TYPE_IA) && (type != TYPE_TA)) {
-        isc_throw(BadValue, "Invalid Pool6 type specified");
+    if ((type != Lease::TYPE_NA) && (type != Lease::TYPE_TA)) {
+        isc_throw(BadValue, "Invalid Pool6 type specified:"
+                  << static_cast<int>(type));
     }
 }
 
-Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
-             uint8_t prefix_len)
-    :Pool(prefix, IOAddress("::")),
-     type_(type) {
+Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& prefix,
+             uint8_t prefix_len, uint8_t delegated_len /* = 128 */)
+    :Pool(type, prefix, IOAddress("::")), prefix_len_(delegated_len) {
 
     // check if the prefix is sane
     if (!prefix.isV6()) {
         isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
     }
 
-    // check if the prefix length is sane 
+    // check if the prefix length is sane
     if (prefix_len == 0 || prefix_len > 128) {
-        isc_throw(BadValue, "Invalid prefix length");
+        isc_throw(BadValue, "Invalid prefix length: " << prefix_len);
+    }
+
+    if (prefix_len > delegated_len) {
+        isc_throw(BadValue, "Delegated length (" << static_cast<int>(delegated_len)
+                  << ") must be longer than prefix length ("
+                  << static_cast<int>(prefix_len) << ")");
+    }
+
+    if ( ( (type == Lease::TYPE_NA) || (type == Lease::TYPE_TA)) &&
+         (delegated_len != 128)) {
+        isc_throw(BadValue, "For IA or TA pools, delegated prefix length must "
+                  << " be 128.");
     }
 
     /// @todo: We should probably implement checks against weird addresses
@@ -114,5 +140,15 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
     last_ = lastAddrInPrefix(prefix, prefix_len);
 }
 
+std::string
+Pool6::toText() const {
+    std::stringstream tmp;
+    tmp << "type=" << Lease::typeToText(type_) << ", " << first_.toText()
+        << "-" << last_.toText() << ", delegated_len="
+        << static_cast<int>(prefix_len_);
+    return (tmp.str());
+}
+
+
 }; // end of isc::dhcp namespace
 }; // end of isc namespace
diff --git a/src/lib/dhcpsrv/pool.h b/src/lib/dhcpsrv/pool.h
index e0a6f3c..ae8cb58 100644
--- a/src/lib/dhcpsrv/pool.h
+++ b/src/lib/dhcpsrv/pool.h
@@ -16,8 +16,8 @@
 #define POOL_H
 
 #include <asiolink/io_address.h>
-
 #include <boost/shared_ptr.hpp>
+#include <dhcpsrv/lease.h>
 
 #include <vector>
 
@@ -31,6 +31,8 @@ namespace dhcp {
 class Pool {
 
 public:
+    /// @note:
+    /// PoolType enum was removed. Please use Lease::Type instead
 
     /// @brief returns Pool-id
     ///
@@ -58,6 +60,25 @@ public:
     /// @return true, if the address is in pool
     bool inRange(const isc::asiolink::IOAddress& addr) const;
 
+    /// @brief Returns pool type (v4, v6 non-temporary, v6 temp, v6 prefix)
+    /// @return returns pool type
+    Lease::Type getType() const {
+        return (type_);
+    }
+
+    /// @brief returns textual representation of the pool
+    ///
+    /// @return textual representation
+    virtual std::string toText() const;
+
+    /// @brief virtual destructor
+    ///
+    /// We need Pool to be a polymorphic class, so we could dynamic cast
+    /// from PoolPtr to Pool6Ptr if we need to. A class becomes polymorphic,
+    /// when there is at least one virtual method.
+    virtual ~Pool() {
+    }
+
 protected:
 
     /// @brief protected constructor
@@ -65,7 +86,12 @@ protected:
     /// This constructor is protected to prevent anyone from instantiating
     /// Pool class directly. Instances of Pool4 and Pool6 should be created
     /// instead.
-    Pool(const isc::asiolink::IOAddress& first,
+    ///
+    /// @param type type of lease that will be served from this pool
+    /// @param first first address of a range
+    /// @param last last address of a range
+    Pool(Lease::Type type,
+         const isc::asiolink::IOAddress& first,
          const isc::asiolink::IOAddress& last);
 
     /// @brief returns the next unique Pool-ID
@@ -91,6 +117,9 @@ protected:
     ///
     /// @todo: This field is currently not used.
     std::string comments_;
+
+    /// @brief defines a lease type that will be served from this pool
+    Lease::Type type_;
 };
 
 /// @brief Pool information for IPv4 addresses
@@ -117,9 +146,6 @@ public:
 /// @brief a pointer an IPv4 Pool
 typedef boost::shared_ptr<Pool4> Pool4Ptr;
 
-/// @brief a container for IPv4 Pools
-typedef std::vector<Pool4Ptr> Pool4Collection;
-
 /// @brief Pool information for IPv6 addresses and prefixes
 ///
 /// It holds information about pool6, i.e. a range of IPv6 address space that
@@ -127,55 +153,75 @@ typedef std::vector<Pool4Ptr> Pool4Collection;
 class Pool6 : public Pool {
 public:
 
-    /// @brief specifies Pool type
-    ///
-    /// Currently there are 3 pool types defined in DHCPv6:
-    /// - Non-temporary addresses (conveyed in IA_NA)
-    /// - Temporary addresses (conveyed in IA_TA)
-    /// - Delegated Prefixes (conveyed in IA_PD)
-    /// There is a new one being worked on (IA_PA, see draft-ietf-dhc-host-gen-id), but
-    /// support for it is not planned for now.
-    typedef enum {
-        TYPE_IA,
-        TYPE_TA,
-        TYPE_PD
-    }  Pool6Type;
-
     /// @brief the constructor for Pool6 "min-max" style definition
     ///
-    /// @param type type of the pool (IA, TA or PD)
+    /// @throw BadValue if PD is define (PD can be only prefix/len)
+    ///
+    /// @param type type of the pool (IA or TA)
     /// @param first the first address in a pool
     /// @param last the last address in a pool
-    Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
+    Pool6(Lease::Type type, const isc::asiolink::IOAddress& first,
           const isc::asiolink::IOAddress& last);
 
     /// @brief the constructor for Pool6 "prefix/len" style definition
     ///
+    /// For addressed, this is just a prefix/len definition. For prefixes,
+    /// there is one extra additional parameter delegated_len. It specifies
+    /// a size of delegated prefixes that the pool will be split into. For
+    /// example pool 2001:db8::/56, delegated_len=64 means that there is a
+    /// pool 2001:db8::/56. It will be split into 256 prefixes of length /64,
+    /// e.g. 2001:db8:0:1::/64, 2001:db8:0:2::/64 etc.
+    ///
+    /// Naming convention:
+    /// A smaller prefix length yields a shorter prefix which describes a larger
+    /// set of addresses. A larger length yields a longer prefix which describes
+    /// a smaller set of addresses.
+    ///
+    /// Obviously, prefix_len must define shorter or equal prefix length than
+    /// delegated_len, so prefix_len <= delegated_len. Note that it is slightly
+    /// confusing: bigger (larger) prefix actually has smaller prefix length,
+    /// e.g. /56 is a bigger prefix than /64, but has shorter (smaller) prefix
+    /// length.
+    ///
+    /// @throw BadValue if delegated_len is defined for non-PD types or
+    ///        when delegated_len < prefix_len
+    ///
     /// @param type type of the pool (IA, TA or PD)
     /// @param prefix specifies prefix of the pool
-    /// @param prefix_len specifies length of the prefix of the pool
-    Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
-          uint8_t prefix_len);
+    /// @param prefix_len specifies prefix length of the pool
+    /// @param delegated_len specifies lenght of the delegated prefixes
+    Pool6(Lease::Type type, const isc::asiolink::IOAddress& prefix,
+          uint8_t prefix_len, uint8_t delegated_len = 128);
 
     /// @brief returns pool type
     ///
     /// @return pool type
-    Pool6Type getType() const {
+    Lease::Type getType() const {
         return (type_);
     }
 
-private:
-    /// @brief defines a pool type
-    Pool6Type type_;
+    /// @brief returns delegated prefix length
+    ///
+    /// This may be useful for "prefix/len" style definition for
+    /// addresses, but is mostly useful for prefix pools.
+    /// @return prefix length (1-128)
+    uint8_t getLength() {
+        return (prefix_len_);
+    }
 
+    /// @brief returns textual representation of the pool
+    ///
+    /// @return textual representation
+    virtual std::string toText() const;
+
+private:
+    /// @brief Defines prefix length (for TYPE_PD only)
+    uint8_t prefix_len_;
 };
 
 /// @brief a pointer an IPv6 Pool
 typedef boost::shared_ptr<Pool6> Pool6Ptr;
 
-/// @brief a container for IPv6 Pools
-typedef std::vector<Pool6Ptr> Pool6Collection;
-
 /// @brief a pointer to either IPv4 or IPv6 Pool
 typedef boost::shared_ptr<Pool> PoolPtr;
 
diff --git a/src/lib/dhcpsrv/subnet.cc b/src/lib/dhcpsrv/subnet.cc
index 50a0fee..d861afe 100644
--- a/src/lib/dhcpsrv/subnet.cc
+++ b/src/lib/dhcpsrv/subnet.cc
@@ -30,15 +30,17 @@ Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
                const Triplet<uint32_t>& valid_lifetime)
     :id_(getNextID()), prefix_(prefix), prefix_len_(len), t1_(t1),
      t2_(t2), valid_(valid_lifetime),
-     last_allocated_(lastAddrInPrefix(prefix, len)) {
+     last_allocated_ia_(lastAddrInPrefix(prefix, len)),
+     last_allocated_ta_(lastAddrInPrefix(prefix, len)),
+     last_allocated_pd_(lastAddrInPrefix(prefix, len)) {
     if ((prefix.isV6() && len > 128) ||
         (prefix.isV4() && len > 32)) {
-        isc_throw(BadValue, 
+        isc_throw(BadValue,
                   "Invalid prefix length specified for subnet: " << len);
     }
 }
 
-bool 
+bool
 Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
     IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
     IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);
@@ -86,53 +88,167 @@ Subnet::getOptionDescriptor(const std::string& option_space,
     return (*range.first);
 }
 
-std::string 
+void Subnet::addVendorOption(const OptionPtr& option, bool persistent,
+                             uint32_t vendor_id){
+
+    validateOption(option);
+
+    vendor_option_spaces_.addItem(OptionDescriptor(option, persistent), vendor_id);
+}
+
+Subnet::OptionContainerPtr
+Subnet::getVendorOptionDescriptors(uint32_t vendor_id) const {
+    return (vendor_option_spaces_.getItems(vendor_id));
+}
+
+Subnet::OptionDescriptor
+Subnet::getVendorOptionDescriptor(uint32_t vendor_id, uint16_t option_code) {
+    OptionContainerPtr options = getVendorOptionDescriptors(vendor_id);
+    if (!options || options->empty()) {
+        return (OptionDescriptor(false));
+    }
+    const OptionContainerTypeIndex& idx = options->get<1>();
+    const OptionContainerTypeRange& range = idx.equal_range(option_code);
+    if (std::distance(range.first, range.second) == 0) {
+        return (OptionDescriptor(false));
+    }
+
+    return (*range.first);
+}
+
+void Subnet::delVendorOptions() {
+    vendor_option_spaces_.clearItems();
+}
+
+isc::asiolink::IOAddress Subnet::getLastAllocated(Lease::Type type) const {
+    // check if the type is valid (and throw if it isn't)
+    checkType(type);
+
+    switch (type) {
+    case Lease::TYPE_V4:
+    case Lease::TYPE_NA:
+        return last_allocated_ia_;
+    case Lease::TYPE_TA:
+        return last_allocated_ta_;
+    case Lease::TYPE_PD:
+        return last_allocated_pd_;
+    default:
+        isc_throw(BadValue, "Pool type " << type << " not supported");
+    }
+}
+
+void Subnet::setLastAllocated(Lease::Type type,
+                              const isc::asiolink::IOAddress& addr) {
+
+    // check if the type is valid (and throw if it isn't)
+    checkType(type);
+
+    switch (type) {
+    case Lease::TYPE_V4:
+    case Lease::TYPE_NA:
+        last_allocated_ia_ = addr;
+        return;
+    case Lease::TYPE_TA:
+        last_allocated_ta_ = addr;
+        return;
+    case Lease::TYPE_PD:
+        last_allocated_pd_ = addr;
+        return;
+    default:
+        isc_throw(BadValue, "Pool type " << type << " not supported");
+    }
+}
+
+std::string
 Subnet::toText() const {
     std::stringstream tmp;
     tmp << prefix_.toText() << "/" << static_cast<unsigned int>(prefix_len_);
     return (tmp.str());
 }
 
+void Subnet4::checkType(Lease::Type type) const {
+    if (type != Lease::TYPE_V4) {
+        isc_throw(BadValue, "Only TYPE_V4 is allowed for Subnet4");
+    }
+}
+
 Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
                  const Triplet<uint32_t>& t1,
                  const Triplet<uint32_t>& t2,
                  const Triplet<uint32_t>& valid_lifetime)
-    :Subnet(prefix, length, t1, t2, valid_lifetime) {
+    :Subnet(prefix, length, t1, t2, valid_lifetime),
+    siaddr_(IOAddress("0.0.0.0")) {
     if (!prefix.isV4()) {
         isc_throw(BadValue, "Non IPv4 prefix " << prefix.toText()
                   << " specified in subnet4");
     }
 }
 
-void 
-Subnet::addPool(const PoolPtr& pool) {
-    IOAddress first_addr = pool->getFirstAddress();
-    IOAddress last_addr = pool->getLastAddress();
-
-    if (!inRange(first_addr) || !inRange(last_addr)) {
-        isc_throw(BadValue, "Pool (" << first_addr.toText() << "-" 
-                  << last_addr.toText()
-                  << " does not belong in this (" << prefix_.toText() << "/"
-                  << static_cast<int>(prefix_len_) << ") subnet4");
+void Subnet4::setSiaddr(const isc::asiolink::IOAddress& siaddr) {
+    if (!siaddr.isV4()) {
+        isc_throw(BadValue, "Can't set siaddr to non-IPv4 address "
+                  << siaddr.toText());
     }
+    siaddr_ = siaddr;
+}
 
-    /// @todo: Check that pools do not overlap
+isc::asiolink::IOAddress Subnet4::getSiaddr() const {
+    return (siaddr_);
+}
+
+const PoolCollection& Subnet::getPools(Lease::Type type) const {
+    // check if the type is valid (and throw if it isn't)
+    checkType(type);
+
+    switch (type) {
+    case Lease::TYPE_V4:
+    case Lease::TYPE_NA:
+        return (pools_);
+    case Lease::TYPE_TA:
+        return (pools_ta_);
+    case Lease::TYPE_PD:
+        return (pools_pd_);
+    default:
+        isc_throw(BadValue, "Unsupported pool type: "
+                  << static_cast<int>(type));
+    }
+}
 
-    pools_.push_back(pool);
+PoolCollection& Subnet::getPoolsWritable(Lease::Type type) {
+    // check if the type is valid (and throw if it isn't)
+    checkType(type);
+
+    switch (type) {
+    case Lease::TYPE_V4:
+    case Lease::TYPE_NA:
+        return (pools_);
+    case Lease::TYPE_TA:
+        return (pools_ta_);
+    case Lease::TYPE_PD:
+        return (pools_pd_);
+    default:
+        isc_throw(BadValue, "Invalid pool type specified: "
+                  << static_cast<int>(type));
+    }
 }
 
-PoolPtr Subnet::getPool(isc::asiolink::IOAddress hint) {
+const PoolPtr Subnet::getPool(Lease::Type type, const isc::asiolink::IOAddress& hint,
+                        bool anypool /* true */) const {
+    // check if the type is valid (and throw if it isn't)
+    checkType(type);
+
+    const PoolCollection& pools = getPools(type);
 
     PoolPtr candidate;
-    for (PoolCollection::iterator pool = pools_.begin(); 
-         pool != pools_.end(); ++pool) {
+    for (PoolCollection::const_iterator pool = pools.begin();
+         pool != pools.end(); ++pool) {
 
-        // If we won't find anything better, then let's just use the first pool
-        if (!candidate) {
+        // if we won't find anything better, then let's just use the first pool
+        if (anypool && !candidate) {
             candidate = *pool;
         }
 
-        // If the client provided a pool and there's a pool that hint is valid 
+        // if the client provided a pool and there's a pool that hint is valid
         // in, then let's use that pool
         if ((*pool)->inRange(hint)) {
             return (*pool);
@@ -141,39 +257,65 @@ PoolPtr Subnet::getPool(isc::asiolink::IOAddress hint) {
     return (candidate);
 }
 
-void 
+void
+Subnet::addPool(const PoolPtr& pool) {
+    IOAddress first_addr = pool->getFirstAddress();
+    IOAddress last_addr = pool->getLastAddress();
+
+    if (!inRange(first_addr) || !inRange(last_addr)) {
+        isc_throw(BadValue, "Pool (" << first_addr.toText() << "-"
+                  << last_addr.toText()
+                  << " does not belong in this (" << prefix_.toText() << "/"
+                  << static_cast<int>(prefix_len_) << ") subnet");
+    }
+
+    /// @todo: Check that pools do not overlap
+
+    // check if the type is valid (and throw if it isn't)
+    checkType(pool->getType());
+
+    // Add the pool to the appropriate pools collection
+    getPoolsWritable(pool->getType()).push_back(pool);
+}
+
+void
+Subnet::delPools(Lease::Type type) {
+    getPoolsWritable(type).clear();
+}
+
+void
 Subnet::setIface(const std::string& iface_name) {
     iface_ = iface_name;
 }
 
-std::string 
+std::string
 Subnet::getIface() const {
     return (iface_);
 }
 
-
-
 void
 Subnet4::validateOption(const OptionPtr& option) const {
     if (!option) {
-        isc_throw(isc::BadValue, 
+        isc_throw(isc::BadValue,
                   "option configured for subnet must not be NULL");
     } else if (option->getUniverse() != Option::V4) {
-        isc_throw(isc::BadValue, 
+        isc_throw(isc::BadValue,
                   "expected V4 option to be added to the subnet");
     }
 }
 
-bool 
-Subnet::inPool(const isc::asiolink::IOAddress& addr) const {
+bool
+Subnet::inPool(Lease::Type type, const isc::asiolink::IOAddress& addr) const {
 
     // Let's start with checking if it even belongs to that subnet.
     if (!inRange(addr)) {
         return (false);
     }
 
-    for (PoolCollection::const_iterator pool = pools_.begin(); 
-         pool != pools_.end(); ++pool) {
+    const PoolCollection& pools = getPools(type);
+
+    for (PoolCollection::const_iterator pool = pools.begin();
+         pool != pools.end(); ++pool) {
         if ((*pool)->inRange(addr)) {
             return (true);
         }
@@ -195,13 +337,22 @@ Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
     }
 }
 
+void Subnet6::checkType(Lease::Type type) const {
+    if ( (type != Lease::TYPE_NA) && (type != Lease::TYPE_TA) &&
+         (type != Lease::TYPE_PD)) {
+        isc_throw(BadValue, "Invalid Pool type: " << Lease::typeToText(type)
+                  << "(" << static_cast<int>(type)
+                  << "), must be TYPE_NA, TYPE_TA or TYPE_PD for Subnet6");
+    }
+}
+
 void
 Subnet6::validateOption(const OptionPtr& option) const {
     if (!option) {
-        isc_throw(isc::BadValue, 
+        isc_throw(isc::BadValue,
                   "option configured for subnet must not be NULL");
     } else if (option->getUniverse() != Option::V6) {
-        isc_throw(isc::BadValue, 
+        isc_throw(isc::BadValue,
                   "expected V6 option to be added to the subnet");
     }
 }
diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h
index 0ac5109..31dc947 100644
--- a/src/lib/dhcpsrv/subnet.h
+++ b/src/lib/dhcpsrv/subnet.h
@@ -28,6 +28,7 @@
 #include <dhcpsrv/option_space_container.h>
 #include <dhcpsrv/pool.h>
 #include <dhcpsrv/triplet.h>
+#include <dhcpsrv/lease.h>
 
 namespace isc {
 namespace dhcp {
@@ -46,7 +47,6 @@ namespace dhcp {
 ///
 /// @todo: Implement support for options here
 
-
 /// @brief Unique identifier for a subnet (both v4 and v6)
 typedef uint32_t SubnetID;
 
@@ -180,9 +180,22 @@ public:
     void addOption(const OptionPtr& option, bool persistent,
                    const std::string& option_space);
 
+
+    /// @brief Adds new vendor option instance to the collection.
+    ///
+    /// @param option option instance.
+    /// @param persistent if true, send an option regardless if client
+    /// requested it or not.
+    /// @param vendor_id enterprise id of the vendor space to add an option to.
+    void addVendorOption(const OptionPtr& option, bool persistent,
+                         uint32_t vendor_id);
+
     /// @brief Delete all options configured for the subnet.
     void delOptions();
 
+    /// @brief Deletes all vendor options configured for the subnet.
+    void delVendorOptions();
+
     /// @brief checks if the specified address is in pools
     ///
     /// Note the difference between inSubnet() and inPool(). For a given
@@ -192,22 +205,23 @@ public:
     /// is not always true. For the given example, 2001::1234:abcd would return
     /// true for inSubnet(), but false for inPool() check.
     ///
+    /// @param type type of pools to iterate over
     /// @param addr this address will be checked if it belongs to any pools in
     ///        that subnet
     /// @return true if the address is in any of the pools
-    bool inPool(const isc::asiolink::IOAddress& addr) const;
+    bool inPool(Lease::Type type, const isc::asiolink::IOAddress& addr) const;
 
-    /// @brief return valid-lifetime for addresses in that prefix
+    /// @brief Return valid-lifetime for addresses in that prefix
     Triplet<uint32_t> getValid() const {
         return (valid_);
     }
 
-    /// @brief returns T1 (renew timer), expressed in seconds
+    /// @brief Returns T1 (renew timer), expressed in seconds
     Triplet<uint32_t> getT1() const {
         return (t1_);
     }
 
-    /// @brief returns T2 (rebind timer), expressed in seconds
+    /// @brief Returns T2 (rebind timer), expressed in seconds
     Triplet<uint32_t> getT2() const {
         return (t2_);
     }
@@ -220,6 +234,14 @@ public:
     OptionContainerPtr
     getOptionDescriptors(const std::string& option_space) const;
 
+    /// @brief Return a collection of vendor option descriptors.
+    ///
+    /// @param vendor_id enterprise id of the option space.
+    ///
+    /// @return pointer to collection of options configured for a subnet.
+    OptionContainerPtr
+    getVendorOptionDescriptors(uint32_t vendor_id) const;
+
     /// @brief Return single option descriptor.
     ///
     /// @param option_space name of the option space.
@@ -231,6 +253,16 @@ public:
     getOptionDescriptor(const std::string& option_space,
                         const uint16_t option_code);
 
+    /// @brief Return single vendor option descriptor.
+    ///
+    /// @param vendor_id enterprise id of the option space.
+    /// @param option_code code of the option to be returned.
+    ///
+    /// @return option descriptor found for the specified option space
+    /// and option code.
+    OptionDescriptor
+    getVendorOptionDescriptor(uint32_t vendor_id, uint16_t option_code);
+
     /// @brief returns the last address that was tried from this pool
     ///
     /// This method returns the last address that was attempted to be allocated
@@ -240,10 +272,9 @@ public:
     /// @todo: Define map<SubnetID, IOAddress> somewhere in the
     ///        AllocEngine::IterativeAllocator and keep the data there
     ///
-    /// @return address that was last tried from this pool
-    isc::asiolink::IOAddress getLastAllocated() const {
-        return (last_allocated_);
-    }
+    /// @param type lease type to be returned
+    /// @return address/prefix that was last tried from this pool
+    isc::asiolink::IOAddress getLastAllocated(Lease::Type type) const;
 
     /// @brief sets the last address that was tried from this pool
     ///
@@ -253,15 +284,16 @@ public:
     ///
     /// @todo: Define map<SubnetID, IOAddress> somewhere in the
     ///        AllocEngine::IterativeAllocator and keep the data there
-    void setLastAllocated(const isc::asiolink::IOAddress& addr) {
-        last_allocated_ = addr;
-    }
+    /// @param addr address/prefix to that was tried last
+    /// @param type lease type to be set
+    void setLastAllocated(Lease::Type type,
+                          const isc::asiolink::IOAddress& addr);
 
-    /// @brief returns unique ID for that subnet
+    /// @brief Returns unique ID for that subnet
     /// @return unique ID for that subnet
     SubnetID getID() const { return (id_); }
 
-    /// @brief returns subnet parameters (prefix and prefix length)
+    /// @brief Returns subnet parameters (prefix and prefix length)
     ///
     /// @return (prefix, prefix length) pair
     std::pair<isc::asiolink::IOAddress, uint8_t> get() const {
@@ -272,16 +304,36 @@ public:
     /// @param pool pool to be added
     void addPool(const PoolPtr& pool);
 
+
+    /// @brief Deletes all pools of specified type
+    ///
+    /// This method is used for testing purposes only
+    /// @param type type of pools to be deleted
+    void delPools(Lease::Type type);
+
     /// @brief Returns a pool that specified address belongs to
     ///
+    /// If there is no pool that the address belongs to (hint is invalid), other
+    /// pool of specified type will be returned.
+    ///
+    /// With anypool set to true, this is means give me a pool, preferably
+    /// the one that addr belongs to. With anypool set to false, it means
+    /// give me a pool that addr belongs to (or NULL if here is no such pool)
+    ///
+    /// @param type pool type that the pool is looked for
     /// @param addr address that the returned pool should cover (optional)
-    /// @return Pointer to found Pool4 or Pool6 (or NULL)
-    PoolPtr getPool(isc::asiolink::IOAddress addr);
+    /// @param anypool other pool may be returned as well, not only the one
+    ///        that addr belongs to
+    /// @return found pool (or NULL)
+    const PoolPtr getPool(Lease::Type type, const isc::asiolink::IOAddress& addr,
+                          bool anypool = true) const;
 
     /// @brief Returns a pool without any address specified
+    ///
+    /// @param type pool type that the pool is looked for
     /// @return returns one of the pools defined
-    PoolPtr getPool() {
-        return (getPool(default_pool()));
+    PoolPtr getAnyPool(Lease::Type type) {
+        return (getPool(type, default_pool()));
     }
 
     /// @brief Returns the default address that will be used for pool selection
@@ -290,33 +342,40 @@ public:
     /// and 0.0.0.0 for Subnet4)
     virtual isc::asiolink::IOAddress default_pool() const = 0;
 
-    /// @brief returns all pools
+    /// @brief Returns all pools (const variant)
     ///
     /// The reference is only valid as long as the object that returned it.
     ///
+    /// @param type lease type to be set
     /// @return a collection of all pools
-    const PoolCollection& getPools() const {
-        return pools_;
-    }
+    const PoolCollection& getPools(Lease::Type type) const;
 
-    /// @brief sets name of the network interface for directly attached networks
+    /// @brief Sets name of the network interface for directly attached networks
     ///
     /// @param iface_name name of the interface
     void setIface(const std::string& iface_name);
 
-    /// @brief network interface name used to reach subnet (or "" for remote 
+    /// @brief Network interface name used to reach subnet (or "" for remote
     /// subnets)
     /// @return network interface name for directly attached subnets or ""
     std::string getIface() const;
 
-    /// @brief returns textual representation of the subnet (e.g. 
+    /// @brief Returns textual representation of the subnet (e.g.
     /// "2001:db8::/64")
     ///
     /// @return textual representation
     virtual std::string toText() const;
 
 protected:
-    /// @brief protected constructor
+    /// @brief Returns all pools (non-const variant)
+    ///
+    /// The reference is only valid as long as the object that returned it.
+    ///
+    /// @param type lease type to be set
+    /// @return a collection of all pools
+    PoolCollection& getPoolsWritable(Lease::Type type);
+
+    /// @brief Protected constructor
     //
     /// By making the constructor protected, we make sure that noone will
     /// ever instantiate that class. Pool4 and Pool6 should be used instead.
@@ -339,6 +398,16 @@ protected:
         return (id++);
     }
 
+    /// @brief Checks if used pool type is valid
+    ///
+    /// Allowed type for Subnet4 is Pool::TYPE_V4.
+    /// Allowed types for Subnet6 are Pool::TYPE_{IA,TA,PD}.
+    /// This method is implemented in derived classes.
+    ///
+    /// @param type type to be checked
+    /// @throw BadValue if invalid value is used
+    virtual void checkType(Lease::Type type) const = 0;
+
     /// @brief Check if option is valid and can be added to a subnet.
     ///
     /// @param option option to be validated.
@@ -350,9 +419,15 @@ protected:
     /// a Subnet4 or Subnet6.
     SubnetID id_;
 
-    /// @brief collection of pools in that list
+    /// @brief collection of IPv4 or non-temporary IPv6 pools in that subnet
     PoolCollection pools_;
 
+    /// @brief collection of IPv6 temporary address pools in that subnet
+    PoolCollection pools_ta_;
+
+    /// @brief collection of IPv6 prefix pools in that subnet
+    PoolCollection pools_pd_;
+
     /// @brief a prefix of the subnet
     isc::asiolink::IOAddress prefix_;
 
@@ -377,7 +452,17 @@ protected:
     /// removing a pool, restarting or changing allocation algorithms. For
     /// that purpose it should be only considered a help that should not be
     /// fully trusted.
-    isc::asiolink::IOAddress last_allocated_;
+    isc::asiolink::IOAddress last_allocated_ia_;
+
+    /// @brief last allocated temporary address
+    ///
+    /// See @ref last_allocated_ia_ for details.
+    isc::asiolink::IOAddress last_allocated_ta_;
+
+    /// @brief last allocated IPv6 prefix
+    ///
+    /// See @ref last_allocated_ia_ for details.
+    isc::asiolink::IOAddress last_allocated_pd_;
 
     /// @brief Name of the network interface (if connected directly)
     std::string iface_;
@@ -386,9 +471,17 @@ private:
 
     /// A collection of option spaces grouping option descriptors.
     typedef OptionSpaceContainer<OptionContainer,
-                                 OptionDescriptor> OptionSpaceCollection;
+        OptionDescriptor, std::string> OptionSpaceCollection;
+
+    /// A collection of vendor space option descriptors.
+    typedef OptionSpaceContainer<OptionContainer,
+        OptionDescriptor, uint32_t> VendorOptionSpaceCollection;
+
+    /// Regular options are kept here
     OptionSpaceCollection option_spaces_;
 
+    /// Vendor options are kept here
+    VendorOptionSpaceCollection vendor_option_spaces_;
 };
 
 /// @brief A generic pointer to either Subnet4 or Subnet6 object
@@ -412,6 +505,18 @@ public:
             const Triplet<uint32_t>& t2,
             const Triplet<uint32_t>& valid_lifetime);
 
+    /// @brief Sets siaddr for the Subnet4
+    ///
+    /// Will be used for siaddr field (the next server) that typically is used
+    /// as TFTP server. If not specified, the default value of 0.0.0.0 is
+    /// used.
+    void setSiaddr(const isc::asiolink::IOAddress& siaddr);
+
+    /// @brief Returns siaddr for this subnet
+    ///
+    /// @return siaddr value
+    isc::asiolink::IOAddress getSiaddr() const;
+
 protected:
 
     /// @brief Check if option is valid and can be added to a subnet.
@@ -426,6 +531,17 @@ protected:
     virtual isc::asiolink::IOAddress default_pool() const {
         return (isc::asiolink::IOAddress("0.0.0.0"));
     }
+
+    /// @brief Checks if used pool type is valid
+    ///
+    /// Allowed type for Subnet4 is Pool::TYPE_V4.
+    ///
+    /// @param type type to be checked
+    /// @throw BadValue if invalid value is used
+    virtual void checkType(Lease::Type type) const;
+
+    /// @brief siaddr value for this subnet
+    isc::asiolink::IOAddress siaddr_;
 };
 
 /// @brief A pointer to a Subnet4 object
@@ -490,12 +606,17 @@ protected:
         return (isc::asiolink::IOAddress("::"));
     }
 
+    /// @brief Checks if used pool type is valid
+    ///
+    /// allowed types for Subnet6 are Pool::TYPE_{IA,TA,PD}.
+    ///
+    /// @param type type to be checked
+    /// @throw BadValue if invalid value is used
+    virtual void checkType(Lease::Type type) const;
+
     /// @brief specifies optional interface-id
     OptionPtr interface_id_;
 
-    /// @brief collection of pools in that list
-    Pool6Collection pools_;
-
     /// @brief a triplet with preferred lifetime (in seconds)
     Triplet<uint32_t> preferred_;
 };
diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am
index 087b28d..546ced9 100644
--- a/src/lib/dhcpsrv/tests/Makefile.am
+++ b/src/lib/dhcpsrv/tests/Makefile.am
@@ -13,11 +13,6 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
 # But older GCC compilers don't have the flag.
 AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-TEST_LIBS_LDFLAGS = -Bshareable
-endif
-
 CLEANFILES = *.gcno *.gcda
 
 TESTS_ENVIRONMENT = \
@@ -25,19 +20,30 @@ TESTS_ENVIRONMENT = \
 
 TESTS =
 if HAVE_GTEST
-# Build shared libraries for testing.
+# Build shared libraries for testing. The libtool way to create a shared library
+# is to specify "-avoid-version -export-dynamic -module" in the library LDFLAGS
+# (see http://www.gnu.org/software/libtool/manual/html_node/Link-mode.html).
+# Use of these switches will guarantee that the .so files are created in the
+# .libs folder and they can be dlopened.
+# Note that the shared libraries with callouts should not be used together with
+# the --enable-static-link option. With this option, the bind10 libraries are
+# statically linked with the program and if the callout invokes the methods
+# which belong to these libraries, the library with the callout will get its
+# own copy of the static objects (e.g. logger, ServerHooks) and that will lead
+# to unexpected errors. For this reason, the --enable-static-link option is
+# ignored for unit tests built here.
+
 lib_LTLIBRARIES = libco1.la libco2.la
 
 libco1_la_SOURCES  = callout_library.cc
 libco1_la_CXXFLAGS = $(AM_CXXFLAGS)
 libco1_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
-libco1_la_LDFLAGS = $(TEST_LIBS_LDFLAGS)
+libco1_la_LDFLAGS = -avoid-version -export-dynamic -module
 
 libco2_la_SOURCES  = callout_library.cc
 libco2_la_CXXFLAGS = $(AM_CXXFLAGS)
 libco2_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
-libco2_la_LDFLAGS = $(TEST_LIBS_LDFLAGS)
-
+libco2_la_LDFLAGS = -avoid-version -export-dynamic -module
 
 TESTS += libdhcpsrv_unittests
 
diff --git a/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
index 083c20f..fda8d59 100644
--- a/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
+++ b/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
@@ -55,13 +55,30 @@ public:
     /// @brief the sole constructor
     /// @param engine_type specifies engine type (e.g. iterative)
     /// @param attempts number of lease selection attempts before giving up
-    NakedAllocEngine(AllocEngine::AllocType engine_type, unsigned int attempts)
-        :AllocEngine(engine_type, attempts) {
+    /// @param ipv6 specifies if the engine is IPv6 or IPv4
+    NakedAllocEngine(AllocEngine::AllocType engine_type,
+                     unsigned int attempts, bool ipv6 = true)
+        :AllocEngine(engine_type, attempts, ipv6) {
     }
 
     // Expose internal classes for testing purposes
     using AllocEngine::Allocator;
     using AllocEngine::IterativeAllocator;
+    using AllocEngine::getAllocator;
+
+    /// @brief IterativeAllocator with internal methods exposed
+    class NakedIterativeAllocator: public AllocEngine::IterativeAllocator {
+    public:
+
+        /// @brief constructor
+        /// @param type pool types that will be interated
+        NakedIterativeAllocator(Lease::Type type)
+            :IterativeAllocator(type) {
+        }
+
+        using AllocEngine::IterativeAllocator::increaseAddress;
+        using AllocEngine::IterativeAllocator::increasePrefix;
+    };
 };
 
 /// @brief Used in Allocation Engine tests for IPv6
@@ -80,36 +97,252 @@ public:
         // instantiate cfg_mgr
         CfgMgr& cfg_mgr = CfgMgr::instance();
 
+        // Configure normal address pool
         subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
-        pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1::10"),
+        pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::10"),
                                    IOAddress("2001:db8:1::20")));
         subnet_->addPool(pool_);
+
+        // Configure PD pool
+        pd_pool_ = Pool6Ptr(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 56, 64));
+        subnet_->addPool(pd_pool_);
+
         cfg_mgr.addSubnet6(subnet_);
 
         factory_.create("type=memfile");
     }
 
+    /// @brief attempts to convert leases collection to a single lease
+    ///
+    /// This operation makes sense if there is at most one lease in the
+    /// collection. Otherwise it will throw.
+    ///
+    /// @param col collection of leases (zero or one leases allowed)
+    /// @throw MultipleRecords if there is more than one lease
+    /// @return Lease6 pointer (or NULL if collection was empty)
+    Lease6Ptr expectOneLease(const Lease6Collection& col) {
+        if (col.size() > 1) {
+            isc_throw(MultipleRecords, "More than one lease found in collection");
+        }
+        if (col.empty()) {
+            return (Lease6Ptr());
+        }
+        return (*col.begin());
+    }
+
     /// @brief checks if Lease6 matches expected configuration
     ///
     /// @param lease lease to be checked
-    void checkLease6(const Lease6Ptr& lease) {
+    /// @param exp_type expected lease type
+    /// @param exp_pd_len expected prefix length
+    void checkLease6(const Lease6Ptr& lease, Lease::Type exp_type,
+                     uint8_t exp_pd_len = 128) {
+
         // that is belongs to the right subnet
         EXPECT_EQ(lease->subnet_id_, subnet_->getID());
         EXPECT_TRUE(subnet_->inRange(lease->addr_));
-        EXPECT_TRUE(subnet_->inPool(lease->addr_));
+        EXPECT_TRUE(subnet_->inPool(exp_type, lease->addr_));
 
         // that it have proper parameters
+        EXPECT_EQ(exp_type, lease->type_);
         EXPECT_EQ(iaid_, lease->iaid_);
         EXPECT_EQ(subnet_->getValid(), lease->valid_lft_);
         EXPECT_EQ(subnet_->getPreferred(), lease->preferred_lft_);
         EXPECT_EQ(subnet_->getT1(), lease->t1_);
         EXPECT_EQ(subnet_->getT2(), lease->t2_);
-        EXPECT_EQ(0, lease->prefixlen_); // this is IA_NA, not IA_PD
+        EXPECT_EQ(exp_pd_len, lease->prefixlen_);
         EXPECT_TRUE(false == lease->fqdn_fwd_);
         EXPECT_TRUE(false == lease->fqdn_rev_);
         EXPECT_TRUE(*lease->duid_ == *duid_);
         // @todo: check cltt
-     }
+    }
+
+    /// @brief Checks if specified address is increased properly
+    ///
+    /// Method uses gtest macros to mark check failure.
+    ///
+    /// @param alloc IterativeAllocator that is tested
+    /// @param input address to be increased
+    /// @param exp_output expected address after increase
+    void
+    checkAddrIncrease(NakedAllocEngine::NakedIterativeAllocator& alloc,
+                      std::string input, std::string exp_output) {
+        EXPECT_EQ(exp_output, alloc.increaseAddress(IOAddress(input)).toText());
+    }
+
+    /// @brief Checks if increasePrefix() works as expected
+    ///
+    /// Method uses gtest macros to mark check failure.
+    ///
+    /// @param alloc allocator to be tested
+    /// @param input IPv6 prefix (as a string)
+    /// @param prefix_len prefix len
+    /// @param exp_output expected output (string)
+    void
+    checkPrefixIncrease(NakedAllocEngine::NakedIterativeAllocator& alloc,
+                        std::string input, uint8_t prefix_len,
+                        std::string exp_output) {
+        EXPECT_EQ(exp_output, alloc.increasePrefix(IOAddress(input), prefix_len)
+                  .toText());
+    }
+
+    /// @brief Checks if the simple allocation can succeed
+    ///
+    /// The type of lease is determined by pool type (pool->getType()
+    ///
+    /// @param pool pool from which the lease will be allocated from
+    /// @param hint address to be used as a hint
+    /// @param fake true - this is fake allocation (SOLICIT)
+    /// @return allocated lease (or NULL)
+    Lease6Ptr simpleAlloc6Test(const Pool6Ptr& pool, const IOAddress& hint,
+                               bool fake) {
+        Lease::Type type = pool->getType();
+        uint8_t expected_len = pool->getLength();
+
+        boost::scoped_ptr<AllocEngine> engine;
+        EXPECT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                     100)));
+        // We can't use ASSERT macros in non-void methods
+        EXPECT_TRUE(engine);
+        if (!engine) {
+            return (Lease6Ptr());
+        }
+
+        Lease6Ptr lease;
+        EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
+                        duid_, iaid_, hint, type, false, false,
+                        "", fake, CalloutHandlePtr())));
+
+        // Check that we got a lease
+        EXPECT_TRUE(lease);
+        if (!lease) {
+            return (Lease6Ptr());
+        }
+
+        // Do all checks on the lease
+        checkLease6(lease, type, expected_len);
+
+        // Check that the lease is indeed in LeaseMgr
+        Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(type,
+                                                                   lease->addr_);
+        if (!fake) {
+            // This is a real (REQUEST) allocation, the lease must be in the DB
+            EXPECT_TRUE(from_mgr);
+            if (!from_mgr) {
+                return (Lease6Ptr());
+            }
+
+            // Now check that the lease in LeaseMgr has the same parameters
+            detailCompareLease(lease, from_mgr);
+        } else {
+            // This is a fake (SOLICIT) allocation, the lease must not be in DB
+            EXPECT_FALSE(from_mgr);
+            if (from_mgr) {
+                return (Lease6Ptr());
+            }
+        }
+
+        return (lease);
+    }
+
+    /// @brief Checks if the address allocation with a hint that is in range,
+    ///        in pool, but is currently used, can succeed
+    ///
+    /// Method uses gtest macros to mark check failure.
+    ///
+    /// @param type lease type
+    /// @param used_addr address should be preallocated (simulates prior
+    ///        allocation by some other user)
+    /// @param requested address requested by the client
+    /// @param expected_pd_len expected PD len (128 for addresses)
+    void allocWithUsedHintTest(Lease::Type type, IOAddress used_addr,
+                               IOAddress requested, uint8_t expected_pd_len) {
+        boost::scoped_ptr<AllocEngine> engine;
+        ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                     100)));
+        ASSERT_TRUE(engine);
+
+        // Let's create a lease and put it in the LeaseMgr
+        DuidPtr duid2 = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0xff)));
+        time_t now = time(NULL);
+        Lease6Ptr used(new Lease6(type, used_addr,
+                                  duid2, 1, 2, 3, 4, now, subnet_->getID()));
+        ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
+
+        // Another client comes in and request an address that is in pool, but
+        // unfortunately it is used already. The same address must not be allocated
+        // twice.
+        Lease6Ptr lease;
+        EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
+                        duid_, iaid_, requested, type, false, false, "", false,
+                        CalloutHandlePtr())));
+
+        // Check that we got a lease
+        ASSERT_TRUE(lease);
+
+        // Allocated address must be different
+        EXPECT_NE(used_addr.toText(), lease->addr_.toText());
+
+        // We should NOT get what we asked for, because it is used already
+        EXPECT_NE(requested.toText(), lease->addr_.toText());
+
+        // Do all checks on the lease
+        checkLease6(lease, type, expected_pd_len);
+
+        // Check that the lease is indeed in LeaseMgr
+        Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
+                                                                   lease->addr_);
+        ASSERT_TRUE(from_mgr);
+
+        // Now check that the lease in LeaseMgr has the same parameters
+        detailCompareLease(lease, from_mgr);
+    }
+
+    /// @brief checks if bogus hint can be ignored and the allocation succeeds
+    ///
+    /// This test checks if the allocation with a hing that is out of the blue
+    /// can succeed. The invalid hint should be ingored completely.
+    ///
+    /// @param type Lease type
+    /// @param hint hint (as send by a client)
+    /// @param expectd_pd_len (used in validation)
+    void allocBogusHint6(Lease::Type type, IOAddress hint,
+                         uint8_t expected_pd_len) {
+        boost::scoped_ptr<AllocEngine> engine;
+        ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                     100)));
+        ASSERT_TRUE(engine);
+
+        // Client would like to get a 3000::abc lease, which does not belong to any
+        // supported lease. Allocation engine should ignore it and carry on
+        // with the normal allocation
+        Lease6Ptr lease;
+        EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
+                        duid_, iaid_, hint, type, false,
+                        false, "", false, CalloutHandlePtr())));
+
+        // Check that we got a lease
+        ASSERT_TRUE(lease);
+
+        // We should NOT get what we asked for, because it is used already
+        EXPECT_NE(hint.toText(), lease->addr_.toText());
+
+        // Do all checks on the lease
+        checkLease6(lease, type, expected_pd_len);
+
+    // Check that the lease is indeed in LeaseMgr
+    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
+                                                               lease->addr_);
+    ASSERT_TRUE(from_mgr);
+
+    // Now check that the lease in LeaseMgr has the same parameters
+    detailCompareLease(lease, from_mgr);
+
+
+
+
+    }
+
 
     virtual ~AllocEngine6Test() {
         factory_.destroy();
@@ -118,7 +351,8 @@ public:
     DuidPtr duid_;            ///< client-identifier (value used in tests)
     uint32_t iaid_;           ///< IA identifier (value used in tests)
     Subnet6Ptr subnet_;       ///< subnet6 (used in tests)
-    Pool6Ptr pool_;           ///< pool belonging to subnet_
+    Pool6Ptr pool_;           ///< NA pool belonging to subnet_
+    Pool6Ptr pd_pool_;        ///< PD pool belonging to subnet_
     LeaseMgrFactory factory_; ///< pointer to LeaseMgr factory
 };
 
@@ -158,7 +392,7 @@ public:
         // Check that is belongs to the right subnet
         EXPECT_EQ(lease->subnet_id_, subnet_->getID());
         EXPECT_TRUE(subnet_->inRange(lease->addr_));
-        EXPECT_TRUE(subnet_->inPool(lease->addr_));
+        EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, lease->addr_));
 
         // Check that it has proper parameters
         EXPECT_EQ(subnet_->getValid(), lease->valid_lft_);
@@ -189,8 +423,8 @@ public:
     Lease4Ptr old_lease_;     ///< Holds previous instance of the lease.
 };
 
-// This test checks if the Allocation Engine can be instantiated and that it
-// parses parameters string properly.
+// This test checks if the v6 Allocation Engine can be instantiated, parses
+// parameters string and allocators are created.
 TEST_F(AllocEngine6Test, constructor) {
     boost::scoped_ptr<AllocEngine> x;
 
@@ -198,155 +432,83 @@ TEST_F(AllocEngine6Test, constructor) {
     ASSERT_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_HASHED, 5)), NotImplemented);
     ASSERT_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_RANDOM, 5)), NotImplemented);
 
-    ASSERT_NO_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
-}
+    ASSERT_NO_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100, true)));
 
-// This test checks if the simple allocation can succeed
-TEST_F(AllocEngine6Test, simpleAlloc6) {
-    boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
-    ASSERT_TRUE(engine);
+    // Check that allocator for normal addresses is created
+    ASSERT_TRUE(x->getAllocator(Lease::TYPE_NA));
 
-    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                               IOAddress("::"), false,
-                                               false, "",
-                                               false, CalloutHandlePtr());
+    // Check that allocator for temporary address is created
+    ASSERT_TRUE(x->getAllocator(Lease::TYPE_TA));
 
-    // Check that we got a lease
-    ASSERT_TRUE(lease);
+    // Check that allocator for prefixes is created
+    ASSERT_TRUE(x->getAllocator(Lease::TYPE_PD));
 
-    // Do all checks on the lease
-    checkLease6(lease);
+    // There should be no V4 allocator
+    EXPECT_THROW(x->getAllocator(Lease::TYPE_V4), BadValue);
+}
 
-    // Check that the lease is indeed in LeaseMgr
-    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
-    ASSERT_TRUE(from_mgr);
+// This test checks if the simple allocation (REQUEST) can succeed
+TEST_F(AllocEngine6Test, simpleAlloc6) {
+    simpleAlloc6Test(pool_, IOAddress("::"), false);
+}
 
-    // Now check that the lease in LeaseMgr has the same parameters
-    detailCompareLease(lease, from_mgr);
+// This test checks if the simple PD allocation (REQUEST) can succeed
+TEST_F(AllocEngine6Test, pdSimpleAlloc6) {
+    simpleAlloc6Test(pd_pool_, IOAddress("::"), false);
 }
 
 // This test checks if the fake allocation (for SOLICIT) can succeed
 TEST_F(AllocEngine6Test, fakeAlloc6) {
-    boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
-    ASSERT_TRUE(engine);
-
-    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                               IOAddress("::"), false,
-                                               false, "", true,
-                                               CalloutHandlePtr());
-
-    // Check that we got a lease
-    ASSERT_TRUE(lease);
-
-    // Do all checks on the lease
-    checkLease6(lease);
 
-    // Check that the lease is NOT in LeaseMgr
-    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
-    ASSERT_FALSE(from_mgr);
+    simpleAlloc6Test(pool_, IOAddress("::"), true);
 }
 
+// This test checks if the fake PD allocation (for SOLICIT) can succeed
+TEST_F(AllocEngine6Test, pdFakeAlloc6) {
+    simpleAlloc6Test(pd_pool_, IOAddress("::"), true);
+};
+
 // This test checks if the allocation with a hint that is valid (in range,
 // in pool and free) can succeed
 TEST_F(AllocEngine6Test, allocWithValidHint6) {
-    boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
-    ASSERT_TRUE(engine);
-
-    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                               IOAddress("2001:db8:1::15"),
-                                               false, false, "",
-                                               false, CalloutHandlePtr());
 
-    // Check that we got a lease
-    ASSERT_TRUE(lease);
+    Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::15"),
+                                       false);
 
     // We should get what we asked for
     EXPECT_EQ(lease->addr_.toText(), "2001:db8:1::15");
-
-    // Do all checks on the lease
-    checkLease6(lease);
-
-    // Check that the lease is indeed in LeaseMgr
-    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
-    ASSERT_TRUE(from_mgr);
-
-    // Now check that the lease in LeaseMgr has the same parameters
-    detailCompareLease(lease, from_mgr);
 }
 
-// This test checks if the allocation with a hint that is in range,
-// in pool, but is currently used) can succeed
+// This test checks if the address allocation with a hint that is in range,
+// in pool, but is currently used, can succeed
 TEST_F(AllocEngine6Test, allocWithUsedHint6) {
-    boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
-    ASSERT_TRUE(engine);
-
-    // Let's create a lease and put it in the LeaseMgr
-    DuidPtr duid2 = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0xff)));
-    time_t now = time(NULL);
-    Lease6Ptr used(new Lease6(Lease6::LEASE_IA_NA, IOAddress("2001:db8:1::1f"),
-                              duid2, 1, 2, 3, 4, now, subnet_->getID()));
-    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
-
-    // Another client comes in and request an address that is in pool, but
-    // unfortunately it is used already. The same address must not be allocated
-    // twice.
-    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                               IOAddress("2001:db8:1::1f"),
-                                               false, false, "",
-                                               false, CalloutHandlePtr());
-    // Check that we got a lease
-    ASSERT_TRUE(lease);
-
-    // Allocated address must be different
-    EXPECT_TRUE(used->addr_.toText() != lease->addr_.toText());
-
-    // We should NOT get what we asked for, because it is used already
-    EXPECT_TRUE(lease->addr_.toText() != "2001:db8:1::1f");
-
-    // Do all checks on the lease
-    checkLease6(lease);
-
-    // Check that the lease is indeed in LeaseMgr
-    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
-    ASSERT_TRUE(from_mgr);
+    allocWithUsedHintTest(Lease::TYPE_NA,
+                          IOAddress("2001:db8:1::1f"), // allocate this as used
+                          IOAddress("2001:db8:1::1f"), // request this addr
+                          128);
+}
 
-    // Now check that the lease in LeaseMgr has the same parameters
-    detailCompareLease(lease, from_mgr);
+// This test checks if the PD allocation with a hint that is in range,
+// in pool, but is currently used, can succeed
+TEST_F(AllocEngine6Test, pdAllocWithUsedHint6) {
+    allocWithUsedHintTest(Lease::TYPE_PD,
+                          IOAddress("2001:db8:1::"), // allocate this prefix as used
+                          IOAddress("2001:db8:1::"), // request this prefix
+                          64);
 }
 
 // This test checks if the allocation with a hint that is out the blue
 // can succeed. The invalid hint should be ignored completely.
 TEST_F(AllocEngine6Test, allocBogusHint6) {
-    boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
-    ASSERT_TRUE(engine);
 
-    // Client would like to get a 3000::abc lease, which does not belong to any
-    // supported lease. Allocation engine should ignore it and carry on
-    // with the normal allocation
-    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                               IOAddress("3000::abc"),
-                                               false, false, "",
-                                               false, CalloutHandlePtr());
-    // Check that we got a lease
-    ASSERT_TRUE(lease);
-
-    // We should NOT get what we asked for, because it is used already
-    EXPECT_TRUE(lease->addr_.toText() != "3000::abc");
-
-    // Do all checks on the lease
-    checkLease6(lease);
+    allocBogusHint6(Lease::TYPE_NA, IOAddress("3000::abc"), 128);
+}
 
-    // Check that the lease is indeed in LeaseMgr
-    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
-    ASSERT_TRUE(from_mgr);
+// This test checks if the allocation with a hint that is out the blue
+// can succeed. The invalid hint should be ignored completely.
+TEST_F(AllocEngine6Test, pdAllocBogusHint6) {
 
-    // Now check that the lease in LeaseMgr has the same parameters
-    detailCompareLease(lease, from_mgr);
+    allocBogusHint6(Lease::TYPE_PD, IOAddress("3000::abc"), 64);
 }
 
 // This test checks that NULL values are handled properly
@@ -356,17 +518,16 @@ TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
     ASSERT_TRUE(engine);
 
     // Allocations without subnet are not allowed
-    Lease6Ptr lease = engine->allocateAddress6(Subnet6Ptr(), duid_, iaid_,
-                                               IOAddress("::"),
-                                               false, false, "", false,
-                                               CalloutHandlePtr());
+    Lease6Ptr lease;
+    EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(
+                    Subnet6Ptr(), duid_, iaid_, IOAddress("::"), Lease::TYPE_NA,
+                    false, false, "", false, CalloutHandlePtr())));
     ASSERT_FALSE(lease);
 
     // Allocations without DUID are not allowed either
-    lease = engine->allocateAddress6(subnet_, DuidPtr(), iaid_,
-                                     IOAddress("::"),
-                                     false, false, "", false,
-                                     CalloutHandlePtr());
+    EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
+                    DuidPtr(), iaid_, IOAddress("::"), Lease::TYPE_NA, false,
+                    false, "", false, CalloutHandlePtr())));
     ASSERT_FALSE(lease);
 }
 
@@ -375,20 +536,179 @@ TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
 // pool
 TEST_F(AllocEngine6Test, IterativeAllocator) {
     boost::scoped_ptr<NakedAllocEngine::Allocator>
-        alloc(new NakedAllocEngine::IterativeAllocator());
+        alloc(new NakedAllocEngine::IterativeAllocator(Lease::TYPE_NA));
 
     for (int i = 0; i < 1000; ++i) {
         IOAddress candidate = alloc->pickAddress(subnet_, duid_, IOAddress("::"));
-        EXPECT_TRUE(subnet_->inPool(candidate));
+        EXPECT_TRUE(subnet_->inPool(Lease::TYPE_NA, candidate));
     }
 }
 
+TEST_F(AllocEngine6Test, IterativeAllocatorAddrStep) {
+    NakedAllocEngine::NakedIterativeAllocator alloc(Lease::TYPE_NA);
+
+    subnet_->delPools(Lease::TYPE_NA); // Get rid of default pool
+
+    Pool6Ptr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
+                             IOAddress("2001:db8:1::5")));
+    Pool6Ptr pool2(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::100"),
+                             IOAddress("2001:db8:1::100")));
+    Pool6Ptr pool3(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::105"),
+                             IOAddress("2001:db8:1::106")));
+    subnet_->addPool(pool1);
+    subnet_->addPool(pool2);
+    subnet_->addPool(pool3);
+
+    // Let's check the first pool (5 addresses here)
+    EXPECT_EQ("2001:db8:1::1", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::2", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::3", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::4", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::5", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+
+    // The second pool is easy - only one address here
+    EXPECT_EQ("2001:db8:1::100", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+
+    // This is the third and last pool, with 2 addresses in it
+    EXPECT_EQ("2001:db8:1::105", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::106", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+
+    // We iterated over all addresses and reached to the end of the last pool.
+    // Let's wrap around and start from the beginning
+    EXPECT_EQ("2001:db8:1::1", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::2", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+}
+
+TEST_F(AllocEngine6Test, IterativeAllocatorPrefixStep) {
+    NakedAllocEngine::NakedIterativeAllocator alloc(Lease::TYPE_PD);
+
+    subnet_.reset(new Subnet6(IOAddress("2001:db8::"), 32, 1, 2, 3, 4));
+
+    Pool6Ptr pool1(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8::"), 56, 60));
+    Pool6Ptr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 48, 48));
+    Pool6Ptr pool3(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:2::"), 56, 64));
+    subnet_->addPool(pool1);
+    subnet_->addPool(pool2);
+    subnet_->addPool(pool3);
+
+    // We have a 2001:db8::/48 subnet that has 3 pools defined in it:
+    // 2001:db8::/56 split into /60 prefixes (16 leases) (or 2001:db8:0:X0::)
+    // 2001:db8:1::/48 split into a single /48 prefix (just 1 lease)
+    // 2001:db8:2::/56 split into /64 prefixes (256 leases) (or 2001:db8:2:XX::)
+
+    // First pool check (Let's check over all 16 leases)
+    EXPECT_EQ("2001:db8::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:10::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:20::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:30::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:40::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:50::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:60::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:70::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:80::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:90::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:a0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:b0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:c0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:d0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:e0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:f0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+
+    // Second pool (just one lease here)
+    EXPECT_EQ("2001:db8:1::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+
+    // Third pool (256 leases, let's check first and last explictly and the
+    // rest over in a pool
+    EXPECT_EQ("2001:db8:2::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    for (int i = 1; i < 255; i++) {
+        stringstream exp;
+        exp << "2001:db8:2:" << hex << i << dec << "::";
+        EXPECT_EQ(exp.str(), alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+
+    }
+    EXPECT_EQ("2001:db8:2:ff::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+
+    // Ok, we've iterated over all prefixes in all pools. We now wrap around.
+    // We're looping over now (iterating over first pool again)
+    EXPECT_EQ("2001:db8::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:10::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+}
+
+// This test verifies that the iterative allocator can step over addresses
+TEST_F(AllocEngine6Test, IterativeAllocatorAddressIncrease) {
+    NakedAllocEngine::NakedIterativeAllocator alloc(Lease::TYPE_NA);
+
+    // Let's pick the first address
+    IOAddress addr1 = alloc.pickAddress(subnet_, duid_, IOAddress("2001:db8:1::10"));
+
+    // Check that we can indeed pick the first address from the pool
+    EXPECT_EQ("2001:db8:1::10", addr1.toText());
+
+    // Check that addresses can be increased properly
+    checkAddrIncrease(alloc, "2001:db8::9", "2001:db8::a");
+    checkAddrIncrease(alloc, "2001:db8::f", "2001:db8::10");
+    checkAddrIncrease(alloc, "2001:db8::10", "2001:db8::11");
+    checkAddrIncrease(alloc, "2001:db8::ff", "2001:db8::100");
+    checkAddrIncrease(alloc, "2001:db8::ffff", "2001:db8::1:0");
+    checkAddrIncrease(alloc, "::", "::1");
+    checkAddrIncrease(alloc, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::");
+}
+
+// This test verifies that the allocator can step over prefixes
+TEST_F(AllocEngine6Test, IterativeAllocatorPrefixIncrease) {
+    NakedAllocEngine::NakedIterativeAllocator alloc(Lease::TYPE_PD);
+
+    // For /128 prefix, increasePrefix should work the same as addressIncrease
+    checkPrefixIncrease(alloc, "2001:db8::9", 128, "2001:db8::a");
+    checkPrefixIncrease(alloc, "2001:db8::f", 128, "2001:db8::10");
+    checkPrefixIncrease(alloc, "2001:db8::10", 128, "2001:db8::11");
+    checkPrefixIncrease(alloc, "2001:db8::ff", 128, "2001:db8::100");
+    checkPrefixIncrease(alloc, "2001:db8::ffff", 128, "2001:db8::1:0");
+    checkPrefixIncrease(alloc, "::", 128, "::1");
+    checkPrefixIncrease(alloc, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, "::");
+
+    // Check that /64 prefixes can be generated
+    checkPrefixIncrease(alloc, "2001:db8::", 64, "2001:db8:0:1::");
+
+    // Check that prefix length not divisible by 8 are working
+    checkPrefixIncrease(alloc, "2001:db8::", 128, "2001:db8::1");
+    checkPrefixIncrease(alloc, "2001:db8::", 127, "2001:db8::2");
+    checkPrefixIncrease(alloc, "2001:db8::", 126, "2001:db8::4");
+    checkPrefixIncrease(alloc, "2001:db8::", 125, "2001:db8::8");
+    checkPrefixIncrease(alloc, "2001:db8::", 124, "2001:db8::10");
+    checkPrefixIncrease(alloc, "2001:db8::", 123, "2001:db8::20");
+    checkPrefixIncrease(alloc, "2001:db8::", 122, "2001:db8::40");
+    checkPrefixIncrease(alloc, "2001:db8::", 121, "2001:db8::80");
+    checkPrefixIncrease(alloc, "2001:db8::", 120, "2001:db8::100");
+
+    // These are not really useful cases, because there are bits set
+    // int the last (128 - prefix_len) bits. Nevertheless, it shows
+    // that the algorithm is working even in such cases
+    checkPrefixIncrease(alloc, "2001:db8::1", 128, "2001:db8::2");
+    checkPrefixIncrease(alloc, "2001:db8::1", 127, "2001:db8::3");
+    checkPrefixIncrease(alloc, "2001:db8::1", 126, "2001:db8::5");
+    checkPrefixIncrease(alloc, "2001:db8::1", 125, "2001:db8::9");
+    checkPrefixIncrease(alloc, "2001:db8::1", 124, "2001:db8::11");
+    checkPrefixIncrease(alloc, "2001:db8::1", 123, "2001:db8::21");
+    checkPrefixIncrease(alloc, "2001:db8::1", 122, "2001:db8::41");
+    checkPrefixIncrease(alloc, "2001:db8::1", 121, "2001:db8::81");
+    checkPrefixIncrease(alloc, "2001:db8::1", 120, "2001:db8::101");
+
+    // Let's try out couple real life scenarios
+    checkPrefixIncrease(alloc, "2001:db8:1:abcd::", 64, "2001:db8:1:abce::");
+    checkPrefixIncrease(alloc, "2001:db8:1:abcd::", 60, "2001:db8:1:abdd::");
+    checkPrefixIncrease(alloc, "2001:db8:1:abcd::", 56, "2001:db8:1:accd::");
+    checkPrefixIncrease(alloc, "2001:db8:1:abcd::", 52, "2001:db8:1:bbcd::");
+
+    // And now let's try something over the top
+    checkPrefixIncrease(alloc, "::", 1, "8000::");
+}
 
 // This test verifies that the iterative allocator really walks over all addresses
 // in all pools in specified subnet. It also must not pick the same address twice
 // unless it runs out of pool space and must start over.
 TEST_F(AllocEngine6Test, IterativeAllocator_manyPools6) {
-    NakedAllocEngine::IterativeAllocator alloc;
+    NakedAllocEngine::IterativeAllocator alloc(Lease::TYPE_NA);
 
     // let's start from 2, as there is 2001:db8:1::10 - 2001:db8:1::20 pool already.
     for (int i = 2; i < 10; ++i) {
@@ -397,7 +717,7 @@ TEST_F(AllocEngine6Test, IterativeAllocator_manyPools6) {
         min << "2001:db8:1::" << hex << i*16 + 1;
         max << "2001:db8:1::" << hex << i*16 + 9;
 
-        Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, IOAddress(min.str()),
+        Pool6Ptr pool(new Pool6(Lease::TYPE_NA, IOAddress(min.str()),
                                 IOAddress(max.str())));
         subnet_->addPool(pool);
     }
@@ -410,11 +730,11 @@ TEST_F(AllocEngine6Test, IterativeAllocator_manyPools6) {
     int cnt = 0;
     while (++cnt) {
         IOAddress candidate = alloc.pickAddress(subnet_, duid_, IOAddress("::"));
-        EXPECT_TRUE(subnet_->inPool(candidate));
+        EXPECT_TRUE(subnet_->inPool(Lease::TYPE_NA, candidate));
 
         // One way to easily verify that the iterative allocator really works is
         // to uncomment the following line and observe its output that it
-        // covers all defined subnets.
+        // covers all defined pools.
         // cout << candidate.toText() << endl;
 
         if (generated_addrs.find(candidate) == generated_addrs.end()) {
@@ -450,14 +770,14 @@ TEST_F(AllocEngine6Test, smallPool6) {
 
     // Create configuration similar to other tests, but with a single address pool
     subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
-    pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, addr, addr)); // just a single address
+    pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address
     subnet_->addPool(pool_);
     cfg_mgr.addSubnet6(subnet_);
 
-    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                               IOAddress("::"),
-                                               false, false, "",
-                                               false, CalloutHandlePtr());
+    Lease6Ptr lease;
+    EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
+                    duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
+                    "", false, CalloutHandlePtr())));
 
     // Check that we got that single lease
     ASSERT_TRUE(lease);
@@ -465,10 +785,11 @@ TEST_F(AllocEngine6Test, smallPool6) {
     EXPECT_EQ("2001:db8:1::ad", lease->addr_.toText());
 
     // Do all checks on the lease
-    checkLease6(lease);
+    checkLease6(lease, Lease::TYPE_NA, 128);
 
     // Check that the lease is indeed in LeaseMgr
-    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
+    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
+                                                               lease->addr_);
     ASSERT_TRUE(from_mgr);
 
     // Now check that the lease in LeaseMgr has the same parameters
@@ -488,24 +809,24 @@ TEST_F(AllocEngine6Test, outOfAddresses6) {
 
     // Create configuration similar to other tests, but with a single address pool
     subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
-    pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, addr, addr)); // just a single address
+    pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address
     subnet_->addPool(pool_);
     cfg_mgr.addSubnet6(subnet_);
 
     // Just a different duid
     DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
     const uint32_t other_iaid = 3568;
-    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, other_duid, other_iaid,
+    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
                                501, 502, 503, 504, subnet_->getID(), 0));
     lease->cltt_ = time(NULL) - 10; // Allocated 10 seconds ago
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
     // There is just a single address in the pool and allocated it to someone
     // else, so the allocation should fail
-    Lease6Ptr lease2 = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                                IOAddress("::"),
-                                                false, false, "", false,
-                                                CalloutHandlePtr());
+    Lease6Ptr lease2;
+    EXPECT_NO_THROW(lease2 = expectOneLease(engine->allocateLeases6(subnet_,
+                    duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
+                    "", false, CalloutHandlePtr())));
     EXPECT_FALSE(lease2);
 }
 
@@ -521,14 +842,14 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
 
     // Create configuration similar to other tests, but with a single address pool
     subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
-    pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, addr, addr)); // just a single address
+    pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address
     subnet_->addPool(pool_);
     cfg_mgr.addSubnet6(subnet_);
 
     // Just a different duid
     DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
     const uint32_t other_iaid = 3568;
-    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, other_duid, other_iaid,
+    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
                                501, 502, 503, 504, subnet_->getID(), 0));
     lease->cltt_ = time(NULL) - 500; // Allocated 500 seconds ago
     lease->valid_lft_ = 495; // Lease was valid for 495 seconds
@@ -538,21 +859,21 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
     ASSERT_TRUE(lease->expired());
 
     // CASE 1: Asking for any address
-    lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
-                                     false, false, "",
-                                     true, CalloutHandlePtr());
+    EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
+                    duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false, "", true,
+                    CalloutHandlePtr())));
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     EXPECT_EQ(addr.toText(), lease->addr_.toText());
 
     // Do all checks on the lease (if subnet-id, preferred/valid times are ok etc.)
-    checkLease6(lease);
+    checkLease6(lease, Lease::TYPE_NA, 128);
 
     // CASE 2: Asking specifically for this address
-    lease = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                     IOAddress(addr.toText()),
-                                     false, false, "",
-                                     true, CalloutHandlePtr());
+    EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
+                    duid_, iaid_, addr, Lease::TYPE_NA, false, false, "",
+                    true, CalloutHandlePtr())));
+
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     EXPECT_EQ(addr.toText(), lease->addr_.toText());
@@ -570,7 +891,7 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
 
     // Create configuration similar to other tests, but with a single address pool
     subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
-    pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, addr, addr)); // just a single address
+    pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address
     subnet_->addPool(pool_);
     cfg_mgr.addSubnet6(subnet_);
 
@@ -578,24 +899,24 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
     DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
     const uint32_t other_iaid = 3568;
     const SubnetID other_subnetid = 999;
-    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, other_duid, other_iaid,
+    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
                                501, 502, 503, 504, other_subnetid, 0));
     lease->cltt_ = time(NULL) - 500; // Allocated 500 seconds ago
     lease->valid_lft_ = 495; // Lease was valid for 495 seconds
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
     // A client comes along, asking specifically for this address
-    lease = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                     IOAddress(addr.toText()),
-                                     false, false, "", false,
-                                     CalloutHandlePtr());
+    EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
+                    duid_, iaid_, addr, Lease::TYPE_NA, false, false, "",
+                    false, CalloutHandlePtr())));
 
     // Check that he got that single lease
     ASSERT_TRUE(lease);
     EXPECT_EQ(addr.toText(), lease->addr_.toText());
 
     // Check that the lease is indeed updated in LeaseMgr
-    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(addr);
+    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                               addr);
     ASSERT_TRUE(from_mgr);
 
     // Now check that the lease in LeaseMgr has the same parameters
@@ -604,13 +925,40 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
 
 // --- IPv4 ---
 
+// This test checks if the v4 Allocation Engine can be instantiated, parses
+// parameters string and allocators are created.
+TEST_F(AllocEngine4Test, constructor) {
+    boost::scoped_ptr<AllocEngine> x;
+
+    // Hashed and random allocators are not supported yet
+    ASSERT_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_HASHED, 5, false)),
+                 NotImplemented);
+    ASSERT_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_RANDOM, 5, false)),
+                 NotImplemented);
+
+    // Create V4 (ipv6=false) Allocation Engine that will try at most
+    // 100 attempts to pick up a lease
+    ASSERT_NO_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100,
+                                            false)));
+
+    // There should be V4 allocator
+    ASSERT_TRUE(x->getAllocator(Lease::TYPE_V4));
+
+    // Check that allocators for V6 stuff are not created
+    EXPECT_THROW(x->getAllocator(Lease::TYPE_NA), BadValue);
+    EXPECT_THROW(x->getAllocator(Lease::TYPE_TA), BadValue);
+    EXPECT_THROW(x->getAllocator(Lease::TYPE_PD), BadValue);
+}
+
+
 // This test checks if the simple IPv4 allocation can succeed
 TEST_F(AllocEngine4Test, simpleAlloc4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
-    Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+    Lease4Ptr lease = engine->allocateLease4(subnet_, clientid_, hwaddr_,
                                                IOAddress("0.0.0.0"),
                                                false, true,
                                                "somehost.example.com.",
@@ -636,10 +984,11 @@ TEST_F(AllocEngine4Test, simpleAlloc4) {
 // This test checks if the fake allocation (for DISCOVER) can succeed
 TEST_F(AllocEngine4Test, fakeAlloc4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
-    Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+    Lease4Ptr lease = engine->allocateLease4(subnet_, clientid_, hwaddr_,
                                                IOAddress("0.0.0.0"),
                                                false, true, "host.example.com.",
                                                true, CalloutHandlePtr(),
@@ -664,10 +1013,11 @@ TEST_F(AllocEngine4Test, fakeAlloc4) {
 // in pool and free) can succeed
 TEST_F(AllocEngine4Test, allocWithValidHint4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
-    Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+    Lease4Ptr lease = engine->allocateLease4(subnet_, clientid_, hwaddr_,
                                                IOAddress("192.0.2.105"),
                                                true, true, "host.example.com.",
                                                false, CalloutHandlePtr(),
@@ -697,7 +1047,8 @@ TEST_F(AllocEngine4Test, allocWithValidHint4) {
 // in pool, but is currently used) can succeed
 TEST_F(AllocEngine4Test, allocWithUsedHint4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     // Let's create a lease and put it in the LeaseMgr
@@ -711,7 +1062,7 @@ TEST_F(AllocEngine4Test, allocWithUsedHint4) {
     // Another client comes in and request an address that is in pool, but
     // unfortunately it is used already. The same address must not be allocated
     // twice.
-    Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+    Lease4Ptr lease = engine->allocateLease4(subnet_, clientid_, hwaddr_,
                                                IOAddress("192.0.2.106"),
                                                false, false, "",
                                                false, CalloutHandlePtr(),
@@ -745,13 +1096,14 @@ TEST_F(AllocEngine4Test, allocWithUsedHint4) {
 // can succeed. The invalid hint should be ignored completely.
 TEST_F(AllocEngine4Test, allocBogusHint4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     // Client would like to get a 3000::abc lease, which does not belong to any
     // supported lease. Allocation engine should ignore it and carry on
     // with the normal allocation
-    Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+    Lease4Ptr lease = engine->allocateLease4(subnet_, clientid_, hwaddr_,
                                                IOAddress("10.1.1.1"),
                                                false, false, "",
                                                false, CalloutHandlePtr(),
@@ -778,13 +1130,14 @@ TEST_F(AllocEngine4Test, allocBogusHint4) {
 
 
 // This test checks that NULL values are handled properly
-TEST_F(AllocEngine4Test, allocateAddress4Nulls) {
+TEST_F(AllocEngine4Test, allocateLease4Nulls) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     // Allocations without subnet are not allowed
-    Lease4Ptr lease = engine->allocateAddress4(SubnetPtr(), clientid_, hwaddr_,
+    Lease4Ptr lease = engine->allocateLease4(SubnetPtr(), clientid_, hwaddr_,
                                                IOAddress("0.0.0.0"),
                                                false, false, "",
                                                false, CalloutHandlePtr(),
@@ -792,7 +1145,7 @@ TEST_F(AllocEngine4Test, allocateAddress4Nulls) {
     EXPECT_FALSE(lease);
 
     // Allocations without HW address are not allowed
-    lease = engine->allocateAddress4(subnet_, clientid_, HWAddrPtr(),
+    lease = engine->allocateLease4(subnet_, clientid_, HWAddrPtr(),
                                      IOAddress("0.0.0.0"),
                                      false, false, "",
                                      false, CalloutHandlePtr(),
@@ -802,7 +1155,7 @@ TEST_F(AllocEngine4Test, allocateAddress4Nulls) {
 
     // Allocations without client-id are allowed
     clientid_ = ClientIdPtr();
-    lease = engine->allocateAddress4(subnet_, ClientIdPtr(), hwaddr_,
+    lease = engine->allocateLease4(subnet_, ClientIdPtr(), hwaddr_,
                                      IOAddress("0.0.0.0"),
                                      true, true, "myhost.example.com.",
                                      false, CalloutHandlePtr(),
@@ -829,12 +1182,12 @@ TEST_F(AllocEngine4Test, allocateAddress4Nulls) {
 // pool
 TEST_F(AllocEngine4Test, IterativeAllocator) {
     boost::scoped_ptr<NakedAllocEngine::Allocator>
-        alloc(new NakedAllocEngine::IterativeAllocator());
+        alloc(new NakedAllocEngine::IterativeAllocator(Lease::TYPE_V4));
 
     for (int i = 0; i < 1000; ++i) {
         IOAddress candidate = alloc->pickAddress(subnet_, clientid_,
                                                  IOAddress("0.0.0.0"));
-        EXPECT_TRUE(subnet_->inPool(candidate));
+        EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, candidate));
     }
 }
 
@@ -843,7 +1196,7 @@ TEST_F(AllocEngine4Test, IterativeAllocator) {
 // in all pools in specified subnet. It also must not pick the same address twice
 // unless it runs out of pool space and must start over.
 TEST_F(AllocEngine4Test, IterativeAllocator_manyPools4) {
-    NakedAllocEngine::IterativeAllocator alloc;
+    NakedAllocEngine::IterativeAllocator alloc(Lease::TYPE_V4);
 
     // Let's start from 2, as there is 2001:db8:1::10 - 2001:db8:1::20 pool already.
     for (int i = 2; i < 10; ++i) {
@@ -866,7 +1219,7 @@ TEST_F(AllocEngine4Test, IterativeAllocator_manyPools4) {
     int cnt = 0;
     while (++cnt) {
         IOAddress candidate = alloc.pickAddress(subnet_, clientid_, IOAddress("0.0.0.0"));
-        EXPECT_TRUE(subnet_->inPool(candidate));
+        EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, candidate));
 
         // One way to easily verify that the iterative allocator really works is
         // to uncomment the following line and observe its output that it
@@ -898,7 +1251,8 @@ TEST_F(AllocEngine4Test, IterativeAllocator_manyPools4) {
 // This test checks if really small pools are working
 TEST_F(AllocEngine4Test, smallPool4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     IOAddress addr("192.0.2.17");
@@ -911,7 +1265,7 @@ TEST_F(AllocEngine4Test, smallPool4) {
     subnet_->addPool(pool_);
     cfg_mgr.addSubnet4(subnet_);
 
-    Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+    Lease4Ptr lease = engine->allocateLease4(subnet_, clientid_, hwaddr_,
                                                IOAddress("0.0.0.0"),
                                                true, true, "host.example.com.",
                                                false, CalloutHandlePtr(),
@@ -940,7 +1294,8 @@ TEST_F(AllocEngine4Test, smallPool4) {
 // to find out a new lease fails.
 TEST_F(AllocEngine4Test, outOfAddresses4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     IOAddress addr("192.0.2.17");
@@ -966,7 +1321,7 @@ TEST_F(AllocEngine4Test, outOfAddresses4) {
     // There is just a single address in the pool and allocated it to someone
     // else, so the allocation should fail
 
-    Lease4Ptr lease2 = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+    Lease4Ptr lease2 = engine->allocateLease4(subnet_, clientid_, hwaddr_,
                                                 IOAddress("0.0.0.0"),
                                                 false, false, "",
                                                 false, CalloutHandlePtr(),
@@ -978,7 +1333,8 @@ TEST_F(AllocEngine4Test, outOfAddresses4) {
 // This test checks if an expired lease can be reused in DISCOVER (fake allocation)
 TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     IOAddress addr("192.0.2.15");
@@ -1008,7 +1364,7 @@ TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) {
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
     // CASE 1: Asking for any address
-    lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+    lease = engine->allocateLease4(subnet_, clientid_, hwaddr_,
                                      IOAddress("0.0.0.0"),
                                      false, false, "",
                                      true, CalloutHandlePtr(),
@@ -1027,7 +1383,7 @@ TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) {
     checkLease4(lease);
 
     // CASE 2: Asking specifically for this address
-    lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+    lease = engine->allocateLease4(subnet_, clientid_, hwaddr_,
                                      IOAddress(addr.toText()),
                                      false, false, "",
                                      true, CalloutHandlePtr(),
@@ -1045,7 +1401,8 @@ TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) {
 // This test checks if an expired lease can be reused in REQUEST (actual allocation)
 TEST_F(AllocEngine4Test, requestReuseExpiredLease4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     IOAddress addr("192.0.2.105");
@@ -1067,7 +1424,7 @@ TEST_F(AllocEngine4Test, requestReuseExpiredLease4) {
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
     // A client comes along, asking specifically for this address
-    lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+    lease = engine->allocateLease4(subnet_, clientid_, hwaddr_,
                                      IOAddress(addr.toText()),
                                      false, true, "host.example.com.",
                                      false, CalloutHandlePtr(),
@@ -1098,7 +1455,8 @@ TEST_F(AllocEngine4Test, renewLease4) {
     boost::scoped_ptr<AllocEngine> engine;
     CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
 
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     IOAddress addr("192.0.2.102");
@@ -1119,7 +1477,7 @@ TEST_F(AllocEngine4Test, renewLease4) {
     // renew it.
     ASSERT_FALSE(lease->expired());
     lease = engine->renewLease4(subnet_, clientid_, hwaddr_, true,
-                                true, "host.example.com.", lease, 
+                                true, "host.example.com.", lease,
                                 callout_handle, false);
     // Check that he got that single lease
     ASSERT_TRUE(lease);
@@ -1262,17 +1620,19 @@ TEST_F(HookAllocEngine6Test, lease6_select) {
 
     CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
 
-    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
-                                               false, false, "",
-                                               false, callout_handle);
+    Lease6Ptr lease;
+    EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
+                    duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
+                    "", false, callout_handle)));
     // Check that we got a lease
     ASSERT_TRUE(lease);
 
     // Do all checks on the lease
-    checkLease6(lease);
+    checkLease6(lease, Lease::TYPE_NA, 128);
 
     // Check that the lease is indeed in LeaseMgr
-    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
+    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
+                                                               lease->addr_);
     ASSERT_TRUE(from_mgr);
 
     // Check that callouts were indeed called
@@ -1326,14 +1686,15 @@ TEST_F(HookAllocEngine6Test, change_lease6_select) {
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "lease6_select", lease6_select_different_callout));
 
-    // Normally, dhcpv6_srv would passed the handle when calling allocateAddress6,
+    // Normally, dhcpv6_srv would passed the handle when calling allocateLeases6,
     // but in tests we need to create it on our own.
     CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
 
-    // Call allocateAddress6. Callouts should be triggered here.
-    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
-                                               false, false, "",
-                                               false, callout_handle);
+    // Call allocateLeases6. Callouts should be triggered here.
+    Lease6Ptr lease;
+    EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
+                    duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
+                    "", false, callout_handle)));
     // Check that we got a lease
     ASSERT_TRUE(lease);
 
@@ -1345,7 +1706,8 @@ TEST_F(HookAllocEngine6Test, change_lease6_select) {
     EXPECT_EQ(valid_override_, lease->valid_lft_);
 
     // Now check if the lease is in the database
-    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
+    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
+                                                               lease->addr_);
     ASSERT_TRUE(from_mgr);
 
     // Check if values in the database are overridden
@@ -1471,7 +1833,8 @@ TEST_F(HookAllocEngine4Test, lease4_select) {
 
     // Create allocation engine (hook names are registered in its ctor)
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     // Initialize Hooks Manager
@@ -1484,7 +1847,7 @@ TEST_F(HookAllocEngine4Test, lease4_select) {
 
     CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
 
-    Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+    Lease4Ptr lease = engine->allocateLease4(subnet_, clientid_, hwaddr_,
                                                IOAddress("0.0.0.0"),
                                                false, false, "",
                                                false, callout_handle,
@@ -1534,7 +1897,8 @@ TEST_F(HookAllocEngine4Test, change_lease4_select) {
 
     // Create allocation engine (hook names are registered in its ctor)
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     // Initialize Hooks Manager
@@ -1545,16 +1909,16 @@ TEST_F(HookAllocEngine4Test, change_lease4_select) {
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "lease4_select", lease4_select_different_callout));
 
-    // Normally, dhcpv4_srv would passed the handle when calling allocateAddress4,
+    // Normally, dhcpv4_srv would passed the handle when calling allocateLease4,
     // but in tests we need to create it on our own.
     CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
 
-    // Call allocateAddress4. Callouts should be triggered here.
-    Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
-                                               IOAddress("0.0.0.0"),
-                                               false, false, "",
-                                               false, callout_handle,
-                                               old_lease_);
+    // Call allocateLease4. Callouts should be triggered here.
+    Lease4Ptr lease = engine->allocateLease4(subnet_, clientid_, hwaddr_,
+                                             IOAddress("0.0.0.0"),
+                                             false, false, "",
+                                             false, callout_handle,
+                                             old_lease_);
     // Check that we got a lease
     ASSERT_TRUE(lease);
 
@@ -1575,6 +1939,4 @@ TEST_F(HookAllocEngine4Test, change_lease4_select) {
     EXPECT_EQ(valid_override_, from_mgr->valid_lft_);
 }
 
-
-
 }; // End of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
index 38d2f0a..94f78c3 100644
--- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
@@ -579,20 +579,55 @@ TEST_F(CfgMgrTest, optionSpace6) {
 TEST_F(CfgMgrTest, addActiveIface) {
     CfgMgr& cfg_mgr = CfgMgr::instance();
 
-    cfg_mgr.addActiveIface("eth0");
-    cfg_mgr.addActiveIface("eth1");
+    EXPECT_NO_THROW(cfg_mgr.addActiveIface("eth0"));
+    EXPECT_NO_THROW(cfg_mgr.addActiveIface("eth1"));
 
     EXPECT_TRUE(cfg_mgr.isActiveIface("eth0"));
     EXPECT_TRUE(cfg_mgr.isActiveIface("eth1"));
     EXPECT_FALSE(cfg_mgr.isActiveIface("eth2"));
 
-    cfg_mgr.deleteActiveIfaces();
+    EXPECT_NO_THROW(cfg_mgr.deleteActiveIfaces());
 
     EXPECT_FALSE(cfg_mgr.isActiveIface("eth0"));
     EXPECT_FALSE(cfg_mgr.isActiveIface("eth1"));
     EXPECT_FALSE(cfg_mgr.isActiveIface("eth2"));
 }
 
+
+// This test verifies that it is possible to specify interfaces that server
+// should listen on.
+TEST_F(CfgMgrTest, addUnicastAddresses) {
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+
+    EXPECT_NO_THROW(cfg_mgr.addActiveIface("eth1/2001:db8::1"));
+    EXPECT_NO_THROW(cfg_mgr.addActiveIface("eth2/2001:db8::2"));
+    EXPECT_NO_THROW(cfg_mgr.addActiveIface("eth3"));
+
+    EXPECT_TRUE(cfg_mgr.isActiveIface("eth1"));
+    EXPECT_TRUE(cfg_mgr.isActiveIface("eth2"));
+    EXPECT_TRUE(cfg_mgr.isActiveIface("eth3"));
+    EXPECT_FALSE(cfg_mgr.isActiveIface("eth4"));
+
+    ASSERT_TRUE(cfg_mgr.getUnicast("eth1"));
+    EXPECT_EQ("2001:db8::1", cfg_mgr.getUnicast("eth1")->toText());
+    EXPECT_EQ("2001:db8::2", cfg_mgr.getUnicast("eth2")->toText());
+    EXPECT_FALSE(cfg_mgr.getUnicast("eth3"));
+    EXPECT_FALSE(cfg_mgr.getUnicast("eth4"));
+
+    EXPECT_NO_THROW(cfg_mgr.deleteActiveIfaces());
+
+    EXPECT_FALSE(cfg_mgr.isActiveIface("eth1"));
+    EXPECT_FALSE(cfg_mgr.isActiveIface("eth2"));
+    EXPECT_FALSE(cfg_mgr.isActiveIface("eth3"));
+    EXPECT_FALSE(cfg_mgr.isActiveIface("eth4"));
+
+    ASSERT_FALSE(cfg_mgr.getUnicast("eth1"));
+    ASSERT_FALSE(cfg_mgr.getUnicast("eth2"));
+    EXPECT_FALSE(cfg_mgr.getUnicast("eth3"));
+    EXPECT_FALSE(cfg_mgr.getUnicast("eth4"));
+}
+
+
 // This test verifies that it is possible to set the flag which configures the
 // server to listen on all interfaces.
 TEST_F(CfgMgrTest, activateAllIfaces) {
@@ -618,6 +653,27 @@ TEST_F(CfgMgrTest, activateAllIfaces) {
     EXPECT_FALSE(cfg_mgr.isActiveIface("eth2"));
 }
 
+/// @todo Add unit-tests for testing:
+/// - addActiveIface() with invalid interface name
+/// - addActiveIface() with the same interface twice
+/// - addActiveIface() with a bogus address
+///
+/// This is somewhat tricky. Care should be taken here, because it is rather
+/// difficult to decide if interface name is valid or not. Some servers, e.g.
+/// dibbler, allow to specify interface names that are not currently present in
+/// the system. The server accepts them, but upon discovering that they are
+/// yet available (for different definitions of not being available), adds
+/// the to to-be-activated list.
+///
+/// Cases covered by dibbler are:
+/// - missing interface (e.g. PPP connection that is not established yet)
+/// - downed interface (no link local address, no way to open sockets)
+/// - up, but not running interface (wifi up, but not associated)
+/// - tentative addresses (interface up and running, but DAD procedure is
+///   still in progress)
+/// - weird interfaces without link-local addresses (don't ask, 6rd tunnels
+///   look weird to me as well)
+
 // No specific tests for getSubnet6. That method (2 overloaded versions) is tested
 // in Dhcpv6SrvTest.selectSubnetAddr and Dhcpv6SrvTest.selectSubnetIface
 // (see src/bin/dhcp6/tests/dhcp6_srv_unittest.cc)
diff --git a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
index 62f901c..928be23 100644
--- a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
+++ b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
@@ -250,6 +250,16 @@ TEST_F(DhcpParserTest, interfaceListParserTest) {
     EXPECT_TRUE(cfg_mgr.isActiveIface("eth2"));
 }
 
+// Checks whether option space can be detected as vendor-id
+TEST_F(DhcpParserTest, vendorOptionSpace) {
+    EXPECT_EQ(0, SubnetConfigParser::optionSpaceToVendorId(""));
+    EXPECT_EQ(0, SubnetConfigParser::optionSpaceToVendorId("dhcp4"));
+    EXPECT_EQ(0, SubnetConfigParser::optionSpaceToVendorId("vendor-"));
+    EXPECT_EQ(1, SubnetConfigParser::optionSpaceToVendorId("vendor-1"));
+    EXPECT_EQ(4491, SubnetConfigParser::optionSpaceToVendorId("vendor-4491"));
+    EXPECT_EQ(12345678, SubnetConfigParser::optionSpaceToVendorId("vendor-12345678"));
+}
+
 /// @brief Test Implementation of abstract OptionDataParser class. Allows
 /// testing basic option parsing.
 class UtestOptionDataParser : public OptionDataParser {
@@ -714,9 +724,8 @@ public:
     template<typename ContainerType, typename ValueType>
     void checkValueEq(const boost::shared_ptr<ContainerType>& ref_values,
                       const boost::shared_ptr<ContainerType>& values) {
-        ValueType param;
-        ASSERT_NO_THROW(param = values->getParam("foo"));
-        EXPECT_EQ(ref_values->getParam("foo"), param);
+        ASSERT_NO_THROW(values->getParam("foo"));
+        EXPECT_EQ(ref_values->getParam("foo"), values->getParam("foo"));
     }
 
     /// @brief Check that the storages of the specific type hold different
@@ -734,9 +743,8 @@ public:
     template<typename ContainerType, typename ValueType>
     void checkValueNeq(const boost::shared_ptr<ContainerType>& ref_values,
                        const boost::shared_ptr<ContainerType>& values) {
-        ValueType param;
-        ASSERT_NO_THROW(param = values->getParam("foo"));
-        EXPECT_NE(ref_values->getParam("foo"), param);
+        ASSERT_NO_THROW(values->getParam("foo"));
+        EXPECT_NE(ref_values->getParam("foo"), values->getParam("foo"));
     }
 
     /// @brief Check that option definition storage in the context holds
diff --git a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
index bdbc168..ece3e59 100644
--- a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
@@ -16,6 +16,8 @@
 
 #include <asiolink/io_address.h>
 #include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/memfile_lease_mgr.h>
+#include <dhcpsrv/tests/test_utils.h>
 
 #include <gtest/gtest.h>
 
@@ -28,6 +30,7 @@ using namespace std;
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
+using namespace isc::dhcp::test;
 
 // This is a concrete implementation of a Lease database.  It does not do
 // anything useful and is used for abstract LeaseMgr class testing.
@@ -128,29 +131,32 @@ public:
     /// @param addr address of the searched lease
     ///
     /// @return smart pointer to the lease (or NULL if a lease is not found)
-    virtual Lease6Ptr getLease6(const isc::asiolink::IOAddress&) const {
+    virtual Lease6Ptr getLease6(Lease::Type /* not used yet */,
+                                const isc::asiolink::IOAddress&) const {
         return (Lease6Ptr());
     }
 
     /// @brief Returns existing IPv6 lease for a given DUID+IA combination
     ///
-    /// @param duid client DUID
-    /// @param iaid IA identifier
+    /// @param duid ignored
+    /// @param iaid ignored
     ///
-    /// @return collection of IPv6 leases
-    virtual Lease6Collection getLease6(const DUID&, uint32_t) const {
-        return (Lease6Collection());
+    /// @return whatever is set in leases6_ field
+    virtual Lease6Collection getLeases6(Lease::Type /* not used yet */,
+                                        const DUID&, uint32_t) const {
+        return (leases6_);
     }
 
-    /// @brief Returns existing IPv6 lease for a given DUID+IA combination
+    /// @brief Returns existing IPv6 lease for a given DUID+IA+subnet-id combination
     ///
-    /// @param duid client DUID
-    /// @param iaid IA identifier
-    /// @param subnet_id identifier of the subnet the lease must belong to
+    /// @param duid ignored
+    /// @param iaid ignored
+    /// @param subnet_id ignored
     ///
-    /// @return smart pointer to the lease (or NULL if a lease is not found)
-    virtual Lease6Ptr getLease6(const DUID&, uint32_t, SubnetID) const {
-        return (Lease6Ptr());
+    /// @return whatever is set in leases6_ field
+    virtual Lease6Collection getLeases6(Lease::Type /* not used yet */,
+                                        const DUID&, uint32_t, SubnetID) const {
+        return (leases6_);
     }
 
     /// @brief Updates IPv4 lease.
@@ -217,6 +223,17 @@ public:
     /// @brief Rollback transactions
     virtual void rollback() {
     }
+
+    // We need to use it in ConcreteLeaseMgr
+    using LeaseMgr::getLease6;
+
+    Lease6Collection leases6_; ///< getLease6 methods return this as is
+};
+
+class LeaseMgrTest : public GenericLeaseMgrTest {
+public:
+    LeaseMgrTest() {
+    }
 };
 
 namespace {
@@ -236,7 +253,7 @@ const uint32_t IAID = 7;
 ///
 /// This test checks if the LeaseMgr can be instantiated and that it
 /// parses parameters string properly.
-TEST(LeaseMgr, getParameter) {
+TEST_F(LeaseMgrTest, getParameter) {
 
     LeaseMgr::ParameterMap pmap;
     pmap[std::string("param1")] = std::string("value1");
@@ -248,6 +265,44 @@ TEST(LeaseMgr, getParameter) {
     EXPECT_THROW(leasemgr.getParameter("param3"), BadValue);
 }
 
+// This test checks if getLease6() method is working properly for 0 (NULL),
+// 1 (return the lease) and more than 1 leases (throw).
+TEST_F(LeaseMgrTest, getLease6) {
+
+    LeaseMgr::ParameterMap pmap;
+    boost::scoped_ptr<ConcreteLeaseMgr> mgr(new ConcreteLeaseMgr(pmap));
+
+    vector<Lease6Ptr> leases = createLeases6();
+
+    mgr->leases6_.clear();
+    // For no leases, the function should return NULL pointer
+    Lease6Ptr lease;
+
+    // the getLease6() is calling getLeases6(), which is a dummy. It returns
+    // whatever is there in leases6_ field.
+    EXPECT_NO_THROW(lease = mgr->getLease6(leasetype6_[1], *leases[1]->duid_,
+                                           leases[1]->iaid_,
+                                           leases[1]->subnet_id_));
+    EXPECT_TRUE(Lease6Ptr() == lease);
+
+    // For a single lease, the function should return that lease
+    mgr->leases6_.push_back(leases[1]);
+    EXPECT_NO_THROW(lease = mgr->getLease6(leasetype6_[1], *leases[1]->duid_,
+                                           leases[1]->iaid_,
+                                           leases[1]->subnet_id_));
+    EXPECT_TRUE(lease);
+
+    EXPECT_NO_THROW(detailCompareLease(lease, leases[1]));
+
+    // Add one more lease. There are 2 now. It should throw
+    mgr->leases6_.push_back(leases[2]);
+
+    EXPECT_THROW(lease = mgr->getLease6(leasetype6_[1], *leases[1]->duid_,
+                                        leases[1]->iaid_,
+                                        leases[1]->subnet_id_),
+                 MultipleRecords);
+}
+
 // There's no point in calling any other methods in LeaseMgr, as they
 // are purely virtual, so we would only call ConcreteLeaseMgr methods.
 // Those methods are just stubs that do not return anything.
@@ -259,14 +314,20 @@ TEST(LeaseMgr, getParameter) {
 TEST(Lease4, constructor) {
 
     // Random values for the tests
+    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
     std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
 
+    const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
     std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
     ClientId clientid(clientid_vec);
 
     // ...and a time
     const time_t current_time = time(NULL);
 
+    // Other random constants.
+    const uint32_t SUBNET_ID = 42;
+    const uint32_t VALID_LIFETIME = 500;
+
     // We want to check that various addresses work, so let's iterate over
     // these.
     const uint32_t ADDRESS[] = {
@@ -277,8 +338,9 @@ TEST(Lease4, constructor) {
 
         // Create the lease
         Lease4 lease(ADDRESS[i], HWADDR, sizeof(HWADDR),
-                     CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
-                     SUBNET_ID);
+                     CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0,
+                     current_time, SUBNET_ID, true, true,
+                     "hostname.example.com.");
 
         EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_));
         EXPECT_EQ(0, lease.ext_);
@@ -290,9 +352,9 @@ TEST(Lease4, constructor) {
         EXPECT_EQ(current_time, lease.cltt_);
         EXPECT_EQ(SUBNET_ID, lease.subnet_id_);
         EXPECT_FALSE(lease.fixed_);
-        EXPECT_TRUE(lease.hostname_.empty());
-        EXPECT_FALSE(lease.fqdn_fwd_);
-        EXPECT_FALSE(lease.fqdn_rev_);
+        EXPECT_EQ("hostname.example.com.", lease.hostname_);
+        EXPECT_TRUE(lease.fqdn_fwd_);
+        EXPECT_TRUE(lease.fqdn_rev_);
         EXPECT_TRUE(lease.comments_.empty());
     }
 }
@@ -301,14 +363,20 @@ TEST(Lease4, constructor) {
 TEST(Lease4, copyConstructor) {
 
     // Random values for the tests
+    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
     std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
 
+    const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
     std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
     ClientId clientid(clientid_vec);
 
     // ...and a time
     const time_t current_time = time(NULL);
 
+    // Other random constants.
+    const uint32_t SUBNET_ID = 42;
+    const uint32_t VALID_LIFETIME = 500;
+
     // Create the lease
     Lease4 lease(0xffffffff, HWADDR, sizeof(HWADDR),
                  CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
@@ -329,14 +397,20 @@ TEST(Lease4, copyConstructor) {
 TEST(Lease4, operatorAssign) {
 
     // Random values for the tests
+    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
     std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
 
+    const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
     std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
     ClientId clientid(clientid_vec);
 
     // ...and a time
     const time_t current_time = time(NULL);
 
+    // Other random constants.
+    const uint32_t SUBNET_ID = 42;
+    const uint32_t VALID_LIFETIME = 500;
+
     // Create the lease
     Lease4 lease(0xffffffff, HWADDR, sizeof(HWADDR),
                  CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
@@ -407,11 +481,14 @@ TEST(Lease4, operatorEquals) {
 
     // Random values for the tests
     const uint32_t ADDRESS = 0x01020304;
+    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
     std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
     const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
     std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
     ClientId clientid(clientid_vec);
     const time_t current_time = time(NULL);
+    const uint32_t SUBNET_ID = 42;
+    const uint32_t VALID_LIFETIME = 500;
 
     // Check when the leases are equal.
     Lease4 lease1(ADDRESS, HWADDR, sizeof(HWADDR),
@@ -530,7 +607,7 @@ TEST(Lease4, operatorEquals) {
 
 // Lease6 is also defined in lease_mgr.h, so is tested in this file as well.
 // This test checks if the Lease6 structure can be instantiated correctly
-TEST(Lease6, Lease6Constructor) {
+TEST(Lease6, Lease6ConstructorDefault) {
 
     // check a variety of addresses with different bits set.
     const char* ADDRESS[] = {
@@ -543,30 +620,81 @@ TEST(Lease6, Lease6Constructor) {
     // Other values
     uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
     DuidPtr duid(new DUID(llt, sizeof(llt)));
+    uint32_t iaid = 7;      // Just a number
     SubnetID subnet_id = 8; // Just another number
 
     for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
         IOAddress addr(ADDRESS[i]);
-        Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr,
-                               duid, IAID, 100, 200, 50, 80,
+        Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr,
+                               duid, iaid, 100, 200, 50, 80,
                                subnet_id));
 
         EXPECT_TRUE(lease->addr_ == addr);
         EXPECT_TRUE(*lease->duid_ == *duid);
-        EXPECT_TRUE(lease->iaid_ == IAID);
+        EXPECT_TRUE(lease->iaid_ == iaid);
         EXPECT_TRUE(lease->subnet_id_ == subnet_id);
-        EXPECT_TRUE(lease->type_ == Lease6::LEASE_IA_NA);
+        EXPECT_TRUE(lease->type_ == Lease::TYPE_NA);
         EXPECT_TRUE(lease->preferred_lft_ == 100);
         EXPECT_TRUE(lease->valid_lft_ == 200);
         EXPECT_TRUE(lease->t1_ == 50);
         EXPECT_TRUE(lease->t2_ == 80);
+        EXPECT_FALSE(lease->fqdn_fwd_);
+        EXPECT_FALSE(lease->fqdn_rev_);
+        EXPECT_TRUE(lease->hostname_.empty());
+
     }
 
     // Lease6 must be instantiated with a DUID, not with NULL pointer
     IOAddress addr(ADDRESS[0]);
     Lease6Ptr lease2;
-    EXPECT_THROW(lease2.reset(new Lease6(Lease6::LEASE_IA_NA, addr,
-                                         DuidPtr(), IAID, 100, 200, 50, 80,
+    EXPECT_THROW(lease2.reset(new Lease6(Lease::TYPE_NA, addr,
+                                         DuidPtr(), iaid, 100, 200, 50, 80,
+                                         subnet_id)), InvalidOperation);
+}
+
+// This test verifies that the Lease6 constructor which accepts FQDN data,
+// sets the data correctly for the lease.
+TEST(Lease6, Lease6ConstructorWithFQDN) {
+
+    // check a variety of addresses with different bits set.
+    const char* ADDRESS[] = {
+        "::", "::1", "2001:db8:1::456",
+        "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+        "8000::", "8000::1",
+        "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+    };
+
+    // Other values
+    uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+    DuidPtr duid(new DUID(llt, sizeof(llt)));
+    uint32_t iaid = 7;      // Just a number
+    SubnetID subnet_id = 8; // Just another number
+
+    for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
+        IOAddress addr(ADDRESS[i]);
+        Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr,
+                               duid, iaid, 100, 200, 50, 80, subnet_id,
+                                   true, true, "host.example.com."));
+
+        EXPECT_TRUE(lease->addr_ == addr);
+        EXPECT_TRUE(*lease->duid_ == *duid);
+        EXPECT_TRUE(lease->iaid_ == iaid);
+        EXPECT_TRUE(lease->subnet_id_ == subnet_id);
+        EXPECT_TRUE(lease->type_ == Lease::TYPE_NA);
+        EXPECT_TRUE(lease->preferred_lft_ == 100);
+        EXPECT_TRUE(lease->valid_lft_ == 200);
+        EXPECT_TRUE(lease->t1_ == 50);
+        EXPECT_TRUE(lease->t2_ == 80);
+        EXPECT_TRUE(lease->fqdn_fwd_);
+        EXPECT_TRUE(lease->fqdn_rev_);
+        EXPECT_EQ("host.example.com.", lease->hostname_);
+    }
+
+    // Lease6 must be instantiated with a DUID, not with NULL pointer
+    IOAddress addr(ADDRESS[0]);
+    Lease6Ptr lease2;
+    EXPECT_THROW(lease2.reset(new Lease6(Lease::TYPE_NA, addr,
+                                         DuidPtr(), iaid, 100, 200, 50, 80,
                                          subnet_id)), InvalidOperation);
 }
 
@@ -578,15 +706,15 @@ TEST(Lease6, matches) {
     uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
     DuidPtr duid(new DUID(llt, sizeof(llt)));
 
-    Lease6 lease1(Lease6::LEASE_IA_NA, IOAddress("2001:db8:1::1"), duid,
-                                                 IAID, 100, 200, 50, 80,
-                                                 SUBNET_ID);
+    Lease6 lease1(Lease6::TYPE_NA, IOAddress("2001:db8:1::1"), duid,
+                  IAID, 100, 200, 50, 80,
+                  SUBNET_ID);
     lease1.hostname_ = "lease1.example.com.";
     lease1.fqdn_fwd_ = true;
     lease1.fqdn_rev_ = true;
-    Lease6 lease2(Lease6::LEASE_IA_NA, IOAddress("2001:db8:1::1"), duid,
-                                                 IAID, 200, 300, 90, 70,
-                                                 SUBNET_ID);
+    Lease6 lease2(Lease6::TYPE_NA, IOAddress("2001:db8:1::1"), duid,
+                  IAID, 200, 300, 90, 70,
+                  SUBNET_ID);
     lease2.hostname_ = "lease1.example.com.";
     lease2.fqdn_fwd_ = false;
     lease2.fqdn_rev_ = true;
@@ -602,7 +730,7 @@ TEST(Lease6, matches) {
     lease1.addr_ = lease2.addr_;
 
     // Modify lease type.
-    lease1.type_ = Lease6::LEASE_IA_TA;
+    lease1.type_ = Lease6::TYPE_TA;
     EXPECT_FALSE(lease1.matches(lease2));
     lease1.type_ = lease2.type_;
 
@@ -629,7 +757,7 @@ TEST(Lease6, matches) {
 /// Checks that the operator==() correctly compares two leases for equality.
 /// As operator!=() is also defined for this class, every check on operator==()
 /// is followed by the reverse check on operator!=().
-TEST(Lease6, operatorEquals) {
+TEST(Lease6, OperatorEquals) {
 
     // check a variety of addresses with different bits set.
     const IOAddress addr("2001:db8:1::456");
@@ -639,9 +767,9 @@ TEST(Lease6, operatorEquals) {
     SubnetID subnet_id = 8; // just another number
 
     // Check for equality.
-    Lease6 lease1(Lease6::LEASE_IA_NA, addr, duid, iaid, 100, 200, 50, 80,
+    Lease6 lease1(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80,
                                subnet_id);
-    Lease6 lease2(Lease6::LEASE_IA_NA, addr, duid, iaid, 100, 200, 50, 80,
+    Lease6 lease2(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80,
                                subnet_id);
 
     // cltt_ constructs with time(NULL), make sure they are always equal
@@ -659,7 +787,7 @@ TEST(Lease6, operatorEquals) {
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
 
-    lease1.type_ = Lease6::LEASE_IA_PD;
+    lease1.type_ = Lease::TYPE_PD;
     EXPECT_FALSE(lease1 == lease2);
     EXPECT_TRUE(lease1 != lease2);
     lease1.type_ = lease2.type_;
@@ -774,7 +902,7 @@ TEST(Lease6, Lease6Expired) {
     const DuidPtr duid(new DUID(duid_array, sizeof(duid_array)));
     const uint32_t iaid = 7;        // Just a number
     const SubnetID subnet_id = 8;   // Just another number
-    Lease6 lease(Lease6::LEASE_IA_NA, addr, duid, iaid, 100, 200, 50, 80,
+    Lease6 lease(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80,
                                subnet_id);
 
     // Case 1: a second before expiration
diff --git a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
index 08186dc..a995f1a 100644
--- a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
@@ -18,7 +18,7 @@
 #include <dhcp/duid.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/memfile_lease_mgr.h>
-
+#include <dhcpsrv/tests/test_utils.h>
 #include <gtest/gtest.h>
 
 #include <iostream>
@@ -28,10 +28,11 @@ using namespace std;
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
+using namespace isc::dhcp::test;
 
 namespace {
 // empty class for now, but may be extended once Addr6 becomes bigger
-class MemfileLeaseMgrTest : public ::testing::Test {
+class MemfileLeaseMgrTest : public GenericLeaseMgrTest {
 public:
     MemfileLeaseMgrTest() {
     }
@@ -59,7 +60,7 @@ TEST_F(MemfileLeaseMgrTest, getTypeAndName) {
 // Checks that adding/getting/deleting a Lease6 object works.
 TEST_F(MemfileLeaseMgrTest, addGetDelete6) {
     const LeaseMgr::ParameterMap pmap;  // Empty parameter map
-    boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr(new Memfile_LeaseMgr(pmap));
+    boost::scoped_ptr<LeaseMgr> lease_mgr(new Memfile_LeaseMgr(pmap));
 
     IOAddress addr("2001:db8:1::456");
 
@@ -70,7 +71,7 @@ TEST_F(MemfileLeaseMgrTest, addGetDelete6) {
 
     SubnetID subnet_id = 8; // just another number
 
-    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr,
+    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr,
                                duid, iaid, 100, 200, 50, 80,
                                subnet_id));
 
@@ -79,10 +80,12 @@ TEST_F(MemfileLeaseMgrTest, addGetDelete6) {
     // should not be allowed to add a second lease with the same address
     EXPECT_FALSE(lease_mgr->addLease(lease));
 
-    Lease6Ptr x = lease_mgr->getLease6(IOAddress("2001:db8:1::234"));
+    Lease6Ptr x = lease_mgr->getLease6(Lease::TYPE_NA,
+                                       IOAddress("2001:db8:1::234"));
     EXPECT_EQ(Lease6Ptr(), x);
 
-    x = lease_mgr->getLease6(IOAddress("2001:db8:1::456"));
+    x = lease_mgr->getLease6(Lease::TYPE_NA,
+                             IOAddress("2001:db8:1::456"));
     ASSERT_TRUE(x);
 
     EXPECT_EQ(x->addr_.toText(), addr.toText());
@@ -92,14 +95,15 @@ TEST_F(MemfileLeaseMgrTest, addGetDelete6) {
 
     // These are not important from lease management perspective, but
     // let's check them anyway.
-    EXPECT_EQ(x->type_, Lease6::LEASE_IA_NA);
+    EXPECT_EQ(x->type_, Lease::TYPE_NA);
     EXPECT_EQ(x->preferred_lft_, 100);
     EXPECT_EQ(x->valid_lft_, 200);
     EXPECT_EQ(x->t1_, 50);
     EXPECT_EQ(x->t2_, 80);
 
     // Test getLease6(duid, iaid, subnet_id) - positive case
-    Lease6Ptr y = lease_mgr->getLease6(*duid, iaid, subnet_id);
+    Lease6Ptr y = lease_mgr->getLease6(Lease::TYPE_NA, *duid, iaid,
+                                       subnet_id);
     ASSERT_TRUE(y);
     EXPECT_TRUE(*y->duid_ == *duid);
     EXPECT_EQ(y->iaid_, iaid);
@@ -107,16 +111,19 @@ TEST_F(MemfileLeaseMgrTest, addGetDelete6) {
 
     // Test getLease6(duid, iaid, subnet_id) - wrong iaid
     uint32_t invalid_iaid = 9; // no such iaid
-    y = lease_mgr->getLease6(*duid, invalid_iaid, subnet_id);
+    y = lease_mgr->getLease6(Lease::TYPE_NA, *duid, invalid_iaid,
+                             subnet_id);
     EXPECT_FALSE(y);
 
     uint32_t invalid_subnet_id = 999;
-    y = lease_mgr->getLease6(*duid, iaid, invalid_subnet_id);
+    y = lease_mgr->getLease6(Lease::TYPE_NA, *duid, iaid,
+                             invalid_subnet_id);
     EXPECT_FALSE(y);
 
     // truncated duid
     DuidPtr invalid_duid(new DUID(llt, sizeof(llt) - 1));
-    y = lease_mgr->getLease6(*invalid_duid, iaid, subnet_id);
+    y = lease_mgr->getLease6(Lease::TYPE_NA, *invalid_duid, iaid,
+                             subnet_id);
     EXPECT_FALSE(y);
 
     // should return false - there's no such address
@@ -126,10 +133,87 @@ TEST_F(MemfileLeaseMgrTest, addGetDelete6) {
     EXPECT_TRUE(lease_mgr->deleteLease(IOAddress("2001:db8:1::456")));
 
     // after the lease is deleted, it should really be gone
-    x = lease_mgr->getLease6(IOAddress("2001:db8:1::456"));
+    x = lease_mgr->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::456"));
     EXPECT_EQ(Lease6Ptr(), x);
 }
 
-// TODO: Write more memfile tests
+// @todo Write more memfile tests
+
+// Simple test about lease4 retrieval through client id method
+TEST_F(MemfileLeaseMgrTest, getLease4ClientId) {
+    const LeaseMgr::ParameterMap pmap;
+    boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr(new Memfile_LeaseMgr(pmap));
+    // Let's initialize a specific lease ...
+    Lease4Ptr lease = initializeLease4(straddress4_[1]);
+    EXPECT_TRUE(lease_mgr->addLease(lease));
+    Lease4Collection returned = lease_mgr->getLease4(*lease->client_id_);
+
+    ASSERT_EQ(1, returned.size());
+    // We should retrieve our lease...
+    detailCompareLease(lease, *returned.begin());
+    lease = initializeLease4(straddress4_[2]);
+    returned = lease_mgr->getLease4(*lease->client_id_);
+
+    ASSERT_EQ(0, returned.size());
+}
+
+// Checks that lease4 retrieval client id is null is working
+TEST_F(MemfileLeaseMgrTest, getLease4NullClientId) {
+    const LeaseMgr::ParameterMap pmap;
+    boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr(new Memfile_LeaseMgr(pmap));
+    // Let's initialize a specific lease ... But this time
+    // We keep its client id for further lookup and
+    // We clearly 'reset' it ...
+    Lease4Ptr lease = initializeLease4(straddress4_[4]);
+    ClientIdPtr client_id = lease->client_id_;
+    lease->client_id_ = ClientIdPtr();
+    EXPECT_TRUE(lease_mgr->addLease(lease));
+
+    Lease4Collection returned = lease_mgr->getLease4(*client_id);
+    // Shouldn't have our previous lease ...
+    ASSERT_EQ(0, returned.size());
+}
+
+// Checks lease4 retrieval through HWAddr
+TEST_F(MemfileLeaseMgrTest, getLease4HWAddr) {
+    const LeaseMgr::ParameterMap pmap;
+    boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr(new Memfile_LeaseMgr(pmap));
+    // Let's initialize two different leases 4 and just add the first ...
+    Lease4Ptr leaseA = initializeLease4(straddress4_[5]);
+    HWAddr hwaddrA(leaseA->hwaddr_, HTYPE_ETHER);
+    HWAddr hwaddrB(vector<uint8_t>(6, 0x80), HTYPE_ETHER);
+
+    EXPECT_TRUE(lease_mgr->addLease(leaseA));
+
+    // we should not have a lease, with this MAC Addr
+    Lease4Collection returned = lease_mgr->getLease4(hwaddrB);
+    ASSERT_EQ(0, returned.size());
+
+    // But with this one
+    returned = lease_mgr->getLease4(hwaddrA);
+    ASSERT_EQ(1, returned.size());
+}
+
+// Checks lease4 retrieval with clientId, HWAddr and subnet_id
+TEST_F(MemfileLeaseMgrTest, getLease4ClientIdHWAddrSubnetId) {
+    const LeaseMgr::ParameterMap pmap;
+    boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr(new Memfile_LeaseMgr(pmap));
+
+    Lease4Ptr leaseA = initializeLease4(straddress4_[4]);
+    Lease4Ptr leaseB = initializeLease4(straddress4_[5]);
+    HWAddr hwaddrA(leaseA->hwaddr_, HTYPE_ETHER);
+    HWAddr hwaddrB(leaseB->hwaddr_, HTYPE_ETHER);
+    EXPECT_TRUE(lease_mgr->addLease(leaseA));
+    // First case we should retrieve our lease
+    Lease4Ptr lease = lease_mgr->getLease4(*leaseA->client_id_, hwaddrA, leaseA->subnet_id_);
+    detailCompareLease(lease, leaseA);
+    lease = lease_mgr->getLease4(*leaseB->client_id_, hwaddrA, leaseA->subnet_id_);
+    detailCompareLease(lease, leaseA);
+    // But not the folowing, with different  hwaddr and subnet
+    lease = lease_mgr->getLease4(*leaseA->client_id_, hwaddrB, leaseA->subnet_id_);
+    EXPECT_TRUE(lease == Lease4Ptr());
+    lease = lease_mgr->getLease4(*leaseA->client_id_, hwaddrA, leaseB->subnet_id_);
+    EXPECT_TRUE(lease == Lease4Ptr());
+}
 
 }; // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
index 5b2fa9e..d5e00ab 100644
--- a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -20,6 +20,7 @@
 #include <dhcpsrv/tests/test_utils.h>
 #include <exceptions/exceptions.h>
 
+
 #include <gtest/gtest.h>
 
 #include <algorithm>
@@ -39,18 +40,6 @@ namespace {
 // This holds statements to create and destroy the schema.
 #include "schema_copy.h"
 
-// IPv4 and IPv6 addresses used in the tests
-const char* ADDRESS4[] = {
-    "192.0.2.0", "192.0.2.1", "192.0.2.2", "192.0.2.3",
-    "192.0.2.4", "192.0.2.5", "192.0.2.6", "192.0.2.7",
-    NULL
-};
-const char* ADDRESS6[] = {
-    "2001:db8::0", "2001:db8::1", "2001:db8::2", "2001:db8::3",
-    "2001:db8::4", "2001:db8::5", "2001:db8::6", "2001:db8::7",
-    NULL
-};
-
 // Connection strings.
 // Database: keatest
 // Host: localhost
@@ -155,26 +144,12 @@ void createSchema() {
 /// Opens the database prior to each test and closes it afterwards.
 /// All pending transactions are deleted prior to closure.
 
-class MySqlLeaseMgrTest : public ::testing::Test {
+class MySqlLeaseMgrTest : public GenericLeaseMgrTest {
 public:
     /// @brief Constructor
     ///
     /// Deletes everything from the database and opens it.
     MySqlLeaseMgrTest() {
-        // Initialize address strings and IOAddresses
-        for (int i = 0; ADDRESS4[i] != NULL; ++i) {
-            string addr(ADDRESS4[i]);
-            straddress4_.push_back(addr);
-            IOAddress ioaddr(addr);
-            ioaddress4_.push_back(ioaddr);
-        }
-
-        for (int i = 0; ADDRESS6[i] != NULL; ++i) {
-            string addr(ADDRESS6[i]);
-            straddress6_.push_back(addr);
-            IOAddress ioaddr(addr);
-            ioaddress6_.push_back(ioaddr);
-        }
 
         // Ensure schema is the correct one.
         destroySchema();
@@ -214,319 +189,6 @@ public:
         lmptr_ = &(LeaseMgrFactory::instance());
     }
 
-    /// @brief Initialize Lease4 Fields
-    ///
-    /// Returns a pointer to a Lease4 structure.  Different values are put into
-    /// the lease according to the address passed.
-    ///
-    /// This is just a convenience function for the test methods.
-    ///
-    /// @param address Address to use for the initialization
-    ///
-    /// @return Lease4Ptr.  This will not point to anything if the
-    ///         initialization failed (e.g. unknown address).
-    Lease4Ptr initializeLease4(std::string address) {
-        Lease4Ptr lease(new Lease4());
-
-        // Set the address of the lease
-        lease->addr_ = IOAddress(address);
-
-        // Initialize unused fields.
-        lease->ext_ = 0;                            // Not saved
-        lease->t1_ = 0;                             // Not saved
-        lease->t2_ = 0;                             // Not saved
-        lease->fixed_ = false;                      // Unused
-        lease->hostname_ = std::string("");         // Unused
-        lease->fqdn_fwd_ = false;                   // Unused
-        lease->fqdn_rev_ = false;                   // Unused
-        lease->comments_ = std::string("");         // Unused
-
-        // Set other parameters.  For historical reasons, address 0 is not used.
-        if (address == straddress4_[0]) {
-            lease->hwaddr_ = vector<uint8_t>(6, 0x08);
-            lease->client_id_ = ClientIdPtr(
-                new ClientId(vector<uint8_t>(8, 0x42)));
-            lease->valid_lft_ = 8677;
-            lease->cltt_ = 168256;
-            lease->subnet_id_ = 23;
-
-        } else if (address == straddress4_[1]) {
-            lease->hwaddr_ = vector<uint8_t>(6, 0x19);
-            lease->client_id_ = ClientIdPtr(
-                new ClientId(vector<uint8_t>(8, 0x53)));
-            lease->valid_lft_ = 3677;
-            lease->cltt_ = 123456;
-            lease->subnet_id_ = 73;
-
-        } else if (address == straddress4_[2]) {
-            lease->hwaddr_ = vector<uint8_t>(6, 0x2a);
-            lease->client_id_ = ClientIdPtr(
-                new ClientId(vector<uint8_t>(8, 0x64)));
-            lease->valid_lft_ = 5412;
-            lease->cltt_ = 234567;
-            lease->subnet_id_ = 73;                         // Same as lease 1
-
-        } else if (address == straddress4_[3]) {
-            lease->hwaddr_ = vector<uint8_t>(6, 0x19);      // Same as lease 1
-            lease->client_id_ = ClientIdPtr(
-                new ClientId(vector<uint8_t>(8, 0x75)));
-
-            // The times used in the next tests are deliberately restricted - we
-            // should be able to cope with valid lifetimes up to 0xffffffff.
-            //  However, this will lead to overflows.
-            // @TODO: test overflow conditions when code has been fixed
-            lease->valid_lft_ = 7000;
-            lease->cltt_ = 234567;
-            lease->subnet_id_ = 37;
-
-        } else if (address == straddress4_[4]) {
-            lease->hwaddr_ = vector<uint8_t>(6, 0x4c);
-            // Same ClientId as straddr4_[1]
-            lease->client_id_ = ClientIdPtr(
-                new ClientId(vector<uint8_t>(8, 0x53)));    // Same as lease 1
-            lease->valid_lft_ = 7736;
-            lease->cltt_ = 222456;
-            lease->subnet_id_ = 85;
-
-        } else if (address == straddress4_[5]) {
-            lease->hwaddr_ = vector<uint8_t>(6, 0x19);      // Same as lease 1
-            // Same ClientId and IAID as straddress4_1
-            lease->client_id_ = ClientIdPtr(
-                new ClientId(vector<uint8_t>(8, 0x53)));    // Same as lease 1
-            lease->valid_lft_ = 7832;
-            lease->cltt_ = 227476;
-            lease->subnet_id_ = 175;
-
-        } else if (address == straddress4_[6]) {
-            lease->hwaddr_ = vector<uint8_t>(6, 0x6e);
-            // Same ClientId as straddress4_1
-            lease->client_id_ = ClientIdPtr(
-                new ClientId(vector<uint8_t>(8, 0x53)));    // Same as lease 1
-            lease->valid_lft_ = 1832;
-            lease->cltt_ = 627476;
-            lease->subnet_id_ = 112;
-
-        } else if (address == straddress4_[7]) {
-            lease->hwaddr_ = vector<uint8_t>();             // Empty
-            lease->client_id_ = ClientIdPtr();              // Empty
-            lease->valid_lft_ = 7975;
-            lease->cltt_ = 213876;
-            lease->subnet_id_ = 19;
-
-        } else {
-            // Unknown address, return an empty pointer.
-            lease.reset();
-
-        }
-
-        return (lease);
-    }
-
-    /// @brief Initialize Lease6 Fields
-    ///
-    /// Returns a pointer to a Lease6 structure.  Different values are put into
-    /// the lease according to the address passed.
-    ///
-    /// This is just a convenience function for the test methods.
-    ///
-    /// @param address Address to use for the initialization
-    ///
-    /// @return Lease6Ptr.  This will not point to anything if the initialization
-    ///         failed (e.g. unknown address).
-    Lease6Ptr initializeLease6(std::string address) {
-        Lease6Ptr lease(new Lease6());
-
-        // Set the address of the lease
-        lease->addr_ = IOAddress(address);
-
-        // Initialize unused fields.
-        lease->t1_ = 0;                             // Not saved
-        lease->t2_ = 0;                             // Not saved
-        lease->fixed_ = false;                      // Unused
-        lease->hostname_ = std::string("");         // Unused
-        lease->fqdn_fwd_ = false;                   // Unused
-        lease->fqdn_rev_ = false;                   // Unused
-        lease->comments_ = std::string("");         // Unused
-
-        // Set other parameters.  For historical reasons, address 0 is not used.
-        if (address == straddress6_[0]) {
-            lease->type_ = Lease6::LEASE_IA_TA;
-            lease->prefixlen_ = 4;
-            lease->iaid_ = 142;
-            lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x77)));
-            lease->preferred_lft_ = 900;
-            lease->valid_lft_ = 8677;
-            lease->cltt_ = 168256;
-            lease->subnet_id_ = 23;
-
-        } else if (address == straddress6_[1]) {
-            lease->type_ = Lease6::LEASE_IA_TA;
-            lease->prefixlen_ = 0;
-            lease->iaid_ = 42;
-            lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
-            lease->preferred_lft_ = 3600;
-            lease->valid_lft_ = 3677;
-            lease->cltt_ = 123456;
-            lease->subnet_id_ = 73;
-
-        } else if (address == straddress6_[2]) {
-            lease->type_ = Lease6::LEASE_IA_PD;
-            lease->prefixlen_ = 7;
-            lease->iaid_ = 89;
-            lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x3a)));
-            lease->preferred_lft_ = 1800;
-            lease->valid_lft_ = 5412;
-            lease->cltt_ = 234567;
-            lease->subnet_id_ = 73;                     // Same as lease 1
-
-        } else if (address == straddress6_[3]) {
-            lease->type_ = Lease6::LEASE_IA_NA;
-            lease->prefixlen_ = 28;
-            lease->iaid_ = 0xfffffffe;
-            vector<uint8_t> duid;
-            for (uint8_t i = 31; i < 126; ++i) {
-                duid.push_back(i);
-            }
-            lease->duid_ = DuidPtr(new DUID(duid));
-
-            // The times used in the next tests are deliberately restricted - we
-            // should be able to cope with valid lifetimes up to 0xffffffff.
-            //  However, this will lead to overflows.
-            // @TODO: test overflow conditions when code has been fixed
-            lease->preferred_lft_ = 7200;
-            lease->valid_lft_ = 7000;
-            lease->cltt_ = 234567;
-            lease->subnet_id_ = 37;
-
-        } else if (address == straddress6_[4]) {
-            // Same DUID and IAID as straddress6_1
-            lease->type_ = Lease6::LEASE_IA_PD;
-            lease->prefixlen_ = 15;
-            lease->iaid_ = 42;
-            lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
-            lease->preferred_lft_ = 4800;
-            lease->valid_lft_ = 7736;
-            lease->cltt_ = 222456;
-            lease->subnet_id_ = 671;
-
-        } else if (address == straddress6_[5]) {
-            // Same DUID and IAID as straddress6_1
-            lease->type_ = Lease6::LEASE_IA_PD;
-            lease->prefixlen_ = 24;
-            lease->iaid_ = 42;                          // Same as lease 4
-            lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
-                                                        // Same as lease 4
-            lease->preferred_lft_ = 5400;
-            lease->valid_lft_ = 7832;
-            lease->cltt_ = 227476;
-            lease->subnet_id_ = 175;
-
-        } else if (address == straddress6_[6]) {
-            // Same DUID as straddress6_1
-            lease->type_ = Lease6::LEASE_IA_PD;
-            lease->prefixlen_ = 24;
-            lease->iaid_ = 93;
-            lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
-                                                        // Same as lease 4
-            lease->preferred_lft_ = 5400;
-            lease->valid_lft_ = 1832;
-            lease->cltt_ = 627476;
-            lease->subnet_id_ = 112;
-
-        } else if (address == straddress6_[7]) {
-            // Same IAID as straddress6_1
-            lease->type_ = Lease6::LEASE_IA_PD;
-            lease->prefixlen_ = 24;
-            lease->iaid_ = 42;
-            lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0xe5)));
-            lease->preferred_lft_ = 5600;
-            lease->valid_lft_ = 7975;
-            lease->cltt_ = 213876;
-            lease->subnet_id_ = 19;
-
-        } else {
-            // Unknown address, return an empty pointer.
-            lease.reset();
-
-        }
-
-        return (lease);
-    }
-
-    /// @brief Check Leases present and different
-    ///
-    /// Checks a vector of lease pointers and ensures that all the leases
-    /// they point to are present and different.  If not, a GTest assertion
-    /// will fail.
-    ///
-    /// @param leases Vector of pointers to leases
-    template <typename T>
-    void checkLeasesDifferent(const std::vector<T>& leases) const {
-
-        // Check they were created
-        for (int i = 0; i < leases.size(); ++i) {
-            ASSERT_TRUE(leases[i]);
-        }
-
-        // Check they are different
-        for (int i = 0; i < (leases.size() - 1); ++i) {
-            for (int j = (i + 1); j < leases.size(); ++j) {
-                stringstream s;
-                s << "Comparing leases " << i << " & " << j << " for equality";
-                SCOPED_TRACE(s.str());
-                EXPECT_TRUE(*leases[i] != *leases[j]);
-            }
-        }
-    }
-
-    /// @brief Creates leases for the test
-    ///
-    /// Creates all leases for the test and checks that they are different.
-    ///
-    /// @return vector<Lease4Ptr> Vector of pointers to leases
-    vector<Lease4Ptr> createLeases4() {
-
-        // Create leases for each address
-        vector<Lease4Ptr> leases;
-        for (int i = 0; i < straddress4_.size(); ++i) {
-            leases.push_back(initializeLease4(straddress4_[i]));
-        }
-        EXPECT_EQ(8, leases.size());
-
-        // Check all were created and that they are different.
-        checkLeasesDifferent(leases);
-
-        return (leases);
-    }
-
-    /// @brief Creates leases for the test
-    ///
-    /// Creates all leases for the test and checks that they are different.
-    ///
-    /// @return vector<Lease6Ptr> Vector of pointers to leases
-    vector<Lease6Ptr> createLeases6() {
-
-        // Create leases for each address
-        vector<Lease6Ptr> leases;
-        for (int i = 0; i < straddress6_.size(); ++i) {
-            leases.push_back(initializeLease6(straddress6_[i]));
-        }
-        EXPECT_EQ(8, leases.size());
-
-        // Check all were created and that they are different.
-        checkLeasesDifferent(leases);
-
-        return (leases);
-    }
-
-
-    // Member variables
-
-    LeaseMgr*   lmptr_;             ///< Pointer to the lease manager
-    vector<string>  straddress4_;   ///< String forms of IPv4 addresses
-    vector<IOAddress> ioaddress4_;  ///< IOAddress forms of IPv4 addresses
-    vector<string>  straddress6_;   ///< String forms of IPv6 addresses
-    vector<IOAddress> ioaddress6_;  ///< IOAddress forms of IPv6 addresses
 };
 
 /// @brief Check that database can be opened
@@ -779,6 +441,31 @@ TEST_F(MySqlLeaseMgrTest, lease4NullClientId) {
 
 }
 
+/// @brief Verify that too long hostname for Lease4 is not accepted.
+///
+/// Checks that the it is not possible to create a lease when the hostname
+/// length exceeds 255 characters.
+TEST_F(MySqlLeaseMgrTest, lease4InvalidHostname) {
+    // Get the leases to be used for the test.
+    vector<Lease4Ptr> leases = createLeases4();
+
+    // Create a dummy hostname, consisting of 255 characters.
+    leases[1]->hostname_.assign(255, 'a');
+    ASSERT_TRUE(lmptr_->addLease(leases[1]));
+
+    // The new lease must be in the database.
+    Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
+    detailCompareLease(leases[1], l_returned);
+
+    // Let's delete the lease, so as we can try to add it again with
+    // invalid hostname.
+    EXPECT_TRUE(lmptr_->deleteLease(ioaddress4_[1]));
+
+    // Create a hostname with 256 characters. It should not be accepted.
+    leases[1]->hostname_.assign(256, 'a');
+    EXPECT_THROW(lmptr_->addLease(leases[1]), DbOperationError);
+}
+
 /// @brief Basic Lease6 Checks
 ///
 /// Checks that the addLease, getLease6 (by address) and deleteLease (with an
@@ -797,15 +484,15 @@ TEST_F(MySqlLeaseMgrTest, basicLease6) {
     // Reopen the database to ensure that they actually got stored.
     reopen();
 
-    Lease6Ptr l_returned = lmptr_->getLease6(ioaddress6_[1]);
+    Lease6Ptr l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
     ASSERT_TRUE(l_returned);
     detailCompareLease(leases[1], l_returned);
 
-    l_returned = lmptr_->getLease6(ioaddress6_[2]);
+    l_returned = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
     ASSERT_TRUE(l_returned);
     detailCompareLease(leases[2], l_returned);
 
-    l_returned = lmptr_->getLease6(ioaddress6_[3]);
+    l_returned = lmptr_->getLease6(leasetype6_[3], ioaddress6_[3]);
     ASSERT_TRUE(l_returned);
     detailCompareLease(leases[3], l_returned);
 
@@ -815,16 +502,41 @@ TEST_F(MySqlLeaseMgrTest, basicLease6) {
     // Delete a lease, check that it's gone, and that we can't delete it
     // a second time.
     EXPECT_TRUE(lmptr_->deleteLease(ioaddress6_[1]));
-    l_returned = lmptr_->getLease6(ioaddress6_[1]);
+    l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
     EXPECT_FALSE(l_returned);
     EXPECT_FALSE(lmptr_->deleteLease(ioaddress6_[1]));
 
     // Check that the second address is still there.
-    l_returned = lmptr_->getLease6(ioaddress6_[2]);
+    l_returned = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
     ASSERT_TRUE(l_returned);
     detailCompareLease(leases[2], l_returned);
 }
 
+/// @brief Verify that too long hostname for Lease6 is not accepted.
+///
+/// Checks that the it is not possible to create a lease when the hostname
+/// length exceeds 255 characters.
+TEST_F(MySqlLeaseMgrTest, lease6InvalidHostname) {
+    // Get the leases to be used for the test.
+    vector<Lease6Ptr> leases = createLeases6();
+
+    // Create a dummy hostname, consisting of 255 characters.
+    leases[1]->hostname_.assign(255, 'a');
+    ASSERT_TRUE(lmptr_->addLease(leases[1]));
+
+    // The new lease must be in the database.
+    Lease6Ptr l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
+    detailCompareLease(leases[1], l_returned);
+
+    // Let's delete the lease, so as we can try to add it again with
+    // invalid hostname.
+    EXPECT_TRUE(lmptr_->deleteLease(ioaddress6_[1]));
+
+    // Create a hostname with 256 characters. It should not be accepted.
+    leases[1]->hostname_.assign(256, 'a');
+    EXPECT_THROW(lmptr_->addLease(leases[1]), DbOperationError);
+}
+
 /// @brief Check GetLease4 methods - access by Hardware Address
 ///
 /// Adds leases to the database and checks that they can be accessed via
@@ -888,7 +600,7 @@ TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSize) {
         leases[1]->hwaddr_.resize(i, i);
         EXPECT_TRUE(lmptr_->addLease(leases[1]));
         // @todo: Simply use HWAddr directly once 2589 is implemented
-        Lease4Collection returned = 
+        Lease4Collection returned =
             lmptr_->getLease4(HWAddr(leases[1]->hwaddr_, HTYPE_ETHER));
 
         ASSERT_EQ(1, returned.size());
@@ -917,7 +629,7 @@ TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSubnetId) {
     // Get the leases matching the hardware address of lease 1 and
     // subnet ID of lease 1.  Result should be a single lease - lease 1.
     // @todo: Simply use HWAddr directly once 2589 is implemented
-    Lease4Ptr returned = lmptr_->getLease4(HWAddr(leases[1]->hwaddr_, 
+    Lease4Ptr returned = lmptr_->getLease4(HWAddr(leases[1]->hwaddr_,
         HTYPE_ETHER), leases[1]->subnet_id_);
 
     ASSERT_TRUE(returned);
@@ -954,16 +666,11 @@ TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSubnetId) {
     leases[1]->addr_ = leases[2]->addr_;
     EXPECT_TRUE(lmptr_->addLease(leases[1]));
     // @todo: Simply use HWAddr directly once 2589 is implemented
-    EXPECT_THROW(returned = lmptr_->getLease4(HWAddr(leases[1]->hwaddr_, 
-                                                    HTYPE_ETHER), 
-                                             leases[1]->subnet_id_), 
+    EXPECT_THROW(returned = lmptr_->getLease4(HWAddr(leases[1]->hwaddr_,
+                                                    HTYPE_ETHER),
+                                             leases[1]->subnet_id_),
                  isc::dhcp::MultipleRecords);
 
-    // Delete all leases in the database
-    for (int i = 0; ADDRESS4[i] != NULL; ++i) {
-        IOAddress addr(ADDRESS4[i]);
-        (void) lmptr_->deleteLease(addr);
-    }
 }
 
 // @brief Get lease4 by hardware address and subnet ID (2)
@@ -981,8 +688,8 @@ TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSubnetIdSize) {
         leases[1]->hwaddr_.resize(i, i);
         EXPECT_TRUE(lmptr_->addLease(leases[1]));
         // @todo: Simply use HWAddr directly once 2589 is implemented
-        Lease4Ptr returned = lmptr_->getLease4(HWAddr(leases[1]->hwaddr_, 
-                                                      HTYPE_ETHER), 
+        Lease4Ptr returned = lmptr_->getLease4(HWAddr(leases[1]->hwaddr_,
+                                                      HTYPE_ETHER),
                                                leases[1]->subnet_id_);
         ASSERT_TRUE(returned);
         detailCompareLease(leases[1], returned);
@@ -1117,7 +824,7 @@ TEST_F(MySqlLeaseMgrTest, getLease4ClientIdSubnetId) {
 ///
 /// Adds leases to the database and checks that they can be accessed via
 /// a combination of DIUID and IAID.
-TEST_F(MySqlLeaseMgrTest, getLease6DuidIaid) {
+TEST_F(MySqlLeaseMgrTest, getLeases6DuidIaid) {
     // Get the leases to be used for the test.
     vector<Lease6Ptr> leases = createLeases6();
     ASSERT_LE(6, leases.size());    // Expect to access leases 0 through 5
@@ -1128,11 +835,12 @@ TEST_F(MySqlLeaseMgrTest, getLease6DuidIaid) {
     }
 
     // Get the leases matching the DUID and IAID of lease[1].
-    Lease6Collection returned = lmptr_->getLease6(*leases[1]->duid_,
-                                                  leases[1]->iaid_);
+    Lease6Collection returned = lmptr_->getLeases6(leasetype6_[1],
+                                                   *leases[1]->duid_,
+                                                   leases[1]->iaid_);
 
-    // Should be three leases, matching leases[1], [4] and [5].
-    ASSERT_EQ(3, returned.size());
+    // Should be two leases, matching leases[1] and [4].
+    ASSERT_EQ(2, returned.size());
 
     // Easiest way to check is to look at the addresses.
     vector<string> addresses;
@@ -1143,25 +851,25 @@ TEST_F(MySqlLeaseMgrTest, getLease6DuidIaid) {
     sort(addresses.begin(), addresses.end());
     EXPECT_EQ(straddress6_[1], addresses[0]);
     EXPECT_EQ(straddress6_[4], addresses[1]);
-    EXPECT_EQ(straddress6_[5], addresses[2]);
 
     // Check that nothing is returned when either the IAID or DUID match
     // nothing.
-    returned = lmptr_->getLease6(*leases[1]->duid_, leases[1]->iaid_ + 1);
+    returned = lmptr_->getLeases6(leasetype6_[1], *leases[1]->duid_,
+                                  leases[1]->iaid_ + 1);
     EXPECT_EQ(0, returned.size());
 
     // Alter the leases[1] DUID to match nothing in the database.
     vector<uint8_t> duid_vector = leases[1]->duid_->getDuid();
     ++duid_vector[0];
     DUID new_duid(duid_vector);
-    returned = lmptr_->getLease6(new_duid, leases[1]->iaid_);
+    returned = lmptr_->getLeases6(leasetype6_[1], new_duid, leases[1]->iaid_);
     EXPECT_EQ(0, returned.size());
 }
 
 // @brief Get Lease4 by DUID and IAID (2)
 //
 // Check that the system can cope with a DUID of any size.
-TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSize) {
+TEST_F(MySqlLeaseMgrTest, getLeases6DuidIaidSize) {
 
     // Create leases, although we need only one.
     vector<Lease6Ptr> leases = createLeases6();
@@ -1178,8 +886,9 @@ TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSize) {
         vector<uint8_t> duid_vec(i, i);
         leases[1]->duid_.reset(new DUID(duid_vec));
         EXPECT_TRUE(lmptr_->addLease(leases[1]));
-        Lease6Collection returned = lmptr_->getLease6(*leases[1]->duid_,
-                                                      leases[1]->iaid_);
+        Lease6Collection returned = lmptr_->getLeases6(leasetype6_[1],
+                                                       *leases[1]->duid_,
+                                                       leases[1]->iaid_);
         ASSERT_EQ(1, returned.size());
         detailCompareLease(leases[1], *returned.begin());
         (void) lmptr_->deleteLease(leases[1]->addr_);
@@ -1190,6 +899,104 @@ TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSize) {
     // tests.
 }
 
+/// @brief Check that getLease6 methods discriminate by lease type.
+///
+/// Adds six leases, two per lease type all with the same duid and iad but
+/// with alternating subnet_ids.
+/// It then verifies that all of getLeases6() method variants correctly
+/// discriminate between the leases based on lease type alone.
+TEST_F(MySqlLeaseMgrTest, lease6LeaseTypeCheck) {
+
+    Lease6Ptr empty_lease(new Lease6());
+
+    DuidPtr duid(new DUID(vector<uint8_t>(8, 0x77)));
+
+    // Initialize unused fields.
+    empty_lease->t1_ = 0;                             // Not saved
+    empty_lease->t2_ = 0;                             // Not saved
+    empty_lease->fixed_ = false;                      // Unused
+    empty_lease->comments_ = std::string("");         // Unused
+    empty_lease->iaid_ = 142;
+    empty_lease->duid_ = DuidPtr(new DUID(*duid));
+    empty_lease->subnet_id_ = 23;
+    empty_lease->preferred_lft_ = 100;
+    empty_lease->valid_lft_ = 100;
+    empty_lease->cltt_ = 100;
+    empty_lease->fqdn_fwd_ = true;
+    empty_lease->fqdn_rev_ = true;
+    empty_lease->hostname_ = "myhost.example.com.";
+    empty_lease->prefixlen_ = 4;
+
+    // Make Two leases per lease type, all with the same  DUID, IAID but
+    // alternate the subnet_ids.
+    vector<Lease6Ptr> leases;
+    for (int i = 0; i < 6; ++i) {
+          Lease6Ptr lease(new Lease6(*empty_lease));
+          lease->type_ = leasetype6_[i / 2];
+          lease->addr_ = IOAddress(straddress6_[i]);
+          lease->subnet_id_ += (i % 2);
+          leases.push_back(lease);
+          EXPECT_TRUE(lmptr_->addLease(lease));
+     }
+
+    // Verify getting a single lease by type and address.
+    for (int i = 0; i < 6; ++i) {
+        // Look for exact match for each lease type.
+        Lease6Ptr returned = lmptr_->getLease6(leasetype6_[i / 2],
+                                               leases[i]->addr_);
+        // We should match one per lease type.
+        ASSERT_TRUE(returned);
+        EXPECT_TRUE(*returned == *leases[i]);
+
+        // Same address but wrong lease type, should not match.
+        returned = lmptr_->getLease6(leasetype6_[i / 2 + 1], leases[i]->addr_);
+        ASSERT_FALSE(returned);
+    }
+
+    // Verify getting a collection of leases by type, DUID, and IAID.
+    // Iterate over the lease types, asking for leases based on
+    // lease type, DUID, and IAID.
+    for (int i = 0; i < 3; ++i) {
+        Lease6Collection returned = lmptr_->getLeases6(leasetype6_[i],
+                                                       *duid, 142);
+        // We should match two per lease type.
+        ASSERT_EQ(2, returned.size());
+
+        // Collection order returned is not guaranteed.
+        // Easiest way to check is to look at the addresses.
+        vector<string> addresses;
+        for (Lease6Collection::const_iterator it = returned.begin();
+            it != returned.end(); ++it) {
+            addresses.push_back((*it)->addr_.toText());
+        }
+        sort(addresses.begin(), addresses.end());
+
+        // Now verify that the lease addresses match.
+        EXPECT_EQ(addresses[0], leases[(i * 2)]->addr_.toText());
+        EXPECT_EQ(addresses[1], leases[(i * 2 + 1)]->addr_.toText());
+    }
+
+    // Verify getting a collection of leases by type, DUID, IAID, and subnet id.
+    // Iterate over the lease types, asking for leases based on
+    // lease type, DUID, IAID, and subnet_id.
+    for (int i = 0; i < 3; ++i) {
+        Lease6Collection returned = lmptr_->getLeases6(leasetype6_[i],
+                                                   *duid, 142, 23);
+        // We should match one per lease type.
+        ASSERT_EQ(1, returned.size());
+        EXPECT_TRUE(*(returned[0]) == *leases[i * 2]);
+    }
+
+    // Verify getting a single lease by type, duid, iad, and subnet id.
+    for (int i = 0; i < 6; ++i) {
+        Lease6Ptr returned = lmptr_->getLease6(leasetype6_[i / 2],
+                                                *duid, 142, (23 + (i % 2)));
+        // We should match one per lease type.
+        ASSERT_TRUE(returned);
+        EXPECT_TRUE(*returned == *leases[i]);
+    }
+}
+
 /// @brief Check GetLease6 methods - access by DUID/IAID/SubnetID
 ///
 /// Adds leases to the database and checks that they can be accessed via
@@ -1202,7 +1009,7 @@ TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSubnetId) {
     }
 
     // Get the leases matching the DUID and IAID of lease[1].
-    Lease6Ptr returned = lmptr_->getLease6(*leases[1]->duid_,
+    Lease6Ptr returned = lmptr_->getLease6(leasetype6_[1], *leases[1]->duid_,
                                            leases[1]->iaid_,
                                            leases[1]->subnet_id_);
     ASSERT_TRUE(returned);
@@ -1210,23 +1017,24 @@ TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSubnetId) {
 
     // Modify each of the three parameters (DUID, IAID, Subnet ID) and
     // check that nothing is returned.
-    returned = lmptr_->getLease6(*leases[1]->duid_, leases[1]->iaid_ + 1,
-                                 leases[1]->subnet_id_);
+    returned = lmptr_->getLease6(leasetype6_[1], *leases[1]->duid_,
+                                 leases[1]->iaid_ + 1, leases[1]->subnet_id_);
     EXPECT_FALSE(returned);
 
-    returned = lmptr_->getLease6(*leases[1]->duid_, leases[1]->iaid_,
-                                 leases[1]->subnet_id_ + 1);
+    returned = lmptr_->getLease6(leasetype6_[1], *leases[1]->duid_,
+                                 leases[1]->iaid_, leases[1]->subnet_id_ + 1);
     EXPECT_FALSE(returned);
 
     // Alter the leases[1] DUID to match nothing in the database.
     vector<uint8_t> duid_vector = leases[1]->duid_->getDuid();
     ++duid_vector[0];
     DUID new_duid(duid_vector);
-    returned = lmptr_->getLease6(new_duid, leases[1]->iaid_,
+    returned = lmptr_->getLease6(leasetype6_[1], new_duid, leases[1]->iaid_,
                                  leases[1]->subnet_id_);
     EXPECT_FALSE(returned);
 }
 
+
 // @brief Get Lease4 by DUID, IAID & subnet ID (2)
 //
 // Check that the system can cope with a DUID of any size.
@@ -1247,7 +1055,7 @@ TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSubnetIdSize) {
         vector<uint8_t> duid_vec(i, i);
         leases[1]->duid_.reset(new DUID(duid_vec));
         EXPECT_TRUE(lmptr_->addLease(leases[1]));
-        Lease6Ptr returned = lmptr_->getLease6(*leases[1]->duid_,
+        Lease6Ptr returned = lmptr_->getLease6(leasetype6_[1], *leases[1]->duid_,
                                                leases[1]->iaid_,
                                                leases[1]->subnet_id_);
         ASSERT_TRUE(returned);
@@ -1273,6 +1081,9 @@ TEST_F(MySqlLeaseMgrTest, updateLease4) {
     // Modify some fields in lease 1 (not the address) and update it.
     ++leases[1]->subnet_id_;
     leases[1]->valid_lft_ *= 2;
+    leases[1]->hostname_ = "modified.hostname.";
+    leases[1]->fqdn_fwd_ = !leases[1]->fqdn_fwd_;
+    leases[1]->fqdn_rev_ = !leases[1]->fqdn_rev_;;
     lmptr_->updateLease4(leases[1]);
 
     // ... and check what is returned is what is expected.
@@ -1299,6 +1110,10 @@ TEST_F(MySqlLeaseMgrTest, updateLease4) {
     ASSERT_TRUE(l_returned);
     detailCompareLease(leases[1], l_returned);
 
+    // Try to update the lease with the too long hostname.
+    leases[1]->hostname_.assign(256, 'a');
+    EXPECT_THROW(lmptr_->updateLease4(leases[1]), isc::dhcp::DbOperationError);
+
     // Try updating a lease not in the database.
     lmptr_->deleteLease(ioaddress4_[2]);
     EXPECT_THROW(lmptr_->updateLease4(leases[2]), isc::dhcp::NoSuchLease);
@@ -1316,42 +1131,49 @@ TEST_F(MySqlLeaseMgrTest, updateLease6) {
     EXPECT_TRUE(lmptr_->addLease(leases[1]));
     lmptr_->commit();
 
-    Lease6Ptr l_returned = lmptr_->getLease6(ioaddress6_[1]);
+    Lease6Ptr l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
     ASSERT_TRUE(l_returned);
     detailCompareLease(leases[1], l_returned);
 
     // Modify some fields in lease 1 (not the address) and update it.
     ++leases[1]->iaid_;
-    leases[1]->type_ = Lease6::LEASE_IA_PD;
+    leases[1]->type_ = Lease::TYPE_PD;
     leases[1]->valid_lft_ *= 2;
+    leases[1]->hostname_ = "modified.hostname.v6.";
+    leases[1]->fqdn_fwd_ = !leases[1]->fqdn_fwd_;
+    leases[1]->fqdn_rev_ = !leases[1]->fqdn_rev_;;
     lmptr_->updateLease6(leases[1]);
     lmptr_->commit();
 
     // ... and check what is returned is what is expected.
     l_returned.reset();
-    l_returned = lmptr_->getLease6(ioaddress6_[1]);
+    l_returned = lmptr_->getLease6(Lease::TYPE_PD, ioaddress6_[1]);
     ASSERT_TRUE(l_returned);
     detailCompareLease(leases[1], l_returned);
 
     // Alter the lease again and check.
     ++leases[1]->iaid_;
-    leases[1]->type_ = Lease6::LEASE_IA_TA;
+    leases[1]->type_ = Lease::TYPE_TA;
     leases[1]->cltt_ += 6;
     leases[1]->prefixlen_ = 93;
     lmptr_->updateLease6(leases[1]);
 
     l_returned.reset();
-    l_returned = lmptr_->getLease6(ioaddress6_[1]);
+    l_returned = lmptr_->getLease6(Lease::TYPE_TA, ioaddress6_[1]);
     ASSERT_TRUE(l_returned);
     detailCompareLease(leases[1], l_returned);
 
     // Check we can do an update without changing data.
     lmptr_->updateLease6(leases[1]);
     l_returned.reset();
-    l_returned = lmptr_->getLease6(ioaddress6_[1]);
+    l_returned = lmptr_->getLease6(Lease::TYPE_TA, ioaddress6_[1]);
     ASSERT_TRUE(l_returned);
     detailCompareLease(leases[1], l_returned);
 
+    // Try to update the lease with the too long hostname.
+    leases[1]->hostname_.assign(256, 'a');
+    EXPECT_THROW(lmptr_->updateLease6(leases[1]), isc::dhcp::DbOperationError);
+
     // Try updating a lease not in the database.
     EXPECT_THROW(lmptr_->updateLease6(leases[2]), isc::dhcp::NoSuchLease);
 }
diff --git a/src/lib/dhcpsrv/tests/pool_unittest.cc b/src/lib/dhcpsrv/tests/pool_unittest.cc
index 4e18a3c..402ca2a 100644
--- a/src/lib/dhcpsrv/tests/pool_unittest.cc
+++ b/src/lib/dhcpsrv/tests/pool_unittest.cc
@@ -39,14 +39,14 @@ TEST(Pool4Test, constructor_first_last) {
     EXPECT_EQ(IOAddress("192.0.2.255"), pool1.getLastAddress());
 
     // This is Pool4, IPv6 addresses do not belong here
-    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::1"),
+    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("2001:db8::1"),
                        IOAddress("192.168.0.5")), BadValue);
-    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"),
+    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("192.168.0.2"),
                        IOAddress("2001:db8::1")), BadValue);
 
     // Should throw. Range should be 192.0.2.1-192.0.2.2, not
     // the other way around.
-    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.0.2.2"),
+    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("192.0.2.2"),
                        IOAddress("192.0.2.1")), BadValue);
 }
 
@@ -99,57 +99,68 @@ TEST(Pool4Test, unique_id) {
             }
         }
     }
-
 }
 
+// Simple check if toText returns reasonable values
+TEST(Poo4Test,toText) {
+    Pool4 pool1(IOAddress("192.0.2.7"), IOAddress("192.0.2.17"));
+    EXPECT_EQ("type=V4, 192.0.2.7-192.0.2.17", pool1.toText());
+
+    Pool4 pool2(IOAddress("192.0.2.128"), 28);
+    EXPECT_EQ("type=V4, 192.0.2.128-192.0.2.143", pool2.toText());
+}
 
 TEST(Pool6Test, constructor_first_last) {
 
     // let's construct 2001:db8:1:: - 2001:db8:1::ffff:ffff:ffff:ffff pool
-    Pool6 pool1(Pool6::TYPE_IA, IOAddress("2001:db8:1::"),
+    Pool6 pool1(Lease::TYPE_NA, IOAddress("2001:db8:1::"),
                 IOAddress("2001:db8:1::ffff:ffff:ffff:ffff"));
 
-    EXPECT_EQ(Pool6::TYPE_IA, pool1.getType());
+    EXPECT_EQ(Lease::TYPE_NA, pool1.getType());
     EXPECT_EQ(IOAddress("2001:db8:1::"), pool1.getFirstAddress());
     EXPECT_EQ(IOAddress("2001:db8:1::ffff:ffff:ffff:ffff"),
               pool1.getLastAddress());
 
     // This is Pool6, IPv4 addresses do not belong here
-    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::1"),
+    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("2001:db8::1"),
                        IOAddress("192.168.0.5")), BadValue);
-    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"),
+    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("192.168.0.2"),
                        IOAddress("2001:db8::1")), BadValue);
 
     // Should throw. Range should be 2001:db8::1 - 2001:db8::2, not
     // the other way around.
-    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::2"),
+    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("2001:db8::2"),
                        IOAddress("2001:db8::1")), BadValue);
 }
 
 TEST(Pool6Test, constructor_prefix_len) {
 
     // let's construct 2001:db8:1::/96 pool
-    Pool6 pool1(Pool6::TYPE_IA, IOAddress("2001:db8:1::"), 96);
+    Pool6 pool1(Lease::TYPE_NA, IOAddress("2001:db8:1::"), 96);
 
-    EXPECT_EQ(Pool6::TYPE_IA, pool1.getType());
+    EXPECT_EQ(Lease::TYPE_NA, pool1.getType());
     EXPECT_EQ("2001:db8:1::", pool1.getFirstAddress().toText());
     EXPECT_EQ("2001:db8:1::ffff:ffff", pool1.getLastAddress().toText());
 
     // No such thing as /130 prefix
-    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::"), 130),
+    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("2001:db8::"), 130),
                  BadValue);
 
     // /0 prefix does not make sense
-    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::"), 0),
+    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("2001:db8::"), 0),
                  BadValue);
 
     // This is Pool6, IPv4 addresses do not belong here
-    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"), 96),
+    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("192.168.0.2"), 96),
+                 BadValue);
+
+    // Delegated prefix length for addresses must be /128
+    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::"), 96, 125),
                  BadValue);
 }
 
 TEST(Pool6Test, in_range) {
-   Pool6 pool1(Pool6::TYPE_IA, IOAddress("2001:db8:1::1"),
+   Pool6 pool1(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
                IOAddress("2001:db8:1::f"));
 
    EXPECT_FALSE(pool1.inRange(IOAddress("2001:db8:1::")));
@@ -160,6 +171,65 @@ TEST(Pool6Test, in_range) {
    EXPECT_FALSE(pool1.inRange(IOAddress("::")));
 }
 
+// Checks that Prefix Delegation pools are handled properly
+TEST(Pool6Test, PD) {
+
+    // Let's construct 2001:db8:1::/96 PD pool, split into /112 prefixes
+    Pool6 pool1(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 96, 112);
+
+    EXPECT_EQ(Lease::TYPE_PD, pool1.getType());
+    EXPECT_EQ(112, pool1.getLength());
+    EXPECT_EQ("2001:db8:1::", pool1.getFirstAddress().toText());
+    EXPECT_EQ("2001:db8:1::ffff:ffff", pool1.getLastAddress().toText());
+
+    // Check that it's not possible to have min-max range for PD
+    EXPECT_THROW(Pool6 pool2(Lease::TYPE_PD, IOAddress("2001:db8:1::1"),
+                             IOAddress("2001:db8:1::f")), BadValue);
+
+    // Check that it's not allowed to delegate bigger prefix than the pool
+    // Let's try to split /64 prefix into /56 chunks (should be impossible)
+    EXPECT_THROW(Pool6 pool3(Lease::TYPE_PD, IOAddress("2001:db8:1::"),
+                             64, 56), BadValue);
+
+    // It should be possible to have a pool split into just a single chunk
+    // Let's try to split 2001:db8:1::/77 into a single /77 delegated prefix
+    EXPECT_NO_THROW(Pool6 pool4(Lease::TYPE_PD, IOAddress("2001:db8:1::"),
+                                77, 77));
+}
+
+// Checks that temporary address pools are handled properly
+TEST(Pool6Test, TA) {
+    // Note: since we defined TA pool types during PD work, we can test it
+    // now. Although the configuration to take advantage of it is not
+    // planned for now, we will support it some day.
+
+    // Let's construct 2001:db8:1::/96 temporary addresses
+    Pool6Ptr pool1;
+    EXPECT_NO_THROW(pool1.reset(new Pool6(Lease::TYPE_TA,
+                                          IOAddress("2001:db8:1::"), 96)));
+
+    // Check that TA range can be only defined for single addresses
+    EXPECT_THROW(Pool6(Lease::TYPE_TA, IOAddress("2001:db8:1::"), 96, 127),
+                 BadValue);
+
+    ASSERT_TRUE(pool1);
+    EXPECT_EQ(Lease::TYPE_TA, pool1->getType());
+    EXPECT_EQ(128, pool1->getLength()); // singular addresses, not prefixes
+    EXPECT_EQ("2001:db8:1::", pool1->getFirstAddress().toText());
+    EXPECT_EQ("2001:db8:1::ffff:ffff", pool1->getLastAddress().toText());
+
+    // Check that it's possible to have min-max range for TA
+    Pool6Ptr pool2;
+    EXPECT_NO_THROW(pool2.reset(new Pool6(Lease::TYPE_TA,
+                                          IOAddress("2001:db8:1::1"),
+                                          IOAddress("2001:db8:1::f"))));
+    ASSERT_TRUE(pool2);
+    EXPECT_EQ(Lease::TYPE_TA, pool2->getType());
+    EXPECT_EQ(128, pool2->getLength()); // singular addresses, not prefixes
+    EXPECT_EQ("2001:db8:1::1", pool2->getFirstAddress().toText());
+    EXPECT_EQ("2001:db8:1::f", pool2->getLastAddress().toText());
+}
+
 // This test creates 100 pools and verifies that their IDs are unique.
 TEST(Pool6Test, unique_id) {
 
@@ -167,7 +237,7 @@ TEST(Pool6Test, unique_id) {
     std::vector<Pool6Ptr> pools;
 
     for (int i = 0; i < num_pools; ++i) {
-        pools.push_back(Pool6Ptr(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1::"),
+        pools.push_back(Pool6Ptr(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::"),
                                            IOAddress("2001:db8:1::ffff:ffff:ffff:ffff"))));
     }
 
@@ -181,4 +251,16 @@ TEST(Pool6Test, unique_id) {
 
 }
 
+// Simple check if toText returns reasonable values
+TEST(Poo6Test,toText) {
+    Pool6 pool1(Lease::TYPE_NA, IOAddress("2001:db8::1"),
+                IOAddress("2001:db8::2"));
+    EXPECT_EQ("type=IA_NA, 2001:db8::1-2001:db8::2, delegated_len=128",
+              pool1.toText());
+
+    Pool6 pool2(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 96, 112);
+    EXPECT_EQ("type=IA_PD, 2001:db8:1::-2001:db8:1::ffff:ffff, delegated_len=112",
+              pool2.toText());
+}
+
 }; // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/schema_copy.h b/src/lib/dhcpsrv/tests/schema_copy.h
index f534fa8..0ccb74e 100644
--- a/src/lib/dhcpsrv/tests/schema_copy.h
+++ b/src/lib/dhcpsrv/tests/schema_copy.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -49,7 +49,10 @@ const char* create_statement[] = {
         "client_id VARBINARY(128),"
         "valid_lifetime INT UNSIGNED,"
         "expire TIMESTAMP,"
-        "subnet_id INT UNSIGNED"
+        "subnet_id INT UNSIGNED,"
+        "fqdn_fwd BOOL,"
+        "fqdn_rev BOOL,"
+        "hostname VARCHAR(255)"
         ") ENGINE = INNODB",
 
     "CREATE INDEX lease4_by_hwaddr_subnet_id ON lease4 (hwaddr, subnet_id)",
@@ -65,7 +68,10 @@ const char* create_statement[] = {
         "pref_lifetime INT UNSIGNED,"
         "lease_type TINYINT,"
         "iaid INT UNSIGNED,"
-        "prefix_len TINYINT UNSIGNED"
+        "prefix_len TINYINT UNSIGNED,"
+        "fqdn_fwd BOOL,"
+        "fqdn_rev BOOL,"
+        "hostname VARCHAR(255)"
         ") ENGINE = INNODB",
 
     "CREATE INDEX lease6_by_iaid_subnet_id_duid ON lease6 (iaid, subnet_id, duid)",
diff --git a/src/lib/dhcpsrv/tests/subnet_unittest.cc b/src/lib/dhcpsrv/tests/subnet_unittest.cc
index fbcebcb..d0dd57a 100644
--- a/src/lib/dhcpsrv/tests/subnet_unittest.cc
+++ b/src/lib/dhcpsrv/tests/subnet_unittest.cc
@@ -58,6 +58,24 @@ TEST(Subnet4Test, in_range) {
     EXPECT_FALSE(subnet.inRange(IOAddress("255.255.255.255")));
 }
 
+// Checks whether siaddr field can be set and retrieved correctly.
+TEST(Subnet4Test, siaddr) {
+    Subnet4 subnet(IOAddress("192.0.2.1"), 24, 1000, 2000, 3000);
+
+    // Check if the default is 0.0.0.0
+    EXPECT_EQ("0.0.0.0", subnet.getSiaddr().toText());
+
+    // Check that we can set it up
+    EXPECT_NO_THROW(subnet.setSiaddr(IOAddress("1.2.3.4")));
+
+    // Check that we can get it back
+    EXPECT_EQ("1.2.3.4", subnet.getSiaddr().toText());
+
+    // Check that only v4 addresses are supported
+    EXPECT_THROW(subnet.setSiaddr(IOAddress("2001:db8::1")),
+        BadValue);
+}
+
 TEST(Subnet4Test, Pool4InSubnet4) {
 
     Subnet4Ptr subnet(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3));
@@ -66,24 +84,25 @@ TEST(Subnet4Test, Pool4InSubnet4) {
     PoolPtr pool2(new Pool4(IOAddress("192.1.2.128"), 26));
     PoolPtr pool3(new Pool4(IOAddress("192.1.2.192"), 30));
 
-    subnet->addPool(pool1);
+    EXPECT_NO_THROW(subnet->addPool(pool1));
 
     // If there's only one pool, get that pool
-    PoolPtr mypool = subnet->getPool();
+    PoolPtr mypool = subnet->getAnyPool(Lease::TYPE_V4);
     EXPECT_EQ(mypool, pool1);
 
 
-    subnet->addPool(pool2);
-    subnet->addPool(pool3);
+    EXPECT_NO_THROW(subnet->addPool(pool2));
+    EXPECT_NO_THROW(subnet->addPool(pool3));
 
     // If there are more than one pool and we didn't provide hint, we
     // should get the first pool
-    mypool = subnet->getPool();
+    EXPECT_NO_THROW(mypool = subnet->getAnyPool(Lease::TYPE_V4));
 
     EXPECT_EQ(mypool, pool1);
 
     // If we provide a hint, we should get a pool that this hint belongs to
-    mypool = subnet->getPool(IOAddress("192.1.2.195"));
+    EXPECT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4,
+                                             IOAddress("192.1.2.195")));
 
     EXPECT_EQ(mypool, pool3);
 
@@ -139,27 +158,27 @@ TEST(Subnet4Test, inRangeinPool) {
     EXPECT_TRUE(subnet->inRange(IOAddress("192.1.1.1")));
 
     // ... but it does not belong to any pool within
-    EXPECT_FALSE(subnet->inPool(IOAddress("192.1.1.1")));
+    EXPECT_FALSE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.1.1.1")));
 
     // the last address that is in range, but out of pool
     EXPECT_TRUE(subnet->inRange(IOAddress("192.1.255.255")));
-    EXPECT_FALSE(subnet->inPool(IOAddress("192.1.255.255")));
+    EXPECT_FALSE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.1.255.255")));
 
     // the first address that is in range, in pool
     EXPECT_TRUE(subnet->inRange(IOAddress("192.2.0.0")));
-    EXPECT_TRUE (subnet->inPool(IOAddress("192.2.0.0")));
+    EXPECT_TRUE (subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.0.0")));
 
     // let's try something in the middle as well
     EXPECT_TRUE(subnet->inRange(IOAddress("192.2.3.4")));
-    EXPECT_TRUE (subnet->inPool(IOAddress("192.2.3.4")));
+    EXPECT_TRUE (subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4")));
 
     // the last address that is in range, in pool
     EXPECT_TRUE(subnet->inRange(IOAddress("192.2.255.255")));
-    EXPECT_TRUE (subnet->inPool(IOAddress("192.2.255.255")));
+    EXPECT_TRUE (subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.255.255")));
 
     // the first address that is in range, but out of pool
     EXPECT_TRUE(subnet->inRange(IOAddress("192.3.0.0")));
-    EXPECT_FALSE(subnet->inPool(IOAddress("192.3.0.0")));
+    EXPECT_FALSE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.3.0.0")));
 }
 
 // This test checks if the toText() method returns text representation
@@ -175,6 +194,74 @@ TEST(Subnet4Test, get) {
     EXPECT_EQ(28, subnet->get().second);
 }
 
+
+// Checks if last allocated address/prefix is stored/retrieved properly
+TEST(Subnet4Test, lastAllocated) {
+    IOAddress addr("192.0.2.17");
+
+    IOAddress last("192.0.2.255");
+
+    Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3));
+
+    // Check initial conditions (all should be set to the last address in range)
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Lease::TYPE_V4).toText());
+
+    // Now set last allocated for IA
+    EXPECT_NO_THROW(subnet->setLastAllocated(Lease::TYPE_V4, addr));
+    EXPECT_EQ(addr.toText(), subnet->getLastAllocated(Lease::TYPE_V4).toText());
+
+    // No, you can't set the last allocated IPv6 address in IPv4 subnet
+    EXPECT_THROW(subnet->setLastAllocated(Lease::TYPE_TA, addr), BadValue);
+    EXPECT_THROW(subnet->setLastAllocated(Lease::TYPE_TA, addr), BadValue);
+    EXPECT_THROW(subnet->setLastAllocated(Lease::TYPE_PD, addr), BadValue);
+}
+
+// Checks if the V4 is the only allowed type for Pool4 and if getPool()
+// is working properly.
+TEST(Subnet4Test, PoolType) {
+
+    Subnet4Ptr subnet(new Subnet4(IOAddress("192.2.0.0"), 16, 1, 2, 3));
+
+    PoolPtr pool1(new Pool4(IOAddress("192.2.1.0"), 24));
+    PoolPtr pool2(new Pool4(IOAddress("192.2.2.0"), 24));
+    PoolPtr pool3(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:3::"), 64));
+    PoolPtr pool4(new Pool6(Lease::TYPE_TA, IOAddress("2001:db8:1:4::"), 64));
+    PoolPtr pool5(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:1::"), 64));
+
+    // There should be no pools of any type by default
+    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Lease::TYPE_V4));
+
+    // It should not be possible to ask for V6 pools in Subnet4
+    EXPECT_THROW(subnet->getAnyPool(Lease::TYPE_NA), BadValue);
+    EXPECT_THROW(subnet->getAnyPool(Lease::TYPE_TA), BadValue);
+    EXPECT_THROW(subnet->getAnyPool(Lease::TYPE_PD), BadValue);
+
+    // Let's add a single V4 pool and check that it can be retrieved
+    EXPECT_NO_THROW(subnet->addPool(pool1));
+
+    // If there's only one IA pool, get that pool (without and with hint)
+    EXPECT_EQ(pool1, subnet->getAnyPool(Lease::TYPE_V4));
+    EXPECT_EQ(pool1, subnet->getPool(Lease::TYPE_V4, IOAddress("192.0.1.167")));
+
+    // Let's add additional V4 pool
+    EXPECT_NO_THROW(subnet->addPool(pool2));
+
+    // Try without hints
+    EXPECT_EQ(pool1, subnet->getAnyPool(Lease::TYPE_V4));
+
+    // Try with valid hints
+    EXPECT_EQ(pool1, subnet->getPool(Lease::TYPE_V4, IOAddress("192.2.1.5")));
+    EXPECT_EQ(pool2, subnet->getPool(Lease::TYPE_V4, IOAddress("192.2.2.254")));
+
+    // Try with bogus hints (hints should be ingored)
+    EXPECT_EQ(pool1, subnet->getPool(Lease::TYPE_V4, IOAddress("10.1.1.1")));
+
+    // Trying to add Pool6 to Subnet4 is a big no,no!
+    EXPECT_THROW(subnet->addPool(pool3), BadValue);
+    EXPECT_THROW(subnet->addPool(pool4), BadValue);
+    EXPECT_THROW(subnet->addPool(pool5), BadValue);
+}
+
 // Tests for Subnet6
 
 TEST(Subnet6Test, constructor) {
@@ -208,52 +295,122 @@ TEST(Subnet6Test, Pool6InSubnet6) {
 
     Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
 
-    PoolPtr pool1(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:1::"), 64));
-    PoolPtr pool2(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:2::"), 64));
-    PoolPtr pool3(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:3::"), 64));
+    PoolPtr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"), 64));
+    PoolPtr pool2(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:2::"), 64));
+    PoolPtr pool3(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:3::"), 64));
 
     subnet->addPool(pool1);
 
     // If there's only one pool, get that pool
-    PoolPtr mypool = subnet->getPool();
+    PoolPtr mypool = subnet->getAnyPool(Lease::TYPE_NA);
     EXPECT_EQ(mypool, pool1);
 
-
     subnet->addPool(pool2);
     subnet->addPool(pool3);
 
     // If there are more than one pool and we didn't provide hint, we
     // should get the first pool
-    mypool = subnet->getPool();
+    mypool = subnet->getAnyPool(Lease::TYPE_NA);
 
     EXPECT_EQ(mypool, pool1);
 
     // If we provide a hint, we should get a pool that this hint belongs to
-    mypool = subnet->getPool(IOAddress("2001:db8:1:3::dead:beef"));
+    mypool = subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8:1:3::dead:beef"));
 
     EXPECT_EQ(mypool, pool3);
 }
 
+// Check if Subnet6 supports different types of pools properly.
+TEST(Subnet6Test, PoolTypes) {
+
+    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
+
+    PoolPtr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"), 64));
+    PoolPtr pool2(new Pool6(Lease::TYPE_TA, IOAddress("2001:db8:1:2::"), 64));
+    PoolPtr pool3(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:3::"), 64));
+    PoolPtr pool4(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:4::"), 64));
+
+    PoolPtr pool5(new Pool4(IOAddress("192.0.2.0"), 24));
+
+    // There should be no pools of any type by default
+    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Lease::TYPE_NA));
+    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Lease::TYPE_TA));
+    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Lease::TYPE_PD));
+
+    // Trying to get IPv4 pool from Subnet6 is not allowed
+    EXPECT_THROW(subnet->getAnyPool(Lease::TYPE_V4), BadValue);
+
+    // Let's add a single IA pool and check that it can be retrieved
+    EXPECT_NO_THROW(subnet->addPool(pool1));
+
+    // If there's only one IA pool, get that pool
+    EXPECT_EQ(pool1, subnet->getAnyPool(Lease::TYPE_NA));
+    EXPECT_EQ(pool1, subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8:1:1::1")));
+
+    // Check if pools of different type are not returned
+    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Lease::TYPE_TA));
+    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Lease::TYPE_PD));
+
+    // We ask with good hints, but wrong types, should return nothing
+    EXPECT_EQ(PoolPtr(), subnet->getPool(Lease::TYPE_PD, IOAddress("2001:db8:1:2::1")));
+    EXPECT_EQ(PoolPtr(), subnet->getPool(Lease::TYPE_TA, IOAddress("2001:db8:1:3::1")));
+
+    // Let's add TA and PD pools
+    EXPECT_NO_THROW(subnet->addPool(pool2));
+    EXPECT_NO_THROW(subnet->addPool(pool3));
+
+    // Try without hints
+    EXPECT_EQ(pool1, subnet->getAnyPool(Lease::TYPE_NA));
+    EXPECT_EQ(pool2, subnet->getAnyPool(Lease::TYPE_TA));
+    EXPECT_EQ(pool3, subnet->getAnyPool(Lease::TYPE_PD));
+
+    // Try with valid hints
+    EXPECT_EQ(pool1, subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8:1:1::1")));
+    EXPECT_EQ(pool2, subnet->getPool(Lease::TYPE_TA, IOAddress("2001:db8:1:2::1")));
+    EXPECT_EQ(pool3, subnet->getPool(Lease::TYPE_PD, IOAddress("2001:db8:1:3::1")));
+
+    // Try with bogus hints (hints should be ingored)
+    EXPECT_EQ(pool1, subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8:1:7::1")));
+    EXPECT_EQ(pool2, subnet->getPool(Lease::TYPE_TA, IOAddress("2001:db8:1:7::1")));
+    EXPECT_EQ(pool3, subnet->getPool(Lease::TYPE_PD, IOAddress("2001:db8:1:7::1")));
+
+    // Let's add a second PD pool
+    EXPECT_NO_THROW(subnet->addPool(pool4));
+
+    // Without hints, it should return the first pool
+    EXPECT_EQ(pool3, subnet->getAnyPool(Lease::TYPE_PD));
+
+    // With valid hint, it should return that hint
+    EXPECT_EQ(pool3, subnet->getPool(Lease::TYPE_PD, IOAddress("2001:db8:1:3::1")));
+    EXPECT_EQ(pool4, subnet->getPool(Lease::TYPE_PD, IOAddress("2001:db8:1:4::1")));
+
+    // With invalid hint, it should return the first pool
+    EXPECT_EQ(pool3, subnet->getPool(Lease::TYPE_PD, IOAddress("2001:db8::123")));
+
+    // Adding Pool4 to Subnet6 is a big no, no!
+    EXPECT_THROW(subnet->addPool(pool5), BadValue);
+}
+
 TEST(Subnet6Test, Subnet6_Pool6_checks) {
 
     Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
 
     // this one is in subnet
-    Pool6Ptr pool1(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:1::"), 64));
+    Pool6Ptr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"), 64));
     subnet->addPool(pool1);
 
     // this one is larger than the subnet!
-    Pool6Ptr pool2(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::"), 48));
+    Pool6Ptr pool2(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8::"), 48));
 
     EXPECT_THROW(subnet->addPool(pool2), BadValue);
 
 
     // this one is totally out of blue
-    Pool6Ptr pool3(new Pool6(Pool6::TYPE_IA, IOAddress("3000::"), 16));
+    Pool6Ptr pool3(new Pool6(Lease::TYPE_NA, IOAddress("3000::"), 16));
     EXPECT_THROW(subnet->addPool(pool3), BadValue);
 
 
-    Pool6Ptr pool4(new Pool6(Pool6::TYPE_IA, IOAddress("4001:db8:1::"), 80));
+    Pool6Ptr pool4(new Pool6(Lease::TYPE_NA, IOAddress("4001:db8:1::"), 80));
     EXPECT_THROW(subnet->addPool(pool4), BadValue);
 }
 
@@ -458,39 +615,109 @@ TEST(Subnet6Test, getOptionDescriptor) {
     }
 }
 
+
+TEST(Subnet6Test, addVendorOptions) {
+
+    uint32_t vendor_id1 = 12345678;
+    uint32_t vendor_id2 = 87654321;
+    uint32_t vendor_id_bogus = 1111111;
+
+    // Create as subnet to add options to it.
+    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
+
+    // Differentiate options by their codes (100-109)
+    for (uint16_t code = 100; code < 110; ++code) {
+        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
+        ASSERT_NO_THROW(subnet->addVendorOption(option, false, vendor_id1));
+    }
+
+    // Add 7 options to another option space. The option codes partially overlap
+    // with option codes that we have added to dhcp6 option space.
+    for (uint16_t code = 105; code < 112; ++code) {
+        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
+        ASSERT_NO_THROW(subnet->addVendorOption(option, false, vendor_id2));
+    }
+
+    // Get options from the Subnet and check if all 10 are there.
+    Subnet::OptionContainerPtr options = subnet->getVendorOptionDescriptors(vendor_id1);
+    ASSERT_TRUE(options);
+    ASSERT_EQ(10, options->size());
+
+    // Validate codes of options added to dhcp6 option space.
+    uint16_t expected_code = 100;
+    for (Subnet::OptionContainer::const_iterator option_desc = options->begin();
+         option_desc != options->end(); ++option_desc) {
+        ASSERT_TRUE(option_desc->option);
+        EXPECT_EQ(expected_code, option_desc->option->getType());
+        ++expected_code;
+    }
+
+    options = subnet->getVendorOptionDescriptors(vendor_id2);
+    ASSERT_TRUE(options);
+    ASSERT_EQ(7, options->size());
+
+    // Validate codes of options added to isc option space.
+    expected_code = 105;
+    for (Subnet::OptionContainer::const_iterator option_desc = options->begin();
+         option_desc != options->end(); ++option_desc) {
+        ASSERT_TRUE(option_desc->option);
+        EXPECT_EQ(expected_code, option_desc->option->getType());
+        ++expected_code;
+    }
+
+    // Try to get options from a non-existing option space.
+    options = subnet->getVendorOptionDescriptors(vendor_id_bogus);
+    ASSERT_TRUE(options);
+    EXPECT_TRUE(options->empty());
+
+    // Delete options from all spaces.
+    subnet->delVendorOptions();
+
+    // Make sure that all options have been removed.
+    options = subnet->getVendorOptionDescriptors(vendor_id1);
+    ASSERT_TRUE(options);
+    EXPECT_TRUE(options->empty());
+
+    options = subnet->getVendorOptionDescriptors(vendor_id2);
+    ASSERT_TRUE(options);
+    EXPECT_TRUE(options->empty());
+}
+
+
+
 // This test verifies that inRange() and inPool() methods work properly.
 TEST(Subnet6Test, inRangeinPool) {
     Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8::"), 32, 1, 2, 3, 4));
 
     // this one is in subnet
-    Pool6Ptr pool1(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::10"),
+    Pool6Ptr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8::10"),
                              IOAddress("2001:db8::20")));
     subnet->addPool(pool1);
 
     // 192.1.1.1 belongs to the subnet...
     EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::1")));
     // ... but it does not belong to any pool within
-    EXPECT_FALSE(subnet->inPool(IOAddress("2001:db8::1")));
+    EXPECT_FALSE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::1")));
 
     // the last address that is in range, but out of pool
     EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::f")));
-    EXPECT_FALSE(subnet->inPool(IOAddress("2001:db8::f")));
+    EXPECT_FALSE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::f")));
 
     // the first address that is in range, in pool
     EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::10")));
-    EXPECT_TRUE (subnet->inPool(IOAddress("2001:db8::10")));
+    EXPECT_TRUE (subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::10")));
 
     // let's try something in the middle as well
     EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::18")));
-    EXPECT_TRUE (subnet->inPool(IOAddress("2001:db8::18")));
+    EXPECT_TRUE (subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18")));
 
     // the last address that is in range, in pool
     EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::20")));
-    EXPECT_TRUE (subnet->inPool(IOAddress("2001:db8::20")));
+    EXPECT_TRUE (subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::20")));
 
     // the first address that is in range, but out of pool
     EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::21")));
-    EXPECT_FALSE(subnet->inPool(IOAddress("2001:db8::21")));
+    EXPECT_FALSE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::21")));
 }
 
 // This test checks if the toText() method returns text representation
@@ -532,4 +759,39 @@ TEST(Subnet6Test, interfaceId) {
 
 }
 
+// Checks if last allocated address/prefix is stored/retrieved properly
+TEST(Subnet6Test, lastAllocated) {
+    IOAddress ia("2001:db8:1::1");
+    IOAddress ta("2001:db8:1::abcd");
+    IOAddress pd("2001:db8:1::1234:5678");
+
+    IOAddress last("2001:db8:1::ffff:ffff:ffff:ffff");
+
+    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 64, 1, 2, 3, 4));
+
+    // Check initial conditions (all should be set to the last address in range)
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Lease::TYPE_NA).toText());
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Lease::TYPE_TA).toText());
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Lease::TYPE_PD).toText());
+
+    // Now set last allocated for IA
+    EXPECT_NO_THROW(subnet->setLastAllocated(Lease::TYPE_NA, ia));
+    EXPECT_EQ(ia.toText(), subnet->getLastAllocated(Lease::TYPE_NA).toText());
+
+    // TA and PD should be unchanged
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Lease::TYPE_TA).toText());
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Lease::TYPE_PD).toText());
+
+    // Now set TA and PD
+    EXPECT_NO_THROW(subnet->setLastAllocated(Lease::TYPE_TA, ta));
+    EXPECT_NO_THROW(subnet->setLastAllocated(Lease::TYPE_PD, pd));
+
+    EXPECT_EQ(ia.toText(), subnet->getLastAllocated(Lease::TYPE_NA).toText());
+    EXPECT_EQ(ta.toText(), subnet->getLastAllocated(Lease::TYPE_TA).toText());
+    EXPECT_EQ(pd.toText(), subnet->getLastAllocated(Lease::TYPE_PD).toText());
+
+    // No, you can't set the last allocated IPv4 address in IPv6 subnet
+    EXPECT_THROW(subnet->setLastAllocated(Lease::TYPE_V4, ia), BadValue);
+}
+
 };
diff --git a/src/lib/dhcpsrv/tests/test_libraries.h.in b/src/lib/dhcpsrv/tests/test_libraries.h.in
index b5e80a0..a2843dd 100644
--- a/src/lib/dhcpsrv/tests/test_libraries.h.in
+++ b/src/lib/dhcpsrv/tests/test_libraries.h.in
@@ -19,32 +19,18 @@
 
 namespace {
 
-
-// Take care of differences in DLL naming between operating systems.
-
-#ifdef OS_OSX
-#define DLL_SUFFIX ".dylib"
-
-#else
-#define DLL_SUFFIX ".so"
-
-#endif
-
-
 // Names of the libraries used in these tests.  These libraries are built using
 // libtool, so we need to look in the hidden ".libs" directory to locate the
 // shared library.
 
 // Library with load/unload functions creating marker files to check their
 // operation.
-static const char* CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1"
-                                           DLL_SUFFIX;
-static const char* CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2"
-                                           DLL_SUFFIX;
+static const char* CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1.so";
+static const char* CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2.so";
 
 // Name of a library which is not present.
-static const char* NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere"
-                                         DLL_SUFFIX;
+static const char* NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";
+
 } // anonymous namespace
 
 
diff --git a/src/lib/dhcpsrv/tests/test_utils.cc b/src/lib/dhcpsrv/tests/test_utils.cc
index d8794fb..9b4263f 100644
--- a/src/lib/dhcpsrv/tests/test_utils.cc
+++ b/src/lib/dhcpsrv/tests/test_utils.cc
@@ -13,12 +13,35 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include "test_utils.h"
+#include <asiolink/io_address.h>
 #include <gtest/gtest.h>
+#include <sstream>
+
+using namespace std;
+using namespace isc::asiolink;
 
 namespace isc {
 namespace dhcp {
 namespace test {
 
+// IPv4 and IPv6 addresses used in the tests
+const char* ADDRESS4[] = {
+    "192.0.2.0", "192.0.2.1", "192.0.2.2", "192.0.2.3",
+    "192.0.2.4", "192.0.2.5", "192.0.2.6", "192.0.2.7",
+    NULL
+};
+const char* ADDRESS6[] = {
+    "2001:db8::0", "2001:db8::1", "2001:db8::2", "2001:db8::3",
+    "2001:db8::4", "2001:db8::5", "2001:db8::6", "2001:db8::7",
+    NULL
+};
+
+// Lease types that correspond to ADDRESS6 leases
+static const Lease::Type LEASETYPE6[] = {
+    Lease::TYPE_NA, Lease::TYPE_TA, Lease::TYPE_PD, Lease::TYPE_NA,
+    Lease::TYPE_TA, Lease::TYPE_PD, Lease::TYPE_NA, Lease::TYPE_TA
+};
+
 void
 detailCompareLease(const Lease4Ptr& first, const Lease4Ptr& second) {
     // Compare address strings.  Comparison of address objects is not used, as
@@ -75,6 +98,377 @@ detailCompareLease(const Lease6Ptr& first, const Lease6Ptr& second) {
     EXPECT_EQ(first->hostname_, second->hostname_);
 }
 
+
+GenericLeaseMgrTest::GenericLeaseMgrTest()
+    :lmptr_(NULL) {
+    // Initialize address strings and IOAddresses
+    for (int i = 0; ADDRESS4[i] != NULL; ++i) {
+        string addr(ADDRESS4[i]);
+        straddress4_.push_back(addr);
+        IOAddress ioaddr(addr);
+        ioaddress4_.push_back(ioaddr);
+    }
+
+    for (int i = 0; ADDRESS6[i] != NULL; ++i) {
+        string addr(ADDRESS6[i]);
+        straddress6_.push_back(addr);
+        IOAddress ioaddr(addr);
+        ioaddress6_.push_back(ioaddr);
+
+        /// Let's create different lease types. We use LEASETYPE6 values as
+        /// a template
+        leasetype6_.push_back(LEASETYPE6[i]);
+    }
+}
+
+/// @brief Initialize Lease4 Fields
+///
+/// Returns a pointer to a Lease4 structure.  Different values are put into
+/// the lease according to the address passed.
+///
+/// This is just a convenience function for the test methods.
+///
+/// @param address Address to use for the initialization
+///
+/// @return Lease4Ptr.  This will not point to anything if the
+///         initialization failed (e.g. unknown address).
+Lease4Ptr
+GenericLeaseMgrTest::initializeLease4(std::string address) {
+    Lease4Ptr lease(new Lease4());
+
+    // Set the address of the lease
+    lease->addr_ = IOAddress(address);
+
+    // Initialize unused fields.
+    lease->ext_ = 0;                            // Not saved
+    lease->t1_ = 0;                             // Not saved
+    lease->t2_ = 0;                             // Not saved
+    lease->fixed_ = false;                      // Unused
+    lease->comments_ = std::string("");         // Unused
+
+    // Set other parameters.  For historical reasons, address 0 is not used.
+    if (address == straddress4_[0]) {
+        lease->hwaddr_ = vector<uint8_t>(6, 0x08);
+        lease->client_id_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x42)));
+        lease->valid_lft_ = 8677;
+        lease->cltt_ = 168256;
+        lease->subnet_id_ = 23;
+        lease->fqdn_rev_ = true;
+        lease->fqdn_fwd_ = false;
+        lease->hostname_ = "myhost.example.com.";
+
+        } else if (address == straddress4_[1]) {
+        lease->hwaddr_ = vector<uint8_t>(6, 0x19);
+        lease->client_id_ = ClientIdPtr(
+            new ClientId(vector<uint8_t>(8, 0x53)));
+        lease->valid_lft_ = 3677;
+        lease->cltt_ = 123456;
+        lease->subnet_id_ = 73;
+        lease->fqdn_rev_ = true;
+        lease->fqdn_fwd_ = true;
+        lease->hostname_ = "myhost.example.com.";
+
+    } else if (address == straddress4_[2]) {
+        lease->hwaddr_ = vector<uint8_t>(6, 0x2a);
+        lease->client_id_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x64)));
+        lease->valid_lft_ = 5412;
+        lease->cltt_ = 234567;
+        lease->subnet_id_ = 73;                         // Same as lease 1
+        lease->fqdn_rev_ = false;
+        lease->fqdn_fwd_ = false;
+        lease->hostname_ = "";
+
+    } else if (address == straddress4_[3]) {
+        lease->hwaddr_ = vector<uint8_t>(6, 0x19);      // Same as lease 1
+        lease->client_id_ = ClientIdPtr(
+            new ClientId(vector<uint8_t>(8, 0x75)));
+
+        // The times used in the next tests are deliberately restricted - we
+        // should be able to cope with valid lifetimes up to 0xffffffff.
+        //  However, this will lead to overflows.
+        // @TODO: test overflow conditions when code has been fixed
+        lease->valid_lft_ = 7000;
+        lease->cltt_ = 234567;
+        lease->subnet_id_ = 37;
+        lease->fqdn_rev_ = true;
+        lease->fqdn_fwd_ = true;
+        lease->hostname_ = "otherhost.example.com.";
+
+    } else if (address == straddress4_[4]) {
+        lease->hwaddr_ = vector<uint8_t>(6, 0x4c);
+        // Same ClientId as straddr4_[1]
+        lease->client_id_ = ClientIdPtr(
+            new ClientId(vector<uint8_t>(8, 0x53)));    // Same as lease 1
+        lease->valid_lft_ = 7736;
+        lease->cltt_ = 222456;
+        lease->subnet_id_ = 85;
+        lease->fqdn_rev_ = true;
+        lease->fqdn_fwd_ = true;
+        lease->hostname_ = "otherhost.example.com.";
+
+    } else if (address == straddress4_[5]) {
+        lease->hwaddr_ = vector<uint8_t>(6, 0x19);      // Same as lease 1
+        // Same ClientId and IAID as straddress4_1
+        lease->client_id_ = ClientIdPtr(
+            new ClientId(vector<uint8_t>(8, 0x53)));    // Same as lease 1
+        lease->valid_lft_ = 7832;
+        lease->cltt_ = 227476;
+        lease->subnet_id_ = 175;
+        lease->fqdn_rev_ = false;
+        lease->fqdn_fwd_ = false;
+        lease->hostname_ = "otherhost.example.com.";
+        } else if (address == straddress4_[6]) {
+        lease->hwaddr_ = vector<uint8_t>(6, 0x6e);
+        // Same ClientId as straddress4_1
+        lease->client_id_ = ClientIdPtr(
+            new ClientId(vector<uint8_t>(8, 0x53)));    // Same as lease 1
+        lease->valid_lft_ = 1832;
+        lease->cltt_ = 627476;
+        lease->subnet_id_ = 112;
+        lease->fqdn_rev_ = false;
+        lease->fqdn_fwd_ = true;
+        lease->hostname_ = "myhost.example.com.";
+
+    } else if (address == straddress4_[7]) {
+        lease->hwaddr_ = vector<uint8_t>();             // Empty
+        lease->client_id_ = ClientIdPtr();              // Empty
+        lease->valid_lft_ = 7975;
+        lease->cltt_ = 213876;
+        lease->subnet_id_ = 19;
+        lease->fqdn_rev_ = true;
+        lease->fqdn_fwd_ = true;
+        lease->hostname_ = "myhost.example.com.";
+
+    } else {
+        // Unknown address, return an empty pointer.
+        lease.reset();
+
+    }
+
+    return (lease);
+}
+
+/// @brief Initialize Lease6 Fields
+///
+/// Returns a pointer to a Lease6 structure.  Different values are put into
+/// the lease according to the address passed.
+///
+/// This is just a convenience function for the test methods.
+///
+/// @param address Address to use for the initialization
+///
+/// @return Lease6Ptr.  This will not point to anything if the initialization
+///         failed (e.g. unknown address).
+Lease6Ptr
+GenericLeaseMgrTest::initializeLease6(std::string address) {
+    Lease6Ptr lease(new Lease6());
+
+    // Set the address of the lease
+    lease->addr_ = IOAddress(address);
+
+    // Initialize unused fields.
+    lease->t1_ = 0;                             // Not saved
+    lease->t2_ = 0;                             // Not saved
+    lease->fixed_ = false;                      // Unused
+    lease->comments_ = std::string("");         // Unused
+
+    // Set other parameters.  For historical reasons, address 0 is not used.
+    if (address == straddress6_[0]) {
+        lease->type_ = leasetype6_[0];
+        lease->prefixlen_ = 4;
+        lease->iaid_ = 142;
+        lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x77)));
+        lease->preferred_lft_ = 900;
+        lease->valid_lft_ = 8677;
+        lease->cltt_ = 168256;
+        lease->subnet_id_ = 23;
+        lease->fqdn_fwd_ = true;
+        lease->fqdn_rev_ = true;
+        lease->hostname_ = "myhost.example.com.";
+
+    } else if (address == straddress6_[1]) {
+        lease->type_ = leasetype6_[1];
+        lease->prefixlen_ = 0;
+        lease->iaid_ = 42;
+        lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
+        lease->preferred_lft_ = 3600;
+        lease->valid_lft_ = 3677;
+        lease->cltt_ = 123456;
+        lease->subnet_id_ = 73;
+        lease->fqdn_fwd_ = false;
+        lease->fqdn_rev_ = true;
+        lease->hostname_ = "myhost.example.com.";
+
+    } else if (address == straddress6_[2]) {
+        lease->type_ = leasetype6_[2];
+        lease->prefixlen_ = 7;
+        lease->iaid_ = 89;
+        lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x3a)));
+        lease->preferred_lft_ = 1800;
+        lease->valid_lft_ = 5412;
+        lease->cltt_ = 234567;
+        lease->subnet_id_ = 73;                     // Same as lease 1
+        lease->fqdn_fwd_ = false;
+        lease->fqdn_rev_ = false;
+        lease->hostname_ = "myhost.example.com.";
+
+    } else if (address == straddress6_[3]) {
+        lease->type_ = leasetype6_[3];
+        lease->prefixlen_ = 28;
+        lease->iaid_ = 0xfffffffe;
+        vector<uint8_t> duid;
+        for (uint8_t i = 31; i < 126; ++i) {
+            duid.push_back(i);
+        }
+        lease->duid_ = DuidPtr(new DUID(duid));
+
+        // The times used in the next tests are deliberately restricted - we
+        // should be able to cope with valid lifetimes up to 0xffffffff.
+        //  However, this will lead to overflows.
+        // @TODO: test overflow conditions when code has been fixed
+        lease->preferred_lft_ = 7200;
+        lease->valid_lft_ = 7000;
+        lease->cltt_ = 234567;
+        lease->subnet_id_ = 37;
+        lease->fqdn_fwd_ = true;
+        lease->fqdn_rev_ = false;
+        lease->hostname_ = "myhost.example.com.";
+
+    } else if (address == straddress6_[4]) {
+        // Same DUID and IAID as straddress6_1
+        lease->type_ = leasetype6_[4];
+        lease->prefixlen_ = 15;
+        lease->iaid_ = 42;
+        lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
+        lease->preferred_lft_ = 4800;
+        lease->valid_lft_ = 7736;
+        lease->cltt_ = 222456;
+        lease->subnet_id_ = 671;
+        lease->fqdn_fwd_ = true;
+        lease->fqdn_rev_ = true;
+        lease->hostname_ = "otherhost.example.com.";
+
+    } else if (address == straddress6_[5]) {
+        // Same DUID and IAID as straddress6_1
+        lease->type_ = leasetype6_[5];
+        lease->prefixlen_ = 24;
+        lease->iaid_ = 42;                          // Same as lease 4
+        lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
+        // Same as lease 4
+        lease->preferred_lft_ = 5400;
+        lease->valid_lft_ = 7832;
+        lease->cltt_ = 227476;
+        lease->subnet_id_ = 175;
+        lease->fqdn_fwd_ = false;
+        lease->fqdn_rev_ = true;
+        lease->hostname_ = "hostname.example.com.";
+
+    } else if (address == straddress6_[6]) {
+        // Same DUID as straddress6_1
+        lease->type_ = leasetype6_[6];
+        lease->prefixlen_ = 24;
+        lease->iaid_ = 93;
+        lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
+        // Same as lease 4
+        lease->preferred_lft_ = 5400;
+        lease->valid_lft_ = 1832;
+        lease->cltt_ = 627476;
+        lease->subnet_id_ = 112;
+        lease->fqdn_fwd_ = false;
+        lease->fqdn_rev_ = true;
+        lease->hostname_ = "hostname.example.com.";
+
+    } else if (address == straddress6_[7]) {
+        // Same IAID as straddress6_1
+        lease->type_ = leasetype6_[7];
+        lease->prefixlen_ = 24;
+        lease->iaid_ = 42;
+        lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0xe5)));
+        lease->preferred_lft_ = 5600;
+        lease->valid_lft_ = 7975;
+        lease->cltt_ = 213876;
+        lease->subnet_id_ = 19;
+        lease->fqdn_fwd_ = false;
+        lease->fqdn_rev_ = true;
+        lease->hostname_ = "hostname.example.com.";
+
+    } else {
+        // Unknown address, return an empty pointer.
+        lease.reset();
+
+    }
+
+    return (lease);
+}
+
+/// @brief Check Leases present and different
+///
+/// Checks a vector of lease pointers and ensures that all the leases
+/// they point to are present and different.  If not, a GTest assertion
+/// will fail.
+///
+/// @param leases Vector of pointers to leases
+template <typename T>
+void GenericLeaseMgrTest::checkLeasesDifferent(const std::vector<T>& leases) const {
+
+    // Check they were created
+    for (int i = 0; i < leases.size(); ++i) {
+        ASSERT_TRUE(leases[i]);
+    }
+
+    // Check they are different
+    for (int i = 0; i < (leases.size() - 1); ++i) {
+        for (int j = (i + 1); j < leases.size(); ++j) {
+            stringstream s;
+            s << "Comparing leases " << i << " & " << j << " for equality";
+            SCOPED_TRACE(s.str());
+            EXPECT_TRUE(*leases[i] != *leases[j]);
+        }
+    }
+}
+
+/// @brief Creates leases for the test
+///
+/// Creates all leases for the test and checks that they are different.
+///
+/// @return vector<Lease4Ptr> Vector of pointers to leases
+vector<Lease4Ptr>
+GenericLeaseMgrTest::createLeases4() {
+
+    // Create leases for each address
+    vector<Lease4Ptr> leases;
+    for (int i = 0; i < straddress4_.size(); ++i) {
+        leases.push_back(initializeLease4(straddress4_[i]));
+    }
+    EXPECT_EQ(8, leases.size());
+
+    // Check all were created and that they are different.
+    checkLeasesDifferent(leases);
+
+    return (leases);
+}
+
+/// @brief Creates leases for the test
+///
+/// Creates all leases for the test and checks that they are different.
+///
+/// @return vector<Lease6Ptr> Vector of pointers to leases
+vector<Lease6Ptr>
+GenericLeaseMgrTest::createLeases6() {
+
+    // Create leases for each address
+    vector<Lease6Ptr> leases;
+    for (int i = 0; i < straddress6_.size(); ++i) {
+        leases.push_back(initializeLease6(straddress6_[i]));
+    }
+    EXPECT_EQ(8, leases.size());
+
+    // Check all were created and that they are different.
+    checkLeasesDifferent(leases);
+
+    return (leases);
+}
+
 };
 };
 };
diff --git a/src/lib/dhcpsrv/tests/test_utils.h b/src/lib/dhcpsrv/tests/test_utils.h
index 46df9fc..af10a31 100644
--- a/src/lib/dhcpsrv/tests/test_utils.h
+++ b/src/lib/dhcpsrv/tests/test_utils.h
@@ -16,6 +16,8 @@
 #define LIBDHCPSRV_TEST_UTILS_H
 
 #include <dhcpsrv/lease_mgr.h>
+#include <gtest/gtest.h>
+#include <vector>
 
 namespace isc {
 namespace dhcp {
@@ -41,6 +43,73 @@ detailCompareLease(const Lease6Ptr& first, const Lease6Ptr& second);
 void
 detailCompareLease(const Lease4Ptr& first, const Lease4Ptr& second);
 
+/// @brief Test Fixture class with utility functions for LeaseMgr backends
+///
+/// It contains utility functions, like dummy lease creation.
+/// All concrete LeaseMgr test classes should be derived from it.
+class GenericLeaseMgrTest : public ::testing::Test {
+public:
+    GenericLeaseMgrTest();
+
+    /// @brief Initialize Lease4 Fields
+    ///
+    /// Returns a pointer to a Lease4 structure.  Different values are put into
+    /// the lease according to the address passed.
+    ///
+    /// This is just a convenience function for the test methods.
+    ///
+    /// @param address Address to use for the initialization
+    ///
+    /// @return Lease4Ptr.  This will not point to anything if the
+    ///         initialization failed (e.g. unknown address).
+    Lease4Ptr initializeLease4(std::string address);
+
+    /// @brief Initialize Lease6 Fields
+    ///
+    /// Returns a pointer to a Lease6 structure.  Different values are put into
+    /// the lease according to the address passed.
+    ///
+    /// This is just a convenience function for the test methods.
+    ///
+    /// @param address Address to use for the initialization
+    ///
+    /// @return Lease6Ptr.  This will not point to anything if the initialization
+    ///         failed (e.g. unknown address).
+    Lease6Ptr initializeLease6(std::string address);
+
+    /// @brief Check Leases present and different
+    ///
+    /// Checks a vector of lease pointers and ensures that all the leases
+    /// they point to are present and different.  If not, a GTest assertion
+    /// will fail.
+    ///
+    /// @param leases Vector of pointers to leases
+    template <typename T>
+        void checkLeasesDifferent(const std::vector<T>& leases) const;
+
+    /// @brief Creates leases for the test
+    ///
+    /// Creates all leases for the test and checks that they are different.
+    ///
+    /// @return vector<Lease4Ptr> Vector of pointers to leases
+    std::vector<Lease4Ptr> createLeases4();
+
+    /// @brief Creates leases for the test
+    ///
+    /// Creates all leases for the test and checks that they are different.
+    ///
+    /// @return vector<Lease6Ptr> Vector of pointers to leases
+    std::vector<Lease6Ptr> createLeases6();
+
+    // Member variables
+    std::vector<std::string>  straddress4_;   ///< String forms of IPv4 addresses
+    std::vector<isc::asiolink::IOAddress> ioaddress4_;  ///< IOAddress forms of IPv4 addresses
+    std::vector<std::string>  straddress6_;   ///< String forms of IPv6 addresses
+    std::vector<Lease::Type> leasetype6_; ///< Lease types
+    std::vector<isc::asiolink::IOAddress> ioaddress6_;  ///< IOAddress forms of IPv6 addresses
+
+    LeaseMgr*   lmptr_;             ///< Pointer to the lease manager
+};
 
 };
 };
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index bbf33ed..bda4e85 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 CLEANFILES = *.gcno *.gcda
-CLEANFILES += rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc
+CLEANFILES += rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc s-rdatacode
 # These two are created with rrtype/class.h, so not explicitly listed in
 # BUILT_SOURCES.
 CLEANFILES += python/rrtype_constants_inc.cc
@@ -157,8 +157,12 @@ nodist_libb10_dns___la_SOURCES = rdataclass.cc rrparamregistry.cc
 rrclass.h: rrclass-placeholder.h
 rrtype.h: rrtype-placeholder.h
 rrparamregistry.cc: rrparamregistry-placeholder.cc
-rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc: Makefile
+
+s-rdatacode: Makefile
 	$(PYTHON) ./gen-rdatacode.py
+	touch $@
+
+rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc: s-rdatacode
 
 libdns___includedir = $(includedir)/$(PACKAGE_NAME)/dns
 libdns___include_HEADERS = \
diff --git a/src/lib/dns/master_loader.cc b/src/lib/dns/master_loader.cc
index 5c96563..6b6e091 100644
--- a/src/lib/dns/master_loader.cc
+++ b/src/lib/dns/master_loader.cc
@@ -224,15 +224,10 @@ private:
             // after the RR class below.
         }
 
-        const MaybeRRClass rrclass =
-            RRClass::createFromText(rrparam_token.getString());
+        boost::scoped_ptr<RRClass> rrclass
+            (RRClass::createFromText(rrparam_token.getString()));
         if (rrclass) {
-            // FIXME: The following code re-parses the rrparam_token to
-            // make an RRClass instead of using the MaybeRRClass above,
-            // because some old versions of boost::optional (that we
-            // still want to support) have a bug (see trac #2593). This
-            // workaround should be removed at some point in the future.
-            if (RRClass(rrparam_token.getString()) != zone_class_) {
+            if (*rrclass != zone_class_) {
                 isc_throw(InternalException, "Class mismatch: " << *rrclass <<
                           " vs. " << zone_class_);
             }
@@ -280,11 +275,7 @@ private:
     // care about where it comes from).  see LimitTTL() for parameter
     // post_parsing.
     void setDefaultTTL(const RRTTL& ttl, bool post_parsing) {
-        if (!default_ttl_) {
-            default_ttl_.reset(new RRTTL(ttl));
-        } else {
-            *default_ttl_ = ttl;
-        }
+        assignTTL(default_ttl_, ttl);
         limitTTL(*default_ttl_, post_parsing);
     }
 
@@ -296,9 +287,9 @@ private:
         // We use the factory version instead of RRTTL constructor as we
         // need to expect cases where ttl_txt does not actually represent a TTL
         // but an RR class or type.
-        const MaybeRRTTL maybe_ttl = RRTTL::createFromText(ttl_txt);
-        if (maybe_ttl) {
-            current_ttl_ = maybe_ttl;
+        RRTTL* rrttl = RRTTL::createFromText(ttl_txt);
+        if (rrttl) {
+            current_ttl_.reset(rrttl);
             limitTTL(*current_ttl_, false);
             return (true);
         }
@@ -329,7 +320,7 @@ private:
                     dynamic_cast<const rdata::generic::SOA&>(*rdata).
                     getMinimum();
                 setDefaultTTL(RRTTL(ttl_val), true);
-                current_ttl_ = *default_ttl_;
+                assignTTL(current_ttl_, *default_ttl_);
             } else {
                 // On catching the exception we'll try to reach EOL again,
                 // so we need to unget it now.
@@ -338,7 +329,7 @@ private:
                                         "no TTL specified; load rejected");
             }
         } else if (!explicit_ttl && default_ttl_) {
-            current_ttl_ = *default_ttl_;
+            assignTTL(current_ttl_, *default_ttl_);
         } else if (!explicit_ttl && warn_rfc1035_ttl_) {
             // Omitted (class and) TTL values are default to the last
             // explicitly stated values (RFC 1035, Sec. 5.1).
@@ -395,6 +386,17 @@ private:
         }
     }
 
+    /// \brief Assign the right RRTTL's value to the left RRTTL. If one
+    /// doesn't exist in the scoped_ptr, make a new RRTTL copy of the
+    /// right argument.
+    static void assignTTL(boost::scoped_ptr<RRTTL>& left, const RRTTL& right) {
+        if (!left) {
+            left.reset(new RRTTL(right));
+        } else {
+            *left = right;
+        }
+    }
+
 private:
     MasterLexer lexer_;
     const Name zone_origin_;
@@ -407,8 +409,10 @@ private:
     boost::scoped_ptr<RRTTL> default_ttl_; // Default TTL of RRs used when
                                            // unspecified.  If NULL no default
                                            // is known.
-    MaybeRRTTL current_ttl_; // The TTL used most recently.  Initially unset.
-                             // Once set always stores a valid RRTTL.
+    boost::scoped_ptr<RRTTL> current_ttl_; // The TTL used most recently.
+                                           // Initially unset. Once set
+                                           // always stores a valid
+                                           // RRTTL.
     const MasterLoader::Options options_;
     const std::string master_file_;
     std::string string_token_;
diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc
index dba0e7b..89da497 100644
--- a/src/lib/dns/message.cc
+++ b/src/lib/dns/message.cc
@@ -495,7 +495,7 @@ Message::getTSIGRecord() const {
 
 unsigned int
 Message::getRRCount(const Section section) const {
-    if (section >= MessageImpl::NUM_SECTIONS) {
+    if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
         isc_throw(OutOfRange, "Invalid message section: " << section);
     }
     return (impl_->counts_[section]);
@@ -511,7 +511,7 @@ Message::addRRset(const Section section, RRsetPtr rrset) {
         isc_throw(InvalidMessageOperation,
                   "addRRset performed in non-render mode");
     }
-    if (section >= MessageImpl::NUM_SECTIONS) {
+    if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
         isc_throw(OutOfRange, "Invalid message section: " << section);
     }
 
@@ -524,7 +524,7 @@ bool
 Message::hasRRset(const Section section, const Name& name,
                   const RRClass& rrclass, const RRType& rrtype)
 {
-    if (section >= MessageImpl::NUM_SECTIONS) {
+    if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
         isc_throw(OutOfRange, "Invalid message section: " << section);
     }
 
@@ -546,7 +546,7 @@ Message::hasRRset(const Section section, const RRsetPtr& rrset) {
 
 bool
 Message::removeRRset(const Section section, RRsetIterator& iterator) {
-    if (section >= MessageImpl::NUM_SECTIONS) {
+    if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
         isc_throw(OutOfRange, "Invalid message section: " << section);
     }
 
@@ -575,7 +575,7 @@ Message::clearSection(const Section section) {
         isc_throw(InvalidMessageOperation,
                   "clearSection performed in non-render mode");
     }
-    if (section >= MessageImpl::NUM_SECTIONS) {
+    if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
         isc_throw(OutOfRange, "Invalid message section: " << section);
     }
     if (section == Message::SECTION_QUESTION) {
@@ -732,7 +732,7 @@ int
 MessageImpl::parseSection(const Message::Section section,
                           InputBuffer& buffer, Message::ParseOptions options)
 {
-    assert(section < MessageImpl::NUM_SECTIONS);
+    assert(static_cast<int>(section) < MessageImpl::NUM_SECTIONS);
 
     unsigned int added = 0;
 
@@ -984,7 +984,7 @@ Message::clear(Mode mode) {
 
 void
 Message::appendSection(const Section section, const Message& source) {
-    if (section >= MessageImpl::NUM_SECTIONS) {
+    if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
         isc_throw(OutOfRange, "Invalid message section: " << section);
     }
 
@@ -1130,7 +1130,7 @@ Message::endQuestion() const {
 ///
 const SectionIterator<RRsetPtr>
 Message::beginSection(const Section section) const {
-    if (section >= MessageImpl::NUM_SECTIONS) {
+    if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
         isc_throw(OutOfRange, "Invalid message section: " << section);
     }
     if (section == SECTION_QUESTION) {
@@ -1143,7 +1143,7 @@ Message::beginSection(const Section section) const {
 
 const SectionIterator<RRsetPtr>
 Message::endSection(const Section section) const {
-    if (section >= MessageImpl::NUM_SECTIONS) {
+    if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
         isc_throw(OutOfRange, "Invalid message section: " << section);
     }
     if (section == SECTION_QUESTION) {
diff --git a/src/lib/dns/messagerenderer.h b/src/lib/dns/messagerenderer.h
index 092d6de..7defe95 100644
--- a/src/lib/dns/messagerenderer.h
+++ b/src/lib/dns/messagerenderer.h
@@ -367,7 +367,6 @@ public:
     using AbstractMessageRenderer::CASE_INSENSITIVE;
     using AbstractMessageRenderer::CASE_SENSITIVE;
 
-    /// \brief Constructor from an output buffer.
     MessageRenderer();
 
     virtual ~MessageRenderer();
diff --git a/src/lib/dns/rdata/any_255/tsig_250.cc b/src/lib/dns/rdata/any_255/tsig_250.cc
index 796e320..3252cfd 100644
--- a/src/lib/dns/rdata/any_255/tsig_250.cc
+++ b/src/lib/dns/rdata/any_255/tsig_250.cc
@@ -26,6 +26,7 @@
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
 #include <dns/rcode.h>
+#include <dns/tsigkey.h>
 #include <dns/tsigerror.h>
 #include <dns/rdata/generic/detail/lexer_util.h>
 
@@ -75,6 +76,9 @@ TSIGImpl*
 TSIG::constructFromLexer(MasterLexer& lexer, const Name* origin) {
     const Name& algorithm =
         createNameFromLexer(lexer, origin ? origin : &Name::ROOT_NAME());
+    const Name& canonical_algorithm_name =
+        (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ?
+            TSIGKey::HMACMD5_NAME() : algorithm;
 
     const string& time_txt =
         lexer.getNextToken(MasterToken::STRING).getString();
@@ -154,8 +158,8 @@ TSIG::constructFromLexer(MasterLexer& lexer, const Name* origin) {
     // RFC2845 says Other Data is "empty unless Error == BADTIME".
     // However, we don't enforce that.
 
-    return (new TSIGImpl(algorithm, time_signed, fudge, mac, orig_id,
-                         error, other_data));
+    return (new TSIGImpl(canonical_algorithm_name, time_signed, fudge, mac,
+                         orig_id, error, other_data));
 }
 
 /// \brief Constructor from string.
@@ -302,8 +306,11 @@ TSIG::TSIG(InputBuffer& buffer, size_t) :
         buffer.readData(&other_data[0], other_len);
     }
 
-    impl_ = new TSIGImpl(algorithm, time_signed, fudge, mac, original_id,
-                         error, other_data);
+    const Name& canonical_algorithm_name =
+        (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ?
+            TSIGKey::HMACMD5_NAME() : algorithm;
+    impl_ = new TSIGImpl(canonical_algorithm_name, time_signed, fudge, mac,
+                         original_id, error, other_data);
 }
 
 TSIG::TSIG(const Name& algorithm, uint64_t time_signed, uint16_t fudge,
@@ -324,8 +331,11 @@ TSIG::TSIG(const Name& algorithm, uint64_t time_signed, uint16_t fudge,
         isc_throw(InvalidParameter,
                   "TSIG Other data length and data inconsistent");
     }
-    impl_ = new TSIGImpl(algorithm, time_signed, fudge, mac_size, mac,
-                         original_id, error, other_len, other_data);
+    const Name& canonical_algorithm_name =
+        (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ?
+            TSIGKey::HMACMD5_NAME() : algorithm;
+    impl_ = new TSIGImpl(canonical_algorithm_name, time_signed, fudge, mac_size,
+                         mac, original_id, error, other_len, other_data);
 }
 
 /// \brief The copy constructor.
diff --git a/src/lib/dns/rrclass-placeholder.h b/src/lib/dns/rrclass-placeholder.h
index 89dc49d..b4f1851 100644
--- a/src/lib/dns/rrclass-placeholder.h
+++ b/src/lib/dns/rrclass-placeholder.h
@@ -35,16 +35,6 @@ namespace dns {
 // forward declarations
 class AbstractMessageRenderer;
 
-class RRClass; // forward declaration to define MaybeRRClass.
-
-/// \brief A shortcut for a compound type to represent RRClass-or-not.
-///
-/// A value of this type can be interpreted in a boolean context, whose
-/// value is \c true if and only if it contains a valid RRClass object.
-/// And, if it contains a valid RRClass object, its value is accessible
-/// using \c operator*, just like a bare pointer to \c RRClass.
-typedef boost::optional<RRClass> MaybeRRClass;
-
 ///
 /// \brief A standard DNS module exception that is thrown if an RRClass object
 /// is being constructed from an unrecognized string.
@@ -163,12 +153,8 @@ public:
     /// <code>RRClass(const std::string&)</code> constructor.
     ///
     /// If the given text represents a valid RRClass, it returns a
-    /// \c MaybeRRClass object that stores a corresponding \c RRClass
-    /// object, which is accessible via \c operator*().  In this case
-    /// the returned object will be interpreted as \c true in a boolean
-    /// context.  If the given text does not represent a valid RRClass,
-    /// it returns a \c MaybeRRClass object which is interpreted as
-    /// \c false in a boolean context.
+    /// pointer to a new \c RRClass object. If the given text does not
+    /// represent a valid RRClass, it returns \c NULL.
     ///
     /// One main purpose of this function is to minimize the overhead
     /// when the given text does not represent a valid RR class.  For
@@ -183,9 +169,9 @@ public:
     /// This function never throws the \c InvalidRRClass exception.
     ///
     /// \param class_str A string representation of the \c RRClass.
-    /// \return A MaybeRRClass object either storing an RRClass object
-    /// for the given text or a \c false value.
-    static MaybeRRClass createFromText(const std::string& class_str);
+    /// \return A new RRClass object for the given text or a \c NULL
+    /// value.
+    static RRClass* createFromText(const std::string& class_str);
 
     ///
     /// We use the default copy constructor intentionally.
diff --git a/src/lib/dns/rrclass.cc b/src/lib/dns/rrclass.cc
index 76a2e73..81c307c 100644
--- a/src/lib/dns/rrclass.cc
+++ b/src/lib/dns/rrclass.cc
@@ -59,14 +59,14 @@ RRClass::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeUint16(classcode_);
 }
 
-MaybeRRClass
+RRClass*
 RRClass::createFromText(const string& class_str) {
     uint16_t class_code;
     if (RRParamRegistry::getRegistry().textToClassCode(class_str,
                                                        class_code)) {
-        return (MaybeRRClass(class_code));
+        return (new RRClass(class_code));
     }
-    return (MaybeRRClass());
+    return (NULL);
 }
 
 ostream&
diff --git a/src/lib/dns/rrttl.cc b/src/lib/dns/rrttl.cc
index f4b02c0..73ac259 100644
--- a/src/lib/dns/rrttl.cc
+++ b/src/lib/dns/rrttl.cc
@@ -166,13 +166,13 @@ RRTTL::RRTTL(const std::string& ttlstr) {
     }
 }
 
-MaybeRRTTL
+RRTTL*
 RRTTL::createFromText(const string& ttlstr) {
     uint32_t ttlval;
     if (parseTTLString(ttlstr, ttlval, NULL)) {
-        return (MaybeRRTTL(ttlval));
+        return (new RRTTL(ttlval));
     }
-    return (MaybeRRTTL());
+    return (NULL);
 }
 
 RRTTL::RRTTL(InputBuffer& buffer) {
diff --git a/src/lib/dns/rrttl.h b/src/lib/dns/rrttl.h
index 23d57f4..35403b6 100644
--- a/src/lib/dns/rrttl.h
+++ b/src/lib/dns/rrttl.h
@@ -32,16 +32,6 @@ namespace dns {
 // forward declarations
 class AbstractMessageRenderer;
 
-class RRTTL;                    // forward declaration to define MaybeRRTTL
-
-/// \brief A shortcut for a compound type to represent RRTTL-or-not.
-///
-/// A value of this type can be interpreted in a boolean context, whose
-/// value is \c true if and only if it contains a valid RRTTL object.
-/// And, if it contains a valid RRTTL object, its value is accessible
-/// using \c operator*, just like a bare pointer to \c RRTTL.
-typedef boost::optional<RRTTL> MaybeRRTTL;
-
 ///
 /// \brief A standard DNS module exception that is thrown if an RRTTL object
 /// is being constructed from an unrecognized string.
@@ -123,12 +113,9 @@ public:
     /// possible exception handling.   This version is provided for such
     /// purpose.
     ///
-    /// If the given text represents a valid RRTTL, it returns a \c MaybeRRTTL
-    /// object that stores a corresponding \c RRTTL object, which is
-    /// accessible via \c operator*().  In this case the returned object will
-    /// be interpreted as \c true in a boolean context.  If the given text
-    /// does not represent a valid RRTTL, it returns a \c MaybeRRTTL object
-    /// which is interpreted as \c false in a boolean context.
+    /// If the given text represents a valid RRTTL, it returns a pointer
+    /// to a new RRTTL object. If the given text does not represent a
+    /// valid RRTTL, it returns \c NULL..
     ///
     /// One main purpose of this function is to minimize the overhead
     /// when the given text does not represent a valid RR TTL.  For this
@@ -142,9 +129,8 @@ public:
     /// This function never throws the \c InvalidRRTTL exception.
     ///
     /// \param ttlstr A string representation of the \c RRTTL.
-    /// \return An MaybeRRTTL object either storing an RRTTL object for
-    /// the given text or a \c false value.
-    static MaybeRRTTL createFromText(const std::string& ttlstr);
+    /// \return A new RRTTL object for the given text or a \c NULL value.
+    static RRTTL* createFromText(const std::string& ttlstr);
     ///
     //@}
 
diff --git a/src/lib/dns/tests/rdata_tsig_unittest.cc b/src/lib/dns/tests/rdata_tsig_unittest.cc
index d351b40..270a1b2 100644
--- a/src/lib/dns/tests/rdata_tsig_unittest.cc
+++ b/src/lib/dns/tests/rdata_tsig_unittest.cc
@@ -143,6 +143,10 @@ TEST_F(Rdata_TSIG_Test, fromText) {
     // multi-line rdata
     checkFromText_None("hmac-md5.sig-alg.reg.int. ( 1286779327 300 \n"
                        "0 16020 BADKEY 0 )");
+
+    // short-form HMAC-MD5 name
+    const any::TSIG tsig6("hmac-md5. 1286779327 300 0 16020 BADKEY 0");
+    EXPECT_EQ(0, tsig6.compare(rdata_tsig));
 };
 
 TEST_F(Rdata_TSIG_Test, badText) {
diff --git a/src/lib/dns/tests/rrclass_unittest.cc b/src/lib/dns/tests/rrclass_unittest.cc
index 17af873..b79aedf 100644
--- a/src/lib/dns/tests/rrclass_unittest.cc
+++ b/src/lib/dns/tests/rrclass_unittest.cc
@@ -20,10 +20,13 @@
 
 #include <dns/tests/unittest_util.h>
 
+#include <boost/scoped_ptr.hpp>
+
 using namespace std;
 using namespace isc;
 using namespace isc::dns;
 using namespace isc::util;
+using boost::scoped_ptr;
 
 namespace {
 class RRClassTest : public ::testing::Test {
@@ -97,11 +100,12 @@ TEST_F(RRClassTest, toText) {
 }
 
 TEST_F(RRClassTest, createFromText) {
-    const MaybeRRClass rrclass("IN");
-    EXPECT_TRUE(rrclass);
-    EXPECT_EQ("IN", rrclass->toText());
-    EXPECT_TRUE(RRClass::createFromText("CH"));
-    EXPECT_FALSE(RRClass::createFromText("ZZ"));
+    scoped_ptr<RRClass> chclass(RRClass::createFromText("CH"));
+    EXPECT_TRUE(chclass);
+    EXPECT_EQ("CH", chclass->toText());
+
+    scoped_ptr<RRClass> zzclass(RRClass::createFromText("ZZ"));
+    EXPECT_FALSE(zzclass);
 }
 
 TEST_F(RRClassTest, toWireBuffer) {
diff --git a/src/lib/dns/tests/rrttl_unittest.cc b/src/lib/dns/tests/rrttl_unittest.cc
index 8897102..c849c44 100644
--- a/src/lib/dns/tests/rrttl_unittest.cc
+++ b/src/lib/dns/tests/rrttl_unittest.cc
@@ -26,6 +26,7 @@ using namespace std;
 using namespace isc;
 using namespace isc::dns;
 using namespace isc::util;
+using boost::scoped_ptr;
 
 namespace {
 class RRTTLTest : public ::testing::Test {
@@ -75,6 +76,12 @@ TEST_F(RRTTLTest, getValue) {
     EXPECT_EQ(0xffffffff, ttl_max.getValue());
 }
 
+TEST_F(RRTTLTest, copyConstruct) {
+    const RRTTL ttl1(3600);
+    const RRTTL ttl2(ttl1);
+    EXPECT_EQ(ttl1.getValue(), ttl2.getValue());
+}
+
 TEST_F(RRTTLTest, fromText) {
     // Border cases
     EXPECT_EQ(0, RRTTL("0").getValue());
@@ -88,14 +95,14 @@ TEST_F(RRTTLTest, fromText) {
 }
 
 TEST_F(RRTTLTest, createFromText) {
-    // It returns an actual RRTT iff the given text is recognized as a
+    // It returns an actual RRTTL iff the given text is recognized as a
     // valid RR TTL.
-    MaybeRRTTL maybe_ttl = RRTTL::createFromText("3600");
-    EXPECT_TRUE(maybe_ttl);
-    EXPECT_EQ(RRTTL(3600), *maybe_ttl);
+    scoped_ptr<RRTTL> good_ttl(RRTTL::createFromText("3600"));
+    EXPECT_TRUE(good_ttl);
+    EXPECT_EQ(RRTTL(3600), *good_ttl);
 
-    maybe_ttl = RRTTL::createFromText("bad");
-    EXPECT_FALSE(maybe_ttl);
+    scoped_ptr<RRTTL> bad_ttl(RRTTL::createFromText("bad"));
+    EXPECT_FALSE(bad_ttl);
 }
 
 void
diff --git a/src/lib/dns/tests/tsigkey_unittest.cc b/src/lib/dns/tests/tsigkey_unittest.cc
index c1367be..eaf4040 100644
--- a/src/lib/dns/tests/tsigkey_unittest.cc
+++ b/src/lib/dns/tests/tsigkey_unittest.cc
@@ -38,6 +38,7 @@ protected:
 
 TEST_F(TSIGKeyTest, algorithmNames) {
     EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), TSIGKey::HMACMD5_NAME());
+    EXPECT_EQ(Name("hmac-md5"), TSIGKey::HMACMD5_SHORT_NAME());
     EXPECT_EQ(Name("hmac-sha1"), TSIGKey::HMACSHA1_NAME());
     EXPECT_EQ(Name("hmac-sha256"), TSIGKey::HMACSHA256_NAME());
     EXPECT_EQ(Name("hmac-sha224"), TSIGKey::HMACSHA224_NAME());
@@ -47,6 +48,9 @@ TEST_F(TSIGKeyTest, algorithmNames) {
     // Also check conversion to cryptolink definitions
     EXPECT_EQ(isc::cryptolink::MD5, TSIGKey(key_name, TSIGKey::HMACMD5_NAME(),
                                             NULL, 0).getAlgorithm());
+    EXPECT_EQ(isc::cryptolink::MD5,
+              TSIGKey(key_name, TSIGKey::HMACMD5_SHORT_NAME(),
+                      NULL, 0).getAlgorithm());
     EXPECT_EQ(isc::cryptolink::SHA1, TSIGKey(key_name, TSIGKey::HMACSHA1_NAME(),
                                              NULL, 0).getAlgorithm());
     EXPECT_EQ(isc::cryptolink::SHA256, TSIGKey(key_name,
@@ -71,6 +75,13 @@ TEST_F(TSIGKeyTest, construct) {
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, secret.c_str(),
                         secret.size(), key.getSecret(), key.getSecretLength());
 
+    TSIGKey key_short_md5(key_name, TSIGKey::HMACMD5_SHORT_NAME(),
+                          secret.c_str(), secret.size());
+    EXPECT_EQ(key_name, key.getKeyName());
+    EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), key.getAlgorithmName());
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, secret.c_str(),
+                        secret.size(), key.getSecret(), key.getSecretLength());
+
     // "unknown" algorithm is only accepted with empty secret.
     EXPECT_THROW(TSIGKey(key_name, Name("unknown-alg"),
                          secret.c_str(), secret.size()),
diff --git a/src/lib/dns/tsigkey.cc b/src/lib/dns/tsigkey.cc
index 7075203..24a6f57 100644
--- a/src/lib/dns/tsigkey.cc
+++ b/src/lib/dns/tsigkey.cc
@@ -36,6 +36,9 @@ namespace {
         if (name == TSIGKey::HMACMD5_NAME()) {
             return (isc::cryptolink::MD5);
         }
+        if (name == TSIGKey::HMACMD5_SHORT_NAME()) {
+            return (isc::cryptolink::MD5);
+        }
         if (name == TSIGKey::HMACSHA1_NAME()) {
             return (isc::cryptolink::SHA1);
         }
@@ -68,6 +71,9 @@ TSIGKey::TSIGKeyImpl {
     {
         // Convert the key and algorithm names to the canonical form.
         key_name_.downcase();
+        if (algorithm == isc::cryptolink::MD5) {
+            algorithm_name_ = TSIGKey::HMACMD5_NAME();
+        }
         algorithm_name_.downcase();
     }
     Name key_name_;
@@ -206,6 +212,12 @@ Name& TSIGKey::HMACMD5_NAME() {
 }
 
 const
+Name& TSIGKey::HMACMD5_SHORT_NAME() {
+    static Name alg_name("hmac-md5");
+    return (alg_name);
+}
+
+const
 Name& TSIGKey::HMACSHA1_NAME() {
     static Name alg_name("hmac-sha1");
     return (alg_name);
diff --git a/src/lib/dns/tsigkey.h b/src/lib/dns/tsigkey.h
index b10660c..e623be9 100644
--- a/src/lib/dns/tsigkey.h
+++ b/src/lib/dns/tsigkey.h
@@ -203,6 +203,7 @@ public:
     /// We'll add others as we see the need for them.
     //@{
     static const Name& HMACMD5_NAME();    ///< HMAC-MD5 (RFC2845)
+    static const Name& HMACMD5_SHORT_NAME();
     static const Name& HMACSHA1_NAME();   ///< HMAC-SHA1 (RFC4635)
     static const Name& HMACSHA256_NAME(); ///< HMAC-SHA256 (RFC4635)
     static const Name& HMACSHA224_NAME(); ///< HMAC-SHA256 (RFC4635)
diff --git a/src/lib/hooks/Makefile.am b/src/lib/hooks/Makefile.am
index d9ea39e..a8d7af1 100644
--- a/src/lib/hooks/Makefile.am
+++ b/src/lib/hooks/Makefile.am
@@ -12,8 +12,11 @@ AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 
 
 # Define rule to build logging source files from message file
-hooks_messages.h hooks_messages.cc: hooks_messages.mes
+hooks_messages.h hooks_messages.cc: s-messages
+
+s-messages: hooks_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/hooks/hooks_messages.mes
+	touch $@
 
 # Tell automake that the message files are built as part of the build process
 # (so that they are built before the main library is built).
@@ -23,13 +26,13 @@ BUILT_SOURCES = hooks_messages.h hooks_messages.cc
 EXTRA_DIST = hooks_messages.mes
 
 # Get rid of generated message files on a clean
-CLEANFILES = *.gcno *.gcda hooks_messages.h hooks_messages.cc
+CLEANFILES = *.gcno *.gcda hooks_messages.h hooks_messages.cc s-messages
 
 lib_LTLIBRARIES = libb10-hooks.la
 libb10_hooks_la_SOURCES  =
 libb10_hooks_la_SOURCES += callout_handle.cc callout_handle.h
 libb10_hooks_la_SOURCES += callout_manager.cc callout_manager.h
-libb10_hooks_la_SOURCES += hooks.h
+libb10_hooks_la_SOURCES += hooks.h hooks.cc
 libb10_hooks_la_SOURCES += hooks_log.cc hooks_log.h
 libb10_hooks_la_SOURCES += hooks_manager.cc hooks_manager.h
 libb10_hooks_la_SOURCES += library_handle.cc library_handle.h
diff --git a/src/lib/hooks/callout_handle.cc b/src/lib/hooks/callout_handle.cc
index ce9ef82..cbd992c 100644
--- a/src/lib/hooks/callout_handle.cc
+++ b/src/lib/hooks/callout_handle.cc
@@ -30,7 +30,8 @@ namespace hooks {
 CalloutHandle::CalloutHandle(const boost::shared_ptr<CalloutManager>& manager,
                     const boost::shared_ptr<LibraryManagerCollection>& lmcoll)
     : lm_collection_(lmcoll), arguments_(), context_collection_(),
-      manager_(manager), skip_(false) {
+      manager_(manager), server_hooks_(ServerHooks::getServerHooks()),
+      skip_(false) {
 
     // Call the "context_create" hook.  We should be OK doing this - although
     // the constructor has not finished running, all the member variables
@@ -148,7 +149,7 @@ CalloutHandle::getHookName() const {
     // ... and look up the hook.
     string hook = "";
     try {
-        hook = ServerHooks::getServerHooks().getName(index);
+        hook = server_hooks_.getName(index);
     } catch (const NoSuchHook&) {
         // Hook index is invalid, so this methods probably called from outside
         // a callout being executed via a call to CalloutManager::callCallouts.
diff --git a/src/lib/hooks/callout_handle.h b/src/lib/hooks/callout_handle.h
index eb57fd4..91d7f23 100644
--- a/src/lib/hooks/callout_handle.h
+++ b/src/lib/hooks/callout_handle.h
@@ -28,6 +28,8 @@
 namespace isc {
 namespace hooks {
 
+class ServerHooks;
+
 /// @brief No such argument
 ///
 /// Thrown if an attempt is made access an argument that does not exist.
@@ -369,6 +371,11 @@ private:
     /// Callout manager.
     boost::shared_ptr<CalloutManager> manager_;
 
+    /// Reference to the singleton ServerHooks object.  See the
+    /// @ref hooksmgMaintenanceGuide for information as to why the class holds
+    /// a reference instead of accessing the singleton within the code.
+    ServerHooks& server_hooks_;
+
     /// "Skip" flag, indicating if the caller should bypass remaining callouts.
     bool skip_;
 };
diff --git a/src/lib/hooks/callout_manager.cc b/src/lib/hooks/callout_manager.cc
index 166fda1..9dd5c60 100644
--- a/src/lib/hooks/callout_manager.cc
+++ b/src/lib/hooks/callout_manager.cc
@@ -31,7 +31,8 @@ namespace hooks {
 
 // Constructor
 CalloutManager::CalloutManager(int num_libraries)
-    : current_hook_(-1), current_library_(-1),
+    : server_hooks_(ServerHooks::getServerHooks()),
+      current_hook_(-1), current_library_(-1),
       hook_vector_(ServerHooks::getServerHooks().getCount()),
       library_handle_(this), pre_library_handle_(this, 0),
       post_library_handle_(this, INT_MAX), num_libraries_(num_libraries)
@@ -72,7 +73,7 @@ CalloutManager::registerCallout(const std::string& name, CalloutPtr callout) {
 
     // Get the index associated with this hook (validating the name in the
     // process).
-    int hook_index = ServerHooks::getServerHooks().getIndex(name);
+    int hook_index = server_hooks_.getIndex(name);
 
     // Iterate through the callout vector for the hook from start to end,
     // looking for the first entry where the library index is greater than
@@ -147,21 +148,19 @@ CalloutManager::callCallouts(int hook_index, CalloutHandle& callout_handle) {
                 if (status == 0) {
                     LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS,
                               HOOKS_CALLOUT_CALLED).arg(current_library_)
-                        .arg(ServerHooks::getServerHooks()
-                            .getName(current_hook_))
+                        .arg(server_hooks_.getName(current_hook_))
                         .arg(PointerConverter(i->second).dlsymPtr());
                 } else {
                     LOG_ERROR(hooks_logger, HOOKS_CALLOUT_ERROR)
                         .arg(current_library_)
-                        .arg(ServerHooks::getServerHooks()
-                            .getName(current_hook_))
+                        .arg(server_hooks_.getName(current_hook_))
                         .arg(PointerConverter(i->second).dlsymPtr());
                 }
             } catch (const std::exception& e) {
                 // Any exception, not just ones based on isc::Exception
                 LOG_ERROR(hooks_logger, HOOKS_CALLOUT_EXCEPTION)
                     .arg(current_library_)
-                    .arg(ServerHooks::getServerHooks().getName(current_hook_))
+                    .arg(server_hooks_.getName(current_hook_))
                     .arg(PointerConverter(i->second).dlsymPtr())
                     .arg(e.what());
             }
@@ -184,7 +183,7 @@ CalloutManager::deregisterCallout(const std::string& name, CalloutPtr callout) {
 
     // Get the index associated with this hook (validating the name in the
     // process).
-    int hook_index = ServerHooks::getServerHooks().getIndex(name);
+    int hook_index = server_hooks_.getIndex(name);
 
     /// Construct a CalloutEntry matching the current library and the callout
     /// we want to remove.
@@ -227,7 +226,7 @@ CalloutManager::deregisterAllCallouts(const std::string& name) {
 
     // Get the index associated with this hook (validating the name in the
     // process).
-    int hook_index = ServerHooks::getServerHooks().getIndex(name);
+    int hook_index = server_hooks_.getIndex(name);
 
     /// Construct a CalloutEntry matching the current library (the callout
     /// pointer is NULL as we are not checking that).
diff --git a/src/lib/hooks/callout_manager.h b/src/lib/hooks/callout_manager.h
index e1b9e57..8ca275b 100644
--- a/src/lib/hooks/callout_manager.h
+++ b/src/lib/hooks/callout_manager.h
@@ -338,6 +338,13 @@ private:
         }
     };
 
+    // Member variables
+
+    /// Reference to the singleton ServerHooks object.  See the
+    /// @ref hooksmgMaintenanceGuide for information as to why the class holds
+    /// a reference instead of accessing the singleton within the code.
+    ServerHooks& server_hooks_;
+
     /// Current hook.  When a call is made to callCallouts, this holds the
     /// index of the current hook.  It is set to an invalid value (-1)
     /// otherwise.
diff --git a/src/lib/hooks/hooks.cc b/src/lib/hooks/hooks.cc
new file mode 100644
index 0000000..e5645d5
--- /dev/null
+++ b/src/lib/hooks/hooks.cc
@@ -0,0 +1,34 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <hooks/hooks.h>
+#include <log/logger_support.h>
+
+#include <string>
+
+
+namespace isc {
+namespace hooks {
+
+// Load the logging message dictionary if not already loaded
+
+void
+hooksStaticLinkInit() {
+    if (!isc::log::isLoggingInitialized()) {
+        isc::log::initLogger(std::string("userlib"));
+    }
+}
+
+} // namespace hooks
+} // namespace isc
diff --git a/src/lib/hooks/hooks.h b/src/lib/hooks/hooks.h
index a059d79..42dfaca 100644
--- a/src/lib/hooks/hooks.h
+++ b/src/lib/hooks/hooks.h
@@ -15,6 +15,7 @@
 #ifndef HOOKS_H
 #define HOOKS_H
 
+#include <config.h>
 #include <hooks/callout_handle.h>
 #include <hooks/library_handle.h>
 
@@ -35,4 +36,42 @@ typedef int (*unload_function_ptr)();
 
 } // Anonymous namespace
 
+namespace isc {
+namespace hooks {
+
+/// @brief User-Library Initialization for Statically-Linked BIND 10
+///
+/// If BIND 10 is statically-linked, a user-created hooks library will not be
+/// able to access symbols in it.  In particular, it will not be able to access
+/// singleton objects.
+///
+/// The hooks framework handles some of this.  For example, although there is
+/// a singleton ServerHooks object, hooks framework objects store a reference
+/// to it when they are created.  When the user library needs to register a
+/// callout (which requires access to the ServerHooks information), it accesses
+/// the ServerHooks object through a pointer passed from the BIND 10 image.
+///
+/// The logging framework is more problematical. Here the code is partly
+/// statically linked (the BIND 10 logging library) and partly shared (the
+/// log4cplus).  The state of the former is not accessible to the user library,
+/// but the state of the latter is.  So within the user library, we need to
+/// initialize the BIND 10 logging library but not initialize the log4cplus
+/// code.  Some of the initialization is done when the library is loaded, but
+/// other parts are done at run-time.
+///
+/// This function - to be called by the user library code in its load() function
+/// when running against a statically linked BIND 10 - initializes the BIND 10
+/// logging library.  In particular, it loads the message dictionary with the
+/// text of the BIND 10 messages.
+///
+/// @note This means that the virtual address space is loaded with two copies
+/// of the message dictionary.  Depending on how the user libraries are linked,
+/// loading multiple user libraries may involve loading one message dictionary
+/// per library.
+
+void hooksStaticLinkInit();
+
+} // namespace hooks
+} // namespace isc
+
 #endif  // HOOKS_H
diff --git a/src/lib/hooks/hooks_maintenance.dox b/src/lib/hooks/hooks_maintenance.dox
index c3c4946..51a07dc 100644
--- a/src/lib/hooks/hooks_maintenance.dox
+++ b/src/lib/hooks/hooks_maintenance.dox
@@ -271,4 +271,112 @@
  this may mean the server suspending all processing of incoming requests
  until all currently executing requests have completed and data object
  destroyed, reloading the libraries, then resuming processing.
+
+ @subsection hooksmgStaticLinking Hooks and Statically-Linked BIND 10
+
+ BIND 10 has the configuration option to allow static linking.  What this
+ means is that it links against the static BIND 10 libraries and not
+ the shareable ones - although it links against the shareable system
+ libraries like "libc" and "libstdc++" and well as the sharable libraries
+ for third-party packages such as log4cplus and MySql.
+
+ Static linking poses a problem for dynamically-loaded hooks libraries
+ as some of the code in them - in particular the hooks framework and
+ the logging code - depend on global objects created within the BIND
+ 10 libraries.  In the normal course of events (BIND 10 linked against
+ shared libraries), when BIND 10 is run and the operating system loads
+ a BIND 10 shared library containing a global object, address space
+ is assigned for it.  When the hooks framework loads a user-library
+ linked against the same BIND 10 shared library, the operating system
+ recognises that the library is already loaded (and initialized) and
+ uses its definition of the global object.  Thus both the code in the
+ BIND 10 image and the code in the user-written shared library
+ reference the same object.
+
+ If BIND 10 is statically linked, the linker allocates address space
+ in the BIND 10 image for the global object and does not include any
+ reference to the shared library containing it.  When BIND 10 now loads
+ the user-written shared library - and so loads the BIND 10 library code
+ containing the global object - the operating system does not know that
+ the object already exists.  Instead, it allocates new address space.
+ The version of BIND 10 in memory therefore has two copies of the object:
+ one referenced by code in the BIND 10 image, and one referenced by code
+ in the user-written hooks library.  This causes problems - information
+ put in one copy is not available to the other.
+
+ Particular problems were encountered with global objects the hooks library
+ and in the logging library, so some code to alleviate the problem has been
+ included.
+
+ The issue in the hooks library is the singleton @ref
+ isc::hooks::ServerHooks object, used by the user-written hooks library
+ if it attempts to register or deregister callouts.  The contents of the
+ singleton - the names of the hook points and their index - are set by
+ the relevant BIND 10 server; this information is not available in the
+ singleton created in the user's hooks library.
+
+ Within the code users by the user's hooks library, the ServerHooks
+ object is used by @ref isc::hooks::CalloutHandle and @ref
+ isc::hooks::CalloutManager objects.  Both these objects are passed to the
+ hooks library code when a callout is called: the former directly through
+ the callout argument list, the latter indirectly as a pointer to it is
+ stored in the CalloutHandle.  This allows a solution to the problem:
+ instead of accessing the singleton via ServerHooks::getServerHooks(),
+ the constructors of these objects store a reference to the singleton
+ ServerHooks when they are created and use that reference to access
+ ServerHooks data.  Since both CalloutHandle and CalloutManager are
+ created in the statically-linked BIND 10 server, use of the reference
+ means that it is the singleton within the server - and not the one
+ within the user's hooks library - that is referenced.
+
+ The solution of the logging problem is not so straightforward.  Within
+ BIND 10, there are two logging components, the BIND 10 logging framework
+ and the log4cplus libraries.  Owing to static linking, there are two
+ instances of the former; but as static linking uses shared libraries of
+ third-party products, there is one instance of the latter.  What further
+ complicates matters is that initialization of the logging framework is
+ in two parts: static initialization and run-time initialization.
+
+ The logging initialization comprises the following:
+
+ -# Static initialization of the log4cplus global variables.
+ -# Static initialization of messages in the various BIND 10 libraries.
+ -# Static initialization of logging framework.
+ -# Run-time initialization of the logging framework.
+ -# Run-time initialization of log4cplus
+
+ As both the BIND 10 server and the user-written hooks libraries use the
+ log4cplus shared library, item 1 - the static initialization of the log4cplus
+ global variables is performed once.
+
+ The next two tasks - static initialization of the messages in the BIND
+ 10 libraries and the static initialization of the logging framework -
+ are performed twice, once in the context of the BIND 10 server and
+ once in the context of the hooks library.  For this reason, run-time
+ initialization of the logging framework needs to be performed twice,
+ once in the context of the BIND 10 server and once in the context of the
+ user-written hooks library.  However, the standard logging framework
+ initialization code also performs the last task, initialization of
+ log4cplus, something that causes problems if executed more than once.
+
+ To get round this, the function isc::hooks::hooksStaticLinkInit()
+ has been written.  It executes the only part of the logging framework
+ run-time initialization that actually pertains to the logging framework
+ and not log4cplus, namely loading the message dictionary with the
+ statically-initialized messages in the BIND 10 libraries.
+ This should be executed by any hooks library linking against a statically
+ initialized BIND 10.  (In fact, running it against a dynamically-linked
+ BIND 10 should have no effect, as the load operation discards any duplicate
+ message entries.)  The hooks library tests do this, the code being
+ copnditionally compiled within a test of the USE_STATIC_LINK macro, set
+ by the configure script.
+
+ @note Not everything is completely rosy with logging and static linking.
+ In particular, there appears to be an issue with the scenario where a
+ user-written hooks library is run by a statically-linked BIND 10 and then
+ unloaded.  As far as can be determined, on unload the system attempts to
+ delete the same logger twice.  This is alleviated by explictly clearing
+ the loggerptr_ variable in the isc::log::Logger destructor, but there
+ is a suspicion that some memory might be lost in these circumstances.
+ This is still under investigation.
 */
diff --git a/src/lib/hooks/hooks_messages.mes b/src/lib/hooks/hooks_messages.mes
index ebaed41..53090ab 100644
--- a/src/lib/hooks/hooks_messages.mes
+++ b/src/lib/hooks/hooks_messages.mes
@@ -109,6 +109,14 @@ was called.  The function threw an exception (an error indication)
 during execution, which is an error condition.  The library has been
 unloaded and no callouts from it will be installed.
 
+% HOOKS_LOAD_FRAMEWORK_EXCEPTION 'load' function in hook library %1 threw an exception: reason %2
+A "load" function was found in the library named in the message and
+was called.  Either the hooks framework or the function threw an
+exception (an error indication) during execution, which is an error
+condition; the cause of the exception is recorded in the message.
+The library has been unloaded and no callouts from it will be
+installed.
+
 % HOOKS_LOAD_SUCCESS 'load' function in hook library %1 returned success
 This is a debug message issued when the "load" function has been found
 in a hook library and has been successfully called.
@@ -152,6 +160,13 @@ called, but in the process generated an exception (an error indication).
 The unload process continued after this message and the library has
 been unloaded.
 
+% HOOKS_UNLOAD_FRAMEWORK_EXCEPTION 'unload' function in hook library %1 threw an exception, reason %2
+During the unloading of a library, an "unload" function was found.
+It was called, but in the process either it or the hooks framework
+generated an exception (an error indication); the cause of the error
+is recorded in the message.  The unload process continued after
+this message and the library has been unloaded.
+
 % HOOKS_UNLOAD_SUCCESS 'unload' function in hook library %1 returned success
 This is a debug message issued when an "unload" function has been found
 in a hook library during the unload process, called, and returned success.
diff --git a/src/lib/hooks/hooks_user.dox b/src/lib/hooks/hooks_user.dox
index 8aa75c1..9c56861 100644
--- a/src/lib/hooks/hooks_user.dox
+++ b/src/lib/hooks/hooks_user.dox
@@ -156,7 +156,7 @@ int version() {
     return (BIND10_HOOKS_VERSION);
 }
 
-};
+}
 @endcode
 
 The file "hooks/hooks.h" is specified relative to the BIND 10 libraries
@@ -211,6 +211,8 @@ extern std::fstream interesting;
 #include <hooks/hooks.h>
 #include "library_common.h"
 
+using namespace isc::hooks;
+
 // "Interesting clients" log file handle definition.
 std::fstream interesting;
 
@@ -229,7 +231,7 @@ int unload() {
     return (0);
 }
 
-};
+}
 @endcode
 
 Notes:
@@ -276,7 +278,7 @@ All callouts are declared with the signature:
 @code
 extern "C" {
 int callout(CalloutHandle& handle);
-};
+}
 @endcode
 
 (As before, the callout is declared with "C" linkage.)  Information is passed
@@ -454,16 +456,15 @@ hardware address of the incoming packet, classify it, and write it,
 together with the assigned IP address, to a log file.  Although we could
 do this in one callout, for this example we'll use two:
 
-- pkt_rcvd - a callout on this hook is invoked when a packet has been
-received and has been parsed.  It is passed a single argument, "query"
+- pkt4_receive - a callout on this hook is invoked when a packet has been
+received and has been parsed.  It is passed a single argument, "query4"
 which is an isc::dhcp::Pkt4 object (representing a DHCP v4 packet).
 We will do the classification here.
 
-- v4_lease_write_post - called when the lease (an assignment of an IPv4
-address to a client for a fixed period of time) has been written to the
-database. It is passed two arguments, the query ("query")
-and the response (called "reply").  This is the point at which the
-example code will write the hardware and IP addresses to the log file.
+- pkt4_send - called when a response is just about to be sent back to
+the client.  It is passed a single argument "response4".  This is the
+point at which the example code will write the hardware and IP addresses
+to the log file.
 
 The standard for naming callouts is to give them the same name as
 the hook.  If this is done, the callouts will be automatically found
@@ -473,7 +474,7 @@ case, so the code for the first callout (used to classify the client's
 hardware address) is:
 
 @code
-// pkt_rcvd.cc
+// pkt_receive4.cc
 
 #include <hooks/hooks.h>
 #include <dhcp/pkt4.h>
@@ -482,47 +483,51 @@ hardware address) is:
 #include <string>
 
 using namespace isc::dhcp;
+using namespace isc::hooks;
 using namespace std;
 
 extern "C" {
 
-// This callout is called at the "pkt_rcvd" hook.
-int pkt_rcvd(CalloutHandle& handle) {
+// This callout is called at the "pkt4_receive" hook.
+int pkt4_receive(CalloutHandle& handle) {
 
     // A pointer to the packet is passed to the callout via a "boost" smart
     // pointer. The include file "pkt4.h" typedefs a pointer to the Pkt4
     // object as Pkt4Ptr.  Retrieve a pointer to the object.
-    Pkt4Ptr query_ptr;
-    handle.getArgument("query", query_ptr);
+    Pkt4Ptr query4_ptr;
+    handle.getArgument("query4", query4_ptr);
 
     // Point to the hardware address.
-    HwAddrPtr hwaddr_ptr = query_ptr->getHWAddr();
+    HWAddrPtr hwaddr_ptr = query4_ptr->getHWAddr();
 
     // The hardware address is held in a public member variable. We'll classify
     // it as interesting if the sum of all the bytes in it is divisible by 4.
     //  (This is a contrived example after all!)
     long sum = 0;
     for (int i = 0; i < hwaddr_ptr->hwaddr_.size(); ++i) {
-        sum += hwaddr_ptr->hwadr_[i];
+        sum += hwaddr_ptr->hwaddr_[i];
     }
 
     // Classify it.
     if (sum % 4 == 0) {
         // Store the text form of the hardware address in the context to pass
         // to the next callout.
-        handle.setContext("hwaddr", hwaddr_ptr->hwaddr_.toText());
+        string hwaddr = hwaddr_ptr->toText();
+        handle.setContext("hwaddr", hwaddr);
     }
 
     return (0);
 };
+
+}
 @endcode
 
-The pct_rcvd callout placed the hardware address of an interesting client in
+The pkt4_receive callout placed the hardware address of an interesting client in
 the "hwaddr" context for the packet.  Turning now to the callout that will
 write this information to the log file:
 
 @code
-// v4_lease_write.cc
+// pkt4_send.cc
 
 #include <hooks/hooks.h>
 #include <dhcp/pkt4.h>
@@ -531,28 +536,28 @@ write this information to the log file:
 #include <string>
 
 using namespace isc::dhcp;
+using namespace isc::hooks;
 using namespace std;
 
 extern "C" {
 
-// This callout is called at the "v4_lease_write_post" hook.
-int v4_lease_write_post(CalloutHandle& handle) {
+// This callout is called at the "pkt4_send" hook.
+int pkt4_send(CalloutHandle& handle) {
 
     // Obtain the hardware address of the "interesting" client.  We have to
     // use a try...catch block here because if the client was not interesting,
     // no information would be set and getArgument would thrown an exception.
     string hwaddr;
-    try (handle.getArgument("hwaddr", hwaddr) {
+    try {
+        handle.getContext("hwaddr", hwaddr);
 
-        // getArgument didn't throw so the client is interesting.  Get a pointer
-        // to the reply.  Note that the argument list for this hook also
-        // contains a pointer to the query: we don't need to access that in this
-        // example.
-        Pkt4Ptr reply;
-        handle.getArgument("reply", reply);
+        // getContext didn't throw so the client is interesting.  Get a pointer
+        // to the reply.
+        Pkt4Ptr response4_ptr;
+        handle.getArgument("response4", response4_ptr);
 
         // Get the string form of the IP address.
-        string ipaddr = reply->getYiaddr().toText();
+        string ipaddr = response4_ptr->getYiaddr().toText();
 
         // Write the information to the log file.
         interesting << hwaddr << " " << ipaddr << "\n";
@@ -561,16 +566,15 @@ int v4_lease_write_post(CalloutHandle& handle) {
         flush(interesting);
 
     } catch (const NoSuchCalloutContext&) {
-
-        // No such element in the per-request context with the name
-        // "hwaddr".  We will do nothing, so just dismiss the exception.
-
-    }
+        // No such element in the per-request context with the name "hwaddr".
+        // This means that the request was not an interesting, so do nothing
+        // and dismiss the exception.
+     }
 
     return (0);
 }
 
-};
+}
 @endcode
 
 @subsection hooksdgBuild Building the Library
@@ -586,9 +590,9 @@ command line needed to create the library using the Gnu C++ compiler on a
 Linux system is:
 
 @code
-g++ -I /usr/include/bind10 -L /usr/lib/bind10 -fpic -shared -o example.so \
-    load_unload.cc pkt_rcvd.cc v4_lease_write.cc version.cc \
-    -lb10-dhcp++ -lb10-util -lb10-exceptions
+g++ -I /usr/include/bind10 -L /usr/lib/bind10/lib -fpic -shared -o example.so \
+    load_unload.cc pkt4_receive.cc pkt4_send.cc version.cc \
+    -lb10-dhcpsrv -lb10-dhcp++ -lb10-hooks -lb10-log -lb10-util -lb10-exceptions
 @endcode
 
 Notes:
@@ -621,6 +625,11 @@ module, the following bindctl commands must be executed:
 The DHCPv4 server will load the library and execute the callouts each time a
 request is received.
 
+ at note The above assumes that the hooks library will be used with a version of
+BIND 10 that is dynamically-linked.  For information regarding running
+hooks libraries against a statically-linked BIND 10, see
+ at ref hooksdgStaticallyLinkedBind10.
+
 @section hooksdgAdvancedTopics Advanced Topics
 
 @subsection hooksdgContextCreateDestroy Context Creation and Destruction
@@ -633,12 +642,12 @@ to initialize per-request context. The second is called after all
 server-defined hooks have been processed, and is to allow a library to
 tidy up.
 
-As an example, the v4_lease_write example above required that the code
+As an example, the pkt4_send example above required that the code
 check for an exception being thrown when accessing the "hwaddr" context
 item in case it was not set.  An alternative strategy would have been to
 provide a callout for the "context_create" hook and set the context item
 "hwaddr" to an empty string. Instead of needing to handle an exception,
-v4_lease_write would be guaranteed to get something when looking for
+pkt4_send would be guaranteed to get something when looking for
 the hwaddr item and so could write or not write the output depending on
 the value.
 
@@ -662,8 +671,8 @@ Here it is assumed that the hooks library is performing some form of
 security checking on the packet and needs to maintain information in
 a user-specified "SecurityInformation" object. (The details of this
 fictitious object are of no concern here.) The object is created in
-the context_create callout and used in both the pkt4_rcvd and the
-v4_lease_write_post callouts.
+the context_create callout and used in both the pkt4_receive and the
+pkt4_send callouts.
 
 @code
 // Storing information in a "raw" pointer.  Assume that the
@@ -682,7 +691,7 @@ int context_create(CalloutHandle& handle) {
 }
 
 // Callouts that use the context
-int pktv_rcvd(CalloutHandle& handle) {
+int pkt4_receive(CalloutHandle& handle) {
     // Retrieve the pointer to the SecurityInformation object
     SecurityInformation si;
     handle.getContext("security_information", si);
@@ -695,7 +704,7 @@ int pktv_rcvd(CalloutHandle& handle) {
     // altered, so there is no need to call setContext() again.
 }
 
-int v4_lease_write_post(CalloutHandle& handle) {
+int pkt4_send(CalloutHandle& handle) {
     // Retrieve the pointer to the SecurityInformation object
     SecurityInformation si;
     handle.getContext("security_information", si);
@@ -741,9 +750,9 @@ int context_create(CalloutHandle& handle) {
 }
 
 // Other than the data type, a shared pointer has similar semantics to a "raw"
-// pointer.  Only the code from pkt_rcvd is shown here.
+// pointer.  Only the code from pkt4_receive is shown here.
 
-int pktv_rcvd(CalloutHandle& handle) {
+int pkt4_receive(CalloutHandle& handle) {
     // Retrieve the pointer to the SecurityInformation object
     boost::shared_ptr<SecurityInformation> si;
     handle.setContext("security_information", si);
@@ -773,7 +782,7 @@ As briefly mentioned in @ref hooksdgExampleCallouts, the standard is for
 callouts in the user library to have the same name as the name of the
 hook to which they are being attached.  This convention was followed
 in the tutorial, e.g.  the callout that needed to be attached to the
-"pkt_rcvd" hook was named pkt_rcvd.
+"pkt4_receive" hook was named pkt4_receive.
 
 The reason for this convention is that when the library is loaded, the
 hook framework automatically searches the library for functions with
@@ -805,7 +814,7 @@ The following sections cover some of the ways in which these can be used.
 
 The example in the tutorial used standard names for the callouts.  As noted
 above, it is possible to use non-standard names.  Suppose, instead of the
-callout names "pkt_rcvd" and "v4_lease_write", we had named our callouts
+callout names "pkt4_receive" and "pkt4_send", we had named our callouts
 "classify" and "write_data".  The hooks framework would not have registered
 these callouts, so we would have needed to do it ourself.  The place to
 do this is the "load" framework function, and its code would have had to
@@ -815,8 +824,8 @@ been modified to:
 int load(LibraryHandle& libhandle) {
     // Register the callouts on the hooks. We assume that a header file
     // declares the "classify" and "write_data" functions.
-    libhandle.registerCallout("pkt_rcvd", classify);
-    libhandle.registerCallout("v4_lease_write", write_data);
+    libhandle.registerCallout("pkt4_receive", classify);
+    libhandle.registerCallout("pkt4_send", write_data);
 
     // Open the log file
     interesting.open("/data/clients/interesting.log",
@@ -839,8 +848,8 @@ To register multiple callouts on a hook, just call
 LibraryHandle::registerCallout multiple times on the same hook, e.g.
 
 @code
-    libhandle.registerCallout("pkt_rcvd", classify);
-    libhandle.registerCallout("pkt_rcvd", write_data);
+    libhandle.registerCallout("pkt4_receive", classify);
+    libhandle.registerCallout("pkt4_receive", write_data);
 @endcode
 
 The hooks framework will call the callouts in the order they are
@@ -859,16 +868,16 @@ is able to be registered on a hook multiple times.
 Using our contrived example again, the DHCPv4 server processes one request
 to completion before it starts processing the next.  With this knowledge,
 we could alter the logic of the code so that the callout attached to the
-"pkt_rcvd" hook registers the callout doing the logging when it detects
+"pkt4_receive" hook registers the callout doing the logging when it detects
 an interesting packet, and the callout doing the logging deregisters
 itself in its execution.  The relevant modifications to the code in
 the tutorial are shown below:
 
 @code
-// pkt_rcvd.cc
+// pkt4_receive.cc
 //      :
 
-int pkt_rcvd(CalloutHandle& handle) {
+int pkt4_receive(CalloutHandle& handle) {
 
             :
             :
@@ -880,7 +889,7 @@ int pkt_rcvd(CalloutHandle& handle) {
         handle.setContext("hwaddr", hwaddr_ptr->hwaddr_.toText());
 
         // Register the callback to log the data.
-        handle.getLibraryHandle().registerCallout("v4_lease_write", write_data);
+        handle.getLibraryHandle().registerCallout("pkt4_send", write_data);
     }
 
     return (0);
@@ -888,7 +897,7 @@ int pkt_rcvd(CalloutHandle& handle) {
 @endcode
 
 @code
-// v4_lease_write.cc
+// pkt4_send.cc
         :
 
 int write_data(CalloutHandle& handle) {
@@ -912,9 +921,9 @@ int write_data(CalloutHandle& handle) {
     flush(interesting);
 
     // We've logged the data, so deregister ourself.  This callout will not
-    // be called again until it is registered by pkt_rcvd.
+    // be called again until it is registered by pkt4_receive.
 
-    handle.getLibraryHandle().deregisterCallout("v4_lease_write", write_data);
+    handle.getLibraryHandle().deregisterCallout("pkt4_send", write_data);
 
     return (0);
 }
@@ -1028,4 +1037,39 @@ appear between "check" and "validate".  On the other hand, if it were
 "logpkt" that registered the new callout, "double_check" would appear
 after "validate".
 
+ at subsection hooksdgStaticallyLinkedBind10 Running Against a Statically-Linked BIND 10
+
+If BIND 10 is built with the --enable-static-link switch (set when
+running the "configure" script), no shared BIND 10 libraries are built;
+instead, archive libraries are created and BIND 10 is linked to them.
+If you create a hooks library also linked against these archive libraries,
+when the library is loaded you end up with two copies of the library code,
+one in BIND 10 and one in your library.
+
+To run successfully, your library needs to perform run-time initialization
+of the BIND 10 code in your library (something performed by BIND 10
+in the case of shared libraries).  To do this, call the function
+isc::hooks::hooksStaticLinkInit() as the first statement of the load()
+function. (If your library does not include a load() function, you need
+to add one.) For example:
+
+ at code
+#include <hooks/hooks.h>
+
+extern "C" {
+
+int version() {
+    return (BIND10_HOOKS_VERSION);
+}
+
+int load() {
+    isc::hooks::hooksStaticLinkInit();
+        :
+}
+
+// Other callout functions
+        :
+
+}
+ at endcode
 */
diff --git a/src/lib/hooks/library_manager.cc b/src/lib/hooks/library_manager.cc
index 70c76ba..4b04005 100644
--- a/src/lib/hooks/library_manager.cc
+++ b/src/lib/hooks/library_manager.cc
@@ -12,6 +12,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <exceptions/exceptions.h>
 #include <hooks/hooks.h>
 #include <hooks/hooks_log.h>
 #include <hooks/callout_manager.h>
@@ -179,6 +180,10 @@ LibraryManager::runLoad() {
         try {
             manager_->setLibraryIndex(index_);
             status = (*pc.loadPtr())(manager_->getLibraryHandle());
+        } catch (const isc::Exception& ex) {
+            LOG_ERROR(hooks_logger, HOOKS_LOAD_FRAMEWORK_EXCEPTION)
+                .arg(library_name_).arg(ex.what());
+            return (false);
         } catch (...) {
             LOG_ERROR(hooks_logger, HOOKS_LOAD_EXCEPTION).arg(library_name_);
             return (false);
@@ -217,6 +222,10 @@ LibraryManager::runUnload() {
         int status = -1;
         try {
             status = (*pc.unloadPtr())();
+        } catch (const isc::Exception& ex) {
+            LOG_ERROR(hooks_logger, HOOKS_UNLOAD_FRAMEWORK_EXCEPTION)
+                .arg(library_name_).arg(ex.what());
+            return (false);
         } catch (...) {
             // Exception generated.  Note a warning as the unload will occur
             // anyway.
diff --git a/src/lib/hooks/tests/Makefile.am b/src/lib/hooks/tests/Makefile.am
index 37fe238..36b6287 100644
--- a/src/lib/hooks/tests/Makefile.am
+++ b/src/lib/hooks/tests/Makefile.am
@@ -9,6 +9,15 @@ AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
 # But older GCC compilers don't have the flag.     
 AM_CXXFLAGS  = $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 
+# BIND 10 libraries against which the test user libraries are linked.
+HOOKS_LIB      = $(top_builddir)/src/lib/hooks/libb10-hooks.la
+LOG_LIB        = $(top_builddir)/src/lib/log/libb10-log.la
+EXCEPTIONS_LIB = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+UTIL_LIB       = $(top_builddir)/src/lib/util/libb10-util.la
+THREADS_LIB    = $(top_builddir)/src/lib/util/threads/libb10-threads.la
+
+ALL_LIBS       = $(HOOKS_LIB) $(LOG_LIB) $(EXCEPTIONS_LIB) $(UTIL_LIB) $(THREADS_LIB)
+
 if USE_CLANGPP
 # see ../Makefile.am
 AM_CXXFLAGS += -Wno-unused-parameter
@@ -22,7 +31,19 @@ TESTS_ENVIRONMENT = \
 
 TESTS =
 if HAVE_GTEST
-# Build shared libraries for testing.
+# Build shared libraries for testing. The libtool way to create a shared library
+# is to specify "-avoid-version -export-dynamic -module" in the library LDFLAGS
+# (see http://www.gnu.org/software/libtool/manual/html_node/Link-mode.html).
+# Use of these switches will guarantee that the .so files are created in the
+# .libs folder and they can be dlopened.
+# Note that the shared libraries with callouts should not be used together with
+# the --enable-static-link option. With this option, the bind10 libraries are
+# statically linked with the program and if the callout invokes the methods
+# which belong to these libraries, the library with the callout will get its
+# own copy of the static objects (e.g. logger, ServerHooks) and that will lead
+# to unexpected errors. For this reason, the --enable-static-link option is
+# ignored for unit tests built here.
+
 lib_LTLIBRARIES = libnvl.la libivl.la libfxl.la libbcl.la liblcl.la liblecl.la \
                   libucl.la libfcl.la
 
@@ -30,43 +51,54 @@ lib_LTLIBRARIES = libnvl.la libivl.la libfxl.la libbcl.la liblcl.la liblecl.la \
 libnvl_la_SOURCES  = no_version_library.cc
 libnvl_la_CXXFLAGS = $(AM_CXXFLAGS)
 libnvl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libnvl_la_LDFLAGS  = -avoid-version -export-dynamic -module
 
 # Incorrect version function
 libivl_la_SOURCES  = incorrect_version_library.cc
 libivl_la_CXXFLAGS = $(AM_CXXFLAGS)
 libivl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libivl_la_LDFLAGS  = -avoid-version -export-dynamic -module
 
 # All framework functions throw an exception
-libfxl_la_SOURCES = framework_exception_library.cc
+libfxl_la_SOURCES  = framework_exception_library.cc
 libfxl_la_CXXFLAGS = $(AM_CXXFLAGS)
 libfxl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libfxl_la_LDFLAGS  = -avoid-version -export-dynamic -module
 
 # The basic callout library - contains standard callouts
 libbcl_la_SOURCES  = basic_callout_library.cc
 libbcl_la_CXXFLAGS = $(AM_CXXFLAGS)
 libbcl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libbcl_la_LDFLAGS  = -avoid-version -export-dynamic -module
+libbcl_la_LIBADD    = $(ALL_LIBS)
 
 # The load callout library - contains a load function
 liblcl_la_SOURCES  = load_callout_library.cc
 liblcl_la_CXXFLAGS = $(AM_CXXFLAGS)
 liblcl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+liblcl_la_LDFLAGS  = -avoid-version -export-dynamic -module
+liblcl_la_LIBADD    = $(ALL_LIBS)
 
 # The load error callout library - contains a load function that returns
 # an error.
 liblecl_la_SOURCES  = load_error_callout_library.cc
 liblecl_la_CXXFLAGS = $(AM_CXXFLAGS)
 liblecl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+liblecl_la_LDFLAGS  = -avoid-version -export-dynamic -module
 
 # The unload callout library - contains an unload function that
 # creates a marker file.
 libucl_la_SOURCES  = unload_callout_library.cc
 libucl_la_CXXFLAGS = $(AM_CXXFLAGS)
 libucl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libucl_la_LDFLAGS  = -avoid-version -export-dynamic -module
 
 # The full callout library - contains all three framework functions.
 libfcl_la_SOURCES  = full_callout_library.cc
 libfcl_la_CXXFLAGS = $(AM_CXXFLAGS)
 libfcl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libfcl_la_LDFLAGS  = -avoid-version -export-dynamic -module
+libfcl_la_LIBADD   = $(ALL_LIBS)
 
 TESTS += run_unittests
 run_unittests_SOURCES  = run_unittests.cc
@@ -85,18 +117,28 @@ nodist_run_unittests_SOURCES += test_libraries.h
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 if USE_STATIC_LINK
-# If specified, only link unit tests static - the test libraries must be
-# build as shared libraries.
-run_unittests_LDFLAGS  += -static
+run_unittests_LDFLAGS += -static
 endif
 
-run_unittests_LDADD    = $(AM_LDADD)    $(GTEST_LDADD)
+run_unittests_LDADD    = $(AM_LDADD) $(GTEST_LDADD)
+run_unittests_LDADD   += $(ALL_LIBS)
+run_unittests_LDADD   += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+
+# As noted in configure.ac, libtool doesn't work perfectly with Darwin: it embeds the
+# final install path in dynamic libraries and loadable modules refer to that path even
+# if its loaded within the source tree, so preventing tests from working - but only
+# when linking statically.  The solution used in other Makefiles (setting the path
+# to the dynamic libraries via an environment variable) don't seem to work.  What does
+# work is to run the unit test using libtool and specifying paths via -dlopen switches.
+# So... If running in an environment where we have to set the library path AND if
+# linking statically, override the "check" target and run the unit tests ourselves.
+if USE_STATIC_LINK
+if SET_ENV_LIBRARY_PATH
+check-TESTS:
+	$(LIBTOOL) --mode=execute -dlopen $(HOOKS_LIB)  -dlopen $(LOG_LIB) -dlopen $(EXCEPTIONS_LIB) -dlopen $(UTIL_LIB)  -dlopen $(THREADS_LIB) ./run_unittests
+endif
+endif
 
-run_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
-run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/hooks/tests/basic_callout_library.cc b/src/lib/hooks/tests/basic_callout_library.cc
index 253de80..0a81f23 100644
--- a/src/lib/hooks/tests/basic_callout_library.cc
+++ b/src/lib/hooks/tests/basic_callout_library.cc
@@ -24,16 +24,16 @@
 /// - A context_create callout is supplied.
 ///
 /// - Three "standard" callouts are supplied corresponding to the hooks
-///   "hookpt_one", "hookpt_two", "hookpt_three".  All do some trivial calculations
-///   on the arguments supplied to it and the context variables, returning
-///   intermediate results through the "result" argument. The result of
-///   executing all four callouts in order is:
+///   "hookpt_one", "hookpt_two", "hookpt_three".  All do some trivial
+///   calculations on the arguments supplied to it and the context variables,
+///   returning intermediate results through the "result" argument. The result
+///   of executing all four callouts in order is:
 ///
 ///   @f[ (10 + data_1) * data_2 - data_3 @f]
 ///
 ///   ...where data_1, data_2 and data_3 are the values passed in arguments of
-///   the same name to the three callouts (data_1 passed to hookpt_one, data_2 to
-///   hookpt_two etc.) and the result is returned in the argument "result".
+///   the same name to the three callouts (data_1 passed to hookpt_one, data_2
+///   to hookpt_two etc.) and the result is returned in the argument "result".
 
 #include <hooks/hooks.h>
 #include <fstream>
@@ -104,12 +104,21 @@ hookpt_three(CalloutHandle& handle) {
     return (0);
 }
 
-// Framework functions.  Only version() is supplied here.
+// Framework functions.
 
 int
 version() {
     return (BIND10_HOOKS_VERSION);
 }
 
-};
+// load() initializes the user library if the main image was statically linked.
+int
+load(isc::hooks::LibraryHandle&) {
+#ifdef USE_STATIC_LINK
+    hooksStaticLinkInit();
+#endif
+    return (0);
+}
+
+}
 
diff --git a/src/lib/hooks/tests/full_callout_library.cc b/src/lib/hooks/tests/full_callout_library.cc
index 33d5660..3a87f54 100644
--- a/src/lib/hooks/tests/full_callout_library.cc
+++ b/src/lib/hooks/tests/full_callout_library.cc
@@ -34,8 +34,8 @@
 ///   @f[ ((7 * data_1) - data_2) * data_3 @f]
 ///
 ///   ...where data_1, data_2 and data_3 are the values passed in arguments of
-///   the same name to the three callouts (data_1 passed to hookpt_one, data_2 to
-///   hookpt_two etc.) and the result is returned in the argument "result".
+///   the same name to the three callouts (data_1 passed to hookpt_one, data_2
+///   to hookpt_two etc.) and the result is returned in the argument "result".
 
 #include <hooks/hooks.h>
 #include <hooks/tests/marker_file.h>
@@ -116,6 +116,10 @@ version() {
 
 int
 load(LibraryHandle& handle) {
+    // Initialize if the main image was statically linked
+#ifdef USE_STATIC_LINK
+    hooksStaticLinkInit();
+#endif
     // Register the non-standard functions
     handle.registerCallout("hookpt_two", hook_nonstandard_two);
     handle.registerCallout("hookpt_three", hook_nonstandard_three);
diff --git a/src/lib/hooks/tests/load_callout_library.cc b/src/lib/hooks/tests/load_callout_library.cc
index ae9f470..59a58b5 100644
--- a/src/lib/hooks/tests/load_callout_library.cc
+++ b/src/lib/hooks/tests/load_callout_library.cc
@@ -30,8 +30,8 @@
 ///   @f[ ((5 * data_1) + data_2) * data_3 @f]
 ///
 ///   ...where data_1, data_2 and data_3 are the values passed in arguments of
-///   the same name to the three callouts (data_1 passed to hookpt_one, data_2 to
-///   hookpt_two etc.) and the result is returned in the argument "result".
+///   the same name to the three callouts (data_1 passed to hookpt_one, data_2
+///   to hookpt_two etc.) and the result is returned in the argument "result".
 
 #include <hooks/hooks.h>
 
@@ -108,6 +108,10 @@ version() {
 }
 
 int load(LibraryHandle& handle) {
+    // Initialize the user library if the main image was statically linked
+#ifdef USE_STATIC_LINK
+    hooksStaticLinkInit();
+#endif
     // Register the non-standard functions
     handle.registerCallout("hookpt_two", hook_nonstandard_two);
     handle.registerCallout("hookpt_three", hook_nonstandard_three);
diff --git a/src/lib/hooks/tests/test_libraries.h.in b/src/lib/hooks/tests/test_libraries.h.in
index bb6a24a..7b5e0e4 100644
--- a/src/lib/hooks/tests/test_libraries.h.in
+++ b/src/lib/hooks/tests/test_libraries.h.in
@@ -19,60 +19,41 @@
 
 namespace {
 
-
-// Take care of differences in DLL naming between operating systems.
-
-#ifdef OS_OSX
-#define DLL_SUFFIX ".dylib"
-
-#else
-#define DLL_SUFFIX ".so"
-
-#endif
-
-
 // Names of the libraries used in these tests.  These libraries are built using
 // libtool, so we need to look in the hidden ".libs" directory to locate the
 // .so file.  Note that we access the .so file - libtool creates this as a
 // like to the real shared library.
 
 // Basic library with context_create and three "standard" callouts.
-static const char* BASIC_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libbcl"
-                                           DLL_SUFFIX;
+static const char* BASIC_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libbcl.so";
 
 // Library with context_create and three "standard" callouts, as well as
 // load() and unload() functions.
-static const char* FULL_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libfcl"
-                                          DLL_SUFFIX;
+static const char* FULL_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libfcl.so";
 
 // Library where the all framework functions throw an exception
-static const char* FRAMEWORK_EXCEPTION_LIBRARY = "@abs_builddir@/.libs/libfxl"
-                                                 DLL_SUFFIX;
+static const char* FRAMEWORK_EXCEPTION_LIBRARY = "@abs_builddir@/.libs/libfxl.so";
 
 // Library where the version() function returns an incorrect result.
-static const char* INCORRECT_VERSION_LIBRARY = "@abs_builddir@/.libs/libivl"
-                                               DLL_SUFFIX;
+static const char* INCORRECT_VERSION_LIBRARY = "@abs_builddir@/.libs/libivl.so";
 
 // Library where some of the callout registration is done with the load()
 // function.
-static const char* LOAD_CALLOUT_LIBRARY = "@abs_builddir@/.libs/liblcl"
-                                          DLL_SUFFIX;
+static const char* LOAD_CALLOUT_LIBRARY = "@abs_builddir@/.libs/liblcl.so";
 
 // Library where the load() function returns an error.
 static const char* LOAD_ERROR_CALLOUT_LIBRARY =
-    "@abs_builddir@/.libs/liblecl" DLL_SUFFIX;
+    "@abs_builddir@/.libs/liblecl.so";
 
 // Name of a library which is not present.
-static const char* NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere"
-                                         DLL_SUFFIX;
+static const char* NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";
 
 // Library that does not include a version function.
-static const char* NO_VERSION_LIBRARY = "@abs_builddir@/.libs/libnvl"
-                                        DLL_SUFFIX;
+static const char* NO_VERSION_LIBRARY = "@abs_builddir@/.libs/libnvl.so";
 
 // Library where there is an unload() function.
-static const char* UNLOAD_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libucl"
-                                            DLL_SUFFIX;
+static const char* UNLOAD_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libucl.so";
+
 } // anonymous namespace
 
 
diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am
index 9febc95..0bd1b05 100644
--- a/src/lib/log/Makefile.am
+++ b/src/lib/log/Makefile.am
@@ -42,10 +42,6 @@ libb10_log_la_CXXFLAGS = $(AM_CXXFLAGS)
 if USE_GXX
 libb10_log_la_CXXFLAGS += -Wno-unused-parameter
 endif
-if USE_CLANGPP
-# Same for clang++, but we need to turn off -Werror completely.
-libb10_log_la_CXXFLAGS += -Wno-error
-endif
 libb10_log_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
 libb10_log_la_LIBADD   = $(top_builddir)/src/lib/util/libb10-util.la
 libb10_log_la_LIBADD  += interprocess/libb10-log_interprocess.la
diff --git a/src/lib/log/logger.cc b/src/lib/log/logger.cc
index a04267c..f7d6799 100644
--- a/src/lib/log/logger.cc
+++ b/src/lib/log/logger.cc
@@ -43,6 +43,11 @@ void Logger::initLoggerImpl() {
 
 Logger::~Logger() {
     delete loggerptr_;
+
+    // The next statement is required for the BIND 10 hooks framework, where
+    // a statically-linked BIND 10 loads and unloads multiple libraries. See
+    // the hooks documentation for more details.
+    loggerptr_ = 0;
 }
 
 // Get Name of Logger
diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am
index 92303e0..a26b348 100644
--- a/src/lib/log/tests/Makefile.am
+++ b/src/lib/log/tests/Makefile.am
@@ -14,10 +14,13 @@ CLEANFILES = *.gcno *.gcda *.lock
 
 EXTRA_DIST = log_test_messages.mes
 BUILT_SOURCES = log_test_messages.h log_test_messages.cc
-log_test_messages.h log_test_messages.cc: log_test_messages.mes
+log_test_messages.h log_test_messages.cc: s-messages
+
+s-messages: log_test_messages.mes
 	$(AM_V_GEN) $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/log/tests/log_test_messages.mes
+	touch $@
 
-CLEANFILES += log_test_messages.h log_test_messages.cc
+CLEANFILES += log_test_messages.h log_test_messages.cc s-messages
 
 noinst_PROGRAMS = logger_example
 logger_example_SOURCES = logger_example.cc
diff --git a/src/lib/nsas/Makefile.am b/src/lib/nsas/Makefile.am
index eb8c0c3..d38cf1a 100644
--- a/src/lib/nsas/Makefile.am
+++ b/src/lib/nsas/Makefile.am
@@ -22,8 +22,11 @@ AM_CXXFLAGS += -Wno-unused-parameter
 endif
 
 # Define rule to build logging source files from message file
-nsas_messages.h nsas_messages.cc: nsas_messages.mes
+nsas_messages.h nsas_messages.cc: s-messages
+
+s-messages: nsas_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/nsas/nsas_messages.mes
+	touch $@
 
 # What is being built.
 lib_LTLIBRARIES = libb10-nsas.la
@@ -38,7 +41,6 @@ BUILT_SOURCES = nsas_messages.h nsas_messages.cc
 
 # Library sources. The generated files will not be in the distribution.
 libb10_nsas_la_SOURCES  = address_entry.h address_entry.cc
-libb10_nsas_la_SOURCES += asiolink.h
 libb10_nsas_la_SOURCES += hash.cc hash.h
 libb10_nsas_la_SOURCES += hash_deleter.h
 libb10_nsas_la_SOURCES += hash_key.cc hash_key.h
@@ -60,4 +62,4 @@ nodist_libb10_nsas_la_SOURCES  = nsas_messages.h nsas_messages.cc
 EXTRA_DIST = nsas_messages.mes
 
 # Make sure that the generated files are got rid of in a clean operation
-CLEANFILES = *.gcno *.gcda nsas_messages.h nsas_messages.cc
+CLEANFILES = *.gcno *.gcda nsas_messages.h nsas_messages.cc s-messages
diff --git a/src/lib/nsas/README b/src/lib/nsas/README
index d0598ca..144bdde 100644
--- a/src/lib/nsas/README
+++ b/src/lib/nsas/README
@@ -1,7 +1,2 @@
 For an overview of the Nameserver Address Store, see the requirements and design
 documents at http://bind10.isc.org/wiki/Resolver.
-
-At the time of writing (19 October 2010), the file asiolink.h is present in this
-directory only for the purposes of development.  When the resolver's
-asynchronous I/O code has been finished, this will be removed and the NSAS will
-use the "real" code.
diff --git a/src/lib/nsas/address_request_callback.h b/src/lib/nsas/address_request_callback.h
index e43dfe2..5d86d58 100644
--- a/src/lib/nsas/address_request_callback.h
+++ b/src/lib/nsas/address_request_callback.h
@@ -15,7 +15,6 @@
 #ifndef ADDRESS_REQUEST_CALLBACK_H
 #define ADDRESS_REQUEST_CALLBACK_H
 
-#include "asiolink.h"
 #include "nameserver_address.h"
 
 namespace isc {
diff --git a/src/lib/nsas/asiolink.h b/src/lib/nsas/asiolink.h
deleted file mode 100644
index b236a0e..0000000
--- a/src/lib/nsas/asiolink.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef ASIOLINK_H
-#define ASIOLINK_H
-
-#include <string>
-#include <sys/socket.h>
-
-#endif // ASIOLINK_H
diff --git a/src/lib/nsas/nameserver_address.h b/src/lib/nsas/nameserver_address.h
index 5f5c7c9..33a1169 100644
--- a/src/lib/nsas/nameserver_address.h
+++ b/src/lib/nsas/nameserver_address.h
@@ -19,7 +19,6 @@
 
 #include <exceptions/exceptions.h>
 
-#include "asiolink.h"
 #include "address_entry.h"
 #include "nsas_types.h"
 
diff --git a/src/lib/nsas/nameserver_entry.h b/src/lib/nsas/nameserver_entry.h
index 3ffdf10..5ff72ff 100644
--- a/src/lib/nsas/nameserver_entry.h
+++ b/src/lib/nsas/nameserver_entry.h
@@ -28,7 +28,6 @@
 #include <util/lru_list.h>
 
 #include "address_entry.h"
-#include "asiolink.h"
 #include "nsas_types.h"
 #include "hash_key.h"
 #include "fetchable.h"
diff --git a/src/lib/nsas/tests/nameserver_entry_unittest.cc b/src/lib/nsas/tests/nameserver_entry_unittest.cc
index e0ec0ad..10f0c20 100644
--- a/src/lib/nsas/tests/nameserver_entry_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_entry_unittest.cc
@@ -30,7 +30,6 @@
 #include <dns/name.h>
 #include <exceptions/exceptions.h>
 
-#include "../asiolink.h"
 #include "../address_entry.h"
 #include "../nameserver_entry.h"
 #include "../nameserver_address.h"
diff --git a/src/lib/nsas/tests/zone_entry_unittest.cc b/src/lib/nsas/tests/zone_entry_unittest.cc
index 8754906..dc34275 100644
--- a/src/lib/nsas/tests/zone_entry_unittest.cc
+++ b/src/lib/nsas/tests/zone_entry_unittest.cc
@@ -23,7 +23,6 @@
 #include <dns/rrclass.h>
 #include <dns/rdataclass.h>
 
-#include "../asiolink.h"
 #include "../zone_entry.h"
 #include "../nameserver_entry.h"
 #include "../address_request_callback.h"
diff --git a/src/lib/nsas/zone_entry.h b/src/lib/nsas/zone_entry.h
index b0c26c3..d53b321 100644
--- a/src/lib/nsas/zone_entry.h
+++ b/src/lib/nsas/zone_entry.h
@@ -30,7 +30,6 @@
 
 #include "hash_key.h"
 #include "nsas_entry.h"
-#include "asiolink.h"
 #include "fetchable.h"
 #include "nsas_types.h"
 #include "glue_hints.h"
diff --git a/src/lib/python/bind10_config.py.in b/src/lib/python/bind10_config.py.in
index e56e908..855dd00 100644
--- a/src/lib/python/bind10_config.py.in
+++ b/src/lib/python/bind10_config.py.in
@@ -17,6 +17,31 @@
 # variables to python scripts and libraries.
 import os
 
+def get_specfile_location(module_name):
+    """Return the path to the module spec file following common convetion.
+
+    This method generates the path commonly used by most BIND 10
+    modules, determined by a well known prefix and the module name.
+
+    A specific module can override this method if it uses a different
+    path for the spec file.
+
+    """
+    # First check if it's running under an 'in-source' environment,
+    # then try commonly used paths and file names.  If found, use it.
+    for ev in ['B10_FROM_SOURCE', 'B10_FROM_BUILD']:
+        if ev in os.environ:
+            specfile = os.environ[ev] + '/src/bin/' + module_name + \
+                       '/' + module_name + '.spec'
+            if os.path.exists(specfile):
+                return specfile
+    # Otherwise, just use the installed path, whether or not it really
+    # exists; leave error handling to the caller.
+    specfile_path = '@datadir@/@PACKAGE@'\
+        .replace('${datarootdir}', '@datarootdir@')\
+        .replace('${prefix}', '@prefix@')
+    return specfile_path + '/' + module_name + '.spec'
+
 def reload():
     # In a function, for testing purposes
     global BIND10_MSGQ_SOCKET_FILE
diff --git a/src/lib/python/isc/log_messages/Makefile.am b/src/lib/python/isc/log_messages/Makefile.am
index 3e265d7..eab82aa 100644
--- a/src/lib/python/isc/log_messages/Makefile.am
+++ b/src/lib/python/isc/log_messages/Makefile.am
@@ -21,6 +21,7 @@ EXTRA_DIST += server_common_messages.py
 EXTRA_DIST += dbutil_messages.py
 EXTRA_DIST += msgq_messages.py
 EXTRA_DIST += pycc_messages.py
+EXTRA_DIST += util_messages.py
 
 CLEANFILES = __init__.pyc
 CLEANFILES += init_messages.pyc
@@ -43,6 +44,7 @@ CLEANFILES += server_common_messages.pyc
 CLEANFILES += dbutil_messages.pyc
 CLEANFILES += msgq_messages.pyc
 CLEANFILES += pycc_messages.pyc
+CLEANFILES += util_messages.pyc
 
 CLEANDIRS = __pycache__
 
diff --git a/src/lib/python/isc/log_messages/util_messages.py b/src/lib/python/isc/log_messages/util_messages.py
new file mode 100644
index 0000000..4298729
--- /dev/null
+++ b/src/lib/python/isc/log_messages/util_messages.py
@@ -0,0 +1 @@
+from work.util_messages import *
diff --git a/src/lib/python/isc/log_messages/work/Makefile.am b/src/lib/python/isc/log_messages/work/Makefile.am
index ad5ee0c..b49ce69 100644
--- a/src/lib/python/isc/log_messages/work/Makefile.am
+++ b/src/lib/python/isc/log_messages/work/Makefile.am
@@ -8,5 +8,7 @@ pythondir = $(pyexecdir)/isc/log_messages/
 CLEANFILES = __init__.pyc __init__.pyo
 CLEANDIRS = __pycache__
 
+EXTRA_DIST = README
+
 clean-local:
 	rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/log_messages/work/README b/src/lib/python/isc/log_messages/work/README
new file mode 100644
index 0000000..37b4534
--- /dev/null
+++ b/src/lib/python/isc/log_messages/work/README
@@ -0,0 +1,5 @@
+The __init__.py.in in this directory is meant to be processed by
+configure so that the generated __init__.py ends up in the builddir, and
+not the srcdir. This is because Python associates a module with a
+directory, and you can't have portions of the module in two separate
+directories.
diff --git a/src/lib/python/isc/notify/notify_out.py b/src/lib/python/isc/notify/notify_out.py
index 9003ff5..f4de7b8 100644
--- a/src/lib/python/isc/notify/notify_out.py
+++ b/src/lib/python/isc/notify/notify_out.py
@@ -128,7 +128,7 @@ class NotifyOut:
     notify message to its slaves). notify service can be started by
     calling  dispatcher(), and it can be stopped by calling shutdown()
     in another thread. '''
-    def __init__(self, datasrc_file, verbose=True):
+    def __init__(self, datasrc_file, counters=None, verbose=True):
         self._notify_infos = {} # key is (zone_name, zone_class)
         self._waiting_zones = []
         self._notifying_zones = []
@@ -143,7 +143,7 @@ class NotifyOut:
         # Use nonblock event to eliminate busy loop
         # If there are no notifying zones, clear the event bit and wait.
         self._nonblock_event = threading.Event()
-        self._counters = Counters()
+        self._counters = counters
 
     def _init_notify_out(self, datasrc_file):
         '''Get all the zones name and its notify target's address.
@@ -508,16 +508,17 @@ class NotifyOut:
             sock = zone_notify_info.create_socket(addrinfo[0])
             sock.sendto(render.get_data(), 0, addrinfo)
             # count notifying by IPv4 or IPv6 for statistics
-            if zone_notify_info.get_socket().family == socket.AF_INET:
-                self._counters.inc('zones',
-                                   zone_notify_info.zone_class,
-                                   zone_notify_info.zone_name,
-                                  'notifyoutv4')
-            elif zone_notify_info.get_socket().family == socket.AF_INET6:
-                self._counters.inc('zones',
-                                   zone_notify_info.zone_class,
-                                   zone_notify_info.zone_name,
-                                  'notifyoutv6')
+            if self._counters is not None:
+                if zone_notify_info.get_socket().family == socket.AF_INET:
+                    self._counters.inc('zones',
+                                       zone_notify_info.zone_class,
+                                       zone_notify_info.zone_name,
+                                      'notifyoutv4')
+                elif zone_notify_info.get_socket().family == socket.AF_INET6:
+                    self._counters.inc('zones',
+                                       zone_notify_info.zone_class,
+                                       zone_notify_info.zone_name,
+                                      'notifyoutv6')
             logger.info(NOTIFY_OUT_SENDING_NOTIFY, AddressFormatter(addrinfo))
         except (socket.error, addr.InvalidAddress) as err:
             logger.error(NOTIFY_OUT_SOCKET_ERROR, AddressFormatter(addrinfo),
diff --git a/src/lib/python/isc/notify/tests/Makefile.am b/src/lib/python/isc/notify/tests/Makefile.am
index d964880..52f2409 100644
--- a/src/lib/python/isc/notify/tests/Makefile.am
+++ b/src/lib/python/isc/notify/tests/Makefile.am
@@ -5,7 +5,7 @@ EXTRA_DIST += testdata/test.sqlite3 testdata/brokentest.sqlite3
 # The rest of the files are actually not necessary, but added for reference
 EXTRA_DIST += testdata/example.com testdata/example.net
 EXTRA_DIST += testdata/nons.example testdata/nosoa.example
-EXTRA_DIST += testdata/multisoa.example
+EXTRA_DIST += testdata/multisoa.example testdata/test_spec1.spec
 
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
 # required by loadable python modules.
diff --git a/src/lib/python/isc/notify/tests/notify_out_test.py b/src/lib/python/isc/notify/tests/notify_out_test.py
index 7097f01..ec4df0d 100644
--- a/src/lib/python/isc/notify/tests/notify_out_test.py
+++ b/src/lib/python/isc/notify/tests/notify_out_test.py
@@ -22,8 +22,10 @@ import socket
 from isc.notify import notify_out, SOCK_DATA
 import isc.log
 from isc.dns import *
+from isc.statistics.dns import Counters
 
 TESTDATA_SRCDIR = os.getenv("TESTDATASRCDIR")
+SPECFILE_LOCATION = TESTDATA_SRCDIR + os.sep + 'test_spec1.spec'
 
 def get_notify_msgdata(zone_name, qid=0):
     """A helper function to generate a notify response in wire format.
@@ -128,7 +130,7 @@ class TestZoneNotifyInfo(unittest.TestCase):
 class TestNotifyOut(unittest.TestCase):
     def setUp(self):
         self._db_file = TESTDATA_SRCDIR + '/test.sqlite3'
-        self._notify = notify_out.NotifyOut(self._db_file)
+        self._notify = notify_out.NotifyOut(self._db_file, counters=Counters(SPECFILE_LOCATION))
         self._notify._notify_infos[('example.com.', 'IN')] = MockZoneNotifyInfo('example.com.', 'IN')
         self._notify._notify_infos[('example.com.', 'CH')] = MockZoneNotifyInfo('example.com.', 'CH')
         self._notify._notify_infos[('example.net.', 'IN')] = MockZoneNotifyInfo('example.net.', 'IN')
diff --git a/src/lib/python/isc/notify/tests/testdata/test_spec1.spec b/src/lib/python/isc/notify/tests/testdata/test_spec1.spec
new file mode 100644
index 0000000..7ac8013
--- /dev/null
+++ b/src/lib/python/isc/notify/tests/testdata/test_spec1.spec
@@ -0,0 +1,57 @@
+{
+  "module_spec": {
+    "module_name": "NotifyOutLike",
+    "module_description": "Test notifier",
+    "config_data": [],
+    "commands": [],
+    "statistics": [
+      {
+        "item_name": "zones",
+        "item_type": "named_set",
+        "item_optional": false,
+        "item_default": {
+          "_SERVER_" : {
+            "notifyoutv4" : 0,
+            "notifyoutv6" : 0
+          }
+        },
+        "item_title": "Zone names",
+        "item_description": "Zone names",
+        "named_set_item_spec": {
+          "item_name": "classname",
+          "item_type": "named_set",
+          "item_optional": false,
+          "item_default": {},
+          "item_title": "RR class name",
+          "item_description": "RR class name",
+          "named_set_item_spec": {
+            "item_name": "zonename",
+            "item_type": "map",
+            "item_optional": false,
+            "item_default": {},
+            "item_title": "Zone name",
+            "item_description": "Zone name",
+            "map_item_spec": [
+              {
+                "item_name": "notifyoutv4",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "IPv4 notifies",
+                "item_description": "Number of IPv4 notifies per zone name sent out"
+              },
+              {
+                "item_name": "notifyoutv6",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "IPv6 notifies",
+                "item_description": "Number of IPv6 notifies per zone name sent out"
+              }
+            ]
+          }
+        }
+      }
+    ]
+  }
+}
diff --git a/src/lib/python/isc/server_common/Makefile.am b/src/lib/python/isc/server_common/Makefile.am
index 54b2885..53e46e0 100644
--- a/src/lib/python/isc/server_common/Makefile.am
+++ b/src/lib/python/isc/server_common/Makefile.am
@@ -7,7 +7,6 @@ python_PYTHON += logger.py
 pythondir = $(pyexecdir)/isc/server_common
 
 BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/work/server_common_messages.py
-BUILT_SOURCES += bind10_server.py
 
 nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/server_common_messages.py
 
diff --git a/src/lib/python/isc/server_common/bind10_server.py b/src/lib/python/isc/server_common/bind10_server.py
new file mode 100644
index 0000000..249d4d9
--- /dev/null
+++ b/src/lib/python/isc/server_common/bind10_server.py
@@ -0,0 +1,251 @@
+# Copyright (C) 2013  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import errno
+import os
+import select
+import signal
+
+import bind10_config
+import isc.log
+import isc.config
+from isc.server_common.logger import logger
+from isc.log_messages.server_common_messages import *
+
+class BIND10ServerFatal(Exception):
+    """Exception raised when the server program encounters a fatal error."""
+    pass
+
+class BIND10Server:
+    """A mixin class for common BIND 10 server implementations.
+
+    It takes care of common initialization such as setting up a module CC
+    session, and running main event loop.  It also handles the "shutdown"
+    command for its normal behavior.  If a specific server class wants to
+    handle this command differently or if it does not support the command,
+    it should override the _command_handler method.
+
+    Specific modules can define module-specific class inheriting this class,
+    instantiate it, and call run() with the module name.
+
+    Methods to be implemented in the actual class:
+      _config_handler: config handler method as specified in ModuleCCSession.
+                       must be exception free; errors should be signaled by
+                       the return value.
+      _mod_command_handler: can be optionally defined to handle
+                            module-specific commands.  should conform to
+                            command handlers as specified in ModuleCCSession.
+                            must be exception free; errors should be signaled
+                            by the return value.
+      _setup_module: can be optionally defined for module-specific
+                     initialization.  This is called after the module CC
+                     session has started, and can be used for registering
+                     interest on remote modules, etc.  If it raises an
+                     exception, the server will be immediately stopped.
+                     Parameter: None, Return: None
+      _shutdown_module: can be optionally defined for module-specific
+                        finalization. This is called right before the
+                        module CC session is stopped. If it raises an
+                        exception, the server will be immediately
+                        stopped.
+                        Parameter: None, Return: None
+
+    """
+    # Will be set to True when the server should stop and shut down.
+    # Can be read via accessor method 'shutdown', mainly for testing.
+    __shutdown = False
+
+    # ModuleCCSession used in the server.  Defined as 'protectd' so tests
+    # can refer to it directly; others should access it via the
+    # 'mod_ccsession' accessor.
+    _mod_cc = None
+
+    # Will be set in run().  Define a tentative value so other methods can
+    # be tested directly.
+    __module_name = ''
+
+    # Basically constant, but allow tests to override it.
+    _select_fn = select.select
+
+    def __init__(self):
+        self._read_callbacks = {}
+        self._write_callbacks = {}
+        self._error_callbacks = {}
+
+    @property
+    def shutdown(self):
+        return self.__shutdown
+
+    @property
+    def mod_ccsession(self):
+        return self._mod_cc
+
+    def _setup_ccsession(self):
+        """Create and start module CC session.
+
+        This is essentially private, but allows tests to override it.
+
+        """
+        self._mod_cc = isc.config.ModuleCCSession(
+            bind10_config.get_specfile_location(self.__module_name),
+            self._config_handler, self._command_handler)
+        self._mod_cc.start()
+
+    def _trigger_shutdown(self):
+        """Initiate a shutdown sequence.
+
+        This method is expected to be called in various ways including
+        in the middle of a signal handler, and is designed to be as simple
+        as possible to minimize side effects.  Actual shutdown will take
+        place in a normal control flow.
+
+        This method is defined as 'protected'.  User classes can use it
+        to shut down the server.
+
+        """
+        self.__shutdown = True
+
+    def _run_internal(self):
+        """Main event loop.
+
+        This method is essentially private, but allows tests to override it.
+
+        """
+
+        logger.info(PYSERVER_COMMON_SERVER_STARTED, self.__module_name)
+        cc_fileno = self._mod_cc.get_socket().fileno()
+        while not self.__shutdown:
+            try:
+                read_fds = list(self._read_callbacks.keys())
+                read_fds.append(cc_fileno)
+                write_fds = list(self._write_callbacks.keys())
+                error_fds = list(self._error_callbacks.keys())
+
+                (reads, writes, errors) = \
+                    self._select_fn(read_fds, write_fds, error_fds)
+            except select.error as ex:
+                # ignore intterruption by signal; regard other select errors
+                # fatal.
+                if ex.args[0] == errno.EINTR:
+                    continue
+                else:
+                    raise
+
+            for fileno in reads:
+                if fileno in self._read_callbacks:
+                    for callback in self._read_callbacks[fileno]:
+                        callback()
+
+            for fileno in writes:
+                if fileno in self._write_callbacks:
+                    for callback in self._write_callbacks[fileno]:
+                        callback()
+
+            for fileno in errors:
+                if fileno in self._error_callbacks:
+                    for callback in self._error_callbacks[fileno]:
+                        callback()
+
+            if cc_fileno in reads:
+                # this shouldn't raise an exception (if it does, we'll
+                # propagate it)
+                self._mod_cc.check_command(True)
+
+        self._shutdown_module()
+        self._mod_cc.send_stopping()
+
+    def _command_handler(self, cmd, args):
+        logger.debug(logger.DBGLVL_TRACE_BASIC, PYSERVER_COMMON_COMMAND,
+                     self.__module_name, cmd)
+        if cmd == 'shutdown':
+            self._trigger_shutdown()
+            answer = isc.config.create_answer(0)
+        else:
+            answer = self._mod_command_handler(cmd, args)
+
+        return answer
+
+    def _mod_command_handler(self, cmd, args):
+        """The default implementation of the module specific command handler"""
+        return isc.config.create_answer(1, "Unknown command: " + str(cmd))
+
+    def _setup_module(self):
+        """The default implementation of the module specific initialization"""
+        pass
+
+    def _shutdown_module(self):
+        """The default implementation of the module specific finalization"""
+        pass
+
+    def watch_fileno(self, fileno, rcallback=None, wcallback=None, \
+                         xcallback=None):
+        """Register the fileno for the internal select() call.
+
+        *callback's are callable objects which would be called when
+        read, write, error events occur on the specified fileno.
+        """
+        if rcallback is not None:
+            if fileno in self._read_callbacks:
+                self._read_callbacks[fileno].append(rcallback)
+            else:
+                self._read_callbacks[fileno] = [rcallback]
+
+        if wcallback is not None:
+            if fileno in self._write_callbacks:
+                self._write_callbacks[fileno].append(wcallback)
+            else:
+                self._write_callbacks[fileno] = [wcallback]
+
+        if xcallback is not None:
+            if fileno in self._error_callbacks:
+                self._error_callbacks[fileno].append(xcallback)
+            else:
+                self._error_callbacks[fileno] = [xcallback]
+
+    def run(self, module_name):
+        """Start the server and let it run until it's told to stop.
+
+        Usually this must be the first method of this class that is called
+        from its user.
+
+        Parameter:
+          module_name (str): the Python module name for the actual server
+            implementation.  Often identical to the directory name in which
+            the implementation files are placed.
+
+        Returns: values expected to be used as program's exit code.
+          0: server has run and finished successfully.
+          1: some error happens
+
+          """
+        try:
+            self.__module_name = module_name
+            shutdown_sighandler = \
+                lambda signal, frame: self._trigger_shutdown()
+            signal.signal(signal.SIGTERM, shutdown_sighandler)
+            signal.signal(signal.SIGINT, shutdown_sighandler)
+            self._setup_ccsession()
+            self._setup_module()
+            self._run_internal()
+            logger.info(PYSERVER_COMMON_SERVER_STOPPED, self.__module_name)
+            return 0
+        except BIND10ServerFatal as ex:
+            logger.error(PYSERVER_COMMON_SERVER_FATAL, self.__module_name,
+                         ex)
+        except Exception as ex:
+            logger.error(PYSERVER_COMMON_UNCAUGHT_EXCEPTION, type(ex).__name__,
+                         ex)
+
+        return 1
diff --git a/src/lib/python/isc/server_common/bind10_server.py.in b/src/lib/python/isc/server_common/bind10_server.py.in
deleted file mode 100644
index 5bbd341..0000000
--- a/src/lib/python/isc/server_common/bind10_server.py.in
+++ /dev/null
@@ -1,275 +0,0 @@
-# Copyright (C) 2013  Internet Systems Consortium.
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
-# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
-# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
-# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-import errno
-import os
-import select
-import signal
-
-import isc.log
-import isc.config
-from isc.server_common.logger import logger
-from isc.log_messages.server_common_messages import *
-
-class BIND10ServerFatal(Exception):
-    """Exception raised when the server program encounters a fatal error."""
-    pass
-
-class BIND10Server:
-    """A mixin class for common BIND 10 server implementations.
-
-    It takes care of common initialization such as setting up a module CC
-    session, and running main event loop.  It also handles the "shutdown"
-    command for its normal behavior.  If a specific server class wants to
-    handle this command differently or if it does not support the command,
-    it should override the _command_handler method.
-
-    Specific modules can define module-specific class inheriting this class,
-    instantiate it, and call run() with the module name.
-
-    Methods to be implemented in the actual class:
-      _config_handler: config handler method as specified in ModuleCCSession.
-                       must be exception free; errors should be signaled by
-                       the return value.
-      _mod_command_handler: can be optionally defined to handle
-                            module-specific commands.  should conform to
-                            command handlers as specified in ModuleCCSession.
-                            must be exception free; errors should be signaled
-                            by the return value.
-      _setup_module: can be optionally defined for module-specific
-                     initialization.  This is called after the module CC
-                     session has started, and can be used for registering
-                     interest on remote modules, etc.  If it raises an
-                     exception, the server will be immediately stopped.
-                     Parameter: None, Return: None
-      _shutdown_module: can be optionally defined for module-specific
-                        finalization. This is called right before the
-                        module CC session is stopped. If it raises an
-                        exception, the server will be immediately
-                        stopped.
-                        Parameter: None, Return: None
-
-    """
-    # Will be set to True when the server should stop and shut down.
-    # Can be read via accessor method 'shutdown', mainly for testing.
-    __shutdown = False
-
-    # ModuleCCSession used in the server.  Defined as 'protectd' so tests
-    # can refer to it directly; others should access it via the
-    # 'mod_ccsession' accessor.
-    _mod_cc = None
-
-    # Will be set in run().  Define a tentative value so other methods can
-    # be tested directly.
-    __module_name = ''
-
-    # Basically constant, but allow tests to override it.
-    _select_fn = select.select
-
-    def __init__(self):
-        self._read_callbacks = {}
-        self._write_callbacks = {}
-        self._error_callbacks = {}
-
-    @property
-    def shutdown(self):
-        return self.__shutdown
-
-    @property
-    def mod_ccsession(self):
-        return self._mod_cc
-
-    def _setup_ccsession(self):
-        """Create and start module CC session.
-
-        This is essentially private, but allows tests to override it.
-
-        """
-        self._mod_cc = isc.config.ModuleCCSession(
-            self._get_specfile_location(), self._config_handler,
-            self._command_handler)
-        self._mod_cc.start()
-
-    def _get_specfile_location(self):
-        """Return the path to the module spec file following common convetion.
-
-        This method generates the path commonly used by most BIND 10 modules,
-        determined by a well known prefix and the module name.
-
-        A specific module can override this method if it uses a different
-        path for the spec file.
-
-        """
-        # First check if it's running under an 'in-source' environment,
-        # then try commonly used paths and file names.  If found, use it.
-        for ev in ['B10_FROM_SOURCE', 'B10_FROM_BUILD']:
-            if ev in os.environ:
-                specfile = os.environ[ev] + '/src/bin/' + self.__module_name +\
-                    '/' + self.__module_name + '.spec'
-                if os.path.exists(specfile):
-                    return specfile
-        # Otherwise, just use the installed path, whether or not it really
-        # exists; leave error handling to the caller.
-        specfile_path = '${datarootdir}/bind10'\
-            .replace('${datarootdir}', '${prefix}/share')\
-            .replace('${prefix}', '/Users/jinmei/opt')
-        return specfile_path + '/' + self.__module_name + '.spec'
-
-    def _trigger_shutdown(self):
-        """Initiate a shutdown sequence.
-
-        This method is expected to be called in various ways including
-        in the middle of a signal handler, and is designed to be as simple
-        as possible to minimize side effects.  Actual shutdown will take
-        place in a normal control flow.
-
-        This method is defined as 'protected'.  User classes can use it
-        to shut down the server.
-
-        """
-        self.__shutdown = True
-
-    def _run_internal(self):
-        """Main event loop.
-
-        This method is essentially private, but allows tests to override it.
-
-        """
-
-        logger.info(PYSERVER_COMMON_SERVER_STARTED, self.__module_name)
-        cc_fileno = self._mod_cc.get_socket().fileno()
-        while not self.__shutdown:
-            try:
-                read_fds = list(self._read_callbacks.keys())
-                read_fds.append(cc_fileno)
-                write_fds = list(self._write_callbacks.keys())
-                error_fds = list(self._error_callbacks.keys())
-
-                (reads, writes, errors) = \
-                    self._select_fn(read_fds, write_fds, error_fds)
-            except select.error as ex:
-                # ignore intterruption by signal; regard other select errors
-                # fatal.
-                if ex.args[0] == errno.EINTR:
-                    continue
-                else:
-                    raise
-
-            for fileno in reads:
-                if fileno in self._read_callbacks:
-                    for callback in self._read_callbacks[fileno]:
-                        callback()
-
-            for fileno in writes:
-                if fileno in self._write_callbacks:
-                    for callback in self._write_callbacks[fileno]:
-                        callback()
-
-            for fileno in errors:
-                if fileno in self._error_callbacks:
-                    for callback in self._error_callbacks[fileno]:
-                        callback()
-
-            if cc_fileno in reads:
-                # this shouldn't raise an exception (if it does, we'll
-                # propagate it)
-                self._mod_cc.check_command(True)
-
-        self._shutdown_module()
-        self._mod_cc.send_stopping()
-
-    def _command_handler(self, cmd, args):
-        logger.debug(logger.DBGLVL_TRACE_BASIC, PYSERVER_COMMON_COMMAND,
-                     self.__module_name, cmd)
-        if cmd == 'shutdown':
-            self._trigger_shutdown()
-            answer = isc.config.create_answer(0)
-        else:
-            answer = self._mod_command_handler(cmd, args)
-
-        return answer
-
-    def _mod_command_handler(self, cmd, args):
-        """The default implementation of the module specific command handler"""
-        return isc.config.create_answer(1, "Unknown command: " + str(cmd))
-
-    def _setup_module(self):
-        """The default implementation of the module specific initialization"""
-        pass
-
-    def _shutdown_module(self):
-        """The default implementation of the module specific finalization"""
-        pass
-
-    def watch_fileno(self, fileno, rcallback=None, wcallback=None, \
-                         xcallback=None):
-        """Register the fileno for the internal select() call.
-
-        *callback's are callable objects which would be called when
-        read, write, error events occur on the specified fileno.
-        """
-        if rcallback is not None:
-            if fileno in self._read_callbacks:
-                self._read_callbacks[fileno].append(rcallback)
-            else:
-                self._read_callbacks[fileno] = [rcallback]
-
-        if wcallback is not None:
-            if fileno in self._write_callbacks:
-                self._write_callbacks[fileno].append(wcallback)
-            else:
-                self._write_callbacks[fileno] = [wcallback]
-
-        if xcallback is not None:
-            if fileno in self._error_callbacks:
-                self._error_callbacks[fileno].append(xcallback)
-            else:
-                self._error_callbacks[fileno] = [xcallback]
-
-    def run(self, module_name):
-        """Start the server and let it run until it's told to stop.
-
-        Usually this must be the first method of this class that is called
-        from its user.
-
-        Parameter:
-          module_name (str): the Python module name for the actual server
-            implementation.  Often identical to the directory name in which
-            the implementation files are placed.
-
-        Returns: values expected to be used as program's exit code.
-          0: server has run and finished successfully.
-          1: some error happens
-
-          """
-        try:
-            self.__module_name = module_name
-            shutdown_sighandler = \
-                lambda signal, frame: self._trigger_shutdown()
-            signal.signal(signal.SIGTERM, shutdown_sighandler)
-            signal.signal(signal.SIGINT, shutdown_sighandler)
-            self._setup_ccsession()
-            self._setup_module()
-            self._run_internal()
-            logger.info(PYSERVER_COMMON_SERVER_STOPPED, self.__module_name)
-            return 0
-        except BIND10ServerFatal as ex:
-            logger.error(PYSERVER_COMMON_SERVER_FATAL, self.__module_name,
-                         ex)
-        except Exception as ex:
-            logger.error(PYSERVER_COMMON_UNCAUGHT_EXCEPTION, type(ex).__name__,
-                         ex)
-
-        return 1
diff --git a/src/lib/python/isc/statistics/counters.py b/src/lib/python/isc/statistics/counters.py
index 87355be..dcdd247 100644
--- a/src/lib/python/isc/statistics/counters.py
+++ b/src/lib/python/isc/statistics/counters.py
@@ -151,15 +151,6 @@ def _concat(*args, sep='/'):
     """
     return sep.join(args)
 
-class _Statistics():
-    """Statistics data set. This class will be remove in the future
-    release."""
-    # default statistics data
-    _data = {}
-    # default statistics spec used in case the specfile is omitted when
-    # constructing a Counters() object
-    _spec = []
-
 class Counters():
     """A class for holding and manipulating all statistics counters
     for a module.  A Counters object may be created by specifying a spec
@@ -174,32 +165,25 @@ class Counters():
     timers can be temporarily disabled.  If disabled, counter values are
     not changed even if methods to update them are invoked."""
 
-    # default statistics data set
-    _statistics = _Statistics()
-
-    def __init__(self, spec_file_name=None):
+    def __init__(self, spec_file_name):
         """A constructor for the Counters class. A path to the spec file
-        can be specified in spec_file_name. Statistics data based on
-        statistics spec can be accumulated if spec_file_name is
-        specified. If omitted, a default statistics spec is used. The
-        default statistics spec is defined in a hidden class named
-        _Statistics(). But the hidden class won't be used and
-        spec_file_name will be required in the future release.
+        can be specified in spec_file_name, which is required. Statistics data
+        based on statistics spec can be accumulated. If an invalid argument
+        including None is specified, ModuleSpecError might be raised.
         """
         self._zones_item_list = []
         self._start_time = {}
         self._disabled = False
         self._rlock = threading.RLock()
-        if not spec_file_name: return
-        # change the default statistics spec
-        self._statistics._spec = \
+        self._statistics_data = {}
+        self._statistics_spec = \
             isc.config.module_spec_from_file(spec_file_name).\
             get_statistics_spec()
 
     def clear_all(self):
         """clears all statistics data"""
         with self._rlock:
-            self._statistics._data = {}
+            self._statistics_data = {}
 
     def disable(self):
         """disables incrementing/decrementing counters"""
@@ -219,8 +203,8 @@ class Counters():
         identifier = _concat(*args)
         with self._rlock:
             if self._disabled: return
-            _inc_counter(self._statistics._data,
-                         self._statistics._spec,
+            _inc_counter(self._statistics_data,
+                         self._statistics_spec,
                          identifier, step)
 
     def inc(self, *args):
@@ -240,7 +224,7 @@ class Counters():
         of the specified counter.  isc.cc.data.DataNotFoundError is
         raised when the counter doesn't have a number yet."""
         identifier = _concat(*args)
-        return _get_counter(self._statistics._data, identifier)
+        return _get_counter(self._statistics_data, identifier)
 
     def start_timer(self, *args):
         """Starts a timer which is identified by args and keeps it
@@ -271,8 +255,8 @@ class Counters():
             # set the end time
             _stop_timer(
                 start_time,
-                self._statistics._data,
-                self._statistics._spec,
+                self._statistics_data,
+                self._statistics_spec,
                 identifier)
             # A datetime value of once used timer should be deleted
             # for a future use.
@@ -293,5 +277,5 @@ class Counters():
         stats module, including each counter. If nothing is counted
         yet, then it returns an empty dictionary."""
         # entire copy
-        statistics_data = self._statistics._data.copy()
+        statistics_data = self._statistics_data.copy()
         return statistics_data
diff --git a/src/lib/python/isc/statistics/dns.py b/src/lib/python/isc/statistics/dns.py
index c224687..2cd5fb6 100644
--- a/src/lib/python/isc/statistics/dns.py
+++ b/src/lib/python/isc/statistics/dns.py
@@ -69,63 +69,6 @@ documentation for isc.statistics.counters for details."""
 import isc.config
 from isc.statistics import counters
 
-class _Statistics():
-    """Statistics data set. This class will be removed in the future
-    release."""
-    # default statistics data
-    _data = {}
-    # default statistics spec used in case the specfile is omitted when
-    # constructing a Counters() object
-    _spec = [
-      {
-        "item_name": "zones",
-        "item_type": "named_set",
-        "item_optional": False,
-        "item_default": {
-          "_SERVER_" : {
-            "notifyoutv4" : 0,
-            "notifyoutv6" : 0
-          }
-        },
-        "item_title": "Zone names",
-        "item_description": "Zone names",
-        "named_set_item_spec": {
-          "item_name": "classname",
-          "item_type": "named_set",
-          "item_optional": False,
-          "item_default": {},
-          "item_title": "RR class name",
-          "item_description": "RR class name",
-          "named_set_item_spec": {
-            "item_name": "zonename",
-            "item_type": "map",
-            "item_optional": False,
-            "item_default": {},
-            "item_title": "Zone name",
-            "item_description": "Zone name",
-            "map_item_spec": [
-              {
-                "item_name": "notifyoutv4",
-                "item_type": "integer",
-                "item_optional": False,
-                "item_default": 0,
-                "item_title": "IPv4 notifies",
-                "item_description": "Number of IPv4 notifies per zone name sent out"
-              },
-              {
-                "item_name": "notifyoutv6",
-                "item_type": "integer",
-                "item_optional": False,
-                "item_default": 0,
-                "item_title": "IPv6 notifies",
-                "item_description": "Number of IPv6 notifies per zone name sent out"
-              }
-            ]
-          }
-        }
-      }
-    ]
-
 class Counters(counters.Counters):
     """A list of counters which can be handled in the class are like
     the following. Also see documentation for
@@ -176,20 +119,18 @@ class Counters(counters.Counters):
     _entire_server = '_SERVER_'
     # zone names are contained under this dirname in the spec file.
     _perzone_prefix = 'zones'
-    # default statistics data set
-    _statistics = _Statistics()
 
-    def __init__(self, spec_file_name=None):
+    def __init__(self, spec_file_name):
         """If the item `zones` is defined in the spec file, it obtains a
         list of counter names under it when initiating.  For behaviors
         other than this, see documentation for
         isc.statistics.counters.Counters.__init__()"""
         counters.Counters.__init__(self, spec_file_name)
         if self._perzone_prefix in \
-                isc.config.spec_name_list(self._statistics._spec):
+                isc.config.spec_name_list(self._statistics_spec):
             self._zones_item_list = isc.config.spec_name_list(
                 isc.config.find_spec_part(
-                    self._statistics._spec,
+                    self._statistics_spec,
                     '%s/%s/%s' % (self._perzone_prefix,
                                   '_CLASS_', self._entire_server)))
 
@@ -199,7 +140,7 @@ class Counters(counters.Counters):
         counter. If nothing is counted yet, then it returns an empty
         dictionary."""
         # entire copy
-        statistics_data = self._statistics._data.copy()
+        statistics_data = self._statistics_data.copy()
         # If there is no 'zones' found in statistics_data,
         # i.e. statistics_data contains no per-zone counter, it just
         # returns statistics_data because calculating total counts
@@ -208,7 +149,7 @@ class Counters(counters.Counters):
             return statistics_data
         zones = statistics_data[self._perzone_prefix]
         # Start calculation for '_SERVER_' counts
-        zones_spec = isc.config.find_spec_part(self._statistics._spec,
+        zones_spec = isc.config.find_spec_part(self._statistics_spec,
                                                self._perzone_prefix)
         zones_data = {}
         for cls in zones.keys():
diff --git a/src/lib/python/isc/statistics/tests/counters_test.py b/src/lib/python/isc/statistics/tests/counters_test.py
index 6b9dd8e..9d61ba3 100644
--- a/src/lib/python/isc/statistics/tests/counters_test.py
+++ b/src/lib/python/isc/statistics/tests/counters_test.py
@@ -49,12 +49,8 @@ class TestBasicMethods(unittest.TestCase):
     TEST_SPECFILE_LOCATION = TESTDATA_SRCDIR + os.sep + 'test_spec1.spec'
 
     def setUp(self):
-        imp.reload(counters)
         self.counters = counters.Counters(self.TEST_SPECFILE_LOCATION)
 
-    def tearDown(self):
-        self.counters.clear_all()
-
     def test_clear_counters(self):
         self.assertRaises(isc.cc.data.DataNotFoundError,
                           self.counters.get, 'counter')
@@ -131,15 +127,15 @@ class TestBasicMethods(unittest.TestCase):
         start_functor(concurrency, number, self.counters.inc,
                       counter_name)
         counters._stop_timer(start_time,
-                            self.counters._statistics._data,
-                            self.counters._statistics._spec,
+                            self.counters._statistics_data,
+                            self.counters._statistics_spec,
                             timer_name)
         self.assertEqual(
-            counters._get_counter(self.counters._statistics._data,
+            counters._get_counter(self.counters._statistics_data,
                                  counter_name),
             concurrency * number)
         self.assertGreaterEqual(
-            counters._get_counter(self.counters._statistics._data,
+            counters._get_counter(self.counters._statistics_data,
                                  timer_name), 0.0)
 
     def test_concat(self):
@@ -158,16 +154,20 @@ class TestBasicMethods(unittest.TestCase):
         b = a + ({},)
         self.assertRaises(TypeError, counters._concat, *b)
 
+    def test_none_of_arg_of_counters(self):
+        """Test Counters raises ModuleSpecError when specifying not valid
+        argument"""
+        self.assertRaises(isc.config.module_spec.ModuleSpecError,
+                          counters.Counters, None)
+        self.assertRaises(isc.config.module_spec.ModuleSpecError,
+                          counters.Counters, '/foo/bar')
+
 class BaseTestCounters():
 
     def setUp(self):
-        imp.reload(counters)
         self._statistics_data = {}
         self.counters = counters.Counters(self.TEST_SPECFILE_LOCATION)
 
-    def tearDown(self):
-        self.counters.clear_all()
-
     def check_get_statistics(self):
         """Checks no differences between the value returned from
         get_statistics() and locally collected statistics data. Also
@@ -186,7 +186,7 @@ class BaseTestCounters():
         else:
             self.assertTrue(isc.config.ModuleSpec(
                     {'module_name': 'Foo',
-                     'statistics': self.counters._statistics._spec}
+                     'statistics': self.counters._statistics_spec}
                     ).validate_statistics(
                     False, self._statistics_data))
 
@@ -203,19 +203,27 @@ class BaseTestCounters():
         self.assertRaises(isc.cc.data.DataNotFoundError,
                           self.counters.get, '__undefined__')
 
-class TestCounters0(unittest.TestCase, BaseTestCounters):
-    TEST_SPECFILE_LOCATION = None
-    def setUp(self):
-        BaseTestCounters.setUp(self)
-    def tearDown(self):
-        BaseTestCounters.tearDown(self)
-
 class TestCounters1(unittest.TestCase, BaseTestCounters):
     TEST_SPECFILE_LOCATION = TESTDATA_SRCDIR + os.sep + 'test_spec1.spec'
     def setUp(self):
         BaseTestCounters.setUp(self)
-    def tearDown(self):
-        BaseTestCounters.tearDown(self)
+
+    def test_counters(self):
+        spec = isc.config.module_spec_from_file(self.TEST_SPECFILE_LOCATION)
+        self.assertEqual(spec.get_statistics_spec(),
+                         self.counters._statistics_spec)
+        for name in isc.config.spec_name_list(self.counters._statistics_spec):
+            self.counters.inc(name)
+            self.assertEqual(self.counters.get(name), 1)
+            # checks disable/enable
+            self.counters.disable()
+            self.counters.inc(name)
+            self.assertEqual(self.counters.get(name), 1)
+            self.counters.enable()
+            self.counters.inc(name)
+            self.assertEqual(self.counters.get(name), 2)
+        self._statistics_data = {'counter':2, 'seconds': 2.0}
+        self.check_get_statistics()
 
 if __name__== "__main__":
     unittest.main()
diff --git a/src/lib/python/isc/statistics/tests/dns_test.py b/src/lib/python/isc/statistics/tests/dns_test.py
index 59187d9..1ebe169 100644
--- a/src/lib/python/isc/statistics/tests/dns_test.py
+++ b/src/lib/python/isc/statistics/tests/dns_test.py
@@ -30,7 +30,6 @@ from isc.statistics import dns
 class BaseTestCounters(counters_test.BaseTestCounters):
 
     def setUp(self):
-        imp.reload(dns)
         self._statistics_data = {}
         self.counters = dns.Counters(self.TEST_SPECFILE_LOCATION)
         self._entire_server    = self.counters._entire_server
@@ -73,7 +72,7 @@ class BaseTestCounters(counters_test.BaseTestCounters):
         # for counters of xfer running
         _suffix = 'xfr_running'
         _xfrrunning_names = \
-            isc.config.spec_name_list(self.counters._statistics._spec,
+            isc.config.spec_name_list(self.counters._statistics_spec,
                                       "", True)
         for name in _xfrrunning_names:
             if name.find(_suffix) != 1: continue
@@ -102,7 +101,7 @@ class BaseTestCounters(counters_test.BaseTestCounters):
         # for ipsocket/unixsocket counters
         _prefix = 'socket/'
         _socket_names = \
-            isc.config.spec_name_list(self.counters._statistics._spec,
+            isc.config.spec_name_list(self.counters._statistics_spec,
                                       "", True)
         for name in _socket_names:
             if name.find(_prefix) != 0: continue
@@ -142,36 +141,24 @@ class TestCounters2(unittest.TestCase, BaseTestCounters):
     TEST_SPECFILE_LOCATION = TESTDATA_SRCDIR + os.sep + 'test_spec2.spec'
     def setUp(self):
         BaseTestCounters.setUp(self)
-    def tearDown(self):
-        BaseTestCounters.tearDown(self)
 
 class TestCounters3(unittest.TestCase, BaseTestCounters):
     TEST_SPECFILE_LOCATION = TESTDATA_SRCDIR + os.sep + 'test_spec3.spec'
     @classmethod
-    def setUpClass(cls):
-        imp.reload(dns)
     def setUp(self):
         BaseTestCounters.setUp(self)
-    def tearDown(self):
-        BaseTestCounters.tearDown(self)
 
 class BaseDummyModule():
     """A base dummy class"""
     TEST_SPECFILE_LOCATION = TESTDATA_SRCDIR + os.sep + 'test_spec2.spec'
-    def __init__(self):
-        self.counters = dns.Counters(self.TEST_SPECFILE_LOCATION)
+    def __init__(self, counters):
+        self.counters = counters
 
     def get_counters(self):
         return self.counters.get_statistics()
 
-    def clear_counters(self):
-        self.counters.clear_all()
-
 class DummyNotifyOut(BaseDummyModule):
     """A dummy class equivalent to notify.notify_out.NotifyOut"""
-    def __init__(self):
-        self.counters = dns.Counters()
-
     def inc_counters(self):
         """increments counters"""
         self.counters.inc('zones', TEST_ZONE_CLASS_STR,
@@ -202,46 +189,23 @@ class DummyUnixSockServer(BaseDummyModule):
 class DummyXfroutServer(BaseDummyModule):
     """A dummy class equivalent to XfroutServer in b10-xfrout"""
     def __init__(self):
-        super().__init__()
-        self.xfrout_sess = DummyXfroutSession()
-        self.unix_socket_server = DummyUnixSockServer()
-        self.notifier = DummyNotifyOut()
+        self.counters = dns.Counters(self.TEST_SPECFILE_LOCATION)
+        self.xfrout_sess = DummyXfroutSession(self.counters)
+        self.unix_socket_server = DummyUnixSockServer(self.counters)
+        self.notifier = DummyNotifyOut(self.counters)
 
     def inc_counters(self):
         self.xfrout_sess.inc_counters()
         self.unix_socket_server.inc_counters()
         self.notifier.inc_counters()
 
-class TestDummyNotifyOut(unittest.TestCase):
-    """Tests counters are incremented in which the spec file is not
-    loaded"""
-    def setUp(self):
-        imp.reload(dns)
-        self.notifier = DummyNotifyOut()
-        self.notifier.inc_counters()
-
-    def tearDown(self):
-        self.notifier.clear_counters()
-
-    def test_counters(self):
-        self.assertEqual(
-            {'zones': {TEST_ZONE_CLASS_STR: { '_SERVER_':
-                           {'notifyoutv4': 1, 'notifyoutv6': 1},
-                                              TEST_ZONE_NAME_STR:
-                           {'notifyoutv4': 1, 'notifyoutv6': 1}}}},
-            self.notifier.get_counters())
-
 class TestDummyXfroutServer(unittest.TestCase):
     """Tests counters are incremented or decremented in which the same
     spec file is multiply loaded in each child class"""
     def setUp(self):
-        imp.reload(dns)
         self.xfrout_server = DummyXfroutServer()
         self.xfrout_server.inc_counters()
 
-    def tearDown(self):
-        self.xfrout_server.clear_counters()
-
     def test_counters(self):
         self.assertEqual(
             {'axfr_running': 0, 'ixfr_running': 0,
diff --git a/src/lib/python/isc/util/Makefile.am b/src/lib/python/isc/util/Makefile.am
index eaeedd8..e742f45 100644
--- a/src/lib/python/isc/util/Makefile.am
+++ b/src/lib/python/isc/util/Makefile.am
@@ -1,8 +1,23 @@
 SUBDIRS = . cio tests
 
-python_PYTHON =  __init__.py process.py socketserver_mixin.py file.py
+python_PYTHON =  __init__.py process.py socketserver_mixin.py file.py \
+	traceback_handler.py
+BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/work/util_messages.py
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/util_messages.py
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
 python_PYTHON += address_formatter.py
 
+EXTRA_DIST = util_messages.mes
+
+CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/work/util_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/util_messages.pyc
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/util_messages.pyo
+
+# Define rule to build logging source files from message file
+$(PYTHON_LOGMSGPKG_DIR)/work/util_messages.py: util_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+		-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/util_messages.mes
+
 pythondir = $(pyexecdir)/isc/util
 
 CLEANDIRS = __pycache__
diff --git a/src/lib/python/isc/util/tests/Makefile.am b/src/lib/python/isc/util/tests/Makefile.am
index 4df6947..be60c9a 100644
--- a/src/lib/python/isc/util/tests/Makefile.am
+++ b/src/lib/python/isc/util/tests/Makefile.am
@@ -1,6 +1,6 @@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
 PYTESTS = process_test.py socketserver_mixin_test.py file_test.py
-PYTESTS += address_formatter_test.py
+PYTESTS += address_formatter_test.py traceback_handler_test.py
 EXTRA_DIST = $(PYTESTS)
 
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
diff --git a/src/lib/python/isc/util/tests/address_formatter_test.py b/src/lib/python/isc/util/tests/address_formatter_test.py
index 295b7c3..d181e70 100644
--- a/src/lib/python/isc/util/tests/address_formatter_test.py
+++ b/src/lib/python/isc/util/tests/address_formatter_test.py
@@ -62,7 +62,5 @@ class AddressFormatterTest(unittest.TestCase):
         self.assertRaises(ValueError, str, AddressFormatter(("::1", 123), 1))
         self.assertRaises(ValueError, str, AddressFormatter(("::1", 123), 1))
 
-
-
 if __name__ == "__main__":
     unittest.main()
diff --git a/src/lib/python/isc/util/tests/traceback_handler_test.py b/src/lib/python/isc/util/tests/traceback_handler_test.py
new file mode 100644
index 0000000..cbd1baa
--- /dev/null
+++ b/src/lib/python/isc/util/tests/traceback_handler_test.py
@@ -0,0 +1,101 @@
+# Copyright (C) 2013  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import unittest
+import os
+import isc.log
+import isc.util.traceback_handler
+
+class TracebackHandlerTest(unittest.TestCase):
+    def setUp(self):
+        """
+        Save some things to be restored later, if we overwrite them
+        for tests.
+        """
+        self.exit = isc.util.traceback_handler.sys.exit
+        self.logger = isc.util.traceback_handler.logger
+        # Sanity check - the functions exist.
+        self.assertTrue(self.exit)
+        self.assertTrue(self.logger)
+
+    def tearDown(self):
+        """
+        Restore mocked things.
+        """
+        isc.util.traceback_handler.sys.exit = self.exit
+        isc.util.traceback_handler.logger = self.logger
+
+    def test_success(self):
+        """
+        Test the handler doesn't influence the result of successful
+        function.
+        """
+        self.called = False
+        def succ():
+            self.called = True
+            return 42
+
+        self.assertEqual(42,
+                         isc.util.traceback_handler.traceback_handler(succ))
+        self.assertTrue(self.called)
+
+    def test_success_no_returned_value(self):
+        """
+        Test the handler handles the case where main() returns nothing.
+        """
+        self.called = False
+        def succ():
+            self.called = True
+            return
+
+        self.assertIsNone(isc.util.traceback_handler.traceback_handler(succ))
+        self.assertTrue(self.called)
+
+    def test_exception(self):
+        """
+        Test the exception is caught and logged, but not propagated.
+        """
+        # Mock up bunch of things
+        self.exited = False
+        def exit(status):
+            self.assertEqual(1, status)
+            self.exited = True
+        isc.util.traceback_handler.sys.exit = exit
+        self.logged = False
+        obj = self
+        class Logger:
+            def fatal(self, message, ename, exception, filename):
+                obj.assertTrue(isinstance(exception, Exception))
+                obj.assertEqual('Exception', ename)
+                obj.assertTrue(os.path.isfile(filename))
+                with open(filename) as f:
+                    text = f.read()
+                obj.assertTrue(text.startswith('Traceback'))
+                os.remove(filename)
+                obj.logged = True
+        isc.util.traceback_handler.logger = Logger()
+        # The failing function
+        def fail():
+            raise Exception('Anybody there?')
+        # Does not raise, but returns nothing
+        self.assertIsNone(isc.util.traceback_handler.traceback_handler(fail))
+        # It logged and exited (sane values for those are checked in the mocks)
+        self.assertTrue(self.exited)
+        self.assertTrue(self.logged)
+
+if __name__ == "__main__":
+    isc.log.init("bind10")
+    isc.log.resetUnitTestRootLogger()
+    unittest.main()
diff --git a/src/lib/python/isc/util/traceback_handler.py b/src/lib/python/isc/util/traceback_handler.py
new file mode 100644
index 0000000..74ec706
--- /dev/null
+++ b/src/lib/python/isc/util/traceback_handler.py
@@ -0,0 +1,39 @@
+# Copyright (C) 2013  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from isc.log_messages.util_messages import *
+import sys
+import tempfile
+import os
+import traceback
+
+logger = isc.log.Logger('util')
+
+def traceback_handler(main):
+    """
+    Handle uncaught exception from the main callable.
+
+    The function runs the callable passed as main (it is called
+    without any provided parameters). If it raises any exception,
+    the exception is logged and the application is terminated.
+    """
+    try:
+        return main()
+    except Exception as e:
+        fd, name = tempfile.mkstemp(text=True)
+        with os.fdopen(fd, 'w') as handle:
+            traceback.print_exc(None, handle)
+        logger.fatal(PYTHON_UNHANDLED_EXCEPTION, type(e).__name__, e, name)
+        sys.exit(1)
diff --git a/src/lib/python/isc/util/util_messages.mes b/src/lib/python/isc/util/util_messages.mes
new file mode 100644
index 0000000..8258e85
--- /dev/null
+++ b/src/lib/python/isc/util/util_messages.mes
@@ -0,0 +1,26 @@
+# Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# No namespace declaration - these constants go in the global namespace
+# of the isc.util.util_messages python module.
+
+% PYTHON_UNHANDLED_EXCEPTION program terminated with exception %1: %2. More info in %3
+A program encountered an unexpected situation and terminated because it
+didn't know how to handle it. The exact problem is logged in the
+message. This might be caused by a bug in the program, a broken
+installation, or just a very rare condition which wasn't handled in the
+code. A full stack trace is left in the generated file. If you report a
+bug for this exception, please include that file. The file will not be
+deleted automatically and you may want to remove it when you no longer
+need the information there.
diff --git a/src/lib/resolve/Makefile.am b/src/lib/resolve/Makefile.am
index 0016684..13e37e1 100644
--- a/src/lib/resolve/Makefile.am
+++ b/src/lib/resolve/Makefile.am
@@ -8,8 +8,11 @@ AM_CPPFLAGS += $(SQLITE_CFLAGS)
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 # Define rule to build logging source files from message file
-resolve_messages.h resolve_messages.cc: resolve_messages.mes
+resolve_messages.h resolve_messages.cc: s-messages
+
+s-messages: resolve_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/resolve/resolve_messages.mes
+	touch $@
 
 # Tell Automake that the nsasdef.{cc,h} source files are created in the build
 # process, so it must create these before doing anything else.  Although they
@@ -19,7 +22,7 @@ resolve_messages.h resolve_messages.cc: resolve_messages.mes
 # present when they are compiled), the safest option is to create it first.
 BUILT_SOURCES = resolve_messages.h resolve_messages.cc
 
-CLEANFILES = *.gcno *.gcda resolve_messages.cc resolve_messages.h
+CLEANFILES = *.gcno *.gcda resolve_messages.cc resolve_messages.h s-messages
 
 lib_LTLIBRARIES = libb10-resolve.la
 libb10_resolve_la_SOURCES = resolve.h resolve.cc
@@ -35,6 +38,7 @@ libb10_resolve_la_LIBADD = $(top_builddir)/src/lib/dns/libb10-dns++.la
 libb10_resolve_la_LIBADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 libb10_resolve_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
 libb10_resolve_la_LIBADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
+libb10_resolve_la_LIBADD += $(top_builddir)/src/lib/nsas/libb10-nsas.la
 
 # The message file should be in the distribution.
 EXTRA_DIST = resolve_messages.mes
@@ -42,8 +46,4 @@ EXTRA_DIST = resolve_messages.mes
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
 libb10_resolve_la_CXXFLAGS = $(AM_CXXFLAGS)
-if USE_CLANGPP
-# For clang++, we need to turn off -Werror completely.
-libb10_resolve_la_CXXFLAGS += -Wno-error
-endif
 libb10_resolve_la_CPPFLAGS = $(AM_CPPFLAGS)
diff --git a/src/lib/server_common/Makefile.am b/src/lib/server_common/Makefile.am
index cf9059a..9ea55d8 100644
--- a/src/lib/server_common/Makefile.am
+++ b/src/lib/server_common/Makefile.am
@@ -33,9 +33,12 @@ libb10_server_common_la_LIBADD += $(top_builddir)/src/lib/acl/libb10-acl.la
 libb10_server_common_la_LIBADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
 libb10_server_common_la_LIBADD += $(top_builddir)/src/lib/util/io/libb10-util-io.la
 BUILT_SOURCES = server_common_messages.h server_common_messages.cc
-server_common_messages.h server_common_messages.cc: server_common_messages.mes
+server_common_messages.h server_common_messages.cc: s-messages
+
+s-messages: server_common_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/server_common/server_common_messages.mes
+	touch $@
 
 EXTRA_DIST = server_common_messages.mes
 
-CLEANFILES = *.gcno *.gcda server_common_messages.h server_common_messages.cc
+CLEANFILES = *.gcno *.gcda server_common_messages.h server_common_messages.cc s-messages
diff --git a/src/lib/server_common/tests/client_unittest.cc b/src/lib/server_common/tests/client_unittest.cc
index 14f6fbc..f962c8d 100644
--- a/src/lib/server_common/tests/client_unittest.cc
+++ b/src/lib/server_common/tests/client_unittest.cc
@@ -63,7 +63,8 @@ protected:
 
 TEST_F(ClientTest, constructIPv4) {
     EXPECT_EQ(AF_INET, client4->getRequestSourceEndpoint().getFamily());
-    EXPECT_EQ(IPPROTO_UDP, client4->getRequestSourceEndpoint().getProtocol());
+    EXPECT_EQ(static_cast<short>(IPPROTO_UDP),
+              client4->getRequestSourceEndpoint().getProtocol());
     EXPECT_EQ("192.0.2.1",
               client4->getRequestSourceEndpoint().getAddress().toText());
     EXPECT_EQ(53214, client4->getRequestSourceEndpoint().getPort());
@@ -77,7 +78,8 @@ TEST_F(ClientTest, constructIPv4) {
 
 TEST_F(ClientTest, constructIPv6) {
     EXPECT_EQ(AF_INET6, client6->getRequestSourceEndpoint().getFamily());
-    EXPECT_EQ(IPPROTO_TCP, client6->getRequestSourceEndpoint().getProtocol());
+    EXPECT_EQ(static_cast<short>(IPPROTO_TCP),
+              client6->getRequestSourceEndpoint().getProtocol());
     EXPECT_EQ("2001:db8::1",
               client6->getRequestSourceEndpoint().getAddress().toText());
     EXPECT_EQ(53216, client6->getRequestSourceEndpoint().getPort());
diff --git a/src/lib/statistics/tests/Makefile.am b/src/lib/statistics/tests/Makefile.am
index 25a3db2..f45a829 100644
--- a/src/lib/statistics/tests/Makefile.am
+++ b/src/lib/statistics/tests/Makefile.am
@@ -40,10 +40,6 @@ run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
 if USE_GXX
 run_unittests_CXXFLAGS += -Wno-unused-parameter
 endif
-if USE_CLANGPP
-# Same for clang++, but we need to turn off -Werror completely.
-run_unittests_CXXFLAGS += -Wno-error
-endif
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/testutils/Makefile.am b/src/lib/testutils/Makefile.am
index 2281b6d..574cc78 100644
--- a/src/lib/testutils/Makefile.am
+++ b/src/lib/testutils/Makefile.am
@@ -11,7 +11,8 @@ libb10_testutils_la_SOURCES = srv_test.h srv_test.cc
 libb10_testutils_la_SOURCES += dnsmessage_test.h dnsmessage_test.cc
 libb10_testutils_la_SOURCES += mockups.h
 libb10_testutils_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-libb10_testutils_la_LIBADD = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+libb10_testutils_la_LIBADD  = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+libb10_testutils_la_LIBADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
 endif
 
 EXTRA_DIST = portconfig.h socket_request.h
diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am
index ab85fa2..d8f3d30 100644
--- a/src/lib/util/tests/Makefile.am
+++ b/src/lib/util/tests/Makefile.am
@@ -50,8 +50,7 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 
 run_unittests_LDADD = $(top_builddir)/src/lib/util/libb10-util.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libb10-util-io.la
-run_unittests_LDADD += \
-	$(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 run_unittests_LDADD += $(GTEST_LDADD)
 endif
diff --git a/src/lib/util/threads/tests/Makefile.am b/src/lib/util/threads/tests/Makefile.am
index a12d221..80c6ece 100644
--- a/src/lib/util/threads/tests/Makefile.am
+++ b/src/lib/util/threads/tests/Makefile.am
@@ -29,8 +29,7 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(PTHREAD_LDFLAGS)
 
 run_unittests_LDADD = $(top_builddir)/src/lib/util/threads/libb10-threads.la
-run_unittests_LDADD += \
-        $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD += $(GTEST_LDADD)
 endif
 
diff --git a/src/lib/xfr/Makefile.am b/src/lib/xfr/Makefile.am
index 5551a5b..5e90e9b 100644
--- a/src/lib/xfr/Makefile.am
+++ b/src/lib/xfr/Makefile.am
@@ -5,10 +5,8 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
-AM_CXXFLAGS += -Wno-unused-parameter # see src/lib/cc/Makefile.am
-if USE_CLANGPP
-AM_CXXFLAGS += -Wno-error
-endif
+AM_CXXFLAGS += -Wno-unused-parameter
+# see src/lib/cc/Makefile.am
 
 CLEANFILES = *.gcno *.gcda
 
diff --git a/tests/lettuce/features/example.feature b/tests/lettuce/features/example.feature
index ee84b46..48cef20 100644
--- a/tests/lettuce/features/example.feature
+++ b/tests/lettuce/features/example.feature
@@ -132,6 +132,36 @@ Feature: Example feature
         A query for www.example.org to 127.0.0.1:47806 should have rcode NOERROR
         A query for www.example.org type A class IN to 127.0.0.1:47806 should have rcode NOERROR
 
+    Scenario: example.org mixed-case query
+        # This scenario performs a mixed-case query and checks that the
+        # response has the name copied from the question exactly
+        # (without any change in case). For why this is necessary, see
+        # section 5.2 of:
+        # http://tools.ietf.org/html/draft-vixie-dnsext-dns0x20-00
+
+        Given I have bind10 running with configuration example.org.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A query for wWw.eXaMpLe.Org should have rcode NOERROR
+        The last query response should have qdcount 1
+        The last query response should have ancount 1
+        The last query response should have nscount 3
+        The last query response should have adcount 0
+        The question section of the last query response should exactly be
+        """
+        ;wWw.eXaMpLe.Org. IN A
+        """
+
     Scenario: changing database
         # This scenario contains a lot of 'wait for' steps
         # If those are not present, the asynchronous nature of the application
diff --git a/tests/lettuce/features/terrain/querying.py b/tests/lettuce/features/terrain/querying.py
index ec75490..d585e5e 100644
--- a/tests/lettuce/features/terrain/querying.py
+++ b/tests/lettuce/features/terrain/querying.py
@@ -110,6 +110,8 @@ class QueryResult(object):
             self.line_handler = self.parse_answer
         elif line == ";; OPT PSEUDOSECTION:\n":
             self.line_handler = self.parse_opt
+        elif line == ";; QUESTION SECTION:\n":
+            self.line_handler = self.parse_question
         elif line == ";; AUTHORITY SECTION:\n":
             self.line_handler = self.parse_authority
         elif line == ";; ADDITIONAL SECTION:\n":
@@ -290,8 +292,8 @@ def check_last_query(step, item, value):
     assert str(value) == str(lq_val),\
            "Got: " + str(lq_val) + ", expected: " + str(value)
 
- at step('([a-zA-Z]+) section of the last query response should be')
-def check_last_query_section(step, section):
+ at step('([a-zA-Z]+) section of the last query response should (exactly )?be')
+def check_last_query_section(step, section, exact):
     """
     Check the entire contents of the given section of the response of the last
     query.
@@ -330,9 +332,10 @@ def check_last_query_section(step, section):
     # replace whitespace of any length by one space
     response_string = re.sub("[ \t]+", " ", response_string)
     expect = re.sub("[ \t]+", " ", step.multiline)
-    # lowercase them
-    response_string = response_string.lower()
-    expect = expect.lower()
+    # lowercase them unless we need to do an exact match
+    if exact is None:
+        response_string = response_string.lower()
+        expect = expect.lower()
     # sort them
     response_string_parts = response_string.split("\n")
     response_string_parts.sort()
diff --git a/tests/lettuce/features/xfrin_notify_handling.feature b/tests/lettuce/features/xfrin_notify_handling.feature
index 4f9d44e..2f43dfd 100644
--- a/tests/lettuce/features/xfrin_notify_handling.feature
+++ b/tests/lettuce/features/xfrin_notify_handling.feature
@@ -28,8 +28,8 @@ Feature: Xfrin incoming notify handling
     # Test1 for Xfrout statistics
     #
     check initial statistics not containing example.org for Xfrout with cmdctl port 47804 except for the following items
-      | item_name                | item_max | item_min |
-      | socket.unixdomain.open   |        1 |        0 |
+      | item_name                | min_value | max_value |
+      | socket.unixdomain.open   |         0 |         1 |
     # Note: .Xfrout.socket.unixdomain.open can be either expected to
     # be 0 or 1 here.  The reason is: if b10-xfrout has started up and is
     # ready for a request from b10-stats, then b10-stats does request
@@ -41,7 +41,13 @@ Feature: Xfrin incoming notify handling
     #
     # Test2 for Xfrin statistics
     #
-    check initial statistics not containing example.org for Xfrin
+    check initial statistics not containing example.org for Xfrin except for the following items
+      | item_name       | min_value | max_value |
+      | soa_in_progress |         0 |         1 |
+      | axfr_running    |         0 |         1 |
+    # Note: soa_in_progress and axfr_running cannot be always a fixed value. The
+    # reason is same as the case of .Xfrout.socket.unixdomain.open. as described
+    # above.
 
     When I send bind10 with cmdctl port 47804 the command Xfrout notify example.org IN
     Then wait for new master stderr message XFROUT_NOTIFY_COMMAND
@@ -99,17 +105,23 @@ Feature: Xfrin incoming notify handling
     wait for new bind10 stderr message XFRIN_RECEIVED_COMMAND
     last bindctl output should not contain "error"
 
-    When I query statistics zones of bind10 module Xfrin with cmdctl
-    The statistics counters are 0 in category .Xfrin.zones.IN except for the following items
-      | item_name                       | item_value | min_value |
-      | _SERVER_.soaoutv6               |          1 |           |
-      | _SERVER_.axfrreqv6              |          1 |           |
-      | _SERVER_.xfrsuccess             |          1 |           |
-      | _SERVER_.last_axfr_duration     |            |       0.0 |
-      | example.org..soaoutv6           |          1 |           |
-      | example.org..axfrreqv6          |          1 |           |
-      | example.org..xfrsuccess         |          1 |           |
-      | example.org..last_axfr_duration |            |       0.0 |
+    When I query statistics of bind10 module Xfrin with cmdctl
+    The statistics counters are 0 in category .Xfrin except for the following items
+      | item_name                                | item_value | min_value |
+      | zones.IN._SERVER_.soaoutv6               |          1 |           |
+      | zones.IN._SERVER_.axfrreqv6              |          1 |           |
+      | zones.IN._SERVER_.xfrsuccess             |          1 |           |
+      | zones.IN._SERVER_.last_axfr_duration     |            |       0.0 |
+      | zones.IN.example.org..soaoutv6           |          1 |           |
+      | zones.IN.example.org..axfrreqv6          |          1 |           |
+      | zones.IN.example.org..xfrsuccess         |          1 |           |
+      | zones.IN.example.org..last_axfr_duration |            |       0.0 |
+      | soa_in_progress                          |          0 |           |
+      | axfr_running                             |          0 |           |
+      | socket.ipv6.tcp.open                     |            |         1 |
+      | socket.ipv6.tcp.close                    |            |         1 |
+      | socket.ipv6.tcp.conn                     |            |         1 |
+      | socket.ipv6.tcp.connfail                 |          0 |           |
 
     #
     # Test for handling incoming notify only in IPv4
@@ -136,14 +148,18 @@ Feature: Xfrin incoming notify handling
     # Test1 for Xfrout statistics
     #
     check initial statistics not containing example.org for Xfrout with cmdctl port 47804 except for the following items
-      | item_name                | item_max | item_min |
-      | socket.unixdomain.open   |        1 |        0 |
+      | item_name                | min_value | max_value |
+      | socket.unixdomain.open   |         0 |         1 |
     # Note: See above about .Xfrout.socket.unixdomain.open.
 
     #
     # Test2 for Xfrin statistics
     #
-    check initial statistics not containing example.org for Xfrin
+    check initial statistics not containing example.org for Xfrin except for the following items
+      | item_name       | min_value | max_value |
+      | soa_in_progress |         0 |         1 |
+      | axfr_running    |         0 |         1 |
+    # Note: See above about soa_in_progress and axfr_running of Xfrin
 
     When I send bind10 with cmdctl port 47804 the command Xfrout notify example.org IN
     Then wait for new master stderr message XFROUT_NOTIFY_COMMAND
@@ -201,17 +217,23 @@ Feature: Xfrin incoming notify handling
     wait for new bind10 stderr message XFRIN_RECEIVED_COMMAND
     last bindctl output should not contain "error"
 
-    When I query statistics zones of bind10 module Xfrin with cmdctl
-    The statistics counters are 0 in category .Xfrin.zones.IN except for the following items
-      | item_name                       | item_value | min_value |
-      | _SERVER_.soaoutv4               |          1 |           |
-      | _SERVER_.axfrreqv4              |          1 |           |
-      | _SERVER_.xfrsuccess             |          1 |           |
-      | _SERVER_.last_axfr_duration     |            |       0.0 |
-      | example.org..soaoutv4           |          1 |           |
-      | example.org..axfrreqv4          |          1 |           |
-      | example.org..xfrsuccess         |          1 |           |
-      | example.org..last_axfr_duration |            |       0.0 |
+    When I query statistics of bind10 module Xfrin with cmdctl
+    The statistics counters are 0 in category .Xfrin except for the following items
+      | item_name                                | item_value | min_value |
+      | zones.IN._SERVER_.soaoutv4               |          1 |           |
+      | zones.IN._SERVER_.axfrreqv4              |          1 |           |
+      | zones.IN._SERVER_.xfrsuccess             |          1 |           |
+      | zones.IN._SERVER_.last_axfr_duration     |            |       0.0 |
+      | zones.IN.example.org..soaoutv4           |          1 |           |
+      | zones.IN.example.org..axfrreqv4          |          1 |           |
+      | zones.IN.example.org..xfrsuccess         |          1 |           |
+      | zones.IN.example.org..last_axfr_duration |            |       0.0 |
+      | soa_in_progress                          |          0 |           |
+      | axfr_running                             |          0 |           |
+      | socket.ipv4.tcp.open                     |            |         1 |
+      | socket.ipv4.tcp.close                    |            |         1 |
+      | socket.ipv4.tcp.conn                     |            |         1 |
+      | socket.ipv4.tcp.connfail                 |          0 |           |
 
     #
     # Test for Xfr request rejected
@@ -238,14 +260,18 @@ Feature: Xfrin incoming notify handling
     # Test1 for Xfrout statistics
     #
     check initial statistics not containing example.org for Xfrout with cmdctl port 47804 except for the following items
-      | item_name                | item_max | item_min |
-      | socket.unixdomain.open   |        1 |        0 |
+      | item_name                | min_value | max_value |
+      | socket.unixdomain.open   |         0 |         1 |
     # Note: See above about .Xfrout.socket.unixdomain.open.
 
     #
     # Test2 for Xfrin statistics
     #
-    check initial statistics not containing example.org for Xfrin
+    check initial statistics not containing example.org for Xfrin except for the following items
+      | item_name       | min_value | max_value |
+      | soa_in_progress |         0 |         1 |
+      | axfr_running    |         0 |         1 |
+    # Note: See above about soa_in_progress and axfr_running of Xfrin
 
     #
     # set transfer_acl rejection
@@ -309,15 +335,21 @@ Feature: Xfrin incoming notify handling
     wait for new bind10 stderr message XFRIN_RECEIVED_COMMAND
     last bindctl output should not contain "error"
 
-    When I query statistics zones of bind10 module Xfrin with cmdctl
-    The statistics counters are 0 in category .Xfrin.zones.IN except for the following items
-      | item_name              | item_value |
-      | _SERVER_.soaoutv6      |          1 |
-      | _SERVER_.axfrreqv6     |          1 |
-      | _SERVER_.xfrfail       |          1 |
-      | example.org..soaoutv6  |          1 |
-      | example.org..axfrreqv6 |          1 |
-      | example.org..xfrfail   |          1 |
+    When I query statistics of bind10 module Xfrin with cmdctl
+    The statistics counters are 0 in category .Xfrin except for the following items
+      | item_name                       | item_value | min_value |
+      | zones.IN._SERVER_.soaoutv6      |            |         1 |
+      | zones.IN._SERVER_.axfrreqv6     |            |         1 |
+      | zones.IN._SERVER_.xfrfail       |            |         1 |
+      | zones.IN.example.org..soaoutv6  |            |         1 |
+      | zones.IN.example.org..axfrreqv6 |            |         1 |
+      | zones.IN.example.org..xfrfail   |            |         1 |
+      | soa_in_progress                 |            |         0 |
+      | axfr_running                    |            |         0 |
+      | socket.ipv6.tcp.open            |            |         1 |
+      | socket.ipv6.tcp.close           |            |         1 |
+      | socket.ipv6.tcp.conn            |            |         1 |
+      | socket.ipv6.tcp.connfail        |          0 |           |
 
     #
     # Test for Xfr request rejected in IPv4
@@ -344,14 +376,18 @@ Feature: Xfrin incoming notify handling
     # Test1 for Xfrout statistics
     #
     check initial statistics not containing example.org for Xfrout with cmdctl port 47804 except for the following items
-      | item_name                | item_max | item_min |
-      | socket.unixdomain.open   |        1 |        0 |
+      | item_name                | min_value | max_value |
+      | socket.unixdomain.open   |         0 |         1 |
     # Note: See above about .Xfrout.socket.unixdomain.open.
 
     #
     # Test2 for Xfrin statistics
     #
-    check initial statistics not containing example.org for Xfrin
+    check initial statistics not containing example.org for Xfrin except for the following items
+      | item_name       | min_value | max_value |
+      | soa_in_progress |         0 |         1 |
+      | axfr_running    |         0 |         1 |
+    # Note: See above about soa_in_progress and axfr_running of Xfrin
 
     #
     # set transfer_acl rejection
@@ -415,15 +451,21 @@ Feature: Xfrin incoming notify handling
     wait for new bind10 stderr message XFRIN_RECEIVED_COMMAND
     last bindctl output should not contain "error"
 
-    When I query statistics zones of bind10 module Xfrin with cmdctl
-    The statistics counters are 0 in category .Xfrin.zones.IN except for the following items
-      | item_name              | item_value |
-      | _SERVER_.soaoutv4      |          1 |
-      | _SERVER_.axfrreqv4     |          1 |
-      | _SERVER_.xfrfail       |          1 |
-      | example.org..soaoutv4  |          1 |
-      | example.org..axfrreqv4 |          1 |
-      | example.org..xfrfail   |          1 |
+    When I query statistics of bind10 module Xfrin with cmdctl
+    The statistics counters are 0 in category .Xfrin except for the following items
+      | item_name                       | item_value | min_value |
+      | zones.IN._SERVER_.soaoutv4      |            |         1 |
+      | zones.IN._SERVER_.axfrreqv4     |            |         1 |
+      | zones.IN._SERVER_.xfrfail       |            |         1 |
+      | zones.IN.example.org..soaoutv4  |            |         1 |
+      | zones.IN.example.org..axfrreqv4 |            |         1 |
+      | zones.IN.example.org..xfrfail   |            |         1 |
+      | soa_in_progress                 |            |         0 |
+      | axfr_running                    |            |         0 |
+      | socket.ipv4.tcp.open            |            |         1 |
+      | socket.ipv4.tcp.close           |            |         1 |
+      | socket.ipv4.tcp.conn            |            |         1 |
+      | socket.ipv4.tcp.connfail        |          0 |           |
 
     #
     # Test for unreachable slave
@@ -456,8 +498,8 @@ Feature: Xfrin incoming notify handling
     When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
     The statistics counters are 0 in category .Xfrout.zones.IN except for the following items
       | item_name                | min_value | max_value |
-      | _SERVER_.notifyoutv6     |         1 |	       5 |
-      | example.org..notifyoutv6 |         1 |	       5 |
+      | _SERVER_.notifyoutv6     |         1 |         5 |
+      | example.org..notifyoutv6 |         1 |         5 |
 
     When I query statistics socket of bind10 module Xfrout with cmdctl port 47804
     The statistics counters are 0 in category .Xfrout.socket.unixdomain except for the following items
@@ -587,3 +629,46 @@ Feature: Xfrin incoming notify handling
     Then wait for master stderr message NOTIFY_OUT_TIMEOUT not NOTIFY_OUT_REPLY_RECEIVED
 
     A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN
+
+    #
+    # Test for unreachable master
+    #
+    Scenario: Handle incoming notify (unreachable master)
+
+    And I have bind10 running with configuration xfrin/retransfer_slave_notify.conf
+    And wait for bind10 stderr message BIND10_STARTED_CC
+    And wait for bind10 stderr message CMDCTL_STARTED
+    And wait for bind10 stderr message AUTH_SERVER_STARTED
+    And wait for bind10 stderr message XFRIN_STARTED
+    And wait for bind10 stderr message ZONEMGR_STARTED
+
+    A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN
+
+    #
+    # Test1 for Xfrin statistics
+    #
+    check initial statistics not containing example.org for Xfrin
+
+    #
+    # execute reftransfer for Xfrin
+    #
+    When I send bind10 the command Xfrin retransfer example.org IN
+    Then wait for new bind10 stderr message XFRIN_CONNECT_MASTER
+    Then wait for new bind10 stderr message ZONEMGR_RECEIVE_XFRIN_FAILED
+
+    #
+    # Test2 for Xfrin statistics
+    #
+    # check initial statistics
+    #
+
+    # wait until the last stats requesting is finished
+    wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+    wait for new bind10 stderr message XFRIN_RECEIVED_COMMAND
+
+    When I query statistics socket of bind10 module Xfrin with cmdctl
+    The statistics counters are 0 in category .Xfrin.socket.ipv6.tcp except for the following items
+      | item_name | min_value |
+      | open      |         1 |
+      | close     |         1 |
+      | connfail  |         1 |
diff --git a/tests/tools/badpacket/Makefile.am b/tests/tools/badpacket/Makefile.am
index b24cf3c..945d0e3 100644
--- a/tests/tools/badpacket/Makefile.am
+++ b/tests/tools/badpacket/Makefile.am
@@ -21,9 +21,6 @@ badpacket_SOURCES += scan.cc scan.h
 badpacket_SOURCES += version.h
 
 badpacket_CXXFLAGS = $(AM_CXXFLAGS)
-if USE_CLANGPP
-badpacket_CXXFLAGS += -Wno-error
-endif
 
 badpacket_LDADD  = $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
 badpacket_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
diff --git a/tests/tools/perfdhcp/Makefile.am b/tests/tools/perfdhcp/Makefile.am
index c4b82b5..31e5a31 100644
--- a/tests/tools/perfdhcp/Makefile.am
+++ b/tests/tools/perfdhcp/Makefile.am
@@ -23,6 +23,7 @@ perfdhcp_SOURCES += command_options.cc command_options.h
 perfdhcp_SOURCES += localized_option.h
 perfdhcp_SOURCES += perf_pkt6.cc perf_pkt6.h
 perfdhcp_SOURCES += perf_pkt4.cc perf_pkt4.h
+perfdhcp_SOURCES += packet_storage.h
 perfdhcp_SOURCES += pkt_transform.cc pkt_transform.h
 perfdhcp_SOURCES += stats_mgr.h
 perfdhcp_SOURCES += test_control.cc test_control.h
diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc
index c0ae6fa..7edb84b 100644
--- a/tests/tools/perfdhcp/command_options.cc
+++ b/tests/tools/perfdhcp/command_options.cc
@@ -12,19 +12,21 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include "command_options.h"
+#include <exceptions/exceptions.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/duid.h>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
 #include <config.h>
+#include <sstream>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <unistd.h>
 
-#include <boost/lexical_cast.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
-
-#include <exceptions/exceptions.h>
-#include <dhcp/iface_mgr.h>
-#include <dhcp/duid.h>
-#include "command_options.h"
 
 using namespace std;
 using namespace isc;
@@ -32,6 +34,63 @@ using namespace isc;
 namespace isc {
 namespace perfdhcp {
 
+CommandOptions::LeaseType::LeaseType()
+    : type_(ADDRESS) {
+}
+
+CommandOptions::LeaseType::LeaseType(const Type lease_type)
+    : type_(lease_type) {
+}
+
+bool
+CommandOptions::LeaseType::is(const Type lease_type) const {
+    return (lease_type == type_);
+}
+
+bool
+CommandOptions::LeaseType::includes(const Type lease_type) const {
+    return (is(ADDRESS_AND_PREFIX) || (lease_type == type_));
+}
+
+void
+CommandOptions::LeaseType::set(const Type lease_type) {
+    type_ = lease_type;
+}
+
+void
+CommandOptions::LeaseType::fromCommandLine(const std::string& cmd_line_arg) {
+    if (cmd_line_arg == "address-only") {
+        type_ = ADDRESS;
+
+    } else if (cmd_line_arg == "prefix-only") {
+        type_ = PREFIX;
+
+    } else if (cmd_line_arg == "address-and-prefix") {
+        type_ = ADDRESS_AND_PREFIX;
+
+    } else {
+        isc_throw(isc::InvalidParameter, "value of lease-type: -e<lease-type>,"
+                  " must be one of the following: 'address-only' or"
+                  " 'prefix-only'");
+    }
+}
+
+std::string
+CommandOptions::LeaseType::toText() const {
+    switch (type_) {
+    case ADDRESS:
+        return ("address-only (IA_NA option added to the client's request)");
+    case PREFIX:
+        return ("prefix-only (IA_PD option added to the client's request)");
+    case ADDRESS_AND_PREFIX:
+        return ("address-and-prefix (Both IA_NA and IA_PD options added to the"
+                " client's request)");
+    default:
+        isc_throw(Unexpected, "internal error: undefined lease type code when"
+                  " returning textual representation of the lease type");
+    }
+}
+
 CommandOptions&
 CommandOptions::instance() {
     static CommandOptions options;
@@ -52,7 +111,9 @@ CommandOptions::reset() {
     // will need to reset all members many times to perform unit tests
     ipversion_ = 0;
     exchange_mode_ = DORA_SARR;
+    lease_type_.set(LeaseType::ADDRESS);
     rate_ = 0;
+    renew_rate_ = 0;
     report_delay_ = 0;
     clients_num_ = 0;
     mac_template_.assign(mac, mac + 6);
@@ -150,7 +211,7 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
     // In this section we collect argument values from command line
     // they will be tuned and validated elsewhere
     while((opt = getopt(argc, argv, "hv46r:t:R:b:n:p:d:D:l:P:a:L:"
-                        "s:iBc1T:X:O:E:S:I:x:w:")) != -1) {
+                        "s:iBc1T:X:O:E:S:I:x:w:e:f:")) != -1) {
         stream << " -" << static_cast<char>(opt);
         if (optarg) {
             stream << " " << optarg;
@@ -232,11 +293,20 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
             }
             break;
 
+        case 'e':
+            initLeaseType();
+            break;
+
         case 'E':
             elp_offset_ = nonNegativeInteger("value of time-offset: -E<value>"
                                              " must not be a negative integer");
             break;
 
+        case 'f':
+            renew_rate_ = positiveInteger("value of the renew rate: -f<renew-rate>"
+                                          " must be a positive integer");
+            break;
+
         case 'h':
             usage();
             return (true);
@@ -438,7 +508,7 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
 
     // If DUID is not specified from command line we need to
     // generate one.
-    if (duid_template_.size() == 0) {
+    if (duid_template_.empty()) {
         generateDuidTemplate();
     }
     return (false);
@@ -506,9 +576,9 @@ CommandOptions::decodeMac(const std::string& base) {
     mac_template_.clear();
     // Get pieces of MAC address separated with : (or even ::)
     while (std::getline(s1, token, ':')) {
-        unsigned int ui = 0;
         // Convert token to byte value using std::istringstream
         if (token.length() > 0) {
+            unsigned int ui = 0;
             try {
                 // Do actual conversion
                 ui = convertHexString(token);
@@ -618,50 +688,66 @@ CommandOptions::validate() const {
           "-B is not compatible with IPv6 (-6)");
     check((getIpVersion() != 6) && (isRapidCommit() != 0),
           "-6 (IPv6) must be set to use -c");
+    check((getIpVersion() != 6) && (getRenewRate() !=0),
+          "-f<renew-rate> may be used with -6 (IPv6) only");
     check((getExchangeMode() == DO_SA) && (getNumRequests().size() > 1),
           "second -n<num-request> is not compatible with -i");
+    check((getIpVersion() == 4) && !getLeaseType().is(LeaseType::ADDRESS),
+          "-6 option must be used if lease type other than '-e address-only'"
+          " is specified");
+    check(!getTemplateFiles().empty() &&
+          !getLeaseType().is(LeaseType::ADDRESS),
+          "template files may be only used with '-e address-only'");
     check((getExchangeMode() == DO_SA) && (getDropTime()[1] != 1.),
           "second -d<drop-time> is not compatible with -i");
     check((getExchangeMode() == DO_SA) &&
           ((getMaxDrop().size() > 1) || (getMaxDropPercentage().size() > 1)),
-          "second -D<max-drop> is not compatible with -i\n");
+          "second -D<max-drop> is not compatible with -i");
     check((getExchangeMode() == DO_SA) && (isUseFirst()),
-          "-1 is not compatible with -i\n");
+          "-1 is not compatible with -i");
     check((getExchangeMode() == DO_SA) && (getTemplateFiles().size() > 1),
-          "second -T<template-file> is not compatible with -i\n");
+          "second -T<template-file> is not compatible with -i");
     check((getExchangeMode() == DO_SA) && (getTransactionIdOffset().size() > 1),
-          "second -X<xid-offset> is not compatible with -i\n");
+          "second -X<xid-offset> is not compatible with -i");
     check((getExchangeMode() == DO_SA) && (getRandomOffset().size() > 1),
-          "second -O<random-offset is not compatible with -i\n");
+          "second -O<random-offset is not compatible with -i");
     check((getExchangeMode() == DO_SA) && (getElapsedTimeOffset() >= 0),
-          "-E<time-offset> is not compatible with -i\n");
+          "-E<time-offset> is not compatible with -i");
     check((getExchangeMode() == DO_SA) && (getServerIdOffset() >= 0),
-          "-S<srvid-offset> is not compatible with -i\n");
+          "-S<srvid-offset> is not compatible with -i");
     check((getExchangeMode() == DO_SA) && (getRequestedIpOffset() >= 0),
-          "-I<ip-offset> is not compatible with -i\n");
+          "-I<ip-offset> is not compatible with -i");
+    check((getExchangeMode() == DO_SA) && (getRenewRate() != 0),
+          "-f<renew-rate> is not compatible with -i");
     check((getExchangeMode() != DO_SA) && (isRapidCommit() != 0),
-          "-i must be set to use -c\n");
+          "-i must be set to use -c");
     check((getRate() == 0) && (getReportDelay() != 0),
-          "-r<rate> must be set to use -t<report>\n");
+          "-r<rate> must be set to use -t<report>");
     check((getRate() == 0) && (getNumRequests().size() > 0),
-          "-r<rate> must be set to use -n<num-request>\n");
+          "-r<rate> must be set to use -n<num-request>");
     check((getRate() == 0) && (getPeriod() != 0),
-          "-r<rate> must be set to use -p<test-period>\n");
+          "-r<rate> must be set to use -p<test-period>");
     check((getRate() == 0) &&
           ((getMaxDrop().size() > 0) || getMaxDropPercentage().size() > 0),
-          "-r<rate> must be set to use -D<max-drop>\n");
+          "-r<rate> must be set to use -D<max-drop>");
+    check((getRate() != 0) && (getRenewRate() > getRate()),
+          "Renew rate specified as -f<renew-rate> must not be greater than"
+          " the rate specified as -r<rate>");
+    check((getRate() == 0) && (getRenewRate() != 0),
+          "Renew rate specified as -f<renew-rate> must not be specified"
+          " when -r<rate> parameter is not specified");
     check((getTemplateFiles().size() < getTransactionIdOffset().size()),
-          "-T<template-file> must be set to use -X<xid-offset>\n");
+          "-T<template-file> must be set to use -X<xid-offset>");
     check((getTemplateFiles().size() < getRandomOffset().size()),
-          "-T<template-file> must be set to use -O<random-offset>\n");
+          "-T<template-file> must be set to use -O<random-offset>");
     check((getTemplateFiles().size() < 2) && (getElapsedTimeOffset() >= 0),
-          "second/request -T<template-file> must be set to use -E<time-offset>\n");
+          "second/request -T<template-file> must be set to use -E<time-offset>");
     check((getTemplateFiles().size() < 2) && (getServerIdOffset() >= 0),
           "second/request -T<template-file> must be set to "
-          "use -S<srvid-offset>\n");
+          "use -S<srvid-offset>");
     check((getTemplateFiles().size() < 2) && (getRequestedIpOffset() >= 0),
           "second/request -T<template-file> must be set to "
-          "use -I<ip-offset>\n");
+          "use -I<ip-offset>");
 
 }
 
@@ -669,6 +755,8 @@ void
 CommandOptions::check(bool condition, const std::string& errmsg) const {
     // The same could have been done with macro or just if statement but
     // we prefer functions to macros here
+    std::ostringstream stream;
+    stream << errmsg << "\n";
     if (condition) {
         isc_throw(isc::InvalidParameter, errmsg);
     }
@@ -706,6 +794,12 @@ CommandOptions::nonEmptyString(const std::string& errmsg) const {
 }
 
 void
+CommandOptions::initLeaseType() {
+    std::string lease_type_arg = optarg;
+    lease_type_.fromCommandLine(lease_type_arg);
+}
+
+void
 CommandOptions::printCommandLine() const {
     std::cout << "IPv" << static_cast<int>(ipversion_) << std::endl;
     if (exchange_mode_ == DO_SA) {
@@ -715,9 +809,13 @@ CommandOptions::printCommandLine() const {
             std::cout << "SOLICIT-ADVERETISE only" << std::endl;
         }
     }
+    std::cout << "lease-type=" << getLeaseType().toText() << std::endl;
     if (rate_ != 0) {
         std::cout << "rate[1/s]=" << rate_ <<  std::endl;
     }
+    if (getRenewRate() != 0) {
+        std::cout << "renew-rate[1/s]=" << getRenewRate() << std::endl;
+    }
     if (report_delay_ != 0) {
         std::cout << "report[s]=" << report_delay_ << std::endl;
     }
@@ -800,13 +898,14 @@ CommandOptions::printCommandLine() const {
 void
 CommandOptions::usage() const {
     std::cout <<
-        "perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]\n"
-        "    [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
-        "    [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
-        "    [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
-        "    [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
-        "    [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
-        "    [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
+        "perfdhcp [-hv] [-4|-6] [-e<lease-type>] [-r<rate>] [-f<renew-rate>]\n"
+        "         [-t<report>] [-R<range>] [-b<base>] [-n<num-request>]\n"
+        "         [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
+        "         [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
+        "         [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
+        "         [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
+        "         [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
+        "         [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
         "\n"
         "The [server] argument is the name/address of the DHCP server to\n"
         "contact.  For DHCPv4 operation, exchanges are initiated by\n"
@@ -838,9 +937,21 @@ CommandOptions::usage() const {
         "-d<drop-time>: Specify the time after which a requeqst is treated as\n"
         "    having been lost.  The value is given in seconds and may contain a\n"
         "    fractional component.  The default is 1 second.\n"
+        "-e<lease-type>: A type of lease being requested from the server. It\n"
+        "    may be one of the following: address-only, prefix-only or\n"
+        "    address-and-prefix. The address-only indicates that the regular\n"
+        "    address (v4 or v6) will be requested. The prefix-only indicates\n"
+        "    that the IPv6 prefix will be requested. The address-and-prefix\n"
+        "    indicates that both IPv6 address and prefix will be requested.\n"
+        "    The '-e prefix-only' and -'e address-and-prefix' must not be\n"
+        "    used with -4.\n"
         "-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
         "    elapsed-time option in the (second/request) template.\n"
         "    The value 0 disables it.\n"
+        "-f<renew-rate>: A rate at which IPv6 Renew requests are sent to\n"
+        "    a server. This value must not be equal or lower than the rate\n"
+        "    specified as -r<rate>. If -r<rate> is not specified, this\n"
+        "    parameter must not be specified too.\n"
         "-h: Print this help.\n"
         "-i: Do only the initial part of an exchange: DO or SA, depending on\n"
         "    whether -6 is given.\n"
diff --git a/tests/tools/perfdhcp/command_options.h b/tests/tools/perfdhcp/command_options.h
index 246fea3..42a31c9 100644
--- a/tests/tools/perfdhcp/command_options.h
+++ b/tests/tools/perfdhcp/command_options.h
@@ -1,3 +1,4 @@
+
 // Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
@@ -15,11 +16,12 @@
 #ifndef COMMAND_OPTIONS_H
 #define COMMAND_OPTIONS_H
 
+#include <boost/noncopyable.hpp>
+
+#include <stdint.h>
 #include <string>
 #include <vector>
 
-#include <boost/noncopyable.hpp>
-
 namespace isc {
 namespace perfdhcp {
 
@@ -30,6 +32,79 @@ namespace perfdhcp {
 ///
 class CommandOptions : public boost::noncopyable {
 public:
+
+    /// \brief A class encapsulating the type of lease being requested from the
+    /// server.
+    ///
+    /// This class comprises convenience functions to convert the lease type
+    /// to the textual format and to match the appropriate lease type with the
+    /// value of the -e<lease-type> parameter specified from the command line.
+    class LeaseType {
+    public:
+
+        /// The lease type code.
+        enum Type {
+            ADDRESS,
+            PREFIX,
+            ADDRESS_AND_PREFIX
+        };
+
+        LeaseType();
+
+        /// \brief Constructor from lease type code.
+        ///
+        /// \param lease_type A lease type code.
+        LeaseType(const Type lease_type);
+
+        /// \brief Checks if lease type has the specified code.
+        ///
+        /// \param lease_type A lease type code to be checked.
+        ///
+        /// \return true if lease type is matched with the specified code.
+        bool is(const Type lease_type) const;
+
+        /// \brief Checks if lease type implies request for the address,
+        /// prefix (or both) as specified by the function argument.
+        ///
+        /// This is a convenience function to check that, for the lease type
+        /// specified from the command line, the address or prefix
+        /// (IA_NA or IA_PD) option should be sent to the server.
+        /// For example, if user specified '-e address-and-prefix' in the
+        /// command line this function will return true for both ADDRESS
+        /// and PREFIX, because both address and prefix is requested from
+        /// the server.
+        ///
+        /// \param lease_type A lease type.
+        ///
+        /// \return true if the lease type implies creation of the address,
+        /// prefix or both as specified by the argument.
+        bool includes(const Type lease_type) const;
+
+        /// \brief Sets the lease type code.
+        ///
+        /// \param lease_type A lease type code.
+        void set(const Type lease_type);
+
+        /// \brief Sets the lease type from the command line argument.
+        ///
+        /// \param cmd_line_arg An argument specified in the command line
+        /// as -e<lease-type>:
+        /// - address-only
+        /// - prefix-only
+        ///
+        /// \throw isc::InvalidParameter if the specified argument is invalid.
+        void fromCommandLine(const std::string& cmd_line_arg);
+
+        /// \brief Return textual representation of the lease type.
+        ///
+        /// \return A textual representation of the lease type.
+        std::string toText() const;
+
+    private:
+        Type type_; ///< A lease type code.
+
+    };
+
     /// 2-way (cmd line param -i) or 4-way exchanges
     enum ExchangeMode {
         DO_SA,
@@ -71,11 +146,21 @@ public:
     /// \return packet exchange mode.
     ExchangeMode getExchangeMode() const { return exchange_mode_; }
 
+    /// \ brief Returns the type of lease being requested.
+    ///
+    /// \return type of lease being requested by perfdhcp.
+    LeaseType getLeaseType() const { return (lease_type_); }
+
     /// \brief Returns echange rate.
     ///
     /// \return exchange rate per second.
     int getRate() const { return rate_; }
 
+    /// \brief Returns a rate at which IPv6 Renew messages are sent.
+    ///
+    /// \return A rate at which IPv6 Renew messages are sent.
+    int getRenewRate() const { return (renew_rate_); }
+
     /// \brief Returns delay between two performance reports.
     ///
     /// \return delay between two consecutive performance reports.
@@ -300,6 +385,11 @@ private:
     /// \throw InvalidParameter if string is empty.
     std::string nonEmptyString(const std::string& errmsg) const;
 
+    /// \brief Decodes the lease type requested by perfdhcp from optarg.
+    ///
+    /// \throw InvalidParameter if lease type value specified is invalid.
+    void initLeaseType();
+
     /// \brief Set number of clients.
     ///
     /// Interprets the getopt() "opt" global variable as the number of clients
@@ -373,8 +463,12 @@ private:
     uint8_t ipversion_;
     /// Packet exchange mode (e.g. DORA/SARR)
     ExchangeMode exchange_mode_;
+    /// Lease Type to be obtained: address only, IPv6 prefix only.
+    LeaseType lease_type_;
     /// Rate in exchange per second
     int rate_;
+    /// A rate at which DHCPv6 Renew messages are sent.
+    int renew_rate_;
     /// Delay between generation of two consecutive
     /// performance reports
     int report_delay_;
@@ -396,7 +490,7 @@ private:
     /// Indicates number of -d<value> parameters specified by user.
     /// If this value goes above 2, command line parsing fails.
     uint8_t drop_time_set_;
-    /// Time to elapse before request is lost. The fisrt value of
+    /// Time to elapse before request is lost. The first value of
     /// two-element vector refers to DO/SA exchanges,
     /// second value refers to RA/RR. Default values are { 1, 1 }
     std::vector<double> drop_time_;
@@ -433,12 +527,12 @@ private:
     /// Indicates that we take server id from first received packet.
     bool use_first_;
     /// Packet template file names. These files store template packets
-    /// that are used for initiating echanges. Template packets
+    /// that are used for initiating exchanges. Template packets
     /// read from files are later tuned with variable data.
     std::vector<std::string> template_file_;
     /// Offset of transaction id in template files. First vector
     /// element points to offset for DISCOVER/SOLICIT messages,
-    /// second element points to trasaction id offset for
+    /// second element points to transaction id offset for
     /// REQUEST messages
     std::vector<int> xid_offset_;
     /// Random value offset in templates. Random value offset
diff --git a/tests/tools/perfdhcp/packet_storage.h b/tests/tools/perfdhcp/packet_storage.h
new file mode 100644
index 0000000..2adb070
--- /dev/null
+++ b/tests/tools/perfdhcp/packet_storage.h
@@ -0,0 +1,161 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef PACKET_STORAGE_H
+#define PACKET_STORAGE_H
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <list>
+#include <stdint.h>
+
+namespace isc {
+namespace perfdhcp {
+
+/// \brief Represents a list of packets with a sequential and random access to
+/// list elements.
+///
+/// The main purpose of this class is to support sending Renew and Release
+/// messages from perfdhcp. The Renew and Release messages are sent for existing
+/// leases only. Therefore, the typical use case for this class is that it holds
+/// a list of Reply messages sent by the server in response to Request messages.
+/// The Request messages hold addresses and/or IPv6 prefixes acquired so they
+/// can be used to identify existing leases. When perfdhcp needs to send Renew
+/// or Release message, it will access one of the elements on this list and
+/// will create the Renew or Release message based on its content. Once the
+/// element (packet) is returned it is also deleted from the list, so as it is
+/// not used again. This class provide either sequential access to the packets
+/// or random access. The random access algorithm is much slower but at least
+/// it allows to simulate more real scenario when the renewing or releasing
+/// client is random.
+///
+/// \tparam Pkt4 or Pkt6 class, which represents DHCPv4 or DHCPv6 message
+/// respectively.
+///
+/// \note Although the class is intended to hold Pkt4 and Pkt6 objects, the
+/// current implementation is generic enough to holds any object wrapped in the
+/// boost::shared_ptr.
+template<typename T>
+class PacketStorage : public boost::noncopyable {
+public:
+    /// A type which represents the pointer to a packet.
+    typedef boost::shared_ptr<T> PacketPtr;
+
+private:
+    /// An internal container actually holding packets.
+    typedef typename std::list<PacketPtr> PacketContainer;
+    /// An iterator to the element in the internal container.
+    typedef typename PacketContainer::iterator PacketContainerIterator;
+
+public:
+
+    /// \brief Constructor.
+    PacketStorage() { }
+
+    /// \brief Appends the new packet object to the collection.
+    ///
+    /// \param packet A pointer to an object representing a packet.
+    void append(const PacketPtr& packet) {
+        storage_.push_back(packet);
+        if (storage_.size() == 1) {
+            current_pointer_ = storage_.begin();
+        }
+    }
+
+    /// \brief Removes packets from the storage.
+    ///
+    /// It is possible to specify a number of packets to be removed
+    /// from a storage. Packets are removed from the beginning of the
+    /// storage. If specified number is greater than the size of the
+    /// storage, all packets are removed.
+    ///
+    /// @param num A number of packets to be removed. If omitted,
+    /// all packets will be removed.
+    void clear(const uint64_t num = 0) {
+        if (num != 0) {
+            PacketContainerIterator last = storage_.begin();
+            std::advance(last, num > size() ? size() : num);
+            current_pointer_ = storage_.erase(storage_.begin(), last);
+        } else {
+            storage_.clear();
+            current_pointer_ = storage_.begin();
+        }
+    }
+
+    /// \brief Checks if the storage has no packets.
+    ///
+    /// \return true if storage is empty, false otherwise.
+    bool empty() const {
+        return (storage_.empty());
+    }
+
+    /// \brief Returns next packet from the storage.
+    ///
+    /// This function returns packets sequentially (in the same order
+    /// in which they have been appended). The returned packet is
+    /// instantly removed from the storage.
+    ///
+    /// \return next packet from the storage.
+    PacketPtr getNext() {
+        if (storage_.empty()) {
+            return (PacketPtr());
+        } else if (current_pointer_ == storage_.end()) {
+            current_pointer_ = storage_.begin();
+        }
+        PacketPtr packet = *current_pointer_;
+        current_pointer_ = storage_.erase(current_pointer_);
+        return (packet);
+    }
+
+    /// \brief Returns random packet from the storage.
+    ///
+    /// This function picks random packet from the storage and returns
+    /// it. It is way slower than the @c getNext function because it has to
+    /// iterate over all existing entries from the beginning of the storage
+    /// to the random packet's position. Therefore, care should be taken
+    /// when using this function to access elements when storage is large.
+    ///
+    /// \return random packet from the storage.
+    PacketPtr getRandom() {
+        if (empty()) {
+            return (PacketPtr());
+        }
+        current_pointer_ = storage_.begin();
+        if (size() > 1) {
+            std::advance(current_pointer_, rand() % (size() - 1));
+        }
+        PacketPtr packet = *current_pointer_;
+        current_pointer_ = storage_.erase(current_pointer_);
+        return (packet);
+    }
+
+    /// \brief Returns number of packets in the storage.
+    ///
+    /// \return number of packets in the storage.
+    uint64_t size() const {
+        return (storage_.size());
+    }
+
+private:
+
+    std::list<PacketPtr> storage_;            ///< Holds all appended packets.
+    PacketContainerIterator current_pointer_; ///< Holds the iterator to the
+                                              ///< next element returned.
+
+};
+
+} // namespace perfdhcp
+} // namespace isc
+
+#endif // PACKET_STORAGE_H
diff --git a/tests/tools/perfdhcp/perf_pkt6.cc b/tests/tools/perfdhcp/perf_pkt6.cc
index 56fe9df..0ede077 100644
--- a/tests/tools/perfdhcp/perf_pkt6.cc
+++ b/tests/tools/perfdhcp/perf_pkt6.cc
@@ -43,7 +43,7 @@ PerfPkt6::rawPack() {
                                options_,
                                getTransidOffset(),
                                getTransid(),
-                               bufferOut_));
+                               buffer_out_));
 }
 
 bool
diff --git a/tests/tools/perfdhcp/pkt_transform.cc b/tests/tools/perfdhcp/pkt_transform.cc
index 15f15f1..0267d33 100644
--- a/tests/tools/perfdhcp/pkt_transform.cc
+++ b/tests/tools/perfdhcp/pkt_transform.cc
@@ -32,7 +32,7 @@ namespace perfdhcp {
 bool
 PktTransform::pack(const Option::Universe universe,
                    const OptionBuffer& in_buffer,
-                   const Option::OptionCollection& options,
+                   const OptionCollection& options,
                    const size_t transid_offset,
                    const uint32_t transid,
                    util::OutputBuffer& out_buffer) {
@@ -75,7 +75,7 @@ PktTransform::pack(const Option::Universe universe,
 bool
 PktTransform::unpack(const Option::Universe universe,
                      const OptionBuffer& in_buffer,
-                     const Option::OptionCollection& options,
+                     const OptionCollection& options,
                      const size_t transid_offset,
                      uint32_t& transid) {
 
@@ -113,13 +113,13 @@ PktTransform::unpack(const Option::Universe universe,
 
 void
 PktTransform::packOptions(const OptionBuffer& in_buffer,
-                          const Option::OptionCollection& options,
+                          const OptionCollection& options,
                           util::OutputBuffer& out_buffer) {
     try {
         // If there are any options on the list, we will use provided
         // options offsets to override them in the output buffer
         // with new contents.
-        for (Option::OptionCollection::const_iterator it = options.begin();
+        for (OptionCollection::const_iterator it = options.begin();
              it != options.end(); ++it) {
             // Get options with their position (offset).
             boost::shared_ptr<LocalizedOption> option =
@@ -157,8 +157,8 @@ PktTransform::packOptions(const OptionBuffer& in_buffer,
 
 void
 PktTransform::unpackOptions(const OptionBuffer& in_buffer,
-                            const Option::OptionCollection& options) {
-    for (Option::OptionCollection::const_iterator it = options.begin();
+                            const OptionCollection& options) {
+    for (OptionCollection::const_iterator it = options.begin();
          it != options.end(); ++it) {
 
         boost::shared_ptr<LocalizedOption> option =
diff --git a/tests/tools/perfdhcp/pkt_transform.h b/tests/tools/perfdhcp/pkt_transform.h
index 51c1c0b..c8b7602 100644
--- a/tests/tools/perfdhcp/pkt_transform.h
+++ b/tests/tools/perfdhcp/pkt_transform.h
@@ -66,7 +66,7 @@ public:
     /// \return false, if pack operation failed.
     static bool pack(const dhcp::Option::Universe universe,
                      const dhcp::OptionBuffer& in_buffer,
-                     const dhcp::Option::OptionCollection& options,
+                     const dhcp::OptionCollection& options,
                      const size_t transid_offset,
                      const uint32_t transid,
                      util::OutputBuffer& out_buffer);
@@ -88,7 +88,7 @@ public:
     /// \return false, if unpack operation failed.
     static bool unpack(const dhcp::Option::Universe universe,
                        const dhcp::OptionBuffer& in_buffer,
-                       const dhcp::Option::OptionCollection& options,
+                       const dhcp::OptionCollection& options,
                        const size_t transid_offset,
                        uint32_t& transid);
 
@@ -135,7 +135,7 @@ private:
     ///
     /// \throw isc::Unexpected if options update failed.
     static void packOptions(const dhcp::OptionBuffer& in_buffer,
-                            const dhcp::Option::OptionCollection& options,
+                            const dhcp::OptionCollection& options,
                             util::OutputBuffer& out_buffer);
 
     /// \brief Reads contents of specified options from buffer.
@@ -159,7 +159,7 @@ private:
     ///
     /// \throw isc::Unexpected if options unpack failed.
     static void unpackOptions(const dhcp::OptionBuffer& in_buffer,
-                              const dhcp::Option::OptionCollection& options);
+                              const dhcp::OptionCollection& options);
 
 };
 
diff --git a/tests/tools/perfdhcp/stats_mgr.h b/tests/tools/perfdhcp/stats_mgr.h
index a8b5d98..d0af943 100644
--- a/tests/tools/perfdhcp/stats_mgr.h
+++ b/tests/tools/perfdhcp/stats_mgr.h
@@ -15,8 +15,9 @@
 #ifndef STATS_MGR_H
 #define STATS_MGR_H
 
-#include <iostream>
-#include <map>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
+#include <exceptions/exceptions.h>
 
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
@@ -27,7 +28,9 @@
 #include <boost/multi_index/mem_fun.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
 
-#include <exceptions/exceptions.h>
+#include <iostream>
+#include <map>
+
 
 namespace isc {
 namespace perfdhcp {
@@ -120,7 +123,8 @@ public:
         XCHG_DO,  ///< DHCPv4 DISCOVER-OFFER
         XCHG_RA,  ///< DHCPv4 REQUEST-ACK
         XCHG_SA,  ///< DHCPv6 SOLICIT-ADVERTISE
-        XCHG_RR   ///< DHCPv6 REQUEST-REPLY
+        XCHG_RR,  ///< DHCPv6 REQUEST-REPLY
+        XCHG_RN   ///< DHCPv6 RENEW-REPLY
     };
 
     /// \brief Exchange Statistics.
@@ -260,6 +264,7 @@ public:
         /// assumed dropped. Negative value disables it.
         /// \param archive_enabled if true packets archive mode is enabled.
         /// In this mode all packets are stored throughout the test execution.
+        /// \param boot_time Holds the timestamp when perfdhcp has been started.
         ExchangeStats(const ExchangeType xchg_type,
                       const double drop_time,
                       const bool archive_enabled,
@@ -1164,6 +1169,8 @@ public:
             return("SOLICIT-ADVERTISE");
         case XCHG_RR:
             return("REQUEST-REPLY");
+        case XCHG_RN:
+            return("RENEW-REPLY");
         default:
             return("Unknown exchange type");
         }
@@ -1183,7 +1190,7 @@ public:
     /// \throw isc::InvalidOperation if no exchange type added to
     /// track statistics.
      void printStats() const {
-        if (exchanges_.size() == 0) {
+        if (exchanges_.empty()) {
             isc_throw(isc::InvalidOperation,
                       "no exchange type added for tracking");
         }
@@ -1238,7 +1245,7 @@ public:
     /// \throw isc::InvalidOperation if no exchange type added to
     /// track statistics or packets archive mode is disabled.
     void printTimestamps() const {
-        if (exchanges_.size() == 0) {
+        if (exchanges_.empty()) {
             isc_throw(isc::InvalidOperation,
                       "no exchange type added for tracking");
         }
@@ -1261,7 +1268,7 @@ public:
     ///
     /// \throw isc::InvalidOperation if no custom counters added for tracking.
     void printCustomCounters() const {
-        if (custom_counters_.size() == 0) {
+        if (custom_counters_.empty()) {
             isc_throw(isc::InvalidOperation, "no custom counters specified");
         }
         for (CustomCountersMapIterator it = custom_counters_.begin();
diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc
index 925e9b6..d94f032 100644
--- a/tests/tools/perfdhcp/test_control.cc
+++ b/tests/tools/perfdhcp/test_control.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -97,6 +97,68 @@ TestControl::TestControl() {
     reset();
 }
 
+void
+TestControl::cleanCachedPackets() {
+    CommandOptions& options = CommandOptions::instance();
+    // When Renews are not sent, Reply packets are not cached so there
+    // is nothing to do.
+    if (options.getRenewRate() == 0) {
+        return;
+    }
+
+    static boost::posix_time::ptime last_clean =
+        microsec_clock::universal_time();
+
+    // Check how much time has passed since last cleanup.
+    time_period time_since_clean(last_clean,
+                                 microsec_clock::universal_time());
+    // Cleanup every 1 second.
+    if (time_since_clean.length().total_seconds() >= 1) {
+        // Calculate how many cached packets to remove. Actually we could
+        // just leave enough packets to handle Renews for 1 second but
+        // since we want to randomize leases to be renewed so leave 5
+        // times more packets to randomize from.
+        // @todo The cache size might be controlled from the command line.
+        if (reply_storage_.size() > 5 * options.getRenewRate()) {
+            reply_storage_.clear(reply_storage_.size() -
+                                 5 * options.getRenewRate());
+        }
+        // Remember when we performed a cleanup for the last time.
+        // We want to do the next cleanup not earlier than in one second.
+        last_clean = microsec_clock::universal_time();
+    }
+}
+
+void
+TestControl::copyIaOptions(const Pkt6Ptr& pkt_from, Pkt6Ptr& pkt_to) {
+    if (!pkt_from || !pkt_to) {
+        isc_throw(BadValue, "NULL pointers must not be specified as arguments"
+                  " for the copyIaOptions function");
+    }
+    // IA_NA
+    if (CommandOptions::instance().getLeaseType()
+        .includes(CommandOptions::LeaseType::ADDRESS)) {
+        OptionPtr option = pkt_from->getOption(D6O_IA_NA);
+        if (!option) {
+            isc_throw(OptionNotFound, "IA_NA option not found in the"
+                      " server's response");
+        }
+        pkt_to->addOption(option);
+    }
+    // IA_PD
+    if (CommandOptions::instance().getLeaseType()
+        .includes(CommandOptions::LeaseType::PREFIX)) {
+        OptionPtr option = pkt_from->getOption(D6O_IA_PD);
+        if (!option) {
+            isc_throw(OptionNotFound, "IA_PD option not found in the"
+                      " server's response");
+        }
+        pkt_to->addOption(option);
+    }
+
+
+}
+
 std::string
 TestControl::byte2Hex(const uint8_t b) const {
     const int b1 = b / 16;
@@ -253,6 +315,31 @@ TestControl::checkExitConditions() const {
     return (false);
 }
 
+Pkt6Ptr
+TestControl::createRenew(const Pkt6Ptr& reply) {
+    if (!reply) {
+        isc_throw(isc::BadValue,"Unable to create Renew packet from the Reply packet"
+                  " because the instance of the Reply is NULL");
+    }
+    Pkt6Ptr renew(new Pkt6(DHCPV6_RENEW, generateTransid()));
+    // Client id.
+    OptionPtr opt_clientid = reply->getOption(D6O_CLIENTID);
+    if (!opt_clientid) {
+        isc_throw(isc::Unexpected, "failed to create Renew packet because client id"
+                  " option has not been found in the Reply from the server");
+    }
+    renew->addOption(opt_clientid);
+    // Server id.
+    OptionPtr opt_serverid = reply->getOption(D6O_SERVERID);
+    if (!opt_serverid) {
+        isc_throw(isc::Unexpected, "failed to create Renew packet because server id"
+                  " option has not been found in the Reply from the server");
+    }
+    renew->addOption(opt_serverid);
+    copyIaOptions(reply, renew);
+    return (renew);
+}
+
 OptionPtr
 TestControl::factoryElapsedTime6(Option::Universe, uint16_t,
                                  const OptionBuffer& buf) {
@@ -290,6 +377,22 @@ TestControl::factoryIana6(Option::Universe, uint16_t,
 }
 
 OptionPtr
+TestControl::factoryIapd6(Option::Universe, uint16_t,
+                          const OptionBuffer& buf) {
+    // @todo allow different values of T1, T2 and IAID.
+    static const uint8_t buf_array[] = {
+        0, 0, 0, 1,                     // IAID = 1
+        0, 0, 3600 >> 8, 3600 & 0xff,   // T1 = 3600
+        0, 0, 5400 >> 8, 5400 & 0xff,   // T2 = 5400
+    };
+    OptionBuffer buf_ia_pd(buf_array, buf_array + sizeof(buf_array));
+    // Append sub-options to IA_PD.
+    buf_ia_pd.insert(buf_ia_pd.end(), buf.begin(), buf.end());
+    return (OptionPtr(new Option(Option::V6, D6O_IA_PD, buf_ia_pd)));
+}
+
+
+OptionPtr
 TestControl::factoryRapidCommit6(Option::Universe, uint16_t,
                                  const OptionBuffer&) {
     return (OptionPtr(new Option(Option::V6, D6O_RAPID_COMMIT, OptionBuffer())));
@@ -380,6 +483,24 @@ TestControl::generateDuid(uint8_t& randomized) const {
     return (duid);
 }
 
+uint32_t
+TestControl::getCurrentTimeout() const {
+    CommandOptions& options = CommandOptions::instance();
+    ptime now(microsec_clock::universal_time());
+    // Check that we haven't passed the moment to send the next set of
+    // packets.
+    if (now >= send_due_ ||
+        (options.getRenewRate() != 0 && now >= renew_due_)) {
+        return (0);
+    }
+
+    // There is a due time to send Solicit and Renew. We should adjust
+    // the timeout to the due time which occurs sooner.
+    ptime due = send_due_ > renew_due_ ? renew_due_ : send_due_;
+    time_period due_period(now, due);
+    return (due_period.length().total_microseconds());
+}
+
 int
 TestControl::getElapsedTimeOffset() const {
     int elp_offset = CommandOptions::instance().getIpVersion() == 4 ?
@@ -410,17 +531,18 @@ TestControl::getElapsedTime(const T& pkt1, const T& pkt2) {
 
 
 uint64_t
-TestControl::getNextExchangesNum() const {
+TestControl::getNextExchangesNum(const boost::posix_time::ptime& send_due,
+                                 const int rate) {
     CommandOptions& options = CommandOptions::instance();
-    // Reset number of exchanges.
-    uint64_t due_exchanges = 0;
     // Get current time.
     ptime now(microsec_clock::universal_time());
-    if (now >= send_due_) {
+    if (now >= send_due) {
+        // Reset number of exchanges.
+        uint64_t due_exchanges = 0;
         // If rate is specified from the command line we have to
         // synchornize with it.
-        if (options.getRate() != 0) {
-            time_period period(send_due_, now);
+        if (rate != 0) {
+            time_period period(send_due, now);
             time_duration duration = period.length();
             // due_factor indicates the number of seconds that
             // sending next chunk of packets will take.
@@ -429,7 +551,7 @@ TestControl::getNextExchangesNum() const {
             due_factor += duration.total_seconds();
             // Multiplying due_factor by expected rate gives the number
             // of exchanges to be initiated.
-            due_exchanges = static_cast<uint64_t>(due_factor * options.getRate());
+            due_exchanges = static_cast<uint64_t>(due_factor * rate);
             // We want to make sure that at least one packet goes out.
             if (due_exchanges == 0) {
                 due_exchanges = 1;
@@ -570,6 +692,9 @@ TestControl::initializeStatsMgr() {
             stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR,
                                           options.getDropTime()[1]);
         }
+        if (options.getRenewRate() != 0) {
+            stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RN);
+        }
     }
     if (testDiags('i')) {
         if (options.getIpVersion() == 4) {
@@ -594,15 +719,15 @@ TestControl::openSocket() const {
     uint16_t port = options.getLocalPort();
     int sock = 0;
 
-    uint8_t family = (options.getIpVersion() == 6) ? AF_INET6 : AF_INET; 
+    uint8_t family = (options.getIpVersion() == 6) ? AF_INET6 : AF_INET;
     IOAddress remoteaddr(servername);
-    
+
     // Check for mismatch between IP option and server address
     if (family != remoteaddr.getFamily()) {
-        isc_throw(InvalidParameter, 
-                  "Values for IP version: " <<  
+        isc_throw(InvalidParameter,
+                  "Values for IP version: " <<
                   static_cast<unsigned int>(options.getIpVersion()) <<
-                  " and server address: " << servername << " are mismatched."); 
+                  " and server address: " << servername << " are mismatched.");
     }
 
     if (port == 0) {
@@ -691,7 +816,7 @@ TestControl::sendPackets(const TestControlSocket& socket,
         if (options.getIpVersion() == 4) {
             // No template packets means that no -T option was specified.
             // We have to build packets ourselfs.
-            if (template_buffers_.size() == 0) {
+            if (template_buffers_.empty()) {
                 sendDiscover4(socket, preload);
             } else {
                 // @todo add defines for packet type index that can be
@@ -701,7 +826,7 @@ TestControl::sendPackets(const TestControlSocket& socket,
         } else {
             // No template packets means that no -T option was specified.
             // We have to build packets ourselfs.
-            if (template_buffers_.size() == 0) {
+            if (template_buffers_.empty()) {
                 sendSolicit6(socket, preload);
             } else {
                 // @todo add defines for packet type index that can be
@@ -723,6 +848,17 @@ TestControl::sendPackets(const TestControlSocket& socket,
     }
 }
 
+uint64_t
+TestControl::sendRenewPackets(const TestControlSocket& socket,
+                              const uint64_t packets_num) {
+    for (uint64_t i = 0; i < packets_num; ++i) {
+        if (!sendRenew(socket)) {
+            return (i);
+        }
+    }
+    return (packets_num);
+}
+
 void
 TestControl::printDiagnostics() const {
     CommandOptions& options = CommandOptions::instance();
@@ -920,7 +1056,7 @@ TestControl::readPacketTemplate(const std::string& file_name) {
     // Expect even number of digits.
     if (hex_digits.size() % 2 != 0) {
         isc_throw(OutOfRange, "odd number of digits in template file");
-    } else if (hex_digits.size() == 0) {
+    } else if (hex_digits.empty()) {
         isc_throw(OutOfRange, "template file " << file_name << " is empty");
     }
     std::vector<uint8_t> binary_stream;
@@ -978,20 +1114,27 @@ TestControl::processReceivedPacket6(const TestControlSocket& socket,
             }
         }
     } else if (packet_type == DHCPV6_REPLY) {
-        stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6);
+        Pkt6Ptr sent_packet = stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR,
+                                                          pkt6);
+        if (sent_packet) {
+            if (CommandOptions::instance().getRenewRate() != 0) {
+                reply_storage_.append(pkt6);
+            }
+        } else {
+            stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RN, pkt6);
+        }
     }
 }
 
 uint64_t
 TestControl::receivePackets(const TestControlSocket& socket) {
-    int timeout = 0;
     bool receiving = true;
     uint64_t received = 0;
     while (receiving) {
         if (CommandOptions::instance().getIpVersion() == 4) {
             Pkt4Ptr pkt4;
             try {
-                pkt4 = IfaceMgr::instance().receive4(timeout);
+                pkt4 = IfaceMgr::instance().receive4(0, getCurrentTimeout());
             } catch (const Exception& e) {
                 std::cerr << "Failed to receive DHCPv4 packet: "
                           << e.what() <<  std::endl;
@@ -1009,7 +1152,7 @@ TestControl::receivePackets(const TestControlSocket& socket) {
         } else if (CommandOptions::instance().getIpVersion() == 6) {
             Pkt6Ptr pkt6;
             try {
-                pkt6 = IfaceMgr::instance().receive6(timeout);
+                pkt6 = IfaceMgr::instance().receive6(0, getCurrentTimeout());
             } catch (const Exception& e) {
                 std::cerr << "Failed to receive DHCPv6 packet: "
                           << e.what() << std::endl;
@@ -1079,6 +1222,11 @@ TestControl::registerOptionFactories6() const {
                                        D6O_IA_NA,
                                        &TestControl::factoryIana6);
 
+        // D6O_IA_PD option factory.
+        LibDHCP::OptionFactoryRegister(Option::V6,
+                                       D6O_IA_PD,
+                                       &TestControl::factoryIapd6);
+
 
     }
     factories_registered = true;
@@ -1105,6 +1253,8 @@ TestControl::reset() {
     send_due_ = microsec_clock::universal_time();
     last_sent_ = send_due_;
     last_report_ = send_due_;
+    renew_due_ = send_due_;
+    last_renew_ = send_due_;
     transid_gen_.reset();
     // Actual generators will have to be set later on because we need to
     // get command line parameters first.
@@ -1172,10 +1322,10 @@ TestControl::run() {
     initializeStatsMgr();
     for (;;) {
         // Calculate send due based on when last exchange was initiated.
-        updateSendDue();
+        updateSendDue(last_sent_, options.getRate(), send_due_);
         // Calculate number of packets to be sent to stay
         // catch up with rate.
-        uint64_t packets_due = getNextExchangesNum();
+        uint64_t packets_due = getNextExchangesNum(send_due_, options.getRate());
         if ((packets_due == 0) && testDiags('i')) {
             if (options.getIpVersion() == 4) {
                 stats_mgr4_->incrementCounter("shortwait");
@@ -1198,11 +1348,29 @@ TestControl::run() {
         // Initiate new DHCP packet exchanges.
         sendPackets(socket, packets_due);
 
+        // If -f<renew-rate> option was specified we have to check how many
+        // Renew packets should be sent to catch up with a desired rate.
+        if ((options.getIpVersion() == 6) && (options.getRenewRate() != 0)) {
+            updateSendDue(last_renew_, options.getRenewRate(), renew_due_);
+            uint64_t renew_packets_due =
+                getNextExchangesNum(renew_due_, options.getRenewRate());
+            // Send renew packets.
+            sendRenewPackets(socket, renew_packets_due);
+        }
+
         // Report delay means that user requested printing number
         // of sent/received/dropped packets repeatedly.
         if (options.getReportDelay() > 0) {
             printIntermediateStats();
         }
+
+        // If we are sending Renews to the server, the Reply packets are cached
+        // so as leases for which we send Renews can be idenitfied. The major
+        // issue with this approach is that most of the time we are caching
+        // more packets than we actually need. This function removes excessive
+        // Reply messages to reduce the memory and CPU utilization. Note that
+        // searches in the long list of Reply packets increases CPU utilization.
+        cleanCachedPackets();
     }
     printStats();
 
@@ -1378,6 +1546,25 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
     saveFirstPacket(pkt4);
 }
 
+bool
+TestControl::sendRenew(const TestControlSocket& socket) {
+    last_renew_ = microsec_clock::universal_time();
+    Pkt6Ptr reply = reply_storage_.getRandom();
+    if (!reply) {
+        return (false);
+    }
+    Pkt6Ptr renew = createRenew(reply);
+    setDefaults6(socket, renew);
+    renew->pack();
+    IfaceMgr::instance().send(renew);
+    if (!stats_mgr6_) {
+        isc_throw(Unexpected, "Statistics Manager for DHCPv6 "
+                  "hasn't been initialized");
+    }
+    stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RN, renew);
+    return (true);
+}
+
 void
 TestControl::sendRequest4(const TestControlSocket& socket,
                           const dhcp::Pkt4Ptr& discover_pkt4,
@@ -1577,13 +1764,13 @@ TestControl::sendRequest6(const TestControlSocket& socket,
         }
         pkt6->addOption(opt_serverid);
     }
-    // Set IA_NA option.
-    OptionPtr opt_ia_na = advertise_pkt6->getOption(D6O_IA_NA);
-    if (!opt_ia_na) {
-        isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received "
-                  "packet");
-    }
-    pkt6->addOption(opt_ia_na);
+
+    // Copy IA_NA or IA_PD option from the Advertise message to the Request
+    // message being sent to the server. This will throw exception if the
+    // option to be copied is not found. Note that this function will copy
+    // one of IA_NA or IA_PD options, depending on the lease-type value
+    // specified in the command line.
+    copyIaOptions(advertise_pkt6, pkt6);
 
     // Set default packet data.
     setDefaults6(socket, pkt6);
@@ -1732,7 +1919,20 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
     }
     pkt6->addOption(Option::factory(Option::V6, D6O_CLIENTID, duid));
     pkt6->addOption(Option::factory(Option::V6, D6O_ORO));
-    pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA));
+
+    // Depending on the lease-type option specified, we should request
+    // IPv6 address (with IA_NA) or IPv6 prefix (IA_PD) or both.
+
+    // IA_NA
+    if (CommandOptions::instance().getLeaseType()
+        .includes(CommandOptions::LeaseType::ADDRESS)) {
+        pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA));
+    }
+    // IA_PD
+    if (CommandOptions::instance().getLeaseType()
+        .includes(CommandOptions::LeaseType::PREFIX)) {
+        pkt6->addOption(Option::factory(Option::V6, D6O_IA_PD));
+    }
 
     setDefaults6(socket, pkt6);
     pkt6->pack();
@@ -1852,16 +2052,17 @@ TestControl::testDiags(const char diag) const {
 }
 
 void
-TestControl::updateSendDue() {
+TestControl::updateSendDue(const boost::posix_time::ptime& last_sent,
+                           const int rate,
+                           boost::posix_time::ptime& send_due) {
     // If default constructor was called, this should not happen but
     // if somebody has changed default constructor it is better to
     // keep this check.
-    if (last_sent_.is_not_a_date_time()) {
+    if (last_sent.is_not_a_date_time()) {
         isc_throw(Unexpected, "time of last sent packet not initialized");
     }
     // Get the expected exchange rate.
     CommandOptions& options = CommandOptions::instance();
-    int rate = options.getRate();
     // If rate was not specified we will wait just one clock tick to
     // send next packet. This simulates best effort conditions.
     long duration = 1;
@@ -1873,14 +2074,14 @@ TestControl::updateSendDue() {
         duration = time_duration::ticks_per_second() / rate;
     }
     // Calculate due time to initiate next chunk of exchanges.
-    send_due_ = last_sent_ + time_duration(0, 0, 0, duration);
+    send_due = last_sent + time_duration(0, 0, 0, duration);
     // Check if it is already due.
     ptime now(microsec_clock::universal_time());
     // \todo verify if this condition is not too tight. In other words
     // verify if this will not produce too many late sends.
     // We might want to look at this once we are done implementing
     // microsecond timeouts in IfaceMgr.
-    if (now > send_due_) {
+    if (now > send_due) {
         if (testDiags('i')) {
             if (options.getIpVersion() == 4) {
                 stats_mgr4_->incrementCounter("latesend");
diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h
index 3983fa6..26adf9f 100644
--- a/tests/tools/perfdhcp/test_control.h
+++ b/tests/tools/perfdhcp/test_control.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -15,20 +15,21 @@
 #ifndef TEST_CONTROL_H
 #define TEST_CONTROL_H
 
-#include <string>
-#include <vector>
-
-#include <boost/noncopyable.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/function.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
+#include "packet_storage.h"
+#include "stats_mgr.h"
 
 #include <dhcp/iface_mgr.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt6.h>
 
-#include "stats_mgr.h"
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include <string>
+#include <vector>
 
 namespace isc {
 namespace perfdhcp {
@@ -55,6 +56,13 @@ static const size_t DHCPV6_SERVERID_OFFSET = 22;
 /// Default DHCPV6 IA_NA offset in the packet template.
 static const size_t DHCPV6_IA_NA_OFFSET = 40;
 
+/// @brief Exception thrown when the required option is not found in a packet.
+class OptionNotFound : public Exception {
+public:
+    OptionNotFound(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
 /// \brief Test Control class.
 ///
 /// This singleton class is used to run the performance test with
@@ -160,7 +168,7 @@ public:
         /// \param socket socket descriptor.
         TestControlSocket(const int socket);
 
-        /// \brief Destriuctor of the socket wrapper class.
+        /// \brief Destructor of the socket wrapper class.
         ///
         /// Destructor closes wrapped socket.
         ~TestControlSocket();
@@ -197,7 +205,7 @@ public:
     /// The default generator pointer.
     typedef boost::shared_ptr<NumberGenerator> NumberGeneratorPtr;
 
-    /// \brief Sequential numbers generatorc class.
+    /// \brief Sequential numbers generator class.
     class SequentialGenerator : public NumberGenerator {
     public:
         /// \brief Constructor.
@@ -213,7 +221,7 @@ public:
             }
         }
 
-        /// \brief Generate number sequentialy.
+        /// \brief Generate number sequentially.
         ///
         /// \return generated number.
         virtual uint32_t generate() {
@@ -241,7 +249,7 @@ public:
     /// brief\ Run performance test.
     ///
     /// Method runs whole performance test. Command line options must
-    /// be parsed prior to running this function. Othewise function will
+    /// be parsed prior to running this function. Otherwise function will
     /// throw exception.
     ///
     /// \throw isc::InvalidOperation if command line options are not parsed.
@@ -280,7 +288,7 @@ protected:
     /// only via \ref instance method.
     TestControl();
 
-    /// \brief Check if test exit condtitions fulfilled.
+    /// \brief Check if test exit conditions fulfilled.
     ///
     /// Method checks if the test exit conditions are fulfilled.
     /// Exit conditions are checked periodically from the
@@ -292,6 +300,27 @@ protected:
     /// \return true if any of the exit conditions is fulfilled.
     bool checkExitConditions() const;
 
+    /// \brief Removes cached DHCPv6 Reply packets every second.
+    ///
+    /// This function wipes cached Reply packets from the storage.
+    /// The number of packets left in the storage after the call
+    /// to this function should guarantee that the Renew packets
+    /// can be sent at the given rate. Note that the Renew packets
+    /// are generated for the existing leases, represented here as
+    /// replies from the server.
+    /// @todo Instead of cleaning packets periodically we could
+    /// just stop adding new packets when the certain threshold
+    /// has been reached.
+    void cleanCachedPackets();
+
+    /// \brief Creates IPv6 packet using options from Reply packet.
+    ///
+    /// \param reply An instance of the Reply packet which contents should
+    /// be used to create an instance of the Renew packet.
+    ///
+    /// \return created Renew packet.
+    dhcp::Pkt6Ptr createRenew(const dhcp::Pkt6Ptr& reply);
+
     /// \brief Factory function to create DHCPv6 ELAPSED_TIME option.
     ///
     /// This factory function creates DHCPv6 ELAPSED_TIME option instance.
@@ -338,6 +367,17 @@ protected:
                                         uint16_t type,
                                         const dhcp::OptionBuffer& buf);
 
+    /// \brief Factory function to create IA_PD option.
+    ///
+    /// this factory function creates DHCPv6 IA_PD option instance.
+    ///
+    /// \param u universe (ignored).
+    /// \param type option-type (ignored).
+    /// \param buf option-buffer carrying sub-options.
+    static dhcp::OptionPtr factoryIapd6(dhcp::Option::Universe u,
+                                        uint16_t type,
+                                        const dhcp::OptionBuffer& buf);
+
     /// \brief Factory function to create DHCPv6 ORO option.
     ///
     /// This factory function creates DHCPv6 Option Request Option instance.
@@ -371,7 +411,7 @@ protected:
 
     /// \brief Factory function to create DHCPv4 Request List option.
     ///
-    /// This factory function creayes DHCPv4 PARAMETER_REQUEST_LIST option
+    /// This factory function creates DHCPv4 PARAMETER_REQUEST_LIST option
     /// instance with the following set of requested options:
     /// - DHO_SUBNET_MASK,
     /// - DHO_BROADCAST_ADDRESS,
@@ -432,6 +472,15 @@ protected:
         return (transid_gen_->generate());
     }
 
+    /// \brief Returns a timeout for packet reception.
+    ///
+    /// The calculation is based on the value of the timestamp
+    /// when the next set of packets is to be sent. If no packet is
+    /// received until then, new packets are sent.
+    ///
+    /// \return A current timeout in microseconds.
+    uint32_t getCurrentTimeout() const;
+
     /// \brief Returns number of exchanges to be started.
     ///
     /// Method returns number of new exchanges to be started as soon
@@ -439,8 +488,13 @@ protected:
     /// is based on current time, due time calculated with
     /// \ref updateSendDue function and expected rate.
     ///
+    /// \param send_due Due time to initiate next chunk set exchanges.
+    /// \param rate A rate at which exchanges are initiated.
+    ///
     /// \return number of exchanges to be started immediately.
-    uint64_t getNextExchangesNum() const;
+    static uint64_t
+    getNextExchangesNum(const boost::posix_time::ptime& send_due,
+                        const int rate);
 
     /// \brief Return template buffer.
     ///
@@ -527,7 +581,7 @@ protected:
     /// \brief Process received DHCPv6 packet.
     ///
     /// Method performs processing of the received DHCPv6 packet,
-    /// updates statistics and responsds to the server if required,
+    /// updates statistics and responds to the server if required,
     /// e.g. when ADVERTISE packet arrives, this function will initiate
     /// REQUEST message to the server.
     ///
@@ -578,7 +632,7 @@ protected:
     /// \brief Register option factory functions for DHCPv4 or DHCPv6.
     ///
     /// Method registers option factory functions for DHCPv4 or DHCPv6,
-    /// depending in whch mode test is currently running.
+    /// depending in which mode test is currently running.
     void registerOptionFactories() const;
 
 
@@ -612,7 +666,7 @@ protected:
     /// type and keeps them around until test finishes. Then they
     /// are printed to the user. If packet of specified type has
     /// been already stored this function perfroms no operation.
-    /// This function does not perform sainty check if packet
+    /// This function does not perform sanity check if packet
     /// pointer is valid. Make sure it is before calling it.
     ///
     /// \param pkt packet to be stored.
@@ -684,6 +738,26 @@ protected:
                      const uint64_t packets_num,
                      const bool preload = false);
 
+    /// \brief Send number of DHCPv6 Renew packets to the server.
+    ///
+    /// \param socket An object representing socket to be used to send packets.
+    /// \param packets_num A number of Renew packets to be send.
+    ///
+    /// \return A number of packets actually sent.
+    uint64_t sendRenewPackets(const TestControlSocket& socket,
+                              const uint64_t packets_num);
+
+    /// \brief Send a renew message using provided socket.
+    ///
+    /// This method will select an existing lease from the Reply packet cache
+    /// If there is no lease that can be renewed this method will return false.
+    ///
+    /// \param socket An object encapsulating socket to be used to send
+    /// a packet.
+    ///
+    /// \return true if packet has been sent, false otherwise.
+    bool sendRenew(const TestControlSocket& socket);
+
     /// \brief Send DHCPv4 REQUEST message.
     ///
     /// Method creates and sends DHCPv4 REQUEST message to the server.
@@ -830,10 +904,35 @@ protected:
     /// Method updates due time to initiate next chunk of exchanges.
     /// Function takes current time, last sent packet's time and
     /// expected rate in its calculations.
-    void updateSendDue();
+    ///
+    /// \param last_sent A time when the last exchange was initiated.
+    /// \param rate A rate at which exchangesa re initiated
+    /// \param [out] send_due A reference to the time object to be updated
+    /// with the next due time.
+    void updateSendDue(const boost::posix_time::ptime& last_sent,
+                       const int rate,
+                       boost::posix_time::ptime& send_due);
 
 private:
 
+    /// \brief Copies IA_NA or IA_PD option from one packet to another.
+    ///
+    /// This function checks the lease-type specified in the command line
+    /// with option -e<lease-type>. If 'address-only' value has been specified
+    /// this function expects that IA_NA option is present in the packet
+    /// encapsulated by pkt_from object. If 'prefix-only' value has been
+    /// specified, this function expects that IA_PD option is present in the
+    /// packet encapsulated by pkt_to object.
+    ///
+    /// \param [in] pkt_from A packet from which options should be copied.
+    /// \param [out] pkt_to A packet to which options should be copied.
+    ///
+    /// \throw isc::perfdhcp::OptionNotFound if a required option is not
+    /// found in the packet from which options should be copied.
+    /// \throw isc::BadValue if any of the specified pointers to packets
+    /// is NULL.
+    void copyIaOptions(const dhcp::Pkt6Ptr& pkt_from, dhcp::Pkt6Ptr& pkt_to);
+
     /// \brief Convert binary value to hex string.
     ///
     /// \todo Consider moving this function to src/lib/util.
@@ -960,13 +1059,19 @@ private:
     boost::posix_time::ptime send_due_;    ///< Due time to initiate next chunk
                                            ///< of exchanges.
     boost::posix_time::ptime last_sent_;   ///< Indicates when the last exchange
-                                           /// was initiated.
+                                           ///< was initiated.
+    boost::posix_time::ptime renew_due_;   ///< Due time to send next set of
+                                           ///< Renew requests.
+    boost::posix_time::ptime last_renew_;  ///< Indicates when the last Renew
+                                           ///< was attempted.
 
     boost::posix_time::ptime last_report_; ///< Last intermediate report time.
 
     StatsMgr4Ptr stats_mgr4_;  ///< Statistics Manager 4.
     StatsMgr6Ptr stats_mgr6_;  ///< Statistics Manager 6.
 
+    PacketStorage<dhcp::Pkt6> reply_storage_; ///< A storage for reply messages.
+
     NumberGeneratorPtr transid_gen_; ///< Transaction id generator.
     NumberGeneratorPtr macaddr_gen_; ///< Numbers generator for MAC address.
 
diff --git a/tests/tools/perfdhcp/tests/Makefile.am b/tests/tools/perfdhcp/tests/Makefile.am
index aa4c0cf..e48757a 100644
--- a/tests/tools/perfdhcp/tests/Makefile.am
+++ b/tests/tools/perfdhcp/tests/Makefile.am
@@ -25,6 +25,7 @@ run_unittests_SOURCES += command_options_unittest.cc
 run_unittests_SOURCES += perf_pkt6_unittest.cc
 run_unittests_SOURCES += perf_pkt4_unittest.cc
 run_unittests_SOURCES += localized_option_unittest.cc
+run_unittests_SOURCES += packet_storage_unittest.cc
 run_unittests_SOURCES += stats_mgr_unittest.cc
 run_unittests_SOURCES += test_control_unittest.cc
 run_unittests_SOURCES += command_options_helper.h
diff --git a/tests/tools/perfdhcp/tests/command_options_helper.h b/tests/tools/perfdhcp/tests/command_options_helper.h
index 253fe12..afa0dd3 100644
--- a/tests/tools/perfdhcp/tests/command_options_helper.h
+++ b/tests/tools/perfdhcp/tests/command_options_helper.h
@@ -15,11 +15,15 @@
 #ifndef COMMAND_OPTIONS_HELPER_H
 #define COMMAND_OPTIONS_HELPER_H
 
+#include "../command_options.h"
+#include <exceptions/exceptions.h>
+
+#include <assert.h>
+#include <iterator>
+#include <cstring>
 #include <string>
 #include <vector>
 
-#include <exceptions/exceptions.h>
-#include "../command_options.h"
 
 namespace isc {
 namespace perfdhcp {
@@ -115,7 +119,7 @@ private:
         // Tokenize string (space is a separator) using begin and end iteratos
         std::vector<std::string> tokens(text_iterator, text_end);
 
-        if (tokens.size() > 0) {
+        if (!tokens.empty()) {
             // Allocate array of C-strings where we will store tokens
             results = new char*[tokens.size()];
             // Store tokens in C-strings array
diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc
index 7a109fb..3431b87 100644
--- a/tests/tools/perfdhcp/tests/command_options_unittest.cc
+++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -28,6 +28,112 @@ using namespace isc;
 using namespace isc::perfdhcp;
 using namespace boost::posix_time;
 
+// Verify that default constructor sets lease type to the expected value.
+TEST(LeaseTypeTest, defaultConstructor) {
+    CommandOptions::LeaseType lease_type;
+    EXPECT_TRUE(lease_type.is(CommandOptions::LeaseType::ADDRESS));
+}
+
+// Verify that the constructor sets the lease type to the specified value.
+TEST(LeaseTypeTest, constructor) {
+    CommandOptions::LeaseType
+        lease_type1(CommandOptions::LeaseType::ADDRESS);
+    EXPECT_TRUE(lease_type1.is(CommandOptions::LeaseType::ADDRESS));
+
+    CommandOptions::LeaseType
+        lease_type2(CommandOptions::LeaseType::PREFIX);
+    EXPECT_TRUE(lease_type2.is(CommandOptions::LeaseType::PREFIX));
+}
+
+// Verify that the lease type can be modified using set() function.
+TEST(LeaseTypeTest, set) {
+    CommandOptions::LeaseType
+        lease_type(CommandOptions::LeaseType::ADDRESS);
+    EXPECT_TRUE(lease_type.is(CommandOptions::LeaseType::ADDRESS));
+
+    lease_type.set(CommandOptions::LeaseType::PREFIX);
+    EXPECT_TRUE(lease_type.is(CommandOptions::LeaseType::PREFIX));
+}
+
+// Verify that the includes() function returns true when the lease type
+// specified with the function argument is the same as the lease type
+// encapsulated by the LeaseType object on which include function is called
+// or when the lease type value encapsulated by this object is
+// ADDRESS_AND_PREFIX.
+TEST(LeaseTypeTest, includes) {
+    // Lease type: ADDRESS
+    CommandOptions::LeaseType lease_type(CommandOptions::LeaseType::ADDRESS);
+    // Lease type IS ADDRESS.
+    ASSERT_TRUE(lease_type.is(CommandOptions::LeaseType::ADDRESS));
+    // Lease type includes the ADDRESS.
+    EXPECT_TRUE(lease_type.includes(CommandOptions::LeaseType::ADDRESS));
+    // Lease type does not include PREFIX.
+    EXPECT_FALSE(lease_type.includes(CommandOptions::LeaseType::PREFIX));
+    // Lease type does not include ADDRESS_AND_PREFIX.
+    EXPECT_FALSE(
+        lease_type.includes(CommandOptions::LeaseType::ADDRESS_AND_PREFIX)
+    );
+
+    // Do the same check for PREFIX.
+    lease_type.set(CommandOptions::LeaseType::PREFIX);
+    EXPECT_FALSE(lease_type.includes(CommandOptions::LeaseType::ADDRESS));
+    EXPECT_TRUE(lease_type.includes(CommandOptions::LeaseType::PREFIX));
+    EXPECT_FALSE(
+        lease_type.includes(CommandOptions::LeaseType::ADDRESS_AND_PREFIX)
+    );
+
+    // When lease type is set to 'address-and-prefix' it means that client
+    // requests both address and prefix (IA_NA and IA_PD). Therefore, the
+    // LeaseType::includes() function should return true for both ADDRESS
+    // and PREFIX.
+    lease_type.set(CommandOptions::LeaseType::ADDRESS_AND_PREFIX);
+    EXPECT_TRUE(lease_type.includes(CommandOptions::LeaseType::ADDRESS));
+    EXPECT_TRUE(lease_type.includes(CommandOptions::LeaseType::PREFIX));
+    EXPECT_TRUE(
+        lease_type.includes(CommandOptions::LeaseType::ADDRESS_AND_PREFIX)
+    );
+
+}
+
+// Verify that the LeaseType::fromCommandLine() function parses the lease-type
+// argument specified as -e<lease-type>.
+TEST(LeaseTypeTest, fromCommandLine) {
+    CommandOptions::LeaseType
+        lease_type(CommandOptions::LeaseType::ADDRESS);
+    ASSERT_TRUE(lease_type.is(CommandOptions::LeaseType::ADDRESS));
+
+    lease_type.fromCommandLine("prefix-only");
+    ASSERT_TRUE(lease_type.is(CommandOptions::LeaseType::PREFIX));
+
+    lease_type.fromCommandLine("address-only");
+    EXPECT_TRUE(lease_type.is(CommandOptions::LeaseType::ADDRESS));
+
+    lease_type.fromCommandLine("address-and-prefix");
+    EXPECT_TRUE(lease_type.is(CommandOptions::LeaseType::ADDRESS_AND_PREFIX));
+
+    EXPECT_THROW(lease_type.fromCommandLine("bogus-parameter"),
+                 isc::InvalidParameter);
+
+}
+
+// Verify that the LeaseType::toText() function returns the textual
+// representation of the lease type specified.
+TEST(LeaseTypeTest, toText) {
+    CommandOptions::LeaseType lease_type;
+    ASSERT_TRUE(lease_type.is(CommandOptions::LeaseType::ADDRESS));
+    EXPECT_EQ("address-only (IA_NA option added to the client's request)",
+              lease_type.toText());
+
+    lease_type.set(CommandOptions::LeaseType::PREFIX);
+    EXPECT_EQ("prefix-only (IA_PD option added to the client's request)",
+              lease_type.toText());
+
+    lease_type.set(CommandOptions::LeaseType::ADDRESS_AND_PREFIX);
+    EXPECT_EQ("address-and-prefix (Both IA_NA and IA_PD options added to the"
+              " client's request)", lease_type.toText());
+
+}
+
 /// \brief Test Fixture Class
 ///
 /// This test fixture class is used to perform
@@ -60,6 +166,7 @@ protected:
         EXPECT_NO_THROW(process("perfdhcp 192.168.0.1"));
         EXPECT_EQ(4, opt.getIpVersion());
         EXPECT_EQ(CommandOptions::DORA_SARR, opt.getExchangeMode());
+        EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::ADDRESS));
         EXPECT_EQ(0, opt.getRate());
         EXPECT_EQ(0, opt.getReportDelay());
         EXPECT_EQ(0, opt.getClientsNum());
@@ -181,6 +288,32 @@ TEST_F(CommandOptionsTest, IpVersion) {
     EXPECT_THROW(process("perfdhcp -c -l ethx all"), isc::InvalidParameter);
 }
 
+TEST_F(CommandOptionsTest, LeaseType) {
+    CommandOptions& opt = CommandOptions::instance();
+    // Check that the -e address-only works for IPv6.
+    ASSERT_NO_THROW(process("perfdhcp -6 -l etx -e address-only all"));
+    EXPECT_EQ(6, opt.getIpVersion());
+    EXPECT_EQ("etx", opt.getLocalName());
+    EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::ADDRESS));
+    // Check that the -e address-only works for IPv4.
+    ASSERT_NO_THROW(process("perfdhcp -4 -l etx -e address-only all"));
+    EXPECT_EQ(4, opt.getIpVersion());
+    EXPECT_EQ("etx", opt.getLocalName());
+    EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::ADDRESS));
+    // Check that the -e prefix-only works.
+    ASSERT_NO_THROW(process("perfdhcp -6 -l etx -e prefix-only all"));
+    EXPECT_EQ(6, opt.getIpVersion());
+    EXPECT_EQ("etx", opt.getLocalName());
+    EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::PREFIX));
+    // Check that -e prefix-only must not coexist with -4 option.
+    EXPECT_THROW(process("perfdhcp -4 -l ethx -e prefix-only all"),
+                 InvalidParameter);
+    // Check that -e prefix-only must not coexist with -T options.
+    EXPECT_THROW(process("perfdhcp -6 -l ethx -e prefix-only -T file1.hex"
+                         " -T file2.hex -E 4 all"), InvalidParameter);
+
+}
+
 TEST_F(CommandOptionsTest, Rate) {
     CommandOptions& opt = CommandOptions::instance();
     EXPECT_NO_THROW(process("perfdhcp -4 -r 10 -l ethx all"));
@@ -201,6 +334,37 @@ TEST_F(CommandOptionsTest, Rate) {
                  isc::InvalidParameter);
 }
 
+TEST_F(CommandOptionsTest, RenewRate) {
+    CommandOptions& opt = CommandOptions::instance();
+    // If -f is specified together with -r the command line should
+    // be accepted and the renew rate should be set.
+    EXPECT_NO_THROW(process("perfdhcp -6 -r 10 -f 10 -l ethx all"));
+    EXPECT_EQ(10, opt.getRenewRate());
+    // Check that the release rate can be set to different value than
+    // rate specified as -r<rate>. Also, swap -f na d-r to make sure
+    // that order doesn't matter.
+    EXPECT_NO_THROW(process("perfdhcp -6 -f 5 -r 10 -l ethx all"));
+    EXPECT_EQ(5, opt.getRenewRate());
+    // The renew-rate of 0 is invalid.
+    EXPECT_THROW(process("perfdhcp -6 -r 10 -f 0 - l ethx all"),
+                 isc::InvalidParameter);
+    // If -r<rate> is not specified the -f<renew-rate> should not
+    // be accepted.
+    EXPECT_THROW(process("perfdhcp -6 -f 10 -l ethx all"),
+                 isc::InvalidParameter);
+    // Currently the -f<renew-rate> can be specified for IPv6 mode
+    // only.
+    EXPECT_THROW(process("perfdhcp -4 -r 10 -f 10 -l ethx all"),
+                 isc::InvalidParameter);
+    // Renew rate should be specified.
+    EXPECT_THROW(process("perfdhcp -6 -r 10 -f -l ethx all"),
+                 isc::InvalidParameter);
+
+    // -f and -i are mutually exclusive
+    EXPECT_THROW(process("perfdhcp -6 -r 10 -f 10 -l ethx -i all"),
+                 isc::InvalidParameter);
+}
+
 TEST_F(CommandOptionsTest, ReportDelay) {
     CommandOptions& opt = CommandOptions::instance();
     EXPECT_NO_THROW(process("perfdhcp -r 100 -t 17 -l ethx all"));
diff --git a/tests/tools/perfdhcp/tests/packet_storage_unittest.cc b/tests/tools/perfdhcp/tests/packet_storage_unittest.cc
new file mode 100644
index 0000000..b6e415b
--- /dev/null
+++ b/tests/tools/perfdhcp/tests/packet_storage_unittest.cc
@@ -0,0 +1,205 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "../packet_storage.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+
+#include <gtest/gtest.h>
+
+namespace {
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace perfdhcp;
+
+/// @todo Implement the tests which use Pkt4 objects once the support for
+/// DHCPv4 renewals / releases is added.
+
+/// The number of packets in the test storage.
+const unsigned int STORAGE_SIZE = 20;
+
+/// Test fixture class for PacketStorage container testing.
+class PacketStorageTest : public ::testing::Test {
+public:
+    /// \brief Constructor, initializes the storage for each test.
+    PacketStorageTest() {
+        for (uint32_t i = 0; i < STORAGE_SIZE; ++i) {
+            storage_.append(createPacket6(DHCPV6_REPLY, i));
+        }
+    }
+
+    /// \brief Creates an instance of the Pkt6.
+    ///
+    /// \param packet_type A type of the packet.
+    /// \param transid Transaction id.
+    /// \return An instance of the Pkt6.
+    Pkt6Ptr createPacket6(const uint16_t packet_type,
+                          const uint32_t transid) {
+        return (Pkt6Ptr(new Pkt6(packet_type, transid)));
+    }
+
+    /// Packet storage under test.
+    PacketStorage<Pkt6> storage_;
+
+};
+
+// This test verifies that the packets in the storage can be accessed
+// sequentially and when a packet is returned, it is removed from the
+// storage. It also verifies the correctness of the behaviour of the
+// empty() and size() functions.
+TEST_F(PacketStorageTest, getNext) {
+    ASSERT_EQ(STORAGE_SIZE, storage_.size());
+    for (int i = 0; i < STORAGE_SIZE; ++i) {
+        Pkt6Ptr packet = storage_.getNext();
+        ASSERT_TRUE(packet) << "NULL packet returned by storage_.getNext() for"
+                            << " iteration number " << i;
+        EXPECT_EQ(i, packet->getTransid());
+        EXPECT_EQ(STORAGE_SIZE - i - 1, storage_.size());
+    }
+    EXPECT_TRUE(storage_.empty());
+    // When storage is empty, the attempt to get the next packet should
+    // result in returning NULL pointer.
+    EXPECT_FALSE(storage_.getNext());
+    // Let's try it again to see if the previous call to getNext didn't
+    // put the storage into the state in which the subsequent calls to
+    // getNext would result in incorrect behaviour.
+    EXPECT_FALSE(storage_.getNext());
+
+    // Let's add a new packet to the empty storage to check that storage
+    // "recovers" from being empty, i.e. that the internal indicator
+    // which points to current packet reinitializes correctly.
+    storage_.append(createPacket6(DHCPV6_REPLY, 100));
+    ASSERT_EQ(1, storage_.size());
+    Pkt6Ptr packet = storage_.getNext();
+    EXPECT_EQ(100, packet->getTransid());
+    EXPECT_FALSE(storage_.getNext());
+}
+
+// This test verifies that the packets in the storage can be accessed
+// randomly and when a packet is returned, it is removed from the
+// storage. It also verifies the correctness of the behaviour of the
+// empty() and size() functions.
+TEST_F(PacketStorageTest, getRandom) {
+    ASSERT_EQ(STORAGE_SIZE, storage_.size());
+    int cnt_equals = 0;
+    for (int i = 0; i < STORAGE_SIZE; ++i) {
+        Pkt6Ptr packet = storage_.getRandom();
+        ASSERT_TRUE(packet) << "NULL packet returned by storage_.getRandom()"
+            " for iteration number " << i;
+        EXPECT_EQ(STORAGE_SIZE - i - 1, storage_.size());
+        cnt_equals += (i == packet->getTransid() ? 1 : 0);
+    }
+    // If the number of times id is equal to i, is the same as the number
+    // of elements then they were NOT accessed randomly.
+    // The odds of 20 elements being randomly  accessed sequential order
+    // is nil isn't it?
+    EXPECT_NE(cnt_equals, STORAGE_SIZE);
+
+    EXPECT_TRUE(storage_.empty());
+    // When storage is empty, the attempt to get the random packet should
+    // result in returning NULL pointer.
+    EXPECT_FALSE(storage_.getRandom());
+    // Let's try it again to see if the previous call to getRandom didn't
+    // put the storage into the state in which the subsequent calls to
+    // getRandom would result in incorrect behaviour.
+    EXPECT_FALSE(storage_.getRandom());
+
+    // Let's add a new packet to the empty storage to check that storage
+    // "recovers" from being empty, i.e. that the internal indicator
+    // which points to the current packet reinitializes correctly.
+    storage_.append(createPacket6(DHCPV6_REPLY, 100));
+    ASSERT_EQ(1, storage_.size());
+    Pkt6Ptr packet = storage_.getRandom();
+    ASSERT_TRUE(packet);
+    EXPECT_EQ(100, packet->getTransid());
+    EXPECT_FALSE(storage_.getRandom());
+}
+
+// This test verifies that the packets in the storage can be accessed
+// either randomly or sequentially in the same time. It verifies that
+// each returned packet is removed from the storage.
+TEST_F(PacketStorageTest, getNextAndRandom) {
+    ASSERT_EQ(STORAGE_SIZE, storage_.size());
+    for (int i = 0; i < STORAGE_SIZE / 2; ++i) {
+        Pkt6Ptr packet_random = storage_.getRandom();
+        ASSERT_TRUE(packet_random) << "NULL packet returned by"
+            " storage_.getRandom() for iteration number " << i;
+        EXPECT_EQ(STORAGE_SIZE - 2 *i - 1, storage_.size());
+        Pkt6Ptr packet_seq = storage_.getNext();
+        ASSERT_TRUE(packet_seq) << "NULL packet returned by"
+            " storage_.getNext()  for iteration number " << i;
+        EXPECT_EQ(STORAGE_SIZE - 2 * i - 2, storage_.size());
+    }
+    EXPECT_TRUE(storage_.empty());
+    EXPECT_FALSE(storage_.getRandom());
+    EXPECT_FALSE(storage_.getNext());
+
+    // Append two packets to the storage to check if it can "recover"
+    // from being empty and that new elements can be accessed.
+    storage_.append(createPacket6(DHCPV6_REPLY, 100));
+    storage_.append(createPacket6(DHCPV6_REPLY, 101));
+    ASSERT_EQ(2, storage_.size());
+    // The newly added elements haven't been accessed yet. So, if we
+    // call getNext the first one should be returned.
+    Pkt6Ptr packet_next = storage_.getNext();
+    ASSERT_TRUE(packet_next);
+    // The first packet has transaction id equal to 100.
+    EXPECT_EQ(100, packet_next->getTransid());
+    // There should be just one packet left in the storage.
+    ASSERT_EQ(1, storage_.size());
+    // The call to getRandom should return the sole packet from the
+    // storage.
+    Pkt6Ptr packet_random = storage_.getRandom();
+    ASSERT_TRUE(packet_random);
+    EXPECT_EQ(101, packet_random->getTransid());
+    // Any further calls to getRandom and getNext should return NULL.
+    EXPECT_FALSE(storage_.getRandom());
+    EXPECT_FALSE(storage_.getNext());
+}
+
+// This test verifies that all packets are removed from the storage when
+// clear() function is invoked.
+TEST_F(PacketStorageTest, clearAll) {
+    ASSERT_EQ(STORAGE_SIZE, storage_.size());
+    ASSERT_NO_THROW(storage_.clear());
+    EXPECT_TRUE(storage_.empty());
+}
+
+// This test verifies that a set of packets can be removed from the
+// storage when a number of packets to be removed is specified. If
+// number of packets to be removed exceeds the storage size, all
+// packets should be removed.
+TEST_F(PacketStorageTest, clear) {
+    // Initially storage should have 20 elements.
+    ASSERT_EQ(STORAGE_SIZE, storage_.size());
+    // Remove 10 of them.
+    ASSERT_NO_THROW(storage_.clear(10));
+    // We should have 10 remaining.
+    ASSERT_EQ(10, storage_.size());
+
+    // Check that the retrieval still works after partial clear.
+    EXPECT_TRUE(storage_.getNext());
+    EXPECT_TRUE(storage_.getRandom());
+    // We should have 10 - 2 = 8 packets in the storage after retrieval.
+    ASSERT_EQ(8, storage_.size());
+
+    // Try to remove more elements that actually is. It
+    // should result in removal of all elements.
+    ASSERT_NO_THROW(storage_.clear(15));
+    EXPECT_TRUE(storage_.empty());
+}
+
+
+} // anonymous namespace
diff --git a/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc b/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
index ebb4f34..41aac82 100644
--- a/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
+++ b/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
@@ -81,6 +81,10 @@ public:
     ///
     /// Method simulates sending or receiving  multiple DHCPv6 packets.
     ///
+    /// \note The xchg_type parameter is passed as non-const value to avoid
+    /// false cppcheck errors which expect enum value being passed by reference.
+    /// This error is not reported when non-const enum is passed by value.
+    ///
     /// \param stats_mgr Statistics Manager instance to be used.
     /// \param xchg_type packet exchange types.
     /// \param packet_type DHCPv6 packet type.
@@ -88,7 +92,7 @@ public:
     /// \param receive simulated packets are received (if true)
     /// or sent (if false)
     void passMultiplePackets6(const boost::shared_ptr<StatsMgr6> stats_mgr,
-                              const StatsMgr6::ExchangeType xchg_type,
+                              StatsMgr6::ExchangeType xchg_type,
                               const uint8_t packet_type,
                               const int num_packets,
                               const bool receive = false) {
@@ -347,7 +351,6 @@ TEST_F(StatsMgrTest, Delays) {
 
     // Send DISCOVER, wait 2s and receive OFFER. This will affect
     // counters in Stats Manager.
-    const unsigned int delay1 = 2;
     passDOPacketsWithDelay(stats_mgr, 2, common_transid);
 
     // Initially min delay is equal to MAX_DOUBLE. After first packets
diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc
index 6aff71c..0d45c88 100644
--- a/tests/tools/perfdhcp/tests/test_control_unittest.cc
+++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc
@@ -12,21 +12,22 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include "command_options_helper.h"
+#include "../test_control.h"
+
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/iface_mgr.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
 #include <cstddef>
 #include <stdint.h>
 #include <string>
 #include <fstream>
 #include <gtest/gtest.h>
 
-#include <boost/date_time/posix_time/posix_time.hpp>
-
-#include <exceptions/exceptions.h>
-#include <asiolink/io_address.h>
-#include <dhcp/dhcp4.h>
-#include <dhcp/iface_mgr.h>
-#include "command_options_helper.h"
-#include "../test_control.h"
-
 using namespace std;
 using namespace boost::posix_time;
 using namespace isc;
@@ -40,7 +41,7 @@ using namespace isc::perfdhcp;
 class NakedTestControl: public TestControl {
 public:
 
-    /// \brief Incremental transaction id generaator.
+    /// \brief Incremental transaction id generator.
     ///
     /// This is incremental transaction id generator. It overrides
     /// the default transaction id generator that generates transaction
@@ -63,11 +64,18 @@ public:
         virtual uint32_t generate() {
             return (++transid_);
         }
+
+        /// \brief Return next transaction id value.
+        uint32_t getNext() const {
+            return (transid_ + 1);
+        }
+
     private:
         uint32_t transid_; ///< Last generated transaction id.
     };
 
     using TestControl::checkExitConditions;
+    using TestControl::createRenew;
     using TestControl::factoryElapsedTime6;
     using TestControl::factoryGeneric;
     using TestControl::factoryIana6;
@@ -85,6 +93,9 @@ public:
     using TestControl::processReceivedPacket6;
     using TestControl::registerOptionFactories;
     using TestControl::sendDiscover4;
+    using TestControl::sendPackets;
+    using TestControl::sendRenewPackets;
+    using TestControl::sendRequest6;
     using TestControl::sendSolicit6;
     using TestControl::setDefaults4;
     using TestControl::setDefaults6;
@@ -123,7 +134,7 @@ public:
     /// truncated.
     ///
     /// \param filename template file to be created.
-    /// \param buffer with binary datato be stored in file.
+    /// \param buffer with binary data to be stored in file.
     /// \param size target size of the file.
     /// \param invalid_chars inject invalid chars to the template file.
     /// \return true if file creation successful.
@@ -281,9 +292,9 @@ public:
         return (cnt);
     }
 
-    /// brief Test generation of mulitple DUIDs
+    /// \brief Test generation of mulitple DUIDs
     ///
-    /// Thie method checks the generation of multiple DUIDs. Number
+    /// This method checks the generation of multiple DUIDs. Number
     /// of iterations depends on the number of simulated clients.
     /// It is expected that DUID's size is 14 (consists of DUID-LLT
     /// HW type field, 4 octets of time value and MAC address). The
@@ -497,7 +508,7 @@ public:
 
         // Incremental transaction id generator will generate
         // predictable values of transaction id for each iteration.
-        // This is important because we need to simulate reponses
+        // This is important because we need to simulate responses
         // from the server and use the same transaction ids as in
         // packets sent by client.
         TestControl::NumberGeneratorPtr
@@ -521,7 +532,8 @@ public:
                 boost::shared_ptr<Pkt6>
                     advertise_pkt6(createAdvertisePkt6(transid));
                 // Receive ADVERTISE and send REQUEST.
-                ASSERT_NO_THROW(tc.processReceivedPacket6(sock, advertise_pkt6));
+                ASSERT_NO_THROW(tc.processReceivedPacket6(sock,
+                                                          advertise_pkt6));
                 ++transid;
             }
             if (tc.checkExitConditions()) {
@@ -623,7 +635,6 @@ public:
         CommandOptionsHelper::process(cmdline);
     }
 
-private:
     /// \brief Create DHCPv4 OFFER packet.
     ///
     /// \param transid transaction id.
@@ -644,22 +655,59 @@ private:
     ///
     /// \param transid transaction id.
     /// \return instance of the packet.
-    boost::shared_ptr<Pkt6>
-    createAdvertisePkt6(uint32_t transid) const {
-        OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
+    Pkt6Ptr
+    createAdvertisePkt6(const uint32_t transid) const {
+        boost::shared_ptr<Pkt6> advertise(new Pkt6(DHCPV6_ADVERTISE, transid));
+        // Add IA_NA if requested by the client.
+        if (CommandOptions::instance().getLeaseType()
+            .includes(CommandOptions::LeaseType::ADDRESS)) {
+            OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
+            advertise->addOption(opt_ia_na);
+        }
+        // Add IA_PD if requested by the client.
+        if (CommandOptions::instance().getLeaseType()
+            .includes(CommandOptions::LeaseType::PREFIX)) {
+            OptionPtr opt_ia_pd = Option::factory(Option::V6, D6O_IA_PD);
+            advertise->addOption(opt_ia_pd);
+        }
         OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID));
         NakedTestControl tc;
         uint8_t randomized = 0;
         std::vector<uint8_t> duid(tc.generateDuid(randomized));
         OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
-        boost::shared_ptr<Pkt6> advertise(new Pkt6(DHCPV6_ADVERTISE, transid));
-        advertise->addOption(opt_ia_na);
         advertise->addOption(opt_serverid);
         advertise->addOption(opt_clientid);
         advertise->updateTimestamp();
         return (advertise);
     }
 
+    Pkt6Ptr
+    createReplyPkt6(const uint32_t transid) const {
+        Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, transid));
+        // Add IA_NA if requested by the client.
+        if (CommandOptions::instance().getLeaseType()
+            .includes(CommandOptions::LeaseType::ADDRESS)) {
+            OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
+            reply->addOption(opt_ia_na);
+        }
+        // Add IA_PD if requested by the client.
+        if (CommandOptions::instance().getLeaseType()
+            .includes(CommandOptions::LeaseType::PREFIX)) {
+            OptionPtr opt_ia_pd = Option::factory(Option::V6, D6O_IA_PD);
+            reply->addOption(opt_ia_pd);
+        }
+        OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID));
+        NakedTestControl tc;
+        uint8_t randomized = 0;
+        std::vector<uint8_t> duid(tc.generateDuid(randomized));
+        OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
+        reply->addOption(opt_serverid);
+        reply->addOption(opt_clientid);
+        reply->updateTimestamp();
+        return (reply);
+
+    }
+
 };
 
 TEST_F(TestControlTest, GenerateDuid) {
@@ -737,7 +785,7 @@ TEST_F(TestControlTest, Options4) {
 
     // Get the option buffer. It should hold the combination of values
     // listed in requested_options array. However their order can be
-    // different in general so we need to search each value separatelly.
+    // different in general so we need to search each value separately.
     const OptionBuffer& requested_options_buf =
         opt_requested_options->getData();
     EXPECT_EQ(requested_options_ref.size(), requested_options_buf.size());
@@ -963,7 +1011,7 @@ TEST_F(TestControlTest, Packet4Exchange) {
     EXPECT_EQ(12, iterations_performed);
 }
 
-TEST_F(TestControlTest, Packet6Exchange) {
+TEST_F(TestControlTest, Packet6ExchangeFromTemplate) {
     // Get the local loopback interface to open socket on
     // it and test packets exchanges. We don't want to fail
     // the test if interface is not available.
@@ -998,11 +1046,128 @@ TEST_F(TestControlTest, Packet6Exchange) {
     // then test should be interrupted and actual number of iterations will
     // be 6.
     const int received_num = 3;
+    // Simulate the number of Solicit-Advertise-Request-Reply (SARR) echanges.
+    // The test function generates server's responses and passes it to the
+    // TestControl class methods for processing. The number of exchanges
+    // actually performed is returned in 'iterations_performed' argument. If
+    // processing is successful, the number of performed iterations should be
+    // equal to the number of exchanges specified with the '-n' command line
+    // parameter (10 in this case). All exchanged packets carry the IA_NA option
+    // to simulate the IPv6 address acquisition and to verify that the
+    // IA_NA options returned by the server are processed correctly.
     testPkt6Exchange(iterations_num, received_num, use_templates,
                      iterations_performed);
     EXPECT_EQ(6, iterations_performed);
 }
 
+TEST_F(TestControlTest, Packet6Exchange) {
+    // Get the local loopback interface to open socket on
+    // it and test packets exchanges. We don't want to fail
+    // the test if interface is not available.
+    std::string loopback_iface(getLocalLoopback());
+    if (loopback_iface.empty()) {
+        std::cout << "Unable to find the loopback interface. Skip test."
+                  << std::endl;
+        return;
+    }
+
+    const int iterations_num = 100;
+    // Set number of iterations to 10.
+    processCmdLine("perfdhcp -l " + loopback_iface
+                   + " -e address-only"
+                   + " -6 -r 100 -n 10 -R 20 -L 10547 ::1");
+    int iterations_performed = 0;
+    // Set number of received packets equal to number of iterations.
+    // This simulates no packet drops.
+    bool use_templates = false;
+
+    // Simulate the number of Solicit-Advertise-Request-Reply (SARR) echanges.
+    // The test function generates server's responses and passes it to the
+    // TestControl class methods for processing. The number of exchanges
+    // actually performed is returned in 'iterations_performed' argument. If
+    // processing is successful, the number of performed iterations should be
+    // equal to the number of exchanges specified with the '-n' command line
+    // parameter (10 in this case). All exchanged packets carry the IA_NA option
+    // to simulate the IPv6 address acqusition and to verify that the IA_NA
+    // options returned by the server are processed correctly.
+    testPkt6Exchange(iterations_num, iterations_num, use_templates,
+                     iterations_performed);
+    // Actual number of iterations should be 10.
+    EXPECT_EQ(10, iterations_performed);
+}
+
+TEST_F(TestControlTest, Packet6ExchangePrefixDelegation) {
+    // Get the local loopback interface to open socket on
+    // it and test packets exchanges. We don't want to fail
+    // the test if interface is not available.
+    std::string loopback_iface(getLocalLoopback());
+    if (loopback_iface.empty()) {
+        std::cout << "Unable to find the loopback interface. Skip test."
+                  << std::endl;
+        return;
+    }
+
+    const int iterations_num = 100;
+    // Set number of iterations to 10.
+    processCmdLine("perfdhcp -l " + loopback_iface
+                   + " -e prefix-only"
+                   + " -6 -r 100 -n 10 -R 20 -L 10547 ::1");
+    int iterations_performed = 0;
+    // Set number of received packets equal to number of iterations.
+    // This simulates no packet drops.
+    bool use_templates = false;
+
+    // Simulate the number of Solicit-Advertise-Request-Reply (SARR) echanges.
+    // The test function generates server's responses and passes it to the
+    // TestControl class methods for processing. The number of exchanges
+    // actually performed is returned in 'iterations_performed' argument. If
+    // processing is successful, the number of performed iterations should be
+    // equal to the number of exchanges specified with the '-n' command line
+    // parameter (10 in this case). All exchanged packets carry the IA_PD option
+    // to simulate the Prefix Delegation and to verify that the IA_PD options
+    // returned by the server are processed correctly.
+    testPkt6Exchange(iterations_num, iterations_num, use_templates,
+                     iterations_performed);
+    // Actual number of iterations should be 10.
+    EXPECT_EQ(10, iterations_performed);
+}
+
+TEST_F(TestControlTest, Packet6ExchangeAddressAndPrefix) {
+    // Get the local loopback interface to open socket on
+    // it and test packets exchanges. We don't want to fail
+    // the test if interface is not available.
+    std::string loopback_iface(getLocalLoopback());
+    if (loopback_iface.empty()) {
+        std::cout << "Unable to find the loopback interface. Skip test."
+                  << std::endl;
+        return;
+    }
+
+    const int iterations_num = 100;
+    // Set number of iterations to 10.
+    processCmdLine("perfdhcp -l " + loopback_iface
+                   + " -e address-and-prefix"
+                   + " -6 -r 100 -n 10 -R 20 -L 10547 ::1");
+    int iterations_performed = 0;
+    // Set number of received packets equal to number of iterations.
+    // This simulates no packet drops.
+    bool use_templates = false;
+    // Simulate the number of Solicit-Advertise-Request-Reply (SARR) echanges.
+    // The test function generates server's responses and passes it to the
+    // TestControl class methods for processing. The number of exchanges
+    // actually performed is returned in 'iterations_performed' argument. If
+    // processing is successful, the number of performed iterations should be
+    // equal to the number of exchanges specified with the '-n' command line
+    // parameter (10 in this case).  All exchanged packets carry either IA_NA
+    // or IA_PD options to simulate the address and prefix acquisition with
+    // the single message and to verify that the IA_NA and IA_PD options
+    // returned by the server are processed correctly.
+    testPkt6Exchange(iterations_num, iterations_num, use_templates,
+                     iterations_performed);
+    // Actual number of iterations should be 10.
+    EXPECT_EQ(10, iterations_performed);
+}
+
 TEST_F(TestControlTest, PacketTemplates) {
     std::vector<uint8_t> template1(256);
     std::string file1("test1.hex");
@@ -1017,7 +1182,7 @@ TEST_F(TestControlTest, PacketTemplates) {
     // Size of the file is 2 times larger than binary data size.
     ASSERT_TRUE(createTemplateFile(file1, template1, template1.size() * 2));
     ASSERT_TRUE(createTemplateFile(file2, template2, template2.size() * 2));
-    CommandOptions& options = CommandOptions::instance();
+
     NakedTestControl tc;
 
     ASSERT_NO_THROW(
@@ -1069,7 +1234,8 @@ TEST_F(TestControlTest, RateControl) {
     CommandOptions& options = CommandOptions::instance();
 
     NakedTestControl tc1;
-    uint64_t xchgs_num = tc1.getNextExchangesNum();
+    uint64_t xchgs_num = tc1.getNextExchangesNum(microsec_clock::universal_time(),
+                                                 options.getRate());
     EXPECT_EQ(options.getAggressivity(), xchgs_num);
 
     // The exchange rate is now 1 per second. We don't know how many
@@ -1080,8 +1246,137 @@ TEST_F(TestControlTest, RateControl) {
         processCmdLine("perfdhcp -l 127.0.0.1 -a 1000000 -r 1 all")
     );
     NakedTestControl tc2;
-    xchgs_num = tc2.getNextExchangesNum();
+    xchgs_num = tc2.getNextExchangesNum(microsec_clock::universal_time(),
+                                        options.getRate());
     EXPECT_GT(xchgs_num, 0);
     EXPECT_LT(xchgs_num, options.getAggressivity());
     // @todo add more thorough checks for rate values.
 }
+
+TEST_F(TestControlTest, processRenew) {
+    std::string loopback_iface(getLocalLoopback());
+    if (loopback_iface.empty()) {
+        std::cout << "Skipping the test because loopback interface could"
+            " not be detected" << std::endl;
+        return;
+    }
+    // This command line specifies that the Renew messages should be sent
+    // with the same rate as the Solicit messages.
+    ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l " + loopback_iface +
+                                   " -r 10 -f 10 -R 10 -L 10547 -n 10 ::1"));
+    // Create a test controller class.
+    NakedTestControl tc;
+    tc.initializeStatsMgr();
+    // Set the transaction id generator to sequential to control to guarantee
+    // that transaction ids are predictable.
+    boost::shared_ptr<NakedTestControl::IncrementalGenerator>
+        generator(new NakedTestControl::IncrementalGenerator());
+    tc.setTransidGenerator(generator);
+    // Socket has to be created so as we can actually send packets.
+    int sock_handle = 0;
+    ASSERT_NO_THROW(sock_handle = tc.openSocket());
+    TestControl::TestControlSocket sock(sock_handle);
+
+    // Send a number of Solicit messages. Each generated Solicit will be
+    // assigned a different transaction id, starting from 1 to 10.
+    tc.sendPackets(sock, 10);
+
+    // Simulate Advertise responses from the server. Each advertise is assigned
+    // a transaction id from the range of 1 to 10, so as they match the
+    // transaction ids from the Solicit messages.
+    for (int i = generator->getNext() - 10; i < generator->getNext(); ++i) {
+        Pkt6Ptr advertise(createAdvertisePkt6(i));
+        // If Advertise is matched with the Solicit the call below will
+        // trigger a corresponding Request. They will be assigned
+        // transaction ids from the range from 11 to 20 (the range of
+        // 1 to 10 has been used by Solicit-Advertise).
+        ASSERT_NO_THROW(tc.processReceivedPacket6(sock, advertise));
+    }
+
+    // Requests have been sent, so now let's simulate responses from the server.
+    // Generate corresponding Reply messages with the transaction ids from the
+    // range from 11 to 20.
+    for (int i = generator->getNext() - 10; i < generator->getNext(); ++i) {
+        Pkt6Ptr reply(createReplyPkt6(i));
+        // Each Reply packet corresponds to the new lease acquired. Since
+        // -f<renew-rate> option has been specified, received Reply
+        // messages are held so as Renew messages can be sent for
+        // existing leases.
+        ASSERT_NO_THROW(tc.processReceivedPacket6(sock, reply));
+    }
+
+    uint64_t renew_num;
+    // Try to send 5 Renew packets. It should be successful because
+    // 10 Reply messages has been received. For each of them we should
+    // be able to send Renew.
+    ASSERT_NO_THROW(renew_num = tc.sendRenewPackets(sock, 5));
+    // Make sure that we have sent 5 packets.
+    EXPECT_EQ(5, renew_num);
+
+    // Try to do it again. We should still have 5 Reply packets for
+    // which Renews haven't been sent yet.
+    ASSERT_NO_THROW(renew_num = tc.sendRenewPackets(sock, 5));
+    EXPECT_EQ(5, renew_num);
+
+    // We used all the Reply packets (we sent Renew for each of them
+    // already). Therefore, no further Renew packets should be sent before
+    // We acquire new leases.
+    ASSERT_NO_THROW(renew_num = tc.sendRenewPackets(sock, 5));
+    // Make sure that no Renew has been sent.
+    EXPECT_EQ(0, renew_num);
+}
+
+TEST_F(TestControlTest, createRenew) {
+    // This command line specifies that the Renew messages should be sent
+    // with the same rate as the Solicit messages.
+    ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l lo -r 10 -f 10 -R 10"
+                                   " -L 10547 -n 10 -e address-and-prefix"
+                                   " ::1"));
+    // Create a test controller class.
+    NakedTestControl tc;
+    // Set the transaction id generator because createRenew function requires
+    // it to generate the transaction id for the Renew packet.
+    boost::shared_ptr<NakedTestControl::IncrementalGenerator>
+        generator(new NakedTestControl::IncrementalGenerator());
+    tc.setTransidGenerator(generator);
+
+    // Create a Reply packet. The createRenew function will need Reply
+    // packet to create a corresponding Renew.
+    Pkt6Ptr reply = createReplyPkt6(1);
+    Pkt6Ptr renew;
+    // Check that Renew is created.
+    ASSERT_NO_THROW(renew = tc.createRenew(reply));
+    ASSERT_TRUE(renew);
+    EXPECT_EQ(DHCPV6_RENEW, renew->getType());
+    EXPECT_EQ(1, renew->getTransid());
+
+    // Now check that the Renew packet created, has expected options. The
+    // payload of these options should be the same as the payload of the
+    // options in the Reply.
+
+    // Client Identifier
+    OptionPtr opt_clientid = renew->getOption(D6O_CLIENTID);
+    ASSERT_TRUE(opt_clientid);
+    EXPECT_TRUE(reply->getOption(D6O_CLIENTID)->getData() ==
+                opt_clientid->getData());
+
+    // Server identifier
+    OptionPtr opt_serverid = renew->getOption(D6O_SERVERID);
+    ASSERT_TRUE(opt_serverid);
+    EXPECT_TRUE(reply->getOption(D6O_SERVERID)->getData() ==
+                opt_serverid->getData());
+
+    // IA_NA
+    OptionPtr opt_ia_na = renew->getOption(D6O_IA_NA);
+    ASSERT_TRUE(opt_ia_na);
+    EXPECT_TRUE(reply->getOption(D6O_IA_NA)->getData() ==
+                opt_ia_na->getData());
+
+    // IA_PD
+    OptionPtr opt_ia_pd = renew->getOption(D6O_IA_PD);
+    ASSERT_TRUE(opt_ia_pd);
+    EXPECT_TRUE(reply->getOption(D6O_IA_PD)->getData() ==
+                opt_ia_pd->getData());
+
+}
+



More information about the bind10-changes mailing list