BIND 10 trac3207, updated. 2f2d2cfd4de37453e0fd7a2c6e23ffce94784771 [3207] Addressed review comments.

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Dec 31 20:43:58 UTC 2013


The branch, trac3207 has been updated
       via  2f2d2cfd4de37453e0fd7a2c6e23ffce94784771 (commit)
       via  51fa695a018b40c1a35363690a352ea03bdc6918 (commit)
       via  7715d34151a84d87702c18ea0717fdb7f63affb0 (commit)
       via  0660b7ceb2b998b765e5ab48568f6dfc8111defa (commit)
       via  687beb26da60ed2ce8e6eb44cd1725aa53f29f55 (commit)
       via  210c5d0939516e7ad8250e4ff056f132e4d8963d (commit)
       via  35bee74e98a1d769ef7d028ca3ef95f3ac4081b0 (commit)
       via  e7cac9c491fc35f45d4593e309b27ce6a33973dd (commit)
       via  ca58ac00fce4cb5f46e534d7ffadb2db4e4ffaf3 (commit)
       via  13d812dc84b6967638efe30dd1dd1a34efcbfa80 (commit)
       via  59bd021c72b3e2d9538b534f3ba00ae4b410da62 (commit)
       via  3ff790761f37375de1f9eff59872405fc26e0364 (commit)
       via  0e2851da1d2f91263c2f474fecdfe14427cf5c34 (commit)
       via  0b4b438f337361eeeb7f5e5dbaf9938c24353812 (commit)
       via  88a4858db206dfcd53a227562198f308f7779a72 (commit)
       via  2133cf13c8ce75c146c1f66179f9c7d9a9228ef8 (commit)
       via  d8045b5e1580a1d0b89a232fd61c10d25a95e769 (commit)
       via  113fef862040f3c52e11071ae363e9016f30df0a (commit)
       via  689bd411d1c25b3bd798a24567cadf4f867475c8 (commit)
       via  8f96d784205c46e35f368e39e9233711639918b9 (commit)
       via  e5ee6e7893e6ecad5760759f85eb0650435f8dbf (commit)
       via  ddfaf375356cb49e1c784ae66c7f223c60537652 (commit)
       via  70941f1375063c52a43a10831dc9707c6303f55a (commit)
       via  39e902dc727aedf0fe5b949516ee746bad38607e (commit)
       via  265d1dabd2c745fd1884fa2afbeb951068dd9168 (commit)
       via  fa547a4d7443e7407a8e6cbb02d3263133b24a0d (commit)
       via  c6158690c389d75686545459618ae0bf16f2cdb8 (commit)
       via  03c2a0f0a538aa4f8bebe9678a23473e0a05ee3f (commit)
       via  684524bc130080e4fa31b65edfd14d58eec37e50 (commit)
       via  ac0abc3cf7c512c87122e0f5ead4f0c2fca04890 (commit)
       via  82c80862e9c1c75d475adb002205b2f5f8259b35 (commit)
       via  39c9499d001a98c8d2f5792563c28a5eb2cc5fcb (commit)
       via  840afe672b9002ddecba485aec09ad085e09baeb (commit)
       via  df4334a7eb130bb0322d271253c4ffe15485a2b7 (commit)
       via  96c135a9247ae93f5ba5c8843b72e7c34aa38825 (commit)
       via  5862eb4c980b550d5206011d4db3b3d2fc649d05 (commit)
       via  12c48d424f606311428eaf1029b6d25b62094fa0 (commit)
       via  68d52986f180fa8574dc853febfa4a21fcec3569 (commit)
       via  833deb1fced98bc568ab238a8e4f6f876ca97283 (commit)
       via  95fd27d60daef2d8ca272ce3fde3911787b1d127 (commit)
       via  633b07c7d425adbeba3cf81bf5dced2df9fbc64a (commit)
       via  5674a5dc2b08f3c594408fc0b933b9824ab453ab (commit)
       via  714ade4bb1e81b5b0dec1a3ef3de556f5fa6f6e3 (commit)
       via  dc1071d7aa1204e731c0a5959d28dc3425aa7f3b (commit)
       via  4895dc78cc15d7b89ab782d7e74ef2ab90255da3 (commit)
       via  fdc7dd1164fad1d1ccd45b2713a231e0434dda08 (commit)
       via  1391bf812cb841eabca6a7e5f67fc0324367eac1 (commit)
       via  dceca9554cb9410dd8d12371b68198b797cb6cfb (commit)
       via  7a96b9bb8c38e0e45b44db09c5c2b015c2609cb6 (commit)
       via  08b88147e8368d7f48a2e130deefbcb86cd645af (commit)
       via  13cb26e18540a27e77a96736eb7d8029997f1ccf (commit)
       via  3c33b14cb434d745187877e56dd3858648343281 (commit)
       via  d20cfa4f0f766b0c28eeea0be28be42685c1d265 (commit)
       via  016bfae00460b4f88adbfd07ed26759eb294ef10 (commit)
       via  ae4ce59aabd5281822630668956425fa6167eca8 (commit)
       via  c4af356c438e33f0edcefcb305eb82d11326860d (commit)
       via  68ec7bd3331f98e87e5a0f59cc0894d4e6792a84 (commit)
       via  d0841b4220752c10810f3664b90a05519796aa3e (commit)
       via  b25d9710abc79dd80179ea256793c1ac5556309a (commit)
       via  19a8a4ef198d0b435f8f525cf5eaf215716084ae (commit)
       via  4dc858542d2f7d7e4f32d3e0def1d3bb27fcfb85 (commit)
       via  f49c4b8942cdbafb85414a1925ff6ca1d381f498 (commit)
       via  5e857a2109cc56c957fc8949343fbc1fd79100c1 (commit)
       via  cfd9d626bef43163918be6c5f93e295f4a0889d9 (commit)
       via  1a1447425c6acc5601ea59d18c3638a0e65f0aed (commit)
       via  af2ab70ac7327f38f0e75f37175d190e0f77fa4d (commit)
       via  5414d2bdabfc5d440848014901c7744979a92486 (commit)
       via  ae4c0644431f0da68e498b3334a9ea2e55825bf6 (commit)
       via  9aad3742c573ad2f16a9b8bf07400e9c0b247e7e (commit)
       via  13f795defaa2a330f4ccc637735f2f3a7d43a0cc (commit)
       via  3ed35605cc7f7aeaec15d04e8c5f54a1b54369b6 (commit)
       via  439951434df8bb361eaf9453a65f386a74700215 (commit)
       via  8f99da735a9f39d514c40d0a295f751dc8edfbcd (commit)
       via  33a7569f381acf79c29b57a20329a8ed94319aa1 (commit)
       via  608638ff330a5d144516d8d556779aad5e6e8cb3 (commit)
       via  3677658ea00a12e19eed9904480c807ba0e5f7a2 (commit)
       via  68aae7c97b56de73918721193ec1ee29bee638e6 (commit)
       via  a52c2327de24151a8a9f1d93f4bb6c3ee3962ac0 (commit)
       via  ddf389fa6171e8b6b57bf8b359d5b2db45727dc1 (commit)
       via  4c74dff22bd0cd130120d98847c16152ddf39bc6 (commit)
       via  cf3e0e1c486079543ad11af9a3ec48f0a70b4b57 (commit)
       via  d186a7f908e4796311fbfb97fc5e534b2bdb681d (commit)
       via  6782bcbb2c658aecf68902af6f50f07cf04cf2f2 (commit)
       via  9fdc1f5af7844126fdc5b578110ab91879666132 (commit)
       via  93e9a85ea91a3ee3b25304a7049921061084c4ea (commit)
       via  fe717f9cfeebf48714de35b1c8f4b1ce323ca67f (commit)
       via  65238e01cb0390c23e4937ed7710e968514fb858 (commit)
       via  00d6ca046beb9f2e494043d4bc1397859d554f6f (commit)
       via  fe99fae754429d42ff01dc01d79fe7b5495beaad (commit)
       via  331b10dc0682b41939e9a184e3c4a2cc9b132803 (commit)
       via  5b9c261d8b66d61f1df4a088e9a283855332c392 (commit)
       via  3ca6b9a20ba4d9931a686fd5b684166d413d700f (commit)
       via  b52a45ce15d894e540d0c8011786a913630dbffc (commit)
       via  c62586a2c2f8aecddbb2c1cfd0c00fb7cf41816a (commit)
       via  8cd003e66527e9224b951bebd0c4c4e675e5827d (commit)
       via  ad0866f984b4a223234c4ba5ea1df55c5cf9bff1 (commit)
       via  183693547ce28671d56bc272859b401c47db8f33 (commit)
       via  e4d1f908e14c82062aab9a391299eb0b2692bf9a (commit)
       via  d0506dcfcf7798859931851c3eb90956dd4bb03c (commit)
       via  09811212553870d80e7958fb1161146a99e2d216 (commit)
       via  4507742b539d4b992b184d3aefdc005264871973 (commit)
       via  67e3af3c8a57868712f85d86d47e1936ee834628 (commit)
       via  721815f9c773352c738b5bcf3a7e88bb9ad4cbf8 (commit)
       via  4e225668a62a8fde0edb28a2938d968989f3cbc4 (commit)
       via  0f7b304c340173ab49759f82ff7cbdf49aef5d7a (commit)
       via  8017a0f12131114498ee21875ff0843310650612 (commit)
       via  0f1ed4205a46eb42ef728ba6b0955c9af384e0be (commit)
       via  d4e570f097fe0eb9009b177a4af285cde0c636cc (commit)
       via  0feb5b9c77c1677b51ea258f1839fdb2efd24ff5 (commit)
       via  a232f3d7d92ebcfb7793dc6b67914299c45c715b (commit)
       via  927511744e90baff4b1d7b4c92797f0c9b1dbea6 (commit)
       via  9bd776e36b7f53a6ee2e4d5a2ea79722ba5fe13b (commit)
       via  ae4a470fa56723eeb41df8145823144031a5039b (commit)
       via  f617e6af8cdf068320d14626ecbe14a73a6da221 (commit)
       via  904161f5137d01126cb076ebebbcc7854fd182d6 (commit)
       via  138fa31d02d9276f1f193b67d9b342357228e35b (commit)
       via  48b7ddcc06665e8b13be3da3e418c31696461484 (commit)
       via  c587763b64ff0e38bab72961ead737b5462ebcce (commit)
       via  d77a5642cc9696757b2559f53b103218b89e9bba (commit)
       via  1c9cf817f494416bfe7dba7896c4e23f331239bc (commit)
       via  9ad9193ea3565d71a2c7a5fb7eff225be8c8e2ab (commit)
       via  e12b9bda527f26fff635e14b28cf60dda8f1cabb (commit)
       via  ee3c393ea080a07dd4e7589ce3195aa43d0080ea (commit)
       via  9ea3131be861042b883033530ffaec46d190dfa2 (commit)
       via  b7dd9c0a496dfc8cc5779f0130a5375af1e84d14 (commit)
       via  6443d1198b26c6fec9461301ff887258651fe423 (commit)
       via  4921d7de6b5623c7e85d2baf8bc978686877345b (commit)
       via  2fb33211cae9d8e7d43edc266279bf4a46fc4658 (commit)
       via  41643199f9742a7c9afd96a2f20138c3f0033c37 (commit)
       via  338d7d58b4d4f0f0cb19d14d8be87b03964e7227 (commit)
       via  3328fb3879e398494bf86d8e74c36188426178aa (commit)
       via  672d49074ea1a9a39e0cd53ef92db33980ceffc0 (commit)
       via  d57c3ae58c721dccc44a84e2eb30cbb60361bed7 (commit)
       via  5d2952c08dfedf586da40857c2434b211b42d183 (commit)
       via  89e903003964299fe321bad57e707b925aaf3a91 (commit)
       via  da025438b6d36873aea91193987e5b9ecbb1352c (commit)
       via  12bb02d70471d46b8e1c7352289f60dc30734ce5 (commit)
       via  9c0b16629926d904e0273d401862fb0939378782 (commit)
       via  887d1e728e9d7bacd5e9daffc28db83d76103549 (commit)
       via  f7ee4ceaddef21ec63d85126611778fc31580957 (commit)
       via  c3cf34189e240dfd2a7006e17f405e2c67c0e0b0 (commit)
       via  f2f02dba84b5c12314f6018294423ebc45c4127e (commit)
       via  7c4d8dae1b063b67e5382c070a900be1e3c9b0a5 (commit)
       via  e1771af6cabe0bad2f87b7c79405a92ce46e38c6 (commit)
       via  0119b50f66d9c1ae139d663d08c99cbe895a53e7 (commit)
       via  ad2132e568f5db4b7926d2bf56214d195061cce1 (commit)
       via  54910e664a3697b1b486b84d08aba0c382a5ecc7 (commit)
       via  91cb5a837770be0fe38b64441ede9bd461443018 (commit)
       via  e03adbe6fe2a91adb5065396bb00aa16fa5fc45f (commit)
       via  e03cb3e3bc2bebf0924288afc7c62ab3382ec8d1 (commit)
       via  e144ab7b4fa2cbaa258849b8f2d31746326a43ea (commit)
       via  070f123aef2d20da364f3f12d57e9be4e070d854 (commit)
       via  7e75d22f278f460b7b61470c62063b345a1f436c (commit)
       via  ad5750db8c708d8849695dec43ff79e64a256078 (commit)
       via  e6df9bca72feedd92ec08dc43b53dfec632f228c (commit)
       via  606b06cef9bae2038a10dad2ac89db8f93708e84 (commit)
       via  32e3681b8ed317fbf9e3e195a854a0830fccbb08 (commit)
       via  578392d0e3f5fd8abbce315321d3ec270d283eb1 (commit)
       via  4c16336d81f8af698f6791c3f2a637106211e72d (commit)
       via  4d07784b1a5773e5773e1ccb683171e0225a098e (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  9d3cf130a0c92c715b8ff1b7ce8f1743ab9c2bc7 (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  4a08e3662e7720ac6c96b7d585084bbe4033c29b (commit)
       via  96fdb92a291b0f8897027e3b1c8b79918ae32483 (commit)
       via  9fbb30d633080d5e6a65cef793b1a990f4f1d5e8 (commit)
       via  9dc05967b079038d66d4a79ba607ac553500e23b (commit)
       via  4cad8855373838b302370a00adee1501ee080d04 (commit)
       via  95fb9fb9e6a8a45b74169ece6c0ab8b092b85b4a (commit)
       via  639db0b57ac5c0d24c1cd021d42385f03d7ce818 (commit)
       via  d7460a8c4d81e973a45b70c27676d87b1a0523d3 (commit)
       via  92740b11ed7dc3a757e2082608596e0a3d173b51 (commit)
       via  1a0dd0c77ad728edfd20bd329db0019f87fb64a4 (commit)
       via  c1ac5b336c1b1be2aa41e4202e997d7a57532935 (commit)
       via  6ff2610c28228d3a91b428b178517ff2bb7fbe69 (commit)
       via  0764a74e461d71c8d3dcb25dc2399a4d18d553f8 (commit)
       via  e7fff7124061a6fb64dfb03d3e652de8b883305c (commit)
       via  d1397da715c4c2fd9039923250f380e3f1172fb3 (commit)
       via  2838c0769e219dbd2c8a479efe87f9bfdb4da8e9 (commit)
       via  f1094390dc096d25df6465d737974a5d6f82e6ea (commit)
       via  d130980f515cc5cfab65df8dd0944b56d5fbcdea (commit)
       via  2d83a3a525c255b565aa57e3bd5efce5ca38a269 (commit)
       via  79569d47bdbece10f1ee4532f33ec23d4c8e00b4 (commit)
       via  4386caec66f57272c1239a3134dc8a0add870506 (commit)
       via  0d62790c36094e5789cdb7ff713e8e58ba9326fd (commit)
       via  235246a1c1aaf52a976896ab1bfc0df551abdb5a (commit)
       via  4527401f4afbd8f7a9dafb7296863d01da0d8c09 (commit)
       via  2c20227ed4c9f1ccb5184e8c9698dcf7a7e79c4d (commit)
       via  9cb427b4940ed7531facf3420f3bdd94afc37f64 (commit)
       via  b107f99d0ae8379ad635e7017d6bccb7d1c50882 (commit)
       via  511e9c6eca07d842e90f080663d9a3ecadb430c2 (commit)
       via  7aeeab642dd16c978464a944fdc732246431a579 (commit)
       via  8842945c12181ee427f7dfc1e7c1e1032f9cc25f (commit)
       via  43c722563552989ee5f7adfe99b0ba75bb56bb87 (commit)
       via  cc480a64481dbce14348038cfa254578fc51623b (commit)
       via  8d746f00a3e7014d78502b3744a426d54fc0e3ef (commit)
       via  fa1c3b6a4edfc169a86410ccf3b35263c6cb8d4a (commit)
       via  342c56224c215ca1cb245df0875878e4f8fad0e6 (commit)
       via  273b3105e21cfa457381983bf0c98f2590c55ebd (commit)
       via  e2dbd7090ae49267e0ff14f593de49629afbf710 (commit)
       via  f83c6c44a65b4cb04d3e2691e5bf0a3bf82d59c0 (commit)
       via  3b9c90d00e5b0c2790a9d7c94b8765b61a6b64a0 (commit)
       via  1e278e019742e8f13b93d6f670d50eb14e675217 (commit)
       via  677e0bd0a52306ea81f3a7bab51257ffc466b856 (commit)
       via  2f3324cbeeada97f94105d9c8ce902f496e34dd3 (commit)
       via  37bb1f516ee852871f61b92d7bb5f50ba79a4b0f (commit)
       via  19cb9064139e3b2adcf26bef7d7fdd38db356010 (commit)
       via  3f5af01217a8e67822a9a25744b06579390a57f5 (commit)
       via  8bf74aab112eeac3471cc9d9ebdfb74cac970176 (commit)
       via  7d35d92626c35bd28ba4cf195fb679f44091bf6b (commit)
       via  d640e2711d36ff59c9adf84b092ccba7f17ef46a (commit)
       via  0e24ff0516f95c8f0ae4a4b82892d97e40891530 (commit)
       via  14aecfe2eb61e067200a91684eec7b04b10e0634 (commit)
       via  08d4a94ef93b4a1fb0ba0fcbadc3793793c8c73a (commit)
       via  e674965bfb4fefd24fd44b70e80429ddea92e6ca (commit)
       via  e18f0aeceb9095198c3081cbd08e772a5715baf8 (commit)
       via  2f326350f8f49fd6db247d38d6c3575e9e82a0a5 (commit)
       via  d2b3d82dc02b9e6fe5fab81b78f6dfc532888d3b (commit)
       via  46e6d05e9f1c6990c17afff7890607db8c5acc04 (commit)
       via  cfd7033c2710b19d92d719f54ceb50fe866261bc (commit)
       via  90b595cca8c6b9041e9680db1d00f41f0d80423b (commit)
       via  494ec81f8f76dc62a1ea51f1719ab0fb8fdc008c (commit)
       via  42d8288e577b331401c4c785c8272a9a5a133c09 (commit)
       via  ee550617afb58aa9ae58f1cee64fb75496d3cfba (commit)
       via  44d1f2b6eda3eb818cfb0d15f618fbaf6d8a9bb7 (commit)
       via  9dc963b3f128ec1ec68ac3b3e778b80718cef973 (commit)
       via  ee888857ee0e9d9d571f8087ff5edbd1ddbd637b (commit)
       via  fc2b2f8fa482e56c2c6f7e1e49db4129e9c5e7e6 (commit)
       via  1d58f76d64c79770956797c41f91e261633f812e (commit)
       via  c8a1da135c0a6e9e82bbbec0fb5ed062a88ee37b (commit)
       via  2093c5f82308d58537cb01130a2e54109cfbd6d7 (commit)
       via  d5d77acc2f46862e365fb42ae983158bd47eee9b (commit)
       via  612d8e5a1a35b9e359d610654c86b9b6fd1246cc (commit)
       via  4924ba4190e5a2df3d7f5fb99c448869ae9133ee (commit)
       via  617c1d7c950beb8ddceb2ab03f37dca55959deb6 (commit)
       via  ccb473862e3046f9cf0dcc923757a4ada7ba093f (commit)
       via  df1550729f9ea49d6b0f90e677726fc0dec1f804 (commit)
       via  782e4e8e612336d637f848c1e2c754906754d79d (commit)
       via  b526ece8f040116089977371782a40e7956d643b (commit)
       via  8ce7f740b84364774d90f3438634577cc556ae66 (commit)
       via  cafeb7250bc2c3400c0d3e7736f6cf3971bf741b (commit)
       via  e91d83ea552f8704e04f099a701a838e14947368 (commit)
       via  b0131d52efae513ed8d86f6c0b3bb4391d32ad16 (commit)
       via  c0554a39903141b168944a9c67ef378e5c923e4a (commit)
       via  ecd5a9bc58e0f15933f5508afec02a0d48ba342e (commit)
       via  fcedc148b175e78928bf0f46d904e088ef549b00 (commit)
       via  46b7d9d9e329ea3ed9a338924e0cec4e3c83ee04 (commit)
       via  5b63f9693efd47c600139b8522c37ea2f01666a0 (commit)
       via  499e6f2e3f5bd141459ba46f90359d9dbc72c125 (commit)
       via  dc10e21d3ace93577e654885981ace4cae92ee09 (commit)
       via  90b12d797d33c23ec96cc877ecf4c84a42f6214a (commit)
       via  f7a34944f319ebffaced2e1ea8a7dd1c420a4c99 (commit)
       via  d9ee65e374924d0f1cde75f4aa9c242218d1ad85 (commit)
       via  847cada3ef9794906046f1b1094e066bed55b581 (commit)
       via  41c4d829e66dfce48146a227ed396bf249a41344 (commit)
       via  13542119b1625ab23d0b30f36d8b887876b9a818 (commit)
       via  9b5c746e9bf6fe264f4aa57dc0e884f4fd5a8d85 (commit)
      from  3b1b5032e2afd07bf7d947e185b59a04d9805e08 (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 2f2d2cfd4de37453e0fd7a2c6e23ffce94784771
Author: Thomas Markwalder <tmark at isc.org>
Date:   Tue Dec 31 15:38:13 2013 -0500

    [3207] Addressed review comments.
    
    Addressed review comments which were largely minor. Limited use of
    extern C linkage to only the callout functions themselves. Added
    a dox page describing the library.  Added namespace user_chk.

commit 51fa695a018b40c1a35363690a352ea03bdc6918
Merge: 3b1b503 7715d34
Author: Thomas Markwalder <tmark at isc.org>
Date:   Tue Dec 31 07:38:34 2013 -0500

    Merge branch 'master' into trac3207
    
    3207 was created quite some time ago, so master was
    merged into it first.

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

Summary of changes:
 .gitignore                                         |    2 +-
 ChangeLog                                          |  154 +-
 Makefile.am                                        |    4 +-
 configure.ac                                       |  611 ++++---
 doc/Doxyfile                                       |    1 +
 doc/design/resolver/03-cache-algorithm             |  234 +++
 doc/design/resolver/03-cache-algorithm.txt         |  256 ---
 doc/devel/contribute.dox                           |  162 ++
 doc/devel/mainpage.dox                             |    9 +-
 doc/guide/Makefile.am                              |    4 +-
 doc/guide/bind10-guide.xml                         |   77 +-
 m4macros/ax_boost_for_bind10.m4                    |   28 +-
 m4macros/ax_sqlite3_for_bind10.m4                  |   21 +-
 src/bin/auth/auth_srv.cc                           |   10 +-
 src/bin/auth/tests/datasrc_config_unittest.cc      |    9 +-
 src/bin/bind10/init.py.in                          |   10 +-
 src/bin/bind10/tests/init_test.py.in               |    2 +-
 src/bin/bindctl/bindcmd.py                         |    5 +
 src/bin/d2/Makefile.am                             |    4 +
 src/bin/d2/{d2_log.h => d2_asio.h}                 |   21 +-
 src/bin/d2/d2_cfg_mgr.h                            |    2 +-
 src/bin/d2/d2_config.h                             |    2 +-
 src/bin/d2/d2_messages.mes                         |  184 +-
 src/bin/d2/d2_process.cc                           |    4 +-
 src/bin/d2/d2_queue_mgr.cc                         |    9 +-
 src/bin/d2/d2_queue_mgr.h                          |    7 +-
 src/bin/d2/d2_update_message.cc                    |    2 +-
 src/bin/d2/d2_update_mgr.cc                        |    6 +-
 src/bin/d2/d2_update_mgr.h                         |    6 +-
 src/bin/d2/d_controller.h                          |    2 +-
 src/bin/d2/d_process.h                             |    4 +-
 src/bin/d2/dns_client.cc                           |    7 +-
 src/bin/d2/nc_add.cc                               |  697 ++++++++
 src/bin/d2/nc_add.h                                |  451 +++++
 src/bin/d2/nc_remove.cc                            |  695 ++++++++
 src/bin/d2/nc_remove.h                             |  435 +++++
 src/bin/d2/nc_trans.cc                             |  183 +-
 src/bin/d2/nc_trans.h                              |  145 +-
 src/bin/d2/state_model.cc                          |   43 +-
 src/bin/d2/state_model.h                           |   17 +-
 src/bin/d2/tests/Makefile.am                       |    9 +-
 src/bin/d2/tests/d2_queue_mgr_unittests.cc         |   43 +-
 src/bin/d2/tests/d2_update_mgr_unittests.cc        |   15 +-
 src/bin/d2/tests/d_test_stubs.cc                   |    2 +-
 src/bin/d2/tests/d_test_stubs.h                    |    2 +-
 src/bin/d2/tests/dns_client_unittests.cc           |    2 +-
 src/bin/d2/tests/nc_add_unittests.cc               | 1871 +++++++++++++++++++
 src/bin/d2/tests/nc_remove_unittests.cc            | 1872 ++++++++++++++++++++
 src/bin/d2/tests/nc_test_utils.cc                  |  658 +++++++
 src/bin/d2/tests/nc_test_utils.h                   |  314 ++++
 src/bin/d2/tests/nc_trans_unittests.cc             |  520 +++++-
 src/bin/dhcp4/Makefile.am                          |    1 +
 src/bin/dhcp4/config_parser.cc                     |   28 +-
 src/bin/dhcp4/dhcp4.dox                            |   73 +-
 src/bin/dhcp4/dhcp4.spec                           |    6 +
 src/bin/dhcp4/dhcp4_messages.mes                   |   38 +
 src/bin/dhcp4/dhcp4_srv.cc                         |  542 +++++-
 src/bin/dhcp4/dhcp4_srv.h                          |  171 ++
 src/bin/dhcp4/tests/Makefile.am                    |    3 +
 src/bin/dhcp4/tests/config_parser_unittest.cc      |  156 ++
 src/bin/dhcp4/tests/dhcp4_srv_unittest.cc          |  462 +++--
 src/bin/dhcp4/tests/dhcp4_test_utils.cc            |   24 +-
 src/bin/dhcp4/tests/dhcp4_test_utils.h             |   20 +-
 src/bin/dhcp4/tests/fqdn_unittest.cc               |  769 ++++++++
 src/bin/dhcp4/tests/wireshark.cc                   |    2 +
 src/bin/dhcp6/config_parser.cc                     |    9 +-
 src/bin/dhcp6/dhcp6.dox                            |   12 +-
 src/bin/dhcp6/dhcp6_srv.cc                         |   61 +-
 src/bin/dhcp6/dhcp6_srv.h                          |   11 +-
 src/bin/dhcp6/tests/Makefile.am                    |    1 +
 src/bin/dhcp6/tests/config_parser_unittest.cc      |  115 ++
 src/bin/dhcp6/tests/dhcp6_srv_unittest.cc          |  825 +++------
 src/bin/dhcp6/tests/fqdn_unittest.cc               |  778 ++++++++
 src/bin/msgq/msgq.py.in                            |   27 +-
 src/bin/msgq/msgq_messages.mes                     |    4 +
 src/bin/msgq/tests/msgq_run_test.py                |   16 +
 src/bin/xfrin/b10-xfrin.xml                        |   82 +
 src/bin/xfrin/tests/xfrin_test.py                  |  121 +-
 src/bin/xfrin/xfrin.py.in                          |   77 +-
 src/bin/xfrin/xfrin.spec                           |  221 +++
 src/hooks/dhcp/user_chk/Makefile.am                |    2 +-
 src/hooks/dhcp/user_chk/libdhcp_user_chk.dox       |  199 +++
 src/hooks/dhcp/user_chk/load_unload.cc             |   11 +-
 src/hooks/dhcp/user_chk/pkt_receive_co.cc          |    3 +-
 src/hooks/dhcp/user_chk/pkt_send_co.cc             |  185 +-
 src/hooks/dhcp/user_chk/subnet_select_co.cc        |    3 +-
 .../dhcp/user_chk/tests/user_file_unittests.cc     |    1 +
 .../dhcp/user_chk/tests/user_registry_unittests.cc |    1 +
 src/hooks/dhcp/user_chk/tests/user_unittests.cc    |    1 +
 src/hooks/dhcp/user_chk/tests/userid_unittests.cc  |    1 +
 src/hooks/dhcp/user_chk/user.cc                    |    4 +
 src/hooks/dhcp/user_chk/user.h                     |    4 +
 src/hooks/dhcp/user_chk/user_chk.h                 |    6 +-
 src/hooks/dhcp/user_chk/user_chk_log.cc            |    4 +
 src/hooks/dhcp/user_chk/user_data_source.h         |    4 +
 src/hooks/dhcp/user_chk/user_file.cc               |    3 +
 src/hooks/dhcp/user_chk/user_file.h                |    4 +
 src/hooks/dhcp/user_chk/user_registry.cc           |    3 +
 src/hooks/dhcp/user_chk/user_registry.h            |    4 +
 src/lib/asiodns/Makefile.am                        |    4 -
 src/lib/asiodns/tcp_server.cc                      |    9 +-
 src/lib/asiodns/tests/Makefile.am                  |    4 -
 src/lib/asiodns/tests/dns_server_unittest.cc       |   29 +-
 src/lib/asiodns/udp_server.cc                      |   10 +-
 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/cc/Makefile.am                             |    6 -
 src/lib/cc/data.cc                                 |   36 +-
 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                 |   34 +-
 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/cache_config.h                     |    1 +
 src/lib/datasrc/client_list.cc                     |    1 +
 src/lib/datasrc/tests/client_list_unittest.cc      |   34 +-
 .../datasrc/tests/memory/zone_finder_unittest.cc   |    5 +-
 src/lib/datasrc/tests/test_client.cc               |   10 +-
 .../datasrc/tests/zone_finder_context_unittest.cc  |    9 +-
 src/lib/dhcp/Makefile.am                           |    4 +-
 src/lib/dhcp/dhcp4.h                               |   23 +-
 src/lib/dhcp/docsis3_option_defs.h                 |   66 +
 src/lib/dhcp/duid.cc                               |    6 +-
 src/lib/dhcp/duid.h                                |   26 +-
 src/lib/dhcp/iface_mgr.cc                          |  104 +-
 src/lib/dhcp/iface_mgr.h                           |  172 +-
 src/lib/dhcp/iface_mgr_bsd.cc                      |   98 +-
 src/lib/dhcp/iface_mgr_linux.cc                    |    2 +-
 src/lib/dhcp/iface_mgr_sun.cc                      |  104 +-
 src/lib/dhcp/libdhcp++.cc                          |  403 ++++-
 src/lib/dhcp/libdhcp++.dox                         |   55 +-
 src/lib/dhcp/libdhcp++.h                           |   70 +
 src/lib/dhcp/option.cc                             |    4 +-
 src/lib/dhcp/option_custom.cc                      |   17 +
 src/lib/dhcp/option_custom.h                       |    1 -
 src/lib/dhcp/option_data_types.cc                  |   18 +
 src/lib/dhcp/option_data_types.h                   |   11 +
 src/lib/dhcp/option_definition.cc                  |  130 +-
 src/lib/dhcp/option_definition.h                   |   70 +-
 src/lib/dhcp/option_vendor.cc                      |   85 +
 src/lib/dhcp/option_vendor.h                       |  101 ++
 src/lib/dhcp/pkt4.cc                               |    2 +-
 src/lib/dhcp/pkt6.cc                               |    4 +-
 src/lib/dhcp/pkt_filter.cc                         |   66 +
 src/lib/dhcp/pkt_filter.h                          |   58 +-
 src/lib/dhcp/pkt_filter_inet.cc                    |   21 +-
 src/lib/dhcp/pkt_filter_inet.h                     |   32 +-
 src/lib/dhcp/pkt_filter_lpf.cc                     |   51 +-
 src/lib/dhcp/pkt_filter_lpf.h                      |   25 +-
 src/lib/dhcp/std_option_defs.h                     |    4 +-
 src/lib/dhcp/tests/Makefile.am                     |    3 +
 src/lib/dhcp/tests/iface_mgr_unittest.cc           |  603 ++++++-
 src/lib/dhcp/tests/libdhcp++_unittest.cc           |  213 ++-
 src/lib/dhcp/tests/option_custom_unittest.cc       |  198 ++-
 src/lib/dhcp/tests/option_data_types_unittest.cc   |   16 +-
 src/lib/dhcp/tests/option_definition_unittest.cc   |   45 +-
 src/lib/dhcp/tests/option_unittest.cc              |    6 +-
 src/lib/dhcp/tests/option_vendor_unittest.cc       |  240 +++
 src/lib/dhcp/tests/pkt4_unittest.cc                |    6 +-
 src/lib/dhcp/tests/pkt6_unittest.cc                |    6 +-
 src/lib/dhcp/tests/pkt_filter_inet_unittest.cc     |  192 +-
 src/lib/dhcp/tests/pkt_filter_lpf_unittest.cc      |  185 +-
 src/lib/dhcp/tests/pkt_filter_test_utils.cc        |  194 ++
 src/lib/dhcp/tests/pkt_filter_test_utils.h         |  167 ++
 src/lib/dhcp/tests/pkt_filter_unittest.cc          |   74 +
 src/lib/dhcp/tests/protocol_util_unittest.cc       |    2 +-
 src/lib/dhcp_ddns/ncr_msg.cc                       |  143 +-
 src/lib/dhcp_ddns/ncr_msg.h                        |   95 +-
 src/lib/dhcp_ddns/tests/ncr_unittests.cc           |  164 +-
 src/lib/dhcpsrv/cfgmgr.cc                          |    2 +-
 src/lib/dhcpsrv/cfgmgr.h                           |   21 +-
 src/lib/dhcpsrv/dhcp_parsers.cc                    |   69 +-
 src/lib/dhcpsrv/dhcp_parsers.h                     |   13 +-
 src/lib/dhcpsrv/lease.cc                           |  105 +-
 src/lib/dhcpsrv/lease.h                            |   49 +
 src/lib/dhcpsrv/lease_mgr.h                        |   14 +
 src/lib/dhcpsrv/memfile_lease_mgr.cc               |   12 +-
 src/lib/dhcpsrv/memfile_lease_mgr.h                |   59 +-
 src/lib/dhcpsrv/mysql_lease_mgr.cc                 |    9 +
 src/lib/dhcpsrv/mysql_lease_mgr.h                  |   15 +-
 src/lib/dhcpsrv/option_space_container.h           |   19 +-
 src/lib/dhcpsrv/subnet.cc                          |   32 +
 src/lib/dhcpsrv/subnet.h                           |   41 +-
 src/lib/dhcpsrv/tests/Makefile.am                  |    1 +
 src/lib/dhcpsrv/tests/cfgmgr_unittest.cc           |   17 +
 src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc     |   10 +
 src/lib/dhcpsrv/tests/lease_mgr_unittest.cc        |  123 ++
 src/lib/dhcpsrv/tests/lease_unittest.cc            |   71 +
 .../dhcpsrv/tests/memfile_lease_mgr_unittest.cc    |   74 +-
 src/lib/dhcpsrv/tests/subnet_unittest.cc           |   70 +
 src/lib/dhcpsrv/tests/test_utils.cc                |  117 +-
 src/lib/dhcpsrv/tests/test_utils.h                 |   23 +-
 src/lib/dns/masterload.cc                          |  205 +--
 src/lib/dns/masterload.h                           |   88 +-
 src/lib/dns/message.cc                             |   18 +-
 src/lib/dns/tests/masterload_unittest.cc           |   14 +-
 src/lib/log/Makefile.am                            |    4 -
 src/lib/python/isc/config/config_data.py           |   15 +-
 .../python/isc/config/tests/config_data_test.py    |    2 +
 src/lib/resolve/Makefile.am                        |    4 -
 .../resolve/tests/recursive_query_unittest_3.cc    |   14 +-
 src/lib/server_common/tests/client_unittest.cc     |    6 +-
 src/lib/statistics/tests/Makefile.am               |    4 -
 src/lib/util/threads/tests/condvar_unittest.cc     |   10 +-
 src/lib/util/threads/tests/lock_unittest.cc        |   10 +-
 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 |   59 +
 tests/tools/badpacket/Makefile.am                  |    3 -
 tests/tools/perfdhcp/Makefile.am                   |    4 +-
 tests/tools/perfdhcp/command_options.cc            |  119 +-
 tests/tools/perfdhcp/command_options.h             |   20 +-
 tests/tools/perfdhcp/packet_storage.h              |  161 ++
 tests/tools/perfdhcp/rate_control.cc               |  158 ++
 tests/tools/perfdhcp/rate_control.h                |  180 ++
 tests/tools/perfdhcp/stats_mgr.h                   |   46 +-
 tests/tools/perfdhcp/test_control.cc               |  345 ++--
 tests/tools/perfdhcp/test_control.h                |  121 +-
 tests/tools/perfdhcp/tests/Makefile.am             |    4 +
 .../tools/perfdhcp/tests/command_options_helper.h  |    8 +-
 .../perfdhcp/tests/command_options_unittest.cc     |   95 +
 .../perfdhcp/tests/packet_storage_unittest.cc      |  205 +++
 .../tools/perfdhcp/tests/rate_control_unittest.cc  |  207 +++
 tests/tools/perfdhcp/tests/stats_mgr_unittest.cc   |   20 +
 .../tools/perfdhcp/tests/test_control_unittest.cc  |  459 ++++-
 234 files changed, 20299 insertions(+), 3087 deletions(-)
 delete mode 100644 doc/design/resolver/03-cache-algorithm.txt
 create mode 100644 doc/devel/contribute.dox
 copy src/bin/d2/{d2_log.h => d2_asio.h} (74%)
 create mode 100644 src/bin/d2/nc_add.cc
 create mode 100644 src/bin/d2/nc_add.h
 create mode 100644 src/bin/d2/nc_remove.cc
 create mode 100644 src/bin/d2/nc_remove.h
 create mode 100644 src/bin/d2/tests/nc_add_unittests.cc
 create mode 100644 src/bin/d2/tests/nc_remove_unittests.cc
 create mode 100644 src/bin/d2/tests/nc_test_utils.cc
 create mode 100644 src/bin/d2/tests/nc_test_utils.h
 create mode 100644 src/bin/dhcp4/tests/fqdn_unittest.cc
 create mode 100644 src/bin/dhcp6/tests/fqdn_unittest.cc
 create mode 100644 src/hooks/dhcp/user_chk/libdhcp_user_chk.dox
 create mode 100644 src/lib/dhcp/docsis3_option_defs.h
 create mode 100644 src/lib/dhcp/option_vendor.cc
 create mode 100644 src/lib/dhcp/option_vendor.h
 create mode 100644 src/lib/dhcp/pkt_filter.cc
 create mode 100644 src/lib/dhcp/tests/option_vendor_unittest.cc
 create mode 100644 src/lib/dhcp/tests/pkt_filter_test_utils.cc
 create mode 100644 src/lib/dhcp/tests/pkt_filter_test_utils.h
 create mode 100644 src/lib/dhcp/tests/pkt_filter_unittest.cc
 create mode 100644 src/lib/dhcpsrv/tests/lease_unittest.cc
 create mode 100644 tests/tools/perfdhcp/packet_storage.h
 create mode 100644 tests/tools/perfdhcp/rate_control.cc
 create mode 100644 tests/tools/perfdhcp/rate_control.h
 create mode 100644 tests/tools/perfdhcp/tests/packet_storage_unittest.cc
 create mode 100644 tests/tools/perfdhcp/tests/rate_control_unittest.cc

-----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 45f9424..65efde8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,7 +13,7 @@ TAGS
 
 *.log
 *.trs
-
+config.h.in~
 /aclocal.m4
 /autom4te.cache/
 /config.guess
diff --git a/ChangeLog b/ChangeLog
index 4944bda..b12cf47 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,155 @@
+720.	[func]		tmark
+	Added the initial implementation of the class, NameAddTransaction,
+	to b10-dhcp-ddns.  This class provides a state machine which implements
+	the logic required to remove forward and reverse DNS entries as described
+	in RFC 4703, section 5.5. This includes the ability to construct the
+	necessary DNS requests.
+	(Trac# 3088, git ca58ac00fce4cb5f46e534d7ffadb2db4e4ffaf3)
+
+719.	[func]		tomek
+	b10-dhcp4: Support for sending back client-id (RFC6842) has been
+	added now. Also a configuration parameter (echo-client-id) has
+	been added, so it is possible to enable backward compatibility
+	("echo-client-id false").
+	(Trac #3210, git 88a4858db206dfcd53a227562198f308f7779a72)
+
+718.	[func]		dclink, tomek
+	libdhcp++: Interface detection implemented for FreeBSD, NetBSD,
+	OpenBSD, Mac OS X and Solaris 11. Thanks to David Carlier for
+	contributing a patch.
+	(Trac #2246, git d8045b5e1580a1d0b89a232fd61c10d25a95e769)
+
+717.	[bug]		marcin
+	Fixed the bug which incorrectly treated DHCPv4 option codes 224-254 as
+	standard options, barring them from being used as custom options.
+	(Trac #2772, git c6158690c389d75686545459618ae0bf16f2cdb8)
+
+716.	[func]		marcin
+	perfdhcp: added support for sending DHCPv6 Relese messages at the specified
+	rate and measure performance. The orphan messages counters are not
+	displayed for individual exchanges anymore. The following ticket: #3261
+	has been submitted to implement global orphan counting for all exchange
+	types.
+	(Trac #3181, git 684524bc130080e4fa31b65edfd14d58eec37e50)
+
+715.	[bug]		marcin
+	libdhcp++: Used the CMSG_SPACE instead of CMSG_LEN macro to calculate
+	msg_controllen field of the DHCPv6 message. Use of CMSG_LEN causes
+	sendmsg failures on OpenBSD due to the bug kernel/6080 on OpenBSD.
+	(Trac #1824, git 39c9499d001a98c8d2f5792563c28a5eb2cc5fcb)
+
+714.	[doc]		tomek
+	BIND10 Contributor's Guide added.
+	(Trac #3109, git 016bfae00460b4f88adbfd07ed26759eb294ef10)
+
+713.	[func]		tmark
+	Added DNS update request construction to d2::NameAddTransaction in
+	b10-dhcp-ddns.  The class now generates all DNS update request variations
+	needed to fulfill it's state machine in compliance with RFC 4703, sections
+	5.3 and 5.4.
+	(Trac# 3241, git dceca9554cb9410dd8d12371b68198b797cb6cfb)
+
+712.	[func]		marcin,dclink
+	b10-dhcp4: If server fails to open a socket on one interface it
+	will log a warning and continue to open sockets on other interfaces.
+	The warning message is communicated from the libdhcp++ via the
+	error handler function supplied by the DHCPv4 server. Thanks to
+	David Carlier for providing a patch.
+	(Trac #2765, git f49c4b8942cdbafb85414a1925ff6ca1d381f498)
+
+711.	[func]		tmark
+	Added the initial implementation of the class, NameAddTransaction,
+	to b10-dhcp-ddns.  This class provides the state model logic
+	described in the DHCP_DDNS design to add or replace forward and
+	reverse DNS entries for a given FQDN.  It does not yet construct
+	the actual DNS update requests, this will be added under Trac#
+	3241.
+	(Trac# 3087, git 8f99da735a9f39d514c40d0a295f751dc8edfbcd)
+
+710.	[build]		jinmei
+	Fixed various build time issues for MacOS X 10.9.  Those include
+	some general fixes and improvements:
+	- (libdns++) masterLoad() functions now use the generic MasterLoader
+	  class as backend, eliminating the restrictions of the previous
+	  versions.
+	- (libcc) fixed a minor portability bug in the JSON parser.  Although
+	  the only known affected system is OS X 10.9 at the moment, that
+	  could potentially cause disruption on other existing and future
+	  systems.
+	Other notes:
+	- if built with googletest, gtest 1.7 (and possibly higher) is
+	  required.
+	- many older versions of Boost don't work.  A known workable version
+	  is 1.54.
+	(Trac #3213, git d4e570f097fe0eb9009b177a4af285cde0c636cc)
+
+709.	[bug]		marcin
+	b10-dhcp6: Server crashed when the client sent FQDN option and did
+	not request FQDN option to be returned.
+	(Trac #3220, git 0f1ed4205a46eb42ef728ba6b0955c9af384e0be)
+
+708.	[bug]		dclink,marcin
+	libdhcpsrv: Fixed a bug in Memfile lease database backend which
+	caused DHCPv4 server crashes when leases with NULL client id
+	were present. Thanks to David Carlier for submitting the patch.
+	(Trac #2940, git a232f3d7d92ebcfb7793dc6b67914299c45c715b)
+
+707.	[bug]		muks
+	Using very large numbers (out of bounds) in config values caused
+	BIND 10 to throw an exception. This has been fixed in a patch
+	contributed by David Carlier.
+	(Trac #3114, git 9bd776e36b7f53a6ee2e4d5a2ea79722ba5fe13b)
+
+706.	[func]		marcin
+	b10-dhcp4: Server processes the DHCPv4 Client FQDN and Host Name
+	options sent by a client and generates the response. as a result
+	of processing, the server generates NameChangeRequests which
+	represent changes to DNS mappings for a particular lease (addition
+	or removal of DNS mappings).
+	Currently all generated NameChangeRequests are dropped. Sending
+	them to b10-dhcp-ddns will be implemented with the future tickets.
+	(Trac #3035, git f617e6af8cdf068320d14626ecbe14a73a6da22)
+
+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
@@ -47,7 +199,7 @@
 	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 abcd)
+	(Trac #3199, git 6e171110c4dd9ae3b1be828b9516efc65c33460b)
 
 690.	[bug]		tomek
 	b10-dhcp4: Relay Agent Info option is now echoed back in
diff --git a/Makefile.am b/Makefile.am
index 2b7d149..6fd0200 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -21,11 +21,11 @@ dist_doc_DATA = AUTHORS COPYING ChangeLog README
 .PHONY: check-valgrind check-valgrind-suppress
 
 install-exec-hook:
-	- at echo -e "\033[1;33m" # switch to yellow color text
+	- at tput smso  # Start standout mode
 	@echo "NOTE: BIND 10 does not automatically start DNS services when it is run"
 	@echo "      in its default configuration. Please see the Guide for information"
 	@echo "      on how to configure these services to be started automatically."
-	- at echo -e "\033[m" # switch back to normal
+	- at tput rmso  # End standout mode
 
 check-valgrind:
 if HAVE_VALGRIND
diff --git a/configure.ac b/configure.ac
index 4bed0c6..f508400 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*)
@@ -200,6 +212,7 @@ AC_HELP_STRING([--disable-setproctitle-check],
 # OS dependent configuration
 SET_ENV_LIBRARY_PATH=no
 ENV_LIBRARY_PATH=LD_LIBRARY_PATH
+bind10_undefined_pthread_behavior=no
 
 case "$host" in
 *-solaris*)
@@ -211,13 +224,25 @@ case "$host" in
 	# Destroying locked mutexes, condition variables being waited
 	# on, etc. are undefined behavior on Solaris, so we set it as
 	# such here.
-	AC_DEFINE([HAS_UNDEFINED_PTHREAD_BEHAVIOR], [1], [Does this platform have some undefined pthreads behavior?])
+	bind10_undefined_pthread_behavior=yes
 	;;
 *-apple-darwin*)
 	# Starting with OSX 10.7 (Lion) we must choose which IPv6 API to use
 	# (RFC2292 or RFC3542).
 	CPPFLAGS="$CPPFLAGS -D__APPLE_USE_RFC_3542"
 
+	# In OS X 10.9 (and possibly any future versions?) pthread_cond_destroy
+	# doesn't work as documented, which makes some of unit tests fail.
+	# Testing a specific system and version is not a good practice, but
+	# identifying this behavior would be too heavy (running a program
+	# with multiple threads), so this is a compromise.  In general,
+	# it should be avoided to rely on 'osx_version' unless there's no
+	# viable alternative.
+	osx_version=`/usr/bin/sw_vers -productVersion`
+	if [ test $osx_version = "10.9" ]; then
+		bind10_undefined_pthread_behavior=yes
+	fi
+
 	# libtool doesn't work perfectly with Darwin: libtool embeds the
 	# final install path in dynamic libraries and our loadable python
 	# modules always refer to that path even if it's loaded within the
@@ -240,6 +265,9 @@ esac
 AM_CONDITIONAL(SET_ENV_LIBRARY_PATH, test $SET_ENV_LIBRARY_PATH = yes)
 AC_SUBST(SET_ENV_LIBRARY_PATH)
 AC_SUBST(ENV_LIBRARY_PATH)
+if [ test $bind10_undefined_pthread_behavior = "yes" ]; then
+   AC_DEFINE([HAS_UNDEFINED_PTHREAD_BEHAVIOR], [1], [Does this platform have some undefined pthreads behavior?])
+fi
 
 # Our experiments have shown Solaris 10 has broken support for the
 # IPV6_USE_MIN_MTU socket option for getsockopt(); it doesn't return the value
@@ -718,6 +746,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=
@@ -755,7 +798,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>
@@ -797,6 +857,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)
@@ -875,6 +936,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
 
@@ -954,6 +1029,7 @@ AC_SUBST(MULTITHREADING_FLAG)
 GTEST_LDFLAGS=
 GTEST_LDADD=
 DISTCHECK_GTEST_CONFIGURE_FLAG=
+GTEST_VERSION="unknown"
 
 if test "x$enable_gtest" = "xyes" ; then
 
@@ -1009,6 +1085,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.])
@@ -1224,302 +1301,303 @@ AC_TRY_LINK(
 AM_CONDITIONAL(HAVE_OPTRESET, test "x$var_optreset_exists" != "xno")
 AM_COND_IF([HAVE_OPTRESET], [AC_DEFINE([HAVE_OPTRESET], [1], [Check for optreset?])])
 
-AC_CONFIG_FILES([Makefile
-                 doc/Makefile
-                 doc/guide/Makefile
-                 doc/design/Makefile
+AC_CONFIG_FILES([compatcheck/Makefile
+                 dns++.pc
                  doc/design/datasrc/Makefile
-                 ext/Makefile
-                 ext/asio/Makefile
+                 doc/design/Makefile
+                 doc/guide/Makefile
+                 doc/Makefile
+                 doc/version.ent
                  ext/asio/asio/Makefile
-                 compatcheck/Makefile
-                 src/Makefile
-                 src/bin/Makefile
+                 ext/asio/Makefile
+                 ext/Makefile
+                 m4macros/Makefile
+                 Makefile
+                 src/bin/auth/auth.spec.pre
+                 src/bin/auth/benchmarks/Makefile
+                 src/bin/auth/gen-statisticsitems.py.pre
+                 src/bin/auth/Makefile
+                 src/bin/auth/spec_config.h.pre
+                 src/bin/auth/tests/Makefile
+                 src/bin/auth/tests/testdata/example-base.zone
+                 src/bin/auth/tests/testdata/example-nsec3.zone
+                 src/bin/auth/tests/testdata/example.zone
+                 src/bin/auth/tests/testdata/Makefile
                  src/bin/bind10/bind10
+                 src/bin/bind10/init.py
                  src/bin/bind10/Makefile
+                 src/bin/bind10/run_bind10.sh
+                 src/bin/bind10/tests/init_test.py
                  src/bin/bind10/tests/Makefile
-                 src/bin/cmdctl/Makefile
-                 src/bin/cmdctl/tests/Makefile
+                 src/bin/bindctl/bindctl_main.py
                  src/bin/bindctl/Makefile
+                 src/bin/bindctl/run_bindctl.sh
+                 src/bin/bindctl/tests/bindctl_test
                  src/bin/bindctl/tests/Makefile
-                 src/bin/cfgmgr/Makefile
+                 src/bin/cfgmgr/b10-cfgmgr.py
                  src/bin/cfgmgr/local_plugins/Makefile
+                 src/bin/cfgmgr/Makefile
+                 src/bin/cfgmgr/plugins/datasrc.spec.pre
                  src/bin/cfgmgr/plugins/Makefile
                  src/bin/cfgmgr/plugins/tests/Makefile
+                 src/bin/cfgmgr/tests/b10-cfgmgr_test.py
                  src/bin/cfgmgr/tests/Makefile
+                 src/bin/cmdctl/cmdctl.py
+                 src/bin/cmdctl/cmdctl.spec.pre
+                 src/bin/cmdctl/Makefile
+                 src/bin/cmdctl/run_b10-cmdctl.sh
+                 src/bin/cmdctl/tests/cmdctl_test
+                 src/bin/cmdctl/tests/Makefile
+                 src/bin/d2/Makefile
+                 src/bin/d2/spec_config.h.pre
+                 src/bin/d2/tests/Makefile
+                 src/bin/d2/tests/test_data_files_config.h
+                 src/bin/dbutil/dbutil.py
                  src/bin/dbutil/Makefile
+                 src/bin/dbutil/run_dbutil.sh
+                 src/bin/dbutil/tests/dbutil_test.sh
                  src/bin/dbutil/tests/Makefile
                  src/bin/dbutil/tests/testdata/Makefile
+                 src/bin/ddns/ddns.py
+                 src/bin/ddns/Makefile
+                 src/bin/ddns/tests/Makefile
+                 src/bin/dhcp4/Makefile
+                 src/bin/dhcp4/spec_config.h.pre
+                 src/bin/dhcp4/tests/Makefile
+                 src/bin/dhcp4/tests/marker_file.h
+                 src/bin/dhcp4/tests/test_libraries.h
+                 src/bin/dhcp6/Makefile
+                 src/bin/dhcp6/spec_config.h.pre
+                 src/bin/dhcp6/tests/Makefile
+                 src/bin/dhcp6/tests/marker_file.h
+                 src/bin/dhcp6/tests/test_data_files_config.h
+                 src/bin/dhcp6/tests/test_libraries.h
+                 src/bin/loadzone/loadzone.py
                  src/bin/loadzone/Makefile
-                 src/bin/loadzone/tests/Makefile
+                 src/bin/loadzone/run_loadzone.sh
+                 src/bin/loadzone/tests/correct/correct_test.sh
                  src/bin/loadzone/tests/correct/Makefile
+                 src/bin/loadzone/tests/Makefile
+                 src/bin/Makefile
                  src/bin/memmgr/Makefile
+                 src/bin/memmgr/memmgr.py
+                 src/bin/memmgr/memmgr.spec.pre
                  src/bin/memmgr/tests/Makefile
                  src/bin/msgq/Makefile
+                 src/bin/msgq/msgq.py
+                 src/bin/msgq/run_msgq.sh
                  src/bin/msgq/tests/Makefile
-                 src/bin/auth/Makefile
-                 src/bin/auth/tests/Makefile
-                 src/bin/auth/tests/testdata/Makefile
-                 src/bin/auth/benchmarks/Makefile
-                 src/bin/ddns/Makefile
-                 src/bin/ddns/tests/Makefile
-                 src/bin/dhcp6/Makefile
-                 src/bin/dhcp6/tests/Makefile
-                 src/bin/dhcp4/Makefile
-                 src/bin/dhcp4/tests/Makefile
-                 src/bin/d2/Makefile
-                 src/bin/d2/tests/Makefile
+                 src/bin/resolver/bench/Makefile
                  src/bin/resolver/Makefile
+                 src/bin/resolver/resolver.spec.pre
+                 src/bin/resolver/spec_config.h.pre
                  src/bin/resolver/tests/Makefile
-                 src/bin/resolver/bench/Makefile
-                 src/bin/sysinfo/Makefile
                  src/bin/sockcreator/Makefile
                  src/bin/sockcreator/tests/Makefile
+                 src/bin/stats/Makefile
+                 src/bin/stats/stats_httpd.py
+                 src/bin/stats/stats.py
+                 src/bin/stats/tests/Makefile
+                 src/bin/stats/tests/testdata/Makefile
+                 src/bin/sysinfo/Makefile
+                 src/bin/sysinfo/run_sysinfo.sh
+                 src/bin/sysinfo/sysinfo.py
+                 src/bin/tests/Makefile
+                 src/bin/tests/process_rename_test.py
+                 src/bin/usermgr/b10-cmdctl-usermgr.py
+                 src/bin/usermgr/Makefile
+                 src/bin/usermgr/run_b10-cmdctl-usermgr.sh
+                 src/bin/usermgr/tests/Makefile
                  src/bin/xfrin/Makefile
+                 src/bin/xfrin/run_b10-xfrin.sh
                  src/bin/xfrin/tests/Makefile
                  src/bin/xfrin/tests/testdata/Makefile
+                 src/bin/xfrin/tests/xfrin_test
+                 src/bin/xfrin/xfrin.py
                  src/bin/xfrout/Makefile
+                 src/bin/xfrout/run_b10-xfrout.sh
                  src/bin/xfrout/tests/Makefile
+                 src/bin/xfrout/tests/xfrout_test
+                 src/bin/xfrout/tests/xfrout_test.py
+                 src/bin/xfrout/xfrout.py
+                 src/bin/xfrout/xfrout.spec.pre
                  src/bin/zonemgr/Makefile
+                 src/bin/zonemgr/run_b10-zonemgr.sh
                  src/bin/zonemgr/tests/Makefile
-                 src/bin/stats/Makefile
-                 src/bin/stats/tests/Makefile
-                 src/bin/stats/tests/testdata/Makefile
-                 src/bin/usermgr/Makefile
-                 src/bin/usermgr/tests/Makefile
-                 src/bin/tests/Makefile
-                 src/hooks/Makefile
+                 src/bin/zonemgr/tests/zonemgr_test
+                 src/bin/zonemgr/zonemgr.py
+                 src/bin/zonemgr/zonemgr.spec.pre
                  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
+                 src/hooks/dhcp/user_chk/tests/test_data_files_config.h
+                 src/hooks/Makefile
+                 src/lib/acl/Makefile
+                 src/lib/acl/tests/Makefile
                  src/lib/asiodns/Makefile
                  src/lib/asiodns/tests/Makefile
-                 src/lib/bench/Makefile
+                 src/lib/asiolink/Makefile
+                 src/lib/asiolink/tests/Makefile
                  src/lib/bench/example/Makefile
+                 src/lib/bench/Makefile
                  src/lib/bench/tests/Makefile
+                 src/lib/cache/Makefile
+                 src/lib/cache/tests/Makefile
                  src/lib/cc/Makefile
+                 src/lib/cc/session_config.h.pre
                  src/lib/cc/tests/Makefile
-                 src/lib/python/Makefile
-                 src/lib/python/isc/Makefile
+                 src/lib/cc/tests/session_unittests_config.h
+                 src/lib/config/Makefile
+                 src/lib/config/tests/data_def_unittests_config.h
+                 src/lib/config/tests/Makefile
+                 src/lib/config/tests/testdata/Makefile
+                 src/lib/cryptolink/Makefile
+                 src/lib/cryptolink/tests/Makefile
+                 src/lib/datasrc/datasrc_config.h.pre
+                 src/lib/datasrc/Makefile
+                 src/lib/datasrc/memory/benchmarks/Makefile
+                 src/lib/datasrc/memory/Makefile
+                 src/lib/datasrc/tests/Makefile
+                 src/lib/datasrc/tests/memory/Makefile
+                 src/lib/datasrc/tests/memory/testdata/Makefile
+                 src/lib/datasrc/tests/testdata/Makefile
+                 src/lib/dhcp_ddns/Makefile
+                 src/lib/dhcp_ddns/tests/Makefile
+                 src/lib/dhcp/Makefile
+                 src/lib/dhcpsrv/Makefile
+                 src/lib/dhcpsrv/tests/Makefile
+                 src/lib/dhcpsrv/tests/test_libraries.h
+                 src/lib/dhcp/tests/Makefile
+                 src/lib/dns/benchmarks/Makefile
+                 src/lib/dns/gen-rdatacode.py
+                 src/lib/dns/Makefile
+                 src/lib/dns/python/Makefile
+                 src/lib/dns/python/tests/Makefile
+                 src/lib/dns/tests/Makefile
+                 src/lib/dns/tests/testdata/Makefile
+                 src/lib/exceptions/Makefile
+                 src/lib/exceptions/tests/Makefile
+                 src/lib/hooks/Makefile
+                 src/lib/hooks/tests/Makefile
+                 src/lib/hooks/tests/marker_file.h
+                 src/lib/hooks/tests/test_libraries.h
+                 src/lib/log/compiler/Makefile
+                 src/lib/log/interprocess/Makefile
+                 src/lib/log/interprocess/tests/Makefile
+                 src/lib/log/Makefile
+                 src/lib/log/tests/buffer_logger_test.sh
+                 src/lib/log/tests/console_test.sh
+                 src/lib/log/tests/destination_test.sh
+                 src/lib/log/tests/init_logger_test.sh
+                 src/lib/log/tests/local_file_test.sh
+                 src/lib/log/tests/logger_lock_test.sh
+                 src/lib/log/tests/Makefile
+                 src/lib/log/tests/severity_test.sh
+                 src/lib/log/tests/tempdir.h
+                 src/lib/Makefile
+                 src/lib/nsas/Makefile
+                 src/lib/nsas/tests/Makefile
+                 src/lib/python/bind10_config.py
                  src/lib/python/isc/acl/Makefile
                  src/lib/python/isc/acl/tests/Makefile
-                 src/lib/python/isc/util/Makefile
-                 src/lib/python/isc/util/tests/Makefile
-                 src/lib/python/isc/util/cio/Makefile
-                 src/lib/python/isc/util/cio/tests/Makefile
-                 src/lib/python/isc/datasrc/Makefile
-                 src/lib/python/isc/datasrc/tests/Makefile
-                 src/lib/python/isc/datasrc/tests/testdata/Makefile
-                 src/lib/python/isc/dns/Makefile
-                 src/lib/python/isc/cc/Makefile
+                 src/lib/python/isc/bind10/Makefile
+                 src/lib/python/isc/bind10/tests/Makefile
                  src/lib/python/isc/cc/cc_generated/Makefile
+                 src/lib/python/isc/cc/Makefile
+                 src/lib/python/isc/cc/tests/cc_test
                  src/lib/python/isc/cc/tests/Makefile
                  src/lib/python/isc/config/Makefile
+                 src/lib/python/isc/config/tests/config_test
                  src/lib/python/isc/config/tests/Makefile
+                 src/lib/python/isc/datasrc/Makefile
+                 src/lib/python/isc/datasrc/tests/Makefile
+                 src/lib/python/isc/datasrc/tests/testdata/Makefile
+                 src/lib/python/isc/ddns/Makefile
+                 src/lib/python/isc/ddns/tests/Makefile
+                 src/lib/python/isc/dns/Makefile
                  src/lib/python/isc/log/Makefile
-                 src/lib/python/isc/log/tests/Makefile
                  src/lib/python/isc/log_messages/Makefile
+                 src/lib/python/isc/log_messages/work/__init__.py
                  src/lib/python/isc/log_messages/work/Makefile
+                 src/lib/python/isc/log/tests/log_console.py
+                 src/lib/python/isc/log/tests/Makefile
+                 src/lib/python/isc/Makefile
+                 src/lib/python/isc/memmgr/Makefile
+                 src/lib/python/isc/memmgr/tests/Makefile
+                 src/lib/python/isc/memmgr/tests/testdata/Makefile
                  src/lib/python/isc/net/Makefile
                  src/lib/python/isc/net/tests/Makefile
                  src/lib/python/isc/notify/Makefile
                  src/lib/python/isc/notify/tests/Makefile
-                 src/lib/python/isc/testutils/Makefile
-                 src/lib/python/isc/bind10/Makefile
-                 src/lib/python/isc/bind10/tests/Makefile
-                 src/lib/python/isc/ddns/Makefile
-                 src/lib/python/isc/ddns/tests/Makefile
-                 src/lib/python/isc/memmgr/Makefile
-                 src/lib/python/isc/memmgr/tests/Makefile
-                 src/lib/python/isc/memmgr/tests/testdata/Makefile
-                 src/lib/python/isc/xfrin/Makefile
-                 src/lib/python/isc/xfrin/tests/Makefile
+                 src/lib/python/isc/notify/tests/notify_out_test
                  src/lib/python/isc/server_common/Makefile
                  src/lib/python/isc/server_common/tests/Makefile
-                 src/lib/python/isc/sysinfo/Makefile
-                 src/lib/python/isc/sysinfo/tests/Makefile
                  src/lib/python/isc/statistics/Makefile
                  src/lib/python/isc/statistics/tests/Makefile
-                 src/lib/config/Makefile
-                 src/lib/config/tests/Makefile
-                 src/lib/config/tests/testdata/Makefile
-                 src/lib/cryptolink/Makefile
-                 src/lib/cryptolink/tests/Makefile
-                 src/lib/dns/Makefile
-                 src/lib/dns/tests/Makefile
-                 src/lib/dns/tests/testdata/Makefile
-                 src/lib/dns/python/Makefile
-                 src/lib/dns/python/tests/Makefile
-                 src/lib/dns/benchmarks/Makefile
-                 src/lib/dhcp/Makefile
-                 src/lib/dhcp/tests/Makefile
-                 src/lib/dhcp_ddns/Makefile
-                 src/lib/dhcp_ddns/tests/Makefile
-                 src/lib/dhcpsrv/Makefile
-                 src/lib/dhcpsrv/tests/Makefile
-                 src/lib/exceptions/Makefile
-                 src/lib/exceptions/tests/Makefile
-                 src/lib/datasrc/Makefile
-                 src/lib/datasrc/memory/Makefile
-                 src/lib/datasrc/memory/benchmarks/Makefile
-                 src/lib/datasrc/tests/Makefile
-                 src/lib/datasrc/tests/testdata/Makefile
-                 src/lib/datasrc/tests/memory/Makefile
-                 src/lib/datasrc/tests/memory/testdata/Makefile
-                 src/lib/xfr/Makefile
-                 src/lib/xfr/tests/Makefile
-                 src/lib/hooks/Makefile
-                 src/lib/hooks/tests/Makefile
-                 src/lib/log/Makefile
-                 src/lib/log/interprocess/Makefile
-                 src/lib/log/interprocess/tests/Makefile
-                 src/lib/log/compiler/Makefile
-                 src/lib/log/tests/Makefile
+                 src/lib/python/isc/sysinfo/Makefile
+                 src/lib/python/isc/sysinfo/tests/Makefile
+                 src/lib/python/isc/testutils/Makefile
+                 src/lib/python/isc/util/cio/Makefile
+                 src/lib/python/isc/util/cio/tests/Makefile
+                 src/lib/python/isc/util/Makefile
+                 src/lib/python/isc/util/tests/Makefile
+                 src/lib/python/isc/xfrin/Makefile
+                 src/lib/python/isc/xfrin/tests/Makefile
+                 src/lib/python/Makefile
                  src/lib/resolve/Makefile
                  src/lib/resolve/tests/Makefile
-                 src/lib/testutils/Makefile
-                 src/lib/testutils/testdata/Makefile
-                 src/lib/nsas/Makefile
-                 src/lib/nsas/tests/Makefile
-                 src/lib/cache/Makefile
-                 src/lib/cache/tests/Makefile
                  src/lib/server_common/Makefile
+                 src/lib/server_common/tests/data_path.h
                  src/lib/server_common/tests/Makefile
-                 src/lib/util/Makefile
+                 src/lib/statistics/Makefile
+                 src/lib/statistics/tests/Makefile
+                 src/lib/testutils/Makefile
+                 src/lib/testutils/testdata/Makefile
                  src/lib/util/io/Makefile
-                 src/lib/util/threads/Makefile
-                 src/lib/util/threads/tests/Makefile
-                 src/lib/util/unittests/Makefile
+                 src/lib/util/Makefile
+                 src/lib/util/python/doxygen2pydoc.py
+                 src/lib/util/python/gen_wiredata.py
                  src/lib/util/python/Makefile
+                 src/lib/util/python/mkpywrapper.py
                  src/lib/util/pyunittests/Makefile
                  src/lib/util/tests/Makefile
-                 src/lib/acl/Makefile
-                 src/lib/acl/tests/Makefile
-                 src/lib/statistics/Makefile
-                 src/lib/statistics/tests/Makefile
+                 src/lib/util/threads/Makefile
+                 src/lib/util/threads/tests/Makefile
+                 src/lib/util/unittests/Makefile
+                 src/lib/xfr/Makefile
+                 src/lib/xfr/tests/Makefile
+                 src/Makefile
+                 tests/lettuce/Makefile
+                 tests/lettuce/setup_intree_bind10.sh
                  tests/Makefile
-                 tests/tools/Makefile
                  tests/tools/badpacket/Makefile
                  tests/tools/badpacket/tests/Makefile
+                 tests/tools/Makefile
                  tests/tools/perfdhcp/Makefile
                  tests/tools/perfdhcp/tests/Makefile
                  tests/tools/perfdhcp/tests/testdata/Makefile
-                 tests/lettuce/Makefile
-                 m4macros/Makefile
-                 dns++.pc
-               ])
-AC_OUTPUT([doc/version.ent
-           src/bin/cfgmgr/b10-cfgmgr.py
-           src/bin/cfgmgr/tests/b10-cfgmgr_test.py
-           src/bin/cfgmgr/plugins/datasrc.spec.pre
-           src/bin/cmdctl/cmdctl.py
-           src/bin/cmdctl/run_b10-cmdctl.sh
-           src/bin/cmdctl/tests/cmdctl_test
-           src/bin/cmdctl/cmdctl.spec.pre
-           src/bin/dbutil/dbutil.py
-           src/bin/dbutil/run_dbutil.sh
-           src/bin/dbutil/tests/dbutil_test.sh
-           src/bin/ddns/ddns.py
-           src/bin/dhcp4/tests/marker_file.h
-           src/bin/dhcp4/tests/test_libraries.h
-           src/bin/dhcp6/tests/marker_file.h
-           src/bin/dhcp6/tests/test_libraries.h
-           src/bin/xfrin/tests/xfrin_test
-           src/bin/xfrin/xfrin.py
-           src/bin/xfrin/run_b10-xfrin.sh
-           src/bin/xfrout/xfrout.py
-           src/bin/xfrout/xfrout.spec.pre
-           src/bin/xfrout/tests/xfrout_test
-           src/bin/xfrout/tests/xfrout_test.py
-           src/bin/xfrout/run_b10-xfrout.sh
-           src/bin/resolver/resolver.spec.pre
-           src/bin/resolver/spec_config.h.pre
-           src/bin/zonemgr/zonemgr.py
-           src/bin/zonemgr/zonemgr.spec.pre
-           src/bin/zonemgr/tests/zonemgr_test
-           src/bin/zonemgr/run_b10-zonemgr.sh
-           src/bin/sysinfo/sysinfo.py
-           src/bin/sysinfo/run_sysinfo.sh
-           src/bin/stats/stats.py
-           src/bin/stats/stats_httpd.py
-           src/bin/bind10/init.py
-           src/bin/bind10/run_bind10.sh
-           src/bin/bind10/tests/init_test.py
-           src/bin/bindctl/run_bindctl.sh
-           src/bin/bindctl/bindctl_main.py
-           src/bin/bindctl/tests/bindctl_test
-           src/bin/loadzone/run_loadzone.sh
-           src/bin/loadzone/tests/correct/correct_test.sh
-           src/bin/loadzone/loadzone.py
-           src/bin/usermgr/run_b10-cmdctl-usermgr.sh
-           src/bin/usermgr/b10-cmdctl-usermgr.py
-           src/bin/memmgr/memmgr.py
-           src/bin/memmgr/memmgr.spec.pre
-           src/bin/msgq/msgq.py
-           src/bin/msgq/run_msgq.sh
-           src/bin/auth/auth.spec.pre
-           src/bin/auth/spec_config.h.pre
-           src/bin/auth/tests/testdata/example.zone
-           src/bin/auth/tests/testdata/example-base.zone
-           src/bin/auth/tests/testdata/example-nsec3.zone
-           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
-           src/lib/python/isc/cc/tests/cc_test
-           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/dns/gen-rdatacode.py
-           src/lib/python/bind10_config.py
-           src/lib/cc/session_config.h.pre
-           src/lib/cc/tests/session_unittests_config.h
-           src/lib/datasrc/datasrc_config.h.pre
-           src/lib/hooks/tests/marker_file.h
-           src/lib/hooks/tests/test_libraries.h
-           src/lib/log/tests/console_test.sh
-           src/lib/log/tests/destination_test.sh
-           src/lib/log/tests/init_logger_test.sh
-           src/lib/log/tests/buffer_logger_test.sh
-           src/lib/log/tests/local_file_test.sh
-           src/lib/log/tests/logger_lock_test.sh
-           src/lib/log/tests/severity_test.sh
-           src/lib/log/tests/tempdir.h
-           src/lib/util/python/doxygen2pydoc.py
-           src/lib/util/python/mkpywrapper.py
-           src/lib/util/python/gen_wiredata.py
-           src/lib/server_common/tests/data_path.h
-           tests/lettuce/setup_intree_bind10.sh
-          ], [
-           chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
-           chmod +x src/bin/xfrin/run_b10-xfrin.sh
-           chmod +x src/bin/xfrout/run_b10-xfrout.sh
-           chmod +x src/bin/zonemgr/run_b10-zonemgr.sh
+])
+
+ AC_CONFIG_COMMANDS([permissions], [
            chmod +x src/bin/bind10/bind10
            chmod +x src/bin/bind10/run_bind10.sh
+           chmod +x src/bin/bindctl/run_bindctl.sh
+           chmod +x src/bin/bindctl/tests/bindctl_test
+           chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
            chmod +x src/bin/cmdctl/tests/cmdctl_test
            chmod +x src/bin/dbutil/run_dbutil.sh
            chmod +x src/bin/dbutil/tests/dbutil_test.sh
-           chmod +x src/bin/xfrin/tests/xfrin_test
-           chmod +x src/bin/xfrout/tests/xfrout_test
-           chmod +x src/bin/zonemgr/tests/zonemgr_test
-           chmod +x src/bin/bindctl/tests/bindctl_test
-           chmod +x src/bin/bindctl/run_bindctl.sh
            chmod +x src/bin/loadzone/run_loadzone.sh
            chmod +x src/bin/loadzone/tests/correct/correct_test.sh
+           chmod +x src/bin/msgq/run_msgq.sh
            chmod +x src/bin/sysinfo/run_sysinfo.sh
            chmod +x src/bin/usermgr/run_b10-cmdctl-usermgr.sh
-           chmod +x src/bin/msgq/run_msgq.sh
+           chmod +x src/bin/xfrin/run_b10-xfrin.sh
+           chmod +x src/bin/xfrin/tests/xfrin_test
+           chmod +x src/bin/xfrout/run_b10-xfrout.sh
+           chmod +x src/bin/xfrout/tests/xfrout_test
+           chmod +x src/bin/zonemgr/run_b10-zonemgr.sh
+           chmod +x src/bin/zonemgr/tests/zonemgr_test
            chmod +x src/lib/dns/gen-rdatacode.py
            chmod +x src/lib/log/tests/console_test.sh
            chmod +x src/lib/log/tests/destination_test.sh
@@ -1527,11 +1605,12 @@ AC_OUTPUT([doc/version.ent
            chmod +x src/lib/log/tests/local_file_test.sh
            chmod +x src/lib/log/tests/logger_lock_test.sh
            chmod +x src/lib/log/tests/severity_test.sh
+           chmod +x src/lib/python/isc/log/tests/log_console.py
            chmod +x src/lib/util/python/doxygen2pydoc.py
-           chmod +x src/lib/util/python/mkpywrapper.py
            chmod +x src/lib/util/python/gen_wiredata.py
-           chmod +x src/lib/python/isc/log/tests/log_console.py
-          ])
+           chmod +x src/lib/util/python/mkpywrapper.py
+])
+
 AC_OUTPUT
 
 dnl Print the results
@@ -1543,39 +1622,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
 
@@ -1602,4 +1711,8 @@ cat <<EOF
   run "make check", you must run "make" first as some files need to be
   generated by "make" before "make check" can be run.
 
+  When running "make install" do not use any form of parallel or job
+  server options (such as GNU make's -j option). Doing so may cause
+  errors.
+
 EOF
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 9967df2..c2aec85 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -667,6 +667,7 @@ INPUT                  = ../src/bin/auth \
                          ../src/bin/dhcp6 \
                          ../src/bin/resolver \
                          ../src/bin/sockcreator \
+                         ../src/hooks/dhcp/user_chk \
                          ../src/lib/acl \
                          ../src/lib/asiolink \
                          ../src/lib/bench \
diff --git a/doc/design/resolver/03-cache-algorithm b/doc/design/resolver/03-cache-algorithm
index 42bfa09..261d824 100644
--- a/doc/design/resolver/03-cache-algorithm
+++ b/doc/design/resolver/03-cache-algorithm
@@ -20,3 +20,237 @@ which algorithm is best for that. If it is very critical, then a
 custom algorithm designed for DNS caching makes sense. If it is not,
 then we can consider using an STL-based data structure.
 
+Effectiveness of Cache
+----------------------
+
+First, I'll try to answer the introductory questions.
+
+In some simplified model, we can express the amount of running time
+for answering queries directly from the cache in the total running
+time including that used for recursive resolution due to cache miss as
+follows:
+
+A = r*Q2*/(r*Q2+ Q1*(1-r))
+where
+A: amount of time for answering queries from the cache per unit time
+   (such as sec, 0<=A<=1)
+r: cache hit rate (0<=r<=1)
+Q1: max qps of the server with 100% cache hit
+Q2: max qps of the server with 0% cache hit
+
+Q1 can be measured easily for given data set; measuring Q2 is tricky
+in general (it requires many external queries with unreliable
+results), but we can still have some not-so-unrealistic numbers
+through controlled simulation.
+
+As a data point for these values, see a previous experimental results
+of mine:
+https://lists.isc.org/pipermail/bind10-dev/2012-July/003628.html
+
+Looking at the "ideal" server implementation (no protocol overhead)
+with the set up 90% and 85% cache hit rate with 1 recursion on cache
+miss, and with the possible maximum total throughput, we can deduce
+Q1 and Q2, which are: 170591qps and 60138qps respectively.
+
+This means, with 90% cache hit rate (r = 0.9), the server would spend
+76% of its run time for receiving queries and answering responses
+directly from the cache: 0.9*60138/(0.9*60138 + 0.1*170591) = 0.76.
+
+I also ran more realistic experiments: using BIND 9.9.2 and unbound
+1.4.19 in the "forward only" mode with crafted query data and the
+forwarded server to emulate the situation of 100% and 0% cache hit
+rates.  I then measured the max response throughput using a
+queryperf-like tool.  In both cases Q2 is about 28% of Q1 (I'm not
+showing specific numbers to avoid unnecessary discussion about
+specific performance of existing servers; it's out of scope of this
+memo).  Using Q2 = 0.28*Q1, above equation with 90% cache hit rate
+will be: A = 0.9 * 0.28 / (0.9*0.28 + 0.1) = 0.716. So the server will
+spend about 72% of its running time to answer queries directly from
+the cache.
+
+Of course, these experimental results are too simplified.  First, in
+these experiments we assumed only one external query is needed on
+cache miss.  In general it can be more; however, it may not actually
+be too optimistic either: in my another research result:
+http://bind10.isc.org/wiki/ResolverPerformanceResearch
+In the more detailed analysis using real query sample and tracing what
+an actual resolver would do, it looked we'd need about 1.44 to 1.63
+external queries per cache miss in average.
+
+Still, of course, the real world cases are not that simple: in reality
+we'd need to deal with timeouts, slower remote servers, unexpected
+intermediate results, etc.  DNSSEC validating resolvers will clearly
+need to do more work.
+
+So, in the real world deployment Q2 should be much smaller than Q1.
+Here are some specific cases of the relationship between Q1 and Q2 for
+given A (assuming r = 0.9):
+
+70%: Q2 = 0.26 * Q1
+60%: Q2 = 0.17 * Q1
+50%: Q2 = 0.11 * Q1
+
+So, even if "recursive resolution is 10 times heavier" than the cache
+only case, we can assume the server spends a half of its run time for
+answering queries directly from the cache at the cache hit rate of
+90%.  I think this is a reasonably safe assumption.
+
+Now, assuming the number of 50% or more, does this suggest we should
+highly optimize the cache?  Opinions may vary on this point, but I
+personally think the answer is yes.  I've written an experimental
+cache only implementation that employs the idea of fully-rendered
+cached data.  On one test machine (2.20GHz AMD64, using a single
+core), queryperf-like benchmark shows it can handle over 180Kqps,
+while BIND 9.9.2 can just handle 41K qps.  The experimental
+implementation skips some necessary features for a production server,
+and cache management itself is always inevitable bottleneck, so the
+production version wouldn't be that fast, but it still suggests it may
+not be very difficult to reach over 100Kqps in production environment
+including recursive resolution overhead.
+
+Cache Types
+-----------
+
+1. Record cache
+
+Conceptually, any recursive resolver (with cache) implementation would
+have cache for RRs (or RRsets in the modern version of protocol) given
+in responses to its external queries.  In BIND 9, it's called the
+"cached DB", using an in-memory rbt-like tree.  unbound calls it
+"rrset cache", which is implemented as a hash table.
+
+2. Delegation cache
+
+Recursive server implementations would also have cache to determine
+the deepest zone cut for a given query name in the recursion process.
+Neither BIND 9 nor unbound has a separate cache for this purpose;
+basically they try to find an NR RRset from the "record cache" whose
+owner name best matches the given query name.
+
+3. Remote server cache
+
+In addition, a recursive server implementation may maintain a cache
+for information of remote authoritative servers.  Both BIND 9 and
+unbound conceptually have this type of cache, although there are some
+non-negligible differences in details.  BIND 9's implementation of
+this cache is called ADB.  Its a hash table whose key is domain name,
+and each entry stores corresponding IPv6/v4 addresses; another data
+structure for each address stores averaged RTT for the address,
+lameness information, EDNS availability, etc.  unbound's
+implementation is called "infrastructure cache".  It's a hash table
+keyed with IP addresses whose entries store similar information as
+that in BIND 9's per address ADB entry.  In unbound a remote server's
+address must be determined by looking up the record cache (rrset cache
+in unbound terminology); unlike BIND 9's ADB, there's no direct
+shortcut from a server's domain name to IP addresses.
+
+4. Full response cache
+
+unbound has an additional cache layer, called the "message cache".
+It's a hash table whose hash key is query parameter (essentially qname
+and type) and entry is a sequence to record (rrset) cache entries.
+This sequence constructs a complete response to the corresponding
+query, so it would help optimize building a response message skipping
+the record cache for each section (answer/authority/additional) of the
+response message.  PowerDNS recursor has (seemingly) the same concept
+called "packet cache" (but I don't know its implementation details
+very much).
+
+BIND 9 doesn't have this type of cache; it always looks into the
+record cache to build a complete response to a given query.
+
+Miscellaneous General Requirements
+----------------------------------
+
+- Minimize contention between threads (if threaded)
+- Cache purge policy: normally only a very small part of cached DNS
+  information will be reused, and those reused are very heavily
+  reused.  So LRU-like algorithm should generally work well, but we'll
+  also need to honor DNS TTL.
+
+Random Ideas for BIND 10
+------------------------
+
+Below are specific random ideas for BIND 10.  Some are based on
+experimental results with reasonably realistic data; some others are
+mostly a guess.
+
+1. Fully rendered response cache
+
+Some real world query samples show that a very small portion of entire
+queries are very popular and queried very often and many times; the
+rest is rarely reused, if any.  Two different data sets show top
+10,000 queries would cover around 80% of total queries, regardless
+of the size of the total queries.  This suggests an idea of having a
+small, highly optimized full response cache.
+
+I tried this idea in the jinmei-l1cache branch.  It's a hash table
+keyed with a tuple of query name and type whose entry stores fully
+rendered, wire-format response image (answer section only, assuming
+the "minimal-responses" option).  It also maintains offsets to each
+RR, so it can easily update TTLs when necessary or rotate RRs if
+optionally requested.  If neither TTL adjustment nor RR rotation is
+required, query handling is just to lookup the hash table and copy the
+pre-rendered data.  Experimental benchmark showed it ran vary fast;
+more than 4 times faster than BIND 9, and even much faster than other
+implementations that have full response cache (although, as usual, the
+comparison is not entirely fair).
+
+Also, the cache size is quite small; the run time memory footprint of
+this server process was just about 5MB.  So, I think it's reasonable
+to have each process/thread have their own copy of this cache to
+completely eliminate contention.  Also, if we can keep the cache size
+this small, it would be easier to dump it to a file on shutdown and
+reuse it on restart.  This will be quite effective (if the downtime is
+reasonably short) because the cached data are expected to be highly
+popular.
+
+2. Record cache
+
+For the normal record cache, I don't have a particular idea beyond
+something obvious, like a hash table to map from query parameters to
+corresponding RRset (or negative information).  But I guess this cache
+should be shared by multiple threads.  That will help reconstruct the
+full response cache data on TTL expiration more efficiently.  And, if
+shared, the data structure should be chosen so that contention
+overhead can be minimized.  In general, I guess something like hash
+tables is more suitable than tree-like structure in that sense.
+
+There's other points to discuss for this cache related to other types
+of cache (see below).
+
+3. Separate delegation cache
+
+One thing I'm guessing is that it may make sense if we have a separate
+cache structure for delegation data.  It's conceptually a set of NS
+RRs so we can identify the best (longest) matching one for a given
+query name.
+
+Analysis of some sets of query data showed the vast majority of
+end client's queries are for A and AAAA (not surprisingly).  So, even
+if we separate this cache from the record cache, the additional
+overhead (both for memory and fetch) will probably (hopefully) be
+marginal.  Separating caches will also help reduce contention between
+threads.  It *might* also help improve lookup performance because this
+can be optimized for longest match search.
+
+4. Remote server cache without involving the record cache
+
+Likewise, it may make sense to maintain the remote server cache
+separately from the record cache.  I guess these AAAA and A records
+are rarely the queried by end clients, so, like the case of delegation
+cache it's possible that the data sets are mostly disjoint.  Also, for
+this purpose the RRsets don't have to have higher trust rank (per
+RFC2181 5.4.1): glue or additional are okay, and, by separating these
+from the record cache, we can avoid accidental promotion of these data
+to trustworthy answers and returning them to clients (BIND 9 had this
+type of bugs before).
+
+Custom vs Existing Library (STL etc)
+------------------------------------
+
+It may have to be discussed, but I guess in many cases we end up
+introducing custom implementation because these caches should be
+highly performance sensitive, directly related to our core business, and
+also have to be memory efficient.  But in some sub-components we may
+be able to benefit from existing generic libraries.
diff --git a/doc/design/resolver/03-cache-algorithm.txt b/doc/design/resolver/03-cache-algorithm.txt
deleted file mode 100644
index 4aacc4d..0000000
--- a/doc/design/resolver/03-cache-algorithm.txt
+++ /dev/null
@@ -1,256 +0,0 @@
-03-cache-algorithm
-
-Introduction
-------------
-Cache performance may be important for the resolver. It might not be
-critical. We need to research this.
-
-One key question is: given a specific cache hit rate, how much of an
-impact does cache performance have?
-
-For example, if we have 90% cache hit rate, will we still be spending
-most of our time in system calls or in looking things up in our cache?
-
-There are several ways we can consider figuring this out, including
-measuring this in existing resolvers (BIND 9, Unbound) or modeling
-with specific values.
-
-Once we know how critical the cache performance is, we can consider
-which algorithm is best for that. If it is very critical, then a
-custom algorithm designed for DNS caching makes sense. If it is not,
-then we can consider using an STL-based data structure.
-
-Effectiveness of Cache
-----------------------
-
-First, I'll try to answer the introductory questions.
-
-In some simplified model, we can express the amount of running time
-for answering queries directly from the cache in the total running
-time including that used for recursive resolution due to cache miss as
-follows:
-
-A = r*Q2*/(r*Q2+ Q1*(1-r))
-where
-A: amount of time for answering queries from the cache per unit time
-   (such as sec, 0<=A<=1)
-r: cache hit rate (0<=r<=1)
-Q1: max qps of the server with 100% cache hit
-Q2: max qps of the server with 0% cache hit
-
-Q1 can be measured easily for given data set; measuring Q2 is tricky
-in general (it requires many external queries with unreliable
-results), but we can still have some not-so-unrealistic numbers
-through controlled simulation.
-
-As a data point for these values, see a previous experimental results
-of mine:
-https://lists.isc.org/pipermail/bind10-dev/2012-July/003628.html
-
-Looking at the "ideal" server implementation (no protocol overhead)
-with the set up 90% and 85% cache hit rate with 1 recursion on cache
-miss, and with the possible maximum total throughput, we can deduce
-Q1 and Q2, which are: 170591qps and 60138qps respectively.
-
-This means, with 90% cache hit rate (r = 0.9), the server would spend
-76% of its run time for receiving queries and answering responses
-directly from the cache: 0.9*60138/(0.9*60138 + 0.1*170591) = 0.76.
-
-I also ran more realistic experiments: using BIND 9.9.2 and unbound
-1.4.19 in the "forward only" mode with crafted query data and the
-forwarded server to emulate the situation of 100% and 0% cache hit
-rates.  I then measured the max response throughput using a
-queryperf-like tool.  In both cases Q2 is about 28% of Q1 (I'm not
-showing specific numbers to avoid unnecessary discussion about
-specific performance of existing servers; it's out of scope of this
-memo).  Using Q2 = 0.28*Q1, above equation with 90% cache hit rate
-will be: A = 0.9 * 0.28 / (0.9*0.28 + 0.1) = 0.716. So the server will
-spend about 72% of its running time to answer queries directly from
-the cache.
-
-Of course, these experimental results are too simplified.  First, in
-these experiments we assumed only one external query is needed on
-cache miss.  In general it can be more; however, it may not actually
-be too optimistic either: in my another research result:
-http://bind10.isc.org/wiki/ResolverPerformanceResearch
-In the more detailed analysis using real query sample and tracing what
-an actual resolver would do, it looked we'd need about 1.44 to 1.63
-external queries per cache miss in average.
-
-Still, of course, the real world cases are not that simple: in reality
-we'd need to deal with timeouts, slower remote servers, unexpected
-intermediate results, etc.  DNSSEC validating resolvers will clearly
-need to do more work.
-
-So, in the real world deployment Q2 should be much smaller than Q1.
-Here are some specific cases of the relationship between Q1 and Q2 for
-given A (assuming r = 0.9):
-
-70%: Q2 = 0.26 * Q1
-60%: Q2 = 0.17 * Q1
-50%: Q2 = 0.11 * Q1
-
-So, even if "recursive resolution is 10 times heavier" than the cache
-only case, we can assume the server spends a half of its run time for
-answering queries directly from the cache at the cache hit rate of
-90%.  I think this is a reasonably safe assumption.
-
-Now, assuming the number of 50% or more, does this suggest we should
-highly optimize the cache?  Opinions may vary on this point, but I
-personally think the answer is yes.  I've written an experimental
-cache only implementation that employs the idea of fully-rendered
-cached data.  On one test machine (2.20GHz AMD64, using a single
-core), queryperf-like benchmark shows it can handle over 180Kqps,
-while BIND 9.9.2 can just handle 41K qps.  The experimental
-implementation skips some necessary features for a production server,
-and cache management itself is always inevitable bottleneck, so the
-production version wouldn't be that fast, but it still suggests it may
-not be very difficult to reach over 100Kqps in production environment
-including recursive resolution overhead.
-
-Cache Types
------------
-
-1. Record cache
-
-Conceptually, any recursive resolver (with cache) implementation would
-have cache for RRs (or RRsets in the modern version of protocol) given
-in responses to its external queries.  In BIND 9, it's called the
-"cached DB", using an in-memory rbt-like tree.  unbound calls it
-"rrset cache", which is implemented as a hash table.
-
-2. Delegation cache
-
-Recursive server implementations would also have cache to determine
-the deepest zone cut for a given query name in the recursion process.
-Neither BIND 9 nor unbound has a separate cache for this purpose;
-basically they try to find an NR RRset from the "record cache" whose
-owner name best matches the given query name.
-
-3. Remote server cache
-
-In addition, a recursive server implementation may maintain a cache
-for information of remote authoritative servers.  Both BIND 9 and
-unbound conceptually have this type of cache, although there are some
-non-negligible differences in details.  BIND 9's implementation of
-this cache is called ADB.  Its a hash table whose key is domain name,
-and each entry stores corresponding IPv6/v4 addresses; another data
-structure for each address stores averaged RTT for the address,
-lameness information, EDNS availability, etc.  unbound's
-implementation is called "infrastructure cache".  It's a hash table
-keyed with IP addresses whose entries store similar information as
-that in BIND 9's per address ADB entry.  In unbound a remote server's
-address must be determined by looking up the record cache (rrset cache
-in unbound terminology); unlike BIND 9's ADB, there's no direct
-shortcut from a server's domain name to IP addresses.
-
-4. Full response cache
-
-unbound has an additional cache layer, called the "message cache".
-It's a hash table whose hash key is query parameter (essentially qname
-and type) and entry is a sequence to record (rrset) cache entries.
-This sequence constructs a complete response to the corresponding
-query, so it would help optimize building a response message skipping
-the record cache for each section (answer/authority/additional) of the
-response message.  PowerDNS recursor has (seemingly) the same concept
-called "packet cache" (but I don't know its implementation details
-very much).
-
-BIND 9 doesn't have this type of cache; it always looks into the
-record cache to build a complete response to a given query.
-
-Miscellaneous General Requirements
-----------------------------------
-
-- Minimize contention between threads (if threaded)
-- Cache purge policy: normally only a very small part of cached DNS
-  information will be reused, and those reused are very heavily
-  reused.  So LRU-like algorithm should generally work well, but we'll
-  also need to honor DNS TTL.
-
-Random Ideas for BIND 10
-------------------------
-
-Below are specific random ideas for BIND 10.  Some are based on
-experimental results with reasonably realistic data; some others are
-mostly a guess.
-
-1. Fully rendered response cache
-
-Some real world query samples show that a very small portion of entire
-queries are very popular and queried very often and many times; the
-rest is rarely reused, if any.  Two different data sets show top
-10,000 queries would cover around 80% of total queries, regardless
-of the size of the total queries.  This suggests an idea of having a
-small, highly optimized full response cache.
-
-I tried this idea in the jinmei-l1cache branch.  It's a hash table
-keyed with a tuple of query name and type whose entry stores fully
-rendered, wire-format response image (answer section only, assuming
-the "minimal-responses" option).  It also maintains offsets to each
-RR, so it can easily update TTLs when necessary or rotate RRs if
-optionally requested.  If neither TTL adjustment nor RR rotation is
-required, query handling is just to lookup the hash table and copy the
-pre-rendered data.  Experimental benchmark showed it ran vary fast;
-more than 4 times faster than BIND 9, and even much faster than other
-implementations that have full response cache (although, as usual, the
-comparison is not entirely fair).
-
-Also, the cache size is quite small; the run time memory footprint of
-this server process was just about 5MB.  So, I think it's reasonable
-to have each process/thread have their own copy of this cache to
-completely eliminate contention.  Also, if we can keep the cache size
-this small, it would be easier to dump it to a file on shutdown and
-reuse it on restart.  This will be quite effective (if the downtime is
-reasonably short) because the cached data are expected to be highly
-popular.
-
-2. Record cache
-
-For the normal record cache, I don't have a particular idea beyond
-something obvious, like a hash table to map from query parameters to
-corresponding RRset (or negative information).  But I guess this cache
-should be shared by multiple threads.  That will help reconstruct the
-full response cache data on TTL expiration more efficiently.  And, if
-shared, the data structure should be chosen so that contention
-overhead can be minimized.  In general, I guess something like hash
-tables is more suitable than tree-like structure in that sense.
-
-There's other points to discuss for this cache related to other types
-of cache (see below).
-
-3. Separate delegation cache
-
-One thing I'm guessing is that it may make sense if we have a separate
-cache structure for delegation data.  It's conceptually a set of NS
-RRs so we can identify the best (longest) matching one for a given
-query name.
-
-Analysis of some sets of query data showed the vast majority of
-end client's queries are for A and AAAA (not surprisingly).  So, even
-if we separate this cache from the record cache, the additional
-overhead (both for memory and fetch) will probably (hopefully) be
-marginal.  Separating caches will also help reduce contention between
-threads.  It *might* also help improve lookup performance because this
-can be optimized for longest match search.
-
-4. Remote server cache without involving the record cache
-
-Likewise, it may make sense to maintain the remote server cache
-separately from the record cache.  I guess these AAAA and A records
-are rarely the queried by end clients, so, like the case of delegation
-cache it's possible that the data sets are mostly disjoint.  Also, for
-this purpose the RRsets don't have to have higher trust rank (per
-RFC2181 5.4.1): glue or additional are okay, and, by separating these
-from the record cache, we can avoid accidental promotion of these data
-to trustworthy answers and returning them to clients (BIND 9 had this
-type of bugs before).
-
-Custom vs Existing Library (STL etc)
-------------------------------------
-
-It may have to be discussed, but I guess in many cases we end up
-introducing custom implementation because these caches should be
-highly performance sensitive, directly related our core business, and
-also have to be memory efficient.  But in some sub components we may
-be able to benefit from existing generic libraries.
diff --git a/doc/devel/contribute.dox b/doc/devel/contribute.dox
new file mode 100644
index 0000000..9103bed
--- /dev/null
+++ b/doc/devel/contribute.dox
@@ -0,0 +1,162 @@
+// 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.
+
+/**
+
+ @page contributorGuide BIND10 Contributor's Guide
+
+So you found a bug in BIND10 or plan to develop an extension and want to
+send a patch? Great! This page will explain how to contribute your
+changes smoothly.
+
+ at section contributorGuideWritePatch Writing a patch
+
+Before you start working on a patch or a new feature, it is a good idea
+to discuss it first with BIND10 developers. You can post your questions
+to the \c bind10-dev mailing list
+(https://lists.isc.org/mailman/listinfo/bind10-dev) for general BIND10
+stuff, or to the \c bind10-dhcp mailing list
+(https://lists.isc.org/mailman/listinfo/bind10-dhcp) for DHCP specific
+topics. If you prefer to get faster feedback, most BIND10 developers
+hang out in the \c bind10 jabber room
+(xmpp:bind10 at conference.jabber.isc.org). Those involved in DHCP also use
+the \c dhcp chatroom (xmpp:dhcp at conference.jabber.isc.org). Feel free to
+join these rooms and talk to us. It is possible that someone else is
+working on your specific issue or perhaps the solution you plan to
+implement is not the best one. Often having a 10 minute talk could save
+many hours of engineering work.
+
+First step would be to get the source code from our Git repository. The
+procedure is very easy and is explained here:
+http://bind10.isc.org/wiki/GitGuidelines.  While it is possible to
+provide a patch against the latest stable release, it makes the review
+process much easier if it is for latest code from the Git \c master
+branch.
+
+Ok, so you have written a patch? Great! Before you submit it, make sure
+that your code compiles. This may seem obvious, but there's more to
+it. You have surely checked that it compiles on your system, but BIND10
+is portable software. Besides Linux, it is compiled and used on
+relatively uncommon systems like OpenBSD and Solaris 11. Will your code
+compile and work there? What about endianess? It is likely that you used
+a regular x86 architecture machine to write your patch, but the software
+is expected to run on many other architectures. You may take a look at
+system specific build notes (http://bind10.isc.org/wiki/SystemSpecificNotes).
+For a complete list of systems we build on, you may take a look at the
+following build farm report: http://git.bind10.isc.org/~tester/builder/builder-new.html .
+
+Does your patch conform to BIND10 coding guidelines
+(http://bind10.isc.org/wiki/CodingGuidelines)? You still can submit a
+patch that does not adhere to it, but that will decrease its chances of
+being accepted. If the deviations are minor, the BIND10 engineer who
+does the review will likely fix the issues. However, if there are lots
+of issues, the reviewer may simply reject the patch and ask you to fix
+it before re-submitting.
+
+ at section contributorGuideUnittests Running unit-tests
+
+One of the ground rules in BIND10 development is that every piece of
+code has to be tested. We now have an extensive set of unit-tests for
+almost every line of code. Even if you are fixing something small,
+like a single line fix, it is encouraged to write unit-tests for that
+change. That is even more true for new code. If you write a new
+function, method or a class, you definitely should write unit-tests
+for it.
+
+BIND10 uses the Google C++ Testing Framework (also called googletest or
+gtest) as a base for our C++ unit-tests. See
+http://code.google.com/p/googletest/ for details. For Python unit-tests,
+we use the its \c unittest library which is included in Python. You must
+have \c gtest installed or at least extracted in a directory before
+compiling BIND10 unit-tests. To enable unit-tests in BIND10, use:
+
+ at code
+./configure --with-gtest=/path/to/your/gtest/dir
+ at endcode
+
+or
+
+ at code
+./configure --with-gtest-source=/path/to/your/gtest/dir
+ at endcode
+
+Depending on how you compiled or installed \c gtest (e.g. from sources
+or using some package management system) one of those two switches will
+find \c gtest. After that you make run unit-tests:
+
+ at code
+make check
+ at endcode
+
+If you happen to add new files or have modified any \c Makefile.am
+files, it is also a good idea to check if you haven't broken the
+distribution process:
+
+ at code
+make distcheck
+ at endcode
+
+There are other useful switches which can be passed to configure. It is
+always a good idea to use \c --enable-logger-checks, which does sanity
+checks on logger parameters. If you happen to modify anything in the
+documentation, use \c --enable-generate-docs. If you are modifying DHCP
+code, you are likely to be interested in enabling the MySQL backend for
+DHCP. Note that if the backend is not enabled, MySQL specific unit-tests
+are skipped. From that perspective, it is useful to use
+\c --with-dhcp-mysql. For a complete list of all switches, use:
+
+ at code
+ ./configure --help
+ at endcode
+
+ at section contributorGuideReview Going through a review
+
+Once all those are checked and working, feel free to create a ticket for
+your patch at http://bind10.isc.org/ or attach your patch to an existing
+ticket if you have fixed it. It would be nice if you also join the
+\c bind10 or \c dhcp chatroom saying that you have submitted a
+patch. Alternatively, you may send a note to the \c bind10-dev or
+\c bind10-dhcp mailing lists.
+
+Here's the tricky part. One of BIND10 developers will review your patch,
+but it may not happen immediately. Unfortunately, developers are usually
+working under a tight schedule, so any extra unplanned review work may
+take a while sometimes. Having said that, we value external
+contributions very much and will do whatever we can to review patches in
+a timely manner. Don't get discouraged if your patch is not accepted
+after first review. To keep the code quality high, we use the same
+review processes for internal code and for external patches. It may take
+some cycles of review/updated patch submissions before the code is
+finally accepted.
+
+Once the process is almost complete, the developer will likely ask you
+how you would like to be credited. The typical answers are by first and
+last name, by nickname, by company name or anonymously. Typically we
+will add a note to the \c ChangeLog and also set you as the author of
+the commit applying the patch. If the contributted feature is big or
+critical for whatever reason, it may also be mentioned in release notes.
+
+ at section contributorGuideExtra Extra steps
+
+If you are interested in knowing the results of more in-depth testing,
+you are welcome to visit the BIND10 build farm:
+http://git.bind10.isc.org/~tester/builder/builder-new.html.  This is a
+live result page with all tests being run on various systems.  Besides
+basic unit-tests, we also have reports from Valgrind (memory debugger),
+cppcheck and clang-analyzer (static code analyzers), Lettuce system
+tests and more. Although it is not possible for non ISC employees to run
+tests on that farm, it is possible that your contributed patch will end
+up there sooner or later.
+
+*/
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
index 97b722a..64e2782 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
@@ -36,12 +36,17 @@
  *
  * Regardless of your field of expertise, you are encouraged to visit the
  * <a href="http://bind10.isc.org/">BIND10 webpage (http://bind10.isc.org)</a>
+ *
+ * @section contrib Contributor's Guide
+ * - @subpage contributorGuide
+ *
  * @section hooksFramework Hooks Framework
  * - @subpage hooksdgDevelopersGuide
  * - @subpage dhcpv4Hooks
  * - @subpage dhcpv6Hooks
  * - @subpage hooksComponentDeveloperGuide
  * - @subpage hooksmgMaintenanceGuide
+ * - @subpage libdhcp_user_chk
  *
  * @section dnsMaintenanceGuide DNS Maintenance Guide
  * - Authoritative DNS (todo)
@@ -54,6 +59,7 @@
  *   - @subpage dhcpv4ConfigParser
  *   - @subpage dhcpv4ConfigInherit
  *   - @subpage dhcpv4OptionsParse
+ *   - @subpage dhcpv4DDNSIntegration
  *   - @subpage dhcpv4Other
  * - @subpage dhcp6
  *   - @subpage dhcpv6Session
@@ -66,6 +72,7 @@
  *   - @subpage libdhcpIntro
  *   - @subpage libdhcpRelay
  *   - @subpage libdhcpIfaceMgr
+ *   - @subpage libdhcpPktFilter
  * - @subpage libdhcpsrv
  *   - @subpage leasemgr
  *   - @subpage cfgmgr
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 bc7cc35..d915e27 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -836,6 +836,11 @@ as a dependency earlier -->
           and documentation, run:
           <screen>$ <userinput>make install</userinput></screen>
         </para>
+        <para>
+          Please don't use any form of parallel or job server options
+          (such as GNU make's <command>-j</command> option) when
+          performing this step. Doing so may cause errors.
+        </para>
         <note>
           <para>The install step may require superuser privileges.</para>
         </note>
@@ -4414,6 +4419,29 @@ Dhcp4/subnet4	[]	list	(default)
 
     </section>
 
+    <section id="dhcp4-echo-client-id">
+      <title>Echoing client-id (RFC6842)</title>
+      <para>Original DHCPv4 spec (RFC2131) states that the DHCPv4
+      server must not send back client-id options when responding to
+      clients. However, in some cases that confused clients that did
+      not have MAC address or client-id. See RFC6842 for details. That
+      behavior has changed with the publication of RFC6842 which
+      updated RFC2131. That update now states that the server must
+      send client-id if client sent it. That is the default behaviour
+      that Kea offers. However, in some cases older devices that do
+      not support RFC6842 may refuse to accept responses that include
+      client-id option. To enable backward compatibility, an optional
+      configuration parameter has been introduced. To configure it,
+      use the following commands:</para>
+
+<screen>
+> <userinput>config add Dhcp4/echo-client-id</userinput>
+> <userinput>config set Dhcp4/echo-client-id False</userinput>
+> <userinput>config commit</userinput>
+</screen>
+
+    </section>
+
     <section id="dhcp4-std">
       <title>Supported Standards</title>
       <para>The following standards and draft standards are currently
@@ -4424,11 +4452,20 @@ Dhcp4/subnet4	[]	list	(default)
             REQUEST, RELEASE, ACK, and NAK.</simpara>
           </listitem>
           <listitem>
-            <simpara><ulink url="http://tools.ietf.org/html/rfc2132">RFC 2132</ulink>: Supported options are: PAD (0),
+            <simpara><ulink url="http://tools.ietf.org/html/rfc2132">RFC 2132</ulink>:
+            Supported options are: PAD (0),
             END(255), Message Type(53), DHCP Server Identifier (54),
             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>
+            <simpara><ulink url="http://tools.ietf.org/html/rfc6842">RFC 6842</ulink>:
+            Server by default sends back client-id option. That capability may be
+            disabled. See <xref linkend="dhcp4-echo-client-id"/> for details.
+            </simpara>
+          </listitem>
       </itemizedlist>
     </section>
 
@@ -4451,21 +4488,6 @@ Dhcp4/renew-timer	1000	integer	(default)
 > <userinput>config commit</userinput></screen>
           </para>
         </listitem>
-          <listitem>
-            <simpara>During the initial IPv4 node configuration, the
-            server is expected to send packets to a node that does not
-            have IPv4 address assigned yet. The server requires
-            certain tricks (or hacks) to transmit such packets. This
-            is not implemented yet, therefore DHCPv4 server supports
-            relayed traffic only (that is, normal point to point
-            communication).</simpara>
-          </listitem>
-
-          <listitem>
-            <simpara>Upon start, the server will open sockets on all
-            interfaces that are not loopback, are up and running and
-            have IPv4 address.</simpara>
-          </listitem>
 
           <listitem>
             <simpara>The DHCPv4 server does not support
@@ -4475,7 +4497,7 @@ Dhcp4/renew-timer	1000	integer	(default)
             available from <ulink url="http://www.isc.org/software/dhcp"/>.</simpara>
           </listitem>
           <listitem>
-            <simpara>Interface detection is currently working on Linux
+            <simpara>Raw sockets operation is working on Linux
             only. See <xref linkend="iface-detect"/> for details.</simpara>
           </listitem>
           <listitem>
@@ -5392,10 +5414,6 @@ Dhcp6/renew-timer	1000	integer	(default)
         <listitem>
           <simpara>DNS Update is not supported.</simpara>
         </listitem>
-        <listitem>
-          <simpara>Interface detection is currently working on Linux
-          only. See <xref linkend="iface-detect"/> for details.</simpara>
-        </listitem>
       </itemizedlist>
     </section>
 
@@ -5430,16 +5448,17 @@ Dhcp6/renew-timer	1000	integer	(default)
 <!-- TODO: point to doxygen docs -->
 
     <section id="iface-detect">
-      <title>Interface detection</title>
+      <title>Interface detection and Socket handling</title>
       <para>Both the DHCPv4 and DHCPv6 components share network
       interface detection routines. Interface detection is
-      currently only supported on Linux systems.</para>
-
-      <para>For non-Linux systems, there is currently a stub
-      implementation provided. The interface manager detects loopback
-      interfaces only as their name (lo or lo0) can be easily predicted.
-      Please contact the BIND 10 development team if you are interested
-      in running DHCP components on systems other than Linux.</para>
+      currently supported on Linux, all BSD family (FreeBSD, NetBSD,
+      OpenBSD), Mac OS X and Solaris 11 systems.</para>
+
+      <para>DHCPv4 requires special raw socket processing to send and receive
+      packets from hosts that do not have IPv4 address assigned yet. Support
+      for this operation is implemented on Linux only, so it is likely that
+      DHCPv4 component will not work in certain cases on systems other than
+      Linux.</para>
     </section>
 
 <!--
diff --git a/m4macros/ax_boost_for_bind10.m4 b/m4macros/ax_boost_for_bind10.m4
index 3045dfb..4e713c9 100644
--- a/m4macros/ax_boost_for_bind10.m4
+++ b/m4macros/ax_boost_for_bind10.m4
@@ -70,6 +70,10 @@ fi
 AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp boost/interprocess/sync/interprocess_upgradable_mutex.hpp boost/date_time/posix_time/posix_time_types.hpp boost/bind.hpp boost/function.hpp],,
   AC_MSG_ERROR([Missing required header files.]))
 
+# clang can cause false positives with -Werror without -Qunused-arguments.
+# it can be triggered if used with ccache.
+AC_CHECK_DECL([__clang__], [CLANG_CXXFLAGS="-Qunused-arguments"], [])
+
 # Detect whether Boost tries to use threads by default, and, if not,
 # make it sure explicitly.  In some systems the automatic detection
 # may depend on preceding header files, and if inconsistency happens
@@ -87,7 +91,7 @@ AC_TRY_COMPILE([
 # Boost offset_ptr is known to not compile on some platforms, depending on
 # boost version, its local configuration, and compiler.  Detect it.
 CXXFLAGS_SAVED="$CXXFLAGS"
-CXXFLAGS="$CXXFLAGS -Werror"
+CXXFLAGS="$CXXFLAGS $CLANG_CXXFLAGS -Werror"
 AC_MSG_CHECKING([Boost offset_ptr compiles])
 AC_TRY_COMPILE([
 #include <boost/interprocess/offset_ptr.hpp>
@@ -102,7 +106,7 @@ CXXFLAGS="$CXXFLAGS_SAVED"
 # FreeBSD ports
 if test "X$GXX" = "Xyes"; then
    CXXFLAGS_SAVED="$CXXFLAGS"
-   CXXFLAGS="$CXXFLAGS -Werror"
+   CXXFLAGS="$CXXFLAGS $CLANG_CXXFLAGS -Werror"
 
    AC_MSG_CHECKING([Boost numeric_cast compiles with -Werror])
    AC_TRY_COMPILE([
@@ -140,11 +144,9 @@ BOOST_MAPPED_FILE_CXXFLAG=
 CXXFLAGS_SAVED="$CXXFLAGS"
 try_flags="no"
 if test "X$GXX" = "Xyes"; then
-  CXXFLAGS="$CXXFLAGS -Wall -Wextra -Werror"
+  CXXFLAGS="$CXXFLAGS $CLANG_CXXFLAGS -Wall -Wextra -Werror"
   try_flags="$try_flags -Wno-error"
 fi
-# clang can cause false positives with -Werror without -Qunused-arguments
-AC_CHECK_DECL([__clang__], [CXXFLAGS="$CXXFLAGS -Qunused-arguments"], [])
 
 AC_MSG_CHECKING([Boost managed_mapped_file compiles])
 CXXFLAGS_SAVED2="$CXXFLAGS"
@@ -152,7 +154,7 @@ for flag in $try_flags; do
   if test "$flag" != no; then
     BOOST_MAPPED_FILE_CXXFLAG="$flag"
   fi
-  CXXFLAGS="$CXXFLAGS $BOOST_MAPPED_FILE_CXXFLAG"
+  CXXFLAGS="$CXXFLAGS $CLANG_CXXFLAGS $BOOST_MAPPED_FILE_CXXFLAG"
   AC_TRY_COMPILE([
   #include <boost/interprocess/managed_mapped_file.hpp>
   ],[
@@ -185,6 +187,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_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/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index ce5c02b..c0f9730 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -70,8 +70,6 @@
 
 using namespace std;
 
-using boost::shared_ptr;
-
 using namespace isc;
 using namespace isc::cc;
 using namespace isc::datasrc;
@@ -277,7 +275,7 @@ public:
     AddressList listen_addresses_;
 
     /// The TSIG keyring
-    const shared_ptr<TSIGKeyRing>* keyring_;
+    const boost::shared_ptr<TSIGKeyRing>* keyring_;
 
     /// The data source client list manager
     auth::DataSrcClientsMgr datasrc_clients_mgr_;
@@ -651,7 +649,7 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message,
 
     try {
         const ConstQuestionPtr question = *message.beginQuestion();
-        const shared_ptr<datasrc::ClientList>
+        const boost::shared_ptr<datasrc::ClientList>
             list(datasrc_holder.findClientList(question->getClass()));
         if (list) {
             const RRType& qtype = question->getType();
@@ -766,7 +764,7 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
     bool is_auth = false;
     {
         auth::DataSrcClientsMgr::Holder datasrc_holder(datasrc_clients_mgr_);
-        const shared_ptr<datasrc::ClientList> dsrc_clients =
+        const boost::shared_ptr<datasrc::ClientList> dsrc_clients =
             datasrc_holder.findClientList(question->getClass());
         is_auth = dsrc_clients &&
             dsrc_clients->find(question->getName(), true, false).exact_match_;
@@ -900,7 +898,7 @@ AuthSrv::setDNSService(isc::asiodns::DNSServiceBase& dnss) {
 }
 
 void
-AuthSrv::setTSIGKeyRing(const shared_ptr<TSIGKeyRing>* keyring) {
+AuthSrv::setTSIGKeyRing(const boost::shared_ptr<TSIGKeyRing>* keyring) {
     impl_->keyring_ = keyring;
 }
 
diff --git a/src/bin/auth/tests/datasrc_config_unittest.cc b/src/bin/auth/tests/datasrc_config_unittest.cc
index b555aa6..ca7e86e 100644
--- a/src/bin/auth/tests/datasrc_config_unittest.cc
+++ b/src/bin/auth/tests/datasrc_config_unittest.cc
@@ -30,7 +30,6 @@ using namespace isc::config;
 using namespace isc::data;
 using namespace isc::dns;
 using namespace std;
-using namespace boost;
 
 namespace {
 
@@ -57,7 +56,7 @@ private:
     ConstElementPtr configuration_;
 };
 
-typedef shared_ptr<FakeList> ListPtr;
+typedef boost::shared_ptr<FakeList> ListPtr;
 
 // Forward declaration.  We need precise definition of DatasrcConfigTest
 // to complete this function.
@@ -77,8 +76,8 @@ datasrcConfigHandler(DatasrcConfigTest* fake_server, const std::string&,
 
 class DatasrcConfigTest : public ::testing::Test {
 public:
-    void setDataSrcClientLists(shared_ptr<std::map<dns::RRClass, ListPtr> >
-                               new_lists)
+    void setDataSrcClientLists(boost::shared_ptr<std::map<dns::RRClass,
+                               ListPtr> > new_lists)
     {
         lists_.clear();         // first empty it
 
@@ -159,7 +158,7 @@ testConfigureDataSource(DatasrcConfigTest& test,
 {
     // We use customized (faked lists) for the List type.  This makes it
     // possible to easily look that they were called.
-    shared_ptr<std::map<dns::RRClass, ListPtr> > lists =
+    boost::shared_ptr<std::map<dns::RRClass, ListPtr> > lists =
         configureDataSourceGeneric<FakeList>(config);
     test.setDataSrcClientLists(lists);
 }
diff --git a/src/bin/bind10/init.py.in b/src/bin/bind10/init.py.in
index cb1847d..67491b3 100755
--- a/src/bin/bind10/init.py.in
+++ b/src/bin/bind10/init.py.in
@@ -490,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)
 
@@ -741,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
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/d2/Makefile.am b/src/bin/d2/Makefile.am
index f27fe38..1da489c 100644
--- a/src/bin/d2/Makefile.am
+++ b/src/bin/d2/Makefile.am
@@ -50,6 +50,7 @@ BUILT_SOURCES = spec_config.h d2_messages.h d2_messages.cc
 pkglibexec_PROGRAMS = b10-dhcp-ddns
 
 b10_dhcp_ddns_SOURCES  = main.cc
+b10_dhcp_ddns_SOURCES += d2_asio.h
 b10_dhcp_ddns_SOURCES += d2_log.cc d2_log.h
 b10_dhcp_ddns_SOURCES += d2_process.cc d2_process.h
 b10_dhcp_ddns_SOURCES += d_controller.cc d_controller.h
@@ -63,6 +64,8 @@ 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_add.cc nc_add.h
+b10_dhcp_ddns_SOURCES += nc_remove.cc nc_remove.h
 b10_dhcp_ddns_SOURCES += nc_trans.cc nc_trans.h
 b10_dhcp_ddns_SOURCES += state_model.cc state_model.h
 
@@ -76,6 +79,7 @@ b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libb10-dhcp_ddns.la
+b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
diff --git a/src/bin/d2/d2_asio.h b/src/bin/d2/d2_asio.h
new file mode 100644
index 0000000..c9458f6
--- /dev/null
+++ b/src/bin/d2/d2_asio.h
@@ -0,0 +1,31 @@
+// 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 D2_ASIO_H
+#define D2_ASIO_H
+
+#include <asiolink/asiolink.h>
+
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Defines a smart pointer to an IOService instance.
+typedef boost::shared_ptr<isc::asiolink::IOService> IOServicePtr;
+
+}; // namespace isc::d2
+}; // namespace isc
+
+#endif
diff --git a/src/bin/d2/d2_cfg_mgr.h b/src/bin/d2/d2_cfg_mgr.h
index d95a890..ec19d4c 100644
--- a/src/bin/d2/d2_cfg_mgr.h
+++ b/src/bin/d2/d2_cfg_mgr.h
@@ -15,9 +15,9 @@
 #ifndef D2_CFG_MGR_H
 #define D2_CFG_MGR_H
 
-#include <asiolink/io_address.h>
 #include <cc/data.h>
 #include <exceptions/exceptions.h>
+#include <d2/d2_asio.h>
 #include <d2/d_cfg_mgr.h>
 #include <d2/d2_config.h>
 
diff --git a/src/bin/d2/d2_config.h b/src/bin/d2/d2_config.h
index 8e3bd34..6eb975e 100644
--- a/src/bin/d2/d2_config.h
+++ b/src/bin/d2/d2_config.h
@@ -15,8 +15,8 @@
 #ifndef D2_CONFIG_H
 #define D2_CONFIG_H
 
-#include <asiolink/io_address.h>
 #include <cc/data.h>
+#include <d2/d2_asio.h>
 #include <d2/d_cfg_mgr.h>
 #include <dhcpsrv/dhcp_parsers.h>
 #include <exceptions/exceptions.h>
diff --git a/src/bin/d2/d2_messages.mes b/src/bin/d2/d2_messages.mes
index e25c122..d4ab054 100644
--- a/src/bin/d2/d2_messages.mes
+++ b/src/bin/d2/d2_messages.mes
@@ -138,7 +138,7 @@ error while decoding a response to DNS Update message. Typically, this error
 will be encountered when a response message is malformed.
 
 % DHCP_DDNS_NO_ELIGIBLE_JOBS although there are queued requests, there are pending transactions for each Queue count: %1  Transaction count: %2
-This is a debug messge issued when all of the queued requests represent clients
+This is a debug message issued when all of the queued requests represent clients
 for which there is a an update already in progress.  This may occur under
 normal operations but should be temporary situation.
 
@@ -190,7 +190,7 @@ indicate a network connectivity or system resource issue.
 
 % DHCP_DDNS_QUEUE_MGR_RESUME_ERROR application could not restart the queue manager, reason: %1
 This is an error message indicating that DHCP_DDNS's Queue Manager could not
-be restarted after stopping due to an a full receive queue.  This means that
+be restarted after stopping due to a full receive queue.  This means that
 the application cannot receive requests. This is most likely due to DHCP_DDNS
 configuration parameters referring to resources such as an IP address or port,
 that is no longer unavailable.  DHCP_DDNS will attempt to restart the queue
@@ -258,3 +258,183 @@ 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.
+
+% DHCP_DDNS_FORWARD_ADD_REJECTED DNS Server, %1, rejected a DNS update request to add the address mapping for FQDN, %2, with an RCODE: %3
+This is an error message issued when an update was rejected by the DNS server
+it was sent to for the reason given by the RCODE. The rcode values are defined
+in RFC 2136.
+
+% DHCP_DDNS_FORWARD_ADD_IO_ERROR DHCP_DDNS encountered an IO error sending a forward mapping add for FQDN %1 to DNS server %2
+This is an error message issued when a communication error occurs while
+DHCP_DDNS is carrying out a forward address update.  The application will
+retry against the same server or others as appropriate.
+
+% DHCP_DDNS_FORWARD_ADD_RESP_CORRUPT DHCP_DDNS received a corrupt response from the DNS server, %1, while adding forward address mapping for FQDN, %2
+This is an error message issued when the response received by DHCP_DDNS, to a
+update request to add a forward address mapping,  is mangled or malformed.
+The application will retry against the same server or others as appropriate.
+
+% DHCP_DDNS_FORWARD_ADD_BAD_DNSCLIENT_STATUS DHCP_DDNS received an unknown DNSClient status: %1, while adding a forward address mapping for FQDN %2 to DNS server %3
+This is an error message issued when DNSClient returns an unrecognized status
+while DHCP_DDNS was adding a forward address mapping.  The request will be
+aborted.  This is most likely a programmatic issue and should be reported.
+
+% DHCP_DDNS_FORWARD_REPLACE_REJECTED DNS Server, %1, rejected a DNS update request to replace the address mapping for FQDN, %2, with an RCODE: %3
+This is an error message issued when an update was rejected by the DNS server
+it was sent to for the reason given by the RCODE. The rcode values are defined
+in RFC 2136.
+
+% DHCP_DDNS_FORWARD_REPLACE_IO_ERROR DHCP_DDNS encountered an IO error sending a forward mapping replace for FQDN %1 to DNS server %2
+This is an error message issued when a communication error occurs while
+DHCP_DDNS is carrying out a forward address update.  The application will
+retry against the same server or others as appropriate.
+
+% DHCP_DDNS_FORWARD_REPLACE_RESP_CORRUPT DHCP_DDNS received a corrupt response from the DNS server, %1, while replacing forward address mapping for FQDN, %2
+This is an error message issued when the response received by DHCP_DDNS, to a
+update request to replace a forward address mapping,  is mangled or malformed.
+The application will retry against the same server or others as appropriate.
+
+% DHCP_DDNS_FORWARD_REPLACE_BAD_DNSCLIENT_STATUS DHCP_DDNS received an unknown DNSClient status: %1, while replacing forward address mapping for FQDN %2 to DNS server %3
+This is an error message issued when DNSClient returns an unrecognized status
+while DHCP_DDNS was replacing a forward address mapping.  The request will be
+aborted.  This is most likely a programmatic issue and should be reported.
+
+% DHCP_DDNS_REVERSE_REPLACE_REJECTED DNS Server, %1, rejected a DNS update request to replace the reverse mapping for FQDN, %2, with an RCODE: %3
+This is an error message issued when an update was rejected by the DNS server
+it was sent to for the reason given by the RCODE. The rcode values are defined
+in RFC 2136.
+
+% DHCP_DDNS_REVERSE_REPLACE_IO_ERROR DHCP_DDNS encountered an IO error sending a reverse mapping replacement for FQDN %1 to DNS server %2
+This is an error message issued when a communication error occurs while
+DHCP_DDNS is carrying out a reverse address update.  The application will
+retry against the same server or others as appropriate.
+
+% DHCP_DDNS_REVERSE_REPLACE_RESP_CORRUPT DHCP_DDNS received a corrupt response from the DNS server, %1, while replacing reverse address mapping for FQDN, %2
+This is an error message issued when the response received by DHCP_DDNS, to a
+update request to replace a reverse address,  is mangled or malformed.
+The application will retry against the same server or others as appropriate.
+
+% DHCP_DDNS_REVERSE_REPLACE_BAD_DNSCLIENT_STATUS DHCP_DDNS received an unknown DNSClient status: %1, while replacing reverse address mapping for FQDN %2 to DNS server %3
+This is an error message issued when DNSClient returns an unrecognized status
+while DHCP_DDNS was replacing a reverse address mapping.  The request will be
+aborted.  This is most likely a programmatic issue and should be reported.
+
+% DHCP_DDNS_TRANS_SEND_ERROR application encountered an unexpected error while attempting to send a DNS update: %1
+This is error message issued when the application is able to construct an update
+message but the attempt to send it suffered a unexpected error. This is most
+likely a programmatic error, rather than a communications issue. Some or all
+of the DNS updates requested as part of this request did not succeed.
+
+% DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE DNS udpate message to add a forward DNS entry could not be constructed for this request: %1, reason: %2
+This is an error message issued when an error occurs attempting to construct
+the server bound packet requesting a forward address addition.  This is due
+to invalid data contained in the NameChangeRequest. The request will be aborted.
+This is most likely a configuration issue.
+
+% DHCP_DDNS_FORWARD_REPLACE_BUILD_FAILURE DNS update message to replace a forward DNS entry could not be constructed from this request: %1, reason: %2
+This is an error message issued when an error occurs attempting to construct
+the server bound packet requesting a forward address replacement.  This is
+due to invalid data contained in the NameChangeRequest. The request will be
+aborted.  This is most likely a configuration issue.
+
+% DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE DNS update message to replace a reverse DNS entry could not be constructed from this request: %1, reason: %2
+This is an error message issued when an error occurs attempting to construct
+the server bound packet requesting a reverse PTR replacement.  This is
+due to invalid data contained in the NameChangeRequest. The request will be
+aborted.  This is most likely a configuration issue.
+
+% DHCP_DDNS_ADD_SUCCEEDED DHCP_DDNS successfully added the DNS mapping addition for this request: %1
+This is a debug message issued after DHCP_DDNS has submitted DNS mapping
+additions which were received and accepted by an appropriate DNS server.
+
+% DHCP_DDNS_ADD_FAILED DHCP_DDNS failed attempting to make DNS mapping additions for this request: %1, event: %2
+This is an error message issued after DHCP_DDNS attempts to submit DNS mapping
+entry additions have failed.  The precise reason for the failure should be
+documented in preceding log entries.
+
+% DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE DNS udpate message to remove a forward DNS Address entry could not be constructed for this request: %1,  reason: %2
+This is an error message issued when an error occurs attempting to construct
+the server bound packet requesting a forward address (A or AAAA) removal.  This
+is due to invalid data contained in the NameChangeRequest. The request will be
+aborted.  This is most likely a configuration issue.
+
+% DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED DNS Server, %1, rejected a DNS update request to remove the forward address mapping for FQDN, %2, with an RCODE: %3
+This is an error message issued when an update was rejected by the DNS server
+it was sent to for the reason given by the RCODE. The rcode values are defined
+in RFC 2136.
+
+% DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR DHCP_DDNS encountered an IO error sending a forward mapping address removal for FQDN %1 to DNS server %2
+This is an error message issued when a communication error occurs while
+DHCP_DDNS is carrying out a forward address remove.  The application will retry
+against the same server or others as appropriate.
+
+% DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT DHCP_DDNS received a corrupt response from the DNS server, %1, while removing forward address mapping for FQDN, %2
+This is an error message issued when the response received by DHCP_DDNS, to a
+update request to remove a forward address mapping, is mangled or malformed.
+The application will retry against the same server or others as appropriate.
+
+% DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS DHCP_DDNS received an unknown DNSClient status: %1, while removing a forward address mapping for FQDN %2 to DNS server %3
+This is an error message issued when DNSClient returns an unrecognized status
+while DHCP_DDNS was removing a forward address mapping.  The request will be
+aborted.  This is most likely a programmatic issue and should be reported.
+
+% DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE DNS udpate message to remove forward DNS RR entries could not be constructed for this request: %1,  reason: %2
+This is an error message issued when an error occurs attempting to construct
+the server bound packet requesting forward RR (DHCID RR) removal.  This is due
+to invalid data contained in the NameChangeRequest. The request will be aborted.This is most likely a configuration issue.
+
+% DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED DNS Server, %1, rejected a DNS update request to remove forward RR entries for FQDN, %2, with an RCODE: %3
+This is an error message issued when an update was rejected by the DNS server
+it was sent to for the reason given by the RCODE. The rcode values are defined
+in RFC 2136.
+
+% DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR DHCP_DDNS encountered an IO error sending a forward RR removal for FQDN %1 to DNS server %2
+This is an error message issued when a communication error occurs while
+DHCP_DDNS is carrying out a forward RR remove.  The application will retry
+against the same server.
+
+% DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT DHCP_DDNS received a corrupt response from the DNS server, %1, while removing forward RRs for FQDN, %2
+This is an error message issued when the response received by DHCP_DDNS, to a
+update request to remove forward RRs mapping, is mangled or malformed.
+The application will retry against the same server or others as appropriate.
+
+% DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS DHCP_DDNS received an unknown DNSClient status: %1, while removing forward RRs for FQDN %2 to DNS server %3
+This is an error message issued when DNSClient returns an unrecognized status
+while DHCP_DDNS was removing forward RRs.  The request will be aborted. This is
+most likely a programmatic issue and should be reported.
+
+% DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE DNS update message to remove a reverse DNS entry could not be constructed from this request: %1,  reason: %2
+This is an error message issued when an error occurs attempting to construct
+the server bound packet requesting a reverse PTR removal.  This is
+due to invalid data contained in the NameChangeRequest. The request will be
+aborted.  This is most likely a configuration issue.
+
+% DHCP_DDNS_REVERSE_REMOVE_REJECTED DNS Server, %1, rejected a DNS update request to remove the reverse mapping for FQDN, %2, with an RCODE: %3
+This is an error message issued when an update was rejected by the DNS server
+it was sent to for the reason given by the RCODE. The rcode values are defined
+in RFC 2136.
+
+% DHCP_DDNS_REVERSE_REMOVE_IO_ERROR DHCP_DDNS encountered an IO error sending a reverse mapping remove for FQDN %1 to DNS server %2
+This is an error message issued when a communication error occurs while
+DHCP_DDNS is carrying out a reverse address update.  The application will
+retry against the same server or others as appropriate.
+
+% DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT DHCP_DDNS received a corrupt response from the DNS server, %1, while removing reverse address mapping for FQDN, %2
+This is an error message issued when the response received by DHCP_DDNS, to a
+update request to remove a reverse address,  is mangled or malformed.
+The application will retry against the same server or others as appropriate.
+
+% DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS DHCP_DDNS received an unknown DNSClient status: %1, while removing reverse address mapping for FQDN %2 to DNS server %3
+This is an error message issued when DNSClient returns an unrecognized status
+while DHCP_DDNS was removing a reverse address mapping.  The request will be
+aborted.  This is most likely a programmatic issue and should be reported.
+
+% DHCP_DDNS_REMOVE_SUCCEEDED DHCP_DDNS successfully removed the DNS mapping addition for this request: %1
+This is a debug message issued after DHCP_DDNS has submitted DNS mapping
+removals which were received and accepted by an appropriate DNS server.
+
+% DHCP_DDNS_REMOVE_FAILED DHCP_DDNS failed attempting to make DNS mapping removals for this request: %1, event: %2
+This is an error message issued after DHCP_DDNS attempts to submit DNS mapping
+entry removals have failed.  The precise reason for the failure should be
+documented in preceding log entries.
+
diff --git a/src/bin/d2/d2_process.cc b/src/bin/d2/d2_process.cc
index 63bd6bd..187bfe1 100644
--- a/src/bin/d2/d2_process.cc
+++ b/src/bin/d2/d2_process.cc
@@ -35,13 +35,13 @@ D2Process::D2Process(const char* name, IOServicePtr io_service)
     // been received.  This means that until we receive the configuration,
     // D2 will neither receive nor process NameChangeRequests.
     // Pass in IOService for NCR IO event processing.
-    queue_mgr_.reset(new D2QueueMgr(*getIoService()));
+    queue_mgr_.reset(new D2QueueMgr(getIoService()));
 
     // Instantiate update manager.
     // Pass in both queue manager and configuration manager.
     // Pass in IOService for DNS update transaction IO event processing.
     D2CfgMgrPtr tmp = getD2CfgMgr();
-    update_mgr_.reset(new D2UpdateMgr(queue_mgr_,  tmp,  *getIoService()));
+    update_mgr_.reset(new D2UpdateMgr(queue_mgr_,  tmp,  getIoService()));
 };
 
 void
diff --git a/src/bin/d2/d2_queue_mgr.cc b/src/bin/d2/d2_queue_mgr.cc
index 4de9c42..041c2e8 100644
--- a/src/bin/d2/d2_queue_mgr.cc
+++ b/src/bin/d2/d2_queue_mgr.cc
@@ -22,10 +22,13 @@ namespace d2 {
 // Makes constant visible to Google test macros.
 const size_t D2QueueMgr::MAX_QUEUE_DEFAULT;
 
-D2QueueMgr::D2QueueMgr(isc::asiolink::IOService& io_service,
-                       const size_t max_queue_size)
+D2QueueMgr::D2QueueMgr(IOServicePtr& io_service, const size_t max_queue_size)
     : io_service_(io_service), max_queue_size_(max_queue_size),
       mgr_state_(NOT_INITTED), target_stop_state_(NOT_INITTED) {
+    if (!io_service_) {
+        isc_throw(D2QueueMgrError, "IOServicePtr cannot be null");
+    }
+
     // Use setter to do validation.
     setMaxQueueSize(max_queue_size);
 }
@@ -129,7 +132,7 @@ D2QueueMgr::startListening() {
 
     // Instruct the listener to start listening and set state accordingly.
     try {
-        listener_->startListening(io_service_);
+        listener_->startListening(*io_service_);
         mgr_state_ = RUNNING;
     } catch (const isc::Exception& ex) {
         isc_throw(D2QueueMgrError, "D2QueueMgr listener start failed: "
diff --git a/src/bin/d2/d2_queue_mgr.h b/src/bin/d2/d2_queue_mgr.h
index 8fe078b..c9b0298 100644
--- a/src/bin/d2/d2_queue_mgr.h
+++ b/src/bin/d2/d2_queue_mgr.h
@@ -17,9 +17,8 @@
 
 /// @file d2_queue_mgr.h This file defines the class D2QueueMgr.
 
-#include <asiolink/io_address.h>
-#include <asiolink/io_service.h>
 #include <exceptions/exceptions.h>
+#include <d2/d2_asio.h>
 #include <dhcp_ddns/ncr_msg.h>
 #include <dhcp_ddns/ncr_io.h>
 
@@ -166,7 +165,7 @@ public:
     /// This value must be greater than zero. It defaults to MAX_QUEUE_DEFAULT.
     ///
     /// @throw D2QueueMgrError if max_queue_size is zero.
-    D2QueueMgr(isc::asiolink::IOService& io_service,
+    D2QueueMgr(IOServicePtr& io_service,
                const size_t max_queue_size = MAX_QUEUE_DEFAULT);
 
     /// @brief Destructor
@@ -328,7 +327,7 @@ public:
     void updateStopState();
 
     /// @brief IOService that our listener should use for IO management.
-    isc::asiolink::IOService& io_service_;
+    IOServicePtr io_service_;
 
     /// @brief Dictates the maximum number of entries allowed in the queue.
     size_t max_queue_size_;
diff --git a/src/bin/d2/d2_update_message.cc b/src/bin/d2/d2_update_message.cc
index 71fb9f3..71ea5b2 100644
--- a/src/bin/d2/d2_update_message.cc
+++ b/src/bin/d2/d2_update_message.cc
@@ -31,7 +31,7 @@ D2UpdateMessage::D2UpdateMessage(const Direction direction)
     if (direction == OUTBOUND) {
         message_.setOpcode(Opcode(Opcode::UPDATE_CODE));
         message_.setHeaderFlag(dns::Message::HEADERFLAG_QR, false);
-
+        message_.setRcode(Rcode::NOERROR());
     }
 }
 
diff --git a/src/bin/d2/d2_update_mgr.cc b/src/bin/d2/d2_update_mgr.cc
index b88b415..4439cc2 100644
--- a/src/bin/d2/d2_update_mgr.cc
+++ b/src/bin/d2/d2_update_mgr.cc
@@ -24,7 +24,7 @@ namespace d2 {
 const size_t D2UpdateMgr::MAX_TRANSACTIONS_DEFAULT;
 
 D2UpdateMgr::D2UpdateMgr(D2QueueMgrPtr& queue_mgr, D2CfgMgrPtr& cfg_mgr,
-                         isc::asiolink::IOService& io_service,
+                         IOServicePtr& io_service,
                          const size_t max_transactions)
     :queue_mgr_(queue_mgr), cfg_mgr_(cfg_mgr), io_service_(io_service) {
     if (!queue_mgr_) {
@@ -36,6 +36,10 @@ D2UpdateMgr::D2UpdateMgr(D2QueueMgrPtr& queue_mgr, D2CfgMgrPtr& cfg_mgr,
                   "D2UpdateMgr configuration manager cannot be null");
     }
 
+    if (!io_service_) {
+        isc_throw(D2UpdateMgrError, "IOServicePtr cannot be null");
+    }
+
     // Use setter to do validation.
     setMaxTransactions(max_transactions);
 }
diff --git a/src/bin/d2/d2_update_mgr.h b/src/bin/d2/d2_update_mgr.h
index 9653253..422049f 100644
--- a/src/bin/d2/d2_update_mgr.h
+++ b/src/bin/d2/d2_update_mgr.h
@@ -17,8 +17,8 @@
 
 /// @file d2_update_mgr.h This file defines the class D2UpdateMgr.
 
-#include <asiolink/io_service.h>
 #include <exceptions/exceptions.h>
+#include <d2/d2_asio.h>
 #include <d2/d2_log.h>
 #include <d2/d2_queue_mgr.h>
 #include <d2/d2_cfg_mgr.h>
@@ -100,7 +100,7 @@ public:
     /// @throw D2UpdateMgrError if either the queue manager or configuration
     /// managers are NULL, or max transactions is less than one.
     D2UpdateMgr(D2QueueMgrPtr& queue_mgr, D2CfgMgrPtr& cfg_mgr,
-                isc::asiolink::IOService& io_service,
+                IOServicePtr& io_service,
                 const size_t max_transactions = MAX_TRANSACTIONS_DEFAULT);
 
     /// @brief Destructor
@@ -228,7 +228,7 @@ private:
     /// passed into transactions to manager their IO events.
     /// (For future reference, multi-threaded transactions would each use their
     /// own IOService instance.)
-    isc::asiolink::IOService& io_service_;
+    IOServicePtr io_service_;
 
     /// @brief Maximum number of concurrent transactions.
     size_t max_transactions_;
diff --git a/src/bin/d2/d_controller.h b/src/bin/d2/d_controller.h
index 100eb44..917ed64 100644
--- a/src/bin/d2/d_controller.h
+++ b/src/bin/d2/d_controller.h
@@ -15,10 +15,10 @@
 #ifndef D_CONTROLLER_H
 #define D_CONTROLLER_H
 
-#include <asiolink/asiolink.h>
 #include <cc/data.h>
 #include <cc/session.h>
 #include <config/ccsession.h>
+#include <d2/d2_asio.h>
 #include <d2/d2_log.h>
 #include <d2/d_process.h>
 #include <exceptions/exceptions.h>
diff --git a/src/bin/d2/d_process.h b/src/bin/d2/d_process.h
index ce86118..7ba74f9 100644
--- a/src/bin/d2/d_process.h
+++ b/src/bin/d2/d_process.h
@@ -15,16 +15,14 @@
 #ifndef D_PROCESS_H
 #define D_PROCESS_H
 
-#include <asiolink/asiolink.h>
 #include <cc/data.h>
+#include <d2/d2_asio.h>
 #include <d2/d_cfg_mgr.h>
 
 #include <boost/shared_ptr.hpp>
 
 #include <exceptions/exceptions.h>
 
-typedef boost::shared_ptr<isc::asiolink::IOService> IOServicePtr;
-
 namespace isc {
 namespace d2 {
 
diff --git a/src/bin/d2/dns_client.cc b/src/bin/d2/dns_client.cc
index a3bff81..a391c28 100644
--- a/src/bin/d2/dns_client.cc
+++ b/src/bin/d2/dns_client.cc
@@ -162,7 +162,7 @@ DNSClientImpl::getStatus(const asiodns::IOFetch::Result result) {
 }
 
 void
-DNSClientImpl::doUpdate(IOService& io_service,
+DNSClientImpl::doUpdate(asiolink::IOService& io_service,
                         const IOAddress& ns_addr,
                         const uint16_t ns_port,
                         D2UpdateMessage& update,
@@ -191,6 +191,7 @@ DNSClientImpl::doUpdate(IOService& io_service,
     // caller that the unsigned timeout value will fit into int.
     IOFetch io_fetch(IOFetch::UDP, io_service, msg_buf, ns_addr, ns_port,
                      in_buf_, this, static_cast<int>(wait));
+
     // Post the task to the task queue in the IO service. Caller will actually
     // run these tasks by executing IOService::run.
     io_service.post(io_fetch);
@@ -213,7 +214,7 @@ DNSClient::getMaxTimeout() {
 }
 
 void
-DNSClient::doUpdate(IOService&,
+DNSClient::doUpdate(asiolink::IOService&,
                     const IOAddress&,
                     const uint16_t,
                     D2UpdateMessage&,
@@ -224,7 +225,7 @@ DNSClient::doUpdate(IOService&,
 }
 
 void
-DNSClient::doUpdate(IOService& io_service,
+DNSClient::doUpdate(asiolink::IOService& io_service,
                     const IOAddress& ns_addr,
                     const uint16_t ns_port,
                     D2UpdateMessage& update,
diff --git a/src/bin/d2/nc_add.cc b/src/bin/d2/nc_add.cc
new file mode 100644
index 0000000..da20763
--- /dev/null
+++ b/src/bin/d2/nc_add.cc
@@ -0,0 +1,697 @@
+// 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/d2_cfg_mgr.h>
+#include <d2/nc_add.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+
+#include <util/buffer.h>
+#include <dns/rdataclass.h>
+
+namespace isc {
+namespace d2 {
+
+// NameAddTransaction states
+const int NameAddTransaction::ADDING_FWD_ADDRS_ST;
+const int NameAddTransaction::REPLACING_FWD_ADDRS_ST;
+const int NameAddTransaction::REPLACING_REV_PTRS_ST;
+
+// NameAddTransaction events
+const int NameAddTransaction::FQDN_IN_USE_EVT;
+const int NameAddTransaction::FQDN_NOT_IN_USE_EVT;
+
+NameAddTransaction::
+NameAddTransaction(IOServicePtr& io_service,
+                   dhcp_ddns::NameChangeRequestPtr& ncr,
+                   DdnsDomainPtr& forward_domain,
+                   DdnsDomainPtr& reverse_domain)
+    : NameChangeTransaction(io_service, ncr, forward_domain, reverse_domain) {
+    if (ncr->getChangeType() != isc::dhcp_ddns::CHG_ADD) {
+        isc_throw (NameAddTransactionError,
+                   "NameAddTransaction, request type must be CHG_ADD");
+    }
+}
+
+NameAddTransaction::~NameAddTransaction(){
+}
+
+void
+NameAddTransaction::defineEvents() {
+    // Call superclass impl first.
+    NameChangeTransaction::defineEvents();
+
+    // Define NameAddTransaction events.
+    defineEvent(FQDN_IN_USE_EVT, "FQDN_IN_USE_EVT");
+    defineEvent(FQDN_NOT_IN_USE_EVT, "FQDN_NOT_IN_USE_EVT");
+}
+
+void
+NameAddTransaction::verifyEvents() {
+    // Call superclass implementation first to verify its events. These are
+    // events common to all transactions, and they must be defined.
+    // SELECT_SERVER_EVT
+    // SERVER_SELECTED_EVT
+    // SERVER_IO_ERROR_EVT
+    // NO_MORE_SERVERS_EVT
+    // IO_COMPLETED_EVT
+    // UPDATE_OK_EVT
+    // UPDATE_FAILED_EVT
+    NameChangeTransaction::verifyEvents();
+
+    // Verify NameAddTransaction events by attempting to fetch them.
+    getEvent(FQDN_IN_USE_EVT);
+    getEvent(FQDN_NOT_IN_USE_EVT);
+}
+
+void
+NameAddTransaction::defineStates() {
+    // Call superclass impl first.
+    NameChangeTransaction::defineStates();
+
+    // Define NameAddTransaction states.
+    defineState(READY_ST, "READY_ST",
+             boost::bind(&NameAddTransaction::readyHandler, this));
+
+    defineState(SELECTING_FWD_SERVER_ST, "SELECTING_FWD_SERVER_ST",
+             boost::bind(&NameAddTransaction::selectingFwdServerHandler, this));
+
+    defineState(SELECTING_REV_SERVER_ST, "SELECTING_REV_SERVER_ST",
+             boost::bind(&NameAddTransaction::selectingRevServerHandler, this));
+
+    defineState(ADDING_FWD_ADDRS_ST, "ADDING_FWD_ADDRS_ST",
+             boost::bind(&NameAddTransaction::addingFwdAddrsHandler, this));
+
+    defineState(REPLACING_FWD_ADDRS_ST, "REPLACING_FWD_ADDRS_ST",
+             boost::bind(&NameAddTransaction::replacingFwdAddrsHandler, this));
+
+    defineState(REPLACING_REV_PTRS_ST, "REPLACING_REV_PTRS_ST",
+             boost::bind(&NameAddTransaction::replacingRevPtrsHandler, this));
+
+    defineState(PROCESS_TRANS_OK_ST, "PROCESS_TRANS_OK_ST",
+             boost::bind(&NameAddTransaction::processAddOkHandler, this));
+
+    defineState(PROCESS_TRANS_FAILED_ST, "PROCESS_TRANS_FAILED_ST",
+             boost::bind(&NameAddTransaction::processAddFailedHandler, this));
+
+}
+void
+NameAddTransaction::verifyStates() {
+    // Call superclass implementation first to verify its states. These are
+    // states common to all transactions, and they must be defined.
+    // READY_ST
+    // SELECTING_FWD_SERVER_ST
+    // SELECTING_REV_SERVER_ST
+    // PROCESS_TRANS_OK_ST
+    // PROCESS_TRANS_FAILED_ST
+    NameChangeTransaction::verifyStates();
+
+    // Verify NameAddTransaction states by attempting to fetch them.
+    getState(ADDING_FWD_ADDRS_ST);
+    getState(REPLACING_FWD_ADDRS_ST);
+    getState(REPLACING_REV_PTRS_ST);
+}
+
+void
+NameAddTransaction::readyHandler() {
+    switch(getNextEvent()) {
+    case START_EVT:
+        if (getForwardDomain()) {
+            // Request includes a forward change, do that first.
+            transition(SELECTING_FWD_SERVER_ST, SELECT_SERVER_EVT);
+        } else {
+            // Reverse change only, transition accordingly.
+            transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT);
+        }
+
+        break;
+    default:
+        // Event is invalid.
+        isc_throw(NameAddTransactionError,
+                  "Wrong event for context: " << getContextStr());
+    }
+}
+
+void
+NameAddTransaction::selectingFwdServerHandler() {
+    switch(getNextEvent()) {
+    case SELECT_SERVER_EVT:
+        // First time through for this transaction, so initialize server
+        // selection.
+        initServerSelection(getForwardDomain());
+        break;
+    case SERVER_IO_ERROR_EVT:
+        // We failed to communicate with current server. Attempt to select
+        // another one below.
+        break;
+    default:
+        // Event is invalid.
+        isc_throw(NameAddTransactionError,
+                  "Wrong event for context: " << getContextStr());
+    }
+
+    // Select the next server from the list of forward servers.
+    if (selectNextServer()) {
+        // We have a server to try.
+        transition(ADDING_FWD_ADDRS_ST, SERVER_SELECTED_EVT);
+    }
+    else {
+        // Server list is exhausted, so fail the transaction.
+        transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT);
+    }
+}
+
+void
+NameAddTransaction::addingFwdAddrsHandler() {
+    if (doOnEntry()) {
+        // Clear the request on initial transition. This allows us to reuse
+        // the request on retries if necessary.
+        clearDnsUpdateRequest();
+    }
+
+    switch(getNextEvent()) {
+    case SERVER_SELECTED_EVT:
+        if (!getDnsUpdateRequest()) {
+            // Request hasn't been constructed yet, so build it.
+            try {
+                buildAddFwdAddressRequest();
+            } catch (const std::exception& ex) {
+                // While unlikely, the build might fail if we have invalid
+                // data.  Should that be the case, we need to fail the
+                // transaction.
+                LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE)
+                          .arg(getNcr()->toText())
+                          .arg(ex.what());
+                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+                break;
+            }
+        }
+
+        // Call sendUpdate() to initiate the async send. Note it also sets
+        // next event to NOP_EVT.
+        sendUpdate();
+        break;
+
+    case IO_COMPLETED_EVT: {
+        switch (getDnsUpdateStatus()) {
+        case DNSClient::SUCCESS: {
+            // We successfully received a response packet from the server.
+            const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
+            if (rcode == dns::Rcode::NOERROR()) {
+                // We were able to add it. Mark it as done.
+                setForwardChangeCompleted(true);
+
+                // If request calls for reverse update then do that next,
+                // otherwise we can process ok.
+                if (getReverseDomain()) {
+                    transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT);
+                } else {
+                    transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT);
+                }
+            } else if (rcode == dns::Rcode::YXDOMAIN()) {
+                // FQDN is in use so we need to attempt to replace
+                // forward address.
+                transition(REPLACING_FWD_ADDRS_ST, FQDN_IN_USE_EVT);
+            } else {
+                // Per RFC4703 any other value means cease.
+                // If we get not authorized should we try the next server in
+                // the list? @todo  This needs some discussion perhaps.
+                LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_ADD_REJECTED)
+                          .arg(getCurrentServer()->getIpAddress())
+                          .arg(getNcr()->getFqdn())
+                          .arg(rcode.getCode());
+                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+            }
+
+            break;
+        }
+
+        case DNSClient::TIMEOUT:
+        case DNSClient::OTHER:
+            // We couldn't send to the current server, log it and set up
+            // to select the next server for a retry.
+            // @note For now we treat OTHER as an IO error like TIMEOUT. It
+            // is not entirely clear if this is accurate.
+            LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_ADD_IO_ERROR)
+                      .arg(getNcr()->getFqdn())
+                      .arg(getCurrentServer()->getIpAddress());
+
+            retryTransition(SELECTING_FWD_SERVER_ST);
+            break;
+
+        case DNSClient::INVALID_RESPONSE:
+            // A response was received but was corrupt. Retry it like an IO
+            // error.
+            LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_ADD_RESP_CORRUPT)
+                      .arg(getCurrentServer()->getIpAddress())
+                      .arg(getNcr()->getFqdn());
+
+            retryTransition(SELECTING_FWD_SERVER_ST);
+            break;
+
+        default:
+            // Any other value and we will fail this transaction, something
+            // bigger is wrong.
+            LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_ADD_BAD_DNSCLIENT_STATUS)
+                      .arg(getDnsUpdateStatus())
+                      .arg(getNcr()->getFqdn())
+                      .arg(getCurrentServer()->getIpAddress());
+
+            transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+            break;
+        } // end switch on dns_status
+
+        break;
+    } // end case IO_COMPLETE_EVT
+
+    default:
+        // Event is invalid.
+        isc_throw(NameAddTransactionError,
+                  "Wrong event for context: " << getContextStr());
+    }
+}
+
+void
+NameAddTransaction::replacingFwdAddrsHandler() {
+    if (doOnEntry()) {
+        // Clear the request on initial transition. This allows us to reuse
+        // the request on retries if necessary.
+        clearDnsUpdateRequest();
+    }
+
+    switch(getNextEvent()) {
+    case FQDN_IN_USE_EVT:
+    case SERVER_SELECTED_EVT:
+        if (!getDnsUpdateRequest()) {
+            // Request hasn't been constructed yet, so build it.
+            try {
+                buildReplaceFwdAddressRequest();
+            } catch (const std::exception& ex) {
+                // While unlikely, the build might fail if we have invalid
+                // data.  Should that be the case, we need to fail the
+                // transaction.
+                LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REPLACE_BUILD_FAILURE)
+                          .arg(getNcr()->toText())
+                          .arg(ex.what());
+                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+                break;
+            }
+        }
+
+        // Call sendUpdate() to initiate the async send. Note it also sets
+        // next event to NOP_EVT.
+        sendUpdate();
+        break;
+
+    case IO_COMPLETED_EVT: {
+        switch (getDnsUpdateStatus()) {
+        case DNSClient::SUCCESS: {
+            // We successfully received a response packet from the server.
+            const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
+            if (rcode == dns::Rcode::NOERROR()) {
+                // We were able to replace the forward mapping. Mark it as done.
+                setForwardChangeCompleted(true);
+
+                // If request calls for reverse update then do that next,
+                // otherwise we can process ok.
+                if (getReverseDomain()) {
+                    transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT);
+                } else {
+                    transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT);
+                }
+            } else if (rcode == dns::Rcode::NXDOMAIN()) {
+                // FQDN is NOT in use so go back and do the forward add address.
+                // Covers the case that it was there when we tried to add it,
+                // but has since been removed per RFC 4703.
+                transition(ADDING_FWD_ADDRS_ST, SERVER_SELECTED_EVT);
+            } else {
+                // Per RFC4703 any other value means cease.
+                // If we get not authorized should try the next server in
+                // the list? @todo  This needs some discussion perhaps.
+                LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REPLACE_REJECTED)
+                          .arg(getCurrentServer()->getIpAddress())
+                          .arg(getNcr()->getFqdn())
+                          .arg(rcode.getCode());
+                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+            }
+
+            break;
+        }
+
+        case DNSClient::TIMEOUT:
+        case DNSClient::OTHER:
+            // We couldn't send to the current server, log it and set up
+            // to select the next server for a retry.
+            // @note For now we treat OTHER as an IO error like TIMEOUT. It
+            // is not entirely clear if this is accurate.
+            LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REPLACE_IO_ERROR)
+                      .arg(getNcr()->getFqdn())
+                      .arg(getCurrentServer()->getIpAddress());
+
+            // If we are out of retries on this server, we go back and start
+            // all over on a new server.
+            retryTransition(SELECTING_FWD_SERVER_ST);
+            break;
+
+        case DNSClient::INVALID_RESPONSE:
+            // A response was received but was corrupt. Retry it like an IO
+            // error.
+            LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REPLACE_RESP_CORRUPT)
+                      .arg(getCurrentServer()->getIpAddress())
+                      .arg(getNcr()->getFqdn());
+
+            // If we are out of retries on this server, we go back and start
+            // all over on a new server.
+            retryTransition(SELECTING_FWD_SERVER_ST);
+            break;
+
+        default:
+            // Any other value and we will fail this transaction, something
+            // bigger is wrong.
+            LOG_ERROR(dctl_logger,
+                      DHCP_DDNS_FORWARD_REPLACE_BAD_DNSCLIENT_STATUS)
+                      .arg(getDnsUpdateStatus())
+                      .arg(getNcr()->getFqdn())
+                      .arg(getCurrentServer()->getIpAddress());
+
+            transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+            break;
+        } // end switch on dns_status
+
+        break;
+    } // end case IO_COMPLETE_EVT
+
+    default:
+        // Event is invalid.
+        isc_throw(NameAddTransactionError,
+                  "Wrong event for context: " << getContextStr());
+    }
+}
+
+void
+NameAddTransaction::selectingRevServerHandler() {
+    switch(getNextEvent()) {
+    case SELECT_SERVER_EVT:
+        // First time through for this transaction, so initialize server
+        // selection.
+        initServerSelection(getReverseDomain());
+        break;
+    case SERVER_IO_ERROR_EVT:
+        // We failed to communicate with current server. Attempt to select
+        // another one below.
+        break;
+    default:
+        // Event is invalid.
+        isc_throw(NameAddTransactionError,
+                  "Wrong event for context: " << getContextStr());
+    }
+
+    // Select the next server from the list of forward servers.
+    if (selectNextServer()) {
+        // We have a server to try.
+        transition(REPLACING_REV_PTRS_ST, SERVER_SELECTED_EVT);
+    }
+    else {
+        // Server list is exhausted, so fail the transaction.
+        transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT);
+    }
+}
+
+
+void
+NameAddTransaction::replacingRevPtrsHandler() {
+    if (doOnEntry()) {
+        // Clear the request on initial transition. This allows us to reuse
+        // the request on retries if necessary.
+        clearDnsUpdateRequest();
+    }
+
+    switch(getNextEvent()) {
+    case SERVER_SELECTED_EVT:
+        if (!getDnsUpdateRequest()) {
+            // Request hasn't been constructed yet, so build it.
+            try {
+                buildReplaceRevPtrsRequest();
+            } catch (const std::exception& ex) {
+                // While unlikely, the build might fail if we have invalid
+                // data.  Should that be the case, we need to fail the
+                // transaction.
+                LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE)
+                          .arg(getNcr()->toText())
+                          .arg(ex.what());
+                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+                break;
+            }
+        }
+
+        // Call sendUpdate() to initiate the async send. Note it also sets
+        // next event to NOP_EVT.
+        sendUpdate();
+        break;
+
+    case IO_COMPLETED_EVT: {
+        switch (getDnsUpdateStatus()) {
+        case DNSClient::SUCCESS: {
+            // We successfully received a response packet from the server.
+            const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
+            if (rcode == dns::Rcode::NOERROR()) {
+                // We were able to update the reverse mapping. Mark it as done.
+                setReverseChangeCompleted(true);
+                transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT);
+            } else {
+                // Per RFC4703 any other value means cease.
+                // If we get not authorized should try the next server in
+                // the list? @todo  This needs some discussion perhaps.
+                LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REPLACE_REJECTED)
+                          .arg(getCurrentServer()->getIpAddress())
+                          .arg(getNcr()->getFqdn())
+                          .arg(rcode.getCode());
+                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+            }
+
+            break;
+        }
+
+        case DNSClient::TIMEOUT:
+        case DNSClient::OTHER:
+            // We couldn't send to the current server, log it and set up
+            // to select the next server for a retry.
+            // @note For now we treat OTHER as an IO error like TIMEOUT. It
+            // is not entirely clear if this is accurate.
+            LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REPLACE_IO_ERROR)
+                      .arg(getNcr()->getFqdn())
+                      .arg(getCurrentServer()->getIpAddress());
+
+            // If we are out of retries on this server, we go back and start
+            // all over on a new server.
+            retryTransition(SELECTING_REV_SERVER_ST);
+            break;
+
+        case DNSClient::INVALID_RESPONSE:
+            // A response was received but was corrupt. Retry it like an IO
+            // error.
+            LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REPLACE_RESP_CORRUPT)
+                      .arg(getCurrentServer()->getIpAddress())
+                      .arg(getNcr()->getFqdn());
+
+            // If we are out of retries on this server, we go back and start
+            // all over on a new server.
+            retryTransition(SELECTING_REV_SERVER_ST);
+            break;
+
+        default:
+            // Any other value and we will fail this transaction, something
+            // bigger is wrong.
+            LOG_ERROR(dctl_logger,
+                      DHCP_DDNS_REVERSE_REPLACE_BAD_DNSCLIENT_STATUS)
+                      .arg(getDnsUpdateStatus())
+                      .arg(getNcr()->getFqdn())
+                      .arg(getCurrentServer()->getIpAddress());
+
+            transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+            break;
+        } // end switch on dns_status
+
+        break;
+    } // end case IO_COMPLETE_EVT
+
+    default:
+        // Event is invalid.
+        isc_throw(NameAddTransactionError,
+                  "Wrong event for context: " << getContextStr());
+    }
+}
+
+void
+NameAddTransaction::processAddOkHandler() {
+    switch(getNextEvent()) {
+    case UPDATE_OK_EVT:
+        LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL, DHCP_DDNS_ADD_SUCCEEDED)
+                  .arg(getNcr()->toText());
+        setNcrStatus(dhcp_ddns::ST_COMPLETED);
+        endModel();
+        break;
+    default:
+        // Event is invalid.
+        isc_throw(NameAddTransactionError,
+                  "Wrong event for context: " << getContextStr());
+    }
+}
+
+void
+NameAddTransaction::processAddFailedHandler() {
+    switch(getNextEvent()) {
+    case UPDATE_FAILED_EVT:
+    case NO_MORE_SERVERS_EVT:
+        LOG_ERROR(dctl_logger, DHCP_DDNS_ADD_FAILED).arg(getNcr()->toText())
+        .arg(getContextStr());
+        setNcrStatus(dhcp_ddns::ST_FAILED);
+        endModel();
+        break;
+    default:
+        // Event is invalid.
+        isc_throw(NameAddTransactionError,
+                  "Wrong event for context: " << getContextStr());
+    }
+}
+
+void
+NameAddTransaction::buildAddFwdAddressRequest() {
+    // Construct an empty request.
+    D2UpdateMessagePtr request = prepNewRequest(getForwardDomain());
+
+    // Construct dns::Name from NCR fqdn.
+    dns::Name fqdn(dns::Name(getNcr()->getFqdn()));
+
+    // Content on this request is based on RFC 4703, section 5.3.1
+    // First build the Prerequisite Section.
+
+    // Create 'FQDN Is Not In Use' prerequisite and add it to the
+    // prerequisite section.
+    // Based on RFC 2136, section 2.4.5
+    dns::RRsetPtr prereq(new dns::RRset(fqdn, dns::RRClass::NONE(),
+                             dns::RRType::ANY(), dns::RRTTL(0)));
+    request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
+
+    // Next build the Update Section.
+
+    // Create the FQDN/IP 'add' RR and add it to the to update section.
+    // Based on RFC 2136, section 2.5.1
+    dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::IN(),
+                         getAddressRRType(), dns::RRTTL(0)));
+
+    addLeaseAddressRdata(update);
+    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+    // Now create the FQDN/DHCID 'add' RR and add it to update section.
+    // Based on RFC 2136, section 2.5.1
+    update.reset(new dns::RRset(fqdn, dns::RRClass::IN(),
+                                dns::RRType::DHCID(), dns::RRTTL(0)));
+    addDhcidRdata(update);
+    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+    // Set the transaction's update request to the new request.
+    setDnsUpdateRequest(request);
+}
+
+void
+NameAddTransaction::buildReplaceFwdAddressRequest() {
+    // Construct an empty request.
+    D2UpdateMessagePtr request = prepNewRequest(getForwardDomain());
+
+    // Construct dns::Name from NCR fqdn.
+    dns::Name fqdn(dns::Name(getNcr()->getFqdn()));
+
+    // Content on this request is based on RFC 4703, section 5.3.2
+    // First build the Prerequisite Section.
+
+    // Create an 'FQDN Is In Use' prerequisite and add it to the
+    // pre-requisite section.
+    // Based on RFC 2136, section 2.4.4
+    dns::RRsetPtr prereq(new dns::RRset(fqdn, dns::RRClass::ANY(),
+                               dns::RRType::ANY(), dns::RRTTL(0)));
+    request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
+
+    // Create an DHCID matches prerequisite RR and add it to the
+    // pre-requisite section.
+    // Based on RFC 2136, section 2.4.2.
+    prereq.reset(new dns::RRset(fqdn, dns::RRClass::IN(),
+                 dns::RRType::DHCID(), dns::RRTTL(0)));
+    addDhcidRdata(prereq);
+    request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
+
+    // Next build the Update Section.
+
+    // Create the FQDN/IP 'delete' RR and add it to the update section.
+    // Based on RFC 2136, section 2.5.2
+    dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::ANY(),
+                         getAddressRRType(), dns::RRTTL(0)));
+    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+    // Create the FQDN/IP 'add' RR and add it to the update section.
+    // Based on RFC 2136, section 2.5.1
+    update.reset(new dns::RRset(fqdn, dns::RRClass::IN(),
+                                getAddressRRType(), dns::RRTTL(0)));
+    addLeaseAddressRdata(update);
+    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+    // Set the transaction's update request to the new request.
+    setDnsUpdateRequest(request);
+}
+
+void
+NameAddTransaction::buildReplaceRevPtrsRequest() {
+    // Construct an empty request.
+    D2UpdateMessagePtr request = prepNewRequest(getReverseDomain());
+
+    // Create the reverse IP address "FQDN".
+    std::string rev_addr = D2CfgMgr::reverseIpAddress(getNcr()->getIpAddress());
+    dns::Name rev_ip(rev_addr);
+
+    // Content on this request is based on RFC 4703, section 5.4
+    // Reverse replacement has no prerequisites so straight on to
+    // building the Update section.
+
+    // Create the PTR 'delete' RR and add it to update section.
+    dns::RRsetPtr update(new dns::RRset(rev_ip, dns::RRClass::ANY(),
+                         dns::RRType::PTR(), dns::RRTTL(0)));
+    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+    // Create the DHCID 'delete' RR and add it to the update section.
+    update.reset(new dns::RRset(rev_ip, dns::RRClass::ANY(),
+                                dns::RRType::DHCID(), dns::RRTTL(0)));
+    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+    // Create the FQDN/IP PTR 'add' RR, add the FQDN as the PTR Rdata
+    // then add it to update section.
+    update.reset(new dns::RRset(rev_ip, dns::RRClass::IN(),
+                                dns::RRType::PTR(), dns::RRTTL(0)));
+    addPtrRdata(update);
+    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+    // Create the FQDN/IP PTR 'add' RR, add the DHCID Rdata
+    // then add it to update section.
+    update.reset(new dns::RRset(rev_ip, dns::RRClass::IN(),
+                                dns::RRType::DHCID(), dns::RRTTL(0)));
+    addDhcidRdata(update);
+    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+    // Set the transaction's update request to the new request.
+    setDnsUpdateRequest(request);
+}
+
+} // namespace isc::d2
+} // namespace isc
diff --git a/src/bin/d2/nc_add.h b/src/bin/d2/nc_add.h
new file mode 100644
index 0000000..1fe167b
--- /dev/null
+++ b/src/bin/d2/nc_add.h
@@ -0,0 +1,451 @@
+// 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_ADD_H
+#define NC_ADD_H
+
+/// @file nc_add.h This file defines the class NameAddTransaction.
+
+#include <d2/nc_trans.h>
+#include <dns/rdata.h>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Thrown if the NameAddTransaction encounters a general error.
+class NameAddTransactionError : public isc::Exception {
+public:
+    NameAddTransactionError(const char* file, size_t line,
+                               const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Embodies the "life-cycle" required to carry out a DDNS Add update.
+///
+/// NameAddTransaction implements a state machine for adding (or replacing) a
+/// forward and/or reverse DNS mapping. This state machine is based upon the
+/// processing logic described in RFC 4703, Sections 5.3 and 5.4.  That logic
+/// may be paraphrased as follows:
+///
+/// @code
+///
+/// If the request includes a forward change:
+///     Select a forward server
+///     Send the server a request to add the forward entry
+///     If the server responds with already in use:
+///         Send a server a request to delete and then add forward entry
+///
+///     If the forward update is unsuccessful:
+///         abandon the update
+///
+/// If the request includes a reverse change:
+///     Select a reverse server
+///     Send a server a request to delete and then add reverse entry
+///
+/// @endcode
+///
+/// This class derives from NameChangeTransaction from which it inherits
+/// states, events, and methods common to NameChangeRequest processing.
+class NameAddTransaction : public NameChangeTransaction {
+public:
+
+    //@{  Additional states needed for NameAdd state model.
+    /// @brief State that attempts to add forward address records.
+    static const int ADDING_FWD_ADDRS_ST = NCT_DERIVED_STATE_MIN + 1;
+
+    /// @brief State that attempts to replace forward address records.
+    static const int REPLACING_FWD_ADDRS_ST = NCT_DERIVED_STATE_MIN + 2;
+
+    /// @brief State that attempts to replace reverse PTR records
+    static const int REPLACING_REV_PTRS_ST = NCT_DERIVED_STATE_MIN + 3;
+    //@}
+
+    //@{ Additional events needed for NameAdd state model.
+    /// @brief Event sent when an add attempt fails with address in use.
+    static const int FQDN_IN_USE_EVT = NCT_DERIVED_EVENT_MIN + 1;
+
+    /// @brief Event sent when replace attempt to fails with address not in use.
+    static const int FQDN_NOT_IN_USE_EVT = NCT_DERIVED_EVENT_MIN + 2;
+    //@}
+
+    /// @brief Constructor
+    ///
+    /// Instantiates an Add 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 NameAddTransaction error if given request is not a CHG_ADD,
+    /// NameChangeTransaction error for base class construction errors.
+    NameAddTransaction(IOServicePtr& io_service,
+                       dhcp_ddns::NameChangeRequestPtr& ncr,
+                       DdnsDomainPtr& forward_domain,
+                       DdnsDomainPtr& reverse_domain);
+
+    /// @brief Destructor
+    virtual ~NameAddTransaction();
+
+protected:
+    /// @brief Adds events defined by NameAddTransaction to the event set.
+    ///
+    /// Invokes NameChangeTransaction's implementation and then defines the
+    /// events unique to NCR Add transaction processing.
+    ///
+    /// @throw StateModelError if an event definition is invalid or a duplicate.
+    virtual void defineEvents();
+
+    /// @brief Validates the contents of the set of events.
+    ///
+    /// Invokes NameChangeTransaction's implementation and then verifies the
+    /// Add transaction's.  This tests that the needed events are in the event
+    /// dictionary.
+    ///
+    /// @throw StateModelError if an event value is undefined.
+    virtual void verifyEvents();
+
+    /// @brief Adds states defined by NameAddTransaction to the state set.
+    ///
+    /// Invokes NameChangeTransaction's implementation and then defines the
+    /// states unique to NCR Add transaction processing.
+    ///
+    /// @throw StateModelError if an state definition is invalid or a duplicate.
+    virtual void defineStates();
+
+    /// @brief Validates the contents of the set of states.
+    ///
+    /// Invokes NameChangeTransaction's implementation and then verifies the
+    /// Add transaction's states. This tests that the needed states are in the
+    /// state dictionary.
+    ///
+    /// @throw StateModelError if an event value is undefined.
+    virtual void verifyStates();
+
+    /// @brief State handler for READY_ST.
+    ///
+    /// Entered from:
+    /// - INIT_ST with next event of START_EVT
+    ///
+    /// The READY_ST is the state the model transitions into when the inherited
+    /// method, startTransaction() is invoked.  This handler, therefore, is the
+    /// entry point into the state model execution.h  Its primary task is to
+    /// determine whether to start with a forward DNS change or a reverse DNS
+    /// change.
+    ///
+    /// Transitions to:
+    /// - SELECTING_FWD_SERVER_ST with next event of SERVER_SELECT_ST if request
+    /// includes a forward change.
+    ///
+    /// - SELECTING_REV_SERVER_ST with next event of SERVER_SELECT_ST if request
+    /// includes only a reverse change.
+    ///
+    /// @throw NameAddTransactionError if upon entry next event is not
+    /// START_EVT.
+    void readyHandler();
+
+    /// @brief State handler for SELECTING_FWD_SERVER_ST.
+    ///
+    /// Entered from:
+    /// - READY_ST with next event of SELECT_SERVER_EVT
+    /// - ADDING_FWD_ADDRS_ST with next event of SERVER_IO_ERROR_EVT
+    /// - REPLACING_FWD_ADDRS_ST with next event of SERVER_IO_ERROR_EVT
+    ///
+    /// Selects the server to be used from the forward domain for the forward
+    /// DNS update.  If next event is SELECT_SERVER_EVT the handler initializes
+    /// the forward domain's server selection mechanism and then attempts to
+    /// select the next server. If next event is SERVER_IO_ERROR_EVT then the
+    /// handler simply attempts to select the next server.
+    ///
+    /// Transitions to:
+    /// - ADDING_FWD_ADDRS_ST with next event of SERVER_SELECTED upon successful
+    /// server selection
+    ///
+    /// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon
+    /// failure to select a server
+    ///
+    /// @throw NameAddTransactionError if upon entry next event is not
+    /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT.
+    void selectingFwdServerHandler();
+
+    /// @brief State handler for SELECTING_REV_SERVER_ST.
+    ///
+    /// Entered from:
+    /// - READY_ST with next event of SELECT_SERVER_EVT
+    /// - ADDING_FWD_ADDRS_ST with next event of SELECT_SERVER_EVT
+    /// - REPLACING_FWD_ADDRS_ST with next event of SELECT_SERVER_EVT
+    /// - REPLACING_REV_PTRS_ST with next event of SERVER_IO_ERROR_EVT
+    ///
+    /// Selects the server to be used from the reverse domain for the reverse
+    /// DNS update.  If next event is SELECT_SERVER_EVT the handler initializes
+    /// the reverse domain's server selection mechanism and then attempts to
+    /// select the next server. If next event is SERVER_IO_ERROR_EVT then the
+    /// handler simply attempts to select the next server.
+    ///
+    /// Transitions to:
+    /// - ADDING_REV_PTRS_ST with next event of SERVER_SELECTED upon successful
+    /// server selection
+    ///
+    /// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon
+    /// failure to select a server
+    ///
+    /// @throw NameAddTransactionError if upon entry next event is not
+    /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT.
+    void selectingRevServerHandler();
+
+    /// @brief State handler for ADD_FWD_ADDRS_ST.
+    ///
+    /// Entered from:
+    /// - SELECTING_FWD_SERVER with next event of SERVER_SELECTED_EVT
+    /// - REPLACING_FWD_ADDRS_ST with next event of SERVER_SELECTED_EVT
+    ///
+    /// Attempts to add a forward DNS entry for a given FQDN.  If this is
+    /// first invocation of the handler after transitioning into this state,
+    /// any previous update request context is deleted.   If next event
+    /// is SERVER_SELECTED_EVT, the handler builds the forward add request,
+    /// schedules an asynchronous send via sendUpdate(), and returns.  Note
+    /// that sendUpdate will post NOP_EVT as next event.
+    ///
+    /// Posting the NOP_EVT will cause runModel() to suspend execution of
+    /// the state model thus affecting a "wait" for the update IO to complete.
+    /// Update completion occurs via the DNSClient callback operator() method
+    /// inherited from NameChangeTransaction.  When invoked this callback will
+    /// post a next event of IO_COMPLETED_EVT and then invoke runModel which
+    /// resumes execution of the state model.
+    ///
+    /// When the handler is invoked with a next event of IO_COMPLETED_EVT,
+    /// the DNS update status is checked and acted upon accordingly:
+    ///
+    /// Transitions to:
+    /// - SELECTING_REV_SERVER_ST with next event of SELECT_SERVER_EVT upon
+    /// successful addition and the request includes a reverse DNS update.
+    ///
+    /// - PROCESS_TRANS_OK_ST with next event of UPDATE_OK_EVT upon successful
+    /// addition and no reverse DNS update is required.
+    ///
+    /// - REPLACING_FWD_ADDRS_ST with next event of FQDN_IN_USE_EVT if the DNS
+    /// server response indicates that an entry for the given FQDN already
+    /// exists.
+    ///
+    /// - PROCESS_TRANS_FAILED_ST with next event of UPDATE_FAILED_EVT if the
+    /// DNS server rejected the update for any other reason or the IO completed
+    /// with an unrecognized status.
+    ///
+    /// - RE-ENTER this states with next event of SERVER_SELECTED_EVT_EVT if
+    /// there was an IO error communicating with the server and the number of
+    /// per server retries has not been exhausted.
+    ///
+    /// - SELECTING_FWD_SERVER_ST with next event of SERVER_IO_ERROR_EVT if
+    /// there was an IO error communicating with the server and the number of
+    /// per server retries has been exhausted.
+    ///
+    /// @throw NameAddTransactionError if upon entry next event is not
+    /// SERVER_SELECTED_EVT or IO_COMPLETE_EVT.
+    void addingFwdAddrsHandler();
+
+    /// @brief State handler for REPLACING_FWD_ADDRS_ST.
+    ///
+    /// Entered from:
+    /// - ADDING_FWD_ADDRS_ST with next event of FQDN_IN_USE_EVT
+    ///
+    /// Attempts to delete and then add a forward DNS entry for a given
+    /// FQDN.  If this is first invocation of the handler after transitioning
+    /// into this state, any previous update request context is deleted.   If
+    /// next event is FDQN_IN_USE_EVT or SERVER_SELECTED_EVT, the handler
+    /// builds the forward replacement request, schedules an asynchronous send
+    /// via sendUpdate(), and returns.  Note that sendUpdate will post NOP_EVT
+    /// as the next event.
+    ///
+    /// Posting the NOP_EVT will cause runModel() to suspend execution of
+    /// the state model thus affecting a "wait" for the update IO to complete.
+    /// Update completion occurs via the DNSClient callback operator() method
+    /// inherited from NameChangeTransaction.  When invoked this callback will
+    /// post a next event of IO_COMPLETED_EVT and then invoke runModel which
+    /// resumes execution of the state model.
+    ///
+    /// When the handler is invoked with a next event of IO_COMPLETED_EVT,
+    /// the DNS update status is checked and acted upon accordingly:
+    ///
+    /// Transitions to:
+    /// - SELECTING_REV_SERVER_ST with a next event of SELECT_SERVER_EVT upon
+    /// successful replacement and the request includes a reverse DNS update.
+    ///
+    /// - PROCESS_TRANS_OK_ST with next event of UPDATE_OK_EVT upon successful
+    /// replacement and the request does not include a reverse DNS update.
+    ///
+    /// - ADDING_FWD_ADDR_STR with a next event of SERVER_SELECTED_EVT  if the
+    /// DNS server response indicates that the FQDN is not in use.  This could
+    /// occur if a previous add attempt indicated the FQDN was in use, but
+    /// that entry has since been removed by another entity prior to this
+    /// replacement attempt.
+    ///
+    /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT if the
+    /// DNS server rejected the update for any other reason or the IO completed
+    /// with an unrecognized status.
+    ///
+    /// - RE-ENTER this state with a next event of SERVER_SELECTED_EVT if
+    /// there was an IO error communicating with the server and the number of
+    /// per server retries has not been exhausted.
+    ///
+    /// - SELECTING_FWD_SERVER_ST with next event of SERVER_IO_ERROR_EVT if
+    /// there was an IO error communicating with the server and the number of
+    /// per server retries has been exhausted.
+    ///
+    /// @throw NameAddTransactionError if upon entry next event is not:
+    /// FQDN_IN_USE_EVT, SERVER_SELECTED_EVT or IO_COMPLETE_EVT.
+    void replacingFwdAddrsHandler();
+
+    /// @brief State handler for REPLACING_REV_PTRS_ST.
+    ///
+    /// Entered from:
+    /// - SELECTING_REV_SERVER_ST with a next event of SERVER_SELECTED_EVT
+    ///
+    /// Attempts to delete and then add a reverse DNS entry for a given FQDN.
+    /// If this is first invocation of the handler after transitioning into
+    /// this state, any previous update request context is deleted.  If next
+    /// event is SERVER_SELECTED_EVT, the handler builds the reverse replacement
+    /// add request, schedules an asynchronous send via sendUpdate(), and
+    /// returns.  Note that sendUpdate will post NOP_EVT as next event.
+    ///
+    /// Posting the NOP_EVT will cause runModel() to suspend execution of
+    /// the state model thus affecting a "wait" for the update IO to complete.
+    /// Update completion occurs via the DNSClient callback operator() method
+    /// inherited from NameChangeTransaction.  When invoked this callback will
+    /// post a next event of IO_COMPLETED_EVT and then invoke runModel which
+    /// resumes execution of the state model.
+    ///
+    /// When the handler is invoked with a next event of IO_COMPLETED_EVT,
+    /// the DNS update status is checked and acted upon accordingly:
+    ///
+    /// Transitions to:
+    /// - PROCESS_TRANS_OK_ST with a next event of UPDATE_OK_EVT upon
+    /// successful replacement.
+    ///
+    /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT If the
+    /// DNS server rejected the update for any reason or the IO completed
+    /// with an unrecognized status.
+    ///
+    /// - RE-ENTER this state with a next event of SERVER_SELECTED_EVT if
+    /// there was an IO error communicating with the server and the number of
+    /// per server retries has not been exhausted.
+    ///
+    /// - SELECTING_REV_SERVER_ST with next event of SERVER_IO_ERROR_EVT if
+    /// there was an IO error communicating with the server and the number of
+    /// per server retries has been exhausted.
+    ///
+    /// @throw NameAddTransactionError if upon entry next event is not:
+    /// SERVER_SELECTED_EVT or IO_COMPLETED_EVT
+    void replacingRevPtrsHandler();
+
+    /// @brief State handler for PROCESS_TRANS_OK_ST.
+    ///
+    /// Entered from:
+    /// - ADDING_FWD_ADDRS_ST with a next event of UPDATE_OK_EVT
+    /// - REPLACING_FWD_ADDRS_ST with a next event of UPDATE_OK_EVT
+    /// - REPLACING_REV_PTRS_ST with a next event of UPDATE_OK_EVT
+    ///
+    /// Sets the transaction action status to indicate success and ends
+    /// model execution.
+    ///
+    /// Transitions to:
+    /// - END_ST with a next event of END_EVT.
+    ///
+    /// @throw NameAddTransactionError if upon entry next event is not:
+    /// UPDATE_OK_EVT
+    void processAddOkHandler();
+
+    /// @brief State handler for PROCESS_TRANS_FAILED_ST.
+    ///
+    /// Entered from:
+    /// - SELECTING_FWD_SERVER_ST with a next event of NO_MORE_SERVERS
+    /// - ADDING_FWD_ADDRS_ST with a next event of UPDATE_FAILED_EVT
+    /// - REPLACING_FWD_ADDRS_ST with a next event of UPDATE_FAILED_EVT
+    /// - SELECTING_REV_SERVER_ST with a next event of NO_MORE_SERVERS
+    /// - REPLACING_REV_PTRS_ST with a next event of UPDATE_FAILED_EVT
+    ///
+    /// Sets the transaction status to indicate failure and ends
+    /// model execution.
+    ///
+    /// Transitions to:
+    /// - END_ST with a next event of FAIL_EVT.
+    ///
+    /// @throw NameAddTransactionError if upon entry next event is not:
+    /// UPDATE_FAILED_EVT
+    void processAddFailedHandler();
+
+    /// @brief Builds a DNS request to add an forward DNS entry for an FQDN
+    ///
+    /// Constructs a DNS update request, based upon the NCR, for adding a
+    /// forward DNS mapping.  Once constructed, the request is stored as
+    /// the transaction's DNS update request.
+    ///
+    /// The request content is adherent to RFC 4703 section 5.3.1:
+    ///
+    /// Prerequisite RRsets:
+    /// 1. An assertion that the FQDN does not exist
+    ///
+    /// Updates RRsets:
+    /// 1. An FQDN/IP RR addition    (type A for IPv4, AAAA for IPv6)
+    /// 2. An FQDN/DHCID RR addition (type DHCID)
+    ///
+    /// @throw This method does not throw but underlying methods may.
+    void buildAddFwdAddressRequest();
+
+    /// @brief Builds a DNS request to replace forward DNS entry for an FQDN
+    ///
+    /// Constructs a DNS update request, based upon the NCR, for replacing a
+    /// forward DNS mapping.  Once constructed, the request is stored as
+    /// the transaction's DNS update request.
+    ///
+    /// The request content is adherent to RFC 4703 section 5.3.2:
+    ///
+    /// Prerequisite RRsets:
+    /// 1. An assertion that the FQDN is in use
+    /// 2. An assertion that the FQDN/DHCID RR exists for the lease client's
+    /// DHCID.
+    ///
+    /// Updates RRsets:
+    /// 1. A deletion of any existing FQDN RRs (type A for IPv4, AAAA for IPv6)
+    /// 2. A FQDN/IP RR addition (type A for IPv4, AAAA for IPv6)
+    ///
+    /// @throw This method does not throw but underlying methods may.
+    void buildReplaceFwdAddressRequest();
+
+    /// @brief Builds a DNS request to replace a reverse DNS entry for an FQDN
+    ///
+    /// Constructs a DNS update request, based upon the NCR, for replacing a
+    /// reverse DNS mapping.  Once constructed, the request is stored as
+    /// the transaction's DNS update request.
+    ///
+    /// The request content is adherent to RFC 4703 section 5.4:
+    ///
+    /// Prerequisite RRsets:
+    /// - There are not prerequisites.
+    ///
+    /// Updates RRsets:
+    /// 1. A delete of any existing PTR RRs for the lease address
+    /// 2. A delete of any existing DHCID RRs for the lease address
+    /// 3. A PTR RR addition for the lease address and FQDN
+    /// 4. A DHCID RR addition for the lease address and lease client DHCID
+    ///
+    /// @throw This method does not throw but underlying methods may.
+    void buildReplaceRevPtrsRequest();
+};
+
+/// @brief Defines a pointer to a NameAddTransaction.
+typedef boost::shared_ptr<NameAddTransaction> NameAddTransactionPtr;
+
+} // namespace isc::d2
+} // namespace isc
+#endif
diff --git a/src/bin/d2/nc_remove.cc b/src/bin/d2/nc_remove.cc
new file mode 100644
index 0000000..d7135bd
--- /dev/null
+++ b/src/bin/d2/nc_remove.cc
@@ -0,0 +1,695 @@
+// 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/d2_cfg_mgr.h>
+#include <d2/nc_remove.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+
+namespace isc {
+namespace d2 {
+
+
+// NameRemoveTransaction states
+const int NameRemoveTransaction::REMOVING_FWD_ADDRS_ST;
+const int NameRemoveTransaction::REMOVING_FWD_RRS_ST;
+const int NameRemoveTransaction::REMOVING_REV_PTRS_ST;
+
+// NameRemoveTransaction events
+// Currently NameRemoveTransaction does not define any events.
+
+NameRemoveTransaction::
+NameRemoveTransaction(IOServicePtr& io_service,
+                   dhcp_ddns::NameChangeRequestPtr& ncr,
+                   DdnsDomainPtr& forward_domain,
+                   DdnsDomainPtr& reverse_domain)
+    : NameChangeTransaction(io_service, ncr, forward_domain, reverse_domain) {
+    if (ncr->getChangeType() != isc::dhcp_ddns::CHG_REMOVE) {
+        isc_throw (NameRemoveTransactionError,
+                   "NameRemoveTransaction, request type must be CHG_REMOVE");
+    }
+}
+
+NameRemoveTransaction::~NameRemoveTransaction(){
+}
+
+void
+NameRemoveTransaction::defineEvents() {
+    // Call superclass impl first.
+    NameChangeTransaction::defineEvents();
+
+    // Define NameRemoveTransaction events.
+    // Currently NameRemoveTransaction does not define any events.
+    // defineEvent(TBD_EVENT, "TBD_EVT");
+}
+
+void
+NameRemoveTransaction::verifyEvents() {
+    // Call superclass implementation first to verify its events. These are
+    // events common to all transactions, and they must be defined.
+    // SELECT_SERVER_EVT
+    // SERVER_SELECTED_EVT
+    // SERVER_IO_ERROR_EVT
+    // NO_MORE_SERVERS_EVT
+    // IO_COMPLETED_EVT
+    // UPDATE_OK_EVT
+    // UPDATE_FAILED_EVT
+    NameChangeTransaction::verifyEvents();
+
+    // Verify NameRemoveTransaction events by attempting to fetch them.
+    // Currently NameRemoveTransaction does not define any events.
+    // getEvent(TBD_EVENT);
+}
+
+void
+NameRemoveTransaction::defineStates() {
+    // Call superclass impl first.
+    NameChangeTransaction::defineStates();
+
+    // Define NameRemoveTransaction states.
+    defineState(READY_ST, "READY_ST",
+                boost::bind(&NameRemoveTransaction::readyHandler, this));
+
+    defineState(SELECTING_FWD_SERVER_ST, "SELECTING_FWD_SERVER_ST",
+                boost::bind(&NameRemoveTransaction::selectingFwdServerHandler,
+                            this));
+
+    defineState(SELECTING_REV_SERVER_ST, "SELECTING_REV_SERVER_ST",
+                boost::bind(&NameRemoveTransaction::selectingRevServerHandler,
+                            this));
+
+    defineState(REMOVING_FWD_ADDRS_ST, "REMOVING_FWD_ADDRS_ST",
+                boost::bind(&NameRemoveTransaction::removingFwdAddrsHandler,
+                            this));
+
+    defineState(REMOVING_FWD_RRS_ST, "REMOVING_FWD_RRS_ST",
+                boost::bind(&NameRemoveTransaction::removingFwdRRsHandler,
+                            this));
+
+    defineState(REMOVING_REV_PTRS_ST, "REMOVING_REV_PTRS_ST",
+                boost::bind(&NameRemoveTransaction::removingRevPtrsHandler,
+                            this));
+
+    defineState(PROCESS_TRANS_OK_ST, "PROCESS_TRANS_OK_ST",
+                boost::bind(&NameRemoveTransaction::processRemoveOkHandler,
+                            this));
+
+    defineState(PROCESS_TRANS_FAILED_ST, "PROCESS_TRANS_FAILED_ST",
+                boost::bind(&NameRemoveTransaction::processRemoveFailedHandler,
+                            this));
+}
+
+void
+NameRemoveTransaction::verifyStates() {
+    // Call superclass implementation first to verify its states. These are
+    // states common to all transactions, and they must be defined.
+    // READY_ST
+    // SELECTING_FWD_SERVER_ST
+    // SELECTING_REV_SERVER_ST
+    // PROCESS_TRANS_OK_ST
+    // PROCESS_TRANS_FAILED_ST
+    NameChangeTransaction::verifyStates();
+
+    // Verify NameRemoveTransaction states by attempting to fetch them.
+    getState(REMOVING_FWD_ADDRS_ST);
+    getState(REMOVING_FWD_RRS_ST);
+    getState(REMOVING_REV_PTRS_ST);
+}
+
+void
+NameRemoveTransaction::readyHandler() {
+    switch(getNextEvent()) {
+    case START_EVT:
+        if (getForwardDomain()) {
+            // Request includes a forward change, do that first.
+            transition(SELECTING_FWD_SERVER_ST, SELECT_SERVER_EVT);
+        } else {
+            // Reverse change only, transition accordingly.
+            transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT);
+        }
+
+        break;
+    default:
+        // Event is invalid.
+        isc_throw(NameRemoveTransactionError,
+                  "Wrong event for context: " << getContextStr());
+    }
+}
+
+void
+NameRemoveTransaction::selectingFwdServerHandler() {
+    switch(getNextEvent()) {
+    case SELECT_SERVER_EVT:
+        // First time through for this transaction, so initialize server
+        // selection.
+        initServerSelection(getForwardDomain());
+        break;
+    case SERVER_IO_ERROR_EVT:
+        // We failed to communicate with current server. Attempt to select
+        // another one below.
+        break;
+    default:
+        // Event is invalid.
+        isc_throw(NameRemoveTransactionError,
+                  "Wrong event for context: " << getContextStr());
+    }
+
+    // Select the next server from the list of forward servers.
+    if (selectNextServer()) {
+        // We have a server to try.
+        transition(REMOVING_FWD_ADDRS_ST, SERVER_SELECTED_EVT);
+    }
+    else {
+        // Server list is exhausted, so fail the transaction.
+        transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT);
+    }
+}
+
+void
+NameRemoveTransaction::removingFwdAddrsHandler() {
+    if (doOnEntry()) {
+        // Clear the request on initial transition. This allows us to reuse
+        // the request on retries if necessary.
+        clearDnsUpdateRequest();
+    }
+
+    switch(getNextEvent()) {
+    case SERVER_SELECTED_EVT:
+        if (!getDnsUpdateRequest()) {
+            // Request hasn't been constructed yet, so build it.
+            try {
+                buildRemoveFwdAddressRequest();
+            } catch (const std::exception& ex) {
+                // While unlikely, the build might fail if we have invalid
+                // data.  Should that be the case, we need to fail the
+                // transaction.
+                LOG_ERROR(dctl_logger,
+                          DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE)
+                          .arg(getNcr()->toText())
+                          .arg(ex.what());
+                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+                break;
+            }
+        }
+
+        // Call sendUpdate() to initiate the async send. Note it also sets
+        // next event to NOP_EVT.
+        sendUpdate();
+        break;
+
+    case IO_COMPLETED_EVT: {
+        switch (getDnsUpdateStatus()) {
+        case DNSClient::SUCCESS: {
+            // We successfully received a response packet from the server.
+            const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
+            if ((rcode == dns::Rcode::NOERROR()) ||
+                (rcode == dns::Rcode::NXDOMAIN())) {
+                // We were able to remove it or it wasn't there, now we
+                // need to remove any other RRs for this FQDN.
+                transition(REMOVING_FWD_RRS_ST, UPDATE_OK_EVT);
+            } else {
+                // Per RFC4703 any other value means cease.
+                // If we get not authorized should we try the next server in
+                // the list? @todo  This needs some discussion perhaps.
+                LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED)
+                          .arg(getCurrentServer()->getIpAddress())
+                          .arg(getNcr()->getFqdn())
+                          .arg(rcode.getCode());
+                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+            }
+
+            break;
+        }
+
+        case DNSClient::TIMEOUT:
+        case DNSClient::OTHER:
+            // We couldn't send to the current server, log it and set up
+            // to select the next server for a retry.
+            // @note For now we treat OTHER as an IO error like TIMEOUT. It
+            // is not entirely clear if this is accurate.
+            LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR)
+                      .arg(getNcr()->getFqdn())
+                      .arg(getCurrentServer()->getIpAddress());
+
+            retryTransition(SELECTING_FWD_SERVER_ST);
+            break;
+
+        case DNSClient::INVALID_RESPONSE:
+            // A response was received but was corrupt. Retry it like an IO
+            // error.
+            LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT)
+                      .arg(getCurrentServer()->getIpAddress())
+                      .arg(getNcr()->getFqdn());
+
+            retryTransition(SELECTING_FWD_SERVER_ST);
+            break;
+
+        default:
+            // Any other value and we will fail this transaction, something
+            // bigger is wrong.
+            LOG_ERROR(dctl_logger,
+                      DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS)
+                      .arg(getDnsUpdateStatus())
+                      .arg(getNcr()->getFqdn())
+                      .arg(getCurrentServer()->getIpAddress());
+
+            transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+            break;
+        } // end switch on dns_status
+
+        break;
+    } // end case IO_COMPLETE_EVT
+
+    default:
+        // Event is invalid.
+        isc_throw(NameRemoveTransactionError,
+                  "Wrong event for context: " << getContextStr());
+    }
+}
+
+
+void
+NameRemoveTransaction::removingFwdRRsHandler() {
+    if (doOnEntry()) {
+        // Clear the request on initial transition. This allows us to reuse
+        // the request on retries if necessary.
+        clearDnsUpdateRequest();
+    }
+
+    switch(getNextEvent()) {
+    case UPDATE_OK_EVT:
+    case SERVER_SELECTED_EVT:
+        if (!getDnsUpdateRequest()) {
+            // Request hasn't been constructed yet, so build it.
+            try {
+                buildRemoveFwdRRsRequest();
+            } catch (const std::exception& ex) {
+                // While unlikely, the build might fail if we have invalid
+                // data.  Should that be the case, we need to fail the
+                // transaction.
+                LOG_ERROR(dctl_logger,
+                          DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE)
+                          .arg(getNcr()->toText())
+                          .arg(ex.what());
+                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+                break;
+            }
+        }
+
+        // Call sendUpdate() to initiate the async send. Note it also sets
+        // next event to NOP_EVT.
+        sendUpdate();
+        break;
+
+    case IO_COMPLETED_EVT: {
+        switch (getDnsUpdateStatus()) {
+        case DNSClient::SUCCESS: {
+            // We successfully received a response packet from the server.
+            const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
+            // @todo Not sure if NXDOMAIN is ok here, but I think so.
+            // A Rcode of NXDOMAIN would mean there are no RRs for the FQDN,
+            // which is fine.  We were asked to delete them, they are not there
+            // so all is well.
+            if ((rcode == dns::Rcode::NOERROR()) ||
+                (rcode == dns::Rcode::NXDOMAIN())) {
+                // We were able to remove the forward mapping. Mark it as done.
+                setForwardChangeCompleted(true);
+
+                // If request calls for reverse update then do that next,
+                // otherwise we can process ok.
+                if (getReverseDomain()) {
+                    transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT);
+                } else {
+                    transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT);
+                }
+            } else {
+                // Per RFC4703 any other value means cease.
+                // If we get not authorized should try the next server in
+                // the list? @todo  This needs some discussion perhaps.
+                LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED)
+                          .arg(getCurrentServer()->getIpAddress())
+                          .arg(getNcr()->getFqdn())
+                          .arg(rcode.getCode());
+                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+            }
+
+            break;
+        }
+
+        case DNSClient::TIMEOUT:
+        case DNSClient::OTHER:
+            // We couldn't send to the current server, log it and set up
+            // to select the next server for a retry.
+            // @note For now we treat OTHER as an IO error like TIMEOUT. It
+            // is not entirely clear if this is accurate.
+            LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR)
+                      .arg(getNcr()->getFqdn())
+                      .arg(getCurrentServer()->getIpAddress());
+
+            // @note If we exhaust the IO retries for the current server
+            // due to IO failures, we will abort the remaining updates.
+            // The rational is that we are only in this state, if the remove
+            // of the forward address RR succeeded (removingFwdAddrsHandler)
+            // on the current server. Therefore  we should not attempt another
+            // removal on a different server.  This is perhaps a point
+            // for discussion.
+            // @todo Should we go ahead with the reverse remove?
+            retryTransition(PROCESS_TRANS_FAILED_ST);
+            break;
+
+        case DNSClient::INVALID_RESPONSE:
+            // A response was received but was corrupt. Retry it like an IO
+            // error.
+            LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT)
+                      .arg(getCurrentServer()->getIpAddress())
+                      .arg(getNcr()->getFqdn());
+
+            // If we are out of retries on this server abandon the transaction.
+            // (Same logic as the case for TIMEOUT above).
+            retryTransition(PROCESS_TRANS_FAILED_ST);
+            break;
+
+        default:
+            // Any other value and we will fail this transaction, something
+            // bigger is wrong.
+            LOG_ERROR(dctl_logger,
+                      DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS)
+                      .arg(getDnsUpdateStatus())
+                      .arg(getNcr()->getFqdn())
+                      .arg(getCurrentServer()->getIpAddress());
+
+            transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+            break;
+        } // end switch on dns_status
+
+        break;
+    } // end case IO_COMPLETE_EVT
+
+    default:
+        // Event is invalid.
+        isc_throw(NameRemoveTransactionError,
+                  "Wrong event for context: " << getContextStr());
+    }
+}
+
+
+void
+NameRemoveTransaction::selectingRevServerHandler() {
+    switch(getNextEvent()) {
+    case SELECT_SERVER_EVT:
+        // First time through for this transaction, so initialize server
+        // selection.
+        initServerSelection(getReverseDomain());
+        break;
+    case SERVER_IO_ERROR_EVT:
+        // We failed to communicate with current server. Attempt to select
+        // another one below.
+        break;
+    default:
+        // Event is invalid.
+        isc_throw(NameRemoveTransactionError,
+                  "Wrong event for context: " << getContextStr());
+    }
+
+    // Select the next server from the list of forward servers.
+    if (selectNextServer()) {
+        // We have a server to try.
+        transition(REMOVING_REV_PTRS_ST, SERVER_SELECTED_EVT);
+    }
+    else {
+        // Server list is exhausted, so fail the transaction.
+        transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT);
+    }
+}
+
+
+void
+NameRemoveTransaction::removingRevPtrsHandler() {
+    if (doOnEntry()) {
+        // Clear the request on initial transition. This allows us to reuse
+        // the request on retries if necessary.
+        clearDnsUpdateRequest();
+    }
+
+    switch(getNextEvent()) {
+    case SERVER_SELECTED_EVT:
+        if (!getDnsUpdateRequest()) {
+            // Request hasn't been constructed yet, so build it.
+            try {
+                buildRemoveRevPtrsRequest();
+            } catch (const std::exception& ex) {
+                // While unlikely, the build might fail if we have invalid
+                // data.  Should that be the case, we need to fail the
+                // transaction.
+                LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE)
+                          .arg(getNcr()->toText())
+                          .arg(ex.what());
+                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+                break;
+            }
+        }
+
+        // Call sendUpdate() to initiate the async send. Note it also sets
+        // next event to NOP_EVT.
+        sendUpdate();
+        break;
+
+    case IO_COMPLETED_EVT: {
+        switch (getDnsUpdateStatus()) {
+        case DNSClient::SUCCESS: {
+            // We successfully received a response packet from the server.
+            const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
+            if ((rcode == dns::Rcode::NOERROR()) ||
+                (rcode == dns::Rcode::NXDOMAIN())) {
+                // We were able to update the reverse mapping. Mark it as done.
+                // @todo For now we are also treating NXDOMAIN as success.
+                setReverseChangeCompleted(true);
+                transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT);
+            } else {
+                // Per RFC4703 any other value means cease.
+                // If we get not authorized should try the next server in
+                // the list? @todo  This needs some discussion perhaps.
+                LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REMOVE_REJECTED)
+                          .arg(getCurrentServer()->getIpAddress())
+                          .arg(getNcr()->getFqdn())
+                          .arg(rcode.getCode());
+                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+            }
+
+            break;
+        }
+
+        case DNSClient::TIMEOUT:
+        case DNSClient::OTHER:
+            // We couldn't send to the current server, log it and set up
+            // to select the next server for a retry.
+            // @note For now we treat OTHER as an IO error like TIMEOUT. It
+            // is not entirely clear if this is accurate.
+            LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REMOVE_IO_ERROR)
+                      .arg(getNcr()->getFqdn())
+                      .arg(getCurrentServer()->getIpAddress());
+
+            // If we are out of retries on this server, we go back and start
+            // all over on a new server.
+            retryTransition(SELECTING_REV_SERVER_ST);
+            break;
+
+        case DNSClient::INVALID_RESPONSE:
+            // A response was received but was corrupt. Retry it like an IO
+            // error.
+            LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT)
+                      .arg(getCurrentServer()->getIpAddress())
+                      .arg(getNcr()->getFqdn());
+
+            // If we are out of retries on this server, we go back and start
+            // all over on a new server.
+            retryTransition(SELECTING_REV_SERVER_ST);
+            break;
+
+        default:
+            // Any other value and we will fail this transaction, something
+            // bigger is wrong.
+            LOG_ERROR(dctl_logger,
+                      DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS)
+                      .arg(getDnsUpdateStatus())
+                      .arg(getNcr()->getFqdn())
+                      .arg(getCurrentServer()->getIpAddress());
+
+            transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+            break;
+        } // end switch on dns_status
+
+        break;
+    } // end case IO_COMPLETE_EVT
+
+    default:
+        // Event is invalid.
+        isc_throw(NameRemoveTransactionError,
+                  "Wrong event for context: " << getContextStr());
+    }
+}
+
+
+void
+NameRemoveTransaction::processRemoveOkHandler() {
+    switch(getNextEvent()) {
+    case UPDATE_OK_EVT:
+        LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL, DHCP_DDNS_REMOVE_SUCCEEDED)
+                  .arg(getNcr()->toText());
+        setNcrStatus(dhcp_ddns::ST_COMPLETED);
+        endModel();
+        break;
+    default:
+        // Event is invalid.
+        isc_throw(NameRemoveTransactionError,
+                  "Wrong event for context: " << getContextStr());
+    }
+}
+
+void
+NameRemoveTransaction::processRemoveFailedHandler() {
+    switch(getNextEvent()) {
+    case UPDATE_FAILED_EVT:
+    case NO_MORE_SERVERS_EVT:
+    case SERVER_IO_ERROR_EVT:
+        LOG_ERROR(dctl_logger, DHCP_DDNS_REMOVE_FAILED).arg(getNcr()->toText())
+        .arg(getEventLabel(getNextEvent()));
+        setNcrStatus(dhcp_ddns::ST_FAILED);
+        endModel();
+        break;
+    default:
+        // Event is invalid.
+        isc_throw(NameRemoveTransactionError,
+                  "Wrong event for context: " << getContextStr());
+    }
+}
+
+void
+NameRemoveTransaction::buildRemoveFwdAddressRequest() {
+    // Construct an empty request.
+    D2UpdateMessagePtr request = prepNewRequest(getForwardDomain());
+
+    // Content on this request is based on RFC 4703, section 5.5, paragraph 4.
+    // Construct dns::Name from NCR fqdn.
+    dns::Name fqdn(dns::Name(getNcr()->getFqdn()));
+    // First build the Prerequisite Section
+
+    // Create an DHCID matches prerequisite RR and add it to the
+    // pre-requisite section
+    // Based on RFC 2136, section 2.4.2.
+    dns::RRsetPtr prereq(new dns::RRset(fqdn, dns::RRClass::IN(),
+                                        dns::RRType::DHCID(), dns::RRTTL(0)));
+    addDhcidRdata(prereq);
+    request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
+
+    // Next build the Update Section
+
+    // Create the FQDN/IP 'delete' RR and add it to the update section.
+    // Add the RR to update section.
+    // Based on 2136 section 2.5.4
+    dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::NONE(),
+                         getAddressRRType(), dns::RRTTL(0)));
+    addLeaseAddressRdata(update);
+    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+    // Set the transaction's update request to the new request.
+    setDnsUpdateRequest(request);
+}
+
+void
+NameRemoveTransaction::buildRemoveFwdRRsRequest() {
+    // Construct an empty request.
+    D2UpdateMessagePtr request = prepNewRequest(getForwardDomain());
+
+    // Construct dns::Name from NCR fqdn.
+    dns::Name fqdn(dns::Name(getNcr()->getFqdn()));
+
+    // Content on this request is based on RFC 4703, section 5.5, paragraph 5.
+    // First build the Prerequisite Section.
+
+    // Now create an DHCID matches prerequisite RR.
+    // Set the RR's RData to DHCID.
+    // Add it to the pre-requisite section.
+    // Based on RFC 2136, section 2.4.2.
+    dns::RRsetPtr prereq(new dns::RRset(fqdn, dns::RRClass::IN(),
+                         dns::RRType::DHCID(), dns::RRTTL(0)));
+    addDhcidRdata(prereq);
+    request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
+
+    // Create an assertion that there are no A RRs for the FQDN.
+    // Add it to the pre-reqs.
+    // Based on RFC 2136, section 2.4.3.
+    prereq.reset(new dns::RRset(fqdn, dns::RRClass::NONE(),
+                                dns::RRType::A(), dns::RRTTL(0)));
+    request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
+
+    // Create an assertion that there are no A RRs for the FQDN.
+    // Add it to the pre-reqs.
+    // Based on RFC 2136, section 2.4.3.
+    prereq.reset(new dns::RRset(fqdn, dns::RRClass::NONE(),
+                                dns::RRType::AAAA(), dns::RRTTL(0)));
+    request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
+
+    // Next build the Update Section.
+
+    // Create the 'delete' of all RRs for FQDN.
+    // Set the message RData to lease address.
+    // Add the RR to update section.
+    // Based on RFC 2136, section 2.5.3.
+    dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::ANY(),
+                         dns::RRType::ANY(), dns::RRTTL(0)));
+    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+    // Set the transaction's update request to the new request.
+    setDnsUpdateRequest(request);
+}
+
+void
+NameRemoveTransaction::buildRemoveRevPtrsRequest() {
+    // Construct an empty request.
+    D2UpdateMessagePtr request = prepNewRequest(getReverseDomain());
+
+    // Create the reverse IP address "FQDN".
+    std::string rev_addr = D2CfgMgr::reverseIpAddress(getNcr()->getIpAddress());
+    dns::Name rev_ip(rev_addr);
+
+    // Content on this request is based on RFC 4703, section 5.5, paragraph 2.
+    // First build the Prerequisite Section.
+    // (Note that per RFC 4703, section 5.4, there is no need to validate
+    // DHCID RR for PTR entries.)
+
+    // Create an assertion that the PTRDNAME in the PTR record matches the
+    // client's FQDN for the address that was released.
+    // Based on RFC 2136, section 3.2.3
+    dns::RRsetPtr prereq(new dns::RRset(rev_ip, dns::RRClass::IN(),
+                                        dns::RRType::PTR(), dns::RRTTL(0)));
+    addPtrRdata(prereq);
+    request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
+
+    // Now, build the Update section.
+
+    // Create a delete of any RRs for the FQDN and add it to update section.
+    // Based on RFC 2136, section 3.4.2.3
+    dns::RRsetPtr update(new dns::RRset(rev_ip, dns::RRClass::ANY(),
+                         dns::RRType::ANY(), dns::RRTTL(0)));
+    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+    // Set the transaction's update request to the new request.
+    setDnsUpdateRequest(request);
+}
+
+} // namespace isc::d2
+} // namespace isc
diff --git a/src/bin/d2/nc_remove.h b/src/bin/d2/nc_remove.h
new file mode 100644
index 0000000..f7b0dcc
--- /dev/null
+++ b/src/bin/d2/nc_remove.h
@@ -0,0 +1,435 @@
+// 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_REMOVE_H
+#define NC_REMOVE_H
+
+/// @file nc_remove.h This file defines the class NameRemoveTransaction.
+
+#include <d2/nc_trans.h>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Thrown if the NameRemoveTransaction encounters a general error.
+class NameRemoveTransactionError : public isc::Exception {
+public:
+    NameRemoveTransactionError(const char* file, size_t line,
+                               const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Embodies the "life-cycle" required to carry out a DDNS Remove update.
+///
+/// NameRemoveTransaction implements a state machine for removing a forward
+/// and/or reverse DNS mappings. This state machine is based upon the processing
+/// logic described in RFC 4703, Section 5.5. That logic may be paraphrased as
+/// follows:
+///
+/// @code
+///
+/// If the request includes a forward change:
+///     Select a forward server
+///     Send the server a request to remove client's specific forward address RR
+///     If it succeeds or the server responds with name no longer in use
+///         Send a server a request to delete any other RRs for that FQDN, such
+///         as the DHCID RR.
+///     otherwise
+///         abandon the update
+///
+/// If the request includes a reverse change:
+///     Select a reverse server
+///     Send a server a request to delete reverse entry (PTR RR)
+///
+/// @endcode
+///
+/// This class derives from NameChangeTransaction from which it inherits
+/// states, events, and methods common to NameChangeRequest processing.
+class NameRemoveTransaction : public NameChangeTransaction {
+public:
+
+    //@{  Additional states needed for NameRemove state model.
+    /// @brief State that attempts to remove specific forward address record.
+    static const int REMOVING_FWD_ADDRS_ST = NCT_DERIVED_STATE_MIN + 1;
+
+    /// @brief State that attempts to remove any other forward RRs for the DHCID
+    static const int REMOVING_FWD_RRS_ST = NCT_DERIVED_STATE_MIN + 2;
+
+    /// @brief State that attempts to remove reverse PTR records
+    static const int REMOVING_REV_PTRS_ST = NCT_DERIVED_STATE_MIN + 3;
+    //@}
+
+    //@{ Additional events needed for NameRemove state model.
+    /// @brief Event sent when replace attempt to fails with address not in use.
+    /// @todo Currently none have been identified.
+    //@}
+
+    /// @brief Constructor
+    ///
+    /// Instantiates an Remove 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 NameRemoveTransaction error if given request is not a CHG_REMOVE,
+    /// NameChangeTransaction error for base class construction errors.
+    NameRemoveTransaction(IOServicePtr& io_service,
+                          dhcp_ddns::NameChangeRequestPtr& ncr,
+                          DdnsDomainPtr& forward_domain,
+                          DdnsDomainPtr& reverse_domain);
+
+    /// @brief Destructor
+    virtual ~NameRemoveTransaction();
+
+protected:
+    /// @brief Adds events defined by NameRemoveTransaction to the event set.
+    ///
+    /// Invokes NameChangeTransaction's implementation and then defines the
+    /// events unique to NCR Remove transaction processing.
+    ///
+    /// @throw StateModelError if an event definition is invalid or a duplicate.
+    virtual void defineEvents();
+
+    /// @brief Validates the contents of the set of events.
+    ///
+    /// Invokes NameChangeTransaction's implementation and then verifies the
+    /// Remove transaction's events. This tests that the needed events are in
+    /// the event dictionary.
+    ///
+    /// @throw StateModelError if an event value is undefined.
+    virtual void verifyEvents();
+
+    /// @brief Adds states defined by NameRemoveTransaction to the state set.
+    ///
+    /// Invokes NameChangeTransaction's implementation and then defines the
+    /// states unique to NCR Remove transaction processing.
+    ///
+    /// @throw StateModelError if an state definition is invalid or a duplicate.
+    virtual void defineStates();
+
+    /// @brief Validates the contents of the set of states.
+    ///
+    /// Invokes NameChangeTransaction's implementation and then verifies the
+    /// Remove transaction's states.  This tests that the needed states are in
+    /// the state dictionary.
+    ///
+    /// @throw StateModelError if an event value is undefined.
+    virtual void verifyStates();
+
+    /// @brief State handler for READY_ST.
+    ///
+    /// Entered from:
+    /// - INIT_ST with next event of START_EVT
+    ///
+    /// The READY_ST is the state the model transitions into when the inherited
+    /// method, startTransaction() is invoked.  This handler, therefore, is the
+    /// entry point into the state model execution.  Its primary task is to
+    /// determine whether to start with a forward DNS change or a reverse DNS
+    /// change.
+    ///
+    /// Transitions to:
+    /// - SELECTING_FWD_SERVER_ST with next event of SERVER_SELECT_ST if request
+    /// includes a forward change.
+    ///
+    /// - SELECTING_REV_SERVER_ST with next event of SERVER_SELECT_ST if request
+    /// includes only a reverse change.
+    ///
+    /// @throw NameRemoveTransactionError if upon entry next event is not
+    /// START_EVT.
+    void readyHandler();
+
+    /// @brief State handler for SELECTING_FWD_SERVER_ST.
+    ///
+    /// Entered from:
+    /// - READY_ST with next event of SELECT_SERVER_EVT
+    /// - REMOVING_FWD_ADDRS_ST with next event of SERVER_IO_ERROR_EVT
+    ///
+    /// Selects the server to be used from the forward domain for the forward
+    /// DNS update.  If next event is SELECT_SERVER_EVT the handler initializes
+    /// the forward domain's server selection mechanism and then attempts to
+    /// select the next server. If next event is SERVER_IO_ERROR_EVT then the
+    /// handler simply attempts to select the next server.
+    ///
+    /// Transitions to:
+    /// - REMOVING_FWD_ADDRS_ST with next event of SERVER_SELECTED upon
+    /// successful server selection
+    ///
+    /// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon
+    /// failure to select a server
+    ///
+    /// @throw NameRemoveTransactionError if upon entry next event is not
+    /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT.
+    void selectingFwdServerHandler();
+
+    /// @brief State handler for SELECTING_REV_SERVER_ST.
+    ///
+    /// Entered from:
+    /// - READY_ST with next event of SELECT_SERVER_EVT
+    /// - REMOVING_FWD_RRS_ST with next event of SELECT_SERVER_EVT
+    /// - REMOVING_REV_PTRS_ST with next event of SERVER_IO_ERROR_EVT
+    ///
+    /// Selects the server to be used from the reverse domain for the reverse
+    /// DNS update.  If next event is SELECT_SERVER_EVT the handler initializes
+    /// the reverse domain's server selection mechanism and then attempts to
+    /// select the next server. If next event is SERVER_IO_ERROR_EVT then the
+    /// handler simply attempts to select the next server.
+    ///
+    /// Transitions to:
+    /// - REMOVING_REV_PTRS_ST with next event of SERVER_SELECTED upon
+    /// successful server selection
+    ///
+    /// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon
+    /// failure to select a server
+    ///
+    /// @throw NameRemoveTransactionError if upon entry next event is not
+    /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT.
+    void selectingRevServerHandler();
+
+    /// @brief State handler for REMOVING_FWD_ADDRS_ST.
+    ///
+    /// Entered from:
+    /// - SELECTING_FWD_SERVER with next event of SERVER_SELECTED_EVT
+    ///
+    /// Attempts to remove the forward DNS entry for a given FQDN, provided
+    /// a DHCID RR exists which matches the requesting DHCID.  If this is
+    /// first invocation of the handler after transitioning into this state,
+    /// any previous update request context is deleted.   If next event
+    /// is SERVER_SELECTED_EVT, the handler builds the forward remove request,
+    /// schedules an asynchronous send via sendUpdate(), and returns.  Note
+    /// that sendUpdate will post NOP_EVT as next event.
+    ///
+    /// Posting the NOP_EVT will cause runModel() to suspend execution of
+    /// the state model thus affecting a "wait" for the update IO to complete.
+    /// Update completion occurs via the DNSClient callback operator() method
+    /// inherited from NameChangeTransaction.  When invoked this callback will
+    /// post a next event of IO_COMPLETED_EVT and then invoke runModel which
+    /// resumes execution of the state model.
+    ///
+    /// When the handler is invoked with a next event of IO_COMPELTED_EVT,
+    /// the DNS update status is checked and acted upon accordingly:
+    ///
+    /// Transitions to:
+    /// - REMOVING_FWD_RRS_ST with next event of UPDATE_OK_EVT upon successful
+    /// removal or RCODE of indication FQDN is no longer in use (NXDOMAIN).
+    ///
+    /// - PROCESS_TRANS_FAILED_ST with next event of UPDATE_FAILED_EVT if the
+    /// DNS server rejected the update for any reason or the IO completed
+    /// with an unrecognized status.
+    ///
+    /// - RE-ENTER this state with next event of SERVER_SELECTED_EVT if
+    /// there was an IO error communicating with the server and the number of
+    /// per server retries has not been exhausted.
+    ///
+    /// - SELECTING_FWD_SERVER_ST with next event of SERVER_IO_ERROR_EVT if
+    /// there was an IO error communicating with the server and the number of
+    /// per server retries has been exhausted.
+    ///
+    /// @throw NameRemoveTransactionError if upon entry next event is not
+    /// SERVER_SELECTED_EVT or IO_COMPLETE_EVT
+    void removingFwdAddrsHandler();
+
+    /// @brief State handler for REMOVING_FWD_RRS_ST.
+    ///
+    /// Entered from:
+    /// - REMOVING_FWD_ADDRS_ST with next event of UPDATE_OK_EVT
+    ///
+    /// Attempts to delete any remaining RRs associated with the given FQDN
+    /// such as the DHCID RR.  If this is first invocation of the handler after
+    /// transitioning into this state, any previous update request context is
+    /// deleted and the handler builds the forward remove request. It then
+    /// schedules an asynchronous send via sendUpdate(),
+    /// and returns.  Note that sendUpdate will post NOP_EVT as the next event.
+    ///
+    /// Posting the NOP_EVT will cause runModel() to suspend execution of
+    /// the state model thus affecting a "wait" for the update IO to complete.
+    /// Update completion occurs via the DNSClient callback operator() method
+    /// inherited from NameChangeTransaction.  When invoked this callback will
+    /// post a next event of IO_COMPLETED_EVT and then invoke runModel which
+    /// resumes execution of the state model.
+    ///
+    /// When the handler is invoked with a next event of IO_COMPELTED_EVT,
+    /// the DNS update status is checked and acted upon accordingly:
+    ///
+    /// Transitions to:
+    /// - SELECTING_REV_SERVER_ST with a next event of SELECT_SERVER_EVT upon
+    /// successful completion and the request includes a reverse DNS update.
+    ///
+    /// - PROCESS_TRANS_OK_ST with next event of UPDATE_OK_EVT upon successful
+    /// completion and the request does not include a reverse DNS update.
+    ///
+    /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT if the
+    /// DNS server rejected the update for any other reason or the IO completed
+    /// with an unrecognized status.
+    ///
+    /// - RE-ENTER this state with a next event of SERVER_SELECTED_EVT if
+    /// there was an IO error communicating with the server and the number of
+    /// per server retries has not been exhausted.
+    ///
+    /// - PROCESS_TRANS_FAILED_ST with a next event of SERVER_IO_ERROR_EVT if
+    /// there we have reached maximum number of retries without success on the
+    /// current server.
+    ///
+    /// @note If we exhaust the IO retries for the current server due to IO
+    /// failures, we will abort the remaining updates.  The rational is that
+    /// we are only in this state, if the remove of the forward address RR
+    /// succeeded (removingFwdAddrsHandler) on the current server so we should
+    /// not attempt another removal on a different server.  This is perhaps a
+    /// point for discussion. @todo Should we go ahead with the reverse remove?
+    ///
+    /// @throw NameRemoveTransactionError if upon entry next event is not:
+    /// UPDATE_OK_EVT or IO_COMPLETE_EVT
+    void removingFwdRRsHandler();
+
+    /// @brief State handler for REMOVING_REV_PTRS_ST.
+    ///
+    /// Entered from:
+    /// - SELECTING_REV_SERVER_ST with a next event of SERVER_SELECTED_EVT
+    ///
+    /// Attempts to delete a reverse DNS entry for a given FQDN. If this is
+    /// first invocation of the handler after transitioning into this state,
+    /// any previous update request context is deleted.  If next event is
+    /// SERVER_SELECTED_EVT, the handler builds the reverse remove request,
+    /// schedules an asynchronous send via sendUpdate(), and then returns.
+    /// Note that sendUpdate will post NOP_EVT as next event.
+    ///
+    /// Posting the NOP_EVT will cause runModel() to suspend execution of
+    /// the state model thus affecting a "wait" for the update IO to complete.
+    /// Update completion occurs via the DNSClient callback operator() method
+    /// inherited from NameChangeTransaction.  When invoked this callback will
+    /// post a next event of IO_COMPLETED_EVT and then invoke runModel which
+    /// resumes execution of the state model.
+    ///
+    /// When the handler is invoked with a next event of IO_COMPELTED_EVT,
+    /// the DNS update status is checked and acted upon accordingly:
+    ///
+    /// Transitions to:
+    /// - PROCESS_TRANS_OK_ST with a next event of UPDATE_OK_EVT upon
+    /// successful completion.
+    ///
+    /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT If the
+    /// DNS server rejected the update for any reason or the IO completed
+    /// with an unrecognized status.
+    ///
+    /// - RE-ENTER this state with a next event of SERVER_SELECTED_EVT if
+    /// there was an IO error communicating with the server and the number of
+    /// per server retries has not been exhausted.
+    ///
+    /// - SELECTING_REV_SERVER_ST with next event of SERVER_IO_ERROR_EVT if
+    /// there was an IO error communicating with the server and the number of
+    /// per server retries has been exhausted.
+    ///
+    /// @throw NameRemoveTransactionError if upon entry next event is not:
+    /// SERVER_SELECTED_EVT or IO_COMPLETED_EVT
+    void removingRevPtrsHandler();
+
+    /// @brief State handler for PROCESS_TRANS_OK_ST.
+    ///
+    /// Entered from:
+    /// - REMOVING_FWD_RRS_ST with a next event of UPDATE_OK_EVT
+    /// - REMOVING_REV_PTRS_ST with a next event of UPDATE_OK_EVT
+    ///
+    /// Sets the transaction action status to indicate success and ends
+    /// model execution.
+    ///
+    /// Transitions to:
+    /// - END_ST with a next event of END_EVT.
+    ///
+    /// @throw NameRemoveTransactionError if upon entry next event is not:
+    /// UPDATE_OK_EVT
+    void processRemoveOkHandler();
+
+    /// @brief State handler for PROCESS_TRANS_FAILED_ST.
+    ///
+    /// Entered from:
+    /// - SELECTING_FWD_SERVER_ST with a next event of NO_MORE_SERVERS
+    /// - REMOVING_FWD_ADDRS_ST with a next event of UPDATE_FAILED_EVT
+    /// - REMOVING_FWD_RRS_ST with a next event of UPDATE_FAILED_EVT
+    /// - REMOVING_FWD_RRS_ST with a next event of SERVER_IO_ERROR_EVT
+    /// - SELECTING_REV_SERVER_ST with a next event of NO_MORE_SERVERS
+    /// - REMOVING_REV_PTRS_ST with a next event of UPDATE_FAILED_EVT
+    ///
+    /// Sets the transaction status to indicate failure and ends
+    /// model execution.
+    ///
+    /// Transitions to:
+    /// - END_ST with a next event of FAIL_EVT.
+    ///
+    /// @throw NameRemoveTransactionError if upon entry next event is not:
+    /// UPDATE_FAILED_EVT
+    void processRemoveFailedHandler();
+
+    /// @brief Builds a DNS request to remove a forward DNS address for a FQDN.
+    ///
+    /// Constructs a DNS update request, based upon the NCR, for removing a
+    /// forward DNS address mapping. Once constructed, the request is stored as
+    /// the transaction's DNS update request.
+    ///
+    /// The request content is adherent to RFC 4703 section 5.5, paragraph 4.
+    ///
+    /// Prerequisite RRsets:
+    /// 1. An assertion that a matching DHCID RR exists
+    ///
+    /// Updates RRsets:
+    /// 1. A delete of the FQDN/IP RR (type A for IPv4, AAAA for IPv6)
+    ///
+    /// @throw This method does not throw but underlying methods may.
+    void buildRemoveFwdAddressRequest();
+
+    /// @brief Builds a DNS request to remove all forward DNS RRs for a FQDN.
+    ///
+    /// Constructs a DNS update request, based upon the NCR, for removing any
+    /// remaining forward DNS RRs, once all A or AAAA entries for the FQDN
+    /// have been removed. Once constructed, the request is stored as the
+    /// transaction's DNS update request.
+    ///
+    /// The request content is adherent to RFC 4703 section 5.5, paragraph 5.
+    ///
+    /// Prerequisite RRsets:
+    /// 1. An assertion that a matching DHCID RR exists
+    /// 2. An assertion that no A RRs for the FQDN exist
+    /// 3. An assertion that no AAAA RRs for the FQDN exist
+    ///
+    /// Updates RRsets:
+    /// 1. A delete of all RRs for the FQDN
+    ///
+    /// @throw This method does not throw but underlying methods may.
+    void buildRemoveFwdRRsRequest();
+
+    /// @brief Builds a DNS request to remove a reverse DNS entry for a FQDN
+    ///
+    /// Constructs a DNS update request, based upon the NCR, for removing a
+    /// reverse DNS mapping.  Once constructed, the request is stored as
+    /// the transaction's DNS update request.
+    ///
+    /// The request content is adherent to RFC 4703 section 5.5, paragraph 2:
+    ///
+    /// Prerequisite RRsets:
+    /// 1. An assertion that a PTR record matching the client's FQDN exists.
+    ///
+    /// Updates RRsets:
+    /// 1. A delete of all RRs for the FQDN
+    ///
+    /// @throw This method does not throw but underlying methods may.
+    void buildRemoveRevPtrsRequest();
+};
+
+/// @brief Defines a pointer to a NameRemoveTransaction.
+typedef boost::shared_ptr<NameRemoveTransaction> NameRemoveTransactionPtr;
+
+
+} // namespace isc::d2
+} // namespace isc
+#endif
diff --git a/src/bin/d2/nc_trans.cc b/src/bin/d2/nc_trans.cc
index 50741fe..1ad9b93 100644
--- a/src/bin/d2/nc_trans.cc
+++ b/src/bin/d2/nc_trans.cc
@@ -14,6 +14,7 @@
 
 #include <d2/d2_log.h>
 #include <d2/nc_trans.h>
+#include <dns/rdata.h>
 
 namespace isc {
 namespace d2 {
@@ -38,18 +39,29 @@ const int NameChangeTransaction::UPDATE_FAILED_EVT;
 
 const int NameChangeTransaction::NCT_DERIVED_EVENT_MIN;
 
+const unsigned int NameChangeTransaction::DNS_UPDATE_DEFAULT_TIMEOUT;
+const unsigned int NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+
 NameChangeTransaction::
-NameChangeTransaction(isc::asiolink::IOService& io_service,
+NameChangeTransaction(IOServicePtr& 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_(),
+     reverse_domain_(reverse_domain), dns_client_(), dns_update_request_(),
      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) {
+     current_server_list_(), current_server_(), next_server_pos_(0),
+     update_attempts_(0) {
+    // @todo if io_service is NULL we are multi-threading and should
+    // instantiate our own
+    if (!io_service_) {
+        isc_throw(NameChangeTransactionError, "IOServicePtr cannot be null");
+    }
+
     if (!ncr_) {
-        isc_throw(NameChangeTransactionError, "NameChangeRequest cannot null");
+        isc_throw(NameChangeTransactionError,
+                  "NameChangeRequest cannot be null");
     }
 
     if (ncr_->isForwardChange() && !(forward_domain_)) {
@@ -82,6 +94,36 @@ NameChangeTransaction::operator()(DNSClient::Status status) {
 }
 
 void
+NameChangeTransaction::sendUpdate(bool /* use_tsig_ */) {
+    try {
+        ++update_attempts_;
+        // @todo add logic to add/replace TSIG key info in request if
+        // use_tsig_ is true. We should be able to navigate to the TSIG key
+        // for the current server.  If not we would need to add that.
+
+        // @todo time out should ultimately be configurable, down to
+        // server level?
+        dns_client_->doUpdate(*io_service_, current_server_->getIpAddress(),
+                              current_server_->getPort(), *dns_update_request_,
+                              DNS_UPDATE_DEFAULT_TIMEOUT);
+
+        // Message is on its way, so the next event should be NOP_EVT.
+        postNextEvent(NOP_EVT);
+    } catch (const std::exception& ex) {
+        // We were unable to initiate the send.
+        // It is presumed that any throw from doUpdate is due to a programmatic
+        // error, such as an unforeseen permutation of data, rather than an IO
+        // failure. IO errors should be caught by the underlying asiolink
+        // mechanisms and manifested as an unsuccessful IO status in the
+        // DNSClient callback.  Any problem here most likely means the request
+        // is corrupt in some way and cannot be completed, therefore we will
+        // log it and transition it to failure.
+        LOG_ERROR(dctl_logger, DHCP_DDNS_TRANS_SEND_ERROR).arg(ex.what());
+        transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+    }
+}
+
+void
 NameChangeTransaction::defineEvents() {
     // Call superclass impl first.
     StateModel::defineEvents();
@@ -140,11 +182,43 @@ NameChangeTransaction::onModelFailure(const std::string& explanation) {
 }
 
 void
+NameChangeTransaction::retryTransition(const int fail_to_state) {
+    if (update_attempts_ < MAX_UPDATE_TRIES_PER_SERVER) {
+        // Re-enter the current state with same server selected.
+        transition(getCurrState(), SERVER_SELECTED_EVT);
+    } else  {
+        // Transition to given fail_to_state state if we are out
+        // of retries.
+        transition(fail_to_state, SERVER_IO_ERROR_EVT);
+    }
+}
+
+void
+NameChangeTransaction::setDnsUpdateRequest(D2UpdateMessagePtr& request) {
+    dns_update_request_ = request;
+}
+
+void
+NameChangeTransaction::clearDnsUpdateRequest() {
+    dns_update_request_.reset();
+}
+
+void
 NameChangeTransaction::setDnsUpdateStatus(const DNSClient::Status& status) {
     dns_update_status_ = status;
 }
 
 void
+NameChangeTransaction::setDnsUpdateResponse(D2UpdateMessagePtr& response) {
+    dns_update_response_ = response;
+}
+
+void
+NameChangeTransaction::clearDnsUpdateResponse() {
+    dns_update_response_.reset();
+}
+
+void
 NameChangeTransaction::setForwardChangeCompleted(const bool value) {
     forward_change_completed_ = value;
 }
@@ -154,6 +228,91 @@ NameChangeTransaction::setReverseChangeCompleted(const bool value) {
     reverse_change_completed_ = value;
 }
 
+void
+NameChangeTransaction::setUpdateAttempts(const size_t value) {
+    update_attempts_ = value;
+}
+
+D2UpdateMessagePtr
+NameChangeTransaction::prepNewRequest(DdnsDomainPtr domain) {
+    if (!domain) {
+        isc_throw(NameChangeTransactionError,
+                  "prepNewRequest - domain cannot be null");
+    }
+
+    try {
+        // Create a "blank" update request.
+        D2UpdateMessagePtr request(new D2UpdateMessage(D2UpdateMessage::
+                                                       OUTBOUND));
+        // Construct the Zone Section.
+        dns::Name zone_name(domain->getName());
+        request->setZone(zone_name, dns::RRClass::IN());
+        return (request);
+    } catch (const std::exception& ex) {
+        isc_throw(NameChangeTransactionError, "Cannot create new request :"
+                  << ex.what());
+    }
+}
+
+void
+NameChangeTransaction::addLeaseAddressRdata(dns::RRsetPtr& rrset) {
+    if (!rrset) {
+        isc_throw(NameChangeTransactionError,
+                  "addLeaseAddressRdata - RRset cannot cannot be null");
+    }
+
+    try {
+        // Manufacture an RData from the lease address then add it to the RR.
+        dns::rdata::ConstRdataPtr rdata;
+        if (ncr_->isV4()) {
+            rdata.reset(new dns::rdata::in::A(ncr_->getIpAddress()));
+        } else {
+            rdata.reset(new dns::rdata::in::AAAA(ncr_->getIpAddress()));
+        }
+        rrset->addRdata(rdata);
+    } catch (const std::exception& ex) {
+        isc_throw(NameChangeTransactionError, "Cannot add address rdata: "
+                  << ex.what());
+    }
+}
+
+void
+NameChangeTransaction::addDhcidRdata(dns::RRsetPtr& rrset) {
+    if (!rrset) {
+        isc_throw(NameChangeTransactionError,
+                  "addDhcidRdata - RRset cannot cannot be null");
+    }
+
+    try {
+        const std::vector<uint8_t>& ncr_dhcid = ncr_->getDhcid().getBytes();
+        util::InputBuffer buffer(ncr_dhcid.data(), ncr_dhcid.size());
+        dns::rdata::ConstRdataPtr rdata (new dns::rdata::in::
+                                         DHCID(buffer, ncr_dhcid.size()));
+        rrset->addRdata(rdata);
+    } catch (const std::exception& ex) {
+        isc_throw(NameChangeTransactionError, "Cannot add DCHID rdata: "
+                  << ex.what());
+    }
+
+}
+
+void
+NameChangeTransaction::addPtrRdata(dns::RRsetPtr& rrset) {
+    if (!rrset) {
+        isc_throw(NameChangeTransactionError,
+                  "addPtrRdata - RRset cannot cannot be null");
+    }
+
+    try {
+        dns::rdata::ConstRdataPtr rdata(new dns::rdata::generic::
+                                        PTR(getNcr()->getFqdn()));
+        rrset->addRdata(rdata);
+    } catch (const std::exception& ex) {
+        isc_throw(NameChangeTransactionError, "Cannot add PTR rdata: "
+                  << ex.what());
+    }
+}
+
 const dhcp_ddns::NameChangeRequestPtr&
 NameChangeTransaction::getNcr() const {
     return (ncr_);
@@ -220,12 +379,16 @@ NameChangeTransaction::getCurrentServer() const {
     return (current_server_);
 }
 
-
 void
 NameChangeTransaction::setNcrStatus(const dhcp_ddns::NameChangeStatus& status) {
     return (ncr_->setStatus(status));
 }
 
+const D2UpdateMessagePtr&
+NameChangeTransaction::getDnsUpdateRequest() const {
+    return (dns_update_request_);
+}
+
 DNSClient::Status
 NameChangeTransaction::getDnsUpdateStatus() const {
     return (dns_update_status_);
@@ -246,5 +409,15 @@ NameChangeTransaction::getReverseChangeCompleted() const {
     return (reverse_change_completed_);
 }
 
+size_t
+NameChangeTransaction::getUpdateAttempts() const {
+    return (update_attempts_);
+}
+
+const dns::RRType&
+NameChangeTransaction::getAddressRRType() const {
+    return (ncr_->isV4() ?  dns::RRType::A() : dns::RRType::AAAA());
+}
+
 } // namespace isc::d2
 } // namespace isc
diff --git a/src/bin/d2/nc_trans.h b/src/bin/d2/nc_trans.h
index d30dff7..86a89d7 100644
--- a/src/bin/d2/nc_trans.h
+++ b/src/bin/d2/nc_trans.h
@@ -17,8 +17,8 @@
 
 /// @file nc_trans.h This file defines the class NameChangeTransaction.
 
-#include <asiolink/io_service.h>
 #include <exceptions/exceptions.h>
+#include <d2/d2_asio.h>
 #include <d2/d2_config.h>
 #include <d2/dns_client.h>
 #include <d2/state_model.h>
@@ -69,7 +69,7 @@ typedef isc::dhcp_ddns::D2Dhcid TransactionKey;
 /// 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
+/// DNSClient via the callback.  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
@@ -151,6 +151,12 @@ public:
     static const int NCT_DERIVED_EVENT_MIN = SM_DERIVED_EVENT_MIN + 101;
     //@}
 
+    /// @brief Defualt time to assign to a single DNS udpate.
+    static const unsigned int DNS_UPDATE_DEFAULT_TIMEOUT = 5 * 1000;
+
+    /// @brief Maximum times to attempt a single update on a given server.
+    static const unsigned int MAX_UPDATE_TRIES_PER_SERVER = 3;
+
     /// @brief Constructor
     ///
     /// Instantiates a transaction that is ready to be started.
@@ -163,7 +169,7 @@ public:
     /// @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,
+    NameChangeTransaction(IOServicePtr& io_service,
                           dhcp_ddns::NameChangeRequestPtr& ncr,
                           DdnsDomainPtr& forward_domain,
                           DdnsDomainPtr& reverse_domain);
@@ -191,11 +197,25 @@ public:
     virtual void operator()(DNSClient::Status status);
 
 protected:
+    /// @brief Send the update request to the current server.
+    ///
+    /// This method increments the update attempt count and then passes the
+    /// current update request to the DNSClient instance to be sent to the
+    /// currently selected server.  Since the send is asynchronous, the method
+    /// posts NOP_EVT as the next event and then returns.
+    ///
+    /// @param use_tsig True if the update should be include a TSIG key. This
+    /// is not yet implemented.
+    ///
+    /// If an exception occurs it will be logged and and the transaction will
+    /// be failed.
+    virtual void sendUpdate(bool use_tsig = false);
+
     /// @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.
+    /// first to maintain the hierarchical chain of event definition.
     /// Derivations of NameChangeTransaction must invoke its implementation
     /// in like fashion.
     ///
@@ -217,7 +237,7 @@ protected:
     ///
     /// 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.
+    /// first to maintain the hierarchical chain of state definition.
     /// Derivations of NameChangeTransaction must invoke its implementation
     /// in like fashion.
     ///
@@ -241,13 +261,35 @@ protected:
     /// 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
+    /// error occurs the transaction is deemed inoperable, and further 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 Determines the state and next event based on update attempts.
+    ///
+    /// This method will post a next event of SERVER_SELECTED_EVT to the
+    /// current state if the number of update attempts has not reached the
+    /// maximum allowed.
+    ///
+    /// If the maximum number of attempts has been reached, it will transition
+    /// to the given state with a next event of SERVER_IO_ERROR_EVT.
+    ///
+    /// @param fail_to_state  State to transition to if maximum attempts
+    /// have been tried.
+    ///
+    void retryTransition(const int fail_to_state);
+
+    /// @brief Sets the update request packet to the given packet.
+    ///
+    /// @param request is the new request packet to assign.
+    void setDnsUpdateRequest(D2UpdateMessagePtr& request);
+
+    /// @brief Destroys the current update request packet.
+    void clearDnsUpdateRequest();
+
     /// @brief Sets the update status to the given status value.
     ///
     /// @param status is the new value for the update status.
@@ -258,6 +300,9 @@ protected:
     /// @param response is the new response packet to assign.
     void setDnsUpdateResponse(D2UpdateMessagePtr& response);
 
+    /// @brief Destroys the current update response packet.
+    void clearDnsUpdateResponse();
+
     /// @brief Sets the forward change completion flag to the given value.
     ///
     /// @param value is the new value to assign to the flag.
@@ -289,7 +334,7 @@ protected:
     ///
     /// 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
+    /// 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
@@ -307,6 +352,62 @@ protected:
     /// @return A const pointer reference to the DNSClient
     const DNSClientPtr& getDNSClient() const;
 
+    /// @brief Sets the update attempt count to the given value.
+    ///
+    /// @param value is the new value to assign.
+    void setUpdateAttempts(const size_t value);
+
+    /// @brief Fetches the IOService the transaction uses for IO processing.
+    ///
+    /// @return returns a const pointer to the IOService.
+    const IOServicePtr& getIOService() {
+        return (io_service_);
+    }
+
+    /// @brief Creates a new DNS update request based on the given domain.
+    ///
+    /// Constructs a new "empty", OUTBOUND, request with the message id set
+    /// and zone section populated based on the given domain.
+    /// It is declared virtual for test purposes.
+    ///
+    /// @return A D2UpdateMessagePtr to the new request.
+    ///
+    /// @throw NameChangeTransactionError if request cannot be constructed.
+    virtual D2UpdateMessagePtr prepNewRequest(DdnsDomainPtr domain);
+
+    /// @brief Adds an RData for the lease address to the given RRset.
+    ///
+    /// Creates an in::A() or in:AAAA() RData instance from the NCR
+    /// lease address and adds it to the given RRset.
+    ///
+    /// @param RRset RRset to which to add the RData
+    ///
+    /// @throw NameChangeTransactionError if RData cannot be constructed or
+    /// the RData cannot be added to the given RRset.
+    void addLeaseAddressRdata(dns::RRsetPtr& rrset);
+
+    /// @brief Adds an RData for the lease client's DHCID to the given RRset.
+    ///
+    /// Creates an in::DHCID() RData instance from the NCR DHCID and adds
+    /// it to the given RRset.
+    ///
+    /// @param RRset RRset to which to add the RData
+    ///
+    /// @throw NameChangeTransactionError if RData cannot be constructed or
+    /// the RData cannot be added to the given RRset.
+    void addDhcidRdata(dns::RRsetPtr& rrset);
+
+    /// @brief Adds an RData for the lease FQDN to the given RRset.
+    ///
+    /// Creates an in::PTR() RData instance from the NCR FQDN and adds
+    /// it to the given RRset.
+    ///
+    /// @param RRset RRset to which to add the RData
+    ///
+    /// @throw NameChangeTransactionError if RData cannot be constructed or
+    /// the RData cannot be added to the given RRset.
+    void addPtrRdata(dns::RRsetPtr& rrset);
+
 public:
     /// @brief Fetches the NameChangeRequest for this transaction.
     ///
@@ -334,16 +435,22 @@ public:
 
     /// @brief Fetches the forward DdnsDomain.
     ///
-    /// @return A pointer reference to the forward DdnsDomain.  If the
+    /// @return A pointer reference to the forward DdnsDomain.  If
     /// 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
+    /// @return A pointer reference to the reverse DdnsDomain.  If
     /// the request does not include a reverse change, the pointer will empty.
     DdnsDomainPtr& getReverseDomain();
 
+    /// @brief Fetches the current DNS update request packet.
+    ///
+    /// @return A const pointer reference to the current D2UpdateMessage
+    /// request.
+    const D2UpdateMessagePtr& getDnsUpdateRequest() const;
+
     /// @brief Fetches the most recent DNS update status.
     ///
     /// @return A DNSClient::Status indicating the result of the most recent
@@ -374,9 +481,21 @@ public:
     /// @return True if the reverse change has been completed, false otherwise.
     bool getReverseChangeCompleted() const;
 
+    /// @brief Fetches the update attempt count for the current update.
+    ///
+    /// @return size_t which is the number of times the current request has
+    /// been attempted against the current server.
+    size_t getUpdateAttempts() const;
+
+    /// @brief Returns the DHCP data type for the lease address
+    ///
+    /// @return constant reference to dns::RRType::A() if the lease address
+    /// is IPv4 or dns::RRType::AAAA() if the lease address is IPv6.
+    const dns::RRType& getAddressRRType() const;
+
 private:
     /// @brief The IOService which should be used to for IO processing.
-    isc::asiolink::IOService& io_service_;
+    IOServicePtr io_service_;
 
     /// @brief The NameChangeRequest that the transaction is to fulfill.
     dhcp_ddns::NameChangeRequestPtr ncr_;
@@ -398,6 +517,9 @@ private:
     /// @brief The DNSClient instance that will carry out DNS packet exchanges.
     DNSClientPtr dns_client_;
 
+    /// @brief The DNS current update request packet.
+    D2UpdateMessagePtr dns_update_request_;
+
     /// @brief The outcome of the most recently completed DNS packet exchange.
     DNSClient::Status dns_update_status_;
 
@@ -421,6 +543,9 @@ private:
     /// 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 Number of transmit attempts for the current request.
+    size_t update_attempts_;
 };
 
 /// @brief Defines a pointer to a NameChangeTransaction.
diff --git a/src/bin/d2/state_model.cc b/src/bin/d2/state_model.cc
index 6786e43..c5a0209 100644
--- a/src/bin/d2/state_model.cc
+++ b/src/bin/d2/state_model.cc
@@ -92,27 +92,13 @@ 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;
+    // Intialize dictionaries of events and states.
+    initDictionaries();
 
     // Set the current state to starting state and enter the run loop.
     setState(start_state);
+
+    // Start running the model.
     runModel(START_EVT);
 }
 
@@ -149,6 +135,27 @@ void
 StateModel::nopStateHandler() {
 }
 
+void
+StateModel::initDictionaries() {
+    // 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;
+}
 
 void
 StateModel::defineEvent(unsigned int event_value, const std::string& label) {
diff --git a/src/bin/d2/state_model.h b/src/bin/d2/state_model.h
index 1596bb6..b7320eb 100644
--- a/src/bin/d2/state_model.h
+++ b/src/bin/d2/state_model.h
@@ -17,7 +17,6 @@
 
 /// @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>
@@ -276,10 +275,9 @@ public:
 
     /// @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.
+    /// This method invokes initDictionaries method to initialize the event
+    /// and state dictionaries and 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.
     ///
@@ -324,6 +322,15 @@ public:
     void nopStateHandler();
 
 protected:
+    /// @brief Initializes the event and state dictionaries.
+    ///
+    /// This method invokes the define and verify methods for both events and
+    /// states to initialize their respective dictionaries. 
+    ///
+    /// @throw StateModelError or others indirectly, as this method calls
+    /// dictionary define and verify methods.
+    void initDictionaries();
+
     /// @brief Populates the set of events.
     ///
     /// This method is used to construct the set of valid events. Each class
diff --git a/src/bin/d2/tests/Makefile.am b/src/bin/d2/tests/Makefile.am
index 0866ea0..8b5e351 100644
--- a/src/bin/d2/tests/Makefile.am
+++ b/src/bin/d2/tests/Makefile.am
@@ -51,7 +51,8 @@ if HAVE_GTEST
 
 TESTS += d2_unittests
 
-d2_unittests_SOURCES = ../d2_log.h ../d2_log.cc
+d2_unittests_SOURCES = ../d2_asio.h
+d2_unittests_SOURCES += ../d2_log.h ../d2_log.cc
 d2_unittests_SOURCES += ../d_process.h
 d2_unittests_SOURCES += ../d_controller.cc ../d2_controller.h
 d2_unittests_SOURCES += ../d2_process.cc ../d2_process.h
@@ -65,6 +66,8 @@ 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_add.cc ../nc_add.h
+d2_unittests_SOURCES += ../nc_remove.cc ../nc_remove.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
@@ -80,6 +83,9 @@ 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_add_unittests.cc
+d2_unittests_SOURCES += nc_remove_unittests.cc
+d2_unittests_SOURCES += nc_test_utils.cc nc_test_utils.h
 d2_unittests_SOURCES += nc_trans_unittests.cc
 d2_unittests_SOURCES += state_model_unittests.cc
 nodist_d2_unittests_SOURCES = ../d2_messages.h ../d2_messages.cc
@@ -94,6 +100,7 @@ d2_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libb10-dhcp_ddns.la
+d2_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
diff --git a/src/bin/d2/tests/d2_queue_mgr_unittests.cc b/src/bin/d2/tests/d2_queue_mgr_unittests.cc
index 18bf95c..a49ab65 100644
--- a/src/bin/d2/tests/d2_queue_mgr_unittests.cc
+++ b/src/bin/d2/tests/d2_queue_mgr_unittests.cc
@@ -12,7 +12,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <asiolink/interval_timer.h>
+#include <d2/d2_asio.h>
 #include <d2/d2_queue_mgr.h>
 #include <dhcp_ddns/ncr_udp.h>
 #include <util/time_utilities.h>
@@ -78,15 +78,19 @@ const long TEST_TIMEOUT = 5 * 1000;
 
 /// @brief Tests that construction with max queue size of zero is not allowed.
 TEST(D2QueueMgrBasicTest, construction1) {
-    isc::asiolink::IOService io_service;
+    IOServicePtr io_service;
 
+    // Verify that constructing with null IOServicePtr is not allowed.
+    EXPECT_THROW((D2QueueMgr(io_service)), D2QueueMgrError);
+
+    io_service.reset(new isc::asiolink::IOService());
     // Verify that constructing with max queue size of zero is not allowed.
     EXPECT_THROW(D2QueueMgr(io_service, 0), D2QueueMgrError);
 }
 
 /// @brief Tests default construction works.
 TEST(D2QueueMgrBasicTest, construction2) {
-    isc::asiolink::IOService io_service;
+    IOServicePtr io_service(new isc::asiolink::IOService());
 
     // Verify that valid constructor works.
     D2QueueMgrPtr queue_mgr;
@@ -97,7 +101,7 @@ TEST(D2QueueMgrBasicTest, construction2) {
 
 /// @brief Tests construction with custom queue size works properly
 TEST(D2QueueMgrBasicTest, construction3) {
-    isc::asiolink::IOService io_service;
+    IOServicePtr io_service(new isc::asiolink::IOService());
 
     // Verify that custom queue size constructor works.
     D2QueueMgrPtr queue_mgr;
@@ -114,7 +118,7 @@ TEST(D2QueueMgrBasicTest, construction3) {
 /// 4. Peek returns the first entry on the queue without altering queue content
 /// 5. Dequeue removes the first entry on the queue
 TEST(D2QueueMgrBasicTest, basicQueue) {
-    isc::asiolink::IOService io_service;
+    IOServicePtr io_service(new isc::asiolink::IOService());
 
     // Construct the manager with max queue size set to number of messages
     // we'll use.
@@ -206,7 +210,7 @@ bool checkSendVsReceived(NameChangeRequestPtr sent_ncr,
 class QueueMgrUDPTest : public virtual ::testing::Test,
                         NameChangeSender::RequestSendHandler {
 public:
-    isc::asiolink::IOService io_service_;
+    IOServicePtr io_service_;
     NameChangeSenderPtr   sender_;
     isc::asiolink::IntervalTimer test_timer_;
     D2QueueMgrPtr queue_mgr_;
@@ -215,7 +219,8 @@ public:
     std::vector<NameChangeRequestPtr> sent_ncrs_;
     std::vector<NameChangeRequestPtr> received_ncrs_;
 
-    QueueMgrUDPTest() : io_service_(), test_timer_(io_service_) {
+    QueueMgrUDPTest() : io_service_(new isc::asiolink::IOService()),
+        test_timer_(*io_service_) {
         isc::asiolink::IOAddress addr(TEST_ADDRESS);
         // Create our sender instance. Note that reuse_address is true.
         sender_.reset(new NameChangeUDPSender(addr, SENDER_PORT,
@@ -245,7 +250,7 @@ public:
     ///
     /// This callback stops all running (hanging) tasks on IO service.
     void testTimeoutHandler() {
-        io_service_.stop();
+        io_service_->stop();
         FAIL() << "Test timeout hit.";
     }
 };
@@ -296,7 +301,7 @@ TEST_F (QueueMgrUDPTest, stateModel) {
 
     // Stopping requires IO cancel, which result in a callback.
     // So process one event and verify we are STOPPED.
-    io_service_.run_one();
+    io_service_->run_one();
     EXPECT_EQ(D2QueueMgr::STOPPED, queue_mgr_->getMgrState());
 
     // Verify that we can re-enter the RUNNING from STOPPED by starting the
@@ -313,7 +318,7 @@ TEST_F (QueueMgrUDPTest, stateModel) {
 
     // Stopping requires IO cancel, which result in a callback.
     // So process one event and verify we are STOPPED.
-    io_service_.run_one();
+    io_service_->run_one();
     EXPECT_EQ(D2QueueMgr::STOPPED, queue_mgr_->getMgrState());
 
     // Verify that we can remove the listener in the STOPPED state and
@@ -355,7 +360,7 @@ TEST_F (QueueMgrUDPTest, liveFeed) {
     ASSERT_EQ(D2QueueMgr::RUNNING, queue_mgr_->getMgrState());
 
     // Place the sender into sending state.
-    ASSERT_NO_THROW(sender_->startSending(io_service_));
+    ASSERT_NO_THROW(sender_->startSending(*io_service_));
     ASSERT_TRUE(sender_->amSending());
 
     // Iterate over the list of requests sending and receiving
@@ -366,8 +371,8 @@ TEST_F (QueueMgrUDPTest, liveFeed) {
         ASSERT_NO_THROW(sender_->sendRequest(send_ncr));
 
         // running two should do the send then the receive
-        io_service_.run_one();
-        io_service_.run_one();
+        io_service_->run_one();
+        io_service_->run_one();
 
         // Verify that the request can be added to the queue and queue
         // size increments accordingly.
@@ -390,8 +395,8 @@ TEST_F (QueueMgrUDPTest, liveFeed) {
         ASSERT_NO_THROW(sender_->sendRequest(send_ncr));
 
         // running two should do the send then the receive
-        EXPECT_NO_THROW(io_service_.run_one());
-        EXPECT_NO_THROW(io_service_.run_one());
+        EXPECT_NO_THROW(io_service_->run_one());
+        EXPECT_NO_THROW(io_service_->run_one());
         EXPECT_EQ(i+1, queue_mgr_->getQueueSize());
     }
 
@@ -400,11 +405,11 @@ TEST_F (QueueMgrUDPTest, liveFeed) {
 
     // Send another. The send should succeed.
     ASSERT_NO_THROW(sender_->sendRequest(send_ncr));
-    EXPECT_NO_THROW(io_service_.run_one());
+    EXPECT_NO_THROW(io_service_->run_one());
 
     // Now execute the receive which should not throw but should move us
     // to STOPPED_QUEUE_FULL state.
-    EXPECT_NO_THROW(io_service_.run_one());
+    EXPECT_NO_THROW(io_service_->run_one());
     EXPECT_EQ(D2QueueMgr::STOPPED_QUEUE_FULL, queue_mgr_->getMgrState());
 
     // Verify queue size did not increase beyond max.
@@ -430,10 +435,10 @@ TEST_F (QueueMgrUDPTest, liveFeed) {
     // Verify that we can again receive requests.
     // Send should be fine.
     ASSERT_NO_THROW(sender_->sendRequest(send_ncr));
-    EXPECT_NO_THROW(io_service_.run_one());
+    EXPECT_NO_THROW(io_service_->run_one());
 
     // Receive should succeed.
-    EXPECT_NO_THROW(io_service_.run_one());
+    EXPECT_NO_THROW(io_service_->run_one());
     EXPECT_EQ(1, queue_mgr_->getQueueSize());
 }
 
diff --git a/src/bin/d2/tests/d2_update_mgr_unittests.cc b/src/bin/d2/tests/d2_update_mgr_unittests.cc
index 0abed5d..e4f0c4c 100644
--- a/src/bin/d2/tests/d2_update_mgr_unittests.cc
+++ b/src/bin/d2/tests/d2_update_mgr_unittests.cc
@@ -12,7 +12,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <asiolink/interval_timer.h>
+#include <d2/d2_asio.h>
 #include <d2/d2_update_mgr.h>
 #include <util/time_utilities.h>
 #include <d_test_stubs.h>
@@ -41,7 +41,7 @@ public:
     ///
     /// Parameters match those needed by D2UpdateMgr.
     D2UpdateMgrWrapper(D2QueueMgrPtr& queue_mgr, D2CfgMgrPtr& cfg_mgr,
-                       isc::asiolink::IOService& io_service,
+                       IOServicePtr& io_service,
                        const size_t max_transactions = MAX_TRANSACTIONS_DEFAULT)
         : D2UpdateMgr(queue_mgr, cfg_mgr, io_service, max_transactions) {
     }
@@ -68,7 +68,7 @@ typedef boost::shared_ptr<D2UpdateMgrWrapper> D2UpdateMgrWrapperPtr;
 /// functions.
 class D2UpdateMgrTest : public ConfigParseTest {
 public:
-    isc::asiolink::IOService io_service_;
+    IOServicePtr io_service_;
     D2QueueMgrPtr queue_mgr_;
     D2CfgMgrPtr cfg_mgr_;
     //D2UpdateMgrPtr update_mgr_;
@@ -77,6 +77,7 @@ public:
     size_t canned_count_;
 
     D2UpdateMgrTest() {
+        io_service_.reset(new isc::asiolink::IOService());
         queue_mgr_.reset(new D2QueueMgr(io_service_));
         cfg_mgr_.reset(new D2CfgMgr());
         update_mgr_.reset(new D2UpdateMgrWrapper(queue_mgr_, cfg_mgr_,
@@ -162,7 +163,7 @@ public:
 /// 4. Default construction works and max transactions is defaulted properly
 /// 5. Construction with custom max transactions works properly
 TEST(D2UpdateMgr, construction) {
-    isc::asiolink::IOService io_service;
+    IOServicePtr io_service(new isc::asiolink::IOService());
     D2QueueMgrPtr queue_mgr;
     D2CfgMgrPtr cfg_mgr;
     D2UpdateMgrPtr update_mgr;
@@ -180,6 +181,12 @@ TEST(D2UpdateMgr, construction) {
 
     ASSERT_NO_THROW(cfg_mgr.reset(new D2CfgMgr()));
 
+    // Verify that constructor fails with invalid io_service.
+    io_service.reset();
+    EXPECT_THROW(D2UpdateMgr(queue_mgr, cfg_mgr, io_service),
+                 D2UpdateMgrError);
+    io_service.reset(new isc::asiolink::IOService());
+
     // Verify that max transactions cannot be zero.
     EXPECT_THROW(D2UpdateMgr(queue_mgr, cfg_mgr, io_service, 0),
                  D2UpdateMgrError);
diff --git a/src/bin/d2/tests/d_test_stubs.cc b/src/bin/d2/tests/d_test_stubs.cc
index ab061ef..319dcd7 100644
--- a/src/bin/d2/tests/d_test_stubs.cc
+++ b/src/bin/d2/tests/d_test_stubs.cc
@@ -32,7 +32,7 @@ const char* valid_d2_config = "{ "
                         "} ],"
                         "\"forward_ddns\" : {"
                         "\"ddns_domains\": [ "
-                        "{ \"name\": \"tmark.org\" , "
+                        "{ \"name\": \"tmark.org.\" , "
                         "  \"key_name\": \"d2_key.tmark.org\" , "
                         "  \"dns_servers\" : [ "
                         "  { \"hostname\": \"one.tmark\" } "
diff --git a/src/bin/d2/tests/d_test_stubs.h b/src/bin/d2/tests/d_test_stubs.h
index 1313687..7680a98 100644
--- a/src/bin/d2/tests/d_test_stubs.h
+++ b/src/bin/d2/tests/d_test_stubs.h
@@ -15,11 +15,11 @@
 #ifndef D_TEST_STUBS_H
 #define D_TEST_STUBS_H
 
-#include <asiolink/asiolink.h>
 #include <cc/data.h>
 #include <cc/session.h>
 #include <config/ccsession.h>
 
+#include <d2/d2_asio.h>
 #include <d2/d_controller.h>
 #include <d2/d_cfg_mgr.h>
 
diff --git a/src/bin/d2/tests/dns_client_unittests.cc b/src/bin/d2/tests/dns_client_unittests.cc
index 9105ab8..db4d6b5 100644
--- a/src/bin/d2/tests/dns_client_unittests.cc
+++ b/src/bin/d2/tests/dns_client_unittests.cc
@@ -261,7 +261,7 @@ public:
 
         // Set the response wait time to 0 so as our test is not hanging. This
         // should cause instant timeout.
-        const int timeout = 0;
+        const int timeout = 500;
         // The doUpdate() function starts asynchronous message exchange with DNS
         // server. When message exchange is done or timeout occurs, the
         // completion callback will be triggered. The doUpdate function returns
diff --git a/src/bin/d2/tests/nc_add_unittests.cc b/src/bin/d2/tests/nc_add_unittests.cc
new file mode 100644
index 0000000..9aecda4
--- /dev/null
+++ b/src/bin/d2/tests/nc_add_unittests.cc
@@ -0,0 +1,1871 @@
+// 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_cfg_mgr.h>
+#include <d2/d2_cfg_mgr.h>
+#include <d2/nc_add.h>
+#include <dns/messagerenderer.h>
+#include <nc_test_utils.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 class derived from NameAddTransaction to provide visiblity
+// to protected methods.
+class NameAddStub : public NameAddTransaction {
+public:
+    NameAddStub(IOServicePtr& io_service,
+                dhcp_ddns::NameChangeRequestPtr& ncr,
+                DdnsDomainPtr& forward_domain,
+                DdnsDomainPtr& reverse_domain)
+        : NameAddTransaction(io_service, ncr, forward_domain, reverse_domain),
+          simulate_send_exception_(false),
+          simulate_build_request_exception_(false) {
+    }
+
+    virtual ~NameAddStub() {
+    }
+
+    /// @brief Simulates sending update requests to the DNS server
+    ///
+    /// This method simulates the initiation of an asynchronous send of
+    /// a DNS update request. It overrides the actual sendUpdate method in
+    /// the base class, thus avoiding an actual send, yet still increments
+    /// the update attempt count and posts a next event of NOP_EVT.
+    ///
+    /// It will also simulate an exception-based failure of sendUpdate, if
+    /// the simulate_send_exception_ flag is true.
+    ///
+    /// @param use_tsig_ Parameter is unused, but present in the base class
+    /// method.
+    ///
+    virtual void sendUpdate(bool /* use_tsig_ = false */) {
+        if (simulate_send_exception_) {
+            // Make the flag a one-shot by resetting it.
+            simulate_send_exception_ = false;
+            // Transition to failed.
+            transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+            return;
+        }
+
+        // Update send attempt count and post a NOP_EVT.
+        setUpdateAttempts(getUpdateAttempts() + 1);
+        postNextEvent(StateModel::NOP_EVT);
+    }
+
+    /// @brief Prepares the initial D2UpdateMessage
+    ///
+    /// This method overrides the NameChangeTransactio implementation to
+    /// provide the ability to simulate an exception throw in the build
+    /// request logic.
+    /// If the one-shot flag, simulate_build_request_exception_ is true,
+    /// this method will throw an exception, otherwise it will invoke the
+    /// base class method, providing normal functionality.
+    ///
+    /// For parameter description see the NameChangeTransaction implementation.
+    virtual D2UpdateMessagePtr prepNewRequest(DdnsDomainPtr domain) {
+        if (simulate_build_request_exception_) {
+            simulate_build_request_exception_ = false;
+            isc_throw (NameAddTransactionError,
+                       "Simulated build requests exception");
+        }
+
+        return (NameChangeTransaction::prepNewRequest(domain));
+    }
+
+    /// @brief Simulates receiving a response
+    ///
+    /// This method simulates the completion of a DNSClient send.  This allows
+    /// the state handler logic devoted to dealing with IO completion to be
+    /// fully exercised without requiring any actual IO.  The two primary
+    /// pieces of information gleaned from IO completion are the DNSClient
+    /// status which indicates whether or not the IO exchange was successful
+    /// and the rcode, which indicates the server's reaction to the request.
+    ///
+    /// This method updates the transaction's DNS status value to that of the
+    /// given parameter, and then constructs and DNS update response message
+    /// with the given rcode value.  To complete the simulation it then posts
+    /// a next event of IO_COMPLETED_EVT.
+    ///
+    /// @param status simulated DNSClient status
+    /// @param rcode  simulated server response code
+    void fakeResponse(const DNSClient::Status& status,
+                      const dns::Rcode& rcode) {
+        // Set the DNS update status.  This is normally set in
+        // DNSClient IO completion handler.
+        setDnsUpdateStatus(status);
+
+        // Construct an empty message with the given Rcode.
+        D2UpdateMessagePtr msg(new D2UpdateMessage(D2UpdateMessage::OUTBOUND));
+        msg->setRcode(rcode);
+
+        // Set the update response to the message.
+        setDnsUpdateResponse(msg);
+
+        // Post the IO completion event.
+        postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+    }
+
+    /// @brief Selects the first forward server.
+    /// Some state handlers require a server to have been selected.
+    /// This selects a server without going through the state
+    /// transition(s) to do so.
+    bool selectFwdServer() {
+        if (getForwardDomain()) {
+            initServerSelection(getForwardDomain());
+            selectNextServer();
+            return (getCurrentServer());
+        }
+
+        return (false);
+    }
+
+    /// @brief Selects the first reverse server.
+    /// Some state handlers require a server to have been selected.
+    /// This selects a server without going through the state
+    /// transition(s) to do so.
+    bool selectRevServer() {
+        if (getReverseDomain()) {
+            initServerSelection(getReverseDomain());
+            selectNextServer();
+            return (getCurrentServer());
+        }
+
+        return (false);
+    }
+
+    /// @brief One-shot flag which will simulate sendUpdate failure if true.
+    bool simulate_send_exception_;
+
+    /// @brief One-shot flag which will simulate an exception when sendUpdate
+    /// failure if true.
+    bool simulate_build_request_exception_;
+
+    using StateModel::postNextEvent;
+    using StateModel::setState;
+    using StateModel::initDictionaries;
+    using NameAddTransaction::defineEvents;
+    using NameAddTransaction::verifyEvents;
+    using NameAddTransaction::defineStates;
+    using NameAddTransaction::verifyStates;
+    using NameAddTransaction::readyHandler;
+    using NameAddTransaction::selectingFwdServerHandler;
+    using NameAddTransaction::getCurrentServer;
+    using NameAddTransaction::addingFwdAddrsHandler;
+    using NameAddTransaction::setDnsUpdateStatus;
+    using NameAddTransaction::replacingFwdAddrsHandler;
+    using NameAddTransaction::selectingRevServerHandler;
+    using NameAddTransaction::replacingRevPtrsHandler;
+    using NameAddTransaction::processAddOkHandler;
+    using NameAddTransaction::processAddFailedHandler;
+    using NameAddTransaction::buildAddFwdAddressRequest;
+    using NameAddTransaction::buildReplaceFwdAddressRequest;
+    using NameAddTransaction::buildReplaceRevPtrsRequest;
+};
+
+typedef boost::shared_ptr<NameAddStub> NameAddStubPtr;
+
+/// @brief Test fixture for testing NameAddTransaction
+///
+/// Note this class uses NameAddStub class to exercise non-public
+/// aspects of NameAddTransaction.
+class NameAddTransactionTest : public ::testing::Test {
+public:
+    IOServicePtr io_service_;
+    DdnsDomainPtr forward_domain_;
+    DdnsDomainPtr reverse_domain_;
+
+    NameAddTransactionTest() : io_service_(new isc::asiolink::IOService()) {
+    }
+
+    static const unsigned int FORWARD_CHG = 0x01;
+    static const unsigned int REVERSE_CHG = 0x02;
+    static const unsigned int FWD_AND_REV_CHG = REVERSE_CHG | FORWARD_CHG;
+
+    virtual ~NameAddTransactionTest() {
+    }
+
+    /// @brief Creates a transaction which requests an IPv4 DNS update.
+    ///
+    /// The transaction is constructed around a predefined (i.e. "canned")
+    /// IPv4 NameChangeRequest. The request has both forward and reverse DNS
+    /// changes requested.  Based upon the change mask, the transaction
+    /// will have either the forward, reverse, or both domains populated.
+    ///
+    /// @param change_mask determines which change directions are requested
+    NameAddStubPtr makeTransaction4(int change_mask=FWD_AND_REV_CHG) {
+        const char* msg_str =
+            "{"
+            " \"change_type\" : 0 , "
+            " \"forward_change\" : true , "
+            " \"reverse_change\" : true , "
+            " \"fqdn\" : \"my.forward.example.com.\" , "
+            " \"ip_address\" : \"192.168.2.1\" , "
+            " \"dhcid\" : \"0102030405060708\" , "
+            " \"lease_expires_on\" : \"20130121132405\" , "
+            " \"lease_length\" : 1300 "
+            "}";
+
+        // Create NameChangeRequest from JSON string.
+        dhcp_ddns::NameChangeRequestPtr ncr = dhcp_ddns::NameChangeRequest::
+                                              fromJSON(msg_str);
+
+        // If the change mask does not include a forward change clear the
+        // forward domain; otherwise create the domain and its servers.
+        if (!(change_mask & FORWARD_CHG)) {
+            ncr->setForwardChange(false);
+            forward_domain_.reset();
+        } else {
+            // Create the forward domain and then its servers.
+            forward_domain_ = makeDomain("example.com.");
+            addDomainServer(forward_domain_, "forward.example.com",
+                            "1.1.1.1");
+            addDomainServer(forward_domain_, "forward2.example.com",
+                            "1.1.1.2");
+        }
+
+        // If the change mask does not include a reverse change clear the
+        // reverse domain; otherwise create the domain and its servers.
+        if (!(change_mask & REVERSE_CHG)) {
+            ncr->setReverseChange(false);
+            reverse_domain_.reset();
+        } else {
+            // Create the reverse domain and its server.
+            reverse_domain_ = makeDomain("2.168.192.in.addr.arpa.");
+            addDomainServer(reverse_domain_, "reverse.example.com",
+                            "2.2.2.2");
+            addDomainServer(reverse_domain_, "reverse2.example.com",
+                            "2.2.2.3");
+        }
+
+        // Now create the test transaction as would occur in update manager.
+        return (NameAddStubPtr(new NameAddStub(io_service_, ncr,
+                                               forward_domain_,
+                                               reverse_domain_)));
+    }
+
+    /// @brief Creates a transaction which requests an IPv6 DNS update.
+    ///
+    /// The transaction is constructed around a predefined (i.e. "canned")
+    /// IPv6 NameChangeRequest. The request has both forward and reverse DNS
+    /// changes requested.  Based upon the change mask, the transaction
+    /// will have either the forward, reverse, or both domains populated.
+    ///
+    /// @param change_mask determines which change directions are requested
+    NameAddStubPtr makeTransaction6(int change_mask=FWD_AND_REV_CHG) {
+        const char* msg_str =
+            "{"
+            " \"change_type\" : 0 , "
+            " \"forward_change\" : true , "
+            " \"reverse_change\" : true , "
+            " \"fqdn\" : \"my6.forward.example.com.\" , "
+            " \"ip_address\" : \"2001:1::100\" , "
+            " \"dhcid\" : \"0102030405060708\" , "
+            " \"lease_expires_on\" : \"20130121132405\" , "
+            " \"lease_length\" : 1300 "
+            "}";
+
+        // Create NameChangeRequest from JSON string.
+        dhcp_ddns::NameChangeRequestPtr ncr = makeNcrFromString(msg_str);
+
+        // If the change mask does not include a forward change clear the
+        // forward domain; otherwise create the domain and its servers.
+        if (!(change_mask & FORWARD_CHG)) {
+            ncr->setForwardChange(false);
+            forward_domain_.reset();
+        } else {
+            // Create the forward domain and then its servers.
+            forward_domain_ = makeDomain("example.com.");
+            addDomainServer(forward_domain_, "fwd6-server.example.com",
+                            "2001:1::5");
+        }
+
+        // If the change mask does not include a reverse change clear the
+        // reverse domain; otherwise create the domain and its servers.
+        if (!(change_mask & REVERSE_CHG)) {
+            ncr->setReverseChange(false);
+            reverse_domain_.reset();
+        } else {
+            // Create the reverse domain and its server.
+            reverse_domain_ = makeDomain("1.2001.ip6.arpa.");
+            addDomainServer(reverse_domain_, "rev6-server.example.com",
+                            "2001:1::6");
+        }
+
+        // Now create the test transaction as would occur in update manager.
+        return (NameAddStubPtr(new NameAddStub(io_service_, ncr,
+                                               forward_domain_,
+                                               reverse_domain_)));
+    }
+
+
+    /// @brief Create a test transaction at a known point in the state model.
+    ///
+    /// Method prepares a new test transaction and sets its state and next
+    /// event values to those given.  This makes the transaction appear to
+    /// be at that point in the state model without having to transition it
+    /// through prerequisite states.   It also provides the ability to set
+    /// which change directions are requested: forward change only, reverse
+    /// change only, or both.
+    ///
+    /// @param state value to set as the current state
+    /// @param event value to post as the next event
+    /// @param change_mask determines which change directions are requested
+    NameAddStubPtr prepHandlerTest(unsigned int state, unsigned int event,
+                                   unsigned int change_mask = FWD_AND_REV_CHG) {
+        NameAddStubPtr name_add = makeTransaction4(change_mask);
+        name_add->initDictionaries();
+        name_add->postNextEvent(event);
+        name_add->setState(state);
+        return (name_add);
+    }
+
+};
+
+/// @brief Tests NameAddTransaction construction.
+/// This test verifies that:
+/// 1. Construction with invalid type of request
+/// 2. Valid construction functions properly
+TEST(NameAddTransaction, construction) {
+    IOServicePtr io_service(new isc::asiolink::IOService());
+
+    const char* msg_str =
+        "{"
+        " \"change_type\" : 1 , "
+        " \"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;
+    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 wrong change type fails.
+    EXPECT_THROW(NameAddTransaction(io_service, ncr,
+                                    forward_domain, reverse_domain),
+                                    NameAddTransactionError);
+
+    // Verify that a valid construction attempt works.
+    ncr->setChangeType(isc::dhcp_ddns::CHG_ADD);
+    EXPECT_NO_THROW(NameAddTransaction(io_service, ncr,
+                                       forward_domain, reverse_domain));
+}
+
+/// @brief Tests event and state dictionary construction and verification.
+TEST_F(NameAddTransactionTest, dictionaryCheck) {
+    NameAddStubPtr name_add;
+    ASSERT_NO_THROW(name_add = makeTransaction4());
+    // Verify that the event and state dictionary validation fails prior
+    // dictionary construction.
+    ASSERT_THROW(name_add->verifyEvents(), StateModelError);
+    ASSERT_THROW(name_add->verifyStates(), StateModelError);
+
+    // Construct both dictionaries.
+    ASSERT_NO_THROW(name_add->defineEvents());
+    ASSERT_NO_THROW(name_add->defineStates());
+
+    // Verify both event and state dictionaries now pass validation.
+    ASSERT_NO_THROW(name_add->verifyEvents());
+    ASSERT_NO_THROW(name_add->verifyStates());
+}
+
+/// @brief Tests construction of a DNS update request for adding a forward
+/// dns entry.
+TEST_F(NameAddTransactionTest, buildForwardAdd) {
+    // Create a IPv4 forward add transaction.
+    // Verify the request builds without error.
+    // and then verify the request contents.
+    NameAddStubPtr name_add;
+    ASSERT_NO_THROW(name_add = makeTransaction4());
+    ASSERT_NO_THROW(name_add->buildAddFwdAddressRequest());
+    checkAddFwdAddressRequest(*name_add);
+
+    // Create a IPv6 forward add transaction.
+    // Verify the request builds without error.
+    // and then verify the request contents.
+    ASSERT_NO_THROW(name_add = makeTransaction6());
+    ASSERT_NO_THROW(name_add->buildAddFwdAddressRequest());
+    checkAddFwdAddressRequest(*name_add);
+}
+
+/// @brief Tests construction of a DNS update request for replacing a forward
+/// dns entry.
+TEST_F(NameAddTransactionTest, buildReplaceFwdAddressRequest) {
+    // Create a IPv4 forward replace transaction.
+    // Verify the request builds without error.
+    // and then verify the request contents.
+    NameAddStubPtr name_add;
+    ASSERT_NO_THROW(name_add = makeTransaction4());
+    ASSERT_NO_THROW(name_add->buildReplaceFwdAddressRequest());
+    checkReplaceFwdAddressRequest(*name_add);
+
+    // Create a IPv6 forward replace transaction.
+    // Verify the request builds without error.
+    // and then verify the request contents.
+    ASSERT_NO_THROW(name_add = makeTransaction6());
+    ASSERT_NO_THROW(name_add->buildReplaceFwdAddressRequest());
+    checkReplaceFwdAddressRequest(*name_add);
+}
+
+/// @brief Tests the construction of a DNS update request for replacing a
+/// reverse dns entry.
+TEST_F(NameAddTransactionTest, buildReplaceRevPtrsRequest) {
+    // Create a IPv4 reverse replace transaction.
+    // Verify the request builds without error.
+    // and then verify the request contents.
+    NameAddStubPtr name_add;
+    ASSERT_NO_THROW(name_add = makeTransaction4());
+    ASSERT_NO_THROW(name_add->buildReplaceRevPtrsRequest());
+    checkReplaceRevPtrsRequest(*name_add);
+
+    // Create a IPv6 reverse replace transaction.
+    // Verify the request builds without error.
+    // and then verify the request contents.
+    ASSERT_NO_THROW(name_add = makeTransaction6());
+    ASSERT_NO_THROW(name_add->buildReplaceRevPtrsRequest());
+    checkReplaceRevPtrsRequest(*name_add);
+}
+
+// Tests the readyHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is START_EVT and request includes only a forward change
+// 2. Posted event is START_EVT and request includes both a forward and a
+// reverse change
+// 3. Posted event is START_EVT and request includes only a reverse change
+// 4. Posted event is invalid
+//
+TEST_F(NameAddTransactionTest, readyHandler) {
+    NameAddStubPtr name_add;
+
+    // Create a transaction which includes only a forward change.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameChangeTransaction::READY_ST,
+                                    StateModel::START_EVT, FORWARD_CHG));
+    // Run readyHandler.
+    EXPECT_NO_THROW(name_add->readyHandler());
+
+    // Verify that a request requiring only a forward change, transitions to
+    // selecting a forward server.
+    EXPECT_EQ(NameChangeTransaction::SELECTING_FWD_SERVER_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+              name_add->getNextEvent());
+
+
+    // Create a transaction which includes both a forward and a reverse change.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameChangeTransaction::READY_ST,
+                                    StateModel::START_EVT, FWD_AND_REV_CHG));
+    // Run readyHandler.
+    EXPECT_NO_THROW(name_add->readyHandler());
+
+    // Verify that a request requiring both forward and reverse, starts with
+    // the forward change by transitioning to selecting a forward server.
+    EXPECT_EQ(NameChangeTransaction::SELECTING_FWD_SERVER_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+              name_add->getNextEvent());
+
+
+    // Create and prep a reverse only transaction.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameChangeTransaction::READY_ST,
+                                    StateModel::START_EVT, REVERSE_CHG));
+    // Run readyHandler.
+    EXPECT_NO_THROW(name_add->readyHandler());
+
+    // Verify that a request requiring only a reverse change, transitions to
+    // selecting a reverse server.
+    EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+              name_add->getNextEvent());
+
+
+    // Create and prep transaction, poised to run the handler but with an
+    // invalid event.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameChangeTransaction::READY_ST,
+                                    StateModel::NOP_EVT));
+
+    // Running the readyHandler should throw.
+    EXPECT_THROW(name_add->readyHandler(), NameAddTransactionError);
+}
+
+// Tests the selectingFwdServerHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is SELECT_SERVER_EVT
+// 2. Posted event is SERVER_IO_ERROR_EVT
+// 3. Posted event is invalid
+//
+TEST_F(NameAddTransactionTest, selectingFwdServerHandler) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameChangeTransaction::
+                                    SELECTING_FWD_SERVER_ST,
+                                    NameChangeTransaction::SELECT_SERVER_EVT));
+
+    // Call selectingFwdServerHandler enough times to select all of the
+    // servers in it's current domain.  The first time, it will be with
+    // next event of SELECT_SERVER_EVT.  Thereafter it will be with a next
+    // event of SERVER_IO_ERROR_EVT.
+    int num_servers = name_add->getForwardDomain()->getServers()->size();
+    for (int i = 0; i < num_servers; ++i) {
+        // Run selectingFwdServerHandler.
+        ASSERT_NO_THROW(name_add->selectingFwdServerHandler())
+                        << " num_servers: " << num_servers
+                        << " selections: " << i;
+
+        // Verify that a server was selected.
+        ASSERT_TRUE(name_add->getCurrentServer())
+                    << " num_servers: " << num_servers << " selections: " << i;
+
+        // Verify that we transitioned correctly.
+        ASSERT_EQ(NameAddTransaction::ADDING_FWD_ADDRS_ST,
+                  name_add->getCurrState())
+                  << " num_servers: " << num_servers << " selections: " << i;
+        ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+                  name_add->getNextEvent())
+                  << " num_servers: " << num_servers << " selections: " << i;
+
+        // Post a server IO error event.  This simulates an IO error occuring
+        // and a need to select the new server.
+        ASSERT_NO_THROW(name_add->postNextEvent(NameChangeTransaction::
+                                                SERVER_IO_ERROR_EVT))
+                        << " num_servers: " << num_servers
+                        << " selections: " << i;
+    }
+
+    // We should have exhausted the list of servers. Processing another
+    // SERVER_IO_ERROR_EVT should transition us to failure.
+    EXPECT_NO_THROW(name_add->selectingFwdServerHandler());
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT,
+              name_add->getNextEvent());
+
+    // Create and prep transaction, poised to run the handler but with an
+    // invalid event.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameChangeTransaction::
+                                    SELECTING_FWD_SERVER_ST,
+                                    StateModel::NOP_EVT));
+
+    // Running the handler should throw.
+    EXPECT_THROW(name_add->selectingFwdServerHandler(),
+                 NameAddTransactionError);
+}
+
+// ************************ addingFwdAddrHandler Tests *****************
+
+// Tests that addingFwdAddrsHandler rejects invalid events.
+TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_InvalidEvent) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler but with
+    // an invalid event.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    StateModel::NOP_EVT));
+
+    // Running the handler should throw.
+    EXPECT_THROW(name_add->addingFwdAddrsHandler(),
+                 NameAddTransactionError);
+}
+
+// Tests addingFwdAddrsHandler with the following scenario:
+//
+//  The request includes only a forward change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates successful update
+//
+TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_FwdOnlyAddOK) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FORWARD_CHG));
+
+    // Should not be an update message yet.
+    D2UpdateMessagePtr update_msg = name_add->getDnsUpdateRequest();
+    ASSERT_FALSE(update_msg);
+
+    // At this point completion flags should be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Run addingFwdAddrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Verify that an update message was constructed properly.
+    checkAddFwdAddressRequest(*name_add);
+
+    // Verify that we are still in this state and next event is NOP_EVT.
+    // This indicates we "sent" the message and are waiting for IO completion.
+    EXPECT_EQ(NameAddTransaction::ADDING_FWD_ADDRS_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::NOP_EVT,
+              name_add->getNextEvent());
+
+    // Simulate receiving a successful update response.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run addingFwdAddrsHandler again to process the response.
+    EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Forward completion should be true, reverse should be false.
+    EXPECT_TRUE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Since it is a forward only change, we should be done.
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+              name_add->getNextEvent());
+}
+
+// Tests addingFwdAddrsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates successful update.
+//
+TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_fwdAndRevAddOK) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FWD_AND_REV_CHG));
+
+    // Run addingFwdAddrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Simulate receiving a successful update response.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run addingFwdAddrsHandler again  to process the response.
+    EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Forward change completion should be true, reverse flag should be false.
+    EXPECT_TRUE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Since the request also includes a reverse change we should
+    // be poised to start it. Verify that we transitioned correctly.
+    EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+              name_add->getNextEvent());
+}
+
+// Tests addingFwdAddrsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates the FQDN is in use.
+//
+TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_FqdnInUse) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT));
+
+    // Run addingFwdAddrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Simulate receiving a FQDN in use response.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::YXDOMAIN());
+
+    // Run addingFwdAddrsHandler again to process the response.
+    EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Completion flags should still be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Since the FQDN is in use, per the RFC we must attempt to replace it.
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameAddTransaction::REPLACING_FWD_ADDRS_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameAddTransaction::FQDN_IN_USE_EVT,
+              name_add->getNextEvent());
+}
+
+// Tests addingFwdAddrsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates the update was rejected.
+//
+TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_OtherRcode) {
+    NameAddStubPtr name_add;
+
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->selectFwdServer());
+
+    // Run addingFwdAddrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Simulate receiving server rejection response. Per RFC, anything other
+    // than no error or FQDN in use is failure.  Arbitrarily choosing refused.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+    // Run addingFwdAddrsHandler again to process the response.
+    EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Completion flags should still be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // We should have failed the transaction. Verify that we transitioned
+    // correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_add->getNextEvent());
+}
+
+// Tests addingFwdAddrsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request send times out MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_Timeout) {
+    NameAddStubPtr name_add;
+
+    // Create and prep a transaction, poised to run the handler.
+    // The log message issued when this test succeeds, displays the
+    // selected server, so we need to select a server before running this
+    // test.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->selectFwdServer());
+
+    // Verify that we can make maximum number of update attempts permitted
+    // and then transition to selecting a new server.
+    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+    for (int i = 1; i <= max_tries; ++i) {
+        const D2UpdateMessagePtr prev_msg = name_add->getDnsUpdateRequest();
+
+        // Run addingFwdAddrsHandler to send the request.
+        EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+        const D2UpdateMessagePtr curr_msg = name_add->getDnsUpdateRequest();
+        if (i == 1) {
+            // First time out we should build the message.
+            EXPECT_FALSE(prev_msg);
+            EXPECT_TRUE(curr_msg);
+        } else {
+            // Subsequent passes should reuse the request. We are only
+            // looking to check that we have not replaced the pointer value
+            // with a new pointer.  This tests the on_entry() logic which
+            // clears the request ONLY upon initial entry into the state.
+            EXPECT_TRUE(prev_msg == curr_msg);
+        }
+
+        // Simulate a server IO timeout.
+        name_add->setDnsUpdateStatus(DNSClient::TIMEOUT);
+        name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run addingFwdAddrsHandler again to process the response.
+        EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_add->getForwardChangeCompleted());
+        EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameAddTransaction::ADDING_FWD_ADDRS_ST,
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+                    name_add->getNextEvent());
+        } else {
+            // Server retries should be exhausted, time for a new server.
+            EXPECT_EQ(NameAddTransaction::SELECTING_FWD_SERVER_ST,
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+                    name_add->getNextEvent());
+        }
+    }
+}
+
+// Tests addingFwdAddrsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent but a corrupt response is received, this occurs
+//  MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_InvalidResponse) {
+    NameAddStubPtr name_add;
+
+    // Create and prep a transaction, poised to run the handler.
+    // The log message issued when this test succeeds, displays the
+    // selected server, so we need to select a server before running this
+    // test.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->selectFwdServer());
+
+    // Verify that we can make maximum number of update attempts permitted
+    // and then transition to selecting a new server.
+    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+    for (int i = 1; i <= max_tries; ++i) {
+        // Run addingFwdAddrsHandler to construct send the request.
+        EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+        // Simulate a corrupt server response.
+        name_add->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+        name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run addingFwdAddrsHandler again to process the response.
+        EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_add->getForwardChangeCompleted());
+        EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameAddTransaction::ADDING_FWD_ADDRS_ST,
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+                    name_add->getNextEvent());
+        } else {
+            // Server retries should be exhausted, time for a new server.
+            EXPECT_EQ(NameAddTransaction::SELECTING_FWD_SERVER_ST,
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+                    name_add->getNextEvent());
+        }
+    }
+
+}
+
+// ************************ replacingFwdAddrHandler Tests *****************
+
+// Tests that replacingFwdAddrsHandler rejects invalid events.
+TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_InvalidEvent) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler but with
+    // an invalid event.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    StateModel::NOP_EVT));
+
+    // Running the handler should throw.
+    EXPECT_THROW(name_add->replacingFwdAddrsHandler(),
+                 NameAddTransactionError);
+}
+
+// Tests replacingFwdAddrsHandler with the following scenario:
+//
+//  The request includes only a forward change.
+//  Initial posted event is FQDN_IN_USE_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates successful update.
+//
+TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_FwdOnlyAddOK) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST,
+                                    NameAddTransaction::
+                                    FQDN_IN_USE_EVT, FORWARD_CHG));
+
+    // Should not be an update message yet.
+    D2UpdateMessagePtr update_msg = name_add->getDnsUpdateRequest();
+    ASSERT_FALSE(update_msg);
+
+    // At this point completion flags should be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Run replacingFwdAddrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Verify that an update message was constructed properly.
+    checkReplaceFwdAddressRequest(*name_add);
+
+    // Verify that we are still in this state and next event is NOP_EVT.
+    // This indicates we "sent" the message and are waiting for IO completion.
+    EXPECT_EQ(NameAddTransaction::REPLACING_FWD_ADDRS_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::NOP_EVT,
+              name_add->getNextEvent());
+
+    // Simulate receiving a successful update response.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run replacingFwdAddrsHandler again to process the response.
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Forward completion should be true, reverse should be false.
+    EXPECT_TRUE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Since it is a forward only change, we should be done.
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+              name_add->getNextEvent());
+}
+
+// Tests replacingFwdAddrsHandler with the following scenario:
+//
+//  The request includes only a forward change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates successful update.
+//
+TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_FwdOnlyAddOK2) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FORWARD_CHG));
+
+    // Run replacingFwdAddrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Simulate receiving a successful update response.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run replacingFwdAddrsHandler again to process the response.
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Forward completion should be true, reverse should be false.
+    EXPECT_TRUE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Since it is a forward only change, we should be done.
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+              name_add->getNextEvent());
+}
+
+// Tests replacingFwdAddrsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is FQDN_IN_USE_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates successful update.
+//
+TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_FwdAndRevAddOK) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST,
+                                    NameAddTransaction::
+                                    FQDN_IN_USE_EVT, FWD_AND_REV_CHG));
+
+    // Run replacingFwdAddrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Simulate receiving a successful update response.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run replacingFwdAddrsHandler again  to process the response.
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Forward change completion should be true, reverse flag should be false.
+    EXPECT_TRUE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Since the request also includes a reverse change we should
+    // be poised to start it. Verify that we transitioned correctly.
+    EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+              name_add->getNextEvent());
+}
+
+
+// Tests replacingFwdAddrsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is FQDN_IN_USE_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates the FQDN is NOT in use.
+//
+TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_FqdnNotInUse) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST,
+                                    NameAddTransaction::
+                                    FQDN_IN_USE_EVT, FWD_AND_REV_CHG));
+
+    // Run replacingFwdAddrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Simulate receiving a FQDN not in use response.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXDOMAIN());
+
+    // Run replacingFwdAddrsHandler again to process the response.
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Completion flags should still be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Since the FQDN is no longer in use, per the RFC, try to add it.
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameAddTransaction::ADDING_FWD_ADDRS_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+              name_add->getNextEvent());
+}
+
+
+// Tests replacingFwdAddrsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  The update request is sent without error.
+//  A server response is received which indicates the update was rejected.
+//
+TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_OtherRcode) {
+    NameAddStubPtr name_add;
+    // Create the transaction.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST,
+                                    NameAddTransaction::
+                                    FQDN_IN_USE_EVT, FWD_AND_REV_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->selectFwdServer());
+
+    // Run replacingFwdAddrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Simulate receiving server rejection response. Per RFC, anything other
+    // than no error or FQDN in use is failure.  Arbitrarily choosing refused.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+    // Run replacingFwdAddrsHandler again to process the response.
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Completion flags should still be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // We should have failed the transaction. Verifiy that we transitioned
+    // correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_add->getNextEvent());
+}
+
+// Tests replacingFwdAddrsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is FQDN_IN_USE_EVT.
+//  The update request send times out MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_Timeout) {
+    NameAddStubPtr name_add;
+
+    // Create the transaction.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST,
+                                    NameAddTransaction::
+                                    FQDN_IN_USE_EVT, FWD_AND_REV_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->selectFwdServer());
+
+
+    // Verify that we can make maximum number of update attempts permitted
+    // and then transition to selecting a new server.
+    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+    for (int i = 1; i <= max_tries; ++i) {
+        const D2UpdateMessagePtr prev_msg = name_add->getDnsUpdateRequest();
+
+        // Run replacingFwdAddrsHandler to send the request.
+        EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+        const D2UpdateMessagePtr curr_msg = name_add->getDnsUpdateRequest();
+        if (i == 1) {
+            // First time out we should build the message.
+            EXPECT_FALSE(prev_msg);
+            EXPECT_TRUE(curr_msg);
+        } else {
+            // Subsequent passes should reuse the request. We are only
+            // looking to check that we have not replaced the pointer value
+            // with a new pointer.  This tests the on_entry() logic which
+            // clears the request ONLY upon initial entry into the state.
+            EXPECT_TRUE(prev_msg == curr_msg);
+        }
+
+        // Simulate a server IO timeout.
+        name_add->setDnsUpdateStatus(DNSClient::TIMEOUT);
+        name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run replacingFwdAddrsHandler again to process the response.
+        EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_add->getForwardChangeCompleted());
+        EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameAddTransaction::REPLACING_FWD_ADDRS_ST,
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+                    name_add->getNextEvent());
+        } else {
+            // Server retries should be exhausted, time for a new server.
+            EXPECT_EQ(NameAddTransaction::SELECTING_FWD_SERVER_ST,
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+                    name_add->getNextEvent());
+        }
+    }
+}
+
+// Tests replacingFwdAddrsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is FQDN_IN_USE_EVT.
+//  The update request is sent but a corrupt response is received, this occurs
+//  MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_CorruptResponse) {
+    NameAddStubPtr name_add;
+
+    // Create the transaction.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST,
+                                    NameAddTransaction::
+                                    FQDN_IN_USE_EVT, FWD_AND_REV_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->selectFwdServer());
+
+    // Verify that we can make maximum number of update attempts permitted
+    // and then transition to selecting a new server.
+    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+    for (int i = 1; i <= max_tries; ++i) {
+        const D2UpdateMessagePtr prev_msg = name_add->getDnsUpdateRequest();
+
+        // Run replacingFwdAddrsHandler to send the request.
+        EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+        const D2UpdateMessagePtr curr_msg = name_add->getDnsUpdateRequest();
+        if (i == 1) {
+            // First time out we should build the message.
+            EXPECT_FALSE(prev_msg);
+            EXPECT_TRUE(curr_msg);
+        } else {
+            // Subsequent passes should reuse the request. We are only
+            // looking to check that we have not replaced the pointer value
+            // with a new pointer.  This tests the on_entry() logic which
+            // clears the request ONLY upon initial entry into the state.
+            EXPECT_TRUE(prev_msg == curr_msg);
+        }
+
+        // Simulate a corrupt server response.
+        name_add->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+        name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run replacingFwdAddrsHandler again to process the response.
+        EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_add->getForwardChangeCompleted());
+        EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameAddTransaction::REPLACING_FWD_ADDRS_ST,
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+                    name_add->getNextEvent());
+        } else {
+            // Server retries should be exhausted, time for a new server.
+            EXPECT_EQ(NameAddTransaction::SELECTING_FWD_SERVER_ST,
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+                    name_add->getNextEvent());
+        }
+    }
+}
+
+// Tests the selectingRevServerHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is SELECT_SERVER_EVT
+// 2. Posted event is SERVER_IO_ERROR_EVT
+// 3. Posted event is invalid
+//
+TEST_F(NameAddTransactionTest, selectingRevServerHandler) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameChangeTransaction::
+                                    SELECTING_REV_SERVER_ST,
+                                    NameChangeTransaction::SELECT_SERVER_EVT));
+
+    // Call selectingRevServerHandler enough times to select all of the
+    // servers in it's current domain.  The first time, it will be with
+    // next event of SELECT_SERVER_EVT.  Thereafter it will be with a next
+    // event of SERVER_IO_ERROR_EVT.
+    int num_servers = name_add->getReverseDomain()->getServers()->size();
+    for (int i = 0; i < num_servers; ++i) {
+        // Run selectingRevServerHandler.
+        ASSERT_NO_THROW(name_add->selectingRevServerHandler())
+                        << " num_servers: " << num_servers
+                        << " selections: " << i;
+
+        // Verify that a server was selected.
+        ASSERT_TRUE(name_add->getCurrentServer())
+                    << " num_servers: " << num_servers
+                    << " selections: " << i;
+
+        // Verify that we transitioned correctly.
+        ASSERT_EQ(NameAddTransaction::REPLACING_REV_PTRS_ST,
+                  name_add->getCurrState())
+                  << " num_servers: " << num_servers << " selections: " << i;
+        ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+                  name_add->getNextEvent())
+                  << " num_servers: " << num_servers << " selections: " << i;
+
+        // Post a server IO error event.  This simulates an IO error occuring
+        // and a need to select the new server.
+        ASSERT_NO_THROW(name_add->postNextEvent(NameChangeTransaction::
+                                                SERVER_IO_ERROR_EVT))
+                        << " num_servers: " << num_servers
+                        << " selections: " << i;
+    }
+
+    // We should have exhausted the list of servers. Processing another
+    // SERVER_IO_ERROR_EVT should transition us to failure.
+    EXPECT_NO_THROW(name_add->selectingRevServerHandler());
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT,
+              name_add->getNextEvent());
+
+    // Create and prep transaction, poised to run the handler but with an
+    // invalid event.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameChangeTransaction::
+                                    SELECTING_REV_SERVER_ST,
+                                    StateModel::NOP_EVT));
+
+    // Running the handler should throw.
+    EXPECT_THROW(name_add->selectingRevServerHandler(),
+                 NameAddTransactionError);
+}
+
+//************************** replacingRevPtrsHandler tests *****************
+
+// Tests that replacingRevPtrsHandler rejects invalid events.
+TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_InvalidEvent) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler but with
+    // an invalid event.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST,
+                                    NameChangeTransaction::
+                                    StateModel::NOP_EVT));
+
+    // Running the handler should throw.
+    EXPECT_THROW(name_add->replacingRevPtrsHandler(),
+                 NameAddTransactionError);
+}
+
+// Tests replacingRevPtrsHandler with the following scenario:
+//
+//  The request includes only a reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates successful update.
+//
+TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_FwdOnlyAddOK) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST,
+                                    NameAddTransaction::
+                                    SERVER_SELECTED_EVT, REVERSE_CHG));
+
+    // Should not be an update message yet.
+    D2UpdateMessagePtr update_msg = name_add->getDnsUpdateRequest();
+    ASSERT_FALSE(update_msg);
+
+    // At this point completion flags should be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Run replacingRevPtrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+    // Verify that an update message was constructed properly.
+    checkReplaceRevPtrsRequest(*name_add);
+
+    // Verify that we are still in this state and next event is NOP_EVT.
+    // This indicates we "sent" the message and are waiting for IO completion.
+    EXPECT_EQ(NameAddTransaction::REPLACING_REV_PTRS_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::NOP_EVT,
+              name_add->getNextEvent());
+
+    // Simulate receiving a successful update response.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run replacingRevPtrsHandler again to process the response.
+    EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+    // Forward completion should be false, reverse should be true.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_TRUE(name_add->getReverseChangeCompleted());
+
+    // Since it is a reverse change, we should be done.
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+              name_add->getNextEvent());
+}
+
+// Tests replacingRevPtrsHandler with the following scenario:
+//
+//  The request includes only a reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates the update was rejected.
+//
+TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_OtherRcode) {
+    NameAddStubPtr name_add;
+    // Create the transaction.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST,
+                                    NameAddTransaction::
+                                    SERVER_SELECTED_EVT, REVERSE_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->selectRevServer());
+
+    // Run replacingRevPtrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+    // Simulate receiving server rejection response. Per RFC, anything other
+    // than no error is failure.  Arbitrarily choosing refused.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+    // Run replacingRevPtrsHandler again to process the response.
+    EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+    // Completion flags should still be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // We should have failed the transaction. Verify that we transitioned
+    // correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_add->getNextEvent());
+}
+
+// Tests replacingRevPtrsHandler with the following scenario:
+//
+//  The request includes only a reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request send times out MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_Timeout) {
+    NameAddStubPtr name_add;
+    // Create the transaction.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST,
+                                    NameAddTransaction::
+                                    SERVER_SELECTED_EVT, REVERSE_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->selectRevServer());
+
+    // Verify that we can make maximum number of update attempts permitted
+    // and then transition to selecting a new server.
+    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+    for (int i = 1; i <= max_tries; ++i) {
+        const D2UpdateMessagePtr prev_msg = name_add->getDnsUpdateRequest();
+
+        // Run replacingRevPtrsHandler to send the request.
+        EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+        const D2UpdateMessagePtr curr_msg = name_add->getDnsUpdateRequest();
+        if (i == 1) {
+            // First time out we should build the message.
+            EXPECT_FALSE(prev_msg);
+            EXPECT_TRUE(curr_msg);
+        } else {
+            // Subsequent passes should reuse the request. We are only
+            // looking to check that we have not replaced the pointer value
+            // with a new pointer.  This tests the on_entry() logic which
+            // clears the request ONLY upon initial entry into the state.
+            EXPECT_TRUE(prev_msg == curr_msg);
+        }
+
+        // Simulate a server IO timeout.
+        name_add->setDnsUpdateStatus(DNSClient::TIMEOUT);
+        name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run replacingRevPtrsHandler again to process the response.
+        EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_add->getForwardChangeCompleted());
+        EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameAddTransaction::REPLACING_REV_PTRS_ST,
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+                    name_add->getNextEvent());
+        } else {
+            // Server retries should be exhausted, time for a new server.
+            EXPECT_EQ(NameAddTransaction::SELECTING_REV_SERVER_ST,
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+                    name_add->getNextEvent());
+        }
+    }
+}
+
+// Tests replacingRevPtrsHandler with the following scenario:
+//
+//  The request includes only a reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent but a corrupt response is received, this occurs
+//  MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_CorruptResponse) {
+    NameAddStubPtr name_add;
+    // Create the transaction.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST,
+                                    NameAddTransaction::
+                                    SERVER_SELECTED_EVT, REVERSE_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->selectRevServer());
+
+    // Verify that we can make maximum number of update attempts permitted
+    // and then transition to selecting a new server.
+    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+    for (int i = 1; i <= max_tries; ++i) {
+        const D2UpdateMessagePtr prev_msg = name_add->getDnsUpdateRequest();
+
+        // Run replacingRevPtrsHandler to send the request.
+        EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+        const D2UpdateMessagePtr curr_msg = name_add->getDnsUpdateRequest();
+        if (i == 1) {
+            // First time out we should build the message.
+            EXPECT_FALSE(prev_msg);
+            EXPECT_TRUE(curr_msg);
+        } else {
+            // Subsequent passes should reuse the request. We are only
+            // looking to check that we have not replaced the pointer value
+            // with a new pointer.  This tests the on_entry() logic which
+            // clears the request ONLY upon initial entry into the state.
+            EXPECT_TRUE(prev_msg == curr_msg);
+        }
+
+        // Simulate a server corrupt response.
+        name_add->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+        name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run replacingRevPtrsHandler again to process the response.
+        EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_add->getForwardChangeCompleted());
+        EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameAddTransaction::REPLACING_REV_PTRS_ST,
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+                    name_add->getNextEvent());
+        } else {
+            // Server retries should be exhausted, time for a new server.
+            EXPECT_EQ(NameAddTransaction::SELECTING_REV_SERVER_ST,
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+                    name_add->getNextEvent());
+        }
+    }
+}
+
+// Tests the processAddOkHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is UPDATE_OK_EVT
+// 2. Posted event is invalid
+//
+TEST_F(NameAddTransactionTest, processAddOkHandler) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+                                    NameChangeTransaction::UPDATE_OK_EVT));
+    // Run processAddOkHandler.
+    EXPECT_NO_THROW(name_add->processAddOkHandler());
+
+    // Verify that a server was selected.
+    EXPECT_EQ(dhcp_ddns::ST_COMPLETED, name_add->getNcrStatus());
+
+    // Verify that the model has ended.
+    EXPECT_EQ(StateModel::END_ST, name_add->getCurrState());
+    EXPECT_EQ(StateModel::END_EVT, name_add->getNextEvent());
+
+
+    // Create and prep transaction, poised to run the handler but with an
+    // invalid event.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+                                    StateModel::NOP_EVT));
+    // Running the handler should throw.
+    EXPECT_THROW(name_add->processAddOkHandler(), NameAddTransactionError);
+}
+
+// Tests the processAddFailedHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is UPDATE_FAILED_EVT
+// 2. Posted event is invalid
+//
+TEST_F(NameAddTransactionTest, processAddFailedHandler) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameChangeTransaction::
+                                    PROCESS_TRANS_FAILED_ST,
+                                    NameChangeTransaction::UPDATE_FAILED_EVT));
+    // Run processAddFailedHandler.
+    EXPECT_NO_THROW(name_add->processAddFailedHandler());
+
+    // Verify that a server was selected.
+    EXPECT_EQ(dhcp_ddns::ST_FAILED, name_add->getNcrStatus());
+
+    // Verify that the model has ended. (Remember, the transaction failed NOT
+    // the model.  The model should have ended normally.)
+    EXPECT_EQ(StateModel::END_ST, name_add->getCurrState());
+    EXPECT_EQ(StateModel::END_EVT, name_add->getNextEvent());
+
+
+    // Create and prep transaction, poised to run the handler but with an
+    // invalid event.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameChangeTransaction::
+                                    PROCESS_TRANS_FAILED_ST,
+                                    StateModel::NOP_EVT));
+    // Running the handler should throw.
+    EXPECT_THROW(name_add->processAddFailedHandler(), NameAddTransactionError);
+}
+
+// Tests the processAddFailedHandler functionality.
+// It verifies behavior for posted event of NO_MORE_SERVERS_EVT.
+TEST_F(NameAddTransactionTest, processAddFailedHandler_NoMoreServers) {
+    NameAddStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameChangeTransaction::
+                                    PROCESS_TRANS_FAILED_ST,
+                                    NameChangeTransaction::
+                                    NO_MORE_SERVERS_EVT));
+
+    // Run processAddFailedHandler.
+    EXPECT_NO_THROW(name_remove->processAddFailedHandler());
+
+    // Verify that a server was selected.
+    EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus());
+
+    // Verify that the model has ended. (Remember, the transaction failed NOT
+    // the model.  The model should have ended normally.)
+    EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
+    EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
+}
+
+// Tests addingFwdAddrsHandler with the following scenario:
+//
+//  The request includes only a forward change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The send update request fails due to an unexpected exception.
+//
+TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_sendUpdateException) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FORWARD_CHG));
+
+    name_add->simulate_send_exception_ = true;
+
+    // Run replacingFwdAddrsHandler to construct and send the request.
+    ASSERT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Completion flags should be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Since IO exceptions should be gracefully handled, any that occur
+    // are unanticipated, and deemed unrecoverable, so the transaction should
+    // be transitioned to failure.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_add->getNextEvent());
+}
+
+// Tests replacingFwdAddrsHandler with the following scenario:
+//
+//  The request includes only a forward change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The send update request fails due to an unexpected exception.
+//
+TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_SendUpdateException) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FORWARD_CHG));
+
+    name_add->simulate_send_exception_ = true;
+
+    // Run replacingFwdAddrsHandler to construct and send the request.
+    ASSERT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Completion flags should be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Since IO exceptions should be gracefully handled, any that occur
+    // are unanticipated, and deemed unrecoverable, so the transaction should
+    // be transitioned to failure.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_add->getNextEvent());
+}
+
+// Tests replacingRevPtrHandler with the following scenario:
+//
+//  The request includes only a reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The send update request fails due to an unexpected exception.
+//
+TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_SendUpdateException) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, REVERSE_CHG));
+
+    name_add->simulate_send_exception_ = true;
+
+    // Run replacingRevPtrsHandler to construct and send the request.
+    ASSERT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+    // Completion flags should be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Since IO exceptions should be gracefully handled, any that occur
+    // are unanticipated, and deemed unrecoverable, so the transaction should
+    // be transitioned to failure.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_add->getNextEvent());
+}
+
+// Tests addingFwdAddrsHandler with the following scenario:
+//
+//  The request includes only a forward change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The request build fails due to an unexpected exception.
+//
+TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_BuildRequestException) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FORWARD_CHG));
+
+    // Set the one-shot exception simulation flag.
+    name_add->simulate_build_request_exception_ = true;
+
+    // Run replacingRevPtrsHandler to construct and send the request.
+    // This should fail with a build request throw which should be caught
+    // in the state handler.
+    ASSERT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Verify we did not attempt to send anything.
+    EXPECT_EQ(0, name_add->getUpdateAttempts());
+
+    // Completion flags should be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Since IO exceptions should be gracefully handled, any that occur
+    // are unanticipated, and deemed unrecoverable, so the transaction should
+    // be transitioned to failure.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_add->getNextEvent());
+}
+
+// Tests replacingFwdAddrsHandler with the following scenario:
+//
+//  The request includes only a forward change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The request build fails due to an unexpected exception.
+//
+TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_BuildRequestException) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FORWARD_CHG));
+
+    // Set the one-shot exception simulation flag.
+    name_add->simulate_build_request_exception_ = true;
+
+    // Run replacingFwdAddrsHandler to construct and send the request.
+    // This should fail with a build request throw which should be caught
+    // in the state handler.
+    ASSERT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Verify we did not attempt to send anything.
+    EXPECT_EQ(0, name_add->getUpdateAttempts());
+
+    // Completion flags should be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Since IO exceptions should be gracefully handled, any that occur
+    // are unanticipated, and deemed unrecoverable, so the transaction should
+    // be transitioned to failure.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_add->getNextEvent());
+}
+
+
+// Tests replacingRevPtrHandler with the following scenario:
+//
+//  The request includes only a reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The request build fails due to an unexpected exception.
+//
+TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_BuildRequestException) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add =
+                    prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, REVERSE_CHG));
+
+    // Set the one-shot exception simulation flag.
+    name_add->simulate_build_request_exception_ = true;
+
+    // Run replacingRevPtrsHandler to construct and send the request.
+    // This should fail with a build request throw which should be caught
+    // in the state handler.
+    ASSERT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+    // Verify we did not attempt to send anything.
+    EXPECT_EQ(0, name_add->getUpdateAttempts());
+
+    // Completion flags should be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Since IO exceptions should be gracefully handled, any that occur
+    // are unanticipated, and deemed unrecoverable, so the transaction should
+    // be transitioned to failure.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_add->getNextEvent());
+}
+
+
+}
diff --git a/src/bin/d2/tests/nc_remove_unittests.cc b/src/bin/d2/tests/nc_remove_unittests.cc
new file mode 100644
index 0000000..37efad6
--- /dev/null
+++ b/src/bin/d2/tests/nc_remove_unittests.cc
@@ -0,0 +1,1872 @@
+// 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_cfg_mgr.h>
+#include <d2/d2_cfg_mgr.h>
+#include <d2/nc_remove.h>
+#include <dns/messagerenderer.h>
+#include <nc_test_utils.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 class derived from NameRemoveTransaction to provide visiblity
+// to protected methods.
+class NameRemoveStub : public NameRemoveTransaction {
+public:
+    NameRemoveStub(IOServicePtr& io_service,
+                   dhcp_ddns::NameChangeRequestPtr& ncr,
+                   DdnsDomainPtr& forward_domain,
+                   DdnsDomainPtr& reverse_domain)
+        : NameRemoveTransaction(io_service, ncr, forward_domain,
+                                reverse_domain),
+          simulate_send_exception_(false),
+          simulate_build_request_exception_(false) {
+    }
+
+    virtual ~NameRemoveStub() {
+    }
+
+    /// @brief Simulates sending update requests to the DNS server
+    ///
+    /// This method simulates the initiation of an asynchronous send of
+    /// a DNS update request. It overrides the actual sendUpdate method in
+    /// the base class, thus avoiding an actual send, yet still increments
+    /// the update attempt count and posts a next event of NOP_EVT.
+    ///
+    /// It will also simulate an exception-based failure of sendUpdate, if
+    /// the simulate_send_exception_ flag is true.
+    ///
+    /// @param use_tsig_ Parameter is unused, but present in the base class
+    /// method.
+    ///
+    virtual void sendUpdate(bool /* use_tsig_ = false */) {
+        if (simulate_send_exception_) {
+            // Make the flag a one-shot by resetting it.
+            simulate_send_exception_ = false;
+            // Transition to failed.
+            transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+            return;
+        }
+
+        // Update send attempt count and post a NOP_EVT.
+        setUpdateAttempts(getUpdateAttempts() + 1);
+        postNextEvent(StateModel::NOP_EVT);
+    }
+
+    /// @brief Prepares the initial D2UpdateMessage
+    ///
+    /// This method overrides the NameChangeTransactio implementation to
+    /// provide the ability to simulate an exception throw in the build
+    /// request logic.
+    /// If the one-shot flag, simulate_build_request_exception_ is true,
+    /// this method will throw an exception, otherwise it will invoke the
+    /// base class method, providing normal functionality.
+    ///
+    /// For parameter description see the NameChangeTransaction implementation.
+    virtual D2UpdateMessagePtr prepNewRequest(DdnsDomainPtr domain) {
+        if (simulate_build_request_exception_) {
+            simulate_build_request_exception_ = false;
+            isc_throw (NameRemoveTransactionError,
+                       "Simulated build requests exception");
+        }
+
+        return (NameChangeTransaction::prepNewRequest(domain));
+    }
+
+    /// @brief Simulates receiving a response
+    ///
+    /// This method simulates the completion of a DNSClient send.  This allows
+    /// the state handler logic devoted to dealing with IO completion to be
+    /// fully exercised without requiring any actual IO.  The two primary
+    /// pieces of information gleaned from IO completion are the DNSClient
+    /// status which indicates whether or not the IO exchange was successful
+    /// and the rcode, which indicates the server's reaction to the request.
+    ///
+    /// This method updates the transaction's DNS status value to that of the
+    /// given parameter, and then constructs and DNS update response message
+    /// with the given rcode value.  To complete the simulation it then posts
+    /// a next event of IO_COMPLETED_EVT.
+    ///
+    /// @param status simulated DNSClient status
+    /// @param rcode  simulated server response code
+    void fakeResponse(const DNSClient::Status& status,
+                      const dns::Rcode& rcode) {
+        // Set the DNS update status.  This is normally set in
+        // DNSClient IO completion handler.
+        setDnsUpdateStatus(status);
+
+        // Construct an empty message with the given Rcode.
+        D2UpdateMessagePtr msg(new D2UpdateMessage(D2UpdateMessage::OUTBOUND));
+        msg->setRcode(rcode);
+
+        // Set the update response to the message.
+        setDnsUpdateResponse(msg);
+
+        // Post the IO completion event.
+        postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+    }
+
+    /// @brief Selects the first forward server.
+    /// Some state handlers require a server to have been selected.
+    /// This selects a server without going through the state
+    /// transition(s) to do so.
+    bool selectFwdServer() {
+        if (getForwardDomain()) {
+            initServerSelection(getForwardDomain());
+            selectNextServer();
+            return (getCurrentServer());
+        }
+
+        return (false);
+    }
+
+    /// @brief Selects the first reverse server.
+    /// Some state handlers require a server to have been selected.
+    /// This selects a server without going through the state
+    /// transition(s) to do so.
+    bool selectRevServer() {
+        if (getReverseDomain()) {
+            initServerSelection(getReverseDomain());
+            selectNextServer();
+            return (getCurrentServer());
+        }
+
+        return (false);
+    }
+
+    /// @brief One-shot flag which will simulate sendUpdate failure if true.
+    bool simulate_send_exception_;
+
+    /// @brief One-shot flag which will simulate an exception when sendUpdate
+    /// failure if true.
+    bool simulate_build_request_exception_;
+
+    using StateModel::postNextEvent;
+    using StateModel::setState;
+    using StateModel::initDictionaries;
+    using NameRemoveTransaction::defineEvents;
+    using NameRemoveTransaction::verifyEvents;
+    using NameRemoveTransaction::defineStates;
+    using NameRemoveTransaction::verifyStates;
+    using NameRemoveTransaction::readyHandler;
+    using NameRemoveTransaction::selectingFwdServerHandler;
+    using NameRemoveTransaction::getCurrentServer;
+    using NameRemoveTransaction::removingFwdAddrsHandler;
+    using NameRemoveTransaction::setDnsUpdateStatus;
+    using NameRemoveTransaction::removingFwdRRsHandler;
+    using NameRemoveTransaction::selectingRevServerHandler;
+    using NameRemoveTransaction::removingRevPtrsHandler;
+    using NameRemoveTransaction::processRemoveOkHandler;
+    using NameRemoveTransaction::processRemoveFailedHandler;
+    using NameRemoveTransaction::buildRemoveFwdAddressRequest;
+    using NameRemoveTransaction::buildRemoveFwdRRsRequest;
+    using NameRemoveTransaction::buildRemoveRevPtrsRequest;
+};
+
+typedef boost::shared_ptr<NameRemoveStub> NameRemoveStubPtr;
+
+/// @brief Test fixture for testing NameRemoveTransaction
+///
+/// Note this class uses NameRemoveStub class to exercise non-public
+/// aspects of NameRemoveTransaction.
+class NameRemoveTransactionTest : public TransactionTest {
+public:
+    NameRemoveTransactionTest() {
+    }
+
+    virtual ~NameRemoveTransactionTest() {
+    }
+
+    /// @brief Creates a transaction which requests an IPv4 DNS update.
+    ///
+    /// The transaction is constructed around a predefined (i.e. "canned")
+    /// IPv4 NameChangeRequest. The request has both forward and reverse DNS
+    /// changes requested.  Based upon the change mask, the transaction
+    /// will have either the forward, reverse, or both domains populated.
+    ///
+    /// @param change_mask determines which change directions are requested
+    NameRemoveStubPtr makeTransaction4(int change_mask) {
+        // Creates IPv4 remove request, forward, and reverse domains.
+        setupForIPv4Transaction(dhcp_ddns::CHG_REMOVE, change_mask);
+
+        // Now create the test transaction as would occur in update manager.
+        return (NameRemoveStubPtr(new NameRemoveStub(io_service_, ncr_,
+                                                     forward_domain_,
+                                                     reverse_domain_)));
+    }
+
+    /// @brief Creates a transaction which requests an IPv6 DNS update.
+    ///
+    /// The transaction is constructed around a predefined (i.e. "canned")
+    /// IPv6 NameChangeRequest. The request has both forward and reverse DNS
+    /// changes requested.  Based upon the change mask, the transaction
+    /// will have either the forward, reverse, or both domains populated.
+    ///
+    /// @param change_mask determines which change directions are requested
+    NameRemoveStubPtr makeTransaction6(int change_mask) {
+        // Creates IPv6 remove request, forward, and reverse domains.
+        setupForIPv6Transaction(dhcp_ddns::CHG_REMOVE, change_mask);
+
+        // Now create the test transaction as would occur in update manager.
+        return (NameRemoveStubPtr(new NameRemoveStub(io_service_, ncr_,
+                                                     forward_domain_,
+                                                     reverse_domain_)));
+    }
+
+    /// @brief Create a test transaction at a known point in the state model.
+    ///
+    /// Method prepares a new test transaction and sets its state and next
+    /// event values to those given.  This makes the transaction appear to
+    /// be at that point in the state model without having to transition it
+    /// through prerequisite states.   It also provides the ability to set
+    /// which change directions are requested: forward change only, reverse
+    /// change only, or both.
+    ///
+    /// @param state value to set as the current state
+    /// @param event value to post as the next event
+    /// @param change_mask determines which change directions are requested
+    /// @param family selects between an IPv4 (AF_INET) and IPv6 (AF_INET6)
+    /// transaction.
+    NameRemoveStubPtr prepHandlerTest(unsigned int state, unsigned int event,
+                                      unsigned int change_mask
+                                      = FWD_AND_REV_CHG,
+                                      short family = AF_INET) {
+        NameRemoveStubPtr name_remove = (family == AF_INET ?
+                                         makeTransaction4(change_mask) :
+                                         makeTransaction6(change_mask));
+        name_remove->initDictionaries();
+        name_remove->postNextEvent(event);
+        name_remove->setState(state);
+        return (name_remove);
+    }
+
+};
+
+/// @brief Tests NameRemoveTransaction construction.
+/// This test verifies that:
+/// 1. Construction with invalid type of request
+/// 2. Valid construction functions properly
+TEST(NameRemoveTransaction, construction) {
+    IOServicePtr io_service(new isc::asiolink::IOService());
+
+    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;
+    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 wrong change type fails.
+    EXPECT_THROW(NameRemoveTransaction(io_service, ncr,
+                                       forward_domain, reverse_domain),
+                                       NameRemoveTransactionError);
+
+    // Verify that a valid construction attempt works.
+    ncr->setChangeType(isc::dhcp_ddns::CHG_REMOVE);
+    EXPECT_NO_THROW(NameRemoveTransaction(io_service, ncr,
+                                          forward_domain, reverse_domain));
+}
+
+/// @brief Tests event and state dictionary construction and verification.
+TEST_F(NameRemoveTransactionTest, dictionaryCheck) {
+    NameRemoveStubPtr name_remove;
+    ASSERT_NO_THROW(name_remove = makeTransaction4(FWD_AND_REV_CHG));
+    // Verify that the event and state dictionary validation fails prior
+    // dictionary construction.
+    ASSERT_THROW(name_remove->verifyEvents(), StateModelError);
+    ASSERT_THROW(name_remove->verifyStates(), StateModelError);
+
+    // Construct both dictionaries.
+    ASSERT_NO_THROW(name_remove->defineEvents());
+    ASSERT_NO_THROW(name_remove->defineStates());
+
+    // Verify both event and state dictionaries now pass validation.
+    ASSERT_NO_THROW(name_remove->verifyEvents());
+    ASSERT_NO_THROW(name_remove->verifyStates());
+}
+
+
+/// @brief Tests construction of a DNS update request for removing forward
+/// DNS address RRs.
+TEST_F(NameRemoveTransactionTest, buildRemoveFwdAddressRequest) {
+    // Create a IPv4 forward add transaction.
+    // Verify the request builds without error.
+    // and then verify the request contents.
+    NameRemoveStubPtr name_remove;
+    ASSERT_NO_THROW(name_remove = makeTransaction4(FORWARD_CHG));
+    ASSERT_NO_THROW(name_remove->buildRemoveFwdAddressRequest());
+    checkRemoveFwdAddressRequest(*name_remove);
+
+    // Create a IPv6 forward add transaction.
+    // Verify the request builds without error.
+    // and then verify the request contents.
+    ASSERT_NO_THROW(name_remove = makeTransaction6(FORWARD_CHG));
+    ASSERT_NO_THROW(name_remove->buildRemoveFwdAddressRequest());
+    checkRemoveFwdAddressRequest(*name_remove);
+}
+
+/// @brief Tests construction of a DNS update request for removing forward
+/// dns RR entries.
+TEST_F(NameRemoveTransactionTest, buildRemoveFwdRRsRequest) {
+    // Create a IPv4 forward replace transaction.
+    // Verify the request builds without error.
+    // and then verify the request contents.
+    NameRemoveStubPtr name_remove;
+    ASSERT_NO_THROW(name_remove = makeTransaction4(FORWARD_CHG));
+    ASSERT_NO_THROW(name_remove->buildRemoveFwdRRsRequest());
+    checkRemoveFwdRRsRequest(*name_remove);
+
+    // Create a IPv6 forward replace transaction.
+    // Verify the request builds without error.
+    // and then verify the request contents.
+    ASSERT_NO_THROW(name_remove = makeTransaction6(FORWARD_CHG));
+    ASSERT_NO_THROW(name_remove->buildRemoveFwdRRsRequest());
+    checkRemoveFwdRRsRequest(*name_remove);
+}
+
+/// @brief Tests the construction of a DNS update request for removing a
+/// reverse dns entry.
+TEST_F(NameRemoveTransactionTest, buildRemoveRevPtrsRequest) {
+    // Create a IPv4 reverse replace transaction.
+    // Verify the request builds without error.
+    // and then verify the request contents.
+    NameRemoveStubPtr name_remove;
+    ASSERT_NO_THROW(name_remove = makeTransaction4(REVERSE_CHG));
+    ASSERT_NO_THROW(name_remove->buildRemoveRevPtrsRequest());
+    checkRemoveRevPtrsRequest(*name_remove);
+
+    // Create a IPv6 reverse replace transaction.
+    // Verify the request builds without error.
+    // and then verify the request contents.
+    ASSERT_NO_THROW(name_remove = makeTransaction6(REVERSE_CHG));
+    ASSERT_NO_THROW(name_remove->buildRemoveRevPtrsRequest());
+    checkRemoveRevPtrsRequest(*name_remove);
+}
+
+// Tests the readyHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is START_EVT and request includes only a forward change
+// 2. Posted event is START_EVT and request includes both a forward and a
+// reverse change
+// 3. Posted event is START_EVT and request includes only a reverse change
+// 4. Posted event is invalid
+//
+TEST_F(NameRemoveTransactionTest, readyHandler) {
+    NameRemoveStubPtr name_remove;
+
+    // Create a transaction which includes only a forward change.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameChangeTransaction::READY_ST,
+                                    StateModel::START_EVT, FORWARD_CHG));
+    // Run readyHandler.
+    EXPECT_NO_THROW(name_remove->readyHandler());
+
+    // Verify that a request requiring only a forward change, transitions to
+    // selecting a forward server.
+    EXPECT_EQ(NameChangeTransaction::SELECTING_FWD_SERVER_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+              name_remove->getNextEvent());
+
+    // Create a transaction which includes both a forward and a reverse change.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameChangeTransaction::READY_ST,
+                                    StateModel::START_EVT, FWD_AND_REV_CHG));
+    // Run readyHandler.
+    EXPECT_NO_THROW(name_remove->readyHandler());
+
+    // Verify that a request requiring both forward and reverse, starts with
+    // the forward change by transitioning to selecting a forward server.
+    EXPECT_EQ(NameChangeTransaction::SELECTING_FWD_SERVER_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+              name_remove->getNextEvent());
+
+    // Create and prep a reverse only transaction.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameChangeTransaction::READY_ST,
+                                    StateModel::START_EVT, REVERSE_CHG));
+    // Run readyHandler.
+    EXPECT_NO_THROW(name_remove->readyHandler());
+
+    // Verify that a request requiring only a reverse change, transitions to
+    // selecting a reverse server.
+    EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+              name_remove->getNextEvent());
+
+    // Create and prep transaction, poised to run the handler but with an
+    // invalid event.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameChangeTransaction::READY_ST,
+                                    StateModel::NOP_EVT));
+
+    // Running the readyHandler should throw.
+    EXPECT_THROW(name_remove->readyHandler(), NameRemoveTransactionError);
+}
+
+
+// Tests the selectingFwdServerHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is SELECT_SERVER_EVT
+// 2. Posted event is SERVER_IO_ERROR_EVT
+// 3. Posted event is invalid
+//
+TEST_F(NameRemoveTransactionTest, selectingFwdServerHandler) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameChangeTransaction::
+                                    SELECTING_FWD_SERVER_ST,
+                                    NameChangeTransaction::SELECT_SERVER_EVT));
+
+    // Call selectingFwdServerHandler enough times to select all of the
+    // servers in it's current domain.  The first time, it will be with
+    // next event of SELECT_SERVER_EVT.  Thereafter it will be with a next
+    // event of SERVER_IO_ERROR_EVT.
+    int num_servers = name_remove->getForwardDomain()->getServers()->size();
+    for (int i = 0; i < num_servers; ++i) {
+        // Run selectingFwdServerHandler.
+        ASSERT_NO_THROW(name_remove->selectingFwdServerHandler())
+                        << " num_servers: " << num_servers
+                        << " selections: " << i;
+
+        // Verify that a server was selected.
+        ASSERT_TRUE(name_remove->getCurrentServer())
+                    << " num_servers: " << num_servers << " selections: " << i;
+
+        // Verify that we transitioned correctly.
+        ASSERT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
+                  name_remove->getCurrState())
+                  << " num_servers: " << num_servers << " selections: " << i;
+        ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+                  name_remove->getNextEvent())
+                  << " num_servers: " << num_servers << " selections: " << i;
+
+        // Post a server IO error event.  This simulates an IO error occuring
+        // and a need to select the new server.
+        ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction::
+                                                   SERVER_IO_ERROR_EVT))
+                        << " num_servers: " << num_servers
+                        << " selections: " << i;
+    }
+
+    // We should have exhausted the list of servers. Processing another
+    // SERVER_IO_ERROR_EVT should transition us to failure.
+    EXPECT_NO_THROW(name_remove->selectingFwdServerHandler());
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT,
+              name_remove->getNextEvent());
+
+    // Create and prep transaction, poised to run the handler but with an
+    // invalid event.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameChangeTransaction::
+                                    SELECTING_FWD_SERVER_ST,
+                                    StateModel::NOP_EVT));
+
+    // Running the handler should throw.
+    EXPECT_THROW(name_remove->selectingFwdServerHandler(),
+                 NameRemoveTransactionError);
+}
+
+// ************************ addingFwdAddrHandler Tests *****************
+
+// Tests that removingFwdAddrsHandler rejects invalid events.
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_InvalidEvent) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler but with
+    // an invalid event.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_ADDRS_ST,
+                                    StateModel::NOP_EVT));
+
+    // Running the handler should throw.
+    EXPECT_THROW(name_remove->removingFwdAddrsHandler(),
+                 NameRemoveTransactionError);
+}
+
+
+// Tests addingFwdAddrsHandler with the following scenario:
+//
+//  The request includes only a forward change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates successful update
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_FwdOnlyOK) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FORWARD_CHG));
+
+    // Should not be an update message yet.
+    D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest();
+    ASSERT_FALSE(update_msg);
+
+    // At this point completion flags should be false.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // Run removingFwdAddrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+    // Verify that an update message was constructed properly.
+    checkRemoveFwdAddressRequest(*name_remove);
+
+    // Verify that we are still in this state and next event is NOP_EVT.
+    // This indicates we "sent" the message and are waiting for IO completion.
+    EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::NOP_EVT,
+              name_remove->getNextEvent());
+
+    // Simulate receiving a successful update response.
+    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run removingFwdAddrsHandler again to process the response.
+    EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+    // Completion flags should both still be false, as we are only partly
+    // done with forward updates.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // Since we succeeded, we should now attempt to remove any remaining
+    // forward RRs.
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameRemoveTransaction::UPDATE_OK_EVT,
+              name_remove->getNextEvent());
+}
+
+// Tests addingFwdAddrsHandler with the following scenario:
+//
+//  The request includes only a forward change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates FQDN is not in use.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_FqdnNotInUse) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FORWARD_CHG));
+
+    // Run removingFwdAddrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+    // Simulate receiving a FQDN not in use response.
+    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXDOMAIN());
+
+    // Run removingFwdAddrsHandler again to process the response.
+    EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+    // Completion flags should both still be false, as we are only partly
+    // done with forward updates.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // There was no address RR to remove, but we will still make sure there
+    // are no other RRs for this FQDN.
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameRemoveTransaction::UPDATE_OK_EVT,
+              name_remove->getNextEvent());
+}
+
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates the update was rejected.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_OtherRcode) {
+    NameRemoveStubPtr name_remove;
+
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_remove->selectFwdServer());
+
+    // Run removingFwdAddrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+    // Simulate receiving server rejection response. Per RFC, anything other
+    // than no error or FQDN not in use is failure. Arbitrarily choosing
+    // refused.
+    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+    // Run removingFwdAddrsHandler again to process the response.
+    EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+    // Completion flags should still be false.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // We should have failed the transaction. Verify that we transitioned
+    // correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_remove->getNextEvent());
+}
+
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request send times out MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_Timeout) {
+    NameRemoveStubPtr name_remove;
+
+    // Create and prep a transaction, poised to run the handler.
+    // The log message issued when this test succeeds, displays the
+    // selected server, so we need to select a server before running this
+    // test.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_remove->selectFwdServer());
+
+    // Verify that we can make maximum number of update attempts permitted
+    // and then transition to selecting a new server.
+    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+    for (int i = 1; i <= max_tries; ++i) {
+        const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();
+
+        // Run removingFwdAddrsHandler to send the request.
+        EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+        const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
+        if (i == 1) {
+            // First time around we should build the message.
+            EXPECT_FALSE(prev_msg);
+            EXPECT_TRUE(curr_msg);
+        } else {
+            // Subsequent passes should reuse the request. We are only
+            // looking to check that we have not replaced the pointer value
+            // with a new pointer.  This tests the on_entry() logic which
+            // clears the request ONLY upon initial entry into the state.
+            EXPECT_TRUE(prev_msg == curr_msg);
+        }
+
+        // Simulate a server IO timeout.
+        name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT);
+        name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run removingFwdAddrsHandler again to process the response.
+        EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+        EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
+                      name_remove->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+                    name_remove->getNextEvent());
+        } else {
+            // Server retries should be exhausted, time for a new server.
+            EXPECT_EQ(NameRemoveTransaction::SELECTING_FWD_SERVER_ST,
+                      name_remove->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+                    name_remove->getNextEvent());
+        }
+    }
+}
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent but a corrupt response is received, this occurs
+//  MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_InvalidResponse) {
+    NameRemoveStubPtr name_remove;
+
+    // Create and prep a transaction, poised to run the handler.
+    // The log message issued when this test succeeds, displays the
+    // selected server, so we need to select a server before running this
+    // test.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_remove->selectFwdServer());
+
+    // Verify that we can make maximum number of update attempts permitted
+    // and then transition to selecting a new server.
+    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+    for (int i = 1; i <= max_tries; ++i) {
+        // Run removingFwdAddrsHandler to construct send the request.
+        EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+        // Simulate a corrupt server response.
+        name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+        name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run removingFwdAddrsHandler again to process the response.
+        EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+        EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
+                      name_remove->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+                    name_remove->getNextEvent());
+        } else {
+            // Server retries should be exhausted, time for a new server.
+            EXPECT_EQ(NameRemoveTransaction::SELECTING_FWD_SERVER_ST,
+                      name_remove->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+                    name_remove->getNextEvent());
+        }
+    }
+
+}
+
+// ************************ removingFwdRRsHandler Tests *****************
+
+// Tests that removingFwdRRsHandler rejects invalid events.
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_InvalidEvent) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler but with
+    // an invalid event.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_RRS_ST,
+                                    StateModel::NOP_EVT));
+
+    // Running the handler should throw.
+    EXPECT_THROW(name_remove->removingFwdRRsHandler(),
+                 NameRemoveTransactionError);
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+//  The request includes only a forward change.
+//  Initial posted event is UPDATE_OK_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates successful update.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FwdOnlyOK) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    UPDATE_OK_EVT, FORWARD_CHG));
+
+    // Should not be an update message yet.
+    D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest();
+    ASSERT_FALSE(update_msg);
+
+    // At this point completion flags should be false.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // Run removingFwdRRsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+    // Verify that an update message was constructed properly.
+    checkRemoveFwdRRsRequest(*name_remove);
+
+    // Verify that we are still in this state and next event is NOP_EVT.
+    // This indicates we "sent" the message and are waiting for IO completion.
+    EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::NOP_EVT,
+              name_remove->getNextEvent());
+
+    // Simulate receiving a successful update response.
+    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run removingFwdRRsHandler again to process the response.
+    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+    // Forward completion should be true, reverse should be false.
+    EXPECT_TRUE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // Since it is a forward only change, we should be done.
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+              name_remove->getNextEvent());
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+//  The request includes only a forward change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates successful update.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FwdOnlyOK2) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FORWARD_CHG));
+
+    // Run removingFwdRRsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+    // Simulate receiving a successful update response.
+    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run removingFwdRRsHandler again to process the response.
+    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+    // Forward completion should be true, reverse should be false.
+    EXPECT_TRUE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // Since it is a forward only change, we should be done.
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+              name_remove->getNextEvent());
+}
+
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is UPDATE_OK_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates successful update.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FwdAndRevOK) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    UPDATE_OK_EVT, FWD_AND_REV_CHG));
+
+    // Run removingFwdRRsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+    // Simulate receiving a successful update response.
+    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run removingFwdRRsHandler again  to process the response.
+    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+    // Forward change completion should be true, reverse flag should be false.
+    EXPECT_TRUE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // Since the request also includes a reverse change we should
+    // be poised to start it. Verify that we transitioned correctly.
+    EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+              name_remove->getNextEvent());
+}
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is UPDATE_OK_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates the FQDN is NOT in use.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FqdnNotInUse) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    UPDATE_OK_EVT, FORWARD_CHG));
+
+    // Run removingFwdRRsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+    // Simulate receiving a FQDN not in use response.
+    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXDOMAIN());
+
+    // Run removingFwdRRsHandler again to process the response.
+    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+    // Forwad completion flag should be true, reverse should still be false.
+    EXPECT_TRUE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // The FQDN is no longer in use, RFC is unclear about this,
+    // but we will treat this as success.
+    // Since it is a forward only change, we should be done.
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+              name_remove->getNextEvent());
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  The update request is sent without error.
+//  A server response is received which indicates the update was rejected.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_OtherRcode) {
+    NameRemoveStubPtr name_remove;
+    // Create the transaction.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    UPDATE_OK_EVT, FWD_AND_REV_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_remove->selectFwdServer());
+
+    // Run removingFwdRRsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+    // Simulate receiving server rejection response. Per RFC, anything other
+    // than no error is failure (we are also treating FQDN not in use is
+    // success). Arbitrarily choosing refused.
+    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+    // Run removingFwdRRsHandler again to process the response.
+    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+    // Completion flags should still be false.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // We should have failed the transaction. Verifiy that we transitioned
+    // correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_remove->getNextEvent());
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is UPDATE_OK_EVT.
+//  The update request send times out MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_Timeout) {
+    NameRemoveStubPtr name_remove;
+
+    // Create the transaction.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_RRS_ST,
+                                    NameChangeTransaction::
+                                    UPDATE_OK_EVT, FWD_AND_REV_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_remove->selectFwdServer());
+
+    // Verify that we can make maximum number of update attempts permitted
+    // and then transition to selecting a new server.
+    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+    for (int i = 1; i <= max_tries; ++i) {
+        const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();
+
+        // Run removingFwdRRsHandler to send the request.
+        EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+        const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
+        if (i == 1) {
+            // First time around we should build the message.
+            EXPECT_FALSE(prev_msg);
+            EXPECT_TRUE(curr_msg);
+        } else {
+            // Subsequent passes should reuse the request. We are only
+            // looking to check that we have not replaced the pointer value
+            // with a new pointer.  This tests the on_entry() logic which
+            // clears the request ONLY upon initial entry into the state.
+            EXPECT_TRUE(prev_msg == curr_msg);
+        }
+
+        // Simulate a server IO timeout.
+        name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT);
+        name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run removingFwdRRsHandler again to process the response.
+        EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+        EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST,
+                      name_remove->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+                    name_remove->getNextEvent());
+        } else {
+            // Server retries should be exhausted.
+            // We should abandon the transaction.
+            EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+                      name_remove->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+                    name_remove->getNextEvent());
+        }
+    }
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is UPDATE_OK_EVT.
+//  The update request is sent but a corrupt response is received, this occurs
+//  MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_InvalidResponse) {
+    NameRemoveStubPtr name_remove;
+
+    // Create the transaction.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_RRS_ST,
+                                    NameChangeTransaction::
+                                    UPDATE_OK_EVT, FWD_AND_REV_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_remove->selectFwdServer());
+
+    // Verify that we can make maximum number of update attempts permitted
+    // and then transition to selecting a new server.
+    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+    for (int i = 1; i <= max_tries; ++i) {
+        const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();
+
+        // Run removingFwdRRsHandler to send the request.
+        EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+        const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
+        if (i == 1) {
+            // First time around we should build the message.
+            EXPECT_FALSE(prev_msg);
+            EXPECT_TRUE(curr_msg);
+        } else {
+            // Subsequent passes should reuse the request. We are only
+            // looking to check that we have not replaced the pointer value
+            // with a new pointer.  This tests the on_entry() logic which
+            // clears the request ONLY upon initial entry into the state.
+            EXPECT_TRUE(prev_msg == curr_msg);
+        }
+
+        // Simulate a corrupt server response.
+        name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+        name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run removingFwdRRsHandler again to process the response.
+        EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+        EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST,
+                      name_remove->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+                    name_remove->getNextEvent());
+        } else {
+            // Server retries should be exhausted.
+            // We should abandon the transaction.
+            EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+                      name_remove->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+                    name_remove->getNextEvent());
+        }
+    }
+}
+
+
+// Tests the selectingRevServerHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is SELECT_SERVER_EVT
+// 2. Posted event is SERVER_IO_ERROR_EVT
+// 3. Posted event is invalid
+//
+TEST_F(NameRemoveTransactionTest, selectingRevServerHandler) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameChangeTransaction::
+                                    SELECTING_REV_SERVER_ST,
+                                    NameChangeTransaction::SELECT_SERVER_EVT));
+
+    // Call selectingRevServerHandler enough times to select all of the
+    // servers in it's current domain.  The first time, it will be with
+    // next event of SELECT_SERVER_EVT.  Thereafter it will be with a next
+    // event of SERVER_IO_ERROR_EVT.
+    int num_servers = name_remove->getReverseDomain()->getServers()->size();
+    for (int i = 0; i < num_servers; ++i) {
+        // Run selectingRevServerHandler.
+        ASSERT_NO_THROW(name_remove->selectingRevServerHandler())
+                        << " num_servers: " << num_servers
+                        << " selections: " << i;
+
+        // Verify that a server was selected.
+        ASSERT_TRUE(name_remove->getCurrentServer())
+                    << " num_servers: " << num_servers
+                    << " selections: " << i;
+
+        // Verify that we transitioned correctly.
+        ASSERT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
+                  name_remove->getCurrState())
+                  << " num_servers: " << num_servers << " selections: " << i;
+        ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+                  name_remove->getNextEvent())
+                  << " num_servers: " << num_servers << " selections: " << i;
+
+        // Post a server IO error event.  This simulates an IO error occuring
+        // and a need to select the new server.
+        ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction::
+                                                SERVER_IO_ERROR_EVT))
+                        << " num_servers: " << num_servers
+                        << " selections: " << i;
+    }
+
+    // We should have exhausted the list of servers. Processing another
+    // SERVER_IO_ERROR_EVT should transition us to failure.
+    EXPECT_NO_THROW(name_remove->selectingRevServerHandler());
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT,
+              name_remove->getNextEvent());
+
+    // Create and prep transaction, poised to run the handler but with an
+    // invalid event.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameChangeTransaction::
+                                    SELECTING_REV_SERVER_ST,
+                                    StateModel::NOP_EVT));
+
+    // Running the handler should throw.
+    EXPECT_THROW(name_remove->selectingRevServerHandler(),
+                 NameRemoveTransactionError);
+}
+
+//************************** removingRevPtrsHandler tests *****************
+
+// Tests that removingRevPtrsHandler rejects invalid events.
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_InvalidEvent) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler but with
+    // an invalid event.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_REV_PTRS_ST,
+                                    StateModel::NOP_EVT));
+
+    // Running the handler should throw.
+    EXPECT_THROW(name_remove->removingRevPtrsHandler(),
+                 NameRemoveTransactionError);
+}
+
+// Tests removingRevPtrsHandler with the following scenario:
+//
+//  The request includes only a reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates successful update.
+//
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_RevOnlyOK) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_REV_PTRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, REVERSE_CHG));
+
+    // Should not be an update message yet.
+    D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest();
+    ASSERT_FALSE(update_msg);
+
+    // At this point completion flags should be false.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // Run removingRevPtrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+    // Verify that an update message was constructed properly.
+    checkRemoveRevPtrsRequest(*name_remove);
+
+    // Verify that we are still in this state and next event is NOP_EVT.
+    // This indicates we "sent" the message and are waiting for IO completion.
+    EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(StateModel::NOP_EVT,
+              name_remove->getNextEvent());
+
+    // Simulate receiving a successful update response.
+    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run removingRevPtrsHandler again to process the response.
+    EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+    // Forward completion should be false, reverse should be true.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_TRUE(name_remove->getReverseChangeCompleted());
+
+    // Since it is a reverse change, we should be done.
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+              name_remove->getNextEvent());
+}
+
+// Tests removingRevPtrsHandler with the following scenario:
+//
+//  The request includes only a reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates FQDN is NOT in use.
+//
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_FqdnNotInUse) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_REV_PTRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, REVERSE_CHG));
+
+    // Should not be an update message yet.
+    D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest();
+    ASSERT_FALSE(update_msg);
+
+    // At this point completion flags should be false.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // Run removingRevPtrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+    // Verify that an update message was constructed properly.
+    checkRemoveRevPtrsRequest(*name_remove);
+
+    // Verify that we are still in this state and next event is NOP_EVT.
+    // This indicates we "sent" the message and are waiting for IO completion.
+    EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(StateModel::NOP_EVT,
+              name_remove->getNextEvent());
+
+    // Simulate receiving a FQDN not in use response.
+    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXDOMAIN());
+
+    // Run removingRevPtrsHandler again to process the response.
+    EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+    // Forward completion should be false, reverse should be true.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_TRUE(name_remove->getReverseChangeCompleted());
+
+    // Since it is a reverse change, we should be done.
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+              name_remove->getNextEvent());
+}
+
+// Tests removingRevPtrsHandler with the following scenario:
+//
+//  The request includes only a reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates the update was rejected.
+//
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_OtherRcode) {
+    NameRemoveStubPtr name_remove;
+    // Create the transaction.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_REV_PTRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, REVERSE_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_remove->selectRevServer());
+
+    // Run removingRevPtrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+    // Simulate receiving server rejection response. Per RFC, anything other
+    // than no error is failure.  Arbitrarily choosing refused.
+    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+    // Run removingRevPtrsHandler again to process the response.
+    EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+    // Completion flags should still be false.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // We should have failed the transaction. Verify that we transitioned
+    // correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_remove->getNextEvent());
+}
+
+// Tests removingRevPtrsHandler with the following scenario:
+//
+//  The request includes only a reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request send times out MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_Timeout) {
+    NameRemoveStubPtr name_remove;
+    // Create the transaction.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_REV_PTRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, REVERSE_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_remove->selectRevServer());
+
+    // Verify that we can make maximum number of update attempts permitted
+    // and then transition to selecting a new server.
+    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+    for (int i = 1; i <= max_tries; ++i) {
+        const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();
+
+        // Run removingRevPtrsHandler to send the request.
+        EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+        const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
+        if (i == 1) {
+            // First time around we should build the message.
+            EXPECT_FALSE(prev_msg);
+            EXPECT_TRUE(curr_msg);
+        } else {
+            // Subsequent passes should reuse the request. We are only
+            // looking to check that we have not replaced the pointer value
+            // with a new pointer.  This tests the on_entry() logic which
+            // clears the request ONLY upon initial entry into the state.
+            EXPECT_TRUE(prev_msg == curr_msg);
+        }
+
+        // Simulate a server IO timeout.
+        name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT);
+        name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run removingRevPtrsHandler again to process the response.
+        EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+        EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
+                      name_remove->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+                      name_remove->getNextEvent());
+        } else {
+            // Server retries should be exhausted, time for a new server.
+            EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
+                      name_remove->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+                      name_remove->getNextEvent());
+        }
+    }
+}
+
+
+// Tests removingRevPtrsHandler with the following scenario:
+//
+//  The request includes only a reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The update request is sent but a corrupt response is received, this occurs
+//  MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_CorruptResponse) {
+    NameRemoveStubPtr name_remove;
+    // Create the transaction.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_REV_PTRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, REVERSE_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_remove->selectRevServer());
+
+    // Verify that we can make maximum number of update attempts permitted
+    // and then transition to selecting a new server.
+    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+    for (int i = 1; i <= max_tries; ++i) {
+        const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();
+
+        // Run removingRevPtrsHandler to send the request.
+        EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+        const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
+        if (i == 1) {
+            // First time around we should build the message.
+            EXPECT_FALSE(prev_msg);
+            EXPECT_TRUE(curr_msg);
+        } else {
+            // Subsequent passes should reuse the request. We are only
+            // looking to check that we have not replaced the pointer value
+            // with a new pointer.  This tests the on_entry() logic which
+            // clears the request ONLY upon initial entry into the state.
+            EXPECT_TRUE(prev_msg == curr_msg);
+        }
+
+        // Simulate a server corrupt response.
+        name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+        name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run removingRevPtrsHandler again to process the response.
+        EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+        EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
+                      name_remove->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+                      name_remove->getNextEvent());
+        } else {
+            // Server retries should be exhausted, time for a new server.
+            EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
+                      name_remove->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+                      name_remove->getNextEvent());
+        }
+    }
+}
+
+// Tests the processRemoveOkHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is UPDATE_OK_EVT
+// 2. Posted event is invalid
+//
+TEST_F(NameRemoveTransactionTest, processRemoveOkHandler) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+                                    NameChangeTransaction::UPDATE_OK_EVT));
+    // Run processRemoveOkHandler.
+    EXPECT_NO_THROW(name_remove->processRemoveOkHandler());
+
+    // Verify that a server was selected.
+    EXPECT_EQ(dhcp_ddns::ST_COMPLETED, name_remove->getNcrStatus());
+
+    // Verify that the model has ended.
+    EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
+    EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
+
+
+    // Create and prep transaction, poised to run the handler but with an
+    // invalid event.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+                                    StateModel::NOP_EVT));
+    // Running the handler should throw.
+    EXPECT_THROW(name_remove->processRemoveOkHandler(),
+                 NameRemoveTransactionError);
+}
+
+// Tests the processRemoveFailedHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is UPDATE_FAILED_EVT
+// 2. Posted event is invalid
+//
+TEST_F(NameRemoveTransactionTest, processRemoveFailedHandler) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameChangeTransaction::
+                                    PROCESS_TRANS_FAILED_ST,
+                                    NameChangeTransaction::UPDATE_FAILED_EVT));
+    // Run processRemoveFailedHandler.
+    EXPECT_NO_THROW(name_remove->processRemoveFailedHandler());
+
+    // Verify that a server was selected.
+    EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus());
+
+    // Verify that the model has ended. (Remember, the transaction failed NOT
+    // the model.  The model should have ended normally.)
+    EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
+    EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
+
+
+    // Create and prep transaction, poised to run the handler but with an
+    // invalid event.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameChangeTransaction::
+                                    PROCESS_TRANS_FAILED_ST,
+                                    StateModel::NOP_EVT));
+    // Running the handler should throw.
+    EXPECT_THROW(name_remove->processRemoveFailedHandler(),
+                 NameRemoveTransactionError);
+}
+
+// Tests the processRemoveFailedHandler functionality.
+// It verifies behavior for posted event of NO_MORE_SERVERS_EVT.
+TEST_F(NameRemoveTransactionTest, processRemoveFailedHandler_NoMoreServers) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameChangeTransaction::
+                                    PROCESS_TRANS_FAILED_ST,
+                                    NameChangeTransaction::
+                                    NO_MORE_SERVERS_EVT));
+
+    // Run processRemoveFailedHandler.
+    EXPECT_NO_THROW(name_remove->processRemoveFailedHandler());
+
+    // Verify that a server was selected.
+    EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus());
+
+    // Verify that the model has ended. (Remember, the transaction failed NOT
+    // the model.  The model should have ended normally.)
+    EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
+    EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
+}
+
+// Tests the processRemoveFailedHandler functionality.
+// It verifies behavior for posted event of SERVER_IO_ERROR_EVT.
+TEST_F(NameRemoveTransactionTest, processRemoveFailedHandler_ServerIOError) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameChangeTransaction::
+                                    PROCESS_TRANS_FAILED_ST,
+                                    NameChangeTransaction::
+                                    SERVER_IO_ERROR_EVT));
+
+    // Run processRemoveFailedHandler.
+    EXPECT_NO_THROW(name_remove->processRemoveFailedHandler());
+
+    // Verify that a server was selected.
+    EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus());
+
+    // Verify that the model has ended. (Remember, the transaction failed NOT
+    // the model.  The model should have ended normally.)
+    EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
+    EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
+}
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+//  The request includes only a forward change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The send update request fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_sendUpdateException) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FORWARD_CHG));
+
+    name_remove->simulate_send_exception_ = true;
+
+    // Run removingFwdAddrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+    // Completion flags should be false.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // Since IO exceptions should be gracefully handled, any that occur
+    // are unanticipated, and deemed unrecoverable, so the transaction should
+    // be transitioned to failure.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_remove->getNextEvent());
+}
+
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+//  The request includes only a forward change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The send update request fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_SendUpdateException) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_RRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FORWARD_CHG));
+
+    name_remove->simulate_send_exception_ = true;
+
+    // Run removingFwdRRsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+    // Completion flags should be false.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // Since IO exceptions should be gracefully handled, any that occur
+    // are unanticipated, and deemed unrecoverable, so the transaction should
+    // be transitioned to failure.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_remove->getNextEvent());
+}
+
+// Tests removingRevPtrHandler with the following scenario:
+//
+//  The request includes only a reverse change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The send update request fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_SendUpdateException) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_REV_PTRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, REVERSE_CHG));
+
+    name_remove->simulate_send_exception_ = true;
+
+    // Run removingRevPtrsHandler to construct and send the request.
+    EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+    // Completion flags should be false.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // Since IO exceptions should be gracefully handled, any that occur
+    // are unanticipated, and deemed unrecoverable, so the transaction should
+    // be transitioned to failure.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_remove->getNextEvent());
+}
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+//  The request includes only a forward change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The request build fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest,
+       removingFwdAddrsHandler_BuildRequestException) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_ADDRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FORWARD_CHG));
+
+    // Set the one-shot exception simulation flag.
+    name_remove->simulate_build_request_exception_ = true;
+
+    // Run removingFwdAddrsHandler to construct and send the request.
+    // This should fail with a build request throw which should be caught
+    // in the state handler.
+    ASSERT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+    // Verify we did not attempt to send anything.
+    EXPECT_EQ(0, name_remove->getUpdateAttempts());
+
+    // Completion flags should be false.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // Since IO exceptions should be gracefully handled, any that occur
+    // are unanticipated, and deemed unrecoverable, so the transaction should
+    // be transitioned to failure.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_remove->getNextEvent());
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+//  The request includes only a forward change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The request build fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest,
+       removingFwdRRsHandler_BuildRequestException) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_FWD_RRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FORWARD_CHG));
+
+    // Set the one-shot exception simulation flag.
+    name_remove->simulate_build_request_exception_ = true;
+
+    // Run removingFwdRRsHandler to construct and send the request.
+    // This should fail with a build request throw which should be caught
+    // in the state handler.
+    ASSERT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+    // Verify we did not attempt to send anything.
+    EXPECT_EQ(0, name_remove->getUpdateAttempts());
+
+    // Completion flags should be false.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // Since IO exceptions should be gracefully handled, any that occur
+    // are unanticipated, and deemed unrecoverable, so the transaction should
+    // be transitioned to failure.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_remove->getNextEvent());
+}
+
+// Tests removingRevPTRsHandler with the following scenario:
+//
+//  The request includes only a forward change.
+//  Initial posted event is SERVER_SELECTED_EVT.
+//  The request build fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest,
+       removingRevPTRsHandler_BuildRequestException) {
+    NameRemoveStubPtr name_remove;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_remove =
+                    prepHandlerTest(NameRemoveTransaction::
+                                    REMOVING_REV_PTRS_ST,
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FORWARD_CHG));
+
+    // Set the one-shot exception simulation flag.
+    name_remove->simulate_build_request_exception_ = true;
+
+    // Run removingRevPtrsHandler to construct and send the request.
+    // This should fail with a build request throw which should be caught
+    // in the state handler.
+    ASSERT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+    // Verify we did not attempt to send anything.
+    EXPECT_EQ(0, name_remove->getUpdateAttempts());
+
+    // Completion flags should be false.
+    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+    EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+    // Since IO exceptions should be gracefully handled, any that occur
+    // are unanticipated, and deemed unrecoverable, so the transaction should
+    // be transitioned to failure.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_remove->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_remove->getNextEvent());
+}
+
+}
diff --git a/src/bin/d2/tests/nc_test_utils.cc b/src/bin/d2/tests/nc_test_utils.cc
new file mode 100644
index 0000000..bebc06c
--- /dev/null
+++ b/src/bin/d2/tests/nc_test_utils.cc
@@ -0,0 +1,658 @@
+// 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_cfg_mgr.h>
+#include <dns/opcode.h>
+#include <dns/messagerenderer.h>
+#include <nc_test_utils.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+
+namespace isc {
+namespace d2 {
+
+const char* TEST_DNS_SERVER_IP = "127.0.0.1";
+size_t TEST_DNS_SERVER_PORT = 5301;
+
+const bool HAS_RDATA = true;
+const bool NO_RDATA = false;
+
+//*************************** FauxServer class ***********************
+
+FauxServer::FauxServer(asiolink::IOService& io_service,
+                       asiolink::IOAddress& address, size_t port)
+    :io_service_(io_service), address_(address), port_(port),
+     server_socket_() {
+    server_socket_.reset(new asio::ip::udp::socket(io_service_.get_io_service(),
+                                                   asio::ip::udp::v4()));
+    server_socket_->set_option(asio::socket_base::reuse_address(true));
+    server_socket_->bind(asio::ip::udp::endpoint(address_.getAddress(), port_));
+}
+
+FauxServer::FauxServer(asiolink::IOService& io_service,
+                       DnsServerInfo& server)
+    :io_service_(io_service), address_(server.getIpAddress()),
+     port_(server.getPort()), server_socket_() {
+    server_socket_.reset(new asio::ip::udp::socket(io_service_.get_io_service(),
+                                                   asio::ip::udp::v4()));
+    server_socket_->set_option(asio::socket_base::reuse_address(true));
+    server_socket_->bind(asio::ip::udp::endpoint(address_.getAddress(), port_));
+}
+
+
+FauxServer::~FauxServer() {
+}
+
+void
+FauxServer::receive (const ResponseMode& response_mode,
+                     const dns::Rcode& response_rcode) {
+    server_socket_->async_receive_from(asio::buffer(receive_buffer_,
+                                                    sizeof(receive_buffer_)),
+                                       remote_,
+                                       boost::bind(&FauxServer::requestHandler,
+                                                   this, _1, _2,
+                                                   response_mode,
+                                                   response_rcode));
+}
+
+void
+FauxServer::requestHandler(const asio::error_code& error,
+                           std::size_t bytes_recvd,
+                           const ResponseMode& response_mode,
+                           const dns::Rcode& response_rcode) {
+    // If we encountered an error or received no data then fail.
+    // We expect the client to send good requests.
+    if (error.value() != 0 || bytes_recvd < 1) {
+        ADD_FAILURE() << "FauxServer receive failed" << error.message();
+        return;
+    }
+
+    // We have a successfully received data. We need to turn it into
+    // a request in order to build a proper response.
+    // Note D2UpdateMessage is geared towards making requests and
+    // reading responses.  This is the opposite perspective so we have
+    // to a bit of roll-your-own here.
+    dns::Message request(dns::Message::PARSE);
+    util::InputBuffer request_buf(receive_buffer_, bytes_recvd);
+    try {
+        request.fromWire(request_buf);
+    } catch (const std::exception& ex) {
+        // If the request cannot be parsed, then fail the test.
+        // We expect the client to send good requests.
+        ADD_FAILURE() << "FauxServer request is corrupt:" << ex.what();
+        return;
+    }
+
+    // The request parsed ok, so let's build a response.
+    // We must use the QID we received in the response or IOFetch will
+    // toss the response out, resulting in eventual timeout.
+    // We fill in the zone with data we know is from the "server".
+    dns::Message response(dns::Message::RENDER);
+    response.setQid(request.getQid());
+    dns::Question question(dns::Name("response.example.com"),
+                           dns::RRClass::ANY(), dns::RRType::SOA());
+    response.addQuestion(question);
+    response.setOpcode(dns::Opcode(dns::Opcode::UPDATE_CODE));
+    response.setHeaderFlag(dns::Message::HEADERFLAG_QR, true);
+
+    // Set the response Rcode to value passed in, default is NOERROR.
+    response.setRcode(response_rcode);
+
+    // Render the response to a buffer.
+    dns::MessageRenderer renderer;
+    util::OutputBuffer response_buf(TEST_MSG_MAX);
+    renderer.setBuffer(&response_buf);
+    response.toWire(renderer);
+
+    // If mode is to ship garbage, then stomp on part of the rendered
+    // message.
+    if (response_mode == CORRUPT_RESP) {
+        response_buf.writeUint16At(0xFFFF, 2);
+    }
+
+    // Ship the reponse via synchronous send.
+    try {
+        int cnt = server_socket_->send_to(asio::
+                                          buffer(response_buf.getData(),
+                                                 response_buf.getLength()),
+                                          remote_);
+        // Make sure we sent what we expect to send.
+        if (cnt != response_buf.getLength()) {
+            ADD_FAILURE() << "FauxServer sent: " << cnt << " expected: "
+                          << response_buf.getLength();
+        }
+    } catch (const std::exception& ex) {
+        ADD_FAILURE() << "FauxServer send failed: " << ex.what();
+    }
+}
+
+//********************** TransactionTest class ***********************
+
+const unsigned int TransactionTest::FORWARD_CHG = 0x01;
+const unsigned int TransactionTest::REVERSE_CHG = 0x02;
+const unsigned int TransactionTest::FWD_AND_REV_CHG = REVERSE_CHG | FORWARD_CHG;
+
+TransactionTest::TransactionTest()
+    : io_service_(new isc::asiolink::IOService()), ncr_(),
+    timer_(*io_service_), run_time_(0) {
+}
+
+TransactionTest::~TransactionTest() {
+}
+
+void
+TransactionTest::runTimedIO(int run_time) {
+    run_time_ = run_time;
+    timer_.setup(boost::bind(&TransactionTest::timesUp, this), run_time_);
+    io_service_->run();
+}
+
+void
+TransactionTest::timesUp() {
+    io_service_->stop();
+    FAIL() << "Test Time: " << run_time_ << " expired";
+}
+
+void
+TransactionTest::setupForIPv4Transaction(dhcp_ddns::NameChangeType chg_type,
+                                         int change_mask) {
+    const char* msg_str =
+        "{"
+        " \"change_type\" : 0 , "
+        " \"forward_change\" : true , "
+        " \"reverse_change\" : true , "
+        " \"fqdn\" : \"my.forward.example.com.\" , "
+        " \"ip_address\" : \"192.168.2.1\" , "
+        " \"dhcid\" : \"0102030405060708\" , "
+        " \"lease_expires_on\" : \"20130121132405\" , "
+        " \"lease_length\" : 1300 "
+        "}";
+
+    // Create NameChangeRequest from JSON string.
+    ncr_ = dhcp_ddns::NameChangeRequest::fromJSON(msg_str);
+
+    // Set the change type.
+    ncr_->setChangeType(chg_type);
+
+    // If the change mask does not include a forward change clear the
+    // forward domain; otherwise create the domain and its servers.
+    if (!(change_mask & FORWARD_CHG)) {
+        ncr_->setForwardChange(false);
+        forward_domain_.reset();
+    } else {
+        // Create the forward domain and then its servers.
+        forward_domain_ = makeDomain("example.com.");
+        addDomainServer(forward_domain_, "forward.example.com",
+                        "127.0.0.1", 5301);
+        addDomainServer(forward_domain_, "forward2.example.com",
+                        "127.0.0.1", 5302);
+    }
+
+    // If the change mask does not include a reverse change clear the
+    // reverse domain; otherwise create the domain and its servers.
+    if (!(change_mask & REVERSE_CHG)) {
+        ncr_->setReverseChange(false);
+        reverse_domain_.reset();
+    } else {
+        // Create the reverse domain and its server.
+        reverse_domain_ = makeDomain("2.168.192.in.addr.arpa.");
+        addDomainServer(reverse_domain_, "reverse.example.com",
+                        "127.0.0.1", 5301);
+        addDomainServer(reverse_domain_, "reverse2.example.com",
+                        "127.0.0.1", 5302);
+    }
+}
+
+void
+TransactionTest::setupForIPv6Transaction(dhcp_ddns::NameChangeType chg_type,
+                                         int change_mask) {
+    const char* msg_str =
+        "{"
+        " \"change_type\" : 0 , "
+        " \"forward_change\" : true , "
+        " \"reverse_change\" : true , "
+        " \"fqdn\" : \"my6.forward.example.com.\" , "
+        " \"ip_address\" : \"2001:1::100\" , "
+        " \"dhcid\" : \"0102030405060708\" , "
+        " \"lease_expires_on\" : \"20130121132405\" , "
+        " \"lease_length\" : 1300 "
+        "}";
+
+    // Create NameChangeRequest from JSON string.
+    ncr_ = makeNcrFromString(msg_str);
+
+    // Set the change type.
+    ncr_->setChangeType(chg_type);
+
+    // If the change mask does not include a forward change clear the
+    // forward domain; otherwise create the domain and its servers.
+    if (!(change_mask & FORWARD_CHG)) {
+        ncr_->setForwardChange(false);
+        forward_domain_.reset();
+    } else {
+        // Create the forward domain and then its servers.
+        forward_domain_ = makeDomain("example.com.");
+        addDomainServer(forward_domain_, "fwd6-server.example.com",
+                        "::1", 5301);
+        addDomainServer(forward_domain_, "fwd6-server2.example.com",
+                        "::1", 5302);
+    }
+
+    // If the change mask does not include a reverse change clear the
+    // reverse domain; otherwise create the domain and its servers.
+    if (!(change_mask & REVERSE_CHG)) {
+        ncr_->setReverseChange(false);
+        reverse_domain_.reset();
+    } else {
+        // Create the reverse domain and its server.
+        reverse_domain_ = makeDomain("1.2001.ip6.arpa.");
+        addDomainServer(reverse_domain_, "rev6-server.example.com",
+                        "::1", 5301);
+        addDomainServer(reverse_domain_, "rev6-server2.example.com",
+                        "::1", 5302);
+    }
+}
+
+
+//********************** Functions ****************************
+
+void
+checkRRCount(const D2UpdateMessagePtr& request,
+             D2UpdateMessage::UpdateMsgSection section, int count) {
+    dns::RRsetIterator rrset_it = request->beginSection(section);
+    dns::RRsetIterator rrset_end = request->endSection(section);
+
+    ASSERT_EQ(count, std::distance(rrset_it, rrset_end));
+}
+
+void
+checkZone(const D2UpdateMessagePtr& request, const std::string& exp_zone_name) {
+    // Verify the zone section info.
+    D2ZonePtr zone = request->getZone();
+    EXPECT_TRUE(zone);
+    EXPECT_EQ(exp_zone_name, zone->getName().toText());
+    EXPECT_EQ(dns::RRClass::IN().getCode(), zone->getClass().getCode());
+}
+
+void
+checkRR(dns::RRsetPtr rrset, const std::string& exp_name,
+              const dns::RRClass& exp_class, const dns::RRType& exp_type,
+              unsigned int exp_ttl, dhcp_ddns::NameChangeRequestPtr ncr,
+              bool has_rdata) {
+    // Verify the FQDN/DHCID RR fields.
+    EXPECT_EQ(exp_name, rrset->getName().toText());
+    EXPECT_EQ(exp_class.getCode(), rrset->getClass().getCode());
+    EXPECT_EQ(exp_type.getCode(), rrset->getType().getCode());
+    EXPECT_EQ(exp_ttl, rrset->getTTL().getValue());
+    if ((!has_rdata) || 
+       (exp_type == dns::RRType::ANY() || exp_class == dns::RRClass::ANY())) {
+        // ANY types do not have RData
+        ASSERT_EQ(0, rrset->getRdataCount());
+        return;
+    }
+
+    ASSERT_EQ(1, rrset->getRdataCount());
+    dns::RdataIteratorPtr rdata_it = rrset->getRdataIterator();
+    ASSERT_TRUE(rdata_it);
+
+    if ((exp_type == dns::RRType::A()) ||
+        (exp_type == dns::RRType::AAAA())) {
+        // should have lease rdata
+        EXPECT_EQ(ncr->getIpAddress(), rdata_it->getCurrent().toText());
+    } else if (exp_type == dns::RRType::PTR())  {
+        // should have PTR rdata
+        EXPECT_EQ(ncr->getFqdn(), rdata_it->getCurrent().toText());
+    } else if (exp_type == dns::RRType::DHCID()) {
+        // should have DHCID rdata
+        const std::vector<uint8_t>& ncr_dhcid = ncr->getDhcid().getBytes();
+        util::InputBuffer buffer(ncr_dhcid.data(), ncr_dhcid.size());
+        dns::rdata::in::DHCID rdata_ref(buffer, ncr_dhcid.size());
+        EXPECT_EQ(0, rdata_ref.compare(rdata_it->getCurrent()));
+    } else {
+        // we got a problem
+        FAIL();
+    }
+}
+
+dns::RRsetPtr
+getRRFromSection(const D2UpdateMessagePtr& request,
+                 D2UpdateMessage::UpdateMsgSection section, int index) {
+    dns::RRsetIterator rrset_it = request->beginSection(section);
+    dns::RRsetIterator rrset_end = request->endSection(section);
+
+    if (std::distance(rrset_it, rrset_end) <= index) {
+        // Return an empty pointer if index is out of range.
+        return (dns::RRsetPtr());
+    }
+
+    std::advance(rrset_it, index);
+    return (*rrset_it);
+}
+
+dhcp_ddns::NameChangeRequestPtr makeNcrFromString(const std::string& ncr_str) {
+    return (dhcp_ddns::NameChangeRequest::fromJSON(ncr_str));
+}
+
+DdnsDomainPtr makeDomain(const std::string& zone_name,
+                         const std::string& key_name) {
+    DdnsDomainPtr domain;
+    DnsServerInfoStoragePtr servers(new DnsServerInfoStorage());
+    domain.reset(new DdnsDomain(zone_name, key_name, servers));
+    return (domain);
+}
+
+void addDomainServer(DdnsDomainPtr& domain, const std::string& name,
+                     const std::string& ip, const size_t port) {
+    DnsServerInfoPtr server(new DnsServerInfo(name, asiolink::IOAddress(ip),
+                                              port));
+    domain->getServers()->push_back(server);
+}
+
+// Verifies that the contents of the given transaction's  DNS update request
+// is correct for adding a forward DNS entry
+void checkAddFwdAddressRequest(NameChangeTransaction& tran) {
+    const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
+    ASSERT_TRUE(request);
+
+    // Safety check.
+    dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr();
+    ASSERT_TRUE(ncr);
+
+    std::string exp_zone_name = tran.getForwardDomain()->getName();
+    std::string exp_fqdn = ncr->getFqdn();
+    const dns::RRType& exp_ip_rr_type = tran.getAddressRRType();
+
+    // Verify the zone section.
+    checkZone(request, exp_zone_name);
+
+    // Verify the PREREQUISITE SECTION
+    // Should be 1 which tests for FQDN does not exist.
+    dns::RRsetPtr rrset;
+    checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 1);
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_PREREQUISITE, 0));
+    checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), dns::RRType::ANY(), 0, ncr);
+
+    // Verify the UPDATE SECTION
+    // Should be 2 RRs: 1 to add the FQDN/IP and one to add the DHCID RR
+    checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 2);
+
+    // First, Verify the FQDN/IP add RR.
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_UPDATE, 0));
+    checkRR(rrset, exp_fqdn, dns::RRClass::IN(), exp_ip_rr_type, 0, ncr);
+
+    // Now, verify the DHCID add RR.
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_UPDATE, 1));
+    checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(), 0, ncr);
+
+    // Verify there are no RRs in the ADDITIONAL Section.
+    checkRRCount(request, D2UpdateMessage::SECTION_ADDITIONAL, 0);
+
+    // Verify that it will render toWire without throwing.
+    dns::MessageRenderer renderer;
+    ASSERT_NO_THROW(request->toWire(renderer));
+}
+
+// Verifies that the contents of the given transaction's  DNS update request
+// is correct for replacing a forward DNS entry
+void checkReplaceFwdAddressRequest(NameChangeTransaction& tran) {
+    const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
+    ASSERT_TRUE(request);
+
+    // Safety check.
+    dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr();
+    ASSERT_TRUE(ncr);
+
+    std::string exp_zone_name = tran.getForwardDomain()->getName();
+    std::string exp_fqdn = ncr->getFqdn();
+    const dns::RRType& exp_ip_rr_type = tran.getAddressRRType();
+
+    // Verify the zone section.
+    checkZone(request, exp_zone_name);
+
+    // Verify the PREREQUISITE SECTION
+    // Should be 2,  1 which tests for FQDN does exist and the other
+    // checks for a matching DHCID.
+    dns::RRsetPtr rrset;
+    checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 2);
+
+    // Verify the FQDN test RR.
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_PREREQUISITE, 0));
+    checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::ANY(), 0, ncr);
+
+    // Verify the DHCID test RR.
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_PREREQUISITE, 1));
+    checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(), 0, ncr);
+
+    // Verify the UPDATE SECTION
+    // Should be 2,  1 which deletes the existing FQDN/IP and one that
+    // adds the new one.
+    checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 2);
+
+    // Verify the FQDN delete RR.
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_UPDATE, 0));
+    checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), exp_ip_rr_type, 0, ncr);
+
+    // Verify the FQDN/IP add RR.
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_UPDATE, 1));
+    checkRR(rrset, exp_fqdn, dns::RRClass::IN(), exp_ip_rr_type, 0, ncr);
+
+    // Verify there are no RRs in the ADDITIONAL Section.
+    checkRRCount(request, D2UpdateMessage::SECTION_ADDITIONAL, 0);
+
+    // Verify that it will render toWire without throwing.
+    dns::MessageRenderer renderer;
+    ASSERT_NO_THROW(request->toWire(renderer));
+}
+
+// Verifies that the contents of the given transaction's  DNS update request
+// is correct for replacing a reverse DNS entry
+void checkReplaceRevPtrsRequest(NameChangeTransaction& tran) {
+    const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
+    ASSERT_TRUE(request);
+
+    // Safety check.
+    dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr();
+    ASSERT_TRUE(ncr);
+
+    std::string exp_zone_name = tran.getReverseDomain()->getName();
+    std::string exp_rev_addr = D2CfgMgr::reverseIpAddress(ncr->getIpAddress());
+
+    // Verify the zone section.
+    checkZone(request, exp_zone_name);
+
+    // Verify there are no RRs in the PREREQUISITE Section.
+    checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 0);
+
+    // Verify the UPDATE Section.
+    // It should contain 4 RRs:
+    // 1. A delete all PTR RRs for the given IP
+    // 2. A delete of all DHCID RRs for the given IP
+    // 3. An add of the new PTR RR
+    // 4. An add of the new DHCID RR
+    dns::RRsetPtr rrset;
+    checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 4);
+
+    // Verify the PTR delete RR.
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_UPDATE, 0));
+    checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::PTR(),
+            0, ncr);
+
+    // Verify the DHCID delete RR.
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_UPDATE, 1));
+    checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::DHCID(),
+            0, ncr);
+
+    // Verify the PTR add RR.
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_UPDATE, 2));
+    checkRR(rrset, exp_rev_addr, dns::RRClass::IN(), dns::RRType::PTR(),
+            0, ncr);
+
+    // Verify the DHCID add RR.
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_UPDATE, 3));
+    checkRR(rrset, exp_rev_addr, dns::RRClass::IN(), dns::RRType::DHCID(),
+            0, ncr);
+
+    // Verify there are no RRs in the ADDITIONAL Section.
+    checkRRCount(request, D2UpdateMessage::SECTION_ADDITIONAL, 0);
+
+    // Verify that it will render toWire without throwing.
+    dns::MessageRenderer renderer;
+    ASSERT_NO_THROW(request->toWire(renderer));
+}
+
+void checkRemoveFwdAddressRequest(NameChangeTransaction& tran) {
+    const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
+    ASSERT_TRUE(request);
+
+    // Safety check.
+    dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr();
+    ASSERT_TRUE(ncr);
+
+    std::string exp_zone_name = tran.getForwardDomain()->getName();
+    std::string exp_fqdn = ncr->getFqdn();
+
+    // Verify the zone section.
+    checkZone(request, exp_zone_name);
+
+    // Verify there is 1 RR in the PREREQUISITE Section.
+    checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 1);
+
+    // Verify the DHCID matching assertion RR.
+    dns::RRsetPtr rrset;
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_PREREQUISITE, 0));
+    checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(),
+            0, ncr);
+
+    // Verify there is 1 RR in the UPDATE Section.
+    checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 1);
+
+    // Verify the FQDN/IP delete RR.
+    const dns::RRType& exp_ip_rr_type = tran.getAddressRRType();
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_UPDATE, 0));
+    checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), exp_ip_rr_type,
+            0, ncr);
+
+    // Verify that it will render toWire without throwing.
+    dns::MessageRenderer renderer;
+    ASSERT_NO_THROW(request->toWire(renderer));
+}
+
+void checkRemoveFwdRRsRequest(NameChangeTransaction& tran) {
+    const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
+    ASSERT_TRUE(request);
+
+    // Safety check.
+    dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr();
+    ASSERT_TRUE(ncr);
+
+    std::string exp_zone_name = tran.getForwardDomain()->getName();
+    std::string exp_fqdn = ncr->getFqdn();
+
+    // Verify the zone section.
+    checkZone(request, exp_zone_name);
+
+    // Verify there is 1 RR in the PREREQUISITE Section.
+    checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 3);
+
+    // Verify the DHCID matches assertion.
+    dns::RRsetPtr rrset;
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_PREREQUISITE, 0));
+    checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(),
+            0, ncr);
+
+    // Verify the NO A RRs assertion.
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_PREREQUISITE, 1));
+    checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), dns::RRType::A(),
+            0, ncr, NO_RDATA);
+
+    // Verify the NO AAAA RRs assertion.
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_PREREQUISITE, 2));
+    checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), dns::RRType::AAAA(),
+            0, ncr, NO_RDATA);
+
+    // Verify there is 1 RR in the UPDATE Section.
+    checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 1);
+
+    // Verify the delete all for the FQDN RR.
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_UPDATE, 0));
+    checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::ANY(),
+            0, ncr);
+
+    // Verify that it will render toWire without throwing.
+    dns::MessageRenderer renderer;
+    ASSERT_NO_THROW(request->toWire(renderer));
+}
+
+void checkRemoveRevPtrsRequest(NameChangeTransaction& tran) {
+    const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
+    ASSERT_TRUE(request);
+
+    // Safety check.
+    dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr();
+    ASSERT_TRUE(ncr);
+
+    std::string exp_zone_name = tran.getReverseDomain()->getName();
+    std::string exp_rev_addr = D2CfgMgr::reverseIpAddress(ncr->getIpAddress());
+
+    // Verify the zone section.
+    checkZone(request, exp_zone_name);
+
+    // Verify there is 1 RR in the PREREQUISITE Section.
+    checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 1);
+
+    // Verify the FQDN-PTRNAME assertion RR.
+    dns::RRsetPtr rrset;
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_PREREQUISITE, 0));
+    checkRR(rrset, exp_rev_addr, dns::RRClass::IN(), dns::RRType::PTR(),
+            0, ncr);
+
+    // Verify there is 1 RR in the UPDATE Section.
+    checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 1);
+
+    // Verify the delete all for the FQDN RR.
+    ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+                                                  SECTION_UPDATE, 0));
+    checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::ANY(),
+            0, ncr);
+
+    // Verify that it will render toWire without throwing.
+    dns::MessageRenderer renderer;
+    ASSERT_NO_THROW(request->toWire(renderer));
+}
+
+
+}; // namespace isc::d2
+}; // namespace isc
diff --git a/src/bin/d2/tests/nc_test_utils.h b/src/bin/d2/tests/nc_test_utils.h
new file mode 100644
index 0000000..9b96b2e
--- /dev/null
+++ b/src/bin/d2/tests/nc_test_utils.h
@@ -0,0 +1,314 @@
+// 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_TEST_UTILS_H
+#define NC_TEST_UTILS_H
+
+/// @file nc_test_utils.h prototypes for functions related transaction testing.
+
+#include <d2/nc_trans.h>
+
+#include <asio/ip/udp.hpp>
+#include <asio/socket_base.hpp>
+#include <gtest/gtest.h>
+
+namespace isc {
+namespace d2 {
+
+extern const char* TEST_DNS_SERVER_IP;
+extern size_t TEST_DNS_SERVER_PORT;
+
+// Not extern'ed to allow use as array size
+const int TEST_MSG_MAX = 1024;
+
+typedef boost::shared_ptr<asio::ip::udp::socket> SocketPtr;
+
+/// @brief This class simulates a DNS server.  It is capable of performing
+/// an asynchronous read, governed by an IOService, and responding to received
+/// requests in a given manner.
+class FauxServer {
+public:
+    enum  ResponseMode {
+        USE_RCODE,   // Generate a response with a given RCODE
+        CORRUPT_RESP  // Generate a corrupt response
+    };
+
+    asiolink::IOService& io_service_;
+    const asiolink::IOAddress& address_;
+    size_t port_;
+    SocketPtr server_socket_;
+    asio::ip::udp::endpoint remote_;
+    uint8_t receive_buffer_[TEST_MSG_MAX];
+
+    /// @brief Constructor
+    ///
+    /// @param io_service IOService to be used for socket IO.
+    /// @param address  IP address at which the server should listen.
+    /// @param port Port number at which the server should listen.
+    FauxServer(asiolink::IOService& io_service, asiolink::IOAddress& address,
+               size_t port);
+
+    /// @brief Constructor
+    ///
+    /// @param io_service IOService to be used for socket IO.
+    /// @param server DnServerInfo of server the DNS server. This supplies the
+    /// server's ip address and port.
+    FauxServer(asiolink::IOService& io_service, DnsServerInfo& server);
+
+    /// @brief Destructor
+    virtual ~FauxServer();
+
+    /// @brief Initiates an asynchronous receive
+    ///
+    /// Starts the server listening for requests.  Upon completion of the
+    /// listen, the callback method, requestHandler, is invoked.
+    ///
+    /// @param response_mode Selects how the server responds to a request
+    /// @param response_rcode The Rcode value set in the response. Not used
+    /// for all modes.
+    void receive (const ResponseMode& response_mode,
+                  const dns::Rcode& response_rcode=dns::Rcode::NOERROR());
+
+    /// @brief Socket IO Completion callback
+    ///
+    /// This method servers as the Server's UDP socket receive callback handler.
+    /// When the receive completes the handler is invoked with the
+    /// @param error result code of the receive (determined by asio layer)
+    /// @param bytes_recvd number of bytes received, if any
+    /// @param response_mode type of response the handler should produce
+    /// @param response_rcode value of Rcode in the response constructed by
+    /// handler
+    void requestHandler(const asio::error_code& error,
+                        std::size_t bytes_recvd,
+                        const ResponseMode& response_mode,
+                        const dns::Rcode& response_rcode);
+};
+
+/// @brief Base class Test fixture for testing transactions.
+class TransactionTest : public ::testing::Test {
+public:
+    IOServicePtr io_service_;
+    dhcp_ddns::NameChangeRequestPtr ncr_;
+    DdnsDomainPtr forward_domain_;
+    DdnsDomainPtr reverse_domain_;
+    asiolink::IntervalTimer timer_;
+    int run_time_;
+
+    /// #brief constants used to specify change directions for a transaction.
+    static const unsigned int FORWARD_CHG;      // Only forward change.
+    static const unsigned int REVERSE_CHG;      // Only reverse change.
+    static const unsigned int FWD_AND_REV_CHG;  // Both forward and reverse.
+
+    TransactionTest();
+    virtual ~TransactionTest();
+
+    /// @brief Run the IO service for no more than a given amount of time.
+    ///
+    /// Uses an IntervalTimer to interrupt the invocation of IOService run(),
+    /// after the given number of milliseconds elapse.  The timer executes
+    /// the timesUp() method if it expires.
+    ///
+    /// @param run_time amount of time in milliseconds to allow run to execute.
+    void runTimedIO(int run_time);
+
+    /// @brief IO Timer expiration handler
+    ///
+    /// Stops the IOSerivce and fails the current test.
+    virtual void timesUp();
+
+    /// @brief Creates a transaction which requests an IPv4 DNS update.
+    ///
+    /// The transaction is constructed around a predefined (i.e. "canned")
+    /// IPv4 NameChangeRequest. The request has both forward and reverse DNS
+    /// changes requested.  Based upon the change mask, the transaction
+    /// will have either the forward, reverse, or both domains populated.
+    ///
+    /// @param change_type selects the type of change requested, CHG_ADD or
+    /// CHG_REMOVE.
+    /// @param change_mask determines which change directions are requested
+    /// FORWARD_CHG, REVERSE_CHG, or FWD_AND_REV_CHG.
+    void setupForIPv4Transaction(dhcp_ddns::NameChangeType change_type,
+                                 int change_mask);
+
+    /// @brief Creates a transaction which requests an IPv6 DNS update.
+    ///
+    /// The transaction is constructed around a predefined (i.e. "canned")
+    /// IPv6 NameChangeRequest. The request has both forward and reverse DNS
+    /// changes requested.  Based upon the change mask, the transaction
+    /// will have either the forward, reverse, or both domains populated.
+    ///
+    /// @param change_type selects the type of change requested, CHG_ADD or
+    /// CHG_REMOVE.
+    /// @param change_mask determines which change directions are requested
+    /// FORWARD_CHG, REVERSE_CHG, or FWD_AND_REV_CHG.
+    void setupForIPv6Transaction(dhcp_ddns::NameChangeType change_type,
+                                 int change_mask);
+};
+
+
+/// @brief Tests the number of RRs in a request section against a given count.
+///
+/// This function actually returns the number of RRsetPtrs in a section. Since
+/// D2 only uses RRsets with a single RData in each (i.e. 1 RR), it is used
+/// as the number of RRs.  The dns::Message::getRRCount() cannot be used for
+/// this as it returns the number of RDatas in an RRSet which does NOT equate
+/// to the number of RRs. RRs with no RData, those with class or type of ANY,
+/// are not counted.
+///
+/// @param request DNS update request to test
+/// @param section enum value of the section to count
+/// @param count the expected number of RRs
+extern void checkRRCount(const D2UpdateMessagePtr& request,
+                         D2UpdateMessage::UpdateMsgSection section, int count);
+
+/// @brief Tests the zone content of a given request.
+///
+/// @param request DNS update request to validate
+/// @param exp_zone_name expected value of the zone name in the zone section
+extern void checkZone(const D2UpdateMessagePtr& request,
+                      const std::string& exp_zone_name);
+
+/// @brief Tests the contents of an RRset
+///
+/// @param rrset Pointer the RRset to test
+/// @param exp_name expected value of RRset name (FQDN or reverse ip)
+/// @param exp_class expected RRClass value of RRset
+/// @param exp_typ expected RRType value of RRset
+/// @param exp_ttl  expected TTL value of RRset
+/// @param ncr NameChangeRequest on which the RRset is based
+/// @param has_rdata if true, RRset's rdata will be checked based on it's
+/// RRType.  Set this to false if the RRset's type supports Rdata but it does
+/// not contain it.  For instance, prerequisites of type NONE have no Rdata
+/// where udpates of type NONE may.
+extern void checkRR(dns::RRsetPtr rrset, const std::string& exp_name,
+                    const dns::RRClass& exp_class, const dns::RRType& exp_type,
+                    unsigned int exp_ttl, dhcp_ddns::NameChangeRequestPtr ncr,
+                    bool has_rdata=true);
+
+/// @brief Fetches an RR(set) from a given section of a request
+///
+/// @param request DNS update request from which the RR should come
+/// @param section enum value of the section from which the RR should come
+/// @param index zero-based index of the RR of interest.
+///
+/// @return Pointer to the RR of interest, empty pointer if the index is out
+/// of range.
+extern dns::RRsetPtr getRRFromSection(const D2UpdateMessagePtr& request,
+                                      D2UpdateMessage::UpdateMsgSection section,
+                                      int index);
+/// @brief Creates a NameChangeRequest from a JSON string
+///
+/// @param ncr_str JSON string form of a NameChangeRequest.  Example:
+/// @code
+///        const char* msg_str =
+///            "{"
+///            " \"change_type\" : 0 , "
+///            " \"forward_change\" : true , "
+///            " \"reverse_change\" : true , "
+///            " \"fqdn\" : \"my.example.com.\" , "
+///            " \"ip_address\" : \"192.168.2.1\" , "
+///            " \"dhcid\" : \"0102030405060708\" , "
+///            " \"lease_expires_on\" : \"20130121132405\" , "
+///            " \"lease_length\" : 1300 "
+///            "}";
+///
+/// @endcode
+
+/// @brief Verifies a forward mapping addition DNS update request
+///
+/// Tests that the DNS Update request for a given transaction, is correct for
+/// adding a forward DNS mapping.
+///
+/// @param tran Transaction containing the request to be verified.
+extern void checkAddFwdAddressRequest(NameChangeTransaction& tran);
+
+/// @brief Verifies a forward mapping replacement DNS update request
+///
+/// Tests that the DNS Update request for a given transaction, is correct for
+/// replacing a forward DNS mapping.
+///
+/// @param tran Transaction containing the request to be verified.
+extern void checkReplaceFwdAddressRequest(NameChangeTransaction& tran);
+
+/// @brief Verifies a reverse mapping replacement DNS update request
+///
+/// Tests that the DNS Update request for a given transaction, is correct for
+/// replacing a reverse DNS mapping.
+///
+/// @param tran Transaction containing the request to be verified.
+extern void checkReplaceRevPtrsRequest(NameChangeTransaction& tran);
+
+/// @brief Verifies a forward address removal DNS update request
+///
+/// Tests that the DNS Update request for a given transaction, is correct for
+/// removing the forward address DNS entry.
+///
+/// @param tran Transaction containing the request to be verified.
+extern void checkRemoveFwdAddressRequest(NameChangeTransaction& tran);
+
+/// @brief Verifies a forward RR removal DNS update request
+///
+/// Tests that the DNS Update request for a given transaction, is correct for
+/// removing forward RR DNS entries.
+///
+/// @param tran Transaction containing the request to be verified.
+extern void checkRemoveFwdRRsRequest(NameChangeTransaction& tran);
+
+/// @brief Verifies a reverse mapping removal DNS update request
+///
+/// Tests that the DNS Update request for a given transaction, is correct for
+/// removing a reverse DNS mapping.
+///
+/// @param tran Transaction containing the request to be verified.
+extern void checkRemoveRevPtrsRequest(NameChangeTransaction& tran);
+
+
+/// @brief Creates a NameChangeRequest from JSON string.
+///
+/// @param ncr_str string of JSON text from which to make the request.
+///
+/// @return Pointer to newly created request.
+///
+/// @throw Underlying methods may throw.
+extern
+dhcp_ddns::NameChangeRequestPtr makeNcrFromString(const std::string& ncr_str);
+
+/// @brief Creates a DdnsDomain with the one server.
+///
+/// @param zone_name zone name of the domain
+/// @param key_name TSIG key name of the TSIG key for this domain
+///
+/// @throw Underlying methods may throw.
+extern DdnsDomainPtr makeDomain(const std::string& zone_name,
+                                const std::string& key_name = "");
+
+/// @brief Creates a DnsServerInfo and adds it to the given DdnsDomain.
+///
+/// The server is created and added to the domain, without duplicate entry
+/// checking.
+///
+/// @param domain DdnsDomain to which to add the server
+/// @param name new server's host name of the server
+/// @param ip new server's ip address
+/// @param port new server's port
+///
+/// @throw Underlying methods may throw.
+extern void addDomainServer(DdnsDomainPtr& domain, const std::string& name,
+                            const std::string& ip = TEST_DNS_SERVER_IP,
+                            const size_t port = TEST_DNS_SERVER_PORT);
+
+}; // namespace isc::d2
+}; // namespace isc
+
+#endif
diff --git a/src/bin/d2/tests/nc_trans_unittests.cc b/src/bin/d2/tests/nc_trans_unittests.cc
index 31785a4..b235ef1 100644
--- a/src/bin/d2/tests/nc_trans_unittests.cc
+++ b/src/bin/d2/tests/nc_trans_unittests.cc
@@ -12,7 +12,19 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <asiolink/interval_timer.h>
 #include <d2/nc_trans.h>
+#include <dns/opcode.h>
+#include <dns/messagerenderer.h>
+#include <log/logger_support.h>
+#include <log/macros.h>
+#include <util/buffer.h>
+#include <nc_test_utils.h>
+
+#include <asio/ip/udp.hpp>
+#include <asio/socket_base.hpp>
+#include <asio.hpp>
+#include <asio/error_code.hpp>
 
 #include <boost/function.hpp>
 #include <boost/bind.hpp>
@@ -40,22 +52,58 @@ public:
     // NameChangeStub events
     static const int SEND_UPDATE_EVT = NCT_DERIVED_EVENT_MIN + 2;
 
+    bool use_stub_callback_;
+
     /// @brief Constructor
     ///
     /// Parameters match those needed by NameChangeTransaction.
-    NameChangeStub(isc::asiolink::IOService& io_service,
+    NameChangeStub(IOServicePtr& io_service,
                    dhcp_ddns::NameChangeRequestPtr& ncr,
                    DdnsDomainPtr forward_domain,
                    DdnsDomainPtr reverse_domain)
         : NameChangeTransaction(io_service, ncr, forward_domain,
-                                reverse_domain) {
+                                reverse_domain),
+                                use_stub_callback_(false) {
     }
 
     /// @brief Destructor
     virtual ~NameChangeStub() {
     }
 
-    /// @brief Empty handler used to statisfy map verification.
+    /// @brief DNSClient callback
+    /// Overrides the callback in NameChangeTranscation to allow testing
+    /// sendUpdate without incorporating exectution of the state model
+    /// into the test.
+    /// It sets the DNS status update and posts IO_COMPLETED_EVT as does
+    /// the normal callback, but rather than invoking runModel it stops
+    /// the IO service.  This allows tests to be constructed that consisted
+    /// of generating a DNS request and invoking sendUpdate to post it and
+    /// wait for response.
+    virtual void operator()(DNSClient::Status status) {
+        if (use_stub_callback_) {
+            setDnsUpdateStatus(status);
+            postNextEvent(IO_COMPLETED_EVT);
+            getIOService()->stop();
+        } else {
+            // For tests which need to use the real callback.
+            NameChangeTransaction::operator()(status);
+        }
+    }
+
+    /// @brief Some tests require a server to be selected.  This method
+    /// selects the first server in the forward domain without involving
+    /// state model execution to do so.
+    bool selectFwdServer() {
+        if (getForwardDomain()) {
+            initServerSelection(getForwardDomain());
+            selectNextServer();
+            return (getCurrentServer());
+        }
+
+        return (false);
+    }
+
+    /// @brief Empty handler used to satisfy map verification.
     void dummyHandler() {
         isc_throw(NameChangeTransactionError,
                   "dummyHandler - invalid event: " << getContextStr());
@@ -182,25 +230,44 @@ public:
         // Invoke the base call implementation first.
         NameChangeTransaction::verifyStates();
 
-        // Define our states.
+        // Check our states.
         getState(DOING_UPDATE_ST);
     }
 
     // Expose the protected methods to be tested.
     using StateModel::runModel;
+    using StateModel::postNextEvent;
+    using StateModel::setState;
+    using StateModel::initDictionaries;
     using NameChangeTransaction::initServerSelection;
     using NameChangeTransaction::selectNextServer;
     using NameChangeTransaction::getCurrentServer;
     using NameChangeTransaction::getDNSClient;
     using NameChangeTransaction::setNcrStatus;
+    using NameChangeTransaction::setDnsUpdateRequest;
+    using NameChangeTransaction::clearDnsUpdateRequest;
     using NameChangeTransaction::setDnsUpdateStatus;
     using NameChangeTransaction::getDnsUpdateResponse;
+    using NameChangeTransaction::setDnsUpdateResponse;
+    using NameChangeTransaction::clearDnsUpdateResponse;
     using NameChangeTransaction::getForwardChangeCompleted;
     using NameChangeTransaction::getReverseChangeCompleted;
     using NameChangeTransaction::setForwardChangeCompleted;
     using NameChangeTransaction::setReverseChangeCompleted;
+    using NameChangeTransaction::setUpdateAttempts;
+    using NameChangeTransaction::transition;
+    using NameChangeTransaction::retryTransition;
+    using NameChangeTransaction::sendUpdate;
+    using NameChangeTransaction::prepNewRequest;
+    using NameChangeTransaction::addLeaseAddressRdata;
+    using NameChangeTransaction::addDhcidRdata;
+    using NameChangeTransaction::addPtrRdata;
 };
 
+// Declare them so Gtest can see them.
+const int NameChangeStub::DOING_UPDATE_ST;
+const int NameChangeStub::SEND_UPDATE_EVT;
+
 /// @brief Defines a pointer to a NameChangeStubPtr instance.
 typedef boost::shared_ptr<NameChangeStub> NameChangeStubPtr;
 
@@ -210,50 +277,79 @@ typedef boost::shared_ptr<NameChangeStub> NameChangeStubPtr;
 /// aspects of NameChangeTransaction.
 class NameChangeTransactionTest : public ::testing::Test {
 public:
-    isc::asiolink::IOService io_service_;
+    IOServicePtr io_service_;
     DdnsDomainPtr forward_domain_;
     DdnsDomainPtr reverse_domain_;
+    asiolink::IntervalTimer timer_;
+    int run_time_;
+
+    NameChangeTransactionTest()
+        : io_service_(new isc::asiolink::IOService()), timer_(*io_service_),
+         run_time_(0) {
+    }
 
     virtual ~NameChangeTransactionTest() {
     }
 
-    /// @brief Instantiates a NameChangeStub built around a canned
-    /// NameChangeRequest.
+    /// @brief Run the IO service for no more than a given amount of time.
+    ///
+    /// Uses an IntervalTimer to interrupt the invocation of IOService run(),
+    /// after the given number of milliseconds elapse.  The timer executes
+    /// the timesUp() method if it expires.
+    ///
+    /// @param run_time amount of time in milliseconds to allow run to execute.
+    void runTimedIO(int run_time) {
+        run_time_ = run_time;
+        timer_.setup(boost::bind(&NameChangeTransactionTest::timesUp, this),
+                     run_time_);
+        io_service_->run();
+    }
+
+    /// @brief IO Timer expiration handler
+    ///
+    /// Stops the IOSerivce and fails the current test.
+    void timesUp() {
+        io_service_->stop();
+        FAIL() << "Test Time: " << run_time_ << " expired";
+    }
+
+    /// @brief  Instantiates a NameChangeStub test transaction
+    /// The transaction is constructed around a predefined (i.e "canned")
+    /// NameChangeRequest. The request has both forward and reverse DNS
+    /// changes requested, and both forward and reverse domains are populated.
     NameChangeStubPtr makeCannedTransaction() {
+        // NCR in JSON form.
         const char* msg_str =
             "{"
             " \"change_type\" : 0 , "
             " \"forward_change\" : true , "
             " \"reverse_change\" : true , "
-            " \"fqdn\" : \"example.com.\" , "
+            " \"fqdn\" : \"my.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;
+        // Create the request from JSON.
+        dhcp_ddns::NameChangeRequestPtr ncr = dhcp_ddns::NameChangeRequest::
+                                              fromJSON(msg_str);
 
-        ncr = dhcp_ddns::NameChangeRequest::fromJSON(msg_str);
+        // Make forward DdnsDomain with 2 forward servers.
+        forward_domain_ = makeDomain("example.com.");
+        addDomainServer(forward_domain_, "forward.example.com",
+                        "127.0.0.1", 5301);
+        addDomainServer(forward_domain_, "forward2.example.com",
+                        "127.0.0.1", 5302);
 
-        // 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 DdnsDomain with one reverse server.
+        reverse_domain_ = makeDomain("2.168.192.in.addr.arpa.");
+        addDomainServer(reverse_domain_, "reverse.example.com",
+                        "127.0.0.1", 5301);
 
-        // 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));
+        // Instantiate the transaction as would be done by update manager.
         return (NameChangeStubPtr(new NameChangeStub(io_service_, ncr,
                                   forward_domain_, reverse_domain_)));
-
     }
 
 };
@@ -267,7 +363,7 @@ public:
 /// requires reverse change.
 /// 4. Valid construction functions properly
 TEST(NameChangeTransaction, construction) {
-    isc::asiolink::IOService io_service;
+    IOServicePtr io_service(new isc::asiolink::IOService());
 
     const char* msg_str =
         "{"
@@ -293,6 +389,13 @@ TEST(NameChangeTransaction, construction) {
     ASSERT_NO_THROW(forward_domain.reset(new DdnsDomain("*", "", servers)));
     ASSERT_NO_THROW(reverse_domain.reset(new DdnsDomain("*", "", servers)));
 
+    // Verify that construction with a null IOServicePtr fails.
+    // @todo Subject to change if multi-threading is implemented.
+    IOServicePtr empty;
+    EXPECT_THROW(NameChangeTransaction(empty, ncr,
+                                       forward_domain, reverse_domain),
+                                       NameChangeTransactionError);
+
     // Verify that construction with an empty NameChangeRequest throws.
     EXPECT_THROW(NameChangeTransaction(io_service, empty_ncr,
                                        forward_domain, reverse_domain),
@@ -363,9 +466,6 @@ TEST_F(NameChangeTransactionTest, accessors) {
     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());
@@ -375,6 +475,59 @@ TEST_F(NameChangeTransactionTest, accessors) {
     EXPECT_TRUE(name_change->getReverseChangeCompleted());
 }
 
+/// @brief Tests DNS update request accessor methods.
+TEST_F(NameChangeTransactionTest, dnsUpdateRequestAccessors) {
+    // Create a transaction.
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+    // Post transaction construction, there should not be an update request.
+    EXPECT_FALSE(name_change->getDnsUpdateRequest());
+
+    // Create a request.
+    D2UpdateMessagePtr req;
+    ASSERT_NO_THROW(req.reset(new D2UpdateMessage(D2UpdateMessage::OUTBOUND)));
+
+    // Use the setter and then verify we can fetch the request.
+    ASSERT_NO_THROW(name_change->setDnsUpdateRequest(req));
+
+    // Post set, we should be able to fetch it.
+    ASSERT_TRUE(name_change->getDnsUpdateRequest());
+
+    // Should be able to clear it.
+    ASSERT_NO_THROW(name_change->clearDnsUpdateRequest());
+
+    // Should be empty again.
+    EXPECT_FALSE(name_change->getDnsUpdateRequest());
+}
+
+/// @brief Tests DNS update request accessor methods.
+TEST_F(NameChangeTransactionTest, dnsUpdateResponseAccessors) {
+    // Create a transaction.
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+    // Post transaction construction, there should not be an update response.
+    EXPECT_FALSE(name_change->getDnsUpdateResponse());
+
+    // Create a response.
+    D2UpdateMessagePtr resp;
+    ASSERT_NO_THROW(resp.reset(new D2UpdateMessage(D2UpdateMessage::INBOUND)));
+
+    // Use the setter and then verify we can fetch the response.
+    ASSERT_NO_THROW(name_change->setDnsUpdateResponse(resp));
+
+    // Post set, we should be able to fetch it.
+    EXPECT_TRUE(name_change->getDnsUpdateResponse());
+
+    // Should be able to clear it.
+    ASSERT_NO_THROW(name_change->clearDnsUpdateResponse());
+
+    // Should be empty again.
+    EXPECT_FALSE(name_change->getDnsUpdateResponse());
+}
+
+
 /// @brief Tests event and state dictionary construction and verification.
 TEST_F(NameChangeTransactionTest, dictionaryCheck) {
     NameChangeStubPtr name_change;
@@ -527,9 +680,9 @@ TEST_F(NameChangeTransactionTest, modelFailure) {
     EXPECT_EQ(dhcp_ddns::ST_FAILED, name_change->getNcrStatus());
 }
 
-/// @brief Tests the ability to use startTransaction to initate the state
+/// @brief Tests the ability to use startTransaction to initiate the state
 /// model execution, and DNSClient callback, operator(), to resume the
-/// the model with a update successful outcome.
+/// model with a update successful outcome.
 TEST_F(NameChangeTransactionTest, successfulUpdateTest) {
     NameChangeStubPtr name_change;
     ASSERT_NO_THROW(name_change = makeCannedTransaction());
@@ -565,7 +718,7 @@ TEST_F(NameChangeTransactionTest, successfulUpdateTest) {
 
 /// @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.
+/// model with a update failure outcome.
 TEST_F(NameChangeTransactionTest, failedUpdateTest) {
     NameChangeStubPtr name_change;
     ASSERT_NO_THROW(name_change = makeCannedTransaction());
@@ -596,4 +749,307 @@ TEST_F(NameChangeTransactionTest, failedUpdateTest) {
     EXPECT_FALSE(name_change->getForwardChangeCompleted());
 }
 
+/// @brief Tests update attempt accessors.
+TEST_F(NameChangeTransactionTest, updateAttempts) {
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+    // Post transaction construction, update attempts should be 0.
+    EXPECT_EQ(0, name_change->getUpdateAttempts());
+
+    // Set it to a known value.
+    name_change->setUpdateAttempts(5);
+
+    // Verify that the value is as expected.
+    EXPECT_EQ(5, name_change->getUpdateAttempts());
+}
+
+/// @brief Tests retryTransition method
+///
+/// Verifies that while the maximum number of update attempts has not
+/// been exceeded, the method will leave the state unchanged but post a
+/// SERVER_SELECTED_EVT.  Once the maximum is exceeded, the method should
+/// transition to the state given with a next event of SERVER_IO_ERROR_EVT.
+TEST_F(NameChangeTransactionTest, retryTransition) {
+    // Create the transaction.
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+    // Define dictionaries.
+    ASSERT_NO_THROW(name_change->initDictionaries());
+
+    // Transition to a known spot.
+    ASSERT_NO_THROW(name_change->transition(
+                    NameChangeStub::DOING_UPDATE_ST,
+                    NameChangeStub::SEND_UPDATE_EVT));
+
+    // Verify we are at the known spot.
+    ASSERT_EQ(NameChangeStub::DOING_UPDATE_ST,
+              name_change->getCurrState());
+    ASSERT_EQ(NameChangeStub::SEND_UPDATE_EVT,
+              name_change->getNextEvent());
+
+    // Verify that we have not exceeded maximum number of attempts.
+    ASSERT_LT(name_change->getUpdateAttempts(),
+              NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER);
+
+    // Call retryTransition.
+    ASSERT_NO_THROW(name_change->retryTransition(
+                    NameChangeTransaction::PROCESS_TRANS_FAILED_ST));
+
+    // Since the number of update attempts is less than the maximum allowed
+    // we should remain in our current state but with next event of
+    // SERVER_SELECTED_EVT posted.
+    ASSERT_EQ(NameChangeStub::DOING_UPDATE_ST,
+              name_change->getCurrState());
+    ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+              name_change->getNextEvent());
+
+    // Now set the number of attempts to the maximum.
+    name_change->setUpdateAttempts(NameChangeTransaction::
+                                   MAX_UPDATE_TRIES_PER_SERVER);
+    // Call retryTransition.
+    ASSERT_NO_THROW(name_change->retryTransition(
+                    NameChangeTransaction::PROCESS_TRANS_FAILED_ST));
+
+    // Since we have exceeded maximum attempts, we should transition to
+    // PROCESS_UPDATE_FAILED_ST with a next event of SERVER_IO_ERROR_EVT.
+    ASSERT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_change->getCurrState());
+    ASSERT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+              name_change->getNextEvent());
 }
+
+/// @brief Tests sendUpdate method when underlying doUpdate throws.
+///
+/// DNSClient::doUpdate can throw for a variety of reasons. This tests
+/// sendUpdate handling of such a throw by passing doUpdate a request
+/// that will not render.
+TEST_F(NameChangeTransactionTest, sendUpdateDoUpdateFailure) {
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+    ASSERT_NO_THROW(name_change->initDictionaries());
+    ASSERT_TRUE(name_change->selectFwdServer());
+
+    // Set the transaction's request to an empty DNS update.
+    D2UpdateMessagePtr req;
+    ASSERT_NO_THROW(req.reset(new D2UpdateMessage(D2UpdateMessage::OUTBOUND)));
+    ASSERT_NO_THROW(name_change->setDnsUpdateRequest(req));
+
+    // Verify that sendUpdate does not throw, but it should fail because
+    // the request won't render.
+    ASSERT_NO_THROW(name_change->sendUpdate());
+
+    // Verify that we transition to failed state and event.
+    ASSERT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+              name_change->getCurrState());
+    ASSERT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+              name_change->getNextEvent());
+}
+
+/// @brief Tests sendUpdate method when underlying doUpdate times out.
+TEST_F(NameChangeTransactionTest, sendUpdateTimeout) {
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+    ASSERT_NO_THROW(name_change->initDictionaries());
+    ASSERT_TRUE(name_change->selectFwdServer());
+
+    // Create a valid request.
+    D2UpdateMessagePtr req;
+    ASSERT_NO_THROW(req.reset(new D2UpdateMessage(D2UpdateMessage::OUTBOUND)));
+    ASSERT_NO_THROW(name_change->setDnsUpdateRequest(req));
+    req->setZone(dns::Name("request.example.com"), dns::RRClass::ANY());
+    req->setRcode(dns::Rcode(dns::Rcode::NOERROR_CODE));
+
+    // Set the flag to use the NameChangeStub's DNSClient callback.
+    name_change->use_stub_callback_ = true;
+
+    // Invoke sendUpdate.
+    ASSERT_NO_THROW(name_change->sendUpdate());
+
+    // Update attempt count should be 1, next event should be NOP_EVT.
+    EXPECT_EQ(1, name_change->getUpdateAttempts());
+    ASSERT_EQ(NameChangeTransaction::NOP_EVT,
+              name_change->getNextEvent());
+
+    // Run IO a bit longer than maximum allowed to permit timeout logic to
+    // execute.
+    runTimedIO(NameChangeTransaction::DNS_UPDATE_DEFAULT_TIMEOUT + 100);
+
+    // Verify that next event is IO_COMPLETED_EVT and DNS status is TIMEOUT.
+    ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
+              name_change->getNextEvent());
+    ASSERT_EQ(DNSClient::TIMEOUT, name_change->getDnsUpdateStatus());
+}
+
+/// @brief Tests sendUpdate method when it receives a corrupt response from
+/// the server.
+TEST_F(NameChangeTransactionTest, sendUpdateCorruptResponse) {
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+    ASSERT_NO_THROW(name_change->initDictionaries());
+    ASSERT_TRUE(name_change->selectFwdServer());
+
+    // Create a server and start it listening.
+    FauxServer server(*io_service_, *(name_change->getCurrentServer()));
+    server.receive(FauxServer::CORRUPT_RESP);
+
+    // Create a valid request for the transaction.
+    D2UpdateMessagePtr req;
+    ASSERT_NO_THROW(req.reset(new D2UpdateMessage(D2UpdateMessage::OUTBOUND)));
+    ASSERT_NO_THROW(name_change->setDnsUpdateRequest(req));
+    req->setZone(dns::Name("request.example.com"), dns::RRClass::ANY());
+    req->setRcode(dns::Rcode(dns::Rcode::NOERROR_CODE));
+
+    // Set the flag to use the NameChangeStub's DNSClient callback.
+    name_change->use_stub_callback_ = true;
+
+    // Invoke sendUpdate.
+    ASSERT_NO_THROW(name_change->sendUpdate());
+
+    // Update attempt count should be 1, next event should be NOP_EVT.
+    EXPECT_EQ(1, name_change->getUpdateAttempts());
+    ASSERT_EQ(NameChangeTransaction::NOP_EVT,
+              name_change->getNextEvent());
+
+    // Run the IO for 500 ms.  This should be more than enough time.
+    runTimedIO(500);
+
+    // Verify that next event is IO_COMPLETED_EVT and DNS status is INVALID.
+    ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
+              name_change->getNextEvent());
+    ASSERT_EQ(DNSClient::INVALID_RESPONSE, name_change->getDnsUpdateStatus());
+}
+
+/// @brief Tests sendUpdate method when the exchange succeeds.
+TEST_F(NameChangeTransactionTest, sendUpdate) {
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+    ASSERT_NO_THROW(name_change->initDictionaries());
+    ASSERT_TRUE(name_change->selectFwdServer());
+
+    // Create a server and start it listening.
+    FauxServer server(*io_service_, *(name_change->getCurrentServer()));
+    server.receive (FauxServer::USE_RCODE, dns::Rcode::NOERROR());
+
+    // Create a valid request for the transaction.
+    D2UpdateMessagePtr req;
+    ASSERT_NO_THROW(req.reset(new D2UpdateMessage(D2UpdateMessage::OUTBOUND)));
+    ASSERT_NO_THROW(name_change->setDnsUpdateRequest(req));
+    req->setZone(dns::Name("request.example.com"), dns::RRClass::ANY());
+    req->setRcode(dns::Rcode(dns::Rcode::NOERROR_CODE));
+
+    // Set the flag to use the NameChangeStub's DNSClient callback.
+    name_change->use_stub_callback_ = true;
+
+    // Invoke sendUpdate.
+    ASSERT_NO_THROW(name_change->sendUpdate());
+
+    // Update attempt count should be 1, next event should be NOP_EVT.
+    EXPECT_EQ(1, name_change->getUpdateAttempts());
+    ASSERT_EQ(NameChangeTransaction::NOP_EVT,
+              name_change->getNextEvent());
+
+    // Run the IO for 500 ms.  This should be more than enough time.
+    runTimedIO(500);
+
+    // Verify that next event is IO_COMPLETED_EVT and DNS status is SUCCESS.
+    ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
+              name_change->getNextEvent());
+    ASSERT_EQ(DNSClient::SUCCESS, name_change->getDnsUpdateStatus());
+
+    // Verify that we have a response and it's Rcode is NOERROR,
+    // and the zone is as expected.
+    D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
+    ASSERT_TRUE(response);
+    ASSERT_EQ(dns::Rcode::NOERROR().getCode(), response->getRcode().getCode());
+    D2ZonePtr zone = response->getZone();
+    EXPECT_TRUE(zone);
+    EXPECT_EQ("response.example.com.", zone->getName().toText());
+}
+
+/// @brief Tests the prepNewRequest method
+TEST_F(NameChangeTransactionTest, prepNewRequest) {
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+    D2UpdateMessagePtr request;
+
+    // prepNewRequest should fail on empty domain.
+    ASSERT_THROW(request = name_change->prepNewRequest(DdnsDomainPtr()),
+        NameChangeTransactionError);
+
+    // Verify that prepNewRequest fails on invalid zone name.
+    // @todo This test becomes obsolete if/when DdnsDomain enforces valid
+    // names as is done in dns::Name.
+    DdnsDomainPtr bsDomain = makeDomain(".badname","");
+    ASSERT_THROW(request = name_change->prepNewRequest(bsDomain),
+        NameChangeTransactionError);
+
+    // Verify that prepNewRequest properly constructs a message given
+    // valid input.
+    ASSERT_NO_THROW(request = name_change->prepNewRequest(forward_domain_));
+    checkZone(request, forward_domain_->getName());
+}
+
+/// @brief Tests the addLeaseAddressRData method
+TEST_F(NameChangeTransactionTest, addLeaseAddressRData) {
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+    dhcp_ddns::NameChangeRequestPtr ncr = name_change->getNcr();
+
+    // Verify we can add a lease RData to an valid RRset.
+    dns::RRsetPtr rrset(new dns::RRset(dns::Name("bs"), dns::RRClass::IN(),
+                                       name_change->getAddressRRType(),
+                                       dns::RRTTL(0)));
+    ASSERT_NO_THROW(name_change->addLeaseAddressRdata(rrset));
+
+    // Verify the Rdata was added and the value is correct.
+    ASSERT_EQ(1, rrset->getRdataCount());
+    dns::RdataIteratorPtr rdata_it = rrset->getRdataIterator();
+    ASSERT_TRUE(rdata_it);
+    EXPECT_EQ(ncr->getIpAddress(), rdata_it->getCurrent().toText());
+
+}
+
+/// @brief Tests the addDhcidRData method
+TEST_F(NameChangeTransactionTest, addDhcidRdata) {
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+    dhcp_ddns::NameChangeRequestPtr ncr = name_change->getNcr();
+
+    // Verify we can add a lease RData to an valid RRset.
+    dns::RRsetPtr rrset(new dns::RRset(dns::Name("bs"), dns::RRClass::IN(),
+                                       dns::RRType::DHCID(), dns::RRTTL(0)));
+    ASSERT_NO_THROW(name_change->addDhcidRdata(rrset));
+
+    // Verify the Rdata was added and the value is correct.
+    ASSERT_EQ(1, rrset->getRdataCount());
+    dns::RdataIteratorPtr rdata_it = rrset->getRdataIterator();
+    ASSERT_TRUE(rdata_it);
+
+    const std::vector<uint8_t>& ncr_dhcid = ncr->getDhcid().getBytes();
+    util::InputBuffer buffer(ncr_dhcid.data(), ncr_dhcid.size());
+    dns::rdata::in::DHCID rdata_ref(buffer, ncr_dhcid.size());
+    EXPECT_EQ(0, rdata_ref.compare(rdata_it->getCurrent()));
+}
+
+/// @brief Tests the addPtrData method
+TEST_F(NameChangeTransactionTest, addPtrRdata) {
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+    dhcp_ddns::NameChangeRequestPtr ncr = name_change->getNcr();
+
+    // Verify we can add a PTR RData to an valid RRset.
+    dns::RRsetPtr rrset (new dns::RRset(dns::Name("bs"), dns::RRClass::IN(),
+                                        dns::RRType::PTR(), dns::RRTTL(0)));
+    ASSERT_NO_THROW(name_change->addPtrRdata(rrset));
+
+    // Verify the Rdata was added and the value is correct.
+    ASSERT_EQ(1, rrset->getRdataCount());
+    dns::RdataIteratorPtr rdata_it = rrset->getRdataIterator();
+    ASSERT_TRUE(rdata_it);
+
+    EXPECT_EQ(ncr->getFqdn(), rdata_it->getCurrent().toText());
+}
+
+}; // anonymous namespace
diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am
index 774d5b4..a38919a 100644
--- a/src/bin/dhcp4/Makefile.am
+++ b/src/bin/dhcp4/Makefile.am
@@ -59,6 +59,7 @@ nodist_b10_dhcp4_SOURCES = dhcp4_messages.h dhcp4_messages.cc
 EXTRA_DIST += dhcp4_messages.mes
 
 b10_dhcp4_LDADD  = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libb10-dhcp_ddns.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
diff --git a/src/bin/dhcp4/config_parser.cc b/src/bin/dhcp4/config_parser.cc
index 3c8ac26..777cce5 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);
@@ -209,7 +216,6 @@ protected:
         return (parser);
     }
 
-
     /// @brief Determines if the given option space name and code describe
     /// a standard option for the DCHP4 server.
     ///
@@ -388,6 +394,8 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
         parser = new DbAccessParser(config_id);
     } else if (config_id.compare("hooks-libraries") == 0) {
         parser = new HooksLibrariesParser(config_id);
+    } else if (config_id.compare("echo-client-id") == 0) {
+        parser = new BooleanParser(config_id, globalContext()->boolean_values_);
     } else {
         isc_throw(NotImplemented,
                 "Parser error: Global configuration parameter not supported: "
@@ -397,6 +405,20 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
     return (parser);
 }
 
+void commitGlobalOptions() {
+    // Although the function is modest for now, it is certain that the number
+    // of global switches will increase over time, hence the name.
+
+    // Set whether v4 server is supposed to echo back client-id (yes = RFC6842
+    // compatible, no = backward compatibility)
+    try {
+        bool echo_client_id = globalContext()->boolean_values_->getParam("echo-client-id");
+        CfgMgr::instance().echoClientId(echo_client_id);
+    } catch (...) {
+        // Ignore errors. This flag is optional
+    }
+}
+
 isc::data::ConstElementPtr
 configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
     if (!config_set) {
@@ -529,6 +551,9 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
                 iface_parser->commit();
             }
 
+            // Apply global options
+            commitGlobalOptions();
+
             // This occurs last as if it succeeds, there is no easy way
             // revert it.  As a result, the failure to commit a subsequent
             // change causes problems when trying to roll back.
@@ -572,4 +597,3 @@ ParserContextPtr& globalContext() {
 
 }; // end of isc::dhcp namespace
 }; // end of isc namespace
-
diff --git a/src/bin/dhcp4/dhcp4.dox b/src/bin/dhcp4/dhcp4.dox
index b77ac24..0d457c6 100644
--- a/src/bin/dhcp4/dhcp4.dox
+++ b/src/bin/dhcp4/dhcp4.dox
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013  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
@@ -84,6 +84,77 @@ counterpart. See \ref dhcpv6ConfigInherit.
 The DHCPv4 server uses the same logic to supply custom callback function to
 parse message option as DHCPv6 server implementation. See \ref dhcpv6OptionsParse.
 
+ 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
+in the same message, the DHCPv4 Client FQDN %Option is processed and the Host
+Name option is ignored. If only Host Name Option is present in the client's
+message, it is used to update DNS.
+
+Server may be configured to use a different name to perform DNS update for the
+client. In this case the server will return one of the DHCPv4 Client FQDN or
+Host Name %Option in its response with the name which was selected for the
+client to indicate that this name will be used to perform DNS update.
+
+The b10-dhcp-ddns process is responsible for the actual communication with the
+DNS, i.e. to send DNS update messages. The b10-dhcp4 module is responsible for
+generating @ref isc::dhcp_ddns::NameChangeRequest and sending it to
+the b10-dhcp-ddns module. The @ref isc::dhcp_ddns::NameChangeRequest object
+represents changes to the DNS bindings, related to acquisition, renewal or
+release of the DHCP lease. The b10-dhcp4 module implements the simple FIFO queue
+of the NameChangeRequest objects. The module logic, which processes the incoming
+DHCPv4 Client FQDN and Host Name Options puts these requests into the FIFO queue.
+
+ at todo Currently the FIFO queue is not processed after the NameChangeRequests are
+generated and added to it. In the future implementation steps it is planned to
+create a code which will check if there are any outstanding requests in the queue
+and send them to the b10-dhcp-ddns module when server is idle waiting for DHCP
+messages.
+
+When client gets an address from the server, a DHCPv4 server may generate 0, 1
+or 2 NameChangeRequests during single message processing. Server generates no
+NameChangeRequests if it is not configured to update DNS or it rejects the DNS
+update for any other reason.
+
+Server may generate 1 NameChangeRequest in a case when client acquired a new
+lease or it releases an existing lease. In the former case, the NameChangeRequest
+type is CHG_ADD, which indicates that the b10-dhcp-ddns module should add a new
+DNS binding for the client, and it is assumed that there is no DNS binding for
+this client already. In the latter case, the NameChangeRequest type is CHG_REMOVE
+to indicate to the b10-dhcp-ddns module that an existing DNS binding should be
+removed from the DNS. The binding consists of the forward and reverse mapping.
+The server may only remove the mapping which it had added. Therefore, the lease
+database holds the information which updates (no update, reverse only update,
+forward only update or both reverse and forward update) have been performed when
+the lease was acquired or renewed. Server checks this information to make a
+decision which mapping it is supposed to remove when lease is released.
+
+Server may generate 2 NameChangeRequests in case a client is renewing a lease and
+it already has a DNS binding for that lease. The DHCPv4 server will check if
+there is an existing lease for the client which has sent a message and if DNS
+Updates had been performed for this lease. If the notion of client's FQDN changes,
+comparing to the information stored in the lease database, the DHCPv4 has to
+remove an existing binding from the DNS and then add a new binding according to
+the new FQDN information received from the client. If the client's FQDN
+information (including the client's name and type of update performed) doesn't
+change comparing to the NameChangeRequest is not generated.
+
+The DHCPv4 Client FQDN %Option comprises flags which communicate to the server
+what updates (if any) client expects the server to perform. Server may be
+configured to obey client's preference or to do FQDN processing in a different way.
+If the server overrides client's preference it will communicate it by sending
+the DHCPv4 Client FQDN %Option in its responses to a client, with the appropriate
+flags set.
+
+ at todo Note, that current implementation doesn't allow configuration of the
+server's behaviour with respect to DNS Updates. This is planned for the future.
+The default behaviour is constituted by the set of constants defined in the
+(upper part of) dhcp4_srv.cc file. Once the configuration is implemented,
+these constants will be removed.
+
 @section dhcpv4Other Other DHCPv4 topics
 
  For hooks API support in DHCPv4, see @ref dhcpv4Hooks.
diff --git a/src/bin/dhcp4/dhcp4.spec b/src/bin/dhcp4/dhcp4.spec
index b507159..9372dfd 100644
--- a/src/bin/dhcp4/dhcp4.spec
+++ b/src/bin/dhcp4/dhcp4.spec
@@ -54,6 +54,12 @@
         "item_default": ""
       },
 
+      { "item_name": "echo-client-id",
+        "item_type": "boolean",
+        "item_optional": true,
+        "item_default": true
+      },
+
       { "item_name": "option-def",
         "item_type": "list",
         "item_optional": false,
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
index 1bfce66..9be599e 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/src/bin/dhcp4/dhcp4_messages.mes
@@ -27,6 +27,11 @@ successfully established a session with the BIND 10 control channel.
 This debug message is issued just before the IPv4 DHCP server attempts
 to establish a session with the BIND 10 control channel.
 
+% DHCP4_CLIENT_NAME_PROC_FAIL failed to process the fqdn or hostname sent by a client: %1
+This debug message is issued when the DHCP server was unable to process the
+FQDN or Hostname option sent by a client. This is likely because the client's
+name was malformed or due to internal server error.
+
 % DHCP4_COMMAND_RECEIVED received command %1, arguments: %2
 A debug message listing the command (and possible arguments) received
 from the BIND 10 control system by the IPv4 DHCP server.
@@ -70,6 +75,16 @@ This message is printed when DHCPv4 server disables an interface from being
 used to receive DHCPv4 traffic. Sockets on this interface will not be opened
 by the Interface Manager until interface is enabled.
 
+% DHCP4_DHCID_COMPUTE_ERROR failed to compute the DHCID for lease: %1, reason: %2
+This error message is logged when the attempt to compute DHCID for a specified
+lease has failed. The lease details and reason for failure is logged in the
+message.
+
+% DHCP4_EMPTY_HOSTNAME received empty hostname from the client, skipping processing of this option
+This debug message is issued when the server received an empty Hostname option
+from a client. Server does not process empty Hostname options and therefore
+option is skipped.
+
 % DHCP4_HOOK_BUFFER_RCVD_SKIP received DHCPv4 buffer was dropped because a callout set the skip flag.
 This debug message is printed when a callout installed on buffer4_receive
 hook point set the skip flag. For this particular hook point, the
@@ -134,6 +149,19 @@ specified client after receiving a REQUEST message from it.  There are many
 possible reasons for such a failure. Additional messages will indicate the
 reason.
 
+% DHCP4_NAME_GEN_UPDATE_FAIL failed to update the lease after generating name for a client: %1
+This message indicates the failure when trying to update the lease and/or
+options in the server's response with the hostname generated by the server
+from the acquired address. The message argument indicates the reason for the
+failure.
+
+% DHCP4_NCR_CREATION_FAILED failed to generate name change requests for DNS: %1
+This message indicates that server was unable to generate NameChangeRequests
+which should be sent to the b10-dhcp_ddns module to create
+new DNS records for the lease being acquired or to update existing records
+for the renewed lease. The reason for the failure is printed in the logged
+message.
+
 % DHCP4_NO_SOCKETS_OPEN no interface configured to listen to DHCP traffic
 This warning message is issued when current server configuration specifies
 no interfaces that server should listen on, or specified interfaces are not
@@ -147,6 +175,10 @@ IPv4 DHCP server but it is not running.
 A debug message issued during startup, this indicates that the IPv4 DHCP
 server is about to open sockets on the specified port.
 
+% DHCP4_OPEN_SOCKET_FAIL failed to create socket: %1
+A warning message issued when IfaceMgr fails to open and bind a socket. The reason
+for the failure is appended as an argument of the log message.
+
 % DHCP4_PACKET_PARSE_FAIL failed to parse incoming packet: %1
 The IPv4 DHCP server has received a packet that it is unable to
 interpret. The reason why the packet is invalid is included in the message.
@@ -211,6 +243,12 @@ failure is given in the message.
 % DHCP4_QUERY_DATA received packet type %1, data is <%2>
 A debug message listing the data received from the client.
 
+% DHCP4_QUEUE_NCR name change request to %1 DNS entry queued: %2
+A debug message which is logged when the NameChangeRequest to add or remove
+a DNS entries for a particular lease has been queued. The first parameter of
+this log message indicates whether the DNS entry is to be added or removed.
+The second parameter carries the details of the NameChangeRequest.
+
 % DHCP4_RELEASE address %1 belonging to client-id %2, hwaddr %3 was released properly.
 This debug message indicates that an address was released properly. It
 is a normal operation during client shutdown.
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index b38d421..5e5572b 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>
@@ -33,9 +35,11 @@
 #include <dhcpsrv/utils.h>
 #include <hooks/callout_handle.h>
 #include <hooks/hooks_manager.h>
+#include <util/strutil.h>
 
 #include <boost/algorithm/string/erase.hpp>
 #include <boost/bind.hpp>
+#include <boost/foreach.hpp>
 
 #include <iomanip>
 #include <fstream>
@@ -43,6 +47,7 @@
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
+using namespace isc::dhcp_ddns;
 using namespace isc::hooks;
 using namespace isc::log;
 using namespace std;
@@ -76,6 +81,36 @@ Dhcp4Hooks Hooks;
 namespace isc {
 namespace dhcp {
 
+namespace {
+
+// @todo The following constants describe server's behavior with respect to the
+// DHCPv4 Client FQDN Option sent by a client. They will be removed
+// when DDNS parameters for DHCPv4 are implemented with the ticket #3033.
+
+// @todo Additional configuration parameter which we may consider is the one
+// that controls whether the DHCP server sends the removal NameChangeRequest
+// if it discovers that the entry for the particular client exists or that
+// it always updates the DNS.
+
+// Should server always include the FQDN option in its response, regardless
+// if it has been requested in Parameter Request List Option (Disabled).
+const bool FQDN_ALWAYS_INCLUDE = false;
+// Enable A RR update delegation to the client (Disabled).
+const bool FQDN_ALLOW_CLIENT_UPDATE = false;
+// Globally enable updates (Enabled).
+const bool FQDN_ENABLE_UPDATE = true;
+// Do update, even if client requested no updates with N flag (Disabled).
+const bool FQDN_OVERRIDE_NO_UPDATE = false;
+// Server performs an update when client requested delegation (Enabled).
+const bool FQDN_OVERRIDE_CLIENT_UPDATE = true;
+// The fully qualified domain-name suffix if partial name provided by
+// a client.
+const char* FQDN_PARTIAL_SUFFIX = "example.com";
+// Should server replace the domain-name supplied by the client (Disabled).
+const bool FQDN_REPLACE_CLIENT_NAME = false;
+
+}
+
 /// @brief file name of a server-id file
 ///
 /// Server must store its server identifier in persistent storage that must not
@@ -105,10 +140,15 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
         // will be able to respond directly.
         IfaceMgr::instance().setMatchingPacketFilter(direct_response_desired);
 
+        // Open sockets only if port is non-zero. Port 0 is used
+        // for non-socket related testing.
         if (port) {
-            // open sockets only if port is non-zero. Port 0 is used
-            // for non-socket related testing.
-            IfaceMgr::instance().openSockets4(port_, use_bcast_);
+            // Create error handler. This handler will be called every time
+            // the socket opening operation fails. We use this handler to
+            // log a warning.
+            isc::dhcp::IfaceMgrErrorMsgCallback error_handler =
+                boost::bind(&Dhcpv4Srv::ifaceMgrSocket4ErrorHandler, _1);
+            IfaceMgr::instance().openSockets4(port_, use_bcast_, error_handler);
         }
 
         string srvid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_ID_FILE);
@@ -439,6 +479,9 @@ Dhcpv4Srv::run() {
             LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL)
                 .arg(e.what());
         }
+
+        // Send NameChangeRequests to the b10-dhcp_ddns module.
+        sendNameChangeRequests();
     }
 
     return (true);
@@ -548,6 +591,52 @@ Dhcpv4Srv::srvidToString(const OptionPtr& srvid) {
     return (addrs[0].toText());
 }
 
+isc::dhcp_ddns::D2Dhcid
+Dhcpv4Srv::computeDhcid(const Lease4Ptr& lease) {
+    if (!lease) {
+        isc_throw(DhcidComputeError, "a pointer to the lease must be not"
+                  " NULL to compute DHCID");
+
+    } else if (lease->hostname_.empty()) {
+        isc_throw(DhcidComputeError, "unable to compute the DHCID for the"
+                  " lease which has empty hostname set");
+
+    }
+
+    // In order to compute DHCID the client's hostname must be encoded in
+    // canonical wire format. It is unlikely that the FQDN is malformed
+    // because it is validated by the classes which encapsulate options
+    // carrying client FQDN. However, if the client name was carried in the
+    // Hostname option it is more likely as it carries the hostname as a
+    // regular string.
+    std::vector<uint8_t> fqdn_wire;
+    try {
+        OptionDataTypeUtil::writeFqdn(lease->hostname_, fqdn_wire, true);
+
+    } catch (const Exception& ex) {
+        isc_throw(DhcidComputeError, "unable to compute DHCID because the"
+                  " hostname: " << lease->hostname_ << " is invalid");
+
+    }
+
+    // Prefer client id to HW address to compute DHCID. If Client Id is
+    // NULL, use HW address.
+    try {
+        if (lease->client_id_) {
+            return (D2Dhcid(lease->client_id_->getClientId(), fqdn_wire));
+
+        } else {
+            HWAddrPtr hwaddr(new HWAddr(lease->hwaddr_, HTYPE_ETHER));
+            return (D2Dhcid(hwaddr, fqdn_wire));
+        }
+    } catch (const Exception& ex) {
+        isc_throw(DhcidComputeError, "unable to compute DHCID: "
+                  << ex.what());
+
+    }
+
+}
+
 void
 Dhcpv4Srv::copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer) {
     answer->setIface(question->getIface());
@@ -564,8 +653,10 @@ Dhcpv4Srv::copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer) {
     answer->setGiaddr(question->getGiaddr());
 
     // Let's copy client-id to response. See RFC6842.
+    // It is possible to disable RFC6842 to keep backward compatibility
+    bool echo = CfgMgr::instance().echoClientId();
     OptionPtr client_id = question->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
-    if (client_id) {
+    if (client_id && echo) {
         answer->addOption(client_id);
     }
 
@@ -635,20 +726,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 };
@@ -678,6 +827,265 @@ Dhcpv4Srv::appendBasicOptions(const Pkt4Ptr& question, Pkt4Ptr& msg) {
 }
 
 void
+Dhcpv4Srv::processClientName(const Pkt4Ptr& query, Pkt4Ptr& answer) {
+    // It is possible that client has sent both Client FQDN and Hostname
+    // option. In such case, server should prefer Client FQDN option and
+    // ignore the Hostname option.
+    try {
+        Option4ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<Option4ClientFqdn>
+            (query->getOption(DHO_FQDN));
+        if (fqdn) {
+            processClientFqdnOption(fqdn, answer);
+
+        } else {
+            OptionCustomPtr hostname = boost::dynamic_pointer_cast<OptionCustom>
+                (query->getOption(DHO_HOST_NAME));
+            if (hostname) {
+                processHostnameOption(hostname, answer);
+            }
+
+        }
+    } catch (const Exception& ex) {
+        // In some rare cases it is possible that the client's name processing
+        // fails. For example, the Hostname option may be malformed, or there
+        // may be an error in the server's logic which would cause multiple
+        // attempts to add the same option to the response message. This
+        // error message aggregates all these errors so they can be diagnosed
+        // from the log. We don't want to throw an exception here because,
+        // it will impact the processing of the whole packet. We rather want
+        // the processing to continue, even if the client's name is wrong.
+        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_CLIENT_NAME_PROC_FAIL)
+            .arg(ex.what());
+    }
+}
+
+void
+Dhcpv4Srv::processClientFqdnOption(const Option4ClientFqdnPtr& fqdn,
+                                   Pkt4Ptr& answer) {
+    // Create the DHCPv4 Client FQDN Option to be included in the server's
+    // response to a client.
+    Option4ClientFqdnPtr fqdn_resp(new Option4ClientFqdn(*fqdn));
+
+    // RFC4702, section 4 - set 'NOS' flags to 0.
+    fqdn_resp->setFlag(Option4ClientFqdn::FLAG_S, 0);
+    fqdn_resp->setFlag(Option4ClientFqdn::FLAG_O, 0);
+    fqdn_resp->setFlag(Option4ClientFqdn::FLAG_N, 0);
+
+    // Conditions when N flag has to be set to indicate that server will not
+    // perform DNS updates:
+    // 1. Updates are globally disabled,
+    // 2. Client requested no update and server respects it,
+    // 3. Client requested that the forward DNS update is delegated to the
+    //    client but server neither respects requests for forward update
+    //    delegation nor it is configured to send update on its own when
+    //    client requested delegation.
+    if (!FQDN_ENABLE_UPDATE ||
+        (fqdn->getFlag(Option4ClientFqdn::FLAG_N) &&
+         !FQDN_OVERRIDE_NO_UPDATE) ||
+        (!fqdn->getFlag(Option4ClientFqdn::FLAG_S) &&
+         !FQDN_ALLOW_CLIENT_UPDATE && !FQDN_OVERRIDE_CLIENT_UPDATE)) {
+        fqdn_resp->setFlag(Option4ClientFqdn::FLAG_N, true);
+
+    // Conditions when S flag is set to indicate that server will perform DNS
+    // update on its own:
+    // 1. Client requested that server performs DNS update and DNS updates are
+    //    globally enabled.
+    // 2. Client requested that server delegates forward update to the client
+    //    but server doesn't respect requests for delegation and it is
+    // configured to perform an update on its own when client requested the
+    // delegation.
+    } else  if (fqdn->getFlag(Option4ClientFqdn::FLAG_S) ||
+                (!fqdn->getFlag(Option4ClientFqdn::FLAG_S) &&
+                 !FQDN_ALLOW_CLIENT_UPDATE && FQDN_OVERRIDE_CLIENT_UPDATE)) {
+        fqdn_resp->setFlag(Option4ClientFqdn::FLAG_S, true);
+    }
+
+    // Server MUST set the O flag if it has overriden the client's setting
+    // of S flag.
+    if (fqdn->getFlag(Option4ClientFqdn::FLAG_S) !=
+        fqdn_resp->getFlag(Option4ClientFqdn::FLAG_S)) {
+        fqdn_resp->setFlag(Option4ClientFqdn::FLAG_O, true);
+    }
+
+    // If client suppled partial or empty domain-name, server should generate
+    // one.
+    if (fqdn->getDomainNameType() == Option4ClientFqdn::PARTIAL) {
+        std::ostringstream name;
+        if (fqdn->getDomainName().empty() || FQDN_REPLACE_CLIENT_NAME) {
+            fqdn_resp->setDomainName("", Option4ClientFqdn::PARTIAL);
+
+        } else {
+            name << fqdn->getDomainName();
+            name << "." << FQDN_PARTIAL_SUFFIX;
+            fqdn_resp->setDomainName(name.str(), Option4ClientFqdn::FULL);
+
+        }
+
+    // Server may be configured to replace a name supplied by a client, even if
+    // client supplied fully qualified domain-name. The empty domain-name is
+    // is set to indicate that the name must be generated when the new lease
+    // is acquired.
+    } else if(FQDN_REPLACE_CLIENT_NAME) {
+        fqdn_resp->setDomainName("", Option4ClientFqdn::PARTIAL);
+    }
+
+    // Add FQDN option to the response message. Note that, there may be some
+    // cases when server may choose not to include the FQDN option in a
+    // response to a client. In such cases, the FQDN should be removed from the
+    // outgoing message. In theory we could cease to include the FQDN option
+    // in this function until it is confirmed that it should be included.
+    // However, we include it here for simplicity. Functions used to acquire
+    // lease for a client will scan the response message for FQDN and if it
+    // is found they will take necessary actions to store the FQDN information
+    // in the lease database as well as to generate NameChangeRequests to DNS.
+    // If we don't store the option in the reponse message, we will have to
+    // propagate it in the different way to the functions which acquire the
+    // lease. This would require modifications to the API of this class.
+    answer->addOption(fqdn_resp);
+}
+
+void
+Dhcpv4Srv::processHostnameOption(const OptionCustomPtr& opt_hostname,
+                                 Pkt4Ptr& answer) {
+    // Do nothing if the DNS updates are disabled.
+    if (!FQDN_ENABLE_UPDATE) {
+        return;
+    }
+
+    std::string hostname = isc::util::str::trim(opt_hostname->readString());
+    unsigned int label_count = OptionDataTypeUtil::getLabelCount(hostname);
+    // The hostname option sent by the client should be at least 1 octet long.
+    // If it isn't we ignore this option.
+    if (label_count == 0) {
+        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_EMPTY_HOSTNAME);
+        return;
+    }
+    // Copy construct the hostname provided by the client. It is entirely
+    // possible that we will use the hostname option provided by the client
+    // to perform the DNS update and we will send the same option to him to
+    // indicate that we accepted this hostname.
+    OptionCustomPtr opt_hostname_resp(new OptionCustom(*opt_hostname));
+
+    // The hostname option may be unqualified or fully qualified. The lab_count
+    // holds the number of labels for the name. The number of 1 means that
+    // there is only root label "." (even for unqualified names, as the
+    // getLabelCount function treats each name as a fully qualified one).
+    // By checking the number of labels present in the hostname we may infer
+    // whether client has sent the fully qualified or unqualified hostname.
+
+    // @todo We may want to reconsider whether it is appropriate for the
+    // client to send a root domain name as a Hostname. There are
+    // also extensions to the auto generation of the client's name,
+    // e.g. conversion to the puny code which may be considered at some point.
+    // For now, we just remain liberal and expect that the DNS will handle
+    // conversion if needed and possible.
+    if (FQDN_REPLACE_CLIENT_NAME || (label_count < 2)) {
+        opt_hostname_resp->writeString("");
+    // If there are two labels, it means that the client has specified
+    // the unqualified name. We have to concatenate the unqalified name
+    // with the domain name.
+    } else if (label_count == 2) {
+        std::ostringstream resp_hostname;
+        resp_hostname << hostname << "." << FQDN_PARTIAL_SUFFIX << ".";
+        opt_hostname_resp->writeString(resp_hostname.str());
+    }
+
+    answer->addOption(opt_hostname_resp);
+}
+
+void
+Dhcpv4Srv::createNameChangeRequests(const Lease4Ptr& lease,
+                                    const Lease4Ptr& old_lease) {
+    if (!lease) {
+        isc_throw(isc::Unexpected,
+                  "NULL lease specified when creating NameChangeRequest");
+    }
+
+    // If old lease is not NULL, it is an indication that the lease has
+    // just been renewed. In such case we may need to generate the
+    // additional NameChangeRequest to remove an existing entry before
+    // we create a NameChangeRequest to add the entry for an updated lease.
+    // We may also decide not to generate any requests at all. This is when
+    // we discover that nothing has changed in the client's FQDN data.
+    if (old_lease) {
+        if (!lease->matches(*old_lease)) {
+            isc_throw(isc::Unexpected,
+                      "there is no match between the current instance of the"
+                      " lease: " << lease->toText() << ", and the previous"
+                      " instance: " << lease->toText());
+        } else {
+            // There will be a NameChangeRequest generated to remove existing
+            // DNS entries if the following conditions are met:
+            // - The hostname is set for the existing lease, we can't generate
+            //   removal request for non-existent hostname.
+            // - A server has performed reverse, forward or both updates.
+            // - FQDN data between the new and old lease do not match.
+            if  ((lease->hostname_ != old_lease->hostname_) ||
+                 (lease->fqdn_fwd_ != old_lease->fqdn_fwd_) ||
+                 (lease->fqdn_rev_ != old_lease->fqdn_rev_)) {
+                queueNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE,
+                                       old_lease);
+
+            // If FQDN data from both leases match, there is no need to update.
+            } else if ((lease->hostname_ == old_lease->hostname_) &&
+                       (lease->fqdn_fwd_ == old_lease->fqdn_fwd_) &&
+                       (lease->fqdn_rev_ == old_lease->fqdn_rev_)) {
+                return;
+            }
+
+        }
+    }
+
+    // We may need to generate the NameChangeRequest for the new lease. It
+    // will be generated only if hostname is set and if forward or reverse
+    // update has been requested.
+    queueNameChangeRequest(isc::dhcp_ddns::CHG_ADD, lease);
+}
+
+void
+Dhcpv4Srv::
+queueNameChangeRequest(const isc::dhcp_ddns::NameChangeType chg_type,
+                       const Lease4Ptr& lease) {
+    // The hostname must not be empty, and at least one type of update
+    // should be requested.
+    if (!lease || lease->hostname_.empty() ||
+        (!lease->fqdn_rev_ && !lease->fqdn_fwd_)) {
+        return;
+    }
+
+    // Create the DHCID for the NameChangeRequest.
+    D2Dhcid dhcid;
+    try {
+        dhcid  = computeDhcid(lease);
+    } catch (const DhcidComputeError& ex) {
+        LOG_ERROR(dhcp4_logger, DHCP4_DHCID_COMPUTE_ERROR)
+            .arg(lease->toText())
+            .arg(ex.what());
+        return;
+    }
+    // Create NameChangeRequest
+    NameChangeRequest ncr(chg_type, lease->fqdn_fwd_, lease->fqdn_rev_,
+                          lease->hostname_, lease->addr_.toText(),
+                          dhcid, lease->cltt_ + lease->valid_lft_,
+                          lease->valid_lft_);
+    // And queue it.
+    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUEUE_NCR)
+        .arg(chg_type == CHG_ADD ? "add" : "remove")
+        .arg(ncr.toText());
+    name_change_reqs_.push(ncr);
+}
+
+void
+Dhcpv4Srv::sendNameChangeRequests() {
+    while (!name_change_reqs_.empty()) {
+        // @todo Once next NameChangeRequest is picked from the queue
+        // we should send it to the b10-dhcp_ddns module. Currently we
+        // just drop it.
+        name_change_reqs_.pop();
+    }
+}
+
+void
 Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
 
     // We need to select a subnet the client is connected in.
@@ -733,6 +1141,28 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
 
     CalloutHandlePtr callout_handle = getCalloutHandle(question);
 
+    std::string hostname;
+    bool fqdn_fwd = false;
+    bool fqdn_rev = false;
+    OptionCustomPtr opt_hostname;
+    Option4ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
+        Option4ClientFqdn>(answer->getOption(DHO_FQDN));
+    if (fqdn) {
+        hostname = fqdn->getDomainName();
+        fqdn_fwd = fqdn->getFlag(Option4ClientFqdn::FLAG_S);
+        fqdn_rev = !fqdn->getFlag(Option4ClientFqdn::FLAG_N);
+    } else {
+        opt_hostname = boost::dynamic_pointer_cast<OptionCustom>
+            (answer->getOption(DHO_HOST_NAME));
+        if (opt_hostname) {
+            hostname = opt_hostname->readString();
+            // @todo It could be configurable what sort of updates the server
+            // is doing when Hostname option was sent.
+            fqdn_fwd = true;
+            fqdn_rev = true;
+        }
+    }
+
     // 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
@@ -740,7 +1170,8 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
     // @todo pass the actual FQDN data.
     Lease4Ptr old_lease;
     Lease4Ptr lease = alloc_engine_->allocateLease4(subnet, client_id, hwaddr,
-                                                    hint, false, false, "",
+                                                      hint, fqdn_fwd, fqdn_rev,
+                                                      hostname,
                                                     fake_allocation,
                                                     callout_handle,
                                                     old_lease);
@@ -756,6 +1187,42 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
 
         answer->setYiaddr(lease->addr_);
 
+        // If there has been Client FQDN or Hostname option sent, but the
+        // hostname is empty, it means that server is responsible for
+        // generating the entire hostname for the client. The example of the
+        // client's name, generated from the IP address is: host-192-0-2-3.
+        if ((fqdn || opt_hostname) && lease->hostname_.empty()) {
+            hostname = lease->addr_.toText();
+            // Replace dots with hyphens.
+            std::replace(hostname.begin(), hostname.end(), '.', '-');
+            ostringstream stream;
+            // The partial suffix will need to be replaced with the actual
+            // domain-name for the client when configuration is implemented.
+            stream << "host-" << hostname << "." << FQDN_PARTIAL_SUFFIX << ".";
+            lease->hostname_ = stream.str();
+            // The operations below are rather safe, but we want to catch
+            // any potential exceptions (e.g. invalid lease database backend
+            // implementation) and log an error.
+            try {
+                // The lease update should be safe, because the lease should
+                // be already in the database. In most cases the exception
+                // would be thrown if the lease was missing.
+                LeaseMgrFactory::instance().updateLease4(lease);
+                // The name update in the option should be also safe,
+                // because the generated name is well formed.
+                if (fqdn) {
+                    fqdn->setDomainName(lease->hostname_,
+                                        Option4ClientFqdn::FULL);
+                } else if (opt_hostname) {
+                    opt_hostname->writeString(lease->hostname_);
+                }
+
+            } catch (const Exception& ex) {
+                LOG_ERROR(dhcp4_logger, DHCP4_NAME_GEN_UPDATE_FAIL)
+                    .arg(ex.what());
+            }
+        }
+
         // IP Address Lease time (type 51)
         opt = OptionPtr(new Option(Option::V4, DHO_DHCP_LEASE_TIME));
         opt->setUint32(lease->valid_lft_);
@@ -767,6 +1234,21 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
         // @todo: send renew timer option (T1, option 58)
         // @todo: send rebind timer option (T2, option 59)
 
+        // @todo Currently the NameChangeRequests are always generated if
+        // real (not fake) allocation is being performed. Should we have
+        // control switch to enable/disable NameChangeRequest creation?
+        // Perhaps we need a way to detect whether the b10-dhcp-ddns module
+        // is up an running?
+        if (!fake_allocation) {
+            try {
+                createNameChangeRequests(lease, old_lease);
+            } catch (const Exception& ex) {
+                LOG_ERROR(dhcp4_logger, DHCP4_NCR_CREATION_FAILED)
+                    .arg(ex.what());
+            }
+
+        }
+
     } else {
         // Allocation engine did not allocate a lease. The engine logged
         // cause of that failure. The only thing left is to insert
@@ -780,6 +1262,9 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
 
         answer->setType(DHCPNAK);
         answer->setYiaddr(IOAddress("0.0.0.0"));
+
+        answer->delOption(DHO_FQDN);
+        answer->delOption(DHO_HOST_NAME);
     }
 }
 
@@ -858,11 +1343,18 @@ Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
     copyDefaultFields(discover, offer);
     appendDefaultOptions(offer, DHCPOFFER);
 
+    // If DISCOVER message contains the FQDN or Hostname option, server
+    // may respond to the client with the appropriate FQDN or Hostname
+    // option to indicate that whether it will take responsibility for
+    // updating DNS when the client sends REQUEST message.
+    processClientName(discover, offer);
+
     assignLease(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.
@@ -884,6 +1376,12 @@ Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
     copyDefaultFields(request, ack);
     appendDefaultOptions(ack, DHCPACK);
 
+    // If REQUEST message contains the FQDN or Hostname option, server
+    // should respond to the client with the appropriate FQDN or Hostname
+    // option to indicate if it takes responsibility for the DNS updates.
+    // This is performed by the function below.
+    processClientName(request, ack);
+
     // Note that we treat REQUEST message uniformly, regardless if this is a
     // first request (requesting for new address), renewing existing address
     // or even rebinding.
@@ -892,6 +1390,7 @@ Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
     // 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.
@@ -916,12 +1415,12 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
 
     try {
         // Do we have a lease for that particular address?
-        Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(release->getYiaddr());
+        Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(release->getCiaddr());
 
         if (!lease) {
             // No such lease - bogus release
             LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_NO_LEASE)
-                .arg(release->getYiaddr().toText())
+                .arg(release->getCiaddr().toText())
                 .arg(release->getHWAddr()->toText())
                 .arg(client_id ? client_id->toText() : "(no client-id)");
             return;
@@ -932,7 +1431,7 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
         if (lease->hwaddr_ != release->getHWAddr()->hwaddr_) {
             // @todo: Print hwaddr from lease as part of ticket #2589
             LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_WRONG_HWADDR)
-                .arg(release->getYiaddr().toText())
+                .arg(release->getCiaddr().toText())
                 .arg(client_id ? client_id->toText() : "(no client-id)")
                 .arg(release->getHWAddr()->toText());
             return;
@@ -942,7 +1441,7 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
         // the client sent us.
         if (lease->client_id_ && client_id && *lease->client_id_ != *client_id) {
             LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_WRONG_CLIENT_ID)
-                .arg(release->getYiaddr().toText())
+                .arg(release->getCiaddr().toText())
                 .arg(client_id->toText())
                 .arg(lease->client_id_->toText());
             return;
@@ -982,11 +1481,15 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
             bool success = LeaseMgrFactory::instance().deleteLease(lease->addr_);
 
             if (success) {
-                // Release successful - we're done here
+                // Release successful
                 LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE)
                     .arg(lease->addr_.toText())
                     .arg(client_id ? client_id->toText() : "(no client-id)")
                     .arg(release->getHWAddr()->toText());
+
+                // Remove existing DNS entries for the lease, if any.
+                queueNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, lease);
+
             } else {
                 // Release failed -
                 LOG_ERROR(dhcp4_logger, DHCP4_RELEASE_FAIL)
@@ -1178,7 +1681,9 @@ Dhcpv4Srv::openActiveSockets(const uint16_t port,
     // sockets are marked active or inactive.
     // @todo Optimization: we should not reopen all sockets but rather select
     // those that have been affected by the new configuration.
-    if (!IfaceMgr::instance().openSockets4(port, use_bcast)) {
+    isc::dhcp::IfaceMgrErrorMsgCallback error_handler =
+        boost::bind(&Dhcpv4Srv::ifaceMgrSocket4ErrorHandler, _1);
+    if (!IfaceMgr::instance().openSockets4(port, use_bcast, error_handler)) {
         LOG_WARN(dhcp4_logger, DHCP4_NO_SOCKETS_OPEN);
     }
 }
@@ -1272,6 +1777,11 @@ Dhcpv4Srv::unpackOptions(const OptionBuffer& buf,
     return (offset);
 }
 
+void
+Dhcpv4Srv::ifaceMgrSocket4ErrorHandler(const std::string& errmsg) {
+    // Log the reason for socket opening failure and return.
+    LOG_WARN(dhcp4_logger, DHCP4_OPEN_SOCKET_FAIL).arg(errmsg);
+}
 
 }   // namespace dhcp
 }   // namespace isc
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index 5c89c85..5b3ba3b 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -18,6 +18,9 @@
 #include <dhcp/dhcp4.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/option.h>
+#include <dhcp/option4_client_fqdn.h>
+#include <dhcp/option_custom.h>
+#include <dhcp_ddns/ncr_msg.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/alloc_engine.h>
 #include <hooks/callout_handle.h>
@@ -25,10 +28,18 @@
 #include <boost/noncopyable.hpp>
 
 #include <iostream>
+#include <queue>
 
 namespace isc {
 namespace dhcp {
 
+/// @brief Exception thrown when DHCID computation failed.
+class DhcidComputeError : public isc::Exception {
+public:
+    DhcidComputeError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
 /// @brief DHCPv4 server service.
 ///
 /// This singleton class represents DHCPv4 server. It contains all
@@ -227,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
@@ -250,6 +273,117 @@ protected:
     /// @param msg the message to add options to.
     void appendBasicOptions(const Pkt4Ptr& question, Pkt4Ptr& msg);
 
+    /// @brief Processes Client FQDN and Hostname Options sent by a client.
+    ///
+    /// Client may send Client FQDN or Hostname option to communicate its name
+    /// to the server. Server may use this name to perform DNS update for the
+    /// lease being assigned to a client. If server takes responsibility for
+    /// updating DNS for a client it may communicate it by sending the Client
+    /// FQDN or Hostname %Option back to the client. Server select a different
+    /// name than requested by a client to update DNS. In such case, the server
+    /// stores this different name in its response.
+    ///
+    /// Client should not send both Client FQDN and Hostname options. However,
+    /// if client sends both options, server should prefer Client FQDN option
+    /// and ignore the Hostname option. If Client FQDN option is not present,
+    /// the Hostname option is processed.
+    ///
+    /// The Client FQDN %Option is processed by this function as described in
+    /// RFC4702.
+    ///
+    /// In response to a Hostname %Option sent by a client, the server may send
+    /// Hostname option with the same or different hostname. If different
+    /// hostname is sent, it is an indication to the client that server has
+    /// overridden the client's preferred name and will rather use this
+    /// different name to update DNS. However, since Hostname option doesn't
+    /// carry an information whether DNS update will be carried by the server
+    /// or not, the client is responsible for checking whether DNS update
+    /// has been performed.
+    ///
+    /// After successful processing options stored in the first parameter,
+    /// this function may add Client FQDN or Hostname option to the response
+    /// message. In some cases, server may cease to add any options to the
+    /// response, i.e. when server doesn't support DNS updates.
+    ///
+    /// This function does not throw. It simply logs the debug message if the
+    /// processing of the FQDN or Hostname failed.
+    ///
+    /// @param query A DISCOVER or REQUEST message from a cient.
+    /// @param [out] answer A response message to be sent to a client.
+    void processClientName(const Pkt4Ptr& query, Pkt4Ptr& answer);
+
+private:
+    /// @brief Process Client FQDN %Option sent by a client.
+    ///
+    /// This function is called by the @c Dhcpv4Srv::processClientName when
+    /// the client has sent the FQDN option in its message to the server.
+    /// It comprises the actual logic to parse the FQDN option and prepare
+    /// the FQDN option to be sent back to the client in the server's
+    /// response.
+    ///
+    /// @param fqdn An DHCPv4 Client FQDN %Option sent by a client.
+    /// @param [out] answer A response message to be sent to a client.
+    void processClientFqdnOption(const Option4ClientFqdnPtr& fqdn,
+                                 Pkt4Ptr& answer);
+
+    /// @brief Process Hostname %Option sent by a client.
+    ///
+    /// This function is called by the @c DHcpv4Srv::processClientName when
+    /// the client has sent the Hostname option in its message to the server.
+    /// It comprises the actual logic to parse the Hostname option and
+    /// prepare the Hostname option to be sent back to the client in the
+    /// server's response.
+    ///
+    /// @param opt_hostname An @c OptionCustom object encapsulating the Hostname
+    /// %Option.
+    /// @param [out] answer A response message to be sent to a client.
+    void processHostnameOption(const OptionCustomPtr& opt_hostname,
+                               Pkt4Ptr& answer);
+
+protected:
+
+    /// @brief Creates NameChangeRequests which correspond to the lease
+    /// which has been acquired.
+    ///
+    /// If this function is called whe an existing lease is renewed, it
+    /// may generate NameChangeRequest to remove existing DNS entries which
+    /// correspond to the old lease instance. This function may cease to
+    /// generate NameChangeRequests if the notion of the client's FQDN hasn't
+    /// changed between an old and new lease.
+    ///
+    /// @param lease A pointer to the new lease which has been acquired.
+    /// @param old_lease A pointer to the instance of the old lease which has
+    /// been replaced by the new lease passed in the first argument. The NULL
+    /// value indicates that the new lease has been allocated, rather than
+    /// lease being renewed.
+    void createNameChangeRequests(const Lease4Ptr& lease,
+                                  const Lease4Ptr& old_lease);
+
+    /// @brief Creates the NameChangeRequest and adds to the queue for
+    /// processing.
+    ///
+    /// This function adds the @c isc::dhcp_ddns::NameChangeRequest to the
+    /// queue and emits the debug message which indicates whether the request
+    /// being added is to remove DNS entry or add a new entry. This function
+    /// is exception free.
+    ///
+    /// @param chg_type A type of the NameChangeRequest (ADD or REMOVE).
+    /// @param lease A lease for which the NameChangeRequest is created and
+    /// queued.
+    void queueNameChangeRequest(const isc::dhcp_ddns::NameChangeType chg_type,
+                                const Lease4Ptr& lease);
+
+    /// @brief Sends all outstanding NameChangeRequests to b10-dhcp-ddns module.
+    ///
+    /// The purpose of this function is to pick all outstanding
+    /// NameChangeRequests from the FIFO queue and send them to b10-dhcp-ddns
+    /// module.
+    ///
+    /// @todo Currently this function simply removes all requests from the
+    /// queue but doesn't send them anywhere. In the future, the
+    /// NameChangeSender will be used to deliver requests to the other module.
+    void sendNameChangeRequests();
+
     /// @brief Attempts to renew received addresses
     ///
     /// Attempts to renew existing lease. This typically includes finding a lease that
@@ -324,6 +458,28 @@ protected:
     /// @return string representation
     static std::string srvidToString(const OptionPtr& opt);
 
+    /// @brief Computes DHCID from a lease.
+    ///
+    /// This method creates an object which represents DHCID. The DHCID value
+    /// is computed as described in RFC4701. The section 3.3. of RFC4701
+    /// specifies the DHCID RR Identifier Type codes:
+    /// - 0x0000 The 1 octet htype followed by glen octets of chaddr
+    /// - 0x0001 The data octets from the DHCPv4 client's Client Identifier
+    /// option.
+    /// - 0x0002 The client's DUID.
+    ///
+    /// Currently this function supports first two of these identifiers.
+    /// The 0x0001 is preferred over the 0x0000 - if the client identifier
+    /// option is present, the former is used. If the client identifier
+    /// is absent, the latter is used.
+    ///
+    /// @todo Implement support for 0x0002 DHCID identifier type.
+    ///
+    /// @param lease A pointer to the structure describing a lease.
+    /// @return An object encapsulating DHCID to be used for DNS updates.
+    /// @throw DhcidComputeError If the computation of the DHCID failed.
+    static isc::dhcp_ddns::D2Dhcid computeDhcid(const Lease4Ptr& lease);
+
     /// @brief Selects a subnet for a given client's packet.
     ///
     /// @param question client's message
@@ -369,6 +525,15 @@ private:
     /// @return Option that contains netmask information
     static OptionPtr getNetmaskOption(const Subnet4Ptr& subnet);
 
+    /// @brief Implements the error handler for socket open failure.
+    ///
+    /// This callback function is installed on the @c isc::dhcp::IfaceMgr
+    /// when IPv4 sockets are being open. When socket fails to open for
+    /// any reason, this function is called. It simply logs the error message.
+    ///
+    /// @param errmsg An error message containing a cause of the failure.
+    static void ifaceMgrSocket4ErrorHandler(const std::string& errmsg);
+
     /// @brief Allocation Engine.
     /// Pointer to the allocation engine that we are currently using
     /// It must be a pointer, because we will support changing engines
@@ -382,6 +547,12 @@ private:
     int hook_index_pkt4_receive_;
     int hook_index_subnet4_select_;
     int hook_index_pkt4_send_;
+
+protected:
+
+    /// Holds a list of @c isc::dhcp_ddns::NameChangeRequest objects which
+    /// are waiting for sending  to b10-dhcp-ddns module.
+    std::queue<isc::dhcp_ddns::NameChangeRequest> name_change_reqs_;
 };
 
 }; // namespace isc::dhcp
diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
index fddac15..c72b8b0 100644
--- a/src/bin/dhcp4/tests/Makefile.am
+++ b/src/bin/dhcp4/tests/Makefile.am
@@ -75,12 +75,14 @@ TESTS += dhcp4_unittests
 dhcp4_unittests_SOURCES = ../dhcp4_srv.h ../dhcp4_srv.cc ../ctrl_dhcp4_srv.cc
 dhcp4_unittests_SOURCES += ../dhcp4_log.h ../dhcp4_log.cc
 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
 dhcp4_unittests_SOURCES += marker_file.cc
 nodist_dhcp4_unittests_SOURCES = ../dhcp4_messages.h ../dhcp4_messages.cc
 nodist_dhcp4_unittests_SOURCES += marker_file.h test_libraries.h
@@ -92,6 +94,7 @@ dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libb10-dhcp_ddns.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc
index d5721c4..1d9ccb7 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>
@@ -556,6 +557,47 @@ TEST_F(Dhcp4ParserTest, nextServerOverride) {
     EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText());
 }
 
+// Check whether it is possible to configure echo-client-id
+TEST_F(Dhcp4ParserTest, echoClientId) {
+
+    ConstElementPtr status;
+
+    string config_false = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"echo-client-id\": false,"
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    string config_true = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"echo-client-id\": true,"
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json_false = Element::fromJSON(config_false);
+    ElementPtr json_true = Element::fromJSON(config_true);
+
+    // Let's check the default. It should be true
+    ASSERT_TRUE(CfgMgr::instance().echoClientId());
+
+    // Now check that "false" configuration is really applied.
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json_false));
+    ASSERT_FALSE(CfgMgr::instance().echoClientId());
+
+    // Now check that "true" configuration is really applied.
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json_true));
+    ASSERT_TRUE(CfgMgr::instance().echoClientId());
+
+    // In any case revert back to the default value (true)
+    CfgMgr::instance().echoClientId(true);
+}
+
 // This test checks if it is possible to override global values
 // on a per subnet basis.
 TEST_F(Dhcp4ParserTest, subnetLocal) {
@@ -1939,6 +1981,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 ed856b2..3787149 100644
--- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -25,8 +25,10 @@
 #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>
@@ -38,6 +40,7 @@
 #include <gtest/gtest.h>
 #include <hooks/server_hooks.h>
 #include <hooks/hooks_manager.h>
+#include <config/ccsession.h>
 
 #include <boost/scoped_ptr.hpp>
 
@@ -52,114 +55,12 @@ using namespace isc::dhcp;
 using namespace isc::data;
 using namespace isc::asiolink;
 using namespace isc::hooks;
+using namespace isc::dhcp::test;
 
-#if 0
 namespace {
 
-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.
-    list<Pkt4Ptr> fake_received_;
-
-    list<Pkt4Ptr> fake_sent_;
-
-    using Dhcpv4Srv::adjustRemoteAddr;
-    using Dhcpv4Srv::processDiscover;
-    using Dhcpv4Srv::processRequest;
-    using Dhcpv4Srv::processRelease;
-    using Dhcpv4Srv::processDecline;
-    using Dhcpv4Srv::processInform;
-    using Dhcpv4Srv::getServerID;
-    using Dhcpv4Srv::loadServerID;
-    using Dhcpv4Srv::generateServerID;
-    using Dhcpv4Srv::writeServerID;
-    using Dhcpv4Srv::sanityCheck;
-    using Dhcpv4Srv::srvidToString;
-    using Dhcpv4Srv::unpackOptions;
-};
-#endif
-
 /// dummy server-id file location
-static const char* SRVID_FILE = "server-id-test.txt";
-
-namespace isc {
-namespace dhcp {
-namespace test {
+const char* SRVID_FILE = "server-id-test.txt";
 
 // Sanity check. Verifies that both Dhcpv4Srv and its derived
 // class NakedDhcpv4Srv can be instantiated and destroyed.
@@ -188,6 +89,26 @@ TEST_F(Dhcpv4SrvTest, basic) {
     EXPECT_TRUE(naked_srv->getServerID());
 }
 
+// This test verifies that exception is not thrown when an error occurs during
+// opening sockets. This test forces an error by adding a fictious interface
+// to the IfaceMgr. An attempt to open socket on this interface must always
+// fail. The DHCPv4 installs the error handler function to prevent exceptions
+// being thrown from the openSockets4 function.
+// @todo The server tests for socket should be extended but currently the
+// ability to unit test the sockets code is somewhat limited.
+TEST_F(Dhcpv4SrvTest, openActiveSockets) {
+    ASSERT_NO_THROW(CfgMgr::instance().activateAllIfaces());
+
+    Iface iface("bogusiface", 255);
+    iface.flag_loopback_ = false;
+    iface.flag_up_ = true;
+    iface.flag_running_ = true;
+    iface.inactive4_ = false;
+    iface.addAddress(IOAddress("192.0.0.0"));
+    IfaceMgr::instance().addInterface(iface);
+    ASSERT_NO_THROW(Dhcpv4Srv::openActiveSockets(DHCP4_SERVER_PORT, false));
+}
+
 // This test verifies that the destination address of the response
 // message is set to giaddr, when giaddr is set to non-zero address
 // in the received message.
@@ -687,6 +608,32 @@ TEST_F(Dhcpv4SrvTest, ManyDiscovers) {
     cout << "Offered address to client3=" << addr3.toText() << endl;
 }
 
+// Checks whether echoing back client-id is controllable, i.e.
+// whether the server obeys echo-client-id and sends (or not)
+// client-id
+TEST_F(Dhcpv4SrvTest, discoverEchoClientId) {
+    NakedDhcpv4Srv srv(0);
+
+    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);
+
+    // Check if we get response at all
+    checkResponse(offer, DHCPOFFER, 1234);
+    checkClientId(offer, clientid);
+
+    CfgMgr::instance().echoClientId(false);
+    offer = srv.processDiscover(dis);
+
+    // Check if we get response at all
+    checkResponse(offer, DHCPOFFER, 1234);
+    checkClientId(offer, clientid);
+}
+
 // This test verifies that incoming REQUEST can be handled properly, that an
 // ACK is generated, that the response has an address and that address
 // really belongs to the configured pool.
@@ -829,6 +776,30 @@ TEST_F(Dhcpv4SrvTest, ManyRequests) {
     cout << "Offered address to client3=" << addr3.toText() << endl;
 }
 
+// Checks whether echoing back client-id is controllable
+TEST_F(Dhcpv4SrvTest, requestEchoClientId) {
+    NakedDhcpv4Srv srv(0);
+
+    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+    dis->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    dis->addOption(clientid);
+
+    // Pass it to the server and get ACK
+    Pkt4Ptr ack = srv.processRequest(dis);
+
+    // Check if we get response at all
+    checkResponse(ack, DHCPACK, 1234);
+    checkClientId(ack, clientid);
+
+    CfgMgr::instance().echoClientId(false);
+    ack = srv.processDiscover(dis);
+
+    // Check if we get response at all
+    checkResponse(ack, DHCPOFFER, 1234);
+    checkClientId(ack, clientid);
+}
+
 
 // This test verifies that incoming (positive) REQUEST/Renewing can be handled properly, that a
 // REPLY is generated, that the response has an address and that address
@@ -983,7 +954,7 @@ TEST_F(Dhcpv4SrvTest, ReleaseBasic) {
     // Generate client-id also duid_
     Pkt4Ptr rel = Pkt4Ptr(new Pkt4(DHCPRELEASE, 1234));
     rel->setRemoteAddr(addr);
-    rel->setYiaddr(addr);
+    rel->setCiaddr(addr);
     rel->addOption(clientid);
     rel->addOption(srv->getServerID());
     rel->setHWAddr(hw);
@@ -1046,7 +1017,7 @@ TEST_F(Dhcpv4SrvTest, ReleaseReject) {
     // Generate client-id also duid_
     Pkt4Ptr rel = Pkt4Ptr(new Pkt4(DHCPRELEASE, 1234));
     rel->setRemoteAddr(addr);
-    rel->setYiaddr(addr);
+    rel->setCiaddr(addr);
     rel->addOption(clientid);
     rel->addOption(srv->getServerID());
     rel->setHWAddr(bogus_hw);
@@ -1138,11 +1109,7 @@ TEST_F(Dhcpv4SrvTest, ServerID) {
     EXPECT_EQ(srvid_text, text);
 }
 
-// Checks if callouts installed on pkt4_receive are indeed called and the
-// all necessary parameters are passed.
-//
-// Note that the test name does not follow test naming convention,
-// but the proper hook name is "buffer4_receive".
+// Checks if received relay agent info option is echoed back to the client
 TEST_F(Dhcpv4SrvTest, relayAgentInfoEcho) {
 
     NakedDhcpv4Srv srv(0);
@@ -1180,6 +1147,91 @@ TEST_F(Dhcpv4SrvTest, relayAgentInfoEcho) {
     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
@@ -2650,7 +2702,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSimple) {
     // Generate client-id also duid_
     Pkt4Ptr rel = Pkt4Ptr(new Pkt4(DHCPRELEASE, 1234));
     rel->setRemoteAddr(addr);
-    rel->setYiaddr(addr);
+    rel->setCiaddr(addr);
     rel->addOption(clientid);
     rel->addOption(srv_->getServerID());
     rel->setHWAddr(hw);
@@ -2763,6 +2815,188 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSkip) {
     //EXPECT_EQ(leases.size(), 1);
 }
 
-}; // end of isc::dhcp::test 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
+}; // end of isc namespace */
diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.cc b/src/bin/dhcp4/tests/dhcp4_test_utils.cc
index 56e96f1..8590927 100644
--- a/src/bin/dhcp4/tests/dhcp4_test_utils.cc
+++ b/src/bin/dhcp4/tests/dhcp4_test_utils.cc
@@ -44,6 +44,7 @@ Dhcpv4SrvTest::Dhcpv4SrvTest()
     pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"), IOAddress("192.0.2.110")));
     subnet_->addPool(pool_);
 
+    CfgMgr::instance().deleteActiveIfaces();
     CfgMgr::instance().deleteSubnets4();
     CfgMgr::instance().addSubnet4(subnet_);
 
@@ -66,6 +67,12 @@ Dhcpv4SrvTest::Dhcpv4SrvTest()
     valid_iface_ = ifaces.begin()->getName();
 }
 
+Dhcpv4SrvTest::~Dhcpv4SrvTest() {
+
+    // Make sure that we revert to default value
+    CfgMgr::instance().echoClientId(true);
+}
+
 void Dhcpv4SrvTest::addPrlOption(Pkt4Ptr& pkt) {
 
     OptionUint8ArrayPtr option_prl =
@@ -331,12 +338,21 @@ void Dhcpv4SrvTest::checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_
 }
 
 void Dhcpv4SrvTest::checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid) {
+
+    bool include_clientid = CfgMgr::instance().echoClientId();
+
     // 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());
+    if (include_clientid) {
+        // Normal mode: echo back (see RFC6842)
+        ASSERT_TRUE(opt);
+        EXPECT_EQ(expected_clientid->getType(), opt->getType());
+        EXPECT_EQ(expected_clientid->len(), opt->len());
+        EXPECT_TRUE(expected_clientid->getData() == opt->getData());
+    } else {
+        // Backward compatibility mode for pre-RFC6842 devices
+        ASSERT_FALSE(opt);
+    }
 }
 
 ::testing::AssertionResult
diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h
index ee19728..26ed87d 100644
--- a/src/bin/dhcp4/tests/dhcp4_test_utils.h
+++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h
@@ -20,6 +20,7 @@
 #define DHCP4_TEST_UTILS_H
 
 #include <gtest/gtest.h>
+#include <dhcp/iface_mgr.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt_filter.h>
 #include <dhcp/pkt_filter_inet.h>
@@ -52,9 +53,10 @@ public:
     }
 
     /// Does nothing.
-    virtual int openSocket(const Iface&, const isc::asiolink::IOAddress&,
-                           const uint16_t, const bool, const bool) {
-        return (0);
+    virtual SocketInfo openSocket(const Iface&,
+                                  const isc::asiolink::IOAddress& addr,
+                                  const uint16_t port, const bool, const bool) {
+        return (SocketInfo(addr, port, 0));
     }
 
     /// Does nothing.
@@ -79,8 +81,7 @@ public:
     Dhcpv4SrvTest();
 
     /// @brief destructor
-    virtual ~Dhcpv4SrvTest() {
-    }
+    virtual ~Dhcpv4SrvTest();
 
     /// @brief Add 'Parameter Request List' option to the packet.
     ///
@@ -187,6 +188,11 @@ public:
     void checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid);
 
     /// @brief Checks if server response (OFFER, ACK, NAK) includes proper client-id
+    ///
+    /// This method follows values reported by CfgMgr in echoClientId() method.
+    /// Depending on its configuration, the client-id is either mandatory or
+    /// forbidden to appear in the response.
+    ///
     /// @param rsp response packet to be validated
     /// @param expected_clientid expected value of client-id
     void checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid);
@@ -357,6 +363,9 @@ public:
     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;
@@ -364,6 +373,7 @@ public:
     using Dhcpv4Srv::sanityCheck;
     using Dhcpv4Srv::srvidToString;
     using Dhcpv4Srv::unpackOptions;
+    using Dhcpv4Srv::name_change_reqs_;
 };
 
 }; // end of isc::dhcp::test namespace
diff --git a/src/bin/dhcp4/tests/fqdn_unittest.cc b/src/bin/dhcp4/tests/fqdn_unittest.cc
new file mode 100644
index 0000000..20fe5d5
--- /dev/null
+++ b/src/bin/dhcp4/tests/fqdn_unittest.cc
@@ -0,0 +1,769 @@
+// 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 <dhcp/option4_client_fqdn.h>
+#include <dhcp/option_int_array.h>
+#include <dhcp4/tests/dhcp4_test_utils.h>
+#include <dhcp_ddns/ncr_msg.h>
+
+#include <gtest/gtest.h>
+#include <boost/scoped_ptr.hpp>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace isc::dhcp_ddns;
+
+namespace {
+
+class NameDhcpv4SrvTest : public Dhcpv4SrvTest {
+public:
+    NameDhcpv4SrvTest() : Dhcpv4SrvTest() {
+        srv_ = new NakedDhcpv4Srv(0);
+    }
+    virtual ~NameDhcpv4SrvTest() {
+        delete srv_;
+    }
+
+    // Create a lease to be used by various tests.
+    Lease4Ptr createLease(const isc::asiolink::IOAddress& addr,
+                          const std::string& hostname,
+                          const bool fqdn_fwd,
+                          const bool fqdn_rev) {
+        const uint8_t hwaddr[] = { 0, 1, 2, 3, 4, 5, 6 };
+        Lease4Ptr lease(new Lease4(addr, hwaddr, sizeof(hwaddr),
+                                   &generateClientId()->getData()[0],
+                                   generateClientId()->getData().size(),
+                                   100, 50, 75, time(NULL), subnet_->getID()));
+        // @todo Set this through the Lease4 constructor.
+        lease->hostname_ = hostname;
+        lease->fqdn_fwd_ = fqdn_fwd;
+        lease->fqdn_rev_ = fqdn_rev;
+
+        return (lease);
+    }
+
+    // Create an instance of the DHCPv4 Client FQDN Option.
+    Option4ClientFqdnPtr
+    createClientFqdn(const uint8_t flags,
+                     const std::string& fqdn_name,
+                     Option4ClientFqdn::DomainNameType fqdn_type) {
+        return (Option4ClientFqdnPtr(new Option4ClientFqdn(flags,
+                                                           Option4ClientFqdn::
+                                                           RCODE_CLIENT(),
+                                                           fqdn_name,
+                                                           fqdn_type)));
+   }
+
+    // Create an instance of the Hostname option.
+    OptionCustomPtr
+    createHostname(const std::string& hostname) {
+        OptionDefinition def("hostname", DHO_HOST_NAME, "string");
+        OptionCustomPtr opt_hostname(new OptionCustom(def, Option::V4));
+        opt_hostname->writeString(hostname);
+        return (opt_hostname);
+    }
+
+    // Generates partial hostname from the address. The format of the
+    // generated address is: host-A-B-C-D, where A.B.C.D is an IP
+    // address.
+    std::string generatedNameFromAddress(const IOAddress& addr) {
+        std::string gen_name = addr.toText();
+        std::replace(gen_name.begin(), gen_name.end(), '.', '-');
+        std::ostringstream hostname;
+        hostname << "host-" << gen_name;
+        return (hostname.str());
+    }
+
+    // Get the Client FQDN Option from the given message.
+    Option4ClientFqdnPtr getClientFqdnOption(const Pkt4Ptr& pkt) {
+        return (boost::dynamic_pointer_cast<
+                Option4ClientFqdn>(pkt->getOption(DHO_FQDN)));
+    }
+
+    // get the Hostname option from the given message.
+    OptionCustomPtr getHostnameOption(const Pkt4Ptr& pkt) {
+        return (boost::dynamic_pointer_cast<
+                OptionCustom>(pkt->getOption(DHO_HOST_NAME)));
+    }
+
+    // Create a message holding DHCPv4 Client FQDN Option.
+    Pkt4Ptr generatePktWithFqdn(const uint8_t msg_type,
+                                const uint8_t fqdn_flags,
+                                const std::string& fqdn_domain_name,
+                                Option4ClientFqdn::DomainNameType fqdn_type,
+                                const bool include_prl,
+                                const bool include_clientid = true) {
+        Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(msg_type, 1234));
+        pkt->setRemoteAddr(IOAddress("192.0.2.3"));
+        // For DISCOVER we don't include server id, because client broadcasts
+        // the message to all servers.
+        if (msg_type != DHCPDISCOVER) {
+            pkt->addOption(srv_->getServerID());
+        }
+
+        if (include_clientid) {
+            pkt->addOption(generateClientId());
+        }
+
+        // Create Client FQDN Option with the specified flags and
+        // domain-name.
+        pkt->addOption(createClientFqdn(fqdn_flags, fqdn_domain_name,
+                                        fqdn_type));
+
+        // Control whether or not to request that server returns the FQDN
+        // option. Server may be configured to always return it or return
+        // only in case client requested it.
+        if (include_prl) {
+            OptionUint8ArrayPtr option_prl =
+                OptionUint8ArrayPtr(new OptionUint8Array(Option::V4,
+                                    DHO_DHCP_PARAMETER_REQUEST_LIST));
+            option_prl->addValue(DHO_FQDN);
+        }
+        return (pkt);
+    }
+
+    // Create a message holding a Hostname option.
+    Pkt4Ptr generatePktWithHostname(const uint8_t msg_type,
+                                    const std::string& hostname) {
+
+        Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(msg_type, 1234));
+        pkt->setRemoteAddr(IOAddress("192.0.2.3"));
+        // For DISCOVER we don't include server id, because client broadcasts
+        // the message to all servers.
+        if (msg_type != DHCPDISCOVER) {
+            pkt->addOption(srv_->getServerID());
+        }
+
+        pkt->addOption(generateClientId());
+
+
+        // Create Client FQDN Option with the specified flags and
+        // domain-name.
+        pkt->addOption(createHostname(hostname));
+
+        return (pkt);
+
+    }
+
+    // Test that server generates the appropriate FQDN option in response to
+    // client's FQDN option.
+    void testProcessFqdn(const Pkt4Ptr& query, const uint8_t exp_flags,
+                         const std::string& exp_domain_name,
+                         Option4ClientFqdn::DomainNameType
+                         exp_domain_type = Option4ClientFqdn::FULL) {
+        ASSERT_TRUE(getClientFqdnOption(query));
+
+        Pkt4Ptr answer;
+        if (query->getType() == DHCPDISCOVER) {
+            answer.reset(new Pkt4(DHCPOFFER, 1234));
+
+        } else {
+            answer.reset(new Pkt4(DHCPACK, 1234));
+
+        }
+        ASSERT_NO_THROW(srv_->processClientName(query, answer));
+
+        Option4ClientFqdnPtr fqdn = getClientFqdnOption(answer);
+        ASSERT_TRUE(fqdn);
+
+        const bool flag_n = (exp_flags & Option4ClientFqdn::FLAG_N) != 0;
+        const bool flag_s = (exp_flags & Option4ClientFqdn::FLAG_S) != 0;
+        const bool flag_o = (exp_flags & Option4ClientFqdn::FLAG_O) != 0;
+        const bool flag_e = (exp_flags & Option4ClientFqdn::FLAG_E) != 0;
+
+        EXPECT_EQ(flag_n, fqdn->getFlag(Option4ClientFqdn::FLAG_N));
+        EXPECT_EQ(flag_s, fqdn->getFlag(Option4ClientFqdn::FLAG_S));
+        EXPECT_EQ(flag_o, fqdn->getFlag(Option4ClientFqdn::FLAG_O));
+        EXPECT_EQ(flag_e, fqdn->getFlag(Option4ClientFqdn::FLAG_E));
+
+        EXPECT_EQ(exp_domain_name, fqdn->getDomainName());
+        EXPECT_EQ(exp_domain_type, fqdn->getDomainNameType());
+
+    }
+
+    // Processes the Hostname option in the client's message and returns
+    // the hostname option which would be sent to the client. It will
+    // throw NULL pointer if the hostname option is not to be included
+    // in the response.
+    OptionCustomPtr processHostname(const Pkt4Ptr& query) {
+        if (!getHostnameOption(query)) {
+            ADD_FAILURE() << "Hostname option not carried in the query";
+        }
+
+        Pkt4Ptr answer;
+        if (query->getType() == DHCPDISCOVER) {
+            answer.reset(new Pkt4(DHCPOFFER, 1234));
+
+        } else {
+            answer.reset(new Pkt4(DHCPACK, 1234));
+
+        }
+        srv_->processClientName(query, answer);
+
+        OptionCustomPtr hostname = getHostnameOption(answer);
+        return (hostname);
+
+    }
+
+    // Test that the client message holding an FQDN is processed and the
+    // NameChangeRequests are generated.
+    void testProcessMessageWithFqdn(const uint8_t msg_type,
+                            const std::string& hostname) {
+        Pkt4Ptr req = generatePktWithFqdn(msg_type, Option4ClientFqdn::FLAG_S |
+                                          Option4ClientFqdn::FLAG_E, hostname,
+                                          Option4ClientFqdn::FULL, true);
+        Pkt4Ptr reply;
+        if (msg_type == DHCPDISCOVER) {
+            ASSERT_NO_THROW(reply = srv_->processDiscover(req));
+
+        } else if (msg_type == DHCPREQUEST) {
+            ASSERT_NO_THROW(reply = srv_->processRequest(req));
+
+        } else if (msg_type == DHCPRELEASE) {
+            ASSERT_NO_THROW(srv_->processRelease(req));
+            return;
+
+        } else {
+            return;
+        }
+
+        if (msg_type == DHCPDISCOVER) {
+            checkResponse(reply, DHCPOFFER, 1234);
+
+        } else {
+            checkResponse(reply, DHCPACK, 1234);
+        }
+
+    }
+
+    // Verify that NameChangeRequest holds valid values.
+    void verifyNameChangeRequest(const isc::dhcp_ddns::NameChangeType type,
+                                 const bool reverse, const bool forward,
+                                 const std::string& addr,
+                                 const std::string& fqdn,
+                                 const std::string& dhcid,
+                                 const time_t cltt,
+                                 const uint16_t len,
+                                 const bool not_strict_expire_check = false) {
+        NameChangeRequest ncr = srv_->name_change_reqs_.front();
+        EXPECT_EQ(type, ncr.getChangeType());
+        EXPECT_EQ(forward, ncr.isForwardChange());
+        EXPECT_EQ(reverse, ncr.isReverseChange());
+        EXPECT_EQ(addr, ncr.getIpAddress());
+        EXPECT_EQ(fqdn, ncr.getFqdn());
+        // Compare dhcid if it is not empty. In some cases, the DHCID is
+        // not known in advance and can't be compared.
+        if (!dhcid.empty()) {
+            EXPECT_EQ(dhcid, ncr.getDhcid().toStr());
+        }
+        // In some cases, the test doesn't have access to the last transmission
+        // time for the particular client. In such cases, the test can use the
+        // current time as cltt but the it may not check the lease expiration time
+        // for equality but rather check that the lease expiration time is not
+        // greater than the current time + lease lifetime.
+        if (not_strict_expire_check) {
+            EXPECT_GE(cltt + len, ncr.getLeaseExpiresOn());
+        } else {
+            EXPECT_EQ(cltt + len, ncr.getLeaseExpiresOn());
+        }
+        EXPECT_EQ(len, ncr.getLeaseLength());
+        EXPECT_EQ(isc::dhcp_ddns::ST_NEW, ncr.getStatus());
+        srv_->name_change_reqs_.pop();
+    }
+
+    NakedDhcpv4Srv* srv_;
+
+};
+
+// Test that the exception is thrown if lease pointer specified as the argument
+// of computeDhcid function is NULL.
+TEST_F(NameDhcpv4SrvTest, dhcidNullLease) {
+    Lease4Ptr lease;
+    EXPECT_THROW(srv_->computeDhcid(lease), isc::dhcp::DhcidComputeError);
+
+}
+
+// Test that the appropriate exception is thrown if the lease object used
+// to compute DHCID comprises wrong hostname.
+TEST_F(NameDhcpv4SrvTest, dhcidWrongHostname) {
+    // First, make sure that the lease with the correct hostname is accepted.
+    Lease4Ptr lease = createLease(IOAddress("192.0.2.3"),
+                                  "myhost.example.com.", true, true);
+    ASSERT_NO_THROW(srv_->computeDhcid(lease));
+
+    // Now, use the wrong hostname. It should result in the exception.
+    lease->hostname_ = "myhost...example.com.";
+    EXPECT_THROW(srv_->computeDhcid(lease), isc::dhcp::DhcidComputeError);
+
+    // Also, empty hostname is wrong.
+    lease->hostname_ = "";
+    EXPECT_THROW(srv_->computeDhcid(lease), isc::dhcp::DhcidComputeError);
+}
+
+// Test that the DHCID is computed correctly, when the lease holds
+// correct hostname and non-NULL client id.
+TEST_F(NameDhcpv4SrvTest, dhcidComputeFromClientId) {
+    Lease4Ptr lease = createLease(IOAddress("192.0.2.3"),
+                                  "myhost.example.com.",
+                                  true, true);
+    isc::dhcp_ddns::D2Dhcid dhcid;
+    ASSERT_NO_THROW(dhcid = srv_->computeDhcid(lease));
+
+    // Make sure that the computed DHCID is valid.
+    std::string dhcid_ref = "00010132E91AA355CFBB753C0F0497A5A9404"
+        "36965B68B6D438D98E680BF10B09F3BCF";
+    EXPECT_EQ(dhcid_ref, dhcid.toStr());
+}
+
+// Test that the DHCID is computed correctly, when the lease holds correct
+// hostname and NULL client id.
+TEST_F(NameDhcpv4SrvTest, dhcidComputeFromHWAddr) {
+    Lease4Ptr lease = createLease(IOAddress("192.0.2.3"),
+                                  "myhost.example.com.",
+                                  true, true);
+    lease->client_id_.reset();
+
+    isc::dhcp_ddns::D2Dhcid dhcid;
+    ASSERT_NO_THROW(dhcid = srv_->computeDhcid(lease));
+
+    // Make sure that the computed DHCID is valid.
+    std::string dhcid_ref = "0000012247F6DC4423C3E8627434A9D6868609"
+        "D88948F78018B215EDCAA30C0C135035";
+    EXPECT_EQ(dhcid_ref, dhcid.toStr());
+}
+
+
+// Test that server confirms to perform the forward and reverse DNS update,
+// when client asks for it.
+TEST_F(NameDhcpv4SrvTest, serverUpdateForwardFqdn) {
+    Pkt4Ptr query = generatePktWithFqdn(DHCPREQUEST,
+                                        Option4ClientFqdn::FLAG_E |
+                                        Option4ClientFqdn::FLAG_S,
+                                        "myhost.example.com.",
+                                        Option4ClientFqdn::FULL,
+                                        true);
+
+    testProcessFqdn(query,
+                    Option4ClientFqdn::FLAG_E | Option4ClientFqdn::FLAG_S,
+                    "myhost.example.com.");
+
+}
+
+// Test that server processes the Hostname option sent by a client and
+// responds with the Hostname option to confirm that the server has
+// taken responsibility for the update.
+TEST_F(NameDhcpv4SrvTest, serverUpdateHostname) {
+    Pkt4Ptr query;
+    ASSERT_NO_THROW(query = generatePktWithHostname(DHCPREQUEST,
+                                                    "myhost.example.com."));
+    OptionCustomPtr hostname;
+    ASSERT_NO_THROW(hostname = processHostname(query));
+
+    ASSERT_TRUE(hostname);
+    EXPECT_EQ("myhost.example.com.", hostname->readString());
+
+}
+
+// Test that the server skips processing of the empty Hostname option.
+TEST_F(NameDhcpv4SrvTest, serverUpdateEmptyHostname) {
+    Pkt4Ptr query;
+    ASSERT_NO_THROW(query = generatePktWithHostname(DHCPREQUEST, ""));
+    OptionCustomPtr hostname;
+    ASSERT_NO_THROW(hostname = processHostname(query));
+    EXPECT_FALSE(hostname);
+}
+
+// Test that the server skips processing of a wrong Hostname option.
+TEST_F(NameDhcpv4SrvTest, serverUpdateWrongHostname) {
+    Pkt4Ptr query;
+    ASSERT_NO_THROW(query = generatePktWithHostname(DHCPREQUEST,
+                                                    "abc..example.com"));
+    OptionCustomPtr hostname;
+    ASSERT_NO_THROW(hostname = processHostname(query));
+    EXPECT_FALSE(hostname);
+}
+
+
+// Test that server generates the fully qualified domain name for the client
+// if client supplies the partial name.
+TEST_F(NameDhcpv4SrvTest, serverUpdateForwardPartialNameFqdn) {
+    Pkt4Ptr query = generatePktWithFqdn(DHCPREQUEST,
+                                        Option4ClientFqdn::FLAG_E |
+                                        Option4ClientFqdn::FLAG_S,
+                                        "myhost",
+                                        Option4ClientFqdn::PARTIAL,
+                                        true);
+
+    testProcessFqdn(query,
+                    Option4ClientFqdn::FLAG_E | Option4ClientFqdn::FLAG_S,
+                    "myhost.example.com.");
+
+}
+
+// Test that server generates the fully qualified domain name for the client
+// if client supplies the unqualified name in the Hostname option.
+TEST_F(NameDhcpv4SrvTest, serverUpdateUnqualifiedHostname) {
+    Pkt4Ptr query;
+    ASSERT_NO_THROW(query = generatePktWithHostname(DHCPREQUEST, "myhost"));
+    OptionCustomPtr hostname;
+    ASSERT_NO_THROW(hostname =  processHostname(query));
+
+    ASSERT_TRUE(hostname);
+    EXPECT_EQ("myhost.example.com.", hostname->readString());
+
+}
+
+// Test that server sets empty domain-name in the FQDN option when client
+// supplied no domain-name. The domain-name is supposed to be set after the
+// lease is acquired. The domain-name is then generated from the IP address
+// assigned to a client.
+TEST_F(NameDhcpv4SrvTest, serverUpdateForwardNoNameFqdn) {
+    Pkt4Ptr query = generatePktWithFqdn(DHCPREQUEST,
+                                        Option4ClientFqdn::FLAG_E |
+                                        Option4ClientFqdn::FLAG_S,
+                                        "",
+                                        Option4ClientFqdn::PARTIAL,
+                                        true);
+
+    testProcessFqdn(query,
+                    Option4ClientFqdn::FLAG_E | Option4ClientFqdn::FLAG_S,
+                    "", Option4ClientFqdn::PARTIAL);
+
+}
+
+// Test server's response when client requests no DNS update.
+TEST_F(NameDhcpv4SrvTest, noUpdateFqdn) {
+    Pkt4Ptr query = generatePktWithFqdn(DHCPREQUEST,
+                                        Option4ClientFqdn::FLAG_E |
+                                        Option4ClientFqdn::FLAG_N,
+                                        "myhost.example.com.",
+                                        Option4ClientFqdn::FULL,
+                                        true);
+    testProcessFqdn(query, Option4ClientFqdn::FLAG_E |
+                    Option4ClientFqdn::FLAG_N,
+                    "myhost.example.com.");
+}
+
+// Test that server does not accept delegation of the forward DNS update
+// to a client.
+TEST_F(NameDhcpv4SrvTest, clientUpdateNotAllowedFqdn) {
+    Pkt4Ptr query = generatePktWithFqdn(DHCPREQUEST,
+                                        Option4ClientFqdn::FLAG_E,
+                                        "myhost.example.com.",
+                                        Option4ClientFqdn::FULL,
+                                        true);
+
+    testProcessFqdn(query, Option4ClientFqdn::FLAG_E |
+                    Option4ClientFqdn::FLAG_S | Option4ClientFqdn::FLAG_O,
+                    "myhost.example.com.");
+
+}
+
+// Test that exactly one NameChangeRequest is generated when the new lease
+// has been acquired (old lease is NULL).
+TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsNewLease) {
+    Lease4Ptr lease = createLease(IOAddress("192.0.2.3"), "myhost.example.com.",
+                                  true, true);
+    Lease4Ptr old_lease;
+
+    ASSERT_NO_THROW(srv_->createNameChangeRequests(lease, old_lease));
+    ASSERT_EQ(1, srv_->name_change_reqs_.size());
+
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+                            "192.0.2.3", "myhost.example.com.",
+                            "00010132E91AA355CFBB753C0F0497A5A940436965"
+                            "B68B6D438D98E680BF10B09F3BCF",
+                            lease->cltt_, 100);
+}
+
+// Test that no NameChangeRequest is generated when a lease is renewed and
+// the FQDN data hasn't changed.
+TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsRenewNoChange) {
+    Lease4Ptr lease = createLease(IOAddress("192.0.2.3"), "myhost.example.com.",
+                                  true, true);
+    Lease4Ptr old_lease = createLease(IOAddress("192.0.2.3"),
+                                      "myhost.example.com.", true, true);
+    old_lease->valid_lft_ += 100;
+
+    ASSERT_NO_THROW(srv_->createNameChangeRequests(lease, old_lease));
+    EXPECT_TRUE(srv_->name_change_reqs_.empty());
+}
+
+// Test that no NameChangeRequest is generated when forward and reverse
+// DNS update flags are not set in the lease.
+TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsNoUpdate) {
+    Lease4Ptr lease1 = createLease(IOAddress("192.0.2.3"),
+                                   "lease1.example.com.",
+                                   true, true);
+    Lease4Ptr lease2 = createLease(IOAddress("192.0.2.3"),
+                                   "lease2.example.com.",
+                                   false, false);
+    ASSERT_NO_THROW(srv_->createNameChangeRequests(lease2, lease1));
+    EXPECT_EQ(1, srv_->name_change_reqs_.size());
+
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
+                            "192.0.2.3", "lease1.example.com.",
+                            "0001013A5B311F5B9FB10DDF8E53689B874F25D"
+                            "62CC147C2FF237A64C90E5A597C9B7A",
+                            lease1->cltt_, 100);
+
+    lease2->hostname_ = "";
+    lease2->fqdn_rev_ = true;
+    lease2->fqdn_fwd_ = true;
+    ASSERT_NO_THROW(srv_->createNameChangeRequests(lease2, lease1));
+    EXPECT_EQ(1, srv_->name_change_reqs_.size());
+
+}
+
+// Test that two NameChangeRequests are generated when the lease is being
+// renewed and the new lease has updated FQDN data.
+TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsRenew) {
+    Lease4Ptr lease1 = createLease(IOAddress("192.0.2.3"),
+                                   "lease1.example.com.",
+                                   true, true);
+    Lease4Ptr lease2 = createLease(IOAddress("192.0.2.3"),
+                                   "lease2.example.com.",
+                                   true, true);
+    ASSERT_NO_THROW(srv_->createNameChangeRequests(lease2, lease1));
+    ASSERT_EQ(2, srv_->name_change_reqs_.size());
+
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
+                            "192.0.2.3", "lease1.example.com.",
+                            "0001013A5B311F5B9FB10DDF8E53689B874F25D"
+                            "62CC147C2FF237A64C90E5A597C9B7A",
+                            lease1->cltt_, 100);
+
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+                            "192.0.2.3", "lease2.example.com.",
+                            "000101F906D2BB752E1B2EECC5FF2BF434C0B2D"
+                            "D6D7F7BD873F4F280165DB8C9DBA7CB",
+                            lease2->cltt_, 100);
+
+}
+
+// This test verifies that exception is thrown when leases passed to the
+// createNameChangeRequests function do not match, i.e. they comprise
+// different IP addresses, client ids etc.
+TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsLeaseMismatch) {
+    Lease4Ptr lease1 = createLease(IOAddress("192.0.2.3"),
+                                   "lease1.example.com.",
+                                   true, true);
+    Lease4Ptr lease2 = createLease(IOAddress("192.0.2.4"),
+                                   "lease2.example.com.",
+                                   true, true);
+    EXPECT_THROW(srv_->createNameChangeRequests(lease2, lease1),
+                 isc::Unexpected);
+}
+
+// Test that the OFFER message generated as a result of the DISCOVER message
+// processing will not result in generation of the NameChangeRequests.
+TEST_F(NameDhcpv4SrvTest, processDiscover) {
+    Pkt4Ptr req = generatePktWithFqdn(DHCPDISCOVER, Option4ClientFqdn::FLAG_S |
+                                      Option4ClientFqdn::FLAG_E,
+                                      "myhost.example.com.",
+                                      Option4ClientFqdn::FULL, true);
+
+    Pkt4Ptr reply;
+    ASSERT_NO_THROW(reply = srv_->processDiscover(req));
+
+    checkResponse(reply, DHCPOFFER, 1234);
+
+    EXPECT_TRUE(srv_->name_change_reqs_.empty());
+}
+
+// Test that server generates client's hostname from the IP address assigned
+// to it when DHCPv4 Client FQDN option specifies an empty domain-name.
+TEST_F(NameDhcpv4SrvTest, processRequestFqdnEmptyDomainName) {
+    Pkt4Ptr req = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S |
+                                      Option4ClientFqdn::FLAG_E,
+                                      "", Option4ClientFqdn::PARTIAL, true);
+
+    Pkt4Ptr reply;
+    ASSERT_NO_THROW(reply = srv_->processRequest(req));
+
+    checkResponse(reply, DHCPACK, 1234);
+
+    // Verify that there is one NameChangeRequest generated.
+    ASSERT_EQ(1, srv_->name_change_reqs_.size());
+    // The hostname is generated from the IP address acquired (yiaddr).
+    std::ostringstream hostname;
+    hostname << generatedNameFromAddress(reply->getYiaddr())
+             << ".example.com.";
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+                            reply->getYiaddr().toText(), hostname.str(),
+                            "", // empty DHCID forces that it is not checked
+                            time(NULL) + subnet_->getValid(),
+                            subnet_->getValid(), true);
+}
+
+// Test that server generates client's hostname from the IP address assigned
+// to it when Hostname option carries the top level domain-name.
+TEST_F(NameDhcpv4SrvTest, processRequestEmptyHostname) {
+    Pkt4Ptr req = generatePktWithHostname(DHCPREQUEST, ".");
+
+    Pkt4Ptr reply;
+    ASSERT_NO_THROW(reply = srv_->processRequest(req));
+
+    checkResponse(reply, DHCPACK, 1234);
+
+    // Verify that there is one NameChangeRequest generated.
+    ASSERT_EQ(1, srv_->name_change_reqs_.size());
+    // The hostname is generated from the IP address acquired (yiaddr).
+    std::ostringstream hostname;
+    hostname << generatedNameFromAddress(reply->getYiaddr()) << ".example.com.";
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+                            reply->getYiaddr().toText(), hostname.str(),
+                            "", // empty DHCID forces that it is not checked
+                            time(NULL), subnet_->getValid(), true);
+}
+
+// Test that client may send two requests, each carrying FQDN option with
+// a different domain-name. Server should use existing lease for the second
+// request but modify the DNS entries for the lease according to the contents
+// of the FQDN sent in the second request.
+TEST_F(NameDhcpv4SrvTest, processTwoRequestsFqdn) {
+    Pkt4Ptr req1 = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S |
+                                       Option4ClientFqdn::FLAG_E,
+                                       "myhost.example.com.",
+                                       Option4ClientFqdn::FULL, true);
+
+    Pkt4Ptr reply;
+    ASSERT_NO_THROW(reply = srv_->processRequest(req1));
+
+    checkResponse(reply, DHCPACK, 1234);
+
+    // Verify that there is one NameChangeRequest generated.
+    ASSERT_EQ(1, srv_->name_change_reqs_.size());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+                            reply->getYiaddr().toText(), "myhost.example.com.",
+                            "00010132E91AA355CFBB753C0F0497A5A940436"
+                            "965B68B6D438D98E680BF10B09F3BCF",
+                            time(NULL), subnet_->getValid(), true);
+
+    // Create another Request message but with a different FQDN. Server
+    // should generate two NameChangeRequests: one to remove existing entry,
+    // another one to add new entry with updated domain-name.
+    Pkt4Ptr req2 = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S |
+                                       Option4ClientFqdn::FLAG_E,
+                                       "otherhost.example.com.",
+                                       Option4ClientFqdn::FULL, true);
+
+    ASSERT_NO_THROW(reply = srv_->processRequest(req2));
+
+    checkResponse(reply, DHCPACK, 1234);
+
+    // There should be two NameChangeRequests. Verify that they are valid.
+    ASSERT_EQ(2, srv_->name_change_reqs_.size());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
+                            reply->getYiaddr().toText(),
+                            "myhost.example.com.",
+                            "00010132E91AA355CFBB753C0F0497A5A940436"
+                            "965B68B6D438D98E680BF10B09F3BCF",
+                            time(NULL), subnet_->getValid(), true);
+
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+                            reply->getYiaddr().toText(),
+                            "otherhost.example.com.",
+                            "000101A5AEEA7498BD5AD9D3BF600E49FF39A7E3"
+                            "AFDCE8C3D0E53F35CC584DD63C89CA",
+                            time(NULL), subnet_->getValid(), true);
+}
+
+// Test that client may send two requests, each carrying Hostname option with
+// a different name. Server should use existing lease for the second request
+// but modify the DNS entries for the lease according to the contents of the
+// Hostname sent in the second request.
+TEST_F(NameDhcpv4SrvTest, processTwoRequestsHostname) {
+    Pkt4Ptr req1 = generatePktWithHostname(DHCPREQUEST, "myhost.example.com.");
+
+    Pkt4Ptr reply;
+    ASSERT_NO_THROW(reply = srv_->processRequest(req1));
+
+    checkResponse(reply, DHCPACK, 1234);
+
+    // Verify that there is one NameChangeRequest generated.
+    ASSERT_EQ(1, srv_->name_change_reqs_.size());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+                            reply->getYiaddr().toText(), "myhost.example.com.",
+                            "00010132E91AA355CFBB753C0F0497A5A940436"
+                            "965B68B6D438D98E680BF10B09F3BCF",
+                            time(NULL), subnet_->getValid(), true);
+
+    // Create another Request message but with a different Hostname. Server
+    // should generate two NameChangeRequests: one to remove existing entry,
+    // another one to add new entry with updated domain-name.
+    Pkt4Ptr req2 = generatePktWithHostname(DHCPREQUEST, "otherhost");
+
+    ASSERT_NO_THROW(reply = srv_->processRequest(req2));
+
+    checkResponse(reply, DHCPACK, 1234);
+
+    // There should be two NameChangeRequests. Verify that they are valid.
+    ASSERT_EQ(2, srv_->name_change_reqs_.size());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
+                            reply->getYiaddr().toText(),
+                            "myhost.example.com.",
+                            "00010132E91AA355CFBB753C0F0497A5A940436"
+                            "965B68B6D438D98E680BF10B09F3BCF",
+                            time(NULL), subnet_->getValid(), true);
+
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+                            reply->getYiaddr().toText(),
+                            "otherhost.example.com.",
+                            "000101A5AEEA7498BD5AD9D3BF600E49FF39A7E3"
+                            "AFDCE8C3D0E53F35CC584DD63C89CA",
+                            time(NULL), subnet_->getValid(), true);
+}
+
+// Test that when the Release message is sent for the previously acquired
+// lease, then server genenerates a NameChangeRequest to remove the entries
+// corresponding to the lease being released.
+TEST_F(NameDhcpv4SrvTest, processRequestRelease) {
+    Pkt4Ptr req = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S |
+                                      Option4ClientFqdn::FLAG_E,
+                                      "myhost.example.com.",
+                                      Option4ClientFqdn::FULL, true);
+
+    Pkt4Ptr reply;
+    ASSERT_NO_THROW(reply = srv_->processRequest(req));
+
+    checkResponse(reply, DHCPACK, 1234);
+
+    // Verify that there is one NameChangeRequest generated.
+    ASSERT_EQ(1, srv_->name_change_reqs_.size());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+                            reply->getYiaddr().toText(), "myhost.example.com.",
+                            "00010132E91AA355CFBB753C0F0497A5A940436"
+                            "965B68B6D438D98E680BF10B09F3BCF",
+                            time(NULL), subnet_->getValid(), true);
+
+    // Create a Release message.
+    Pkt4Ptr rel = Pkt4Ptr(new Pkt4(DHCPRELEASE, 1234));
+    rel->setCiaddr(reply->getYiaddr());
+    rel->setRemoteAddr(IOAddress("192.0.2.3"));
+    rel->addOption(generateClientId());
+    rel->addOption(srv_->getServerID());
+
+    ASSERT_NO_THROW(srv_->processRelease(rel));
+
+    // The lease has been removed, so there should be a NameChangeRequest to
+    // remove corresponding DNS entries.
+    ASSERT_EQ(1, srv_->name_change_reqs_.size());
+}
+
+} // end of anonymous namespace
diff --git a/src/bin/dhcp4/tests/wireshark.cc b/src/bin/dhcp4/tests/wireshark.cc
index b563354..80b4737 100644
--- a/src/bin/dhcp4/tests/wireshark.cc
+++ b/src/bin/dhcp4/tests/wireshark.cc
@@ -100,6 +100,8 @@ Bootstrap Protocol
     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
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index fdf0bae..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);
     }
 };
 
diff --git a/src/bin/dhcp6/dhcp6.dox b/src/bin/dhcp6/dhcp6.dox
index 7684f53..c0284c7 100644
--- a/src/bin/dhcp6/dhcp6.dox
+++ b/src/bin/dhcp6/dhcp6.dox
@@ -116,19 +116,19 @@ into the FIFO queue.
 @todo Currently the FIFO queue is not processed after the NameChangeRequests are
 generated and added to it. In the future implementation steps it is planned to create
 a code which will check if there are any outstanding requests in the queue and
-send them to the bind10-dhcp-ddns module when server is idle waiting for DHCP messages.
+send them to the b10-dhcp-ddns module when server is idle waiting for DHCP messages.
 
 In the simplest case, when client gets one address from the server, a DHCPv6 server
 may generate 0, 1 or 2 NameChangeRequests during single message processing. 
 Server generates no NameChangeRequests if it is not configured to update DNS
  or it rejects the DNS update for any other reason.
 
-Server may generate 1 NameChangeRequests in a situation when a client acquires a
+Server may generate 1 NameChangeRequest in a situation when a client acquires a
 new lease or it releases an existing lease. In the former case, the NameChangeRequest
-type is CHG_ADD, which indicates that the bind10-dhcp-ddns module should add a new DNS
+type is CHG_ADD, which indicates that the b10-dhcp-ddns module should add a new DNS
 binding for the client, and it is assumed that there is no DNS binding for this
 client already. In the latter case, the NameChangeRequest type is CHG_REMOVE to
-indicate to the bind10-dhcp-ddns module that the existing DNS binding should be removed
+indicate to the b10-dhcp-ddns module that the existing DNS binding should be removed
 from the DNS. The binding consists of the forward and reverse mapping.
 A server may only remove the mapping which it had added. Therefore, the lease database
 holds an information which updates (no update, reverse only update, forward only update,
@@ -140,7 +140,7 @@ Server may generate 2 NameChangeRequests in case the client is renewing a lease
 it already has a DNS binding for that lease. Note, that renewal may be triggered
 as a result of sending a RENEW message as well as the REQUEST message. In both cases
 DHCPv6 server will check if there is an existing lease for the client which has sent
-a message, and if there is it will check in the lease database if the DNS Updates had
+a message, and it will check in the lease database if the DNS Updates had
 been performed for this client. If the notion of client's FQDN changes comparing to
 the information stored in the lease database, the DHCPv6 has to remove an existing
 binding from the DNS and then add a new binding according to the new FQDN information
@@ -154,7 +154,7 @@ message being processed. That is 0, 1, 2 for the individual IA_NA. Generation of
 the distinct NameChangeRequests for each IADDR is not supported yet.
 
 The DHCPv6 Client FQDN Option comprises "NOS" flags which communicate to the
-server what updates (if any), client expects the server to perform. Server
+server what updates (if any) client expects the server to perform. Server
 may be configured to obey client's preference or do FQDN processing in a
 different way. If the server overrides client's preference it will communicate it
 by sending the DHCPv6 Client FQDN Option in its responses to a client, with
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 381d7c0..506eefd 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>
@@ -26,6 +27,7 @@
 #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>
@@ -475,7 +477,7 @@ bool Dhcpv6Srv::run() {
                     .arg(e.what());
             }
 
-            // Send NameChangeRequests to the b10-dhcp_ddns module.
+            // Send NameChangeRequests to the b10-dhcp-ddns module.
             sendNameChangeRequests();
         }
     }
@@ -683,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
@@ -994,7 +1047,7 @@ Dhcpv6Srv::appendClientFqdn(const Pkt6Ptr& question,
 
     // If FQDN is NULL, it means that client did not request DNS Update, plus
     // server doesn't force updates.
-    if (fqdn) {
+    if (!fqdn) {
         return;
     }
 
@@ -1162,7 +1215,7 @@ void
 Dhcpv6Srv::sendNameChangeRequests() {
     while (!name_change_reqs_.empty()) {
         // @todo Once next NameChangeRequest is picked from the queue
-        // we should send it to the bind10-dhcp_ddns module. Currently we
+        // we should send it to the b10-dhcp_ddns module. Currently we
         // just drop it.
         name_change_reqs_.pop();
     }
@@ -2114,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);
@@ -2135,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);
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index 65edd77..20b6ea1 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -336,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
@@ -425,7 +434,7 @@ protected:
     /// @brief Sends all outstanding NameChangeRequests to bind10-d2 module.
     ///
     /// The purpose of this function is to pick all outstanding
-    /// NameChangeRequests from the FIFO queue and send them to bind10-dhcp-ddns
+    /// NameChangeRequests from the FIFO queue and send them to b10-dhcp-ddns
     /// module.
     ///
     /// @todo Currently this function simply removes all requests from the
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index 84cc197..f548ec2 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -69,6 +69,7 @@ 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 += fqdn_unittest.cc
 dhcp6_unittests_SOURCES += hooks_unittest.cc
 dhcp6_unittests_SOURCES += dhcp6_test_utils.cc dhcp6_test_utils.h
 dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc
index acfc270..2420172 100644
--- a/src/bin/dhcp6/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp6/tests/config_parser_unittest.cc
@@ -2049,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 ba8b8da..393d466 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -15,20 +15,20 @@
 #include <config.h>
 
 #include <asiolink/io_address.h>
-#include <dhcp_ddns/ncr_msg.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/duid.h>
 #include <dhcp/option.h>
-#include <dhcp/option_custom.h>
 #include <dhcp/option6_addrlst.h>
-#include <dhcp/option6_client_fqdn.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option_int.h>
 #include <dhcp/option_int_array.h>
+#include <dhcp/option_string.h>
+#include <dhcp/option_vendor.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>
@@ -50,257 +50,12 @@ using namespace isc;
 using namespace isc::test;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
-using namespace isc::dhcp_ddns;
 using namespace isc::util;
 using namespace isc::hooks;
 using namespace std;
 
-// namespace has to be named, because friends are defined in Dhcpv6Srv class
-// Maybe it should be isc::test?
 namespace {
 
-// This is a test fixture class for testing the processing of the DHCPv6 Client
-// FQDN Option.
-class FqdnDhcpv6SrvTest : public Dhcpv6SrvTest {
-public:
-    // Constructor
-    FqdnDhcpv6SrvTest()
-        : Dhcpv6SrvTest() {
-        // generateClientId assigns DUID to duid_.
-        generateClientId();
-        lease_.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
-                                duid_, 1234, 501, 502, 503,
-                                504, 1, 0));
-
-    }
-
-    // Destructor
-    virtual ~FqdnDhcpv6SrvTest() {
-    }
-
-    // Construct the DHCPv6 Client FQDN Option using flags and domain-name.
-    Option6ClientFqdnPtr
-    createClientFqdn(const uint8_t flags,
-                     const std::string& fqdn_name,
-                     const Option6ClientFqdn::DomainNameType fqdn_type) {
-        return (Option6ClientFqdnPtr(new Option6ClientFqdn(flags,
-                                                           fqdn_name,
-                                                           fqdn_type)));
-    }
-
-    // Create a message holding DHCPv6 Client FQDN Option.
-    Pkt6Ptr generatePktWithFqdn(uint8_t msg_type,
-                                const uint8_t fqdn_flags,
-                                const std::string& fqdn_domain_name,
-                                const Option6ClientFqdn::DomainNameType
-                                fqdn_type,
-                                const bool include_oro,
-                                OptionPtr srvid = OptionPtr()) {
-        Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
-        pkt->setRemoteAddr(IOAddress("fe80::abcd"));
-        Option6IAPtr ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
-
-        if (msg_type != DHCPV6_REPLY) {
-            IOAddress hint("2001:db8:1:1::dead:beef");
-            OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
-            ia->addOption(hint_opt);
-            pkt->addOption(ia);
-        }
-
-        OptionPtr clientid = generateClientId();
-        pkt->addOption(clientid);
-        if (srvid && (msg_type != DHCPV6_SOLICIT)) {
-            pkt->addOption(srvid);
-        }
-
-        pkt->addOption(createClientFqdn(fqdn_flags, fqdn_domain_name,
-                                        fqdn_type));
-
-        if (include_oro) {
-            OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6,
-                                                           D6O_ORO));
-            oro->addValue(D6O_CLIENT_FQDN);
-            pkt->addOption(oro);
-        }
-
-        return (pkt);
-    }
-
-    // Creates instance of the DHCPv6 message with client id and server id.
-    Pkt6Ptr generateMessageWithIds(const uint8_t msg_type,
-                                   NakedDhcpv6Srv& srv) {
-        Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
-        // Generate client-id.
-        OptionPtr opt_clientid = generateClientId();
-        pkt->addOption(opt_clientid);
-
-        if (msg_type != DHCPV6_SOLICIT) {
-            // Generate server-id.
-            pkt->addOption(srv.getServerID());
-        }
-
-        return (pkt);
-    }
-
-    // Returns an instance of the option carrying FQDN.
-    Option6ClientFqdnPtr getClientFqdnOption(const Pkt6Ptr& pkt) {
-        return (boost::dynamic_pointer_cast<Option6ClientFqdn>
-                (pkt->getOption(D6O_CLIENT_FQDN)));
-    }
-
-    // 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(D6O_IA_NA, iaid, 1500, 3000);
-        Option6IAAddrPtr opt_iaaddr(new Option6IAAddr(D6O_IAADDR, addr,
-                                                      300, 500));
-        opt_ia->addOption(opt_iaaddr);
-        pkt->addOption(opt_ia);
-    }
-
-    // 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(D6O_IA_NA, iaid, 1500, 3000);
-        addStatusCode(status_code, "", opt_ia);
-        pkt->addOption(opt_ia);
-    }
-
-    // Creates status code with the specified code and message.
-    OptionCustomPtr createStatusCode(const uint16_t code,
-                                     const std::string& msg) {
-        OptionDefinition def("status-code", D6O_STATUS_CODE, "record");
-        def.addRecordField("uint16");
-        def.addRecordField("string");
-        OptionCustomPtr opt_status(new OptionCustom(def, Option::V6));
-        opt_status->writeInteger(code);
-        if (!msg.empty()) {
-            opt_status->writeString(msg, 1);
-        }
-        return (opt_status);
-    }
-
-    // Adds Status Code option to the IA.
-    void addStatusCode(const uint16_t code, const std::string& msg,
-                       Option6IAPtr& opt_ia) {
-        opt_ia->addOption(createStatusCode(code, msg));
-    }
-
-    // Test processing of the DHCPv6 Client FQDN Option.
-    void testFqdn(const uint16_t msg_type,
-                  const bool use_oro,
-                  const uint8_t in_flags,
-                  const std::string& in_domain_name,
-                  const Option6ClientFqdn::DomainNameType in_domain_type,
-                  const uint8_t exp_flags,
-                  const std::string& exp_domain_name) {
-        NakedDhcpv6Srv srv(0);
-        Pkt6Ptr question = generatePktWithFqdn(msg_type,
-                                               in_flags,
-                                               in_domain_name,
-                                               in_domain_type,
-                                               use_oro);
-        ASSERT_TRUE(getClientFqdnOption(question));
-
-        Option6ClientFqdnPtr answ_fqdn;
-        ASSERT_NO_THROW(answ_fqdn = srv.processClientFqdn(question));
-        ASSERT_TRUE(answ_fqdn);
-
-        const bool flag_n = (exp_flags & Option6ClientFqdn::FLAG_N) != 0;
-        const bool flag_s = (exp_flags & Option6ClientFqdn::FLAG_S) != 0;
-        const bool flag_o = (exp_flags & Option6ClientFqdn::FLAG_O) != 0;
-
-        EXPECT_EQ(flag_n, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_N));
-        EXPECT_EQ(flag_s, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_S));
-        EXPECT_EQ(flag_o, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_O));
-
-        EXPECT_EQ(exp_domain_name, answ_fqdn->getDomainName());
-        EXPECT_EQ(Option6ClientFqdn::FULL, answ_fqdn->getDomainNameType());
-    }
-
-    // Tests that the client message holding an FQDN is processed and the
-    // lease is acquired.
-    void testProcessMessage(const uint8_t msg_type,
-                            const std::string& hostname,
-                            NakedDhcpv6Srv& srv) {
-        // Create a message of a specified type, add server id and
-        // FQDN option.
-        OptionPtr srvid = srv.getServerID();
-        Pkt6Ptr req = generatePktWithFqdn(msg_type, Option6ClientFqdn::FLAG_S,
-                                          hostname,
-                                          Option6ClientFqdn::FULL,
-                                          true, srvid);
-
-        // For different client's message types we have to invoke different
-        // functions to generate response.
-        Pkt6Ptr reply;
-        if (msg_type == DHCPV6_SOLICIT) {
-            ASSERT_NO_THROW(reply = srv.processSolicit(req));
-
-        } else if (msg_type == DHCPV6_REQUEST) {
-            ASSERT_NO_THROW(reply = srv.processRequest(req));
-
-        } else if (msg_type == DHCPV6_RENEW) {
-            ASSERT_NO_THROW(reply = srv.processRequest(req));
-
-        } else if (msg_type == DHCPV6_RELEASE) {
-            // For Release no lease will be acquired so we have to leave
-            // function here.
-            ASSERT_NO_THROW(reply = srv.processRelease(req));
-            return;
-        } else {
-            // We are not interested in testing other message types.
-            return;
-        }
-
-        // For Solicit, we will get different message type obviously.
-        if (msg_type == DHCPV6_SOLICIT) {
-            checkResponse(reply, DHCPV6_ADVERTISE, 1234);
-
-        } else {
-            checkResponse(reply, DHCPV6_REPLY, 1234);
-        }
-
-        // Check verify that IA_NA is correct.
-        Option6IAAddrPtr addr =
-            checkIA_NA(reply, 234, subnet_->getT1(), subnet_->getT2());
-        ASSERT_TRUE(addr);
-
-        // Check that we have got the address we requested.
-        checkIAAddr(addr, IOAddress("2001:db8:1:1::dead:beef"),
-                    Lease::TYPE_NA);
-
-        if (msg_type != DHCPV6_SOLICIT) {
-            // Check that the lease exists.
-            Lease6Ptr lease =
-                checkLease(duid_, reply->getOption(D6O_IA_NA), addr);
-            ASSERT_TRUE(lease);
-        }
-    }
-
-    // Verify that NameChangeRequest holds valid values.
-    void verifyNameChangeRequest(NakedDhcpv6Srv& srv,
-                                 const isc::dhcp_ddns::NameChangeType type,
-                                 const bool reverse, const bool forward,
-                                 const std::string& addr,
-                                 const std::string& dhcid,
-                                 const uint16_t expires,
-                                 const uint16_t len) {
-        NameChangeRequest ncr = srv.name_change_reqs_.front();
-        EXPECT_EQ(type, ncr.getChangeType());
-        EXPECT_EQ(forward, ncr.isForwardChange());
-        EXPECT_EQ(reverse, ncr.isReverseChange());
-        EXPECT_EQ(addr, ncr.getIpAddress());
-        EXPECT_EQ(dhcid, ncr.getDhcid().toStr());
-        EXPECT_EQ(expires, ncr.getLeaseExpiresOn());
-        EXPECT_EQ(len, ncr.getLeaseLength());
-        EXPECT_EQ(isc::dhcp_ddns::ST_NEW, ncr.getStatus());
-        srv.name_change_reqs_.pop();
-    }
-
-    // Holds a lease used by a test.
-    Lease6Ptr lease_;
-
-};
-
 // This test verifies that incoming SOLICIT can be handled properly when
 // there are no subnets configured.
 //
@@ -1571,377 +1326,6 @@ TEST_F(Dhcpv6SrvTest, ServerID) {
     EXPECT_EQ(duid1_text, text);
 }
 
-// A set of tests verifying server's behaviour when it receives the DHCPv6
-// Client Fqdn Option.
-// @todo: Extend these tests once appropriate configuration parameters are
-// implemented (ticket #3034).
-
-// Test server's response when client requests that server performs AAAA update.
-TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdate) {
-    testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S,
-             "myhost.example.com",
-             Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_S,
-             "myhost.example.com.");
-}
-
-// Test server's response when client provides partial domain-name and requests
-// that server performs AAAA update.
-TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdatePartialName) {
-    testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S, "myhost",
-             Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
-             "myhost.example.com.");
-}
-
-// Test server's response when client provides empty domain-name and requests
-// that server performs AAAA update.
-TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdateNoName) {
-    testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S, "",
-             Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
-             "myhost.example.com.");
-}
-
-// Test server's response when client requests no DNS update.
-TEST_F(FqdnDhcpv6SrvTest, noUpdate) {
-    testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_N,
-             "myhost.example.com",
-             Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_N,
-             "myhost.example.com.");
-}
-
-// Test server's response when client requests that server delegates the AAAA
-// update to the client and this delegation is not allowed.
-TEST_F(FqdnDhcpv6SrvTest, clientAAAAUpdateNotAllowed) {
-    testFqdn(DHCPV6_SOLICIT, true, 0, "myhost.example.com.",
-             Option6ClientFqdn::FULL,
-             Option6ClientFqdn::FLAG_S | Option6ClientFqdn::FLAG_O,
-             "myhost.example.com.");
-}
-
-// Test that exception is thrown if supplied NULL answer packet when
-// creating NameChangeRequests.
-TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAnswer) {
-    NakedDhcpv6Srv srv(0);
-
-    Pkt6Ptr answer;
-    Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
-                                                 "myhost.example.com",
-                                                 Option6ClientFqdn::FULL);
-    EXPECT_THROW(srv.createNameChangeRequests(answer, fqdn),
-                 isc::Unexpected);
-
-}
-
-// Test that exception is thrown if supplied answer from the server
-// contains no DUID when creating NameChangeRequests.
-TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoDUID) {
-    NakedDhcpv6Srv srv(0);
-
-    Pkt6Ptr answer = Pkt6Ptr(new Pkt6(DHCPV6_REPLY, 1234));
-    Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
-                                                 "myhost.example.com",
-                                                 Option6ClientFqdn::FULL);
-
-    EXPECT_THROW(srv.createNameChangeRequests(answer, fqdn),
-                 isc::Unexpected);
-
-}
-
-// Test no NameChangeRequests are added if FQDN option is NULL.
-TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoFQDN) {
-    NakedDhcpv6Srv srv(0);
-
-    // Create Reply message with Client Id and Server id.
-    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
-
-    // Pass NULL FQDN option. No NameChangeRequests should be created.
-    Option6ClientFqdnPtr fqdn;
-    ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
-
-    // There should be no new NameChangeRequests.
-    EXPECT_TRUE(srv.name_change_reqs_.empty());
-}
-
-// Test that NameChangeRequests are not generated if an answer message
-// contains no addresses.
-TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAddr) {
-    NakedDhcpv6Srv srv(0);
-
-    // Create Reply message with Client Id and Server id.
-    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
-
-    Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
-                                                 "myhost.example.com",
-                                                 Option6ClientFqdn::FULL);
-
-    ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
-
-    // We didn't add any IAs, so there should be no NameChangeRequests in th
-    // queue.
-    ASSERT_TRUE(srv.name_change_reqs_.empty());
-}
-
-// Test that a number of NameChangeRequests is created as a result of
-// processing the answer message which holds 3 IAs and when FQDN is
-// specified.
-TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequests) {
-    NakedDhcpv6Srv srv(0);
-
-    // Create Reply message with Client Id and Server id.
-    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
-
-    // Create three IAs, each having different address.
-    addIA(1234, IOAddress("2001:db8:1::1"), answer);
-    addIA(2345, IOAddress("2001:db8:1::2"), answer);
-    addIA(3456, IOAddress("2001:db8:1::3"), answer);
-
-    // Use domain name in upper case. It should be converted to lower-case
-    // before DHCID is calculated. So, we should get the same result as if
-    // we typed domain name in lower-case.
-    Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
-                                                 "MYHOST.EXAMPLE.COM",
-                                                 Option6ClientFqdn::FULL);
-
-    // Create NameChangeRequests. Since we have added 3 IAs, it should
-    // result in generation of 3 distinct NameChangeRequests.
-    ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
-    ASSERT_EQ(3, srv.name_change_reqs_.size());
-
-    // Verify that NameChangeRequests are correct. Each call to the
-    // verifyNameChangeRequest will pop verified request from the queue.
-
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
-                            "2001:db8:1::1",
-                            "000201415AA33D1187D148275136FA30300478"
-                            "FAAAA3EBD29826B5C907B2C9268A6F52",
-                            0, 500);
-
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
-                            "2001:db8:1::2",
-                            "000201415AA33D1187D148275136FA30300478"
-                            "FAAAA3EBD29826B5C907B2C9268A6F52",
-                            0, 500);
-
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
-                            "2001:db8:1::3",
-                            "000201415AA33D1187D148275136FA30300478"
-                            "FAAAA3EBD29826B5C907B2C9268A6F52",
-                            0, 500);
-
-}
-
-// Test creation of the NameChangeRequest to remove both forward and reverse
-// mapping for the given lease.
-TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestFwdRev) {
-    NakedDhcpv6Srv srv(0);
-
-    lease_->fqdn_fwd_ = true;
-    lease_->fqdn_rev_ = true;
-    // Part of the domain name is in upper case, to test that it gets converted
-    // to lower case before DHCID is computed. So, we should get the same DHCID
-    // as if we typed domain-name in lower case.
-    lease_->hostname_ = "MYHOST.example.com.";
-
-    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
-
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
-
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
-                            "2001:db8:1::1",
-                            "000201415AA33D1187D148275136FA30300478"
-                            "FAAAA3EBD29826B5C907B2C9268A6F52",
-                            0, 502);
-
-}
-
-// Test creation of the NameChangeRequest to remove reverse mapping for the
-// given lease.
-TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestRev) {
-    NakedDhcpv6Srv srv(0);
-
-    lease_->fqdn_fwd_ = false;
-    lease_->fqdn_rev_ = true;
-    lease_->hostname_ = "myhost.example.com.";
-
-    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
-
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
-
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, false,
-                            "2001:db8:1::1",
-                            "000201415AA33D1187D148275136FA30300478"
-                            "FAAAA3EBD29826B5C907B2C9268A6F52",
-                            0, 502);
-
-}
-
-// Test that NameChangeRequest to remove DNS records is not generated when
-// neither forward nor reverse DNS update has been performed for a lease.
-TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoUpdate) {
-    NakedDhcpv6Srv srv(0);
-
-    lease_->fqdn_fwd_ = false;
-    lease_->fqdn_rev_ = false;
-
-    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
-
-    EXPECT_TRUE(srv.name_change_reqs_.empty());
-
-}
-
-// Test that NameChangeRequest is not generated if the hostname hasn't been
-// specified for a lease for which forward and reverse mapping has been set.
-TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoHostname) {
-    NakedDhcpv6Srv srv(0);
-
-    lease_->fqdn_fwd_ = true;
-    lease_->fqdn_rev_ = true;
-    lease_->hostname_ = "";
-
-    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
-
-    EXPECT_TRUE(srv.name_change_reqs_.empty());
-
-}
-
-// Test that NameChangeRequest is not generated if the invalid hostname has
-// been specified for a lease for which forward and reverse mapping has been
-// set.
-TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestWrongHostname) {
-    NakedDhcpv6Srv srv(0);
-
-    lease_->fqdn_fwd_ = true;
-    lease_->fqdn_rev_ = true;
-    lease_->hostname_ = "myhost..example.com.";
-
-    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
-
-    EXPECT_TRUE(srv.name_change_reqs_.empty());
-
-}
-
-// Test that Advertise message generated in a response to the Solicit will
-// not result in generation if the NameChangeRequests.
-TEST_F(FqdnDhcpv6SrvTest, processSolicit) {
-    NakedDhcpv6Srv srv(0);
-
-    // Create a Solicit message with FQDN option and generate server's
-    // response using processSolicit function.
-    testProcessMessage(DHCPV6_SOLICIT, "myhost.example.com", srv);
-    EXPECT_TRUE(srv.name_change_reqs_.empty());
-}
-
-// Test that client may send two requests, each carrying FQDN option with
-// a different domain-name. Server should use existing lease for the second
-// request but modify the DNS entries for the lease according to the contents
-// of the FQDN sent in the second request.
-TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
-    NakedDhcpv6Srv srv(0);
-
-    // Create a Request message with FQDN option and generate server's
-    // response using processRequest function. This will result in the
-    // creation of a new lease and the appropriate NameChangeRequest
-    // to add both reverse and forward mapping to DNS.
-    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
-                            "2001:db8:1:1::dead:beef",
-                            "000201415AA33D1187D148275136FA30300478"
-                            "FAAAA3EBD29826B5C907B2C9268A6F52",
-                            0, 4000);
-
-    // Client may send another request message with a new domain-name. In this
-    // case the same lease will be returned. The existing DNS entry needs to
-    // be replaced with a new one. Server should determine that the different
-    // FQDN has been already added to the DNS. As a result, the old DNS
-    // entries should be removed and the entries for the new domain-name
-    // should be added. Therefore, we expect two NameChangeRequests. One to
-    // remove the existing entries, one to add new entries.
-    testProcessMessage(DHCPV6_REQUEST, "otherhost.example.com", srv);
-    ASSERT_EQ(2, srv.name_change_reqs_.size());
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
-                            "2001:db8:1:1::dead:beef",
-                            "000201415AA33D1187D148275136FA30300478"
-                            "FAAAA3EBD29826B5C907B2C9268A6F52",
-                            0, 4000);
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
-                            "2001:db8:1:1::dead:beef",
-                            "000201D422AA463306223D269B6CB7AFE7AAD265FC"
-                            "EA97F93623019B2E0D14E5323D5A",
-                            0, 4000);
-
-}
-
-// Test that client may send Request followed by the Renew, both holding
-// FQDN options, but each option holding different domain-name. The Renew
-// should result in generation of the two NameChangeRequests, one to remove
-// DNS entry added previously when Request was processed, another one to
-// add a new entry for the FQDN held in the Renew.
-TEST_F(FqdnDhcpv6SrvTest, processRequestRenew) {
-    NakedDhcpv6Srv srv(0);
-
-    // Create a Request message with FQDN option and generate server's
-    // response using processRequest function. This will result in the
-    // creation of a new lease and the appropriate NameChangeRequest
-    // to add both reverse and forward mapping to DNS.
-    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
-                            "2001:db8:1:1::dead:beef",
-                            "000201415AA33D1187D148275136FA30300478"
-                            "FAAAA3EBD29826B5C907B2C9268A6F52",
-                            0, 4000);
-
-    // Client may send Renew message with a new domain-name. In this
-    // case the same lease will be returned. The existing DNS entry needs to
-    // be replaced with a new one. Server should determine that the different
-    // FQDN has been already added to the DNS. As a result, the old DNS
-    // entries should be removed and the entries for the new domain-name
-    // should be added. Therefore, we expect two NameChangeRequests. One to
-    // remove the existing entries, one to add new entries.
-    testProcessMessage(DHCPV6_RENEW, "otherhost.example.com", srv);
-    ASSERT_EQ(2, srv.name_change_reqs_.size());
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
-                            "2001:db8:1:1::dead:beef",
-                            "000201415AA33D1187D148275136FA30300478"
-                            "FAAAA3EBD29826B5C907B2C9268A6F52",
-                            0, 4000);
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
-                            "2001:db8:1:1::dead:beef",
-                            "000201D422AA463306223D269B6CB7AFE7AAD265FC"
-                            "EA97F93623019B2E0D14E5323D5A",
-                            0, 4000);
-
-}
-
-TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
-    NakedDhcpv6Srv srv(0);
-
-    // Create a Request message with FQDN option and generate server's
-    // response using processRequest function. This will result in the
-    // creation of a new lease and the appropriate NameChangeRequest
-    // to add both reverse and forward mapping to DNS.
-    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
-                            "2001:db8:1:1::dead:beef",
-                            "000201415AA33D1187D148275136FA30300478"
-                            "FAAAA3EBD29826B5C907B2C9268A6F52",
-                            0, 4000);
-
-    // Client may send Release message. In this case the lease should be
-    // removed and all existing DNS entries for this lease should be
-    // also removed. Therefore, we expect that single NameChangeRequest to
-    // remove DNS entries is generated.
-    testProcessMessage(DHCPV6_RELEASE, "otherhost.example.com", srv);
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
-                            "2001:db8:1:1::dead:beef",
-                            "000201415AA33D1187D148275136FA30300478"
-                            "FAAAA3EBD29826B5C907B2C9268A6F52",
-                            0, 4000);
-
-}
-
 // Checks if server responses are sent to the proper port.
 TEST_F(Dhcpv6SrvTest, portsDirectTraffic) {
 
@@ -2014,9 +1398,208 @@ TEST_F(Dhcpv6SrvTest, docsisTraffic) {
     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);
 
-    /// @todo Check that the ADVERTISE is ok, that it includes all options,
-    /// that is relayed properly, etc.
+    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:
diff --git a/src/bin/dhcp6/tests/fqdn_unittest.cc b/src/bin/dhcp6/tests/fqdn_unittest.cc
new file mode 100644
index 0000000..8a2da69
--- /dev/null
+++ b/src/bin/dhcp6/tests/fqdn_unittest.cc
@@ -0,0 +1,778 @@
+// 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 <dhcp_ddns/ncr_msg.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/option_custom.h>
+#include <dhcp/option6_client_fqdn.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option_int_array.h>
+#include <dhcpsrv/lease.h>
+
+#include <dhcp6/tests/dhcp6_test_utils.h>
+#include <boost/pointer_cast.hpp>
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::test;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::dhcp_ddns;
+using namespace isc::util;
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+/// @brief A test fixture class for testing DHCPv6 Client FQDN Option handling.
+class FqdnDhcpv6SrvTest : public Dhcpv6SrvTest {
+public:
+
+    /// @brief Constructor
+    FqdnDhcpv6SrvTest()
+        : Dhcpv6SrvTest() {
+        // generateClientId assigns DUID to duid_.
+        generateClientId();
+        lease_.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
+                                duid_, 1234, 501, 502, 503,
+                                504, 1, 0));
+
+    }
+
+    /// @brief Destructor
+    virtual ~FqdnDhcpv6SrvTest() {
+    }
+
+    /// @brief Construct the DHCPv6 Client FQDN option using flags and
+    /// domain-name.
+    ///
+    /// @param flags Flags to be set for the created option.
+    /// @param fqdn_name A name which should be stored in the option.
+    /// @param fqdn_type A type of the name carried by the option: partial
+    /// or fully qualified.
+    ///
+    /// @return A pointer to the created option.
+    Option6ClientFqdnPtr
+    createClientFqdn(const uint8_t flags,
+                     const std::string& fqdn_name,
+                     const Option6ClientFqdn::DomainNameType fqdn_type) {
+        return (Option6ClientFqdnPtr(new Option6ClientFqdn(flags,
+                                                           fqdn_name,
+                                                           fqdn_type)));
+    }
+
+    /// @brief Create a message with or without DHCPv6 Client FQDN Option.
+    ///
+    /// @param msg_type A type of the DHCPv6 message to be created.
+    /// @param fqdn_flags Flags to be carried in the FQDN option.
+    /// @param fqdn_domain_name A name to be carried in the FQDN option.
+    /// @param fqdn_type A type of the name carried by the option: partial
+    /// or fully qualified.
+    /// @param include_oro A boolean value which indicates whether the ORO
+    /// option should be added to the message (if true).
+    /// @param srvid server id to be stored in the message.
+    ///
+    /// @return An object representing the created message.
+    Pkt6Ptr generateMessage(uint8_t msg_type,
+                            const uint8_t fqdn_flags,
+                            const std::string& fqdn_domain_name,
+                            const Option6ClientFqdn::DomainNameType
+                            fqdn_type,
+                            const bool include_oro,
+                            OptionPtr srvid = OptionPtr()) {
+        Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
+        pkt->setRemoteAddr(IOAddress("fe80::abcd"));
+        Option6IAPtr ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
+
+        if (msg_type != DHCPV6_REPLY) {
+            IOAddress hint("2001:db8:1:1::dead:beef");
+            OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
+            ia->addOption(hint_opt);
+            pkt->addOption(ia);
+        }
+
+        OptionPtr clientid = generateClientId();
+        pkt->addOption(clientid);
+        if (srvid && (msg_type != DHCPV6_SOLICIT)) {
+            pkt->addOption(srvid);
+        }
+
+        pkt->addOption(createClientFqdn(fqdn_flags, fqdn_domain_name,
+                                            fqdn_type));
+
+        if (include_oro) {
+            OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6,
+                                                           D6O_ORO));
+            oro->addValue(D6O_CLIENT_FQDN);
+            pkt->addOption(oro);
+        }
+
+        return (pkt);
+    }
+
+    /// @brief Creates instance of the DHCPv6 message with client id and
+    /// server id.
+    ///
+    /// @param msg_type A type of the message to be created.
+    /// @param srv An object representing the DHCPv6 server, which
+    /// is used to generate the client identifier.
+    ///
+    /// @return An object representing the created message.
+    Pkt6Ptr generateMessageWithIds(const uint8_t msg_type,
+                                   NakedDhcpv6Srv& srv) {
+        Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
+        // Generate client-id.
+        OptionPtr opt_clientid = generateClientId();
+        pkt->addOption(opt_clientid);
+
+        if (msg_type != DHCPV6_SOLICIT) {
+            // Generate server-id.
+            pkt->addOption(srv.getServerID());
+        }
+
+        return (pkt);
+    }
+
+    /// @brief Returns an instance of the option carrying FQDN.
+    ///
+    /// @param pkt A message holding FQDN option to be returned.
+    ///
+    /// @return An object representing DHCPv6 Client FQDN option.
+    Option6ClientFqdnPtr getClientFqdnOption(const Pkt6Ptr& pkt) {
+        return (boost::dynamic_pointer_cast<Option6ClientFqdn>
+                (pkt->getOption(D6O_CLIENT_FQDN)));
+    }
+
+    /// @brief Adds IA option to the message.
+    ///
+    /// Addded option holds an address.
+    ///
+    /// @param iaid IAID
+    /// @param pkt A DHCPv6 message to which the IA option should be added.
+    void addIA(const uint32_t iaid, const IOAddress& addr, Pkt6Ptr& pkt) {
+        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);
+        pkt->addOption(opt_ia);
+    }
+
+
+    /// @brief Adds IA option to the message.
+    ///
+    /// Added option holds status code.
+    ///
+    /// @param iaid IAID
+    /// @param status_code Status code
+    /// @param pkt A DHCPv6 message to which the option should be added.
+    void addIA(const uint32_t iaid, const uint16_t status_code, Pkt6Ptr& pkt) {
+        Option6IAPtr opt_ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
+        addStatusCode(status_code, "", opt_ia);
+        pkt->addOption(opt_ia);
+    }
+
+    /// @brief Creates status code with the specified code and message.
+    ///
+    /// @param code A status code.
+    /// @param msg A string representation of the message to be added to the
+    /// Status Code option.
+    ///
+    /// @return An object representing the Status Code option.
+    OptionCustomPtr createStatusCode(const uint16_t code,
+                                     const std::string& msg) {
+        OptionDefinition def("status-code", D6O_STATUS_CODE, "record");
+        def.addRecordField("uint16");
+        def.addRecordField("string");
+        OptionCustomPtr opt_status(new OptionCustom(def, Option::V6));
+        opt_status->writeInteger(code);
+        if (!msg.empty()) {
+            opt_status->writeString(msg, 1);
+        }
+        return (opt_status);
+    }
+
+    /// @brief Adds Status Code option to the IA.
+    ///
+    /// @param code A status code value.
+    /// @param msg A string representation of the message to be added to the
+    /// Status Code option.
+    void addStatusCode(const uint16_t code, const std::string& msg,
+                       Option6IAPtr& opt_ia) {
+        opt_ia->addOption(createStatusCode(code, msg));
+    }
+
+    /// @brief Verifies if the DHCPv6 server processes DHCPv6 Client FQDN option
+    /// as expected.
+    ///
+    /// This function simulates generation of the client's message holding FQDN.
+    /// It then calls the server's @c Dhcpv6Srv::processClientFqdn option to
+    /// generate server's response to the FQDN. This function returns the FQDN
+    /// which should be appended to the server's response to the client.
+    /// This function verifies that the FQDN option returned is correct.
+    ///
+    /// @param msg_type A type of the client's message.
+    /// @param use_oro A boolean value which indicates whether the DHCPv6 ORO
+    /// option (requesting return of the FQDN option by the server) should be
+    /// included in the client's message (if true), or not included (if false).
+    /// @param in_flags A value of flags field to be set for the FQDN carried
+    /// in the client's message.
+    /// @param in_domain_name A domain name to be carried in the client's FQDN
+    /// option.
+    /// @param in_domain_type A type of the domain name to be carried in the
+    /// client's FQDM option (partial or fully qualified).
+    /// @param exp_flags A value of flags expected in the FQDN sent by a server.
+    /// @param exp_domain_name A domain name expected in the FQDN sent by a
+    /// server.
+    void testFqdn(const uint16_t msg_type,
+                  const bool use_oro,
+                  const uint8_t in_flags,
+                  const std::string& in_domain_name,
+                  const Option6ClientFqdn::DomainNameType in_domain_type,
+                  const uint8_t exp_flags,
+                  const std::string& exp_domain_name) {
+        NakedDhcpv6Srv srv(0);
+        Pkt6Ptr question = generateMessage(msg_type,
+                                           in_flags,
+                                           in_domain_name,
+                                           in_domain_type,
+                                           use_oro);
+        ASSERT_TRUE(getClientFqdnOption(question));
+
+        Option6ClientFqdnPtr answ_fqdn;
+        ASSERT_NO_THROW(answ_fqdn = srv.processClientFqdn(question));
+        ASSERT_TRUE(answ_fqdn);
+
+        const bool flag_n = (exp_flags & Option6ClientFqdn::FLAG_N) != 0;
+        const bool flag_s = (exp_flags & Option6ClientFqdn::FLAG_S) != 0;
+        const bool flag_o = (exp_flags & Option6ClientFqdn::FLAG_O) != 0;
+
+        EXPECT_EQ(flag_n, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_N));
+        EXPECT_EQ(flag_s, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_S));
+        EXPECT_EQ(flag_o, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_O));
+
+        EXPECT_EQ(exp_domain_name, answ_fqdn->getDomainName());
+        EXPECT_EQ(Option6ClientFqdn::FULL, answ_fqdn->getDomainNameType());
+    }
+
+    /// @brief Tests that the client's message holding an FQDN is processed
+    /// and that lease is acquired.
+    ///
+    /// @param msg_type A type of the client's message.
+    /// @param hostname A domain name in the client's FQDN.
+    /// @param srv A server object, used to process the message.
+    /// @param include_oro A boolean value which indicates whether the ORO
+    /// option should be included in the client's message (if true) or not
+    /// (if false). In the former case, the function will expect that server
+    /// responds with the FQDN option. In the latter case, the function expects
+    /// that the server doesn't respond with the FQDN.
+    void testProcessMessage(const uint8_t msg_type,
+                            const std::string& hostname,
+                            NakedDhcpv6Srv& srv,
+                            const bool include_oro = true) {
+        // Create a message of a specified type, add server id and
+        // FQDN option.
+        OptionPtr srvid = srv.getServerID();
+        Pkt6Ptr req = generateMessage(msg_type, Option6ClientFqdn::FLAG_S,
+                                      hostname,
+                                      Option6ClientFqdn::FULL,
+                                      include_oro, srvid);
+
+        // For different client's message types we have to invoke different
+        // functions to generate response.
+        Pkt6Ptr reply;
+        if (msg_type == DHCPV6_SOLICIT) {
+            ASSERT_NO_THROW(reply = srv.processSolicit(req));
+
+        } else if (msg_type == DHCPV6_REQUEST) {
+            ASSERT_NO_THROW(reply = srv.processRequest(req));
+
+        } else if (msg_type == DHCPV6_RENEW) {
+            ASSERT_NO_THROW(reply = srv.processRequest(req));
+
+        } else if (msg_type == DHCPV6_RELEASE) {
+            // For Release no lease will be acquired so we have to leave
+            // function here.
+            ASSERT_NO_THROW(reply = srv.processRelease(req));
+            return;
+        } else {
+            // We are not interested in testing other message types.
+            return;
+        }
+
+        // For Solicit, we will get different message type obviously.
+        if (msg_type == DHCPV6_SOLICIT) {
+            checkResponse(reply, DHCPV6_ADVERTISE, 1234);
+
+        } else {
+            checkResponse(reply, DHCPV6_REPLY, 1234);
+        }
+
+        // Check verify that IA_NA is correct.
+        Option6IAAddrPtr addr =
+            checkIA_NA(reply, 234, subnet_->getT1(), subnet_->getT2());
+        ASSERT_TRUE(addr);
+
+        // Check that we have got the address we requested.
+        checkIAAddr(addr, IOAddress("2001:db8:1:1::dead:beef"),
+                    Lease::TYPE_NA);
+
+        if (msg_type != DHCPV6_SOLICIT) {
+            // Check that the lease exists.
+            Lease6Ptr lease =
+                checkLease(duid_, reply->getOption(D6O_IA_NA), addr);
+            ASSERT_TRUE(lease);
+        }
+
+        if (include_oro) {
+            ASSERT_TRUE(reply->getOption(D6O_CLIENT_FQDN));
+        } else {
+            ASSERT_FALSE(reply->getOption(D6O_CLIENT_FQDN));
+        }
+    }
+
+    /// @brief Verify that NameChangeRequest holds valid values.
+    ///
+    /// This function picks first NameChangeRequest from the internal server's
+    /// queue and checks that it holds valid parameters. The NameChangeRequest
+    /// is removed from the queue.
+    ///
+    /// @param srv A server object holding a queue of NameChangeRequests.
+    /// @param type An expected type of the NameChangeRequest (Add or Remove).
+    /// @param reverse An expected setting of the reverse update flag.
+    /// @param forward An expected setting of the forward udpate flag.
+    /// @param addr A string representation of the IPv6 address held in the
+    /// NameChangeRequest.
+    /// @param dhcid An expected DHCID value.
+    /// @param expires A timestamp when the lease associated with the
+    /// NameChangeRequest expires.
+    /// @param len A valid lifetime of the lease associated with the
+    /// NameChangeRequest.
+    void verifyNameChangeRequest(NakedDhcpv6Srv& srv,
+                                 const isc::dhcp_ddns::NameChangeType type,
+                                 const bool reverse, const bool forward,
+                                 const std::string& addr,
+                                 const std::string& dhcid,
+                                 const uint16_t expires,
+                                 const uint16_t len) {
+        NameChangeRequest ncr = srv.name_change_reqs_.front();
+        EXPECT_EQ(type, ncr.getChangeType());
+        EXPECT_EQ(forward, ncr.isForwardChange());
+        EXPECT_EQ(reverse, ncr.isReverseChange());
+        EXPECT_EQ(addr, ncr.getIpAddress());
+        EXPECT_EQ(dhcid, ncr.getDhcid().toStr());
+        EXPECT_EQ(expires, ncr.getLeaseExpiresOn());
+        EXPECT_EQ(len, ncr.getLeaseLength());
+        EXPECT_EQ(isc::dhcp_ddns::ST_NEW, ncr.getStatus());
+        srv.name_change_reqs_.pop();
+    }
+
+    // Holds a lease used by a test.
+    Lease6Ptr lease_;
+
+};
+
+// A set of tests verifying server's behaviour when it receives the DHCPv6
+// Client Fqdn Option.
+// @todo: Extend these tests once appropriate configuration parameters are
+// implemented (ticket #3034).
+
+// Test server's response when client requests that server performs AAAA update.
+TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdate) {
+    testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S,
+             "myhost.example.com",
+             Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_S,
+             "myhost.example.com.");
+}
+
+// Test server's response when client provides partial domain-name and requests
+// that server performs AAAA update.
+TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdatePartialName) {
+    testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S, "myhost",
+             Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
+             "myhost.example.com.");
+}
+
+// Test server's response when client provides empty domain-name and requests
+// that server performs AAAA update.
+TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdateNoName) {
+    testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S, "",
+             Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
+             "myhost.example.com.");
+}
+
+// Test server's response when client requests no DNS update.
+TEST_F(FqdnDhcpv6SrvTest, noUpdate) {
+    testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_N,
+             "myhost.example.com",
+             Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_N,
+             "myhost.example.com.");
+}
+
+// Test server's response when client requests that server delegates the AAAA
+// update to the client and this delegation is not allowed.
+TEST_F(FqdnDhcpv6SrvTest, clientAAAAUpdateNotAllowed) {
+    testFqdn(DHCPV6_SOLICIT, true, 0, "myhost.example.com.",
+             Option6ClientFqdn::FULL,
+             Option6ClientFqdn::FLAG_S | Option6ClientFqdn::FLAG_O,
+             "myhost.example.com.");
+}
+
+// Test that exception is thrown if supplied NULL answer packet when
+// creating NameChangeRequests.
+TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAnswer) {
+    NakedDhcpv6Srv srv(0);
+
+    Pkt6Ptr answer;
+    Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
+                                                 "myhost.example.com",
+                                                 Option6ClientFqdn::FULL);
+    EXPECT_THROW(srv.createNameChangeRequests(answer, fqdn),
+                 isc::Unexpected);
+
+}
+
+// Test that exception is thrown if supplied answer from the server
+// contains no DUID when creating NameChangeRequests.
+TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoDUID) {
+    NakedDhcpv6Srv srv(0);
+
+    Pkt6Ptr answer = Pkt6Ptr(new Pkt6(DHCPV6_REPLY, 1234));
+    Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
+                                                 "myhost.example.com",
+                                                 Option6ClientFqdn::FULL);
+
+    EXPECT_THROW(srv.createNameChangeRequests(answer, fqdn),
+                 isc::Unexpected);
+
+}
+
+// Test no NameChangeRequests are added if FQDN option is NULL.
+TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoFQDN) {
+    NakedDhcpv6Srv srv(0);
+
+    // Create Reply message with Client Id and Server id.
+    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
+
+    // Pass NULL FQDN option. No NameChangeRequests should be created.
+    Option6ClientFqdnPtr fqdn;
+    ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
+
+    // There should be no new NameChangeRequests.
+    EXPECT_TRUE(srv.name_change_reqs_.empty());
+}
+
+// Test that NameChangeRequests are not generated if an answer message
+// contains no addresses.
+TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAddr) {
+    NakedDhcpv6Srv srv(0);
+
+    // Create Reply message with Client Id and Server id.
+    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
+
+    Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
+                                                 "myhost.example.com",
+                                                 Option6ClientFqdn::FULL);
+
+    ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
+
+    // We didn't add any IAs, so there should be no NameChangeRequests in th
+    // queue.
+    ASSERT_TRUE(srv.name_change_reqs_.empty());
+}
+
+// Test that a number of NameChangeRequests is created as a result of
+// processing the answer message which holds 3 IAs and when FQDN is
+// specified.
+TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequests) {
+    NakedDhcpv6Srv srv(0);
+
+    // Create Reply message with Client Id and Server id.
+    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
+
+    // Create three IAs, each having different address.
+    addIA(1234, IOAddress("2001:db8:1::1"), answer);
+    addIA(2345, IOAddress("2001:db8:1::2"), answer);
+    addIA(3456, IOAddress("2001:db8:1::3"), answer);
+
+    // Use domain name in upper case. It should be converted to lower-case
+    // before DHCID is calculated. So, we should get the same result as if
+    // we typed domain name in lower-case.
+    Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
+                                                 "MYHOST.EXAMPLE.COM",
+                                                 Option6ClientFqdn::FULL);
+
+    // Create NameChangeRequests. Since we have added 3 IAs, it should
+    // result in generation of 3 distinct NameChangeRequests.
+    ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
+    ASSERT_EQ(3, srv.name_change_reqs_.size());
+
+    // Verify that NameChangeRequests are correct. Each call to the
+    // verifyNameChangeRequest will pop verified request from the queue.
+
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                            "2001:db8:1::1",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 500);
+
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                            "2001:db8:1::2",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 500);
+
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                            "2001:db8:1::3",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 500);
+
+}
+
+// Test creation of the NameChangeRequest to remove both forward and reverse
+// mapping for the given lease.
+TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestFwdRev) {
+    NakedDhcpv6Srv srv(0);
+
+    lease_->fqdn_fwd_ = true;
+    lease_->fqdn_rev_ = true;
+    // Part of the domain name is in upper case, to test that it gets converted
+    // to lower case before DHCID is computed. So, we should get the same DHCID
+    // as if we typed domain-name in lower case.
+    lease_->hostname_ = "MYHOST.example.com.";
+
+    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
+
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
+
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
+                            "2001:db8:1::1",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 502);
+
+}
+
+// Test creation of the NameChangeRequest to remove reverse mapping for the
+// given lease.
+TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestRev) {
+    NakedDhcpv6Srv srv(0);
+
+    lease_->fqdn_fwd_ = false;
+    lease_->fqdn_rev_ = true;
+    lease_->hostname_ = "myhost.example.com.";
+
+    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
+
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
+
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, false,
+                            "2001:db8:1::1",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 502);
+
+}
+
+// Test that NameChangeRequest to remove DNS records is not generated when
+// neither forward nor reverse DNS update has been performed for a lease.
+TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoUpdate) {
+    NakedDhcpv6Srv srv(0);
+
+    lease_->fqdn_fwd_ = false;
+    lease_->fqdn_rev_ = false;
+
+    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
+
+    EXPECT_TRUE(srv.name_change_reqs_.empty());
+
+}
+
+// Test that NameChangeRequest is not generated if the hostname hasn't been
+// specified for a lease for which forward and reverse mapping has been set.
+TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoHostname) {
+    NakedDhcpv6Srv srv(0);
+
+    lease_->fqdn_fwd_ = true;
+    lease_->fqdn_rev_ = true;
+    lease_->hostname_ = "";
+
+    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
+
+    EXPECT_TRUE(srv.name_change_reqs_.empty());
+
+}
+
+// Test that NameChangeRequest is not generated if the invalid hostname has
+// been specified for a lease for which forward and reverse mapping has been
+// set.
+TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestWrongHostname) {
+    NakedDhcpv6Srv srv(0);
+
+    lease_->fqdn_fwd_ = true;
+    lease_->fqdn_rev_ = true;
+    lease_->hostname_ = "myhost..example.com.";
+
+    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
+
+    EXPECT_TRUE(srv.name_change_reqs_.empty());
+
+}
+
+// Test that Advertise message generated in a response to the Solicit will
+// not result in generation if the NameChangeRequests.
+TEST_F(FqdnDhcpv6SrvTest, processSolicit) {
+    NakedDhcpv6Srv srv(0);
+
+    // Create a Solicit message with FQDN option and generate server's
+    // response using processSolicit function.
+    testProcessMessage(DHCPV6_SOLICIT, "myhost.example.com", srv);
+    EXPECT_TRUE(srv.name_change_reqs_.empty());
+}
+
+// Test that client may send two requests, each carrying FQDN option with
+// a different domain-name. Server should use existing lease for the second
+// request but modify the DNS entries for the lease according to the contents
+// of the FQDN sent in the second request.
+TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
+    NakedDhcpv6Srv srv(0);
+
+    // Create a Request message with FQDN option and generate server's
+    // response using processRequest function. This will result in the
+    // creation of a new lease and the appropriate NameChangeRequest
+    // to add both reverse and forward mapping to DNS.
+    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 4000);
+
+    // Client may send another request message with a new domain-name. In this
+    // case the same lease will be returned. The existing DNS entry needs to
+    // be replaced with a new one. Server should determine that the different
+    // FQDN has been already added to the DNS. As a result, the old DNS
+    // entries should be removed and the entries for the new domain-name
+    // should be added. Therefore, we expect two NameChangeRequests. One to
+    // remove the existing entries, one to add new entries.
+    testProcessMessage(DHCPV6_REQUEST, "otherhost.example.com", srv);
+    ASSERT_EQ(2, srv.name_change_reqs_.size());
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 4000);
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201D422AA463306223D269B6CB7AFE7AAD265FC"
+                            "EA97F93623019B2E0D14E5323D5A",
+                            0, 4000);
+
+}
+
+// Test that client may send Request followed by the Renew, both holding
+// FQDN options, but each option holding different domain-name. The Renew
+// should result in generation of the two NameChangeRequests, one to remove
+// DNS entry added previously when Request was processed, another one to
+// add a new entry for the FQDN held in the Renew.
+TEST_F(FqdnDhcpv6SrvTest, processRequestRenew) {
+    NakedDhcpv6Srv srv(0);
+
+    // Create a Request message with FQDN option and generate server's
+    // response using processRequest function. This will result in the
+    // creation of a new lease and the appropriate NameChangeRequest
+    // to add both reverse and forward mapping to DNS.
+    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 4000);
+
+    // Client may send Renew message with a new domain-name. In this
+    // case the same lease will be returned. The existing DNS entry needs to
+    // be replaced with a new one. Server should determine that the different
+    // FQDN has been already added to the DNS. As a result, the old DNS
+    // entries should be removed and the entries for the new domain-name
+    // should be added. Therefore, we expect two NameChangeRequests. One to
+    // remove the existing entries, one to add new entries.
+    testProcessMessage(DHCPV6_RENEW, "otherhost.example.com", srv);
+    ASSERT_EQ(2, srv.name_change_reqs_.size());
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 4000);
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201D422AA463306223D269B6CB7AFE7AAD265FC"
+                            "EA97F93623019B2E0D14E5323D5A",
+                            0, 4000);
+
+}
+
+TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
+    NakedDhcpv6Srv srv(0);
+
+    // Create a Request message with FQDN option and generate server's
+    // response using processRequest function. This will result in the
+    // creation of a new lease and the appropriate NameChangeRequest
+    // to add both reverse and forward mapping to DNS.
+    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 4000);
+
+    // Client may send Release message. In this case the lease should be
+    // removed and all existing DNS entries for this lease should be
+    // also removed. Therefore, we expect that single NameChangeRequest to
+    // remove DNS entries is generated.
+    testProcessMessage(DHCPV6_RELEASE, "otherhost.example.com", srv);
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 4000);
+
+}
+
+// Checks that the server does not include DHCPv6 Client FQDN option in its
+// response when client doesn't include ORO option in the Request.
+TEST_F(FqdnDhcpv6SrvTest, processRequestWithoutFqdn) {
+    NakedDhcpv6Srv srv(0);
+
+    // The last parameter disables use of the ORO to request FQDN option
+    // In this case, we expect that the FQDN option will not be included
+    // in the server's response. The testProcessMessage will check that.
+    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv, false);
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 4000);
+}
+
+}   // end of anonymous namespace
diff --git a/src/bin/msgq/msgq.py.in b/src/bin/msgq/msgq.py.in
index 07ea5ba..c68567c 100755
--- a/src/bin/msgq/msgq.py.in
+++ b/src/bin/msgq/msgq.py.in
@@ -74,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'.
 
@@ -268,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:
@@ -810,6 +827,12 @@ def 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:
diff --git a/src/bin/msgq/msgq_messages.mes b/src/bin/msgq/msgq_messages.mes
index 2bd2515..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.
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/xfrin/b10-xfrin.xml b/src/bin/xfrin/b10-xfrin.xml
index ceae54c..0b58f9a 100644
--- a/src/bin/xfrin/b10-xfrin.xml
+++ b/src/bin/xfrin/b10-xfrin.xml
@@ -358,6 +358,88 @@ operation
         </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 9df2bde..4158638 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -1105,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
@@ -3497,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 79093b2..a894d55 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -611,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)
@@ -621,6 +626,74 @@ class XfrinConnection(asyncore.dispatcher):
         self._transfer_stats = XfrinTransferStats()
         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.
 
@@ -861,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")
diff --git a/src/bin/xfrin/xfrin.spec b/src/bin/xfrin/xfrin.spec
index 826fde4..a662f75 100644
--- a/src/bin/xfrin/xfrin.spec
+++ b/src/bin/xfrin/xfrin.spec
@@ -274,6 +274,227 @@
         "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/hooks/dhcp/user_chk/Makefile.am b/src/hooks/dhcp/user_chk/Makefile.am
index 01453cf..6b68f38 100644
--- a/src/hooks/dhcp/user_chk/Makefile.am
+++ b/src/hooks/dhcp/user_chk/Makefile.am
@@ -25,7 +25,7 @@ AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 BUILT_SOURCES =
 
 # Ensure that the message file is included in the distribution
-EXTRA_DIST =
+EXTRA_DIST = libdhcp_user_chk.dox
 
 # Get rid of generated message files on a clean
 #CLEANFILES = *.gcno *.gcda user_chk_messages.h user_chk_messages.cc s-messages
diff --git a/src/hooks/dhcp/user_chk/libdhcp_user_chk.dox b/src/hooks/dhcp/user_chk/libdhcp_user_chk.dox
new file mode 100644
index 0000000..eb19e06
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/libdhcp_user_chk.dox
@@ -0,0 +1,199 @@
+// 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.
+
+/**
+ at page libdhcp_user_chk DHCP User Check Hooks Library
+
+ at section libdhcp_user_chkIntro Libdhcp_user_chk: An Example Hooks Library
+## Introduction
+libdhcp_user_chk is an example hooks library which customizes the DHCP query
+processing provided by BIND X DHCP server modules (b10-dhcp4 and b10-dhcp6).
+Specifically it allows subnet selection and DHCP response option customization
+based upon a registry of DHCP clients.  Note that the words "client" and "user" are used interchangeably herein.  The intent of the custom behavior is three
+fold:
+
+1. To assign "new" or "unregistered" users to a restricted subnet, while "known"
+or "registered" users are assigned to unrestricted subnets.
+
+2. To allow DHCP response options or vendor option values to be customized
+based upon user identity.
+
+3. To provide a real time record of the user registration activity which can be sampled by an external consumer.
+
+## User Registry Classes
+At the heart of the library is a class hierarchy centered around the class,
+user_chk::UserRegistry.  This class represents a maintainable, searchable
+registry of "known" users and their attributes.  It provides services to load,
+clear, and refresh the registry as well as to add, find, and remove users.
+
+Each entry in the registry is an instance of the class, user_chk::User. Users
+are uniquely identified by their user_chk::UserId. UserIds are comprised of
+data taken from the DHCP request. IPv4 users have a type of "HW_ADDR" and
+their id is the hardware address from the request.  IPv6 users have a type of
+"DUID" and their id is the DUID from the request.
+
+The registry may be manually populated or loaded from a source of data which
+implements the UserDataSource interface. Currently, a single implementation has
+been implemented, user_chk::UserFile. UserFile supports reading the registry
+from a text file in which each line is a user entry in JSON format.  Each entry
+contains the id type and user id along with optional attributes.  Attributes are
+essentially name/value pairs whose significance is left up to the calling layer.
+UserFile does not enforce any specific content beyond id type and id.
+(See user_file.h for more details on file content).
+
+## Callout Processing
+The library implements callouts for packet receive, subnet select, and packet
+send for both IPv4 and IPv6.  Regardless of the protocol type, the process
+flow upon receipt of an inbound request is the same and is as follows:
+
+-# pkt_receive callout is invoked
+    -# Refresh the user registry
+    -# Extract user id from DHCP request and store it to context
+    -#  Look up user id in registry and store resultant user pointer to context
+
+    Note that each time a packet is received, the user registry is refreshed.
+    This ensures that the registry content always has the latest external
+    updates.  The primary goal at this stage is check the registry for the
+    user and push the result to the context making it available to subsequent
+    callouts.
+
+-# subnet_select callout is invoked
+    -# Retrieve the user pointer from context
+    -# If pointer is null (i.e. user is not registered), replace subnet
+    selection with restricted subnet
+
+    By convention, the last subnet in the collection of subnets available is
+    assumed to be the "restricted access" subnet. A more sophisticated mechanism    is likely to be needed.
+
+-# pkt_send callout is invoked:
+    -# Retrieve the user id and user pointer from context
+    -# If user is not null add the options based on user's attributes,
+    otherwise use default user's attributes
+    -# Generate user check outcome
+
+    This final step is what produces the real time record, referred to as the
+    "user check outcome" file.
+
+## Using the library
+Two steps are required in order to use the library:
+-# The user registry file must be created and deployed
+-# The BIND10 DHCP module(s) must be configured to load the library
+
+### Creating the Registry File
+Currently, the library uses a hard coded pathname for the user registry defined
+in load_unload.cc:
+
+    const char* registry_fname = "/tmp/user_chk_registry.txt";
+
+Each line in the file is a self-contained JSON snippet which must have the
+following two entries:
+
+    - "type" whose value is "HW_ADDR" for IPv4 users or "DUID" for IPv6 users
+    - "id" whose value is either the hardware address or the DUID from the
+    request formatted as a sring of hex digits, without delimiters.
+
+and may have the one or more of the following entries:
+
+    - "bootfile" whose value is the pathname of the desired file
+    - "tftp_server" whose value is the hostname or IP address of the desired
+    server
+
+Sample user registry file is shown below:
+
+ at code
+{ "type" : "HW_ADDR", "id" : "0c0e0a01ff04", "bootfile" : "/tmp/v4bootfile" }
+{ "type" : "HW_ADDR", "id" : "0c0e0a01ff06", "tftp_server" : "tftp.v4.example.com" }
+{ "type" : "DUID", "id" : "0001000119efe63b000c01020304", "bootfile" : "/tmp/v6bootfile" }
+{ "type" : "DUID", "id" : "0001000119efe63b000c01020306", "tftp_server" : "tftp.v6.example.com" }
+ at endcode
+
+Note, that it is possible to specify additional attributes. They will be loaded and stored with the user's entry in the registry. This allows the library to be
+extended to perform additional actions based on these attributes.
+
+Upon start up the library will attempt to load this file.  If it does not exist
+the library will unload.
+
+### Configuring the DHCP Modules
+it must be configured as a hook library for the
+desired DHCP server modules.  Note that the user_chk library is installed alongside the BIND10 libraries in "<install-dir>/lib" where <install-dir> is determined by the --prefix option of the configure script.  It defaults to "/usr/local". Assuming the default value then, configuring b10-dhcp4 to load the user_chk
+library could be done with the following BIND10 configuration commands:
+
+ at code
+config add Dhcp4/hook_libraries
+config set Dhcp4/hook_libraries[0] "/usr/local/lib/libdhcp_user_chk.so"
+config commit
+ at endcode
+
+To configure it for b10-dhcp6, the commands are simply as shown below:
+
+ at code
+config add Dhcp6/hook_libraries
+config set Dhcp6/hook_libraries[0] "/usr/local/lib/libdhcp_user_chk.so"
+config commit
+ at endcode
+
+## User Check Outcome
+Once up and running, the library should begin adding entries to the outcome
+file.  Currently, the library uses a hard coded pathname for the user registry defined in load_unload.cc:
+
+    const char* user_chk_output_fname = "/tmp/user_chk_outcome.txt";
+
+If the file cannot be created (or opened), the library will unload.
+
+For each lease granted, the library will add the following information to the
+end of the file: the id type, the user id, the lease or prefix granted, and
+whether or not the user was found in the registry.  This information is written
+in the form of "name=value" with one value per line.  (See subnet_callout.cc for details.)
+
+A sample outcome file is shown below:
+
+ at code
+id_type=HW_ADDR
+client=hwtype=1 0c:0e:0a:01:ff:04
+addr=175.16.1.100
+registered=yes
+id_type=HW_ADDR
+client=hwtype=1 0c:0e:0a:01:ff:05
+addr=152.0.2.10
+registered=no
+id_type=HW_ADDR
+client=hwtype=1 0c:0e:0a:01:ff:06
+addr=175.16.1.101
+registered=yes
+id_type=HW_ADDR
+client=hwtype=1 0c:0e:0a:01:ff:04
+addr=175.16.1.102
+registered=yes
+id_type=DUID
+client=00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:04
+addr=2001:db8:2::1:0:0/96
+registered=yes
+id_type=DUID
+client=00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:04
+addr=2001:db8:2::
+registered=yes
+id_type=DUID
+client=00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:05
+addr=5005:778:2::
+registered=no
+id_type=DUID
+client=00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:06
+addr=2001:db8:2::1
+registered=yes
+ at endcode
+
+Note the library always opens this file in append mode and does not limit its size.
+
+
+*/
diff --git a/src/hooks/dhcp/user_chk/load_unload.cc b/src/hooks/dhcp/user_chk/load_unload.cc
index 58ef404..dff2622 100644
--- a/src/hooks/dhcp/user_chk/load_unload.cc
+++ b/src/hooks/dhcp/user_chk/load_unload.cc
@@ -12,7 +12,7 @@
 // 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.
+/// @file load_unload.cc Defines the load and unload hooks library functions.
 
 #include <hooks/hooks.h>
 #include <user_registry.h>
@@ -23,6 +23,7 @@
 #include <errno.h>
 
 using namespace isc::hooks;
+using namespace user_chk;
 
 /// @brief Pointer to the registry instance.
 UserRegistryPtr user_registry;
@@ -39,15 +40,21 @@ const char* registry_fname = "/tmp/user_chk_registry.txt";
 const char* user_chk_output_fname = "/tmp/user_chk_outcome.txt";
 
 /// @brief Text label of user id in the inbound query in callout context
-const char* query_user_id_label = "query_user_id_label";
+const char* query_user_id_label = "query_user_id";
 
 /// @brief Text label of registered user pointer in callout context
 const char* registered_user_label = "registered_user";
 
 /// @brief Text id used to identify the default IPv4 user in the registry
+/// The format is a string containing an even number of hex digits.  This
+/// value is to look up the default IPv4 user in the user registry for the
+/// the purpose of retrieving default values for user options.
 const char* default_user4_id_str = "00000000";
 
 /// @brief Text id used to identify the default IPv6 user in the registry
+/// The format is a string containing an even number of hex digits.  This
+/// value is to look up the default IPv6 user in the user registry for the
+/// the purpose of retrieving default values for user options.
 const char *default_user6_id_str = "00000000";
 
 // Functions accessed by the hooks framework use C linkage to avoid the name
diff --git a/src/hooks/dhcp/user_chk/pkt_receive_co.cc b/src/hooks/dhcp/user_chk/pkt_receive_co.cc
index d7814a3..bf00922 100644
--- a/src/hooks/dhcp/user_chk/pkt_receive_co.cc
+++ b/src/hooks/dhcp/user_chk/pkt_receive_co.cc
@@ -12,7 +12,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-/// @file Defines the pkt4_receive and pkt6_receive callout functions.
+/// @file pkt_receive.cc Defines the pkt4_receive and pkt6_receive callout functions.
 
 #include <hooks/hooks.h>
 #include <dhcp/pkt4.h>
@@ -22,6 +22,7 @@
 
 using namespace isc::dhcp;
 using namespace isc::hooks;
+using namespace user_chk;
 using namespace std;
 
 // Functions accessed by the hooks framework use C linkage to avoid the name
diff --git a/src/hooks/dhcp/user_chk/pkt_send_co.cc b/src/hooks/dhcp/user_chk/pkt_send_co.cc
index 3f49a6d..e13ea1b 100644
--- a/src/hooks/dhcp/user_chk/pkt_send_co.cc
+++ b/src/hooks/dhcp/user_chk/pkt_send_co.cc
@@ -12,7 +12,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-/// @file Defines the pkt4_send and pkt6_send callout functions.
+/// @file pkt_send.cc Defines the pkt4_send and pkt6_send callout functions.
 
 #include <asiolink/io_address.h>
 #include <hooks/hooks.h>
@@ -23,40 +23,41 @@
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option6_iaprefix.h>
+#include <dhcp/docsis3_option_defs.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt6.h>
 #include <user_chk.h>
 
 using namespace isc::dhcp;
 using namespace isc::hooks;
+using namespace user_chk;
 using namespace std;
 
+// prototypes for local helper functions
+void generate_output_record(const std::string& id_type_str,
+                            const std::string& id_val_str,
+                            const std::string& addr_str,
+                            const bool& registered);
+std::string getV6AddrStr (Pkt6Ptr response);
+std::string getAddrStrIA_NA(OptionPtr options);
+std::string getAddrStrIA_PD(OptionPtr options);
+bool checkIAStatus(boost::shared_ptr<Option6IA>& ia_opt);
+
+void add4Options(Pkt4Ptr& response, const UserPtr& user);
+void add4Option(Pkt4Ptr& response, uint8_t opt_code, std::string& opt_value);
+void add6Options(Pkt6Ptr& response, const UserPtr& user);
+void add6Option(OptionPtr& vendor, uint8_t opt_code, std::string& opt_value);
+const UserPtr& getDefaultUser4();
+const UserPtr& getDefaultUser6();
+
 // 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" {
 
-extern void generate_output_record(const std::string& id_type_str,
-                            const std::string& id_val_str,
-                            const std::string& addr_str,
-                            const bool& registered);
-extern std::string getV6AddrStr (Pkt6Ptr response);
-extern std::string getAddrStrIA_NA(OptionPtr options);
-extern std::string getAddrStrIA_PD(OptionPtr options);
-extern bool checkIAStatus(boost::shared_ptr<Option6IA>& ia_opt);
-
-extern void add4Options(Pkt4Ptr& response, const UserPtr& user);
-extern void add4Option(Pkt4Ptr& response, uint8_t opt_code,
-                        std::string& opt_value);
-extern void add6Options(Pkt6Ptr& response, const UserPtr& user);
-extern void add6Option(OptionPtr& vendor, uint8_t opt_code,
-                       std::string& opt_value);
-extern const UserPtr& getDefaultUser4();
-extern const UserPtr& getDefaultUser6();
-
 /// @brief  This callout is called at the "pkt4_send" hook.
 ///
-/// This function generates the user check outcome and adds vendor options
+/// This function generates the user check outcome and modifies options
 /// to the IPv4 response packet based on whether the user is registered or not.
 ///
 /// It retrieves a pointer to the registered user from the callout context.
@@ -77,7 +78,6 @@ int pkt4_send(CalloutHandle& handle) {
         Pkt4Ptr response;
         handle.getArgument("response4", response);
 
-        // @todo Determine list of types to process and skip the rest.
         uint8_t packet_type = response->getType();
         if (packet_type == DHCPNAK) {
             std::cout << "DHCP UserCheckHook : pkt4_send"
@@ -127,69 +127,9 @@ int pkt4_send(CalloutHandle& handle) {
     return (0);
 }
 
-/// @brief Adds IPv4 options to the response packet based on given user
-///
-/// Adds or replaces IPv4 options with values from the given user, if
-/// the user has corresponding properties defined. Currently it supports
-/// the following options:
-///
-/// - DHO_BOOT_FILE_NAME from user property "bootfile"
-/// - DHO_TFTP_SERVER_NAME from user property "tftp_server"
-///
-/// @param response IPv4 response packet
-/// @param user User from whom properties are sourced
-void add4Options(Pkt4Ptr& response, const UserPtr& user) {
-    // If user is null, do nothing.
-    if (!user) {
-        return;
-    }
-
-    // If the user has bootfile property, update it in the response.
-    std::string opt_value = user->getProperty("bootfile");
-    if (!opt_value.empty()) {
-        std::cout << "DHCP UserCheckHook : add4Options "
-              << "adding boot file:" << opt_value << std::endl;
-
-        // Add boot file to packet.
-        add4Option(response, DHO_BOOT_FILE_NAME, opt_value);
-
-        // Boot file also goes in file field.
-        response->setFile((const uint8_t*)(opt_value.c_str()),
-                          opt_value.length());
-    }
-
-    // If the user has tftp server property, update it in the response.
-    opt_value = user->getProperty("tftp_server");
-    if (!opt_value.empty()) {
-        std::cout << "DHCP UserCheckHook : add4Options "
-              << "adding TFTP server:" << opt_value << std::endl;
-
-        // Add tftp server option to packet.
-        add4Option(response, DHO_TFTP_SERVER_NAME, opt_value);
-    }
-    // add next option here
-}
-
-/// @brief Adds/updates are specific IPv4 string option in response packet.
-///
-/// @param response IPV4 response packet to update
-/// @param opt_code DHCP standard numeric code of the option
-/// @param opt_value String value of the option
-void add4Option(Pkt4Ptr& response, uint8_t opt_code, std::string& opt_value) {
-    // Remove the option if it exists.
-    OptionPtr opt = response->getOption(opt_code);
-    if (opt) {
-        response->delOption(opt_code);
-    }
-
-    // Now add the option.
-    opt.reset(new OptionString(Option::V4, opt_code, opt_value));
-    response->addOption(opt);
-}
-
 /// @brief  This callout is called at the "pkt6_send" hook.
 ///
-/// This function generates the user check outcome and adds vendor options
+/// This function generates the user check outcome and modifies options
 /// to the IPv6 response packet based on whether the user is registered or not.
 ///
 /// It retrieves a pointer to the registered user from the callout context.
@@ -263,6 +203,69 @@ int pkt6_send(CalloutHandle& handle) {
     return (0);
 }
 
+} // extern C
+
+/// @brief Adds IPv4 options to the response packet based on given user
+///
+/// Adds or replaces IPv4 options with values from the given user, if
+/// the user has corresponding properties defined. Currently it supports
+/// the following options:
+///
+/// - DHO_BOOT_FILE_NAME from user property "bootfile"
+/// - DHO_TFTP_SERVER_NAME from user property "tftp_server"
+///
+/// @param response IPv4 response packet
+/// @param user User from whom properties are sourced
+void add4Options(Pkt4Ptr& response, const UserPtr& user) {
+    // If user is null, do nothing.
+    if (!user) {
+        return;
+    }
+
+    // If the user has bootfile property, update it in the response.
+    std::string opt_value = user->getProperty("bootfile");
+    if (!opt_value.empty()) {
+        std::cout << "DHCP UserCheckHook : add4Options "
+              << "adding boot file:" << opt_value << std::endl;
+
+        // Add boot file to packet.
+        add4Option(response, DHO_BOOT_FILE_NAME, opt_value);
+
+        // Boot file also goes in file field.
+        response->setFile((const uint8_t*)(opt_value.c_str()),
+                          opt_value.length());
+    }
+
+    // If the user has tftp server property, update it in the response.
+    opt_value = user->getProperty("tftp_server");
+    if (!opt_value.empty()) {
+        std::cout << "DHCP UserCheckHook : add4Options "
+              << "adding TFTP server:" << opt_value << std::endl;
+
+        // Add tftp server option to packet.
+        add4Option(response, DHO_TFTP_SERVER_NAME, opt_value);
+    }
+    // add next option here
+}
+
+/// @brief Adds/updates are specific IPv4 string option in response packet.
+///
+/// @param response IPV4 response packet to update
+/// @param opt_code DHCP standard numeric code of the option
+/// @param opt_value String value of the option
+void add4Option(Pkt4Ptr& response, uint8_t opt_code, std::string& opt_value) {
+    // Remove the option if it exists.
+    OptionPtr opt = response->getOption(opt_code);
+    if (opt) {
+        response->delOption(opt_code);
+    }
+
+    // Now add the option.
+    opt.reset(new OptionString(Option::V4, opt_code, opt_value));
+    response->addOption(opt);
+}
+
+
 /// @brief Adds IPv6 vendor options to the response packet based on given user
 ///
 /// Adds or replaces IPv6 vendor options with values from the given user, if
@@ -279,7 +282,7 @@ void add6Options(Pkt6Ptr& response, const UserPtr& user) {
         return;
     }
 
-    /// @todo: no packets have vendor opt... do we need to add it
+    /// @todo: if packets have no vendor opt... do we need to add it
     /// if its not there?  If so how?
     OptionPtr vendor = response->getOption(D6O_VENDOR_OPTS);
     if (!vendor) {
@@ -288,17 +291,12 @@ void add6Options(Pkt6Ptr& response, const UserPtr& user) {
         return;
     }
 
-    /// @todo: Use hard coded values (33,32) until we're merged.
-    /// Unfortunately, 3207 was branched from master before
-    /// 3194 was merged in, so this branch does not have
-    /// src/lib/dhcp/docsis3_option_defs.h.
-
     // If the user defines bootfile, set the option in response.
     std::string opt_value = user->getProperty("bootfile");
     if (!opt_value.empty()) {
         std::cout << "DHCP UserCheckHook : add6Options "
                   << "adding boot file:" << opt_value << std::endl;
-        add6Option(vendor, 33, opt_value);
+        add6Option(vendor, DOCSIS3_V6_CONFIG_FILE, opt_value);
     }
 
     // If the user defines tftp server, set the option in response.
@@ -307,7 +305,7 @@ void add6Options(Pkt6Ptr& response, const UserPtr& user) {
         std::cout << "DHCP UserCheckHook : add6Options "
                   << "adding tftp server:" << opt_value << std::endl;
 
-        add6Option(vendor, 32, opt_value);
+        add6Option(vendor, DOCSIS3_V6_TFTP_SERVERS, opt_value);
     }
 
     // add next option here
@@ -390,14 +388,16 @@ void generate_output_record(const std::string& id_type_str,
 /// @brief Stringify the lease address or prefix IPv6 response packet
 ///
 /// Converts the lease value, either an address or a prefix, into a string
-/// suitable for the user check outcome output.
+/// suitable for the user check outcome output.  Note that this will use
+/// the first address or prefix in the response for responses with more than
+/// one value.
 ///
 /// @param response IPv6 response packet from which to extract the lease value.
 ///
 /// @return A string containing the lease value.
 /// @throw isc::BadValue if the response contains neither an IA_NA nor IA_PD
 /// option.
-std::string getV6AddrStr (Pkt6Ptr response) {
+std::string getV6AddrStr(Pkt6Ptr response) {
     OptionPtr tmp = response->getOption(D6O_IA_NA);
     if (tmp) {
         return(getAddrStrIA_NA(tmp));
@@ -548,4 +548,3 @@ const UserPtr& getDefaultUser6() {
                                           default_user6_id_str)));
 }
 
-} // end extern "C"
diff --git a/src/hooks/dhcp/user_chk/subnet_select_co.cc b/src/hooks/dhcp/user_chk/subnet_select_co.cc
index 8a2d2e0..c30f37b 100644
--- a/src/hooks/dhcp/user_chk/subnet_select_co.cc
+++ b/src/hooks/dhcp/user_chk/subnet_select_co.cc
@@ -12,7 +12,7 @@
 // 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.
+/// @file subnet_select.cc Defines the subnet4_select and subnet6_select callout functions.
 
 #include <hooks/hooks.h>
 #include <dhcp/pkt4.h>
@@ -23,6 +23,7 @@
 
 using namespace isc::dhcp;
 using namespace isc::hooks;
+using namespace user_chk;
 using namespace std;
 
 // Functions accessed by the hooks framework use C linkage to avoid the name
diff --git a/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc b/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc
index 5e47bda..bc98c9a 100644
--- a/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc
+++ b/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc
@@ -22,6 +22,7 @@
 #include <gtest/gtest.h>
 
 using namespace std;
+using namespace user_chk;
 
 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
index 1f250c8..44e87c1 100644
--- a/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc
+++ b/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc
@@ -24,6 +24,7 @@
 #include <gtest/gtest.h>
 
 using namespace std;
+using namespace user_chk;
 
 namespace {
 
diff --git a/src/hooks/dhcp/user_chk/tests/user_unittests.cc b/src/hooks/dhcp/user_chk/tests/user_unittests.cc
index 2ca156c..8d76732 100644
--- a/src/hooks/dhcp/user_chk/tests/user_unittests.cc
+++ b/src/hooks/dhcp/user_chk/tests/user_unittests.cc
@@ -21,6 +21,7 @@
 #include <gtest/gtest.h>
 
 using namespace std;
+using namespace user_chk;
 
 namespace {
 
diff --git a/src/hooks/dhcp/user_chk/tests/userid_unittests.cc b/src/hooks/dhcp/user_chk/tests/userid_unittests.cc
index 10e5d4e..cc96353 100644
--- a/src/hooks/dhcp/user_chk/tests/userid_unittests.cc
+++ b/src/hooks/dhcp/user_chk/tests/userid_unittests.cc
@@ -21,6 +21,7 @@
 #include <gtest/gtest.h>
 
 using namespace std;
+using namespace user_chk;
 
 namespace {
 
diff --git a/src/hooks/dhcp/user_chk/user.cc b/src/hooks/dhcp/user_chk/user.cc
index 681f64a..2bf95cf 100644
--- a/src/hooks/dhcp/user_chk/user.cc
+++ b/src/hooks/dhcp/user_chk/user.cc
@@ -22,6 +22,8 @@
 #include <iomanip>
 #include <sstream>
 
+namespace user_chk {
+
 //********************************* UserId ******************************
 
 const char* UserId::HW_ADDRESS_STR = "HW_ADDR";
@@ -215,3 +217,5 @@ const UserId&
 User::getUserId() const {
     return (user_id_);
 }
+
+} // namespace user_chk
diff --git a/src/hooks/dhcp/user_chk/user.h b/src/hooks/dhcp/user_chk/user.h
index cd1f28c..b2d0f16 100644
--- a/src/hooks/dhcp/user_chk/user.h
+++ b/src/hooks/dhcp/user_chk/user.h
@@ -21,6 +21,8 @@
 #include <stdint.h>
 #include <vector>
 
+namespace user_chk {
+
 /// @file user.h This file defines classes: UserId and User.
 /// @brief These classes are used to describe and recognize DHCP lease
 /// clients.
@@ -246,4 +248,6 @@ private:
 /// @brief Defines a smart pointer to a User.
 typedef boost::shared_ptr<User> UserPtr;
 
+} // namespace user_chk
+
 #endif
diff --git a/src/hooks/dhcp/user_chk/user_chk.h b/src/hooks/dhcp/user_chk/user_chk.h
index d19a7c5..1bc2fc3 100644
--- a/src/hooks/dhcp/user_chk/user_chk.h
+++ b/src/hooks/dhcp/user_chk/user_chk.h
@@ -19,6 +19,10 @@
 #include <string>
 
 using namespace std;
+using namespace user_chk;
+
+// The following constants are used throughout the library.  They are defined
+// in load_unload.cc
 
 /// @brief Pointer to the registry instance.
 extern UserRegistryPtr user_registry;
@@ -42,6 +46,6 @@ extern const char* registered_user_label;
 extern const char* default_user4_id_str;
 
 /// @brief Text id used to identify the default IPv6 user in the registry
-extern const char *default_user6_id_str;
+extern const char* default_user6_id_str;
 
 #endif
diff --git a/src/hooks/dhcp/user_chk/user_chk_log.cc b/src/hooks/dhcp/user_chk/user_chk_log.cc
index f104efb..7e4d87f 100644
--- a/src/hooks/dhcp/user_chk/user_chk_log.cc
+++ b/src/hooks/dhcp/user_chk/user_chk_log.cc
@@ -15,4 +15,8 @@
 /// Defines the logger used by the user check hooks library.
 #include <user_chk_log.h>
 
+namespace user_chk {
+
 isc::log::Logger user_chk_logger("user_chk");
+
+} // namespace user_chk
diff --git a/src/hooks/dhcp/user_chk/user_data_source.h b/src/hooks/dhcp/user_chk/user_data_source.h
index a1842cf..5145e10 100644
--- a/src/hooks/dhcp/user_chk/user_data_source.h
+++ b/src/hooks/dhcp/user_chk/user_data_source.h
@@ -18,6 +18,8 @@
 #include <exceptions/exceptions.h>
 #include <user.h>
 
+namespace user_chk {
+
 /// @brief Thrown if UserDataSource encounters an error
 class UserDataSourceError : public isc::Exception {
 public:
@@ -72,4 +74,6 @@ public:
 /// @brief Defines a smart pointer to a UserDataSource.
 typedef boost::shared_ptr<UserDataSource> UserDataSourcePtr;
 
+} // namespace user_chk
+
 #endif
diff --git a/src/hooks/dhcp/user_chk/user_file.cc b/src/hooks/dhcp/user_chk/user_file.cc
index 4181dc4..3ffc99e 100644
--- a/src/hooks/dhcp/user_chk/user_file.cc
+++ b/src/hooks/dhcp/user_chk/user_file.cc
@@ -20,6 +20,8 @@
 #include <errno.h>
 #include <iostream>
 
+namespace user_chk {
+
 UserFile::UserFile(const std::string& fname) : fname_(fname), file_() {
     if (fname_.empty()) {
         isc_throw(UserFileError, "file name cannot be blank");
@@ -157,3 +159,4 @@ UserFile::close() {
     }
 }
 
+} // namespace user_chk
diff --git a/src/hooks/dhcp/user_chk/user_file.h b/src/hooks/dhcp/user_chk/user_file.h
index c0731f0..873dddf 100644
--- a/src/hooks/dhcp/user_chk/user_file.h
+++ b/src/hooks/dhcp/user_chk/user_file.h
@@ -25,6 +25,8 @@
 
 using namespace std;
 
+namespace user_chk {
+
 /// @brief Thrown a UserFile encounters an error.
 /// Note that it derives from UserDataSourceError to comply with the interface.
 class UserFileError : public UserDataSourceError {
@@ -133,4 +135,6 @@ private:
 /// @brief Defines a smart pointer to a UserFile.
 typedef boost::shared_ptr<UserFile> UserFilePtr;
 
+} // namespace user_chk
+
 #endif
diff --git a/src/hooks/dhcp/user_chk/user_registry.cc b/src/hooks/dhcp/user_chk/user_registry.cc
index 44b98a5..80e7a93 100644
--- a/src/hooks/dhcp/user_chk/user_registry.cc
+++ b/src/hooks/dhcp/user_chk/user_registry.cc
@@ -15,6 +15,8 @@
 #include <user_registry.h>
 #include <user.h>
 
+namespace user_chk {
+
 UserRegistry::UserRegistry() {
 }
 
@@ -120,3 +122,4 @@ const UserDataSourcePtr& UserRegistry::getSource() {
     return (source_);
 }
 
+} // namespace user_chk
diff --git a/src/hooks/dhcp/user_chk/user_registry.h b/src/hooks/dhcp/user_chk/user_registry.h
index a0fb7dc..48dc32c 100644
--- a/src/hooks/dhcp/user_chk/user_registry.h
+++ b/src/hooks/dhcp/user_chk/user_registry.h
@@ -26,6 +26,8 @@
 
 using namespace std;
 
+namespace user_chk {
+
 /// @brief Thrown UserRegistry encounters an error
 class UserRegistryError : public isc::Exception {
 public:
@@ -125,4 +127,6 @@ private:
 /// @brief Define a smart pointer to a UserRegistry.
 typedef boost::shared_ptr<UserRegistry> UserRegistryPtr;
 
+} // namespace user_chk
+
 #endif
diff --git a/src/lib/asiodns/Makefile.am b/src/lib/asiodns/Makefile.am
index 5fc436d..dab1a32 100644
--- a/src/lib/asiodns/Makefile.am
+++ b/src/lib/asiodns/Makefile.am
@@ -38,10 +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/asiolink/libb10-asiolink.la
 libb10_asiodns_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
diff --git a/src/lib/asiodns/tcp_server.cc b/src/lib/asiodns/tcp_server.cc
index 5f01d2f..2e96671 100644
--- a/src/lib/asiodns/tcp_server.cc
+++ b/src/lib/asiodns/tcp_server.cc
@@ -31,8 +31,11 @@
 #include <sys/socket.h>
 #include <errno.h>
 
-using namespace asio;
-using asio::ip::udp;
+// Note: we intentionally avoid 'using namespace asio' to avoid conflicts with
+// std:: definitions in C++11.
+using asio::io_service;
+using asio::buffer;
+using asio::const_buffer;
 using asio::ip::tcp;
 
 using namespace std;
@@ -107,7 +110,7 @@ TCPServer::operator()(asio::error_code ec, size_t length) {
                 CORO_YIELD acceptor_->async_accept(*socket_, *this);
                 if (ec) {
                     using namespace asio::error;
-                    const error_code::value_type err_val = ec.value();
+                    const asio::error_code::value_type err_val = ec.value();
                     // The following two cases can happen when this server is
                     // stopped: operation_aborted in case it's stopped after
                     // starting accept().  bad_descriptor in case it's stopped
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/asiodns/tests/dns_server_unittest.cc b/src/lib/asiodns/tests/dns_server_unittest.cc
index d59bf5e..b2fff3e 100644
--- a/src/lib/asiodns/tests/dns_server_unittest.cc
+++ b/src/lib/asiodns/tests/dns_server_unittest.cc
@@ -274,6 +274,7 @@ class TCPClient : public SimpleClient {
         server_ = server;
         socket_.reset(new ip::tcp::socket(service));
         socket_->open(ip::tcp::v6());
+        send_delay_timer_.reset(new deadline_timer(service));
     }
 
 
@@ -324,15 +325,32 @@ class TCPClient : public SimpleClient {
                                 size_t send_bytes)
     {
         if (!error && send_bytes == 2 && send_data_len_delay_ == 0) {
-            sleep(send_data_delay_);
-            socket_->async_send(buffer(data_to_send_.c_str(),
-                                       data_to_send_.size() + 1),
-                    boost::bind(&TCPClient::finishSendHandler, this, _1, _2));
+            // We cannot block here (such as by sleep(3)) since otherwise
+            // the ASIO events may not reliably handled in the server side
+            // as the test expects.  So we use async_wait, and make sure the
+            // control will be given back to the IO service.
+            if (send_data_delay_ > 0) {
+                send_delay_timer_->expires_from_now(boost::posix_time::
+                                                    seconds(send_data_delay_));
+                send_delay_timer_->async_wait(
+                    boost::bind(&TCPClient::sendMessageData, this));
+                return;
+            }
+            sendMessageData();
         }
     }
 
+    void sendMessageData() {
+        socket_->async_send(buffer(data_to_send_.c_str(),
+                                   data_to_send_.size() + 1),
+                            boost::bind(&TCPClient::finishSendHandler, this,
+                                        _1, _2));
+    }
+
     void finishSendHandler(const asio::error_code& error, size_t send_bytes) {
-        if (!error && send_bytes == data_to_send_.size() + 1) {
+        if (error) {
+            getResponseCallBack(error, 0);
+        } else if (send_bytes == data_to_send_.size() + 1) {
             socket_->async_receive(buffer(received_data_, MAX_DATA_LEN),
                    boost::bind(&SimpleClient::getResponseCallBack, this, _1,
                                _2));
@@ -343,6 +361,7 @@ class TCPClient : public SimpleClient {
     ip::tcp::endpoint server_;
     std::string data_to_send_;
     uint16_t data_to_send_len_;
+    boost::shared_ptr<deadline_timer> send_delay_timer_;
 
     size_t send_data_delay_;
     size_t send_data_len_delay_;
diff --git a/src/lib/asiodns/udp_server.cc b/src/lib/asiodns/udp_server.cc
index 649ea92..54fdfd7 100644
--- a/src/lib/asiodns/udp_server.cc
+++ b/src/lib/asiodns/udp_server.cc
@@ -31,8 +31,12 @@
 
 #include <dns/opcode.h>
 
-using namespace asio;
+// Avoid 'using namespace asio' (see tcp_server.cc)
+using asio::io_service;
+using asio::socket_base;
+using asio::buffer;
 using asio::ip::udp;
+using asio::ip::address;
 
 using namespace std;
 using namespace isc::dns;
@@ -56,7 +60,7 @@ struct UDPServer::Data {
      * query, it will only hold parameters until we wait for the
      * first packet. But we do initialize the socket in here.
      */
-    Data(io_service& io_service, const ip::address& addr, const uint16_t port,
+    Data(io_service& io_service, const address& addr, const uint16_t port,
          DNSLookup* lookup, DNSAnswer* answer) :
         io_(io_service), bytes_(0), done_(false),
         lookup_callback_(lookup),
@@ -210,7 +214,7 @@ UDPServer::operator()(asio::error_code ec, size_t length) {
                 // See TCPServer::operator() for details on error handling.
                 if (ec) {
                     using namespace asio::error;
-                    const error_code::value_type err_val = ec.value();
+                    const asio::error_code::value_type err_val = ec.value();
                     if (err_val == operation_aborted ||
                         err_val == bad_descriptor) {
                         return;
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/cc/Makefile.am b/src/lib/cc/Makefile.am
index 1b1e611..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
diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc
index 3518939..a06ccdd 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");
@@ -270,11 +275,11 @@ skipChars(std::istream& in, const char* chars, int& line, int& pos) {
 }
 
 // skip on the input stream to one of the characters in chars
-// if another character is found this function returns false
+// if another character is found this function throws JSONError
 // unless that character is specified in the optional may_skip
 //
-// the character found is left on the stream
-void
+// It returns the found character (as an int value).
+int
 skipTo(std::istream& in, const std::string& file, int& line,
        int& pos, const char* chars, const char* may_skip="")
 {
@@ -293,18 +298,18 @@ skipTo(std::istream& in, const std::string& file, int& line,
                 if (in.peek() == '\n') {
                     pos = 1;
                     ++line;
+                } else {
+                    ++pos;
                 }
                 in.ignore();
-                ++pos;
             }
-            in.putback(c);
-            --pos;
-            return;
+            return (c);
         } else {
             throwJSONError(std::string("'") + std::string(1, c) + "' read, one of \"" + chars + "\" expected", file, line, pos);
         }
     }
     throwJSONError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
+    return (c); // shouldn't reach here, but some compilers require it
 }
 
 // TODO: Should we check for all other official escapes here (and
@@ -461,10 +466,11 @@ fromStringstreamList(std::istream& in, const std::string& file, int& line,
         if (in.peek() != ']') {
             cur_list_element = Element::fromJSON(in, file, line, pos);
             list->add(cur_list_element);
-            skipTo(in, file, line, pos, ",]", WHITESPACE);
+            c = skipTo(in, file, line, pos, ",]", WHITESPACE);
+        } else {
+            c = in.get();
+            ++pos;
         }
-        c = in.get();
-        pos++;
     }
     return (list);
 }
@@ -487,15 +493,11 @@ fromStringstreamMap(std::istream& in, const std::string& file, int& line,
 
             skipTo(in, file, line, pos, ":", WHITESPACE);
             // skip the :
-            in.ignore();
-            pos++;
 
             ConstElementPtr value = Element::fromJSON(in, file, line, pos);
             map->set(key, value);
 
-            skipTo(in, file, line, pos, ",}", WHITESPACE);
-            c = in.get();
-            pos++;
+            c = skipTo(in, file, line, pos, ",}", WHITESPACE);
         }
     }
     return (map);
@@ -591,6 +593,7 @@ Element::fromJSON(std::istream& in, const std::string& file, int& line,
             case '+':
             case '.':
                 in.putback(c);
+                --pos;
                 element = fromStringstreamNumber(in, pos);
                 el_read = true;
                 break;
@@ -599,17 +602,20 @@ Element::fromJSON(std::istream& in, const std::string& file, int& line,
             case 'f':
             case 'F':
                 in.putback(c);
+                --pos;
                 element = fromStringstreamBool(in, file, line, pos);
                 el_read = true;
                 break;
             case 'n':
             case 'N':
                 in.putback(c);
+                --pos;
                 element = fromStringstreamNull(in, file, line, pos);
                 el_read = true;
                 break;
             case '"':
                 in.putback('"');
+                --pos;
                 element = fromStringstreamString(in, file, line, pos);
                 el_read = true;
                 break;
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..fa8e5fc 100644
--- a/src/lib/cc/tests/data_unittests.cc
+++ b/src/lib/cc/tests/data_unittests.cc
@@ -98,16 +98,24 @@ TEST(Element, from_and_to_json) {
     sv.push_back("\"\xFF\"");
 
     BOOST_FOREACH(const std::string& s, sv) {
-        // test << operator, which uses Element::str()
-        std::ostringstream stream;
-        el = Element::fromJSON(s);
-        stream << *el;
-        EXPECT_EQ(s, stream.str());
-
-        // test toWire(ostream), which should also be the same now
-        std::ostringstream wire_stream;
-        el->toWire(wire_stream);
-        EXPECT_EQ(s, wire_stream.str());
+        // Test two types of fromJSON(): with string and istream.
+        for (int i = 0; i < 2; ++i) {
+            // test << operator, which uses Element::str()
+            if (i == 0) {
+                el = Element::fromJSON(s);
+            } else {
+                std::istringstream iss(s);
+                el = Element::fromJSON(iss);
+            }
+            std::ostringstream stream;
+            stream << *el;
+            EXPECT_EQ(s, stream.str());
+
+            // test toWire(ostream), which should also be the same now
+            std::ostringstream wire_stream;
+            el->toWire(wire_stream);
+            EXPECT_EQ(s, wire_stream.str());
+        }
     }
 
     // some parse errors
@@ -418,6 +426,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 +450,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 +474,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 +498,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 +509,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/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/cache_config.h b/src/lib/datasrc/cache_config.h
index 35b5ead..c221066 100644
--- a/src/lib/datasrc/cache_config.h
+++ b/src/lib/datasrc/cache_config.h
@@ -17,6 +17,7 @@
 
 #include <exceptions/exceptions.h>
 
+#include <dns/name.h>
 #include <dns/dns_fwd.h>
 #include <cc/data.h>
 #include <datasrc/memory/load_action.h>
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/tests/client_list_unittest.cc b/src/lib/datasrc/tests/client_list_unittest.cc
index 256e2ed..7de5cda 100644
--- a/src/lib/datasrc/tests/client_list_unittest.cc
+++ b/src/lib/datasrc/tests/client_list_unittest.cc
@@ -47,9 +47,8 @@ using isc::datasrc::memory::ZoneTableSegment;
 using isc::datasrc::memory::InMemoryZoneFinder;
 using namespace isc::data;
 using namespace isc::dns;
-// don't import the entire boost namespace.  It will unexpectedly hide uintXX_t
-// for some systems.
-using boost::shared_ptr;
+// note: don't use 'using [namespace]' for shared_ptr.  It would conflict with
+// C++ std:: definitions.
 using namespace std;
 
 namespace {
@@ -79,7 +78,7 @@ public:
         if (type == "MasterFiles") {
             return (DataSourcePair(0, DataSourceClientContainerPtr()));
         }
-        shared_ptr<MockDataSourceClient>
+        boost::shared_ptr<MockDataSourceClient>
             ds(new MockDataSourceClient(type, configuration));
         // Make sure it is deleted when the test list is deleted.
         to_delete_.push_back(ds);
@@ -88,7 +87,7 @@ public:
 private:
     // Hold list of data sources created internally, so they are preserved
     // until the end of the test and then deleted.
-    vector<shared_ptr<MockDataSourceClient> > to_delete_;
+    vector<boost::shared_ptr<MockDataSourceClient> > to_delete_;
 };
 
 const char* ds_zones[][3] = {
@@ -144,7 +143,7 @@ public:
             "}]"))
     {
         for (size_t i(0); i < ds_count; ++ i) {
-            shared_ptr<MockDataSourceClient>
+            boost::shared_ptr<MockDataSourceClient>
                 ds(new MockDataSourceClient(ds_zones[i]));
             ds_.push_back(ds);
             ds_info_.push_back(ConfigurableClientList::DataSourceInfo(
@@ -230,7 +229,7 @@ public:
     }
     // Check the positive result is as we expect it.
     void positiveResult(const ClientList::FindResult& result,
-                        const shared_ptr<MockDataSourceClient>& dsrc,
+                        const boost::shared_ptr<MockDataSourceClient>& dsrc,
                         const Name& name, bool exact,
                         const char* test, bool from_cache = false)
     {
@@ -242,10 +241,10 @@ public:
         // alive, even when we don't know what it is.
         // Any better idea how to test it actually keeps the thing
         // alive?
-        EXPECT_NE(shared_ptr<ClientList::FindResult::LifeKeeper>(),
+        EXPECT_NE(boost::shared_ptr<ClientList::FindResult::LifeKeeper>(),
                   result.life_keeper_);
         if (from_cache) {
-            EXPECT_NE(shared_ptr<InMemoryZoneFinder>(),
+            EXPECT_NE(boost::shared_ptr<InMemoryZoneFinder>(),
                       boost::dynamic_pointer_cast<InMemoryZoneFinder>(
                           result.finder_)) << "Finder is not from cache";
             EXPECT_TRUE(NULL !=
@@ -308,7 +307,7 @@ public:
         EXPECT_EQ(type, ds->type_);
         EXPECT_TRUE(Element::fromJSON(params)->equals(*ds->configuration_));
         EXPECT_EQ(cache, list_->getDataSources()[index].cache_ !=
-                  shared_ptr<InMemoryClient>());
+                  boost::shared_ptr<InMemoryClient>());
     }
     ConfigurableClientList::CacheStatus doReload(
         const Name& origin, const string& datasrc_name = "");
@@ -316,9 +315,9 @@ public:
         int numZones, const string& zoneName);
 
     const RRClass rrclass_;
-    shared_ptr<TestedList> list_;
+    boost::shared_ptr<TestedList> list_;
     const ClientList::FindResult negative_result_;
-    vector<shared_ptr<MockDataSourceClient> > ds_;
+    vector<boost::shared_ptr<MockDataSourceClient> > ds_;
     vector<ConfigurableClientList::DataSourceInfo> ds_info_;
     const ConstElementPtr config_elem_, config_elem_zones_;
 };
@@ -401,7 +400,7 @@ TEST_P(ListTest, selfTest) {
     EXPECT_EQ(result::NOTFOUND, ds_[0]->findZone(Name("aaa")).code);
     EXPECT_EQ(result::NOTFOUND, ds_[0]->findZone(Name("zzz")).code);
     // Nothing to keep alive here.
-    EXPECT_EQ(shared_ptr<ClientList::FindResult::LifeKeeper>(),
+    EXPECT_EQ(boost::shared_ptr<ClientList::FindResult::LifeKeeper>(),
                   negative_result_.life_keeper_);
 }
 
@@ -804,7 +803,8 @@ TEST_P(ListTest, cacheZones) {
     checkDS(0, "type1", "[\"example.org\", \"example.com\", \"exmaple.cz\"]",
             true);
 
-    const shared_ptr<InMemoryClient> cache(list_->getDataSources()[0].cache_);
+    const boost::shared_ptr<InMemoryClient> cache(
+        list_->getDataSources()[0].cache_);
     EXPECT_EQ(2, cache->getZoneCount());
 
     EXPECT_EQ(result::SUCCESS, cache->findZone(Name("example.org")).code);
@@ -840,7 +840,8 @@ TEST_P(ListTest, badCache) {
         "}]"));
     list_->configure(elem1, true); // shouldn't cause disruption
     checkDS(0, "test_type", "[\"example.org\"]", true);
-    const shared_ptr<InMemoryClient> cache(list_->getDataSources()[0].cache_);
+    const boost::shared_ptr<InMemoryClient> cache(
+        list_->getDataSources()[0].cache_);
     EXPECT_EQ(1, cache->getZoneCount());
     EXPECT_EQ(result::SUCCESS, cache->findZone(Name("example.org")).code);
     // Now, the zone doesn't give an iterator
@@ -895,7 +896,8 @@ TEST_P(ListTest,
         "}]"));
     list_->configure(elem, true); // no disruption
     checkDS(0, "test_type", "[\"example.org\"]", true);
-    const shared_ptr<InMemoryClient> cache(list_->getDataSources()[0].cache_);
+    const boost::shared_ptr<InMemoryClient> cache(
+        list_->getDataSources()[0].cache_);
 
     // Likewise, reload attempt will fail.
     EXPECT_EQ(ConfigurableClientList::CACHE_NOT_WRITABLE,
diff --git a/src/lib/datasrc/tests/memory/zone_finder_unittest.cc b/src/lib/datasrc/tests/memory/zone_finder_unittest.cc
index e07ab27..d3c7f49 100644
--- a/src/lib/datasrc/tests/memory/zone_finder_unittest.cc
+++ b/src/lib/datasrc/tests/memory/zone_finder_unittest.cc
@@ -44,7 +44,6 @@ using namespace isc::dns;
 using namespace isc::dns::rdata;
 using namespace isc::datasrc;
 using namespace isc::testutils;
-using boost::shared_ptr;
 using namespace isc::datasrc::test;
 using namespace isc::datasrc::memory::test;
 using namespace isc::datasrc::memory;
@@ -1612,7 +1611,7 @@ TEST_F(InMemoryZoneFinderTest, findOrphanRRSIG) {
 // handling)
 TEST_F(InMemoryZoneFinderTest, NSECNonExistentTest) {
     const Name name("example.com.");
-    shared_ptr<ZoneTableSegment> ztable_segment(
+    boost::shared_ptr<ZoneTableSegment> ztable_segment(
          new ZoneTableSegmentMock(class_, mem_sgmt_));
     updater_.reset();
     loadZoneIntoTable(*ztable_segment, name, class_,
@@ -1775,7 +1774,7 @@ TEST_F(InMemoryZoneFinderNSEC3Test, findNSEC3MissingOrigin) {
      setNSEC3HashCreator(&creator);
 
      const Name name("example.com.");
-     shared_ptr<ZoneTableSegment> ztable_segment(
+     boost::shared_ptr<ZoneTableSegment> ztable_segment(
           new ZoneTableSegmentMock(class_, mem_sgmt_));
      updater_.reset();
      loadZoneIntoTable(*ztable_segment, name, class_,
diff --git a/src/lib/datasrc/tests/test_client.cc b/src/lib/datasrc/tests/test_client.cc
index c7854ed..f521a6d 100644
--- a/src/lib/datasrc/tests/test_client.cc
+++ b/src/lib/datasrc/tests/test_client.cc
@@ -32,7 +32,6 @@
 #include <fstream>
 
 using namespace std;
-using boost::shared_ptr;
 
 using namespace isc::dns;
 
@@ -48,7 +47,7 @@ addRRset(ZoneUpdaterPtr updater, ConstRRsetPtr rrset) {
 }
 }
 
-shared_ptr<DataSourceClient>
+boost::shared_ptr<DataSourceClient>
 createSQLite3Client(RRClass zclass, const Name& zname,
                     const char* const db_file, const char* const zone_file)
 {
@@ -60,7 +59,7 @@ createSQLite3Client(RRClass zclass, const Name& zname,
     return (createSQLite3Client(zclass, zname, db_file, ifs));
 }
 
-shared_ptr<DataSourceClient>
+boost::shared_ptr<DataSourceClient>
 createSQLite3Client(RRClass zclass, const Name& zname,
                     const char* const db_file, istream& rr_stream)
 {
@@ -75,9 +74,10 @@ createSQLite3Client(RRClass zclass, const Name& zname,
                   "Error setting up; command failed: " << install_cmd);
     }
 
-    shared_ptr<SQLite3Accessor> accessor(
+    boost::shared_ptr<SQLite3Accessor> accessor(
         new SQLite3Accessor(db_file, zclass.toText()));
-    shared_ptr<DatabaseClient> client(new DatabaseClient(zclass, accessor));
+    boost::shared_ptr<DatabaseClient> client(new DatabaseClient(zclass,
+                                                                accessor));
 
     ZoneUpdaterPtr updater = client->getUpdater(zname, true);
     masterLoad(rr_stream, zname, zclass, boost::bind(addRRset, updater, _1));
diff --git a/src/lib/datasrc/tests/zone_finder_context_unittest.cc b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
index 65e9f6c..67118e9 100644
--- a/src/lib/datasrc/tests/zone_finder_context_unittest.cc
+++ b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
@@ -42,7 +42,6 @@
 #include <vector>
 
 using namespace std;
-using boost::shared_ptr;
 
 using namespace isc::data;
 using namespace isc::util;
@@ -59,7 +58,7 @@ namespace {
 const char* const TEST_ZONE_FILE = TEST_DATA_DIR "/contexttest.zone";
 
 // Convenient shortcut
-typedef shared_ptr<DataSourceClient> DataSourceClientPtr;
+typedef boost::shared_ptr<DataSourceClient> DataSourceClientPtr;
 
 // This is the type used as the test parameter.  Note that this is
 // intentionally a plain old type (i.e. a function pointer), not a class;
@@ -75,7 +74,7 @@ createInMemoryClient(RRClass zclass, const Name& zname) {
             " \"params\":"
             "  {\"" + zname.toText() + "\": \"" +
             string(TEST_ZONE_FILE) + "\"}}"), true);
-    shared_ptr<ZoneTableSegment> ztable_segment(
+    boost::shared_ptr<ZoneTableSegment> ztable_segment(
         ZoneTableSegment::create(zclass, cache_conf.getSegmentType()));
     memory::ZoneWriter writer(*ztable_segment,
                               cache_conf.getLoadAction(zclass, zname),
@@ -83,8 +82,8 @@ createInMemoryClient(RRClass zclass, const Name& zname) {
     writer.load();
     writer.install();
     writer.cleanup();
-    shared_ptr<InMemoryClient> client(new InMemoryClient(ztable_segment,
-                                                         zclass));
+    boost::shared_ptr<InMemoryClient> client(new InMemoryClient(ztable_segment,
+                                                                zclass));
 
     return (client);
 }
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index 7402dbe..eb9309a 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -29,6 +29,7 @@ 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
@@ -40,7 +41,7 @@ libb10_dhcp___la_SOURCES += option_string.cc option_string.h
 libb10_dhcp___la_SOURCES += protocol_util.cc protocol_util.h
 libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
 libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
-libb10_dhcp___la_SOURCES += pkt_filter.h
+libb10_dhcp___la_SOURCES += pkt_filter.h pkt_filter.cc
 libb10_dhcp___la_SOURCES += pkt_filter_inet.cc pkt_filter_inet.h
 
 if OS_LINUX
@@ -48,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 2bce3a8..392478a 100644
--- a/src/lib/dhcp/dhcp4.h
+++ b/src/lib/dhcp/dhcp4.h
@@ -164,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/duid.cc b/src/lib/dhcp/duid.cc
index 8570d26..80fa724 100644
--- a/src/lib/dhcp/duid.cc
+++ b/src/lib/dhcp/duid.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
@@ -46,7 +46,7 @@ DUID::DUID(const uint8_t* data, size_t len) {
     duid_ = std::vector<uint8_t>(data, data + len);
 }
 
-std::vector<uint8_t> DUID::getDuid() const {
+const std::vector<uint8_t>& DUID::getDuid() const {
     return (duid_);
 }
 
@@ -104,7 +104,7 @@ ClientId::ClientId(const uint8_t *clientid, size_t len)
 }
 
 // Returns a copy of client-id data
-std::vector<uint8_t> ClientId::getClientId() const {
+const std::vector<uint8_t>& ClientId::getClientId() const {
     return (duid_);
 }
 
diff --git a/src/lib/dhcp/duid.h b/src/lib/dhcp/duid.h
index d20a58d..1cd8388 100644
--- a/src/lib/dhcp/duid.h
+++ b/src/lib/dhcp/duid.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
@@ -58,12 +58,13 @@ class DUID {
 
     /// @brief Returns a const reference to the actual DUID value
     ///
-    /// Note: For safety reasons, this method returns a copy of data as
-    /// otherwise the reference would be only valid as long as the object that
-    /// returned it. In any case, this method should be used only sporadically.
-    /// If there are frequent uses, we must implement some other method
-    /// (e.g. storeSelf()) that will avoid data copying.
-    std::vector<uint8_t> getDuid() const;
+    /// @warning Since this function returns a reference to the vector (not a
+    /// copy) the returned object must be used with caution because it remains
+    /// valid only for the time period when the object which returned it is
+    /// valid.
+    ///
+    /// @return A reference to a vector holding a DUID.
+    const std::vector<uint8_t>& getDuid() const;
 
     /// @brief Returns the DUID type
     DUIDType getType() const;
@@ -116,8 +117,15 @@ public:
     /// @brief Constructor based on array and array size
     ClientId(const uint8_t* clientid, size_t len);
 
-    /// @brief Returns reference to the client-id data
-    std::vector<uint8_t> getClientId() const;
+    /// @brief Returns reference to the client-id data.
+    ///
+    /// @warning Since this function returns a reference to the vector (not a
+    /// copy) the returned object must be used with caution because it remains
+    /// valid only for the time period when the object which returned it is
+    /// valid.
+    ///
+    /// @return A reference to a vector holding a client identifier.
+    const std::vector<uint8_t>& getClientId() const;
 
     /// @brief Returns textual representation of a DUID (e.g. 00:01:02:03:ff)
     std::string toText() const;
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index 0ad35a5..66738ce 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -26,7 +26,8 @@
 #include <exceptions/exceptions.h>
 #include <util/io/pktinfo_utilities.h>
 
-
+#include <cstring>
+#include <errno.h>
 #include <fstream>
 #include <sstream>
 
@@ -88,6 +89,10 @@ Iface::closeSockets(const uint16_t family) {
             // Close and delete the socket and move to the
             // next one.
             close(sock->sockfd_);
+            // Close fallback socket if open.
+            if (sock->fallbackfd_ >= 0) {
+                close(sock->fallbackfd_);
+            }
             sockets_.erase(sock++);
 
         } else {
@@ -148,6 +153,10 @@ bool Iface::delSocket(uint16_t sockfd) {
     while (sock!=sockets_.end()) {
         if (sock->sockfd_ == sockfd) {
             close(sockfd);
+            // Close fallback socket if open.
+            if (sock->fallbackfd_ >= 0) {
+                close(sock->fallbackfd_);
+            }
             sockets_.erase(sock);
             return (true); //socket found
         }
@@ -284,8 +293,9 @@ void IfaceMgr::stubDetectIfaces() {
     addInterface(iface);
 }
 
-bool IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast) {
-    int sock;
+bool
+IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast,
+                       IfaceMgrErrorMsgCallback error_handler) {
     int count = 0;
 
 // This option is used to bind sockets to particular interfaces.
@@ -322,6 +332,7 @@ bool IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast) {
                 continue;
             }
 
+            int sock = -1;
             // If selected interface is broadcast capable set appropriate
             // options on the socket so as it can receive and send broadcast
             // messages.
@@ -331,36 +342,53 @@ bool IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast) {
                 // bind to INADDR_ANY address but we can do it only once. Thus,
                 // if one socket has been bound we can't do it any further.
                 if (!bind_to_device && bcast_num > 0) {
-                    isc_throw(SocketConfigError, "SO_BINDTODEVICE socket option is"
-                              << " not supported on this OS; therefore, DHCP"
-                              << " server can only listen broadcast traffic on"
-                              << " a single interface");
+                    handleSocketConfigError("SO_BINDTODEVICE socket option is"
+                                            " not supported on this OS;"
+                                            " therefore, DHCP server can only"
+                                            " listen broadcast traffic on a"
+                                            " single interface",
+                                            error_handler);
+                    continue;
 
                 } else {
-                    // We haven't open any broadcast sockets yet, so we can
-                    // open at least one more.
-                    sock = openSocket(iface->getName(), *addr, port, true, true);
-                    // Binding socket to an interface is not supported so we can't
-                    // open any more broadcast sockets. Increase the number of
-                    // opened broadcast sockets.
+                    try {
+                        // We haven't open any broadcast sockets yet, so we can
+                        // open at least one more.
+                        sock = openSocket(iface->getName(), *addr, port,
+                                          true, true);
+                    } catch (const Exception& ex) {
+                        handleSocketConfigError(ex.what(), error_handler);
+                        continue;
+
+                    }
+                    // Binding socket to an interface is not supported so we
+                    // can't open any more broadcast sockets. Increase the
+                    // number of open broadcast sockets.
                     if (!bind_to_device) {
                         ++bcast_num;
                     }
                 }
 
             } else {
-                // Not broadcast capable, do not set broadcast flags.
-                sock = openSocket(iface->getName(), *addr, port, false, false);
+                try {
+                    // Not broadcast capable, do not set broadcast flags.
+                    sock = openSocket(iface->getName(), *addr, port,
+                                      false, false);
+                } catch (const Exception& ex) {
+                    handleSocketConfigError(ex.what(), error_handler);
+                    continue;
+                }
 
             }
             if (sock < 0) {
                 const char* errstr = strerror(errno);
-                isc_throw(SocketConfigError, "failed to open IPv4 socket"
-                          << " supporting broadcast traffic, reason:"
-                          << errstr);
+                handleSocketConfigError(std::string("failed to open IPv4 socket,"
+                                                    " reason:") + errstr,
+                                        error_handler);
+            } else {
+                ++count;
             }
 
-            count++;
         }
     }
     return (count > 0);
@@ -460,6 +488,21 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
 }
 
 void
+IfaceMgr::handleSocketConfigError(const std::string& errmsg,
+                                  IfaceMgrErrorMsgCallback handler) {
+    // If error handler is installed, we don't want to throw an exception, but
+    // rather call this handler.
+    if (handler != NULL) {
+        handler(errmsg);
+
+    } else {
+        isc_throw(SocketConfigError, errmsg);
+
+    }
+}
+
+
+void
 IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
     for (IfaceCollection::const_iterator iface=ifaces_.begin();
          iface!=ifaces_.end();
@@ -741,7 +784,7 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
         }
     }
 
-    SocketInfo info(sock, addr, port);
+    SocketInfo info(addr, port, sock);
     iface.addSocket(info);
 
     return (sock);
@@ -754,13 +797,11 @@ int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr,
     // Skip checking if the packet_filter_ is non-NULL because this check
     // has been already done when packet filter object was set.
 
-    int sock = packet_filter_->openSocket(iface, addr, port,
-                                          receive_bcast, send_bcast);
-
-    SocketInfo info(sock, addr, port);
+    SocketInfo info = packet_filter_->openSocket(iface, addr, port,
+                                                 receive_bcast, send_bcast);
     iface.addSocket(info);
 
-    return (sock);
+    return (info.sockfd_);
 }
 
 bool
@@ -852,13 +893,22 @@ IfaceMgr::send(const Pkt6Ptr& pkt) {
     struct in6_pktinfo *pktinfo = convertPktInfo6(CMSG_DATA(cmsg));
     memset(pktinfo, 0, sizeof(struct in6_pktinfo));
     pktinfo->ipi6_ifindex = pkt->getIndex();
-    m.msg_controllen = cmsg->cmsg_len;
+    // According to RFC3542, section 20.2, the msg_controllen field
+    // may be set using CMSG_SPACE (which includes padding) or
+    // using CMSG_LEN. Both forms appear to work fine on Linux, FreeBSD,
+    // NetBSD, but OpenBSD appears to have a bug, discussed here:
+    // http://www.archivum.info/mailing.openbsd.bugs/2009-02/00017/
+    // kernel-6080-msg_controllen-of-IPV6_PKTINFO.html
+    // which causes sendmsg to return EINVAL if the CMSG_LEN is
+    // used to set the msg_controllen value.
+    m.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
 
     pkt->updateTimestamp();
 
     result = sendmsg(getSocket(*pkt), &m, 0);
     if (result < 0) {
-        isc_throw(SocketWriteError, "Pkt6 send failed: sendmsg() returned " << result);
+        isc_throw(SocketWriteError, "pkt6 send failed: sendmsg() returned"
+                  " with an error: " << strerror(errno));
     }
 
     return (result);
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
index 2f48813..e02b9b1 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -22,6 +22,7 @@
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt_filter.h>
 
+#include <boost/function.hpp>
 #include <boost/noncopyable.hpp>
 #include <boost/scoped_array.hpp>
 #include <boost/shared_ptr.hpp>
@@ -72,19 +73,49 @@ public:
 
 /// Holds information about socket.
 struct SocketInfo {
-    uint16_t sockfd_; /// socket descriptor
+
     isc::asiolink::IOAddress addr_; /// bound address
     uint16_t port_;   /// socket port
     uint16_t family_; /// IPv4 or IPv6
 
+    /// @brief Socket descriptor (a.k.a. primary socket).
+    int sockfd_;
+
+    /// @brief Fallback socket descriptor.
+    ///
+    /// This socket descriptor holds the handle to the fallback socket.
+    /// The fallback socket is created when there is a need for the regular
+    /// datagram socket to be bound to an IP address and port, besides
+    /// primary socket (sockfd_) which is actually used to receive and process
+    /// the DHCP messages. The fallback socket (if exists) is always associated
+    /// with the primary socket. In particular, the need for the fallback socket
+    /// arises when raw socket is a primary one. When primary socket is open,
+    /// it is bound to an interface not the address and port. The implications
+    /// include the possibility that the other process (e.g. the other instance
+    /// of DHCP server) will bind to the same address and port through which the
+    /// raw socket receives the DHCP messages.Another implication is that the
+    /// kernel, being unaware of the DHCP server operating through the raw
+    /// socket, will respond with the ICMP "Destination port unreachable"
+    /// messages when DHCP messages are only received through the raw socket.
+    /// In order to workaround the issues mentioned here, the fallback socket
+    /// should be opened so as/ the kernel is aware that the certain address
+    /// and port is in use.
+    ///
+    /// The fallback description is supposed to be set to a negative value if
+    /// the fallback socket is closed (not open).
+    int fallbackfd_;
+
     /// @brief SocketInfo constructor.
     ///
-    /// @param sockfd socket descriptor
-    /// @param addr an address the socket is bound to
-    /// @param port a port the socket is bound to
-    SocketInfo(uint16_t sockfd, const isc::asiolink::IOAddress& addr,
-               uint16_t port)
-        :sockfd_(sockfd), addr_(addr), port_(port), family_(addr.getFamily()) { }
+    /// @param addr An address the socket is bound to.
+    /// @param port A port the socket is bound to.
+    /// @param sockfd Socket descriptor.
+    /// @param fallbackfd A descriptor of the fallback socket.
+    SocketInfo(const isc::asiolink::IOAddress& addr, const uint16_t port,
+               const int sockfd, const int fallbackfd = -1)
+        : addr_(addr), port_(port), family_(addr.getFamily()),
+          sockfd_(sockfd), fallbackfd_(fallbackfd) { }
+
 };
 
 
@@ -176,9 +207,10 @@ public:
     ///
     /// @note Implementation of this method is OS-dependent as bits have
     /// different meaning on each OS.
+    /// We need to make it 64 bits, because Solaris uses 64, not 32 bits.
     ///
     /// @param flags bitmask value returned by OS in interface detection
-    void setFlags(uint32_t flags);
+    void setFlags(uint64_t flags);
 
     /// @brief Returns interface index.
     ///
@@ -332,7 +364,9 @@ public:
 
     /// Interface flags (this value is as is returned by OS,
     /// it may mean different things on different OSes).
-    uint32_t flags_;
+    /// Solaris based os have unsigned long flags field (64 bits).
+    /// It is usually 32 bits, though.
+    uint64_t flags_;
 
     /// Indicates that IPv4 sockets should (true) or should not (false)
     /// be opened on this interface.
@@ -343,6 +377,13 @@ public:
     bool inactive6_;
 };
 
+/// @brief This type describes the callback function invoked when error occurs
+/// in the IfaceMgr.
+///
+/// @param errmsg An error message.
+typedef
+boost::function<void(const std::string& errmsg)> IfaceMgrErrorMsgCallback;
+
 /// @brief Handles network interfaces, transmission and reception.
 ///
 /// IfaceMgr is an interface manager class that detects available network
@@ -386,7 +427,7 @@ public:
     /// @return true if direct response is supported.
     bool isDirectResponseSupported() const;
 
-    /// @brief Returns interface with specified interface index
+    /// @brief Returns interfac specified interface index
     ///
     /// @param ifindex index of searched interface
     ///
@@ -579,9 +620,20 @@ public:
                                     const uint16_t port);
 
 
-    /// Opens IPv6 sockets on detected interfaces.
+    /// @brief Opens IPv6 sockets on detected interfaces.
     ///
-    /// Will throw exception if socket creation fails.
+    /// @todo This function will throw an exception immediately when a socket
+    /// fails to open. This is undersired behavior because it will preclude
+    /// other sockets from opening. We should strive to provide similar mechanism
+    /// that has been introduced for V4 sockets. If socket creation fails the
+    /// appropriate error handler is called and once the handler returns the
+    /// function contnues to open other sockets. The change in the IfaceMgr
+    /// is quite straight forward and it is proven to work for V4. However,
+    /// unit testing it is a bit involved, because for unit testing we need
+    /// a replacement of the openSocket6 function which will mimic the
+    /// behavior of the real socket opening. For the V4 we have the means to
+    /// to achieve that with the replaceable PktFilter class. For V6, the
+    /// implementation is hardcoded in the openSocket6.
     ///
     /// @param port specifies port number (usually DHCP6_SERVER_PORT)
     ///
@@ -589,16 +641,71 @@ public:
     /// @return true if any sockets were open
     bool openSockets6(const uint16_t port = DHCP6_SERVER_PORT);
 
-    /// Opens IPv4 sockets on detected interfaces.
-    /// Will throw exception if socket creation fails.
+    /// @brief Opens IPv4 sockets on detected interfaces.
+    ///
+    /// This function attempts to open sockets on all interfaces which have been
+    /// detected by @c IfaceMgr and meet the following conditions:
+    /// - interface is not a local loopback,
+    /// - interface is running (connected),
+    /// - interface is up,
+    /// - interface is active, e.g. selected from the configuration to be used
+    /// to listen DHCPv4 messages,
+    /// - interface has an IPv4 address assigned.
+    ///
+    /// The type of the socket being open depends on the selected Packet Filter
+    /// represented by a class derived from @c isc::dhcp::PktFilter abstract
+    /// class.
+    ///
+    /// It is possible to specify whether sockets should be broadcast capable.
+    /// In most of the cases, the sockets should support broadcast traffic, e.g.
+    /// DHCPv4 server and relay need to listen to broadcast messages sent by
+    /// clients. If the socket has to be open on the particular interface, this
+    /// interface must have broadcast flag set. If this condition is not met,
+    /// the socket will be created in the unicast-only mode. If there are
+    /// multiple broadcast-capable interfaces present, they may be all open
+    /// in a broadcast mode only if the OS supports SO_BINDTODEVICE (bind socket
+    /// to a device) socket option. If this option is not supported, only the
+    /// first broadcast-capable socket will be opened in the broadcast mode.
+    /// The error will be reported for sockets being opened on other interfaces.
+    /// If the socket is bound to a device (interface), the broadcast traffic
+    /// sent to this interface will be received on this interface only.
+    /// This allows the DHCPv4 server or relay to detect the interface on which
+    /// the broadcast message has been received. This interface is later used
+    /// to send a response.
+    ///
+    /// On the systems with multiple interfaces, it is often desired that the
+    /// failure to open a socket on a particular interface doesn't cause a
+    /// fatal error and sockets should be opened on remaining interfaces.
+    /// However, the warning about the failure for the particular socket should
+    /// be communicated to the caller. The libdhcp++ is a common library with
+    /// no logger associated with it. Most of the functions in this library
+    /// communicate errors via exceptions. In case of openSockets4 function
+    /// exception must not be thrown if the function is supposed to continue
+    /// opening sockets, despite an error. Therefore, if such a behavior is
+    /// desired, the error handler function can be passed as a parameter.
+    /// This error handler is called (if present) with an error string.
+    /// Typically, error handler will simply log an error using an application
+    /// logger, but it can do more sophisticated error handling too.
+    ///
+    /// @todo It is possible that additional parameters will have to be added
+    /// to the error handler, e.g. Iface if it was really supposed to do
+    /// some more sophisticated error handling.
+    ///
+    /// If the error handler is not installed (is NULL), the exception is thrown
+    /// for each failure (default behavior).
     ///
     /// @param port specifies port number (usually DHCP4_SERVER_PORT)
     /// @param use_bcast configure sockets to support broadcast messages.
+    /// @param error_handler A pointer to an error handler function which is
+    /// called by the openSockets4 when it fails to open a socket. This
+    /// parameter can be NULL to indicate that the callback should not be used.
     ///
-    /// @throw SocketOpenFailure if tried and failed to open socket.
+    /// @throw SocketOpenFailure if tried and failed to open socket and callback
+    /// function hasn't been specified.
     /// @return true if any sockets were open
     bool openSockets4(const uint16_t port = DHCP4_SERVER_PORT,
-                      const bool use_bcast = true);
+                      const bool use_bcast = true,
+                      IfaceMgrErrorMsgCallback error_handler = NULL);
 
     /// @brief Closes all open sockets.
     /// Is used in destructor, but also from Dhcpv4Srv and Dhcpv6Srv classes.
@@ -676,6 +783,15 @@ public:
     /// not having address assigned.
     void setMatchingPacketFilter(const bool direct_response_desired = false);
 
+    /// @brief Adds an interface to list of known interfaces.
+    ///
+    /// @param iface reference to Iface object.
+    /// @note This function must be public because it has to be callable
+    /// from unit tests.
+    void addInterface(const Iface& iface) {
+        ifaces_.push_back(iface);
+    }
+
     /// A value of socket descriptor representing "not specified" state.
     static const int INVALID_SOCKET = -1;
 
@@ -720,13 +836,6 @@ protected:
     /// @return socket descriptor
     int openSocket6(Iface& iface, const isc::asiolink::IOAddress& addr, uint16_t port);
 
-    /// @brief Adds an interface to list of known interfaces.
-    ///
-    /// @param iface reference to Iface object.
-    void addInterface(const Iface& iface) {
-        ifaces_.push_back(iface);
-    }
-
     /// @brief Detects network interfaces.
     ///
     /// This method will eventually detect available interfaces. For now
@@ -825,6 +934,23 @@ private:
     getLocalAddress(const isc::asiolink::IOAddress& remote_addr,
                     const uint16_t port);
 
+    /// @brief Handles an error which occurs during configuration of a socket.
+    ///
+    /// If the handler callback is specified (non-NULL), this handler is
+    /// called and the specified error message is passed to it. If the
+    /// handler is not specified, the @c isc::dhcpSocketConfigError exception
+    /// is thrown with the specified message.
+    ///
+    /// This function should be called to handle errors which occur during
+    /// socket opening, binding or configuration (e.g. setting socket options
+    /// etc).
+    ///
+    /// @param errmsg An error message to be passed to a handlder function or
+    /// to the @c isc::dhcp::SocketConfigError exception.
+    /// @param handler An error handler function or NULL.
+    void handleSocketConfigError(const std::string& errmsg,
+                                 IfaceMgrErrorMsgCallback handler);
+
     /// Holds instance of a class derived from PktFilter, used by the
     /// IfaceMgr to open sockets and send/receive packets through these
     /// sockets. It is possible to supply custom object using
diff --git a/src/lib/dhcp/iface_mgr_bsd.cc b/src/lib/dhcp/iface_mgr_bsd.cc
index 2293877..be73bcf 100644
--- a/src/lib/dhcp/iface_mgr_bsd.cc
+++ b/src/lib/dhcp/iface_mgr_bsd.cc
@@ -20,6 +20,12 @@
 #include <dhcp/pkt_filter_inet.h>
 #include <exceptions/exceptions.h>
 
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if_dl.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+
 using namespace std;
 using namespace isc;
 using namespace isc::asiolink;
@@ -28,11 +34,97 @@ using namespace isc::dhcp;
 namespace isc {
 namespace dhcp {
 
+/// This is a BSD specific interface detection method.
 void
 IfaceMgr::detectIfaces() {
-    /// @todo do the actual detection on BSDs. Currently just calling
-    /// stub implementation.
-    stubDetectIfaces();
+    struct ifaddrs* iflist = 0;// The whole interface list
+    struct ifaddrs* ifptr = 0; // The interface we're processing now
+
+    // Gets list of ifaddrs struct
+    if(getifaddrs(&iflist) != 0) {
+        isc_throw(Unexpected, "Network interfaces detection failed.");
+    }
+
+    typedef map<string, Iface> ifaceLst;
+    ifaceLst::iterator iface_iter;
+    ifaceLst ifaces;
+
+    // First lookup for getting interfaces ...
+    for (ifptr = iflist; ifptr != 0; ifptr = ifptr->ifa_next) {
+        const char * ifname = ifptr->ifa_name;
+        uint ifindex = 0;
+
+        if (!(ifindex = if_nametoindex(ifname))) {
+            // Interface name does not have corresponding index ...
+            freeifaddrs(iflist);
+            isc_throw(Unexpected, "Interface " << ifname << " has no index");
+        }
+
+        if ((iface_iter = ifaces.find(ifname)) != ifaces.end()) {
+            continue;
+        }
+
+        Iface iface(ifname, ifindex);
+        iface.setFlags(ifptr->ifa_flags);
+        ifaces.insert(pair<string, Iface>(ifname, iface));
+    }
+
+    // Second lookup to get MAC and IP addresses
+    for (ifptr = iflist; ifptr != 0; ifptr = ifptr->ifa_next) {
+        if ((iface_iter = ifaces.find(ifptr->ifa_name)) == ifaces.end()) {
+            continue;
+        }
+        // Common byte pointer for following data
+        const uint8_t * ptr = 0;
+        if(ifptr->ifa_addr->sa_family == AF_LINK) {
+            // HWAddr
+            struct sockaddr_dl * ldata =
+                reinterpret_cast<struct sockaddr_dl *>(ifptr->ifa_addr);
+            ptr = reinterpret_cast<uint8_t *>(LLADDR(ldata));
+
+            iface_iter->second.setHWType(ldata->sdl_type);
+            iface_iter->second.setMac(ptr, ldata->sdl_alen);
+        } else if(ifptr->ifa_addr->sa_family == AF_INET6) {
+            // IPv6 Addr
+            struct sockaddr_in6 * adata =
+                reinterpret_cast<struct sockaddr_in6 *>(ifptr->ifa_addr);
+            ptr = reinterpret_cast<uint8_t *>(&adata->sin6_addr);
+
+            IOAddress a = IOAddress::fromBytes(AF_INET6, ptr);
+            iface_iter->second.addAddress(a);
+        } else {
+            // IPv4 Addr
+            struct sockaddr_in * adata =
+                reinterpret_cast<struct sockaddr_in *>(ifptr->ifa_addr);
+            ptr = reinterpret_cast<uint8_t *>(&adata->sin_addr);
+
+            IOAddress a = IOAddress::fromBytes(AF_INET, ptr);
+            iface_iter->second.addAddress(a);
+        }
+    }
+
+    freeifaddrs(iflist);
+
+    // Interfaces registering
+    for(ifaceLst::const_iterator iface_iter = ifaces.begin();
+        iface_iter != ifaces.end(); ++iface_iter) {
+        ifaces_.push_back(iface_iter->second);
+    }
+}
+
+/// @brief sets flag_*_ fields
+///
+/// Like Linux version, os specific flags
+///
+/// @params flags
+void Iface::setFlags(uint64_t flags) {
+    flags_ = flags;
+
+    flag_loopback_ = flags & IFF_LOOPBACK;
+    flag_up_ = flags & IFF_UP;
+    flag_running_ = flags & IFF_RUNNING;
+    flag_multicast_ = flags & IFF_MULTICAST;
+    flag_broadcast_ = flags & IFF_BROADCAST;
 }
 
 void IfaceMgr::os_send4(struct msghdr& /*m*/,
diff --git a/src/lib/dhcp/iface_mgr_linux.cc b/src/lib/dhcp/iface_mgr_linux.cc
index f31c353..dddeb52 100644
--- a/src/lib/dhcp/iface_mgr_linux.cc
+++ b/src/lib/dhcp/iface_mgr_linux.cc
@@ -502,7 +502,7 @@ void IfaceMgr::detectIfaces() {
 /// on different OSes.
 ///
 /// @param flags flags bitfield read from OS
-void Iface::setFlags(uint32_t flags) {
+void Iface::setFlags(uint64_t flags) {
     flags_ = flags;
 
     flag_loopback_ = flags & IFF_LOOPBACK;
diff --git a/src/lib/dhcp/iface_mgr_sun.cc b/src/lib/dhcp/iface_mgr_sun.cc
index 46c4a97..fe2b0b3 100644
--- a/src/lib/dhcp/iface_mgr_sun.cc
+++ b/src/lib/dhcp/iface_mgr_sun.cc
@@ -20,6 +20,12 @@
 #include <dhcp/pkt_filter_inet.h>
 #include <exceptions/exceptions.h>
 
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if_dl.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+
 using namespace std;
 using namespace isc;
 using namespace isc::asiolink;
@@ -28,11 +34,100 @@ using namespace isc::dhcp;
 namespace isc {
 namespace dhcp {
 
+/// This is a Solaris specific interface detection code. It works on Solaris 11
+/// only, as earlier versions did not support getifaddrs() API.
 void
 IfaceMgr::detectIfaces() {
-    /// @todo do the actual detection on Solaris. Currently just calling
-    /// stub implementation.
-    stubDetectIfaces();
+    struct ifaddrs * iflist = 0, * ifptr = 0;
+
+    // Gets list of ifaddrs struct
+    if(getifaddrs(& iflist) != 0) {
+        isc_throw(Unexpected, "Network interfaces detection failed.");
+    }
+
+    typedef std::map<string, Iface> ifaceLst;
+    ifaceLst::iterator iface_iter;
+    ifaceLst ifaces;
+
+    // First lookup for getting interfaces ...
+    for(ifptr = iflist; ifptr != 0; ifptr = ifptr->ifa_next) {
+        const char * ifname = ifptr->ifa_name;
+        uint ifindex = 0;
+
+        if (!(ifindex = if_nametoindex(ifname))) {
+            // Interface name does not have corresponding index ...
+            freeifaddrs(iflist);
+            isc_throw(Unexpected, "Interface " << ifname << " has no index");
+        }
+
+        iface_iter = ifaces.find(ifname);
+        if (iface_iter != ifaces.end()) {
+            continue;
+        }
+
+        Iface iface(ifname, ifindex);
+        iface.setFlags(ifptr->ifa_flags);
+        ifaces.insert(pair<string, Iface>(ifname, iface));
+    }
+
+    // Second lookup to get MAC and IP addresses
+    for (ifptr = iflist; ifptr != 0; ifptr = ifptr->ifa_next) {
+
+        iface_iter = ifaces.find(ifptr->ifa_name);
+        if (iface_iter == ifaces.end()) {
+            continue;
+        }
+        // Common byte pointer for following data
+        const uint8_t * ptr = 0;
+        if (ifptr->ifa_addr->sa_family == AF_LINK) {
+            // HWAddr
+            struct sockaddr_dl * ldata =
+                reinterpret_cast<struct sockaddr_dl *>(ifptr->ifa_addr);
+            ptr = reinterpret_cast<uint8_t *>(LLADDR(ldata));
+
+            iface_iter->second.setHWType(ldata->sdl_type);
+            iface_iter->second.setMac(ptr, ldata->sdl_alen);
+        } else if (ifptr->ifa_addr->sa_family == AF_INET6) {
+            // IPv6 Addr
+            struct sockaddr_in6 * adata =
+                reinterpret_cast<struct sockaddr_in6 *>(ifptr->ifa_addr);
+            ptr = reinterpret_cast<uint8_t *>(& adata->sin6_addr);
+
+            IOAddress a = IOAddress::fromBytes(AF_INET6, ptr);
+            iface_iter->second.addAddress(a);
+        } else {
+            // IPv4 Addr
+            struct sockaddr_in * adata =
+                reinterpret_cast<struct sockaddr_in *>(ifptr->ifa_addr);
+            ptr = reinterpret_cast<uint8_t *>(& adata->sin_addr);
+
+            IOAddress a = IOAddress::fromBytes(AF_INET, ptr);
+            iface_iter->second.addAddress(a);
+        }
+    }
+
+    freeifaddrs(iflist);
+
+    // Interfaces registering
+    for (ifaceLst::const_iterator iface_iter = ifaces.begin();
+        iface_iter != ifaces.end(); ++iface_iter) {
+        ifaces_.push_back(iface_iter->second);
+    }
+}
+
+/// @brief sets flag_*_ fields
+///
+/// Like Linux version, os specific flags
+///
+/// @params flags
+void Iface::setFlags(uint64_t flags) {
+    flags_ = flags;
+
+    flag_loopback_ = flags & IFF_LOOPBACK;
+    flag_up_ = flags & IFF_UP;
+    flag_running_ = flags & IFF_RUNNING;
+    flag_multicast_ = flags & IFF_MULTICAST;
+    flag_broadcast_ = flags & IFF_BROADCAST;
 }
 
 void IfaceMgr::os_send4(struct msghdr& /*m*/,
@@ -40,7 +135,8 @@ void IfaceMgr::os_send4(struct msghdr& /*m*/,
                         size_t /*control_buf_len*/,
                         const Pkt4Ptr& /*pkt*/) {
   // @todo: Are there any specific actions required before sending IPv4 packet
-  // on BSDs? See iface_mgr_linux.cc for working Linux implementation.
+  // on Solaris based systems? See iface_mgr_linux.cc
+  // for working Linux implementation.
 }
 
 bool IfaceMgr::os_receive4(struct msghdr& /*m*/, Pkt4Ptr& /*pkt*/) {
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index 351fe8c..4577c64 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) {
@@ -91,9 +163,9 @@ LibDHCP::isStandardOption(const Option::Universe u, const uint16_t code) {
               code == 126 ||
               code == 127 ||
               (code > 146 && code < 150) ||
-              (code > 177  && code < 208) ||
+              (code > 177 && code < 208) ||
               (code > 213 && code <  220) ||
-              (code > 221 && code < 224))) {
+              (code > 221 && code < 255))) {
                 return (true);
             }
 
@@ -128,14 +200,22 @@ LibDHCP::optionFactory(Option::Universe u,
 
 
 size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
+                               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,
+                               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,6 +385,191 @@ 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 OptionCollection& options) {
@@ -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++.dox b/src/lib/dhcp/libdhcp++.dox
index eabc392..ab4d7eb 100644
--- a/src/lib/dhcp/libdhcp++.dox
+++ b/src/lib/dhcp/libdhcp++.dox
@@ -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
@@ -19,8 +19,9 @@
 
 libdhcp++ is an all-purpose DHCP-manipulation library, written in
 C++. It offers packet parsing and assembly, DHCPv4 and DHCPv6
-options parsing and ssembly, interface detection (currently on
-Linux systems only) and socket operations. It is a generic purpose library that
+options parsing and assembly, interface detection (currently on
+Linux, FreeBSD, NetBSD, OpenBSD, Max OS X, and Solaris 11) and socket operations.
+It is a generic purpose library that
 can be used by server, client, relay, performance tools and other DHCP-related
 tools. For server specific library, see \ref libdhcpsrv. Please do not
 add any server-specific code to libdhcp++ and use \ref libdhcpsrv instead.
@@ -137,4 +138,52 @@ Another useful methods are dedicated to transmission
 Note that receive4() and receive6() methods may return NULL, e.g.
 when timeout is reached or if dhcp daemon receives a signal.
 
+ at section libdhcpPktFilter Switchable Packet Filter objects used by Interface Manager
+
+The well known problem of DHCPv4 implementation is that it must be able to
+provision devices which don't have an IPv4 address yet (the IPv4 address is
+one of the configuration parameters provided by DHCP server to a client).
+One way to communicate with such a device is to send server's response to
+a broadcast address. An obvious drawback of this approach is that the server's
+response will be received and processed by all clients in the particular
+network. Therefore, the preferred approach is that the server unicasts its
+response to a new address being assigned for the client. This client will
+identify itself as a target of this message by checking chaddr and/or
+Client Identifier value. At the same time, the other clients in the network
+will not receive the unicast message. The major problem that arises with this
+approach is that the client without an IP address doesn't respond to ARP
+messages. As a result, server's response will not be sent over IP/UDP
+socket because the system kernel will fail to resolve client's link-layer
+address.
+
+Kea supports the use of raw sockets to create a complete Data-link/IP/UDP/DHCPv4
+stack. By creating each layer of the outgoing packet, the Kea logic has full
+control over the frame contents and it may bypass the use of ARP to inject the
+link layer address into the frame. The raw socket is bound to a specific interface,
+not to the IP address/UDP port. Therefore, the system kernel doesn't have
+means to verify that Kea is listening to the DHCP traffic on the specific address
+and port. This has two major implications:
+- It is possible to run another DHCPv4 sever instance which will bind socket to the
+same address and port.
+- An attempt to send a unicast message to the DHCPv4 server will result in ICMP
+"Port Unreachable" message being sent by the kernel (which is unaware that the
+DHCPv4 service is actually running).
+In order to overcome these issues, the isc::dhcp::PktFilterLPF opens a
+regular IP/UDP socket which coexists with the raw socket. The socket is referred
+to as "fallback socket" in the Kea code. All packets received through this socket
+are discarded.
+
+In general, the use of datagram sockets is preferred over raw sockets.
+For convenience, the switchable Packet Filter objects are used to manage
+sockets for different purposes. These objects implement the socket opening
+operation and sending/receiving messages over this socket. For example:
+the isc::dhcp::PktFilterLPF object opens a raw socket.
+The isc::dhcp::PktFilterLPF::send and isc::dhcp::PktFilterLPF::receive
+methods encode/decode full data-link/IP/UDP/DHCPv4 stack. The
+isc::dhcp::PktFilterInet supports sending and receiving messages over
+the regular IP/UDP socket. The isc::dhcp::PktFilterInet should be used in all
+cases when an application using the libdhcp++ doesn't require sending
+DHCP messages to a device which doesn't have an address yet.
+
+
 */
diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h
index 71cf3f4..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)
@@ -108,9 +119,12 @@ 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,
+                                 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,6 +149,7 @@ 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,
+                                 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 46889e0..f5ab75e 100644
--- a/src/lib/dhcp/option.cc
+++ b/src/lib/dhcp/option.cc
@@ -135,10 +135,10 @@ Option::unpackOptions(const OptionBuffer& buf) {
 
     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_);
diff --git a/src/lib/dhcp/option_custom.cc b/src/lib/dhcp/option_custom.cc
index 0cca9b3..278c0c9 100644
--- a/src/lib/dhcp/option_custom.cc
+++ b/src/lib/dhcp/option_custom.cc
@@ -249,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
@@ -316,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.
diff --git a/src/lib/dhcp/option_custom.h b/src/lib/dhcp/option_custom.h
index a7d2b95..6ae0b18 100644
--- a/src/lib/dhcp/option_custom.h
+++ b/src/lib/dhcp/option_custom.h
@@ -105,7 +105,6 @@ public:
     template<typename T>
     void addArrayDataField(const T value) {
         checkArrayType();
-
         OptionDataType data_type = definition_.getType();
         if (OptionDataTypeTraits<T>::type != data_type) {
             isc_throw(isc::dhcp::InvalidDataType,
diff --git a/src/lib/dhcp/option_data_types.cc b/src/lib/dhcp/option_data_types.cc
index 3c55ada..4c29596 100644
--- a/src/lib/dhcp/option_data_types.cc
+++ b/src/lib/dhcp/option_data_types.cc
@@ -227,6 +227,24 @@ OptionDataTypeUtil::writeFqdn(const std::string& fqdn,
     }
 }
 
+unsigned int
+OptionDataTypeUtil::getLabelCount(const std::string& text_name) {
+    // The isc::dns::Name class doesn't accept empty names. However, in some
+    // cases we may be dealing with empty names (e.g. sent by the DHCP clients).
+    // Empty names should not be sent as hostnames but if they are, for some
+    // reason, we don't want to throw an exception from this function. We
+    // rather want to signal empty name by returning 0 number of labels.
+    if (text_name.empty()) {
+        return (0);
+    }
+    try {
+        isc::dns::Name name(text_name);
+        return (name.getLabelCount());
+    } catch (const isc::Exception& ex) {
+        isc_throw(BadDataTypeCast, ex.what());
+    }
+}
+
 std::string
 OptionDataTypeUtil::readString(const std::vector<uint8_t>& buf) {
     std::string value;
diff --git a/src/lib/dhcp/option_data_types.h b/src/lib/dhcp/option_data_types.h
index 35d6a1f..d9d8a52 100644
--- a/src/lib/dhcp/option_data_types.h
+++ b/src/lib/dhcp/option_data_types.h
@@ -375,6 +375,17 @@ public:
                           std::vector<uint8_t>& buf,
                           const bool downcase = false);
 
+    /// @brief Return the number of labels in the Name.
+    ///
+    /// If the specified name is empty the 0 is returned.
+    ///
+    /// @param text_name A text representation of the name.
+    ///
+    /// @return A number of labels in the provided name or 0 if the
+    /// name string is empty.
+    /// @throw isc::dhcp::BadDataTypeCast if provided name is malformed.
+    static unsigned int getLabelCount(const std::string& text_name);
+
     /// @brief Read string value from a buffer.
     ///
     /// @param buf input buffer.
diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc
index 8c1cad0..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>
@@ -115,10 +117,26 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
                                 OptionBufferConstIter begin,
                                 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));
@@ -182,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());
     }
@@ -371,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 &&
@@ -388,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)
@@ -570,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 878abeb..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
@@ -425,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);
@@ -438,11 +465,22 @@ 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).
@@ -492,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.
     ///
@@ -601,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_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 468037a..8a5b8ab 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -219,7 +219,7 @@ Pkt4::unpack() {
     // a vector as an input.
     buffer_in.readVector(opts_buffer, opts_len);
     if (callback_.empty()) {
-        LibDHCP::unpackOptions4(opts_buffer, options_);
+        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
diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc
index 57daee0..308880e 100644
--- a/src/lib/dhcp/pkt6.cc
+++ b/src/lib/dhcp/pkt6.cc
@@ -330,7 +330,7 @@ Pkt6::unpackMsg(OptionBuffer::const_iterator begin,
         // 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, options_);
+            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
@@ -377,7 +377,7 @@ Pkt6::unpackRelayMsg() {
             // 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, relay.options_,
+                LibDHCP::unpackOptions6(opt_buffer, "dhcp6", relay.options_,
                                         &relay_msg_offset, &relay_msg_len);
             } else {
                 callback_(opt_buffer, "dhcp6", relay.options_,
diff --git a/src/lib/dhcp/pkt_filter.cc b/src/lib/dhcp/pkt_filter.cc
new file mode 100644
index 0000000..9c1995d
--- /dev/null
+++ b/src/lib/dhcp/pkt_filter.cc
@@ -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.
+
+#include <config.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/pkt_filter.h>
+
+#include <sys/fcntl.h>
+#include <sys/socket.h>
+
+namespace isc {
+namespace dhcp {
+
+int
+PktFilter::openFallbackSocket(const isc::asiolink::IOAddress& addr,
+                              const uint16_t port) {
+    // Create socket.
+    int sock = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sock < 0) {
+        isc_throw(SocketConfigError, "failed to create fallback socket for"
+                  " address " << addr.toText() << ", port " << port
+                  << ", reason: " << strerror(errno));
+    }
+    // Bind the socket to a specified address and port.
+    struct sockaddr_in addr4;
+    memset(&addr4, 0, sizeof(addr4));
+    addr4.sin_family = AF_INET;
+    addr4.sin_addr.s_addr = htonl(addr);
+    addr4.sin_port = htons(port);
+
+    if (bind(sock, reinterpret_cast<struct sockaddr*>(&addr4),
+             sizeof(addr4)) < 0) {
+        // Remember to close the socket if we failed to bind it.
+        close(sock);
+        isc_throw(SocketConfigError, "failed to bind fallback socket to"
+                  " address " << addr.toText() << ", port " << port
+                  << ", reason: " << strerror(errno)
+                  << " - is another DHCP server running?");
+    }
+
+    // Set socket to non-blocking mode. This is to prevent the read from the
+    // fallback socket to block message processing on the primary socket.
+    if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) {
+        close(sock);
+        isc_throw(SocketConfigError, "failed to set SO_NONBLOCK option on the"
+                  " fallback socket, bound to " << addr.toText() << ", port "
+                  << port << ", reason: " << strerror(errno));
+    }
+    // Successfully created and bound a fallback socket. Return a descriptor.
+    return (sock);
+}
+
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
diff --git a/src/lib/dhcp/pkt_filter.h b/src/lib/dhcp/pkt_filter.h
index 204b25e..e35b75d 100644
--- a/src/lib/dhcp/pkt_filter.h
+++ b/src/lib/dhcp/pkt_filter.h
@@ -67,20 +67,30 @@ public:
     /// @return true of the direct response is supported.
     virtual bool isDirectResponseSupported() const = 0;
 
-    /// @brief Open socket.
+    /// @brief Open primary and fallback socket.
     ///
-    /// @param iface interface descriptor
-    /// @param addr address on the interface to be used to send packets.
-    /// @param port port number.
-    /// @param receive_bcast configure socket to receive broadcast messages
+    /// A method implementation in the derived class may open one or two
+    /// sockets:
+    /// - a primary socket - used for communication with clients. DHCP messages
+    /// received using this socket are processed and the same socket is used
+    /// to send a response to the client.
+    /// - a fallback socket which is optionally opened if there is a need for
+    /// the presence of the socket which can be bound to a specific IP address
+    /// and UDP port (e.g. raw primary socket can't be). For the details, see
+    /// the documentation of @c isc::dhcp::SocketInfo.
+    ///
+    /// @param iface Interface descriptor.
+    /// @param addr Address on the interface to be used to send packets.
+    /// @param port Port number.
+    /// @param receive_bcast Configure socket to receive broadcast messages
     /// @param send_bcast configure socket to send broadcast messages.
     ///
-    /// @return created socket's descriptor
-    virtual int openSocket(const Iface& iface,
-                           const isc::asiolink::IOAddress& addr,
-                           const uint16_t port,
-                           const bool receive_bcast,
-                           const bool send_bcast) = 0;
+    /// @return A structure describing a primary and fallback socket.
+    virtual SocketInfo openSocket(const Iface& iface,
+                                  const isc::asiolink::IOAddress& addr,
+                                  const uint16_t port,
+                                  const bool receive_bcast,
+                                  const bool send_bcast) = 0;
 
     /// @brief Receive packet over specified socket.
     ///
@@ -100,6 +110,32 @@ public:
     /// @return result of sending the packet. It is 0 if successful.
     virtual int send(const Iface& iface, uint16_t sockfd,
                      const Pkt4Ptr& pkt) = 0;
+
+protected:
+
+    /// @brief Default implementation to open a fallback socket.
+    ///
+    /// This method provides a means to open a fallback socket and bind it
+    /// to a given IPv4 address and UDP port. This function may be used by the
+    /// derived classes to create a fallback socket. It can be overriden
+    /// in the derived classes if it happens to be insufficient on some
+    /// environments.
+    ///
+    /// The fallback socket is meant to be opened together with the other socket
+    /// (a.k.a. primary socket) used to receive and handle DHCPv4 traffic. The
+    /// traffic received through the fallback should be dropped. The reasoning
+    /// behind opening the fallback socket is explained in the documentation of
+    /// @c isc::dhcp::SocketInfo structure.
+    ///
+    /// @param addr An IPv4 address to bind the socket to.
+    /// @param port A port number to bind socket to.
+    ///
+    /// @return A fallback socket descriptor. This descriptor should be assigned
+    /// to the @c fallbackfd_ field of the @c isc::dhcp::SocketInfo structure.
+    /// @throw isc::dhcp::SocketConfigError if socket opening, binding or
+    /// configuration fails.
+    virtual int openFallbackSocket(const isc::asiolink::IOAddress& addr,
+                                   const uint16_t port);
 };
 
 /// Pointer to a PktFilter object.
diff --git a/src/lib/dhcp/pkt_filter_inet.cc b/src/lib/dhcp/pkt_filter_inet.cc
index 62695e5..1694798 100644
--- a/src/lib/dhcp/pkt_filter_inet.cc
+++ b/src/lib/dhcp/pkt_filter_inet.cc
@@ -16,6 +16,8 @@
 #include <dhcp/iface_mgr.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt_filter_inet.h>
+#include <errno.h>
+#include <cstring>
 
 using namespace isc::asiolink;
 
@@ -28,11 +30,12 @@ PktFilterInet::PktFilterInet()
 {
 }
 
-int PktFilterInet::openSocket(const Iface& iface,
-                              const isc::asiolink::IOAddress& addr,
-                              const uint16_t port,
-                              const bool receive_bcast,
-                              const bool send_bcast) {
+SocketInfo
+PktFilterInet::openSocket(const Iface& iface,
+                          const isc::asiolink::IOAddress& addr,
+                          const uint16_t port,
+                          const bool receive_bcast,
+                          const bool send_bcast) {
 
     struct sockaddr_in addr4;
     memset(&addr4, 0, sizeof(sockaddr));
@@ -90,7 +93,8 @@ int PktFilterInet::openSocket(const Iface& iface,
     }
 #endif
 
-    return (sock);
+    SocketInfo sock_desc(addr, port, sock);
+    return (sock_desc);
 
 }
 
@@ -233,14 +237,15 @@ PktFilterInet::send(const Iface&, uint16_t sockfd,
     struct in_pktinfo* pktinfo =(struct in_pktinfo *)CMSG_DATA(cmsg);
     memset(pktinfo, 0, sizeof(struct in_pktinfo));
     pktinfo->ipi_ifindex = pkt->getIndex();
-    m.msg_controllen = cmsg->cmsg_len;
+    m.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
 #endif
 
     pkt->updateTimestamp();
 
     int result = sendmsg(sockfd, &m, 0);
     if (result < 0) {
-        isc_throw(SocketWriteError, "pkt4 send failed");
+        isc_throw(SocketWriteError, "pkt4 send failed: sendmsg() returned "
+                  " with an error: " << strerror(errno));
     }
 
     return (result);
diff --git a/src/lib/dhcp/pkt_filter_inet.h b/src/lib/dhcp/pkt_filter_inet.h
index 95c9224..690622c 100644
--- a/src/lib/dhcp/pkt_filter_inet.h
+++ b/src/lib/dhcp/pkt_filter_inet.h
@@ -44,20 +44,22 @@ public:
         return (false);
     }
 
-    /// @brief Open socket.
+    /// @brief Open primary and fallback socket.
     ///
-    /// @param iface interface descriptor
-    /// @param addr address on the interface to be used to send packets.
-    /// @param port port number.
-    /// @param receive_bcast configure socket to receive broadcast messages
-    /// @param send_bcast configure socket to send broadcast messages.
+    /// @param iface Interface descriptor.
+    /// @param addr Address on the interface to be used to send packets.
+    /// @param port Port number.
+    /// @param receive_bcast Configure socket to receive broadcast messages
+    /// @param send_bcast Configure socket to send broadcast messages.
     ///
-    /// @return created socket's descriptor
-    virtual int openSocket(const Iface& iface,
-                           const isc::asiolink::IOAddress& addr,
-                           const uint16_t port,
-                           const bool receive_bcast,
-                           const bool send_bcast);
+    /// @return A structure describing a primary and fallback socket.
+    /// @throw isc::dhcp::SocketConfigError if error occurs when opening,
+    /// binding or configuring the socket.
+    virtual SocketInfo openSocket(const Iface& iface,
+                                  const isc::asiolink::IOAddress& addr,
+                                  const uint16_t port,
+                                  const bool receive_bcast,
+                                  const bool send_bcast);
 
     /// @brief Receive packet over specified socket.
     ///
@@ -65,6 +67,10 @@ public:
     /// @param socket_info structure holding socket information
     ///
     /// @return Received packet
+    /// @throw isc::dhcp::SocketReadError if an error occurs during reception
+    /// of the packet.
+    /// @throw An execption thrown by the isc::dhcp::Pkt4 object if DHCPv4
+    /// message parsing fails.
     virtual Pkt4Ptr receive(const Iface& iface, const SocketInfo& socket_info);
 
     /// @brief Send packet over specified socket.
@@ -74,6 +80,8 @@ public:
     /// @param pkt packet to be sent
     ///
     /// @return result of sending a packet. It is 0 if successful.
+    /// @throw isc::dhcp::SocketWriteError if an error occures during sending
+    /// a DHCP message through the socket.
     virtual int send(const Iface& iface, uint16_t sockfd,
                      const Pkt4Ptr& pkt);
 
diff --git a/src/lib/dhcp/pkt_filter_lpf.cc b/src/lib/dhcp/pkt_filter_lpf.cc
index e0964d5..426d58c 100644
--- a/src/lib/dhcp/pkt_filter_lpf.cc
+++ b/src/lib/dhcp/pkt_filter_lpf.cc
@@ -102,13 +102,22 @@ using namespace isc::util;
 namespace isc {
 namespace dhcp {
 
-int
-PktFilterLPF::openSocket(const Iface& iface, const isc::asiolink::IOAddress&,
+SocketInfo
+PktFilterLPF::openSocket(const Iface& iface,
+                         const isc::asiolink::IOAddress& addr,
                          const uint16_t port, const bool,
                          const bool) {
 
+    // Open fallback socket first. If it fails, it will give us an indication
+    // that there is another service (perhaps DHCP server) running.
+    // The function will throw an exception and effectivelly cease opening
+    // raw socket below.
+    int fallback = openFallbackSocket(addr, port);
+
+    // The fallback is open, so we are good to open primary socket.
     int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
     if (sock < 0) {
+        close(fallback);
         isc_throw(SocketConfigError, "Failed to create raw LPF socket");
     }
 
@@ -126,6 +135,7 @@ PktFilterLPF::openSocket(const Iface& iface, const isc::asiolink::IOAddress&,
     if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter_program,
                    sizeof(filter_program)) < 0) {
         close(sock);
+        close(fallback);
         isc_throw(SocketConfigError, "Failed to install packet filtering program"
                   << " on the socket " << sock);
     }
@@ -142,17 +152,39 @@ PktFilterLPF::openSocket(const Iface& iface, const isc::asiolink::IOAddress&,
     if (bind(sock, reinterpret_cast<const struct sockaddr*>(&sa),
              sizeof(sa)) < 0) {
         close(sock);
+        close(fallback);
         isc_throw(SocketConfigError, "Failed to bind LPF socket '" << sock
                   << "' to interface '" << iface.getName() << "'");
     }
 
-    return (sock);
+    return (SocketInfo(addr, port, sock, fallback));
 
 }
 
 Pkt4Ptr
 PktFilterLPF::receive(const Iface& iface, const SocketInfo& socket_info) {
     uint8_t raw_buf[IfaceMgr::RCVBUFSIZE];
+    // First let's get some data from the fallback socket. The data will be
+    // discarded but we don't want the socket buffer to bloat. We get the
+    // packets from the socket in loop but most of the time the loop will
+    // end after receiving one packet. The call to recv returns immediately
+    // when there is no data left on the socket because the socket is
+    // non-blocking.
+    // @todo In the normal conditions, both the primary socket and the fallback
+    // socket are in sync as they are set to receive packets on the same
+    // address and port. The reception of packets on the fallback socket
+    // shouldn't cause significant lags in packet reception. If we find in the
+    // future that it does, the sort of threshold could be set for the maximum
+    // bytes received on the fallback socket in a single round. Further
+    // optimizations would include an asynchronous read from the fallback socket
+    // when the DHCP server is idle.
+    int datalen;
+    do {
+        datalen = recv(socket_info.fallbackfd_, raw_buf, sizeof(raw_buf), 0);
+    } while (datalen > 0);
+
+    // Now that we finished getting data from the fallback socket, we
+    // have to get the data from the raw socket too.
     int data_len = read(socket_info.sockfd_, raw_buf, sizeof(raw_buf));
     // If negative value is returned by read(), it indicates that an
     // error occured. If returned value is 0, no data was read from the
@@ -209,9 +241,16 @@ PktFilterLPF::send(const Iface& iface, uint16_t sockfd, const Pkt4Ptr& pkt) {
 
     OutputBuffer buf(14);
 
-    HWAddrPtr hwaddr(new HWAddr(iface.getMac(), iface.getMacLen(),
-                                iface.getHWType()));
-    pkt->setLocalHWAddr(hwaddr);
+    // Some interfaces may have no HW address - e.g. loopback interface.
+    // For these interfaces the HW address length is 0. If this is the case,
+    // then we will rely on the functions which construct the IP/UDP headers
+    // to provide a default HW addres. Otherwise, create the HW address
+    // object using the HW address of the interface.
+    if (iface.getMacLen() > 0) {
+        HWAddrPtr hwaddr(new HWAddr(iface.getMac(), iface.getMacLen(),
+                                    iface.getHWType()));
+        pkt->setLocalHWAddr(hwaddr);
+    }
 
 
     // Ethernet frame header.
diff --git a/src/lib/dhcp/pkt_filter_lpf.h b/src/lib/dhcp/pkt_filter_lpf.h
index d36719f..279c864 100644
--- a/src/lib/dhcp/pkt_filter_lpf.h
+++ b/src/lib/dhcp/pkt_filter_lpf.h
@@ -41,21 +41,20 @@ public:
         return (true);
     }
 
-    /// @brief Open socket.
+    /// @brief Open primary and fallback socket.
     ///
-    /// @param iface interface descriptor
-    /// @param addr address on the interface to be used to send packets.
-    /// @param port port number.
-    /// @param receive_bcast configure socket to receive broadcast messages
-    /// @param send_bcast configure socket to send broadcast messages.
+    /// @param iface Interface descriptor.
+    /// @param addr Address on the interface to be used to send packets.
+    /// @param port Port number.
+    /// @param receive_bcast Configure socket to receive broadcast messages
+    /// @param send_bcast Configure socket to send broadcast messages.
     ///
-    /// @throw isc::NotImplemented always
-    /// @return created socket's descriptor
-    virtual int openSocket(const Iface& iface,
-                           const isc::asiolink::IOAddress& addr,
-                           const uint16_t port,
-                           const bool receive_bcast,
-                           const bool send_bcast);
+    /// @return A structure describing a primary and fallback socket.
+    virtual SocketInfo openSocket(const Iface& iface,
+                                  const isc::asiolink::IOAddress& addr,
+                                  const uint16_t port,
+                                  const bool receive_bcast,
+                                  const bool send_bcast);
 
     /// @brief Receive packet over specified socket.
     ///
diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h
index 526c80a..9f64553 100644
--- a/src/lib/dhcp/std_option_defs.h
+++ b/src/lib/dhcp/std_option_defs.h
@@ -16,6 +16,8 @@
 #define STD_OPTION_DEFS_H
 
 #include <dhcp/option_data_types.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
 
 namespace {
 
@@ -218,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_IPV6_ADDRESS_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 3f40ddc..19678dd 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -45,9 +45,12 @@ 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_unittest.cc
 libdhcp___unittests_SOURCES += pkt_filter_inet_unittest.cc
+libdhcp___unittests_SOURCES += pkt_filter_test_utils.h pkt_filter_test_utils.cc
 
 if OS_LINUX
 libdhcp___unittests_SOURCES += pkt_filter_lpf_unittest.cc
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index b936b5c..129b9ca 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -20,6 +20,7 @@
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt_filter.h>
 
+#include <boost/bind.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
 
@@ -78,19 +79,36 @@ public:
         return (false);
     }
 
-    /// Pretends to open socket. Only records a call to this function.
-    /// This function returns fake socket descriptor (always the same).
-    /// Note that the returned value has been selected to be unique
-    /// (because real values are rather less than 255). Values greater
-    /// than 255 are not recommended because they cause warnings to be
-    /// reported by Valgrind when invoking close() on them.
-    virtual int openSocket(const Iface&,
-                           const isc::asiolink::IOAddress&,
-                           const uint16_t,
-                           const bool,
-                           const bool) {
+    /// @brief Pretend to open a socket.
+    ///
+    /// This function doesn't open a real socket. It always returns the
+    /// same fake socket descriptor. It also records the fact that it has
+    /// been called in the public open_socket_called_ member.
+    /// As in the case of opening a real socket, this function will check
+    /// if there is another fake socket "bound" to the same address and port.
+    /// If there is, it will throw an exception. This allows to simulate the
+    /// conditions when one of the sockets can't be open because there is
+    /// a socket already open and test how IfaceMgr will handle it.
+    ///
+    /// @param iface An interface on which the socket is to be opened.
+    /// @param addr An address to which the socket is to be bound.
+    /// @param port A port to which the socket is to be bound.
+    virtual SocketInfo openSocket(const Iface& iface,
+                                  const isc::asiolink::IOAddress& addr,
+                                  const uint16_t port,
+                                  const bool,
+                                  const bool) {
+        // Check if there is any other socket bound to the specified address
+        // and port on this interface.
+        const Iface::SocketCollection& sockets = iface.getSockets();
+        for (Iface::SocketCollection::const_iterator socket = sockets.begin();
+             socket != sockets.end(); ++socket) {
+            if ((socket->addr_ == addr) && (socket->port_ == port)) {
+                isc_throw(SocketConfigError, "test socket bind error");
+            }
+        }
         open_socket_called_ = true;
-        return (255);
+        return (SocketInfo(addr, port, 255));
     }
 
     /// Does nothing
@@ -112,16 +130,115 @@ public:
 class NakedIfaceMgr: public IfaceMgr {
     // "Naked" Interface Manager, exposes internal fields
 public:
+
+    /// @brief Constructor.
     NakedIfaceMgr() {
+        loDetect();
+    }
+
+    /// @brief detects name of the loopback interface
+    ///
+    /// This method detects name of the loopback interface.
+    static void loDetect() {
+        // Poor man's interface detection.  It will go away as soon as proper
+        // interface detection is implemented
+        if (if_nametoindex("lo") > 0) {
+            snprintf(LOOPBACK, BUF_SIZE - 1, "lo");
+        } else if (if_nametoindex("lo0") > 0) {
+            snprintf(LOOPBACK, BUF_SIZE - 1, "lo0");
+        } else {
+            cout << "Failed to detect loopback interface. Neither "
+                 << "lo nor lo0 worked. I give up." << endl;
+            FAIL();
+        }
+    }
+
+    /// @brief Returns the collection of existing interfaces.
+    IfaceCollection& getIfacesLst() { return (ifaces_); }
+
+    /// @brief This function creates fictious interfaces with fictious
+    /// addresses.
+    ///
+    /// These interfaces can be used in tests that don't actually try
+    /// to open the sockets on these interfaces. Some tests use mock
+    /// objects to mimic sockets being open. These interfaces are
+    /// suitable for such tests.
+    void createIfaces() {
+
+        ifaces_.clear();
+
+        // local loopback
+        ifaces_.push_back(createIface("lo", 0, "127.0.0.1"));
+        // eth0
+        ifaces_.push_back(createIface("eth0", 1, "10.0.0.1"));
+        // eth1
+        ifaces_.push_back(createIface("eth1", 2, "192.0.2.3"));
+    }
+
+    /// @brief Create an object representing interface.
+    ///
+    /// Apart from creating an interface, this function also sets the
+    /// interface flags:
+    /// - loopback flag if interface name is "lo"
+    /// - up always true
+    /// - running always true
+    /// - inactive always to false
+    ///
+    /// If one needs to modify the default flag settings, the setIfaceFlags
+    /// function should be used.
+    ///
+    /// @param name A name of the interface to be created.
+    /// @param ifindex An index of the interface to be created.
+    /// @param addr An IP address to be assigned to the interface.
+    ///
+    /// @return An object representing interface.
+    static Iface createIface(const std::string& name, const int ifindex,
+                             const std::string& addr) {
+        Iface iface(name, ifindex);
+        iface.addAddress(IOAddress(addr));
+        if (name == "lo") {
+            iface.flag_loopback_ = true;
+        }
+        iface.flag_up_ = true;
+        iface.flag_running_ = true;
+        iface.inactive4_ = false;
+        return (iface);
+    }
+
+    /// @brief Modified flags on the interface.
+    ///
+    /// @param name A name of the interface.
+    /// @param loopback A new value of the loopback flag.
+    /// @param up A new value of the up flag.
+    /// @param running A new value of the running flag.
+    /// @param inactive A new value of the inactive flag.
+    void setIfaceFlags(const std::string& name, const bool loopback,
+                       const bool up, const bool running,
+                       const bool inactive) {
+        for (IfaceMgr::IfaceCollection::iterator iface = ifaces_.begin();
+             iface != ifaces_.end(); ++iface) {
+            if (iface->getName() == name) {
+                iface->flag_loopback_ = loopback;
+                iface->flag_up_ = up;
+                iface->flag_running_ = running;
+                iface->inactive4_ = inactive;
+            }
+        }
     }
-    IfaceCollection & getIfacesLst() { return ifaces_; }
 };
 
-// Dummy class for now, but this will be expanded when needed
+/// @brief A test fixture class for IfaceMgr.
+///
+/// @todo Sockets being opened by IfaceMgr tests should be managed by
+/// the test fixture. In particular, the class should close sockets after
+/// each test. Current approach where test cases are responsible for
+/// closing sockets is resource leak prone, especially in case of the
+/// test failure path.
 class IfaceMgrTest : public ::testing::Test {
 public:
-    // These are empty for now, but let's keep them around
-    IfaceMgrTest() {
+    /// @brief Constructor.
+    IfaceMgrTest()
+        : errors_count_(0) {
     }
 
     ~IfaceMgrTest() {
@@ -166,28 +283,34 @@ public:
         return (NULL);
     }
 
+    /// @brief Implements an IfaceMgr error handler.
+    ///
+    /// This function can be installed as an error handler for the
+    /// IfaceMgr::openSockets4 function. The error handler is invoked
+    /// when an attempt to open a particular socket fails for any reason.
+    /// Typically, the error handler will log a warning. When the error
+    /// handler returns, the openSockets4 function should continue opening
+    /// sockets on other interfaces.
+    ///
+    /// @param errmsg An error string indicating the reason for failure.
+    void ifaceMgrErrorHandler(const std::string&) {
+        // Increase the counter of invocations to this function. By checking
+        // this number, a test amy check if the expected number of errors
+        // has occurred.
+        ++errors_count_;
+    }
+
+    /// Holds the invocation counter for ifaceMgrErrorHandler.
+    int errors_count_;
+
 };
 
 // We need some known interface to work reliably. Loopback interface is named
 // lo on Linux and lo0 on BSD boxes. We need to find out which is available.
 // This is not a real test, but rather a workaround that will go away when
-// interface detection is implemented.
-
-// NOTE: At this stage of development, write access to current directory
-// during running tests is required.
+// interface detection is implemented on all OSes.
 TEST_F(IfaceMgrTest, loDetect) {
-
-    // Poor man's interface detection.  It will go away as soon as proper
-    // interface detection is implemented
-    if (if_nametoindex("lo") > 0) {
-        snprintf(LOOPBACK, BUF_SIZE - 1, "lo");
-    } else if (if_nametoindex("lo0") > 0) {
-        snprintf(LOOPBACK, BUF_SIZE - 1, "lo0");
-    } else {
-        cout << "Failed to detect loopback interface. Neither "
-             << "lo nor lo0 worked. I give up." << endl;
-        FAIL();
-    }
+    NakedIfaceMgr::loDetect();
 }
 
 // Uncomment this test to create packet writer. It will
@@ -798,13 +921,6 @@ TEST_F(IfaceMgrTest, sendReceive6) {
     // assume the one or the other will always be chosen for sending data. Therefore
     // we should accept both values as source ports.
     EXPECT_TRUE((rcvPkt->getRemotePort() == 10546) || (rcvPkt->getRemotePort() == 10547));
-
-    // 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);
 }
 
 TEST_F(IfaceMgrTest, sendReceive4) {
@@ -986,6 +1102,54 @@ TEST_F(IfaceMgrTest, setMatchingPacketFilter) {
     EXPECT_TRUE(iface_mgr->isDirectResponseSupported());
 }
 
+// This test checks that it is not possible to open two sockets: IP/UDP
+// and raw (LPF) socket and bind to the same address and port. The
+// raw socket should be opened together with the fallback IP/UDP socket.
+// The fallback socket should fail to open when there is another IP/UDP
+// socket bound to the same address and port. Failing to open the fallback
+// socket should preclude the raw socket from being open.
+TEST_F(IfaceMgrTest, checkPacketFilterLPFSocket) {
+    IOAddress loAddr("127.0.0.1");
+    int socket1 = -1, socket2 = -1;
+    // Create two instances of IfaceMgr.
+    boost::scoped_ptr<NakedIfaceMgr> iface_mgr1(new NakedIfaceMgr());
+    ASSERT_TRUE(iface_mgr1);
+    boost::scoped_ptr<NakedIfaceMgr> iface_mgr2(new NakedIfaceMgr());
+    ASSERT_TRUE(iface_mgr2);
+
+    // Let IfaceMgr figure out which Packet Filter to use when
+    // direct response capability is not desired. It should pick
+    // PktFilterInet.
+    EXPECT_NO_THROW(iface_mgr1->setMatchingPacketFilter(false));
+    // Let's open a loopback socket with handy unpriviliged port number
+    socket1 = iface_mgr1->openSocket(LOOPBACK, loAddr,
+                                     DHCP4_SERVER_PORT + 10000);
+
+    EXPECT_GE(socket1, 0);
+
+    // Then the second use PkFilterLPF mode
+    EXPECT_NO_THROW(iface_mgr2->setMatchingPacketFilter(true));
+
+    // The socket is open and bound. Another attempt to open socket and
+    // bind to the same address and port should result in an exception.
+    EXPECT_THROW(
+        socket2 = iface_mgr2->openSocket(LOOPBACK, loAddr,
+                                         DHCP4_SERVER_PORT + 10000),
+        isc::dhcp::SocketConfigError
+    );
+    // Surprisingly we managed to open another socket. We have to close it
+    // to prevent resource leak.
+    if (socket2 >= 0) {
+        close(socket2);
+        ADD_FAILURE() << "Two sockets opened and bound to the same IP"
+            " address and UDP port";
+    }
+
+    if (socket1 >= 0) {
+        close(socket1);
+    }
+}
+
 #else
 
 // This non-Linux specific test checks whether it is possible to use
@@ -1045,6 +1209,157 @@ TEST_F(IfaceMgrTest, socket4) {
     close(socket1);
 }
 
+// This test verifies that IPv4 sockets are open on all interfaces (except
+// loopback), when interfaces are up, running and active (not disabled from
+// the DHCP configuration).
+TEST_F(IfaceMgrTest, openSockets4) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+
+    // Use the custom packet filter object. This object mimics the socket
+    // opening operation - the real socket is not open.
+    boost::shared_ptr<TestPktFilter> custom_packet_filter(new TestPktFilter());
+    ASSERT_TRUE(custom_packet_filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(custom_packet_filter));
+
+    // Simulate opening sockets using the dummy packet filter.
+    ASSERT_NO_THROW(ifacemgr.openSockets4(DHCP4_SERVER_PORT, true, NULL));
+
+    // Expect that the sockets are open on both eth0 and eth1.
+    EXPECT_EQ(1, ifacemgr.getIface("eth0")->getSockets().size());
+    EXPECT_EQ(1, ifacemgr.getIface("eth1")->getSockets().size());
+    // Socket shouldn't have been opened on loopback.
+    EXPECT_TRUE(ifacemgr.getIface("lo")->getSockets().empty());
+}
+
+// This test verifies that the socket is not open on the interface which is
+// down, but sockets are open on all other non-loopback interfaces.
+TEST_F(IfaceMgrTest, openSockets4IfaceDown) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+
+    boost::shared_ptr<TestPktFilter> custom_packet_filter(new TestPktFilter());
+    ASSERT_TRUE(custom_packet_filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(custom_packet_filter));
+
+    // Boolean parameters specify that eth0 is:
+    // - not a loopback
+    // - is "down" (not up)
+    // - is not running
+    // - is active (is not inactive)
+    ifacemgr.setIfaceFlags("eth0", false, false, true, false);
+    ASSERT_FALSE(ifacemgr.getIface("eth0")->flag_up_);
+    ASSERT_NO_THROW(ifacemgr.openSockets4(DHCP4_SERVER_PORT, true, NULL));
+
+    // There should be no socket on eth0 open, because interface was down.
+    EXPECT_TRUE(ifacemgr.getIface("eth0")->getSockets().empty());
+    // Expecting that the socket is open on eth1 because it was up, running
+    // and active.
+    EXPECT_EQ(1, ifacemgr.getIface("eth1")->getSockets().size());
+    // Never open socket on loopback interface.
+    EXPECT_TRUE(ifacemgr.getIface("lo")->getSockets().empty());
+}
+
+// This test verifies that the socket is not open on the interface which is
+// disabled from the DHCP configuration, but sockets are open on all other
+// non-loopback interfaces.
+TEST_F(IfaceMgrTest, openSockets4IfaceInactive) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+
+    boost::shared_ptr<TestPktFilter> custom_packet_filter(new TestPktFilter());
+    ASSERT_TRUE(custom_packet_filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(custom_packet_filter));
+
+    // Boolean parameters specify that eth1 is:
+    // - not a loopback
+    // - is up
+    // - is running
+    // - is inactive
+    ifacemgr.setIfaceFlags("eth1", false, true, true, true);
+    ASSERT_TRUE(ifacemgr.getIface("eth1")->inactive4_);
+    ASSERT_NO_THROW(ifacemgr.openSockets4(DHCP4_SERVER_PORT, true, NULL));
+
+    // The socket on eth0 should be open because interface is up, running and
+    // active (not disabled through DHCP configuration, for example).
+    EXPECT_EQ(1, ifacemgr.getIface("eth0")->getSockets().size());
+    // There should be no socket open on eth1 because it was marked inactive.
+    EXPECT_TRUE(ifacemgr.getIface("eth1")->getSockets().empty());
+    // Sockets are not open on loopback interfaces too.
+    EXPECT_TRUE(ifacemgr.getIface("lo")->getSockets().empty());
+}
+
+// Test that exception is thrown when trying to bind a new socket to the port
+// and address which is already in use by another socket.
+TEST_F(IfaceMgrTest, openSockets4NoErrorHandler) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+
+    boost::shared_ptr<TestPktFilter> custom_packet_filter(new TestPktFilter());
+    ASSERT_TRUE(custom_packet_filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(custom_packet_filter));
+
+    // Open socket on eth1. The openSockets4 should detect that this
+    // socket has been already open and an attempt to open another socket
+    // and bind to this address and port should fail.
+    ASSERT_NO_THROW(ifacemgr.openSocket("eth1", IOAddress("192.0.2.3"),
+                                        DHCP4_SERVER_PORT));
+
+    // The function throws an exception when it tries to open a socket
+    // and bind it to the address in use.
+    EXPECT_THROW(ifacemgr.openSockets4(DHCP4_SERVER_PORT, true, NULL),
+                 isc::dhcp::SocketConfigError);
+
+}
+
+// Test that the external error handler is called when trying to bind a new
+// socket to the address and port being in use. The sockets on the other
+// interfaces should open just fine..
+TEST_F(IfaceMgrTest, openSocket4ErrorHandler) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+
+    boost::shared_ptr<TestPktFilter> custom_packet_filter(new TestPktFilter());
+    ASSERT_TRUE(custom_packet_filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(custom_packet_filter));
+
+    // Open socket on eth0. The openSockets4 should detect that this
+    // socket has been already open and an attempt to open another socket
+    // and bind to this address and port should fail.
+    ASSERT_NO_THROW(ifacemgr.openSocket("eth0", IOAddress("10.0.0.1"),
+                                        DHCP4_SERVER_PORT));
+
+    // Install an error handler before trying to open sockets. This handler
+    // should be called when the IfaceMgr fails to open socket on eth0.
+    isc::dhcp::IfaceMgrErrorMsgCallback error_handler =
+        boost::bind(&IfaceMgrTest::ifaceMgrErrorHandler, this, _1);
+    ASSERT_NO_THROW(ifacemgr.openSockets4(DHCP4_SERVER_PORT, true, error_handler));
+    // We expect that an error occured when we tried to open a socket on
+    // eth0, but the socket on eth1 should open just fine.
+    EXPECT_EQ(1, errors_count_);
+
+    // Reset errors count.
+    errors_count_ = 0;
+
+    // Now that we have two sockets open, we can try this again but this time
+    // we should get two errors: one when opening a socket on eth0, another one
+    // when opening a socket on eth1.
+    ASSERT_NO_THROW(ifacemgr.openSockets4(DHCP4_SERVER_PORT, true, error_handler));
+    EXPECT_EQ(2, errors_count_);
+
+}
+
+
 // Test the Iface structure itself
 TEST_F(IfaceMgrTest, iface) {
     boost::scoped_ptr<Iface> iface;
@@ -1118,18 +1433,28 @@ TEST_F(IfaceMgrTest, iface_methods) {
 TEST_F(IfaceMgrTest, socketInfo) {
 
     // Check that socketinfo for IPv4 socket is functional
-    SocketInfo sock1(7, IOAddress("192.0.2.56"), DHCP4_SERVER_PORT + 7);
+    SocketInfo sock1(IOAddress("192.0.2.56"), DHCP4_SERVER_PORT + 7, 7);
     EXPECT_EQ(7, sock1.sockfd_);
+    EXPECT_EQ(-1, sock1.fallbackfd_);
     EXPECT_EQ("192.0.2.56", sock1.addr_.toText());
     EXPECT_EQ(AF_INET, sock1.family_);
     EXPECT_EQ(DHCP4_SERVER_PORT + 7, sock1.port_);
 
+    // Check that non-default value of the fallback socket descriptor is set
+    SocketInfo sock2(IOAddress("192.0.2.53"), DHCP4_SERVER_PORT + 8, 8, 10);
+    EXPECT_EQ(8, sock2.sockfd_);
+    EXPECT_EQ(10, sock2.fallbackfd_);
+    EXPECT_EQ("192.0.2.53", sock2.addr_.toText());
+    EXPECT_EQ(AF_INET, sock2.family_);
+    EXPECT_EQ(DHCP4_SERVER_PORT + 8, sock2.port_);
+
     // Check that socketinfo for IPv6 socket is functional
-    SocketInfo sock2(9, IOAddress("2001:db8:1::56"), DHCP4_SERVER_PORT + 9);
-    EXPECT_EQ(9, sock2.sockfd_);
-    EXPECT_EQ("2001:db8:1::56", sock2.addr_.toText());
-    EXPECT_EQ(AF_INET6, sock2.family_);
-    EXPECT_EQ(DHCP4_SERVER_PORT + 9, sock2.port_);
+    SocketInfo sock3(IOAddress("2001:db8:1::56"), DHCP4_SERVER_PORT + 9, 9);
+    EXPECT_EQ(9, sock3.sockfd_);
+    EXPECT_EQ(-1, sock3.fallbackfd_);
+    EXPECT_EQ("2001:db8:1::56", sock3.addr_.toText());
+    EXPECT_EQ(AF_INET6, sock3.family_);
+    EXPECT_EQ(DHCP4_SERVER_PORT + 9, sock3.port_);
 
     // Now let's test if IfaceMgr handles socket info properly
     scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
@@ -1137,6 +1462,7 @@ TEST_F(IfaceMgrTest, socketInfo) {
     ASSERT_TRUE(loopback);
     loopback->addSocket(sock1);
     loopback->addSocket(sock2);
+    loopback->addSocket(sock3);
 
     Pkt6 pkt6(DHCPV6_REPLY, 123456);
 
@@ -1191,6 +1517,7 @@ TEST_F(IfaceMgrTest, socketInfo) {
 
     EXPECT_NO_THROW(
         ifacemgr->getIface(LOOPBACK)->delSocket(7);
+        ifacemgr->getIface(LOOPBACK)->delSocket(8);
     );
 
     // It should throw again, there's no usable socket anymore.
@@ -1386,7 +1713,7 @@ void parse_ifconfig(const std::string& textFile, IfaceMgr::IfaceCollection& ifac
 // TODO: temporarily disabled, see ticket #1529
 TEST_F(IfaceMgrTest, DISABLED_detectIfaces_linux) {
 
-    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+    scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
     IfaceMgr::IfaceCollection& detectedIfaces = ifacemgr->getIfacesLst();
 
     const std::string textFile = "ifconfig.txt";
@@ -1491,10 +1818,192 @@ TEST_F(IfaceMgrTest, DISABLED_detectIfaces_linux) {
             FAIL();
         }
     }
+}
+#endif
 
-    delete ifacemgr;
+#if defined(OS_BSD)
+#include <net/if_dl.h>
+#endif
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+
+/// @brief Checks the index of this interface
+/// @param iface Kea interface structure to be checked
+/// @param ifptr original structure returned by getifaddrs
+/// @return true if index is returned properly
+bool
+checkIfIndex(const Iface & iface,
+             struct ifaddrs *& ifptr) {
+    return (iface.getIndex() == if_nametoindex(ifptr->ifa_name));
 }
+
+/// @brief Checks if the interface has proper flags set
+/// @param iface Kea interface structure to be checked
+/// @param ifptr original structure returned by getifaddrs
+/// @return true if flags are set properly
+bool
+checkIfFlags(const Iface & iface,
+             struct ifaddrs *& ifptr) {
+        bool flag_loopback_ = ifptr->ifa_flags & IFF_LOOPBACK;
+        bool flag_up_ = ifptr->ifa_flags & IFF_UP;
+        bool flag_running_ = ifptr->ifa_flags & IFF_RUNNING;
+        bool flag_multicast_ = ifptr->ifa_flags & IFF_MULTICAST;
+
+    return ((iface.flag_loopback_ == flag_loopback_) &&
+                        (iface.flag_up_ == flag_up_) &&
+                        (iface.flag_running_ == flag_running_) &&
+                        (iface.flag_multicast_ == flag_multicast_));
+}
+
+/// @brief Checks if MAC Address/IP Addresses are properly well formed
+/// @param iface Kea interface structure to be checked
+/// @param ifptr original structure returned by getifaddrs
+/// @return true if addresses are returned properly
+bool
+checkIfAddrs(const Iface & iface, struct ifaddrs *& ifptr) {
+    const unsigned char * p = 0;
+#if defined(OS_LINUX)
+    // Workaround for Linux ...
+    if(ifptr->ifa_data != 0) {
+        // We avoid localhost as it has no MAC Address
+        if (!strncmp(iface.getName().c_str(), "lo", 2)) {
+            return (true);
+        }
+
+        struct ifreq ifr;
+        memset(& ifr.ifr_name, 0, sizeof ifr.ifr_name);
+        strncpy(ifr.ifr_name, iface.getName().c_str(), sizeof ifr.ifr_name);
+
+        int s = -1; // Socket descriptor
+
+        if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+            isc_throw(Unexpected, "Cannot create AF_INET socket");
+        }
+
+        if (ioctl(s, SIOCGIFHWADDR, & ifr) < 0) {
+            close(s);
+            isc_throw(Unexpected, "Cannot set SIOCGIFHWADDR flag");
+        }
+
+        const uint8_t * p =
+            reinterpret_cast<uint8_t *>(ifr.ifr_ifru.ifru_hwaddr.sa_data);
+
+        close(s);
+
+        /// @todo: Check MAC address length. For some interfaces it is
+        /// different than 6. Some have 0, while some exotic ones (like
+        /// infiniband) have 20.
+        return (!memcmp(p, iface.getMac(), iface.getMacLen()));
+    }
+#endif
+
+    if(!ifptr->ifa_addr) {
+        return (false);
+    }
+
+    switch(ifptr->ifa_addr->sa_family) {
+#if defined(OS_BSD)
+        case AF_LINK: {
+            // We avoid localhost as it has no MAC Address
+            if (!strncmp(iface.getName().c_str(), "lo", 2)) {
+                return (true);
+            }
+
+            struct sockaddr_dl * hwdata =
+                   reinterpret_cast<struct sockaddr_dl *>(ifptr->ifa_addr);
+            p = reinterpret_cast<uint8_t *>(LLADDR(hwdata));
+
+            // Extract MAC address length
+            if (hwdata->sdl_alen != iface.getMacLen()) {
+                return (false);
+            }
+
+            return (!memcmp(p, iface.getMac(), hwdata->sdl_alen));
+        }
 #endif
+        case AF_INET: {
+            struct sockaddr_in * v4data =
+                   reinterpret_cast<struct sockaddr_in *>(ifptr->ifa_addr);
+            p = reinterpret_cast<uint8_t *>(& v4data->sin_addr);
+
+            IOAddress addrv4 = IOAddress::fromBytes(AF_INET, p);
+
+            for (Iface::AddressCollection::const_iterator a =
+                     iface.getAddresses().begin();
+                 a != iface.getAddresses().end(); ++ a) {
+                if(a->isV4() && (*a) == addrv4) {
+                    return (true);
+                }
+            }
+
+            return (false);
+        }
+        case AF_INET6: {
+            struct sockaddr_in6 * v6data =
+                   reinterpret_cast<struct sockaddr_in6 *>(ifptr->ifa_addr);
+            p = reinterpret_cast<uint8_t *>(& v6data->sin6_addr);
+
+            IOAddress addrv6 = IOAddress::fromBytes(AF_INET6, p);
+
+            for(Iface::AddressCollection::const_iterator a =
+                    iface.getAddresses().begin();
+                a != iface.getAddresses().end(); ++ a) {
+                if(a->isV6() && (*a) == addrv6) {
+                    return (true);
+                }
+            }
+
+            return (false);
+        }
+        default:
+            return (true);
+    }
+}
+
+TEST_F(IfaceMgrTest, detectIfaces) {
+
+    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+    IfaceMgr::IfaceCollection& detectedIfaces = ifacemgr->getIfacesLst();
+
+    // We are using struct ifaddrs as it is the only good portable one
+    // ifreq and ioctls are far from portabe. For BSD ifreq::ifa_flags field
+    // is only a short which, nowadays, can be negative
+    struct ifaddrs * iflist = 0, * ifptr = 0;
+
+    if(getifaddrs(& iflist) != 0) {
+        isc_throw(Unexpected, "Cannot detect interfaces");
+    }
+
+    for (ifptr = iflist; ifptr != 0; ifptr = ifptr->ifa_next) {
+        for (IfaceMgr::IfaceCollection::const_iterator i = detectedIfaces.begin();
+            i != detectedIfaces.end(); ++ i) {
+            if (!strncmp(ifptr->ifa_name, (*i).getName().c_str(),
+                         (*i).getName().size())) {
+
+                // Typically unit-tests try to be silent. But interface detection
+                // is tricky enough to warrant additional prints.
+                std::cout << "Checking interface " << i->getName() << std::endl;
+
+                // Check if interface index is reported properly
+                EXPECT_TRUE(checkIfIndex(*i, ifptr));
+
+                // Check if flags are reported properly
+                EXPECT_TRUE(checkIfFlags(*i, ifptr));
+
+                // Check if addresses are reported properly
+                EXPECT_TRUE(checkIfAddrs(*i, ifptr));
+
+            }
+        }
+    }
+
+    freeifaddrs(iflist);
+    iflist = 0;
+
+    delete ifacemgr;
+}
 
 volatile bool callback_ok;
 
diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc
index 776b084..ec9dd9f 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) {
@@ -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);
 
@@ -293,10 +352,10 @@ TEST_F(LibDhcpTest, unpackOptions6) {
 
     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::OptionCollection::const_iterator x = options.find(1);
     ASSERT_FALSE(x == options.end()); // option 1 should exist
@@ -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,20 +484,53 @@ TEST_F(LibDhcpTest, packOptions4) {
     OptionPtr opt4(new Option(Option::V4,254, payload[3]));
     OptionPtr opt5(new Option(Option::V4,128, payload[4]));
 
+    // 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) {
@@ -421,7 +539,7 @@ TEST_F(LibDhcpTest, unpackOptions4) {
     isc::dhcp::OptionCollection options; // list of options
 
     ASSERT_NO_THROW(
-        LibDHCP::unpackOptions4(v4packed, options);
+        LibDHCP::unpackOptions4(v4packed, "dhcp4", options);
     );
 
     isc::dhcp::OptionCollection::const_iterator x = options.find(12);
@@ -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) {
@@ -482,7 +643,10 @@ TEST_F(LibDhcpTest, isStandardOption4) {
                                           187, 188, 189, 190, 191, 192, 193, 194, 195,
                                           196, 197, 198, 199, 200, 201, 202, 203, 204,
                                           205, 206, 207, 214, 215, 216, 217, 218, 219,
-                                          222, 223 };
+                                          222, 223, 224, 225, 226, 227, 228, 229, 230,
+                                          231, 232, 233, 234, 235, 236, 237, 238, 239,
+                                          240, 241, 242, 243, 244, 245, 246, 247, 248,
+                                          249, 250, 251, 252, 253, 254 };
     const size_t unassigned_num = sizeof(unassigned_codes) / sizeof(unassigned_codes[0]);
 
     // Try all possible option codes.
@@ -653,8 +817,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 +832,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,
@@ -744,8 +913,14 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
     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));
@@ -767,7 +942,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
@@ -844,7 +1019,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,
@@ -872,8 +1047,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/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_data_types_unittest.cc b/src/lib/dhcp/tests/option_data_types_unittest.cc
index fd5294c..717a330 100644
--- a/src/lib/dhcp/tests/option_data_types_unittest.cc
+++ b/src/lib/dhcp/tests/option_data_types_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
@@ -62,6 +62,20 @@ public:
     }
 };
 
+// The goal of this test is to verify that the getLabelCount returns the
+// correct number of labels in the domain name specified as a string
+// parameter.
+TEST_F(OptionDataTypesTest, getLabelCount) {
+    EXPECT_EQ(0, OptionDataTypeUtil::getLabelCount(""));
+    EXPECT_EQ(1, OptionDataTypeUtil::getLabelCount("."));
+    EXPECT_EQ(2, OptionDataTypeUtil::getLabelCount("example"));
+    EXPECT_EQ(3, OptionDataTypeUtil::getLabelCount("example.com"));
+    EXPECT_EQ(3, OptionDataTypeUtil::getLabelCount("example.com."));
+    EXPECT_EQ(4, OptionDataTypeUtil::getLabelCount("myhost.example.com"));
+    EXPECT_THROW(OptionDataTypeUtil::getLabelCount(".abc."),
+                 isc::dhcp::BadDataTypeCast);
+}
+
 // The goal of this test is to verify that an IPv4 address being
 // stored in a buffer (wire format) can be read into IOAddress
 // object.
diff --git a/src/lib/dhcp/tests/option_definition_unittest.cc b/src/lib/dhcp/tests/option_definition_unittest.cc
index 723aeef..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) {
diff --git a/src/lib/dhcp/tests/option_unittest.cc b/src/lib/dhcp/tests/option_unittest.cc
index 3a47d0e..a3aea9f 100644
--- a/src/lib/dhcp/tests/option_unittest.cc
+++ b/src/lib/dhcp/tests/option_unittest.cc
@@ -61,6 +61,8 @@ public:
     /// 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,
@@ -69,7 +71,7 @@ public:
     /// 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&,
+                   const std::string& option_space,
                    isc::dhcp::OptionCollection& options,
                    size_t* relay_msg_offset,
                    size_t* relay_msg_len) {
@@ -77,7 +79,7 @@ public:
         // callback has been actually called.
         executed_ = true;
         // Use default implementation of the unpack algorithm to parse options.
-        return (LibDHCP::unpackOptions6(buf, options, relay_msg_offset,
+        return (LibDHCP::unpackOptions6(buf, option_space, options, relay_msg_offset,
                                         relay_msg_len));
     }
 
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 242e7d7..72ffff7 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -66,17 +66,19 @@ public:
     /// 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&,
+                   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, options));
+        return (LibDHCP::unpackOptions4(buf, option_space, options));
     }
 
     /// A flag which indicates if callback function has been called.
diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc
index 0687321..e18b545 100644
--- a/src/lib/dhcp/tests/pkt6_unittest.cc
+++ b/src/lib/dhcp/tests/pkt6_unittest.cc
@@ -66,6 +66,8 @@ public:
     /// 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,
@@ -74,7 +76,7 @@ public:
     /// 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&,
+                   const std::string& option_space,
                    isc::dhcp::OptionCollection& options,
                    size_t* relay_msg_offset,
                    size_t* relay_msg_len) {
@@ -82,7 +84,7 @@ public:
         // callback has been actually called.
         executed_ = true;
         // Use default implementation of the unpack algorithm to parse options.
-        return (LibDHCP::unpackOptions6(buf, options, relay_msg_offset,
+        return (LibDHCP::unpackOptions6(buf, option_space, options, relay_msg_offset,
                                         relay_msg_len));
     }
 
diff --git a/src/lib/dhcp/tests/pkt_filter_inet_unittest.cc b/src/lib/dhcp/tests/pkt_filter_inet_unittest.cc
index eaf2e62..c21a996 100644
--- a/src/lib/dhcp/tests/pkt_filter_inet_unittest.cc
+++ b/src/lib/dhcp/tests/pkt_filter_inet_unittest.cc
@@ -17,6 +17,7 @@
 #include <dhcp/iface_mgr.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt_filter_inet.h>
+#include <dhcp/tests/pkt_filter_test_utils.h>
 
 #include <gtest/gtest.h>
 
@@ -32,63 +33,12 @@ const uint16_t PORT = 10067;
 /// Size of the buffer holding received packets.
 const size_t RECV_BUF_SIZE = 2048;
 
-/// This class handles has simple algorithm checking
-/// presence of loopback interface and initializing
-/// its index.
-class PktFilterInetTest : public ::testing::Test {
+// Test fixture class inherits from the class common for all packet
+// filter tests.
+class PktFilterInetTest : public isc::dhcp::test::PktFilterTest {
 public:
-
-    /// @brief Constructor
-    ///
-    /// This constructor initializes socket_ member to a negative value.
-    /// Explcit initialization is performed here because some of the
-    /// tests do not initialize this value. In such cases, destructor
-    /// could invoke close() on uninitialized socket descriptor which
-    /// would result in errors being reported by Valgrind.
-    PktFilterInetTest()
-        : socket_(-1) {
-        // Initialize ifname_ and ifindex_.
-        loInit();
-    }
-
-    /// @brief Destructor
-    ///
-    /// Closes open socket (if any).
-    ~PktFilterInetTest() {
-        // Cleanup after each test. This guarantees
-        // that the socket does not hang after a test.
-        if (socket_ >= 0) {
-            close(socket_);
-        }
+    PktFilterInetTest() : PktFilterTest(PORT) {
     }
-
-    /// @brief Detect loopback interface.
-    ///
-    /// @todo this function will be removed once cross-OS interface
-    /// detection is implemented
-    void loInit() {
-        if (if_nametoindex("lo") > 0) {
-            ifname_ = "lo";
-            ifindex_ = if_nametoindex("lo");
-
-        } else if (if_nametoindex("lo0") > 0) {
-            ifname_ = "lo0";
-            ifindex_ = if_nametoindex("lo0");
-
-        } else {
-            std::cout << "Failed to detect loopback interface. Neither "
-                      << "lo nor lo0 worked. Giving up." << std::endl;
-            FAIL();
-
-
-
-        }
-    }
-
-    std::string ifname_; ///< Loopback interface name
-    uint16_t ifindex_;   ///< Loopback interface index.
-    int socket_;         ///< Socket descriptor.
-
 };
 
 // This test verifies that the PktFilterInet class reports its lack
@@ -112,55 +62,19 @@ TEST_F(PktFilterInetTest, openSocket) {
 
     // Try to open socket.
     PktFilterInet pkt_filter;
-    socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
-    // Check that socket has been opened.
-    ASSERT_GE(socket_, 0);
-
-    // Verify that the socket belongs to AF_INET family.
-    sockaddr_in sock_address;
-    socklen_t sock_address_len = sizeof(sock_address);
-    ASSERT_EQ(0, getsockname(socket_, reinterpret_cast<sockaddr*>(&sock_address),
-                             &sock_address_len));
-    EXPECT_EQ(AF_INET, sock_address.sin_family);
-
-    // Verify that the socket is bound the appropriate address.
-    const std::string bind_addr(inet_ntoa(sock_address.sin_addr));
-    EXPECT_EQ("127.0.0.1", bind_addr);
-
-    // Verify that the socket is bound to appropriate port.
-    EXPECT_EQ(PORT, ntohs(sock_address.sin_port));
-
-    // Verify that the socket has SOCK_DGRAM type.
-    int sock_type;
-    socklen_t sock_type_len = sizeof(sock_type);
-    ASSERT_EQ(0, getsockopt(socket_, SOL_SOCKET, SO_TYPE, &sock_type, &sock_type_len));
-    EXPECT_EQ(SOCK_DGRAM, sock_type);
+    sock_info_ = pkt_filter.openSocket(iface, addr, PORT,
+                                       false, false);
+    // For the packet filter in use, the fallback socket shouldn't be opened.
+    // Fallback is typically opened for raw sockets.
+    EXPECT_LT(sock_info_.fallbackfd_, 0);
+
+    // Test the primary socket.
+    testDgramSocket(sock_info_.sockfd_);
 }
 
 // This test verifies that the packet is correctly sent over the INET
 // datagram socket.
 TEST_F(PktFilterInetTest, send) {
-    // Let's create a DHCPv4 packet.
-    Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 0));
-    ASSERT_TRUE(pkt);
-
-    // Set required fields.
-    pkt->setLocalAddr(IOAddress("127.0.0.1"));
-    pkt->setRemoteAddr(IOAddress("127.0.0.1"));
-    pkt->setRemotePort(PORT);
-    pkt->setLocalPort(PORT + 1);
-    pkt->setIndex(ifindex_);
-    pkt->setIface(ifname_);
-    pkt->setHops(6);
-    pkt->setSecs(42);
-    pkt->setCiaddr(IOAddress("192.0.2.1"));
-    pkt->setSiaddr(IOAddress("192.0.2.2"));
-    pkt->setYiaddr(IOAddress("192.0.2.3"));
-    pkt->setGiaddr(IOAddress("192.0.2.4"));
-
-    // Create the on-wire data.
-    ASSERT_NO_THROW(pkt->pack());
-
     // Packet will be sent over loopback interface.
     Iface iface(ifname_, ifindex_);
     IOAddress addr("127.0.0.1");
@@ -170,27 +84,27 @@ TEST_F(PktFilterInetTest, send) {
     // Open socket. We don't check that the socket has appropriate
     // options and family set because we have checked that in the
     // openSocket test already.
-    socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
-    ASSERT_GE(socket_, 0);
+    sock_info_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+    ASSERT_GE(sock_info_.sockfd_, 0);
 
     // Send the packet over the socket.
-    ASSERT_NO_THROW(pkt_filter.send(iface, socket_, pkt));
+    ASSERT_NO_THROW(pkt_filter.send(iface, sock_info_.sockfd_, test_message_));
 
     // Read the data from socket.
     fd_set readfds;
     FD_ZERO(&readfds);
-    FD_SET(socket_, &readfds);
-    
+    FD_SET(sock_info_.sockfd_, &readfds);
+
     struct timeval timeout;
     timeout.tv_sec = 5;
     timeout.tv_usec = 0;
-    int result = select(socket_ + 1, &readfds, NULL, NULL, &timeout);
+    int result = select(sock_info_.sockfd_ + 1, &readfds, NULL, NULL, &timeout);
     // We should receive some data from loopback interface.
     ASSERT_GT(result, 0);
 
     // Get the actual data.
     uint8_t rcv_buf[RECV_BUF_SIZE];
-    result = recv(socket_, rcv_buf, RECV_BUF_SIZE, 0);
+    result = recv(sock_info_.sockfd_, rcv_buf, RECV_BUF_SIZE, 0);
     ASSERT_GT(result, 0);
 
     // Create the DHCPv4 packet from the received data.
@@ -200,47 +114,16 @@ TEST_F(PktFilterInetTest, send) {
     // Parse the packet.
     ASSERT_NO_THROW(rcvd_pkt->unpack());
 
-    // Verify that the received packet matches sent packet.
-    EXPECT_EQ(pkt->getHops(), rcvd_pkt->getHops());
-    EXPECT_EQ(pkt->getOp(),   rcvd_pkt->getOp());
-    EXPECT_EQ(pkt->getSecs(), rcvd_pkt->getSecs());
-    EXPECT_EQ(pkt->getFlags(), rcvd_pkt->getFlags());
-    EXPECT_EQ(pkt->getCiaddr(), rcvd_pkt->getCiaddr());
-    EXPECT_EQ(pkt->getSiaddr(), rcvd_pkt->getSiaddr());
-    EXPECT_EQ(pkt->getYiaddr(), rcvd_pkt->getYiaddr());
-    EXPECT_EQ(pkt->getGiaddr(), rcvd_pkt->getGiaddr());
-    EXPECT_EQ(pkt->getTransid(), rcvd_pkt->getTransid());
-    EXPECT_TRUE(pkt->getSname() == rcvd_pkt->getSname());
-    EXPECT_TRUE(pkt->getFile() == rcvd_pkt->getFile());
-    EXPECT_EQ(pkt->getHtype(), rcvd_pkt->getHtype());
-    EXPECT_EQ(pkt->getHlen(), rcvd_pkt->getHlen());
+    // Check if the received message is correct.
+    testRcvdMessage(rcvd_pkt);
+
 }
 
 // This test verifies that the DHCPv4 packet is correctly received via
 // INET datagram socket and that it matches sent packet.
 TEST_F(PktFilterInetTest, receive) {
-    // Let's create a DHCPv4 packet.
-    Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 0));
-    ASSERT_TRUE(pkt);
-
-    // Set required fields.
-    pkt->setLocalAddr(IOAddress("127.0.0.1"));
-    pkt->setRemoteAddr(IOAddress("127.0.0.1"));
-    pkt->setRemotePort(PORT);
-    pkt->setLocalPort(PORT + 1);
-    pkt->setIndex(ifindex_);
-    pkt->setIface(ifname_);
-    pkt->setHops(6);
-    pkt->setSecs(42);
-    pkt->setCiaddr(IOAddress("192.0.2.1"));
-    pkt->setSiaddr(IOAddress("192.0.2.2"));
-    pkt->setYiaddr(IOAddress("192.0.2.3"));
-    pkt->setGiaddr(IOAddress("192.0.2.4"));
 
-    // Create the on-wire data.
-    ASSERT_NO_THROW(pkt->pack());
-
-    // Packet will be sent over loopback interface.
+    // Packet will be received over loopback interface.
     Iface iface(ifname_, ifindex_);
     IOAddress addr("127.0.0.1");
 
@@ -249,35 +132,22 @@ TEST_F(PktFilterInetTest, receive) {
     // Open socket. We don't check that the socket has appropriate
     // options and family set because we have checked that in the
     // openSocket test already.
-    socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
-    ASSERT_GE(socket_, 0);
+    sock_info_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+    ASSERT_GE(sock_info_.sockfd_, 0);
 
-    // Send the packet over the socket.
-    ASSERT_NO_THROW(pkt_filter.send(iface, socket_, pkt));
+    // Send a DHCPv4 message to the local loopback address and server's port.
+    sendMessage();
 
     // Receive the packet.
-    SocketInfo socket_info(socket_, IOAddress("127.0.0.1"), PORT);
-    Pkt4Ptr rcvd_pkt = pkt_filter.receive(iface, socket_info);
+    Pkt4Ptr rcvd_pkt = pkt_filter.receive(iface, sock_info_);
     // Check that the packet has been correctly received.
     ASSERT_TRUE(rcvd_pkt);
 
     // Parse the packet.
     ASSERT_NO_THROW(rcvd_pkt->unpack());
 
-    // Verify that the received packet matches sent packet.
-    EXPECT_EQ(pkt->getHops(), rcvd_pkt->getHops());
-    EXPECT_EQ(pkt->getOp(),   rcvd_pkt->getOp());
-    EXPECT_EQ(pkt->getSecs(), rcvd_pkt->getSecs());
-    EXPECT_EQ(pkt->getFlags(), rcvd_pkt->getFlags());
-    EXPECT_EQ(pkt->getCiaddr(), rcvd_pkt->getCiaddr());
-    EXPECT_EQ(pkt->getSiaddr(), rcvd_pkt->getSiaddr());
-    EXPECT_EQ(pkt->getYiaddr(), rcvd_pkt->getYiaddr());
-    EXPECT_EQ(pkt->getGiaddr(), rcvd_pkt->getGiaddr());
-    EXPECT_EQ(pkt->getTransid(), rcvd_pkt->getTransid());
-    EXPECT_TRUE(pkt->getSname() == rcvd_pkt->getSname());
-    EXPECT_TRUE(pkt->getFile() == rcvd_pkt->getFile());
-    EXPECT_EQ(pkt->getHtype(), rcvd_pkt->getHtype());
-    EXPECT_EQ(pkt->getHlen(), rcvd_pkt->getHlen());
+    // Check if the received message is correct.
+    testRcvdMessage(rcvd_pkt);
 }
 
 } // anonymous namespace
diff --git a/src/lib/dhcp/tests/pkt_filter_lpf_unittest.cc b/src/lib/dhcp/tests/pkt_filter_lpf_unittest.cc
index 742a7c9..b0e5c58 100644
--- a/src/lib/dhcp/tests/pkt_filter_lpf_unittest.cc
+++ b/src/lib/dhcp/tests/pkt_filter_lpf_unittest.cc
@@ -18,6 +18,7 @@
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt_filter_lpf.h>
 #include <dhcp/protocol_util.h>
+#include <dhcp/tests/pkt_filter_test_utils.h>
 #include <util/buffer.h>
 
 #include <gtest/gtest.h>
@@ -36,63 +37,12 @@ const uint16_t PORT = 10067;
 /// Size of the buffer holding received packets.
 const size_t RECV_BUF_SIZE = 2048;
 
-/// This class handles has simple algorithm checking
-/// presence of loopback interface and initializing
-/// its index.
-class PktFilterLPFTest : public ::testing::Test {
+// Test fixture class inherits from the class common for all packet
+// filter tests.
+class PktFilterLPFTest : public isc::dhcp::test::PktFilterTest {
 public:
-
-    /// @brief Constructor
-    ///
-    /// This constructor initializes socket_ member to a negative value.
-    /// Explcit initialization is performed here because some of the
-    /// tests do not initialize this value. In such cases, destructor
-    /// could invoke close() on uninitialized socket descriptor which
-    /// would result in errors being reported by Valgrind.
-    PktFilterLPFTest()
-        : socket_(-1) {
-        // Initialize ifname_ and ifindex_.
-        loInit();
-    }
-
-    /// @brief Destructor
-    ///
-    /// Closes open socket (if any).
-    ~PktFilterLPFTest() {
-        // Cleanup after each test. This guarantees
-        // that the socket does not hang after a test.
-        if (socket_ >= 0) {
-            close(socket_);
-        }
-    }
-
-    /// @brief Detect loopback interface.
-    ///
-    /// @todo this function will be removed once cross-OS interface
-    /// detection is implemented
-    void loInit() {
-        if (if_nametoindex("lo") > 0) {
-            ifname_ = "lo";
-            ifindex_ = if_nametoindex("lo");
-
-        } else if (if_nametoindex("lo0") > 0) {
-            ifname_ = "lo0";
-            ifindex_ = if_nametoindex("lo0");
-
-        } else {
-            std::cout << "Failed to detect loopback interface. Neither "
-                      << "lo nor lo0 worked. Giving up." << std::endl;
-            FAIL();
-
-
-
-        }
+    PktFilterLPFTest() : PktFilterTest(PORT) {
     }
-
-    std::string ifname_; ///< Loopback interface name
-    uint16_t ifindex_;   ///< Loopback interface index.
-    int socket_;         ///< Socket descriptor.
-
 };
 
 // This test verifies that the PktFilterLPF class reports its capability
@@ -124,14 +74,18 @@ TEST_F(PktFilterLPFTest, DISABLED_openSocket) {
 
     // Try to open socket.
     PktFilterLPF pkt_filter;
-    socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
-    // Check that socket has been opened.
-    ASSERT_GE(socket_, 0);
+    sock_info_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+
+    // Check that the primary socket has been opened.
+    ASSERT_GE(sock_info_.sockfd_, 0);
+    // Check that the fallback socket has been opened too.
+    ASSERT_GE(sock_info_.fallbackfd_, 0);
 
     // Verify that the socket belongs to AF_PACKET family.
     sockaddr_ll sock_address;
     socklen_t sock_address_len = sizeof(sock_address);
-    ASSERT_EQ(0, getsockname(socket_, reinterpret_cast<sockaddr*>(&sock_address),
+    ASSERT_EQ(0, getsockname(sock_info_.sockfd_,
+                             reinterpret_cast<sockaddr*>(&sock_address),
                              &sock_address_len));
     EXPECT_EQ(AF_PACKET, sock_address.sll_family);
 
@@ -141,39 +95,14 @@ TEST_F(PktFilterLPFTest, DISABLED_openSocket) {
     // Verify that the socket has SOCK_RAW type.
     int sock_type;
     socklen_t sock_type_len = sizeof(sock_type);
-    ASSERT_EQ(0, getsockopt(socket_, SOL_SOCKET, SO_TYPE, &sock_type, &sock_type_len));
+    ASSERT_EQ(0, getsockopt(sock_info_.sockfd_, SOL_SOCKET, SO_TYPE,
+                            &sock_type, &sock_type_len));
     EXPECT_EQ(SOCK_RAW, sock_type);
 }
 
 // This test verifies correctness of sending DHCP packet through the raw
 // socket, whereby all IP stack headers are hand-crafted.
 TEST_F(PktFilterLPFTest, DISABLED_send) {
-        // Let's create a DHCPv4 packet.
-    Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 0));
-    ASSERT_TRUE(pkt);
-
-    // Set required fields.
-    // By setting the local address to broadcast we simulate the
-    // typical scenario when client's request was send to broadcast
-    // address and server by default used it as a source address
-    // in its response. The send() function should be able to detect
-    // it and correct the source address.
-    pkt->setLocalAddr(IOAddress("255.255.255.255"));
-    pkt->setRemoteAddr(IOAddress("127.0.0.1"));
-    pkt->setRemotePort(PORT);
-    pkt->setLocalPort(PORT + 1);
-    pkt->setIndex(ifindex_);
-    pkt->setIface(ifname_);
-    pkt->setHops(6);
-    pkt->setSecs(42);
-    pkt->setCiaddr(IOAddress("192.0.2.1"));
-    pkt->setSiaddr(IOAddress("192.0.2.2"));
-    pkt->setYiaddr(IOAddress("192.0.2.3"));
-    pkt->setGiaddr(IOAddress("192.0.2.4"));
-
-    // Create the on-wire data.
-    ASSERT_NO_THROW(pkt->pack());
-
     // Packet will be sent over loopback interface.
     Iface iface(ifname_, ifindex_);
     IOAddress addr("127.0.0.1");
@@ -183,27 +112,29 @@ TEST_F(PktFilterLPFTest, DISABLED_send) {
     // Open socket. We don't check that the socket has appropriate
     // options and family set because we have checked that in the
     // openSocket test already.
-    socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
-    ASSERT_GE(socket_, 0);
+
+    sock_info_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+
+    ASSERT_GE(sock_info_.sockfd_, 0);
 
     // Send the packet over the socket.
-    ASSERT_NO_THROW(pkt_filter.send(iface, socket_, pkt));
+    ASSERT_NO_THROW(pkt_filter.send(iface, sock_info_.sockfd_, test_message_));
 
     // Read the data from socket.
     fd_set readfds;
     FD_ZERO(&readfds);
-    FD_SET(socket_, &readfds);
-    
+    FD_SET(sock_info_.sockfd_, &readfds);
+
     struct timeval timeout;
     timeout.tv_sec = 5;
     timeout.tv_usec = 0;
-    int result = select(socket_ + 1, &readfds, NULL, NULL, &timeout);
+    int result = select(sock_info_.sockfd_ + 1, &readfds, NULL, NULL, &timeout);
     // We should receive some data from loopback interface.
     ASSERT_GT(result, 0);
 
     // Get the actual data.
     uint8_t rcv_buf[RECV_BUF_SIZE];
-    result = recv(socket_, rcv_buf, RECV_BUF_SIZE, 0);
+    result = recv(sock_info_.sockfd_, rcv_buf, RECV_BUF_SIZE, 0);
     ASSERT_GT(result, 0);
 
     Pkt4Ptr dummy_pkt = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 0));
@@ -223,48 +154,15 @@ TEST_F(PktFilterLPFTest, DISABLED_send) {
     // Parse the packet.
     ASSERT_NO_THROW(rcvd_pkt->unpack());
 
-    // Verify that the received packet matches sent packet.
-    EXPECT_EQ(pkt->getHops(), rcvd_pkt->getHops());
-    EXPECT_EQ(pkt->getOp(),   rcvd_pkt->getOp());
-    EXPECT_EQ(pkt->getSecs(), rcvd_pkt->getSecs());
-    EXPECT_EQ(pkt->getFlags(), rcvd_pkt->getFlags());
-    EXPECT_EQ(pkt->getCiaddr(), rcvd_pkt->getCiaddr());
-    EXPECT_EQ(pkt->getSiaddr(), rcvd_pkt->getSiaddr());
-    EXPECT_EQ(pkt->getYiaddr(), rcvd_pkt->getYiaddr());
-    EXPECT_EQ(pkt->getGiaddr(), rcvd_pkt->getGiaddr());
-    EXPECT_EQ(pkt->getTransid(), rcvd_pkt->getTransid());
-    EXPECT_TRUE(pkt->getSname() == rcvd_pkt->getSname());
-    EXPECT_TRUE(pkt->getFile() == rcvd_pkt->getFile());
-    EXPECT_EQ(pkt->getHtype(), rcvd_pkt->getHtype());
-    EXPECT_EQ(pkt->getHlen(), rcvd_pkt->getHlen());
+    // Check if the received message is correct.
+    testRcvdMessage(rcvd_pkt);
 }
 
 // This test verifies correctness of reception of the DHCP packet over
 // raw socket, whereby all IP stack headers are hand-crafted.
 TEST_F(PktFilterLPFTest, DISABLED_receive) {
 
-    // Let's create a DHCPv4 packet.
-    Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 0));
-    ASSERT_TRUE(pkt);
-
-    // Set required fields.
-    pkt->setLocalAddr(IOAddress("127.0.0.1"));
-    pkt->setRemoteAddr(IOAddress("127.0.0.1"));
-    pkt->setRemotePort(PORT);
-    pkt->setLocalPort(PORT + 1);
-    pkt->setIndex(ifindex_);
-    pkt->setIface(ifname_);
-    pkt->setHops(6);
-    pkt->setSecs(42);
-    pkt->setCiaddr(IOAddress("192.0.2.1"));
-    pkt->setSiaddr(IOAddress("192.0.2.2"));
-    pkt->setYiaddr(IOAddress("192.0.2.3"));
-    pkt->setGiaddr(IOAddress("192.0.2.4"));
-
-    // Create the on-wire data.
-    ASSERT_NO_THROW(pkt->pack());
-
-    // Packet will be sent over loopback interface.
+    // Packet will be received over loopback interface.
     Iface iface(ifname_, ifindex_);
     IOAddress addr("127.0.0.1");
 
@@ -273,35 +171,22 @@ TEST_F(PktFilterLPFTest, DISABLED_receive) {
     // Open socket. We don't check that the socket has appropriate
     // options and family set because we have checked that in the
     // openSocket test already.
-    socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
-    ASSERT_GE(socket_, 0);
+    sock_info_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+    ASSERT_GE(sock_info_.sockfd_, 0);
 
-    // Send the packet over the socket.
-    ASSERT_NO_THROW(pkt_filter.send(iface, socket_, pkt));
+    // Send DHCPv4 message to the local loopback address and server's port.
+    sendMessage();
 
-    // Receive the packet.
-    SocketInfo socket_info(socket_, IOAddress("127.0.0.1"), PORT);
-    Pkt4Ptr rcvd_pkt = pkt_filter.receive(iface, socket_info);
+    // Receive the packet using LPF packet filter.
+    Pkt4Ptr rcvd_pkt = pkt_filter.receive(iface, sock_info_);
     // Check that the packet has been correctly received.
     ASSERT_TRUE(rcvd_pkt);
 
     // Parse the packet.
     ASSERT_NO_THROW(rcvd_pkt->unpack());
 
-    // Verify that the received packet matches sent packet.
-    EXPECT_EQ(pkt->getHops(), rcvd_pkt->getHops());
-    EXPECT_EQ(pkt->getOp(),   rcvd_pkt->getOp());
-    EXPECT_EQ(pkt->getSecs(), rcvd_pkt->getSecs());
-    EXPECT_EQ(pkt->getFlags(), rcvd_pkt->getFlags());
-    EXPECT_EQ(pkt->getCiaddr(), rcvd_pkt->getCiaddr());
-    EXPECT_EQ(pkt->getSiaddr(), rcvd_pkt->getSiaddr());
-    EXPECT_EQ(pkt->getYiaddr(), rcvd_pkt->getYiaddr());
-    EXPECT_EQ(pkt->getGiaddr(), rcvd_pkt->getGiaddr());
-    EXPECT_EQ(pkt->getTransid(), rcvd_pkt->getTransid());
-    EXPECT_TRUE(pkt->getSname() == rcvd_pkt->getSname());
-    EXPECT_TRUE(pkt->getFile() == rcvd_pkt->getFile());
-    EXPECT_EQ(pkt->getHtype(), rcvd_pkt->getHtype());
-    EXPECT_EQ(pkt->getHlen(), rcvd_pkt->getHlen());
+    // Check if the received message is correct.
+    testRcvdMessage(rcvd_pkt);
 }
 
 } // anonymous namespace
diff --git a/src/lib/dhcp/tests/pkt_filter_test_utils.cc b/src/lib/dhcp/tests/pkt_filter_test_utils.cc
new file mode 100644
index 0000000..eb91b9f
--- /dev/null
+++ b/src/lib/dhcp/tests/pkt_filter_test_utils.cc
@@ -0,0 +1,194 @@
+// 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/pkt4.h>
+#include <dhcp/tests/pkt_filter_test_utils.h>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+PktFilterTest::PktFilterTest(const uint16_t port)
+    : port_(port),
+      sock_info_(isc::asiolink::IOAddress("127.0.0.1"), port, -1, -1),
+      send_msg_sock_(-1) {
+    // Initialize ifname_ and ifindex_.
+    loInit();
+    // Initialize test_message_.
+    initTestMessage();
+}
+
+PktFilterTest::~PktFilterTest() {
+    // Cleanup after each test. This guarantees
+    // that the sockets do not hang after a test.
+    if (sock_info_.sockfd_ >= 0) {
+        close(sock_info_.sockfd_);
+    }
+    if (sock_info_.fallbackfd_ >=0) {
+        close(sock_info_.fallbackfd_);
+    }
+    if (send_msg_sock_ >= 0) {
+        close(send_msg_sock_);
+    }
+}
+
+void
+PktFilterTest::initTestMessage() {
+    // Let's create a DHCPv4 message instance.
+    test_message_.reset(new Pkt4(DHCPOFFER, 0));
+
+    // Set required fields.
+    test_message_->setLocalAddr(IOAddress("127.0.0.1"));
+    test_message_->setRemoteAddr(IOAddress("127.0.0.1"));
+    test_message_->setRemotePort(port_);
+    test_message_->setLocalPort(port_ + 1);
+    test_message_->setIndex(ifindex_);
+    test_message_->setIface(ifname_);
+    test_message_->setHops(6);
+    test_message_->setSecs(42);
+    test_message_->setCiaddr(IOAddress("192.0.2.1"));
+    test_message_->setSiaddr(IOAddress("192.0.2.2"));
+    test_message_->setYiaddr(IOAddress("192.0.2.3"));
+    test_message_->setGiaddr(IOAddress("192.0.2.4"));
+
+    try {
+        test_message_->pack();
+    } catch (const isc::Exception& ex) {
+        ADD_FAILURE() << "failed to create test message for PktFilterTest";
+    }
+}
+
+void
+PktFilterTest::loInit() {
+    if (if_nametoindex("lo") > 0) {
+        ifname_ = "lo";
+        ifindex_ = if_nametoindex("lo");
+
+    } else if (if_nametoindex("lo0") > 0) {
+        ifname_ = "lo0";
+        ifindex_ = if_nametoindex("lo0");
+
+    } else {
+        std::cout << "Failed to detect loopback interface. Neither "
+                  << "lo nor lo0 worked. Giving up." << std::endl;
+        FAIL();
+
+    }
+}
+
+void
+PktFilterTest::sendMessage() {
+
+    // Packet will be sent over loopback interface.
+    Iface iface(ifname_, ifindex_);
+    IOAddress addr("127.0.0.1");
+
+    struct sockaddr_in addr4;
+    memset(&addr4, 0, sizeof(sockaddr));
+    addr4.sin_family = AF_INET;
+    addr4.sin_port = htons(port_ + 1);
+
+    send_msg_sock_ = socket(AF_INET, SOCK_DGRAM, 0);
+    ASSERT_GE(send_msg_sock_, 0);
+
+    ASSERT_GE(bind(send_msg_sock_, (struct sockaddr *)&addr4,
+                   sizeof(addr4)), 0);
+
+    struct sockaddr_in dest_addr4;
+    memset(&dest_addr4, 0, sizeof(sockaddr));
+    dest_addr4.sin_family = AF_INET;
+    dest_addr4.sin_port = htons(port_);
+    ASSERT_EQ(sendto(send_msg_sock_, test_message_->getBuffer().getData(),
+                     test_message_->getBuffer().getLength(), 0,
+                     reinterpret_cast<struct sockaddr*>(&dest_addr4),
+                     sizeof(sockaddr)), test_message_->getBuffer().getLength());
+    close(send_msg_sock_);
+    send_msg_sock_ = -1;
+
+}
+
+void
+PktFilterTest::testDgramSocket(const int sock) const {
+    // Check that socket has been opened.
+    ASSERT_GE(sock, 0);
+
+    // Verify that the socket belongs to AF_INET family.
+    sockaddr_in sock_address;
+    socklen_t sock_address_len = sizeof(sock_address);
+    ASSERT_EQ(0, getsockname(sock,
+                             reinterpret_cast<sockaddr*>(&sock_address),
+                             &sock_address_len));
+    EXPECT_EQ(AF_INET, sock_address.sin_family);
+
+    // Verify that the socket is bound the appropriate address.
+    const std::string bind_addr(inet_ntoa(sock_address.sin_addr));
+    EXPECT_EQ("127.0.0.1", bind_addr);
+
+    // Verify that the socket is bound to appropriate port.
+    EXPECT_EQ(port_, ntohs(sock_address.sin_port));
+
+    // Verify that the socket has SOCK_DGRAM type.
+    int sock_type;
+    socklen_t sock_type_len = sizeof(sock_type);
+    ASSERT_EQ(0, getsockopt(sock, SOL_SOCKET, SO_TYPE,
+                            &sock_type, &sock_type_len));
+    EXPECT_EQ(SOCK_DGRAM, sock_type);
+}
+
+void
+PktFilterTest::testRcvdMessage(const Pkt4Ptr& rcvd_msg) const {
+    EXPECT_EQ(test_message_->getHops(), rcvd_msg->getHops());
+    EXPECT_EQ(test_message_->getOp(),   rcvd_msg->getOp());
+    EXPECT_EQ(test_message_->getSecs(), rcvd_msg->getSecs());
+    EXPECT_EQ(test_message_->getFlags(), rcvd_msg->getFlags());
+    EXPECT_EQ(test_message_->getCiaddr(), rcvd_msg->getCiaddr());
+    EXPECT_EQ(test_message_->getSiaddr(), rcvd_msg->getSiaddr());
+    EXPECT_EQ(test_message_->getYiaddr(), rcvd_msg->getYiaddr());
+    EXPECT_EQ(test_message_->getGiaddr(), rcvd_msg->getGiaddr());
+    EXPECT_EQ(test_message_->getTransid(), rcvd_msg->getTransid());
+    EXPECT_TRUE(test_message_->getSname() == rcvd_msg->getSname());
+    EXPECT_TRUE(test_message_->getFile() == rcvd_msg->getFile());
+    EXPECT_EQ(test_message_->getHtype(), rcvd_msg->getHtype());
+    EXPECT_EQ(test_message_->getHlen(), rcvd_msg->getHlen());
+}
+
+bool
+PktFilterStub::isDirectResponseSupported() const {
+    return (true);
+}
+
+SocketInfo
+PktFilterStub::openSocket(const Iface&,
+           const isc::asiolink::IOAddress& addr,
+           const uint16_t port, const bool, const bool) {
+    return (SocketInfo(addr, port, 0));
+}
+
+Pkt4Ptr
+PktFilterStub::receive(const Iface&, const SocketInfo&) {
+    return Pkt4Ptr();
+}
+
+int
+PktFilterStub::send(const Iface&, uint16_t, const Pkt4Ptr&) {
+    return (0);
+}
+
+
+} // end of isc::dhcp::test namespace
+} // end of isc::dhcp namespace
+} // end of isc namespace
diff --git a/src/lib/dhcp/tests/pkt_filter_test_utils.h b/src/lib/dhcp/tests/pkt_filter_test_utils.h
new file mode 100644
index 0000000..1b93b06
--- /dev/null
+++ b/src/lib/dhcp/tests/pkt_filter_test_utils.h
@@ -0,0 +1,167 @@
+// 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 PKT_FILTER_TEST_UTILS_H
+#define PKT_FILTER_TEST_UTILS_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/pkt_filter.h>
+#include <gtest/gtest.h>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @brief Test fixture class for testing classes derived from PktFilter class.
+///
+/// This class implements a simple algorithm checking presence of the loopback
+/// interface and initializing its index. It assumes that the loopback interface
+/// name is one of 'lo' or 'lo0'. If none of those interfaces is found, the
+/// constructor will report a failure.
+///
+/// @todo The interface detection algorithm should be more generic. This will
+/// be possible once the cross-OS interface detection is implemented.
+class PktFilterTest : public ::testing::Test {
+public:
+
+    /// @brief Constructor
+    ///
+    /// This constructor initializes sock_info_ structure to a default value.
+    /// The socket descriptors should be set to a negative value to indicate
+    /// that no socket has been opened. Specific tests will reinitialize this
+    /// structure with the values of the open sockets. For non-negative socket
+    /// descriptors, the class destructor will close associated sockets.
+    PktFilterTest(const uint16_t port);
+
+    /// @brief Destructor
+    ///
+    /// Closes open sockets (if any).
+    virtual ~PktFilterTest();
+
+    /// @brief Initializes DHCPv4 message used by tests.
+    void initTestMessage();
+
+    /// @brief Detect loopback interface.
+    ///
+    /// @todo this function will be removed once cross-OS interface
+    /// detection is implemented
+    void loInit();
+
+    /// @brief Sends a single DHCPv4 message to the loopback address.
+    ///
+    /// This function opens a datagram socket and binds it to the local loopback
+    /// address and client port. The client's port is assumed to be port_ + 1.
+    /// The send_msg_sock_ member holds the socket descriptor so as the socket
+    /// is closed automatically in the destructor. If the function succeeds to
+    /// send a DHCPv4 message, the socket is closed so as the function can be
+    /// called again within the same test.
+    void sendMessage();
+
+    /// @brief Test that the datagram socket is opened correctly.
+    ///
+    /// This function is used by multiple tests.
+    ///
+    /// @param sock A descriptor of the open socket.
+    void testDgramSocket(const int sock) const;
+
+    /// @brief Checks if the received message matches the test_message_.
+    ///
+    /// @param rcvd_msg An instance of the message to be tested.
+    void testRcvdMessage(const Pkt4Ptr& rcvd_msg) const;
+
+    std::string ifname_;   ///< Loopback interface name
+    uint16_t ifindex_;     ///< Loopback interface index.
+    uint16_t port_;        ///< A port number used for the test.
+    isc::dhcp::SocketInfo sock_info_; ///< A structure holding socket info.
+    int send_msg_sock_;    ///< Holds a descriptor of the socket used by
+                           ///< sendMessage function.
+    Pkt4Ptr test_message_; ///< A DHCPv4 message used by tests.
+
+};
+
+/// @brief A stub implementation of the PktFilter class.
+///
+/// This class implements abstract methods of the @c isc::dhcp::test::PktFilter
+/// class. It is used by unit tests, which test protected methods of the
+/// @c isc::dhcp::test::PktFilter class. The implemented abstract methods are
+/// no-op.
+class PktFilterStub : public PktFilter {
+public:
+
+    /// @brief Checks if the direct DHCPv4 response is supported.
+    ///
+    /// This function checks if the direct response capability is supported,
+    /// i.e. if the server can respond to the client which doesn't have an
+    /// address yet. For this dummy class, the true is alaways returned.
+    ///
+    /// @return always true.
+    virtual bool isDirectResponseSupported() const;
+
+    /// @brief Simulate opening of the socket.
+    ///
+    /// This function simulates opening a primary socket. In reality, it doesn't
+    /// open a socket but the socket descriptor returned in the SocketInfo
+    /// structure is always set to 0.
+    ///
+    /// @param iface An interface descriptor.
+    /// @param addr Address on the interface to be used to send packets.
+    /// @param port Port number to bind socket to.
+    /// @param receive_bcast A flag which indicates if socket should be
+    /// configured to receive broadcast packets (if true).
+    /// @param send_bcast A flag which indicates if the socket should be
+    /// configured to send broadcast packets (if true).
+    ///
+    /// @note All parameters are ignored.
+    ///
+    /// @return A SocketInfo structure with the socket descriptor set to 0. The
+    /// fallback socket descriptor is set to a negative value.
+    virtual SocketInfo openSocket(const Iface& iface,
+                                  const isc::asiolink::IOAddress& addr,
+                                  const uint16_t port, const bool, const bool);
+
+    /// @brief Simulate reception of the DHCPv4 message.
+    ///
+    /// @param iface An interface to be used to receive DHCPv4 message.
+    /// @param sock_info A descriptor of the primary and fallback sockets.
+    ///
+    /// @note All parameters are ignored.
+    ///
+    /// @return always a NULL object.
+    virtual Pkt4Ptr receive(const Iface& iface, const SocketInfo& sock_info);
+
+    /// @brief Simulates sending a DHCPv4 message.
+    ///
+    /// This function does nothing.
+    ///
+    /// @param iface An interface to be used to send DHCPv4 message.
+    /// @param port A port used to send a message.
+    /// @param pkt A DHCPv4 to be sent.
+    ///
+    /// @note All parameters are ignored.
+    ///
+    /// @return 0.
+    virtual int send(const Iface& iface, uint16_t port, const Pkt4Ptr& pkt);
+
+    // Change the scope of the protected function so as they can be unit tested.
+    using PktFilter::openFallbackSocket;
+
+};
+
+
+}; // end of isc::dhcp::test namespace
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // PKT_FILTER_TEST_UTILS_H
diff --git a/src/lib/dhcp/tests/pkt_filter_unittest.cc b/src/lib/dhcp/tests/pkt_filter_unittest.cc
new file mode 100644
index 0000000..eef7fea
--- /dev/null
+++ b/src/lib/dhcp/tests/pkt_filter_unittest.cc
@@ -0,0 +1,74 @@
+// 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 <dhcp/iface_mgr.h>
+#include <dhcp/tests/pkt_filter_test_utils.h>
+#include <gtest/gtest.h>
+
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+
+namespace {
+
+/// Port number used by tests.
+const uint16_t PORT = 10067;
+
+class PktFilterBaseClassTest : public isc::dhcp::test::PktFilterTest {
+public:
+    /// @brief Constructor
+    ///
+    /// Does nothing but setting up the UDP port for the test.
+    PktFilterBaseClassTest() : PktFilterTest(PORT) {
+    }
+};
+
+// This test verifies that the fallback socket is successfuly opened and
+// bound using the protected function of the PktFilter class.
+TEST_F(PktFilterBaseClassTest, openFallbackSocket) {
+    // Open socket using the function under test. Note that, we don't have to
+    // close the socket on our own because the test fixture constructor
+    // will handle it.
+    PktFilterStub pkt_filter;
+    ASSERT_NO_THROW(sock_info_.fallbackfd_ =
+                    pkt_filter.openFallbackSocket(IOAddress("127.0.0.1"), PORT))
+        << "Failed to open fallback socket.";
+
+    // Test that the socket has been successfully created.
+    testDgramSocket(sock_info_.fallbackfd_);
+
+    // In addition, we should check that the fallback socket is non-blocking.
+    int sock_flags = fcntl(sock_info_.fallbackfd_, F_GETFL);
+    EXPECT_EQ(O_NONBLOCK, sock_flags & O_NONBLOCK)
+        << "Fallback socket is blocking, it should be non-blocking - check"
+        " fallback socket flags (fcntl).";
+
+    // Now that we have the socket open, let's try to open another one. This
+    // should cause a binding error.
+    int another_sock = -1;
+    EXPECT_THROW(another_sock =
+                 pkt_filter.openFallbackSocket(IOAddress("127.0.0.1"), PORT),
+                 isc::dhcp::SocketConfigError)
+        << "it should be not allowed to open and bind two fallback sockets"
+        " to the same address and port. Surprisingly, the socket bound.";
+    // Hard to believe we managed to open another socket. But if so, we have
+    // to close it to prevent a resource leak.
+    if (another_sock >= 0) {
+        close(another_sock);
+    }
+}
+
+} // anonymous namespace
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/ncr_msg.cc b/src/lib/dhcp_ddns/ncr_msg.cc
index 05cc5d2..418eaa0 100644
--- a/src/lib/dhcp_ddns/ncr_msg.cc
+++ b/src/lib/dhcp_ddns/ncr_msg.cc
@@ -13,6 +13,7 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <dhcp_ddns/ncr_msg.h>
+#include <dns/name.h>
 #include <asiolink/io_address.h>
 #include <asiolink/io_error.h>
 #include <cryptolink/cryptolink.h>
@@ -25,8 +26,23 @@
 namespace isc {
 namespace dhcp_ddns {
 
+
 /********************************* D2Dhcid ************************************/
 
+namespace {
+
+///
+/// @name Constants which define DHCID identifier-type
+//@{
+/// DHCID created from client's HW address.
+const uint8_t DHCID_ID_HWADDR   = 0x0;
+/// DHCID created from client identifier.
+const uint8_t DHCID_ID_CLIENTID = 0x1;
+/// DHCID created from DUID.
+const uint8_t DHCID_ID_DUID     = 0x2;
+
+}
+
 D2Dhcid::D2Dhcid() {
 }
 
@@ -34,6 +50,16 @@ D2Dhcid::D2Dhcid(const std::string& data) {
     fromStr(data);
 }
 
+D2Dhcid::D2Dhcid(const isc::dhcp::HWAddrPtr& hwaddr,
+                 const std::vector<uint8_t>& wire_fqdn) {
+    fromHWAddr(hwaddr, wire_fqdn);
+}
+
+D2Dhcid::D2Dhcid(const std::vector<uint8_t>& clientid_data,
+                 const std::vector<uint8_t>& wire_fqdn) {
+    fromClientId(clientid_data, wire_fqdn);
+}
+
 D2Dhcid::D2Dhcid(const isc::dhcp::DUID& duid,
                  const std::vector<uint8_t>& wire_fqdn) {
     fromDUID(duid, wire_fqdn);
@@ -56,33 +82,62 @@ D2Dhcid::toStr() const {
 }
 
 void
+D2Dhcid::fromClientId(const std::vector<uint8_t>& clientid_data,
+                      const std::vector<uint8_t>& wire_fqdn) {
+    createDigest(DHCID_ID_CLIENTID, clientid_data, wire_fqdn);
+}
+
+void
+D2Dhcid::fromHWAddr(const isc::dhcp::HWAddrPtr& hwaddr,
+                    const std::vector<uint8_t>& wire_fqdn) {
+    if (!hwaddr) {
+        isc_throw(isc::dhcp_ddns::DhcidRdataComputeError,
+                  "unable to compute DHCID from the HW address, "
+                  "NULL pointer has been specified");
+    } else if (hwaddr->hwaddr_.empty()) {
+        isc_throw(isc::dhcp_ddns::DhcidRdataComputeError,
+                  "unable to compute DHCID from the HW address, "
+                  "HW address is empty");
+    }
+    std::vector<uint8_t> hwaddr_data;
+    hwaddr_data.push_back(hwaddr->htype_);
+    hwaddr_data.insert(hwaddr_data.end(), hwaddr->hwaddr_.begin(),
+                       hwaddr->hwaddr_.end());
+    createDigest(DHCID_ID_HWADDR, hwaddr_data, wire_fqdn);
+}
+
+
+void
 D2Dhcid::fromDUID(const isc::dhcp::DUID& duid,
                   const std::vector<uint8_t>& wire_fqdn) {
-    // DHCID created from DUID starts with two bytes representing
-    // a type of the identifier. The value of 0x0002 indicates that
-    // DHCID has been created from DUID. The 3rd byte is equal to 1
-    // which indicates that the SHA-256 algorithm is used to create
-    // a DHCID digest. This value is called digest-type.
-    static uint8_t dhcid_header[] = { 0x00, 0x02, 0x01 };
 
+    createDigest(DHCID_ID_DUID, duid.getDuid(), wire_fqdn);
+}
+
+void
+D2Dhcid::createDigest(const uint8_t identifier_type,
+                      const std::vector<uint8_t>& identifier_data,
+                      const std::vector<uint8_t>& wire_fqdn) {
     // We get FQDN in the wire format, so we don't know if it is
     // valid. It is caller's responsibility to make sure it is in
     // the valid format. Here we just make sure it is not empty.
     if (wire_fqdn.empty()) {
-        isc_throw(isc::dhcp_ddns::NcrMessageError,
+        isc_throw(isc::dhcp_ddns::DhcidRdataComputeError,
                   "empty FQDN used to create DHCID");
     }
 
-    // Get the wire representation of the DUID.
-    std::vector<uint8_t> data = duid.getDuid();
-    // It should be DUID class responsibility to validate the DUID
-    // but let's be on the safe side here and make sure that empty
-    // DUID is not returned.
-    if (data.empty()) {
-        isc_throw(isc::dhcp_ddns::NcrMessageError,
+    // It is a responsibility of the classes which encapsulate client
+    // identifiers, e.g. DUID, to validate the client identifier data.
+    // But let's be on the safe side and at least check that it is not
+    // empty.
+    if (identifier_data.empty()) {
+        isc_throw(isc::dhcp_ddns::DhcidRdataComputeError,
                   "empty DUID used to create DHCID");
     }
 
+    // A data buffer will be used to compute the digest.
+    std::vector<uint8_t> data = identifier_data;
+
     // Append FQDN in the wire format.
     data.insert(data.end(), wire_fqdn.begin(), wire_fqdn.end());
 
@@ -100,14 +155,26 @@ D2Dhcid::fromDUID(const isc::dhcp::DUID& duid,
         secure = sha.process(static_cast<const Botan::byte*>(&data[0]),
                              data.size());
     } catch (const std::exception& ex) {
-        isc_throw(isc::dhcp_ddns::NcrMessageError,
+        isc_throw(isc::dhcp_ddns::DhcidRdataComputeError,
                   "error while generating DHCID from DUID: "
                   << ex.what());
     }
 
-    // The exception unsafe part is finished, so we can finally replace
-    // the contents of bytes_.
-    bytes_.assign(dhcid_header, dhcid_header + sizeof(dhcid_header));
+    // The DHCID RDATA has the following structure:
+    //
+    //    < identifier-type > < digest-type > < digest >
+    //
+    // where identifier type
+
+    // Let's allocate the space for the identifier-type (2 bytes) and
+    // digest-type (1 byte). This is 3 bytes all together.
+    bytes_.resize(3);
+    // Leave first byte 0 and set the second byte. Those two bytes
+    // form the identifier-type.
+    bytes_[1] = identifier_type;
+    // Third byte is always equal to 1, which specifies SHA-256 digest type.
+    bytes_[2] = 1;
+    // Now let's append the digest.
     bytes_.insert(bytes_.end(), secure.begin(), secure.end());
 }
 
@@ -117,11 +184,13 @@ operator<<(std::ostream& os, const D2Dhcid& dhcid) {
     return (os);
 }
 
+
+
 /**************************** NameChangeRequest ******************************/
 
 NameChangeRequest::NameChangeRequest()
     : change_type_(CHG_ADD), forward_change_(false),
-    reverse_change_(false), fqdn_(""), ip_address_(""),
+    reverse_change_(false), fqdn_(""), ip_io_address_("0.0.0.0"),
     dhcid_(), lease_expires_on_(), lease_length_(0), status_(ST_NEW) {
 }
 
@@ -132,10 +201,13 @@ NameChangeRequest::NameChangeRequest(const NameChangeType change_type,
             const uint64_t lease_expires_on,
             const uint32_t lease_length)
     : change_type_(change_type), forward_change_(forward_change),
-    reverse_change_(reverse_change), fqdn_(fqdn), ip_address_(ip_address),
+    reverse_change_(reverse_change), fqdn_(fqdn), ip_io_address_("0.0.0.0"),
     dhcid_(dhcid), lease_expires_on_(lease_expires_on),
     lease_length_(lease_length), status_(ST_NEW) {
 
+    // User setter to validate address.
+    setIpAddress(ip_address);
+
     // Validate the contents. This will throw a NcrMessageError if anything
     // is invalid.
     validateContent();
@@ -291,20 +363,12 @@ NameChangeRequest::toJSON() const  {
 void
 NameChangeRequest::validateContent() {
     //@todo This is an initial implementation which provides a minimal amount
-    // of validation.  FQDN, DHCID, and IP Address members are all currently
+    // of validation.  FQDN and DHCID members are all currently
     // strings, these may be replaced with richer classes.
     if (fqdn_ == "") {
         isc_throw(NcrMessageError, "FQDN cannot be blank");
     }
 
-    // Validate IP Address.
-    try {
-        isc::asiolink::IOAddress io_addr(ip_address_);
-    } catch (const isc::asiolink::IOError& ex) {
-        isc_throw(NcrMessageError,
-                  "Invalid ip address string for ip_address: " << ip_address_);
-    }
-
     // Validate the DHCID.
     if (dhcid_.getBytes().size()  == 0) {
         isc_throw(NcrMessageError, "DHCID cannot be blank");
@@ -410,15 +474,26 @@ NameChangeRequest::setFqdn(isc::data::ConstElementPtr element) {
 
 void
 NameChangeRequest::setFqdn(const std::string& value) {
-    fqdn_ = value;
+    try {
+        dns::Name tmp(value);
+        fqdn_ = tmp.toText();
+    } catch (const std::exception& ex) {
+        isc_throw(NcrMessageError,
+                  "Invalid FQDN value: " << value << ", reason:" << ex.what());
+    }
 }
 
 void
 NameChangeRequest::setIpAddress(const std::string& value) {
-    ip_address_ = value;
+    // Validate IP Address.
+    try {
+        ip_io_address_ = isc::asiolink::IOAddress(value);
+    } catch (const isc::asiolink::IOError& ex) {
+        isc_throw(NcrMessageError,
+                  "Invalid ip address string for ip_address: " << value);
+    }
 }
 
-
 void
 NameChangeRequest::setIpAddress(isc::data::ConstElementPtr element) {
     setIpAddress(element->stringValue());
@@ -515,7 +590,7 @@ NameChangeRequest::toText() const {
            << "Reverse Change: " << (reverse_change_ ? "yes" : "no")
            << std::endl
            << "FQDN: [" << fqdn_ << "]" << std::endl
-           << "IP Address: [" << ip_address_  << "]" << std::endl
+           << "IP Address: [" << ip_io_address_.toText()  << "]" << std::endl
            << "DHCID: [" << dhcid_.toStr() << "]" << std::endl
            << "Lease Expires On: " << getLeaseExpiresOnStr() << std::endl
            << "Lease Length: " << lease_length_ << std::endl;
@@ -529,7 +604,7 @@ NameChangeRequest::operator == (const NameChangeRequest& other) {
             (forward_change_ == other.forward_change_) &&
             (reverse_change_ == other.reverse_change_) &&
             (fqdn_ == other.fqdn_) &&
-            (ip_address_ == other.ip_address_) &&
+            (ip_io_address_ == other.ip_io_address_) &&
             (dhcid_ == other.dhcid_) &&
             (lease_expires_on_ == other.lease_expires_on_) &&
             (lease_length_ == other.lease_length_));
diff --git a/src/lib/dhcp_ddns/ncr_msg.h b/src/lib/dhcp_ddns/ncr_msg.h
index 8f01638..47a7be6 100644
--- a/src/lib/dhcp_ddns/ncr_msg.h
+++ b/src/lib/dhcp_ddns/ncr_msg.h
@@ -22,6 +22,8 @@
 
 #include <cc/data.h>
 #include <dhcp/duid.h>
+#include <dhcp/hwaddr.h>
+#include <dns/name.h>
 #include <exceptions/exceptions.h>
 #include <util/buffer.h>
 #include <util/encode/hex.h>
@@ -40,6 +42,15 @@ public:
         isc::Exception(file, line, what) { };
 };
 
+/// @brief Exception thrown when there is an error occured during computation
+/// of the DHCID.
+class DhcidRdataComputeError : public isc::Exception {
+public:
+    DhcidRdataComputeError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+
 /// @brief Defines the types of DNS updates that can be requested.
 enum NameChangeType {
   CHG_ADD,
@@ -79,6 +90,22 @@ public:
     D2Dhcid(const std::string& data);
 
     /// @brief Constructor, creates an instance of the @c D2Dhcid from the
+    /// HW address.
+    ///
+    /// @param hwaddr A pointer to the object encapsulating HW address.
+    /// @param wire_fqdn A on-wire canonical representation of the FQDN.
+    D2Dhcid(const isc::dhcp::HWAddrPtr& hwaddr,
+            const std::vector<uint8_t>& wire_fqdn);
+
+    /// @brief Constructor, creates an instance of the @c D2Dhcid from the
+    /// client identifier carried in the Client Identifier option.
+    ///
+    /// @param clientid_data Holds the raw bytes representing client identifier.
+    /// @param wire_fqdn A on-wire canonical representation of the FQDN.
+    D2Dhcid(const std::vector<uint8_t>& clientid_data,
+            const std::vector<uint8_t>& wire_fqdn);
+
+    /// @brief Constructor, creates an instance of the @c D2Dhcid from the
     /// @c isc::dhcp::DUID.
     ///
     /// @param duid An object representing DUID.
@@ -101,6 +128,13 @@ public:
     /// or there is an odd number of digits.
     void fromStr(const std::string& data);
 
+    /// @brief Sets the DHCID value based on the Client Identifier.
+    ///
+    /// @param clientid_data Holds the raw bytes representing client identifier.
+    /// @param wire_fqdn A on-wire canonical representation of the FQDN.
+    void fromClientId(const std::vector<uint8_t>& clientid_data,
+                      const std::vector<uint8_t>& wire_fqdn);
+
     /// @brief Sets the DHCID value based on the DUID and FQDN.
     ///
     /// This function requires that the FQDN conforms to the section 3.5
@@ -112,10 +146,17 @@ public:
     void fromDUID(const isc::dhcp::DUID& duid,
                   const std::vector<uint8_t>& wire_fqdn);
 
+    /// @brief Sets the DHCID value based on the HW address and FQDN.
+    ///
+    /// @param hwaddr A pointer to the object encapsulating HW address.
+    /// @param wire_fqdn A on-wire canonical representation of the FQDN.
+    void fromHWAddr(const isc::dhcp::HWAddrPtr& hwaddr,
+                    const std::vector<uint8_t>& wire_fqdn);
+
     /// @brief Returns a reference to the DHCID byte vector.
     ///
     /// @return a reference to the vector.
-    const std::vector<uint8_t>& getBytes() {
+    const std::vector<uint8_t>& getBytes() const {
         return (bytes_);
     }
 
@@ -135,6 +176,22 @@ public:
     }
 
 private:
+
+    /// @brief Creates the DHCID using specified indetifier.
+    ///
+    /// This function creates the DHCID RDATA as specified in RFC4701,
+    /// section 3.5.
+    ///
+    /// @param identifier_type is a less significant byte of the identifier-type
+    /// defined in RFC4701.
+    /// @param identifier_data A buffer holding client identifier raw data -
+    /// e.g. DUID, data carried in the Client Identifier option or client's
+    /// HW address.
+    /// @param A on-wire canonical representation of the FQDN.
+    void createDigest(const uint8_t identifier_type,
+                      const std::vector<uint8_t>& identifier_data,
+                      const std::vector<uint8_t>& wire_fqdn);
+
     /// @brief Storage for the DHCID value in unsigned bytes.
     std::vector<uint8_t> bytes_;
 };
@@ -355,11 +412,32 @@ public:
     /// Element
     void setFqdn(isc::data::ConstElementPtr element);
 
-    /// @brief Fetches the request IP address.
+    /// @brief Fetches the request IP address string.
     ///
     /// @return a string containing the IP address
-    const std::string& getIpAddress() const {
-        return (ip_address_);
+    std::string getIpAddress() const {
+        return (ip_io_address_.toText());
+    }
+
+    /// @brief Fetches the request IP address as an IOAddress.
+    ///
+    /// @return a asiolink::IOAddress containing the IP address
+    const asiolink::IOAddress& getIpIoAddress() const {
+        return (ip_io_address_);
+    }
+
+    /// @brief Returns true if the lease address is a IPv4 lease.
+    ///
+    /// @return boolean true if the lease address family is AF_INET.
+    bool isV4 () const {
+        return (ip_io_address_.isV4());
+    }
+
+    /// @brief Returns true if the lease address is a IPv6 lease.
+    ///
+    /// @return boolean true if the lease address family is AF_INET6.
+    bool isV6 () const {
+        return (ip_io_address_.isV6());
     }
 
     /// @brief Sets the IP address to the given value.
@@ -511,8 +589,13 @@ private:
     /// manipulation.
     std::string fqdn_;
 
-    /// @brief The ip address leased to the FQDN.
-    std::string ip_address_;
+    /// @brief The ip address leased to the FQDN as an IOAddress.
+    ///
+    /// The lease address is used in many places, sometimes as a string
+    /// and sometimes as an IOAddress.  To avoid converting back and forth
+    /// continually over the life span of an NCR, we do it once when the
+    /// ip address is actually set.
+    asiolink::IOAddress ip_io_address_;
 
     /// @brief The lease client's unique DHCID.
     /// @todo Currently, this is uses D2Dhcid it but may be replaced with
diff --git a/src/lib/dhcp_ddns/tests/ncr_unittests.cc b/src/lib/dhcp_ddns/tests/ncr_unittests.cc
index 2b09de1..9386a5f 100644
--- a/src/lib/dhcp_ddns/tests/ncr_unittests.cc
+++ b/src/lib/dhcp_ddns/tests/ncr_unittests.cc
@@ -14,6 +14,7 @@
 
 #include <dhcp_ddns/ncr_msg.h>
 #include <dhcp/duid.h>
+#include <dhcp/hwaddr.h>
 #include <util/time_utilities.h>
 
 #include <gtest/gtest.h>
@@ -124,6 +125,17 @@ const char *invalid_msgs[] =
      " \"lease_expires_on\" : \"20130121132405\" , "
      " \"lease_length\" : 1300 "
      "}",
+    // Malformed FQDN
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \".bad_name\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"20130121132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
     // Bad IP address
      "{"
      " \"change_type\" : 0 , "
@@ -290,6 +302,26 @@ TEST(NameChangeRequestTest, dhcidTest) {
 
 }
 
+/// @brief Test fixture class for testing DHCID creation.
+class DhcidTest : public ::testing::Test {
+public:
+    /// @brief Constructor
+    DhcidTest() {
+        const uint8_t fqdn_data[] = {
+            6, 109, 121, 104, 111, 115, 116,     // myhost.
+            7, 101, 120, 97, 109, 112, 108, 101, // example.
+            3, 99, 111, 109, 0                   // com.
+        };
+        wire_fqdn_.assign(fqdn_data, fqdn_data + sizeof(fqdn_data));
+    }
+
+    /// @brief Destructor
+    virtual ~DhcidTest() {
+    }
+
+    std::vector<uint8_t> wire_fqdn_;
+};
+
 /// Tests that DHCID is correctly created from a DUID and FQDN. The final format
 /// of the DHCID is as follows:
 /// <identifier-type> <digest-type-code> <digest>
@@ -298,25 +330,15 @@ TEST(NameChangeRequestTest, dhcidTest) {
 /// - digest-type-code (1 octet) indicates SHA-256 hashing and is equal 0x1.
 /// - digest = SHA-256(<DUID> <FQDN>)
 /// Note: FQDN is given in the on-wire canonical format.
-TEST(NameChangeRequestTest, dhcidFromDUID) {
+TEST_F(DhcidTest, fromDUID) {
     D2Dhcid dhcid;
 
     // Create DUID.
     uint8_t duid_data[] = { 0, 1, 2, 3, 4, 5, 6 };
     DUID duid(duid_data, sizeof(duid_data));
 
-    // Create FQDN in on-wire format: myhost.example.com. It is encoded
-    // as a set of labels, each preceded by its length. The whole FQDN
-    // is zero-terminated.
-    const uint8_t fqdn_data[] = {
-        6, 109, 121, 104, 111, 115, 116,     // myhost.
-        7, 101, 120, 97, 109, 112, 108, 101, // example.
-        3, 99, 111, 109, 0                   // com.
-    };
-    std::vector<uint8_t> wire_fqdn(fqdn_data, fqdn_data + sizeof(fqdn_data));
-
     // Create DHCID.
-    ASSERT_NO_THROW(dhcid.fromDUID(duid, wire_fqdn));
+    ASSERT_NO_THROW(dhcid.fromDUID(duid, wire_fqdn_));
 
     // The reference DHCID (represented as string of hexadecimal digits)
     // has been calculated using one of the online calculators.
@@ -328,25 +350,15 @@ TEST(NameChangeRequestTest, dhcidFromDUID) {
 }
 
 // Test that DHCID is correctly created when the DUID has minimal length (1).
-TEST(NameChangeRequestTest, dhcidFromMinDUID) {
+TEST_F(DhcidTest, fromMinDUID) {
     D2Dhcid dhcid;
 
     // Create DUID.
     uint8_t duid_data[] = { 1 };
     DUID duid(duid_data, sizeof(duid_data));
 
-    // Create FQDN in on-wire format: myhost.example.com. It is encoded
-    // as a set of labels, each preceded by its length. The whole FQDN
-    // is zero-terminated.
-    const uint8_t fqdn_data[] = {
-        6, 109, 121, 104, 111, 115, 116,     // myhost.
-        7, 101, 120, 97, 109, 112, 108, 101, // example.
-        3, 99, 111, 109, 0                   // com.
-    };
-    std::vector<uint8_t> wire_fqdn(fqdn_data, fqdn_data + sizeof(fqdn_data));
-
     // Create DHCID.
-    ASSERT_NO_THROW(dhcid.fromDUID(duid, wire_fqdn));
+    ASSERT_NO_THROW(dhcid.fromDUID(duid, wire_fqdn_));
 
     // The reference DHCID (represented as string of hexadecimal digits)
     // has been calculated using one of the online calculators.
@@ -358,25 +370,15 @@ TEST(NameChangeRequestTest, dhcidFromMinDUID) {
 }
 
 // Test that DHCID is correctly created when the DUID has maximal length (128).
-TEST(NameChangeRequestTest, dhcidFromMaxDUID) {
+TEST_F(DhcidTest, fromMaxDUID) {
     D2Dhcid dhcid;
 
     // Create DUID.
     std::vector<uint8_t> duid_data(128, 1);
     DUID duid(&duid_data[0], duid_data.size());
 
-    // Create FQDN in on-wire format: myhost.example.com. It is encoded
-    // as a set of labels, each preceded by its length. The whole FQDN
-    // is zero-terminated.
-    const uint8_t fqdn_data[] = {
-        6, 109, 121, 104, 111, 115, 116,     // myhost.
-        7, 101, 120, 97, 109, 112, 108, 101, // example.
-        3, 99, 111, 109, 0                   // com.
-    };
-    std::vector<uint8_t> wire_fqdn(fqdn_data, fqdn_data + sizeof(fqdn_data));
-
     // Create DHCID.
-    ASSERT_NO_THROW(dhcid.fromDUID(duid, wire_fqdn));
+    ASSERT_NO_THROW(dhcid.fromDUID(duid, wire_fqdn_));
 
     // The reference DHCID (represented as string of hexadecimal digits)
     // has been calculated using one of the online calculators.
@@ -387,6 +389,71 @@ TEST(NameChangeRequestTest, dhcidFromMaxDUID) {
     EXPECT_EQ(dhcid_ref, dhcid.toStr());
 }
 
+// This test verifies that DHCID is properly computed from a buffer holding
+// client identifier data.
+TEST_F(DhcidTest, fromClientId) {
+    D2Dhcid dhcid;
+
+    // Create a buffer holding client id..
+    uint8_t clientid_data[] = { 0, 1, 2, 3, 4, 5, 6 };
+    std::vector<uint8_t> clientid(clientid_data,
+                                  clientid_data + sizeof(clientid_data));
+
+    // Create DHCID.
+    ASSERT_NO_THROW(dhcid.fromClientId(clientid, wire_fqdn_));
+
+    // The reference DHCID (represented as string of hexadecimal digits)
+    // has been calculated using one of the online calculators.
+    std::string dhcid_ref = "0001012191B7B21AF97E0E656DF887C5E2D"
+        "EF30E7758A207EDF4CCB2DE8CA37066021C";
+
+    // Make sure that the DHCID is valid.
+    EXPECT_EQ(dhcid_ref, dhcid.toStr());
+
+    // Make sure that the empty FQDN is not accepted.
+    std::vector<uint8_t> empty_wire_fqdn;
+    EXPECT_THROW(dhcid.fromClientId(clientid, empty_wire_fqdn),
+                 isc::dhcp_ddns::DhcidRdataComputeError);
+
+    // Make sure that the empty client identifier is not accepted.
+    clientid.clear();
+    EXPECT_THROW(dhcid.fromClientId(clientid, wire_fqdn_),
+                 isc::dhcp_ddns::DhcidRdataComputeError);
+
+
+}
+
+// This test verifies that DHCID is properly computed from a HW address.
+TEST_F(DhcidTest, fromHWAddr) {
+    D2Dhcid dhcid;
+
+    // Create a buffer holding client id..
+    uint8_t hwaddr_data[] = { 0, 1, 2, 3, 4, 5, 6 };
+    HWAddrPtr hwaddr(new HWAddr(hwaddr_data, sizeof(hwaddr_data),
+                                HTYPE_ETHER));
+
+    // Create DHCID.
+    ASSERT_NO_THROW(dhcid.fromHWAddr(hwaddr, wire_fqdn_));
+
+    // The reference DHCID (represented as string of hexadecimal digits)
+    // has been calculated using one of the online calculators.
+    std::string dhcid_ref = "0000012247F6DC4423C3E8627434A9D686860"
+        "9D88948F78018B215EDCAA30C0C135035";
+
+    // Make sure that the DHCID is valid.
+    EXPECT_EQ(dhcid_ref, dhcid.toStr());
+
+    // Make sure that the empty FQDN is not accepted.
+    std::vector<uint8_t> empty_wire_fqdn;
+    EXPECT_THROW(dhcid.fromHWAddr(hwaddr, empty_wire_fqdn),
+                 isc::dhcp_ddns::DhcidRdataComputeError);
+
+    // Make sure that the NULL HW address is not accepted.
+    hwaddr.reset();
+    EXPECT_THROW(dhcid.fromHWAddr(hwaddr, wire_fqdn_),
+                 isc::dhcp_ddns::DhcidRdataComputeError);
+}
+
 // test operator<< on D2Dhcid
 TEST(NameChangeRequestTest, leftShiftOperation) {
     const D2Dhcid dhcid("010203040A7F8E3D");
@@ -406,7 +473,7 @@ TEST(NameChangeRequestTest, basicJsonTest) {
                             "\"change_type\":1,"
                             "\"forward_change\":true,"
                             "\"reverse_change\":false,"
-                            "\"fqdn\":\"walah.walah.com\","
+                            "\"fqdn\":\"walah.walah.com.\","
                             "\"ip_address\":\"192.168.2.1\","
                             "\"dhcid\":\"010203040A7F8E3D\","
                             "\"lease_expires_on\":\"20130121132405\","
@@ -487,7 +554,7 @@ TEST(NameChangeRequestTest, toFromBufferTest) {
                             "\"change_type\":1,"
                             "\"forward_change\":true,"
                             "\"reverse_change\":false,"
-                            "\"fqdn\":\"walah.walah.com\","
+                            "\"fqdn\":\"walah.walah.com.\","
                             "\"ip_address\":\"192.168.2.1\","
                             "\"dhcid\":\"010203040A7F8E3D\","
                             "\"lease_expires_on\":\"20130121132405\","
@@ -519,6 +586,27 @@ TEST(NameChangeRequestTest, toFromBufferTest) {
     ASSERT_EQ(final_str, msg_str);
 }
 
+/// @brief Tests ip address modification and validation
+TEST(NameChangeRequestTest, ipAddresses) {
+    NameChangeRequest ncr;
+
+    // Verify that a valid IPv4 address works.
+    ASSERT_NO_THROW(ncr.setIpAddress("192.168.1.1"));
+    const asiolink::IOAddress& io_addr4 = ncr.getIpIoAddress();
+    EXPECT_EQ(ncr.getIpAddress(), io_addr4.toText());
+    EXPECT_TRUE(ncr.isV4());
+    EXPECT_FALSE(ncr.isV6());
+
+    // Verify that a valid IPv6 address works.
+    ASSERT_NO_THROW(ncr.setIpAddress("2001:1::f3"));
+    const asiolink::IOAddress& io_addr6 = ncr.getIpIoAddress();
+    EXPECT_EQ(ncr.getIpAddress(), io_addr6.toText());
+    EXPECT_FALSE(ncr.isV4());
+    EXPECT_TRUE(ncr.isV6());
+
+    // Verify that an invalid address fails.
+    ASSERT_THROW(ncr.setIpAddress("x001:1::f3"),NcrMessageError);
+}
 
 } // end of anonymous namespace
 
diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc
index b4fb11d..7789c74 100644
--- a/src/lib/dhcpsrv/cfgmgr.cc
+++ b/src/lib/dhcpsrv/cfgmgr.cc
@@ -350,7 +350,7 @@ CfgMgr::getUnicast(const std::string& iface) const {
 
 CfgMgr::CfgMgr()
     : datadir_(DHCP_DATA_DIR),
-      all_ifaces_active_(false) {
+      all_ifaces_active_(false), echo_v4_client_id_(true) {
     // DHCP_DATA_DIR must be set set with -DDHCP_DATA_DIR="..." in Makefile.am
     // Note: the definition of DHCP_DATA_DIR needs to include quotation marks
     // See AM_CPPFLAGS definition in Makefile.am
diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h
index b063f92..cda59f2 100644
--- a/src/lib/dhcpsrv/cfgmgr.h
+++ b/src/lib/dhcpsrv/cfgmgr.h
@@ -316,6 +316,22 @@ public:
     const isc::asiolink::IOAddress*
     getUnicast(const std::string& iface) const;
 
+    /// @brief Sets whether server should send back client-id in DHCPv4
+    ///
+    /// This is a compatibility flag. The default (true) is compliant with
+    /// RFC6842. False is for backward compatibility.
+    ///
+    /// @param echo should the client-id be sent or not
+    void echoClientId(const bool echo) {
+        echo_v4_client_id_ = echo;
+    }
+
+    /// @brief Returns whether server should send back client-id in DHCPv4.
+    /// @return true if client-id should be returned, false otherwise.
+    bool echoClientId() const {
+        return (echo_v4_client_id_);
+    }
+
 protected:
 
     /// @brief Protected constructor.
@@ -366,7 +382,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_;
@@ -392,6 +408,9 @@ private:
     /// A flag which indicates that server should listen on all available
     /// interfaces.
     bool all_ifaces_active_;
+
+    /// Indicates whether v4 server should send back client-id
+    bool echo_v4_client_id_;
 };
 
 } // namespace isc::dhcp
diff --git a/src/lib/dhcpsrv/dhcp_parsers.cc b/src/lib/dhcpsrv/dhcp_parsers.cc
index 2684e2f..ffd49d3 100644
--- a/src/lib/dhcpsrv/dhcp_parsers.cc
+++ b/src/lib/dhcpsrv/dhcp_parsers.cc
@@ -430,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;
@@ -450,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
@@ -460,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;
@@ -1048,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);
+            }
         }
     }
 
@@ -1078,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/lease.cc b/src/lib/dhcpsrv/lease.cc
index 81a4f87..4ca6a3c 100644
--- a/src/lib/dhcpsrv/lease.cc
+++ b/src/lib/dhcpsrv/lease.cc
@@ -74,6 +74,36 @@ Lease4::Lease4(const Lease4& other)
     }
 }
 
+const std::vector<uint8_t>&
+Lease4::getClientIdVector() const {
+    if(!client_id_) {
+        static std::vector<uint8_t> empty_vec;
+        return (empty_vec);
+    }
+
+    return (client_id_->getClientId());
+}
+
+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) {
@@ -129,6 +159,16 @@ Lease6::Lease6(Type type, const isc::asiolink::IOAddress& addr,
     cltt_ = time(NULL);
 }
 
+const std::vector<uint8_t>&
+Lease6::getDuidVector() const {
+    if (!duid_) {
+        static std::vector<uint8_t> empty_vec;
+        return (empty_vec);
+    }
+
+    return (duid_->getDuid());
+}
+
 std::string
 Lease6::toText() const {
     ostringstream stream;
@@ -175,43 +215,42 @@ Lease4::operator==(const Lease4& other) const {
         return false;
     }
 
-    return (
-        addr_ == other.addr_ &&
-        ext_ == other.ext_ &&
-        hwaddr_ == other.hwaddr_ &&
-        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_
-    );
+    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 (
-        addr_ == other.addr_ &&
-        type_ == other.type_ &&
-        prefixlen_ == other.prefixlen_ &&
-        iaid_ == other.iaid_ &&
-        *duid_ == *other.duid_ &&
-        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 (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
diff --git a/src/lib/dhcpsrv/lease.h b/src/lib/dhcpsrv/lease.h
index 496f38a..8626620 100644
--- a/src/lib/dhcpsrv/lease.h
+++ b/src/lib/dhcpsrv/lease.h
@@ -209,6 +209,31 @@ struct Lease4 : public Lease {
     /// @param other the @c Lease4 object to be copied.
     Lease4(const Lease4& other);
 
+    /// @brief Returns a client identifier.
+    ///
+    /// @warning Since the function returns the reference to a vector (not a
+    /// copy), the returned object should be used with caution because it will
+    /// remain valid only for the period of time when an object which returned
+    /// it exists.
+    ///
+    /// @return A reference to a vector holding client identifier,
+    /// or an empty vector if client identifier is NULL.
+    const std::vector<uint8_t>& getClientIdVector() const;
+
+    /// @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.
@@ -320,6 +345,30 @@ struct Lease6 : public Lease {
         type_(TYPE_NA) {
     }
 
+    /// @brief Returns a reference to a vector representing a DUID.
+    ///
+    /// @warning Since the function returns the reference to a vector (not a
+    /// copy), the returned object should be used with caution because it will
+    /// remain valid only for the period of time when an object which returned
+    /// it exists.
+    ///
+    /// @return A reference to a vector holding a DUID.
+    const std::vector<uint8_t>& getDuidVector() const;
+
+    /// @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
diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h
index a4579b8..9355736 100644
--- a/src/lib/dhcpsrv/lease_mgr.h
+++ b/src/lib/dhcpsrv/lease_mgr.h
@@ -203,6 +203,20 @@ public:
     /// @return lease collection
     virtual Lease4Collection getLease4(const ClientId& clientid) const = 0;
 
+    /// @brief Returns existing IPv4 lease for a given client identifier,
+    /// HW address and subnet identifier.
+    ///
+    /// @todo Consider whether this function is needed or not. In the basic
+    /// DHCPv4 server implementation it is not used by the allocation engine.
+    ///
+    /// @param client_id A client identifier.
+    /// @param hwaddr Hardware address.
+    /// @param subnet_id A subnet identifier.
+    ///
+    /// @return A pointer to the lease or NULL if the lease is not found.
+    virtual Lease4Ptr getLease4(const ClientId& clientid, const HWAddr& hwaddr,
+                                SubnetID subnet_id) const = 0;
+
     /// @brief Returns existing IPv4 lease for specified client-id
     ///
     /// There can be at most one lease for a given HW address in a single
diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.cc b/src/lib/dhcpsrv/memfile_lease_mgr.cc
index 4f73278..dbc3bdd 100644
--- a/src/lib/dhcpsrv/memfile_lease_mgr.cc
+++ b/src/lib/dhcpsrv/memfile_lease_mgr.cc
@@ -80,8 +80,8 @@ Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr) const {
         lease != idx.end(); ++lease) {
 
         // Every Lease4 has a hardware address, so we can compare it
-        if((* lease)->hwaddr_ == hwaddr.hwaddr_) {
-            collection.push_back((* lease));
+        if ((*lease)->hwaddr_ == hwaddr.hwaddr_) {
+            collection.push_back((*lease));
         }
     }
 
@@ -113,9 +113,9 @@ Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
 }
 
 Lease4Collection
-Memfile_LeaseMgr::getLease4(const ClientId& clientid) const {
+Memfile_LeaseMgr::getLease4(const ClientId& client_id) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_MEMFILE_GET_CLIENTID).arg(clientid.toText());
+              DHCPSRV_MEMFILE_GET_CLIENTID).arg(client_id.toText());
     typedef Memfile_LeaseMgr::Lease4Storage::nth_index<0>::type SearchIndex;
     Lease4Collection collection;
     const SearchIndex& idx = storage4_.get<0>();
@@ -124,8 +124,8 @@ Memfile_LeaseMgr::getLease4(const ClientId& clientid) const {
 
         // 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));
+        if((*lease)->client_id_ && *(*lease)->client_id_ == client_id) {
+            collection.push_back((*lease));
         }
     }
 
diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.h b/src/lib/dhcpsrv/memfile_lease_mgr.h
index aa81f00..66548f6 100644
--- a/src/lib/dhcpsrv/memfile_lease_mgr.h
+++ b/src/lib/dhcpsrv/memfile_lease_mgr.h
@@ -16,7 +16,6 @@
 #define MEMFILE_LEASE_MGR_H
 
 #include <dhcp/hwaddr.h>
-#include <dhcpsrv/key_from_key.h>
 #include <dhcpsrv/lease_mgr.h>
 
 #include <boost/multi_index/indexed_by.hpp>
@@ -102,8 +101,8 @@ public:
 
     /// @brief Returns existing IPv4 lease for specified client-id
     ///
-    /// @param clientid client identifier
-    virtual Lease4Collection getLease4(const ClientId& clientid) const;
+    /// @param client_id client identifier
+    virtual Lease4Collection getLease4(const ClientId& client_id) const;
 
     /// @brief Returns IPv4 lease for specified client-id/hwaddr/subnet-id tuple
     ///
@@ -267,20 +266,10 @@ protected:
                 // the lease using three attributes: DUID, IAID, Subnet Id.
                 boost::multi_index::composite_key<
                     Lease6,
-                    // The DUID value can't be directly accessed from the Lease6
-                    // object because it is wrapped with the DUID object (actually
-                    // pointer to this object). Therefore we need to use
-                    // KeyFromKeyExtractor class to extract the DUID value from
-                    // this cascaded structure.
-                    KeyFromKeyExtractor<
-                        // The value of the DUID is accessed by the getDuid() method
-                        // from the DUID object.
-                        boost::multi_index::const_mem_fun<DUID, std::vector<uint8_t>,
-                                                          &DUID::getDuid>,
-                        // The DUID object is stored in the duid_ member of the
-                        // Lease6 object.
-                        boost::multi_index::member<Lease6, DuidPtr, &Lease6::duid_>
-                    >,
+                    // The DUID can be retrieved from the Lease6 object using
+                    // a getDuidVector const function.
+                    boost::multi_index::const_mem_fun<Lease6, const std::vector<uint8_t>&,
+                                                      &Lease6::getDuidVector>,
                     // The two other ingredients of this index are IAID and
                     // subnet id.
                     boost::multi_index::member<Lease6, uint32_t, &Lease6::iaid_>,
@@ -330,20 +319,10 @@ protected:
                 // 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 client id can be retrieved from the Lease4 object by
+                    // calling getClientIdVector const function.
+                    boost::multi_index::const_mem_fun<Lease4, const std::vector<uint8_t>&,
+                                                      &Lease4::getClientIdVector>,
                     // The subnet id is accessed through the subnet_id_ member.
                     boost::multi_index::member<Lease, uint32_t, &Lease::subnet_id_>
                 >
@@ -355,20 +334,10 @@ protected:
                 // 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 client id can be retrieved from the Lease4 object by
+                    // calling getClientIdVector const function.
+                    boost::multi_index::const_mem_fun<Lease4, const std::vector<uint8_t>&,
+                                                      &Lease4::getClientIdVector>,
                     // The hardware address is held in the hwaddr_ member of the
                     // Lease4 object.
                     boost::multi_index::member<Lease4, std::vector<uint8_t>,
diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc
index e695584..623440b 100644
--- a/src/lib/dhcpsrv/mysql_lease_mgr.cc
+++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc
@@ -1622,6 +1622,15 @@ MySqlLeaseMgr::getLease4(const ClientId& clientid) const {
     return (result);
 }
 
+Lease4Ptr
+MySqlLeaseMgr::getLease4(const ClientId&, const HWAddr&, SubnetID) const {
+    /// This function is currently not implemented because allocation engine
+    /// searches for the lease using HW address or client identifier.
+    /// It never uses both parameters in the same time. We need to
+    /// consider if this function is needed at all.
+    isc_throw(NotImplemented, "The MySqlLeaseMgr::getLease4 function was"
+              " called, but it is not implemented");
+}
 
 Lease4Ptr
 MySqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const {
diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.h b/src/lib/dhcpsrv/mysql_lease_mgr.h
index 6fae5e2..ef4dc47 100644
--- a/src/lib/dhcpsrv/mysql_lease_mgr.h
+++ b/src/lib/dhcpsrv/mysql_lease_mgr.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
@@ -220,6 +220,19 @@ public:
     ///        failed.
     virtual Lease4Collection getLease4(const ClientId& clientid) const;
 
+    /// @brief Returns IPv4 lease for the specified client identifier, HW
+    /// address and subnet identifier.
+    ///
+    /// @param client_id A client identifier.
+    /// @param hwaddr Hardware address.
+    /// @param subnet_id A subnet identifier.
+    ///
+    /// @return A pointer to the lease or NULL if the lease is not found.
+    /// @throw isc::NotImplemented On every call as this function is currently
+    /// not implemented for the MySQL backend.
+    virtual Lease4Ptr getLease4(const ClientId& client_id, const HWAddr& hwaddr,
+                                SubnetID subnet_id) const;
+
     /// @brief Returns existing IPv4 lease for specified client-id
     ///
     /// There can be at most one lease for a given HW address in a single
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/subnet.cc b/src/lib/dhcpsrv/subnet.cc
index c1589db..d861afe 100644
--- a/src/lib/dhcpsrv/subnet.cc
+++ b/src/lib/dhcpsrv/subnet.cc
@@ -88,6 +88,38 @@ Subnet::getOptionDescriptor(const std::string& option_space,
     return (*range.first);
 }
 
+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);
diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h
index 6c49f60..31dc947 100644
--- a/src/lib/dhcpsrv/subnet.h
+++ b/src/lib/dhcpsrv/subnet.h
@@ -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
@@ -221,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.
@@ -232,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
@@ -440,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
diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am
index 546ced9..643fd63 100644
--- a/src/lib/dhcpsrv/tests/Makefile.am
+++ b/src/lib/dhcpsrv/tests/Makefile.am
@@ -53,6 +53,7 @@ libdhcpsrv_unittests_SOURCES += alloc_engine_unittest.cc
 libdhcpsrv_unittests_SOURCES += callout_handle_store_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += dbaccess_parser_unittest.cc
+libdhcpsrv_unittests_SOURCES += lease_unittest.cc
 libdhcpsrv_unittests_SOURCES += lease_mgr_factory_unittest.cc
 libdhcpsrv_unittests_SOURCES += lease_mgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += memfile_lease_mgr_unittest.cc
diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
index 94f78c3..08ce768 100644
--- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
@@ -653,6 +653,23 @@ TEST_F(CfgMgrTest, activateAllIfaces) {
     EXPECT_FALSE(cfg_mgr.isActiveIface("eth2"));
 }
 
+// This test verifies that RFC6842 (echo client-id) compatibility may be
+// configured.
+TEST_F(CfgMgrTest, echoClientId) {
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+
+    // Check that the default is true
+    EXPECT_TRUE(cfg_mgr.echoClientId());
+
+    // Check that it can be modified to false
+    cfg_mgr.echoClientId(false);
+    EXPECT_FALSE(cfg_mgr.echoClientId());
+
+    // Check that the default value can be restored
+    cfg_mgr.echoClientId(true);
+    EXPECT_TRUE(cfg_mgr.echoClientId());
+}
+
 /// @todo Add unit-tests for testing:
 /// - addActiveIface() with invalid interface name
 /// - addActiveIface() with the same interface twice
diff --git a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
index 53e304d..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 {
diff --git a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
index 776f593..ab5a8b2 100644
--- a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
@@ -113,6 +113,19 @@ public:
         return (Lease4Collection());
     }
 
+    /// @brief Returns existing IPv4 lease for specified client identifier,
+    /// HW address and subnet identifier.
+    ///
+    /// @param client_id Aclient identifier
+    /// @param hwaddr A HW address.
+    /// @param subnet_id A subnet identifier.
+    ///
+    /// @return A pointer to an existing lease or NULL if lease not found.
+    virtual Lease4Ptr
+    getLease4(const ClientId&, const HWAddr&, SubnetID) const {
+        return (Lease4Ptr());
+    }
+
     /// @brief Returns existing IPv4 lease for specified client-id
     ///
     /// There can be at most one lease for a given HW address in a single
@@ -238,6 +251,17 @@ public:
 
 namespace {
 
+/// Hardware address used by different tests.
+const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
+/// Client id used by different tests.
+const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
+/// Valid lifetime value used by different tests.
+const uint32_t VALID_LIFETIME = 500;
+/// Subnet ID used by different tests.
+const uint32_t SUBNET_ID = 42;
+/// IAID value used by different tests.
+const uint32_t IAID = 7;
+
 /// @brief getParameter test
 ///
 /// This test checks if the LeaseMgr can be instantiated and that it
@@ -415,6 +439,52 @@ TEST(Lease4, operatorAssign) {
     EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_);
 }
 
+// This test verifies that the matches() returns true if two leases differ
+// by values other than address, HW address, Client ID and ext_.
+TEST(Lease4, matches) {
+    // Create two leases which share the same address, HW address, client id
+    // and ext_ value.
+    const time_t current_time = time(NULL);
+    Lease4 lease1(IOAddress("192.0.2.3"), HWADDR, sizeof(HWADDR), CLIENTID,
+                  sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
+                  SUBNET_ID);
+    lease1.hostname_ = "lease1.example.com.";
+    lease1.fqdn_fwd_ = true;
+    lease1.fqdn_rev_ = true;
+    Lease4 lease2(IOAddress("192.0.2.3"), HWADDR, sizeof(HWADDR), CLIENTID,
+                  sizeof(CLIENTID), VALID_LIFETIME + 10, current_time - 10,
+                  100, 200, SUBNET_ID);
+    lease2.hostname_ = "lease2.example.com.";
+    lease2.fqdn_fwd_ = false;
+    lease2.fqdn_rev_ = true;
+
+    // Leases should match.
+    EXPECT_TRUE(lease1.matches(lease2));
+    EXPECT_TRUE(lease2.matches(lease1));
+
+    // Change address, leases should not match anymore.
+    lease1.addr_ = IOAddress("192.0.2.4");
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.addr_ = lease2.addr_;
+
+    // Change HW address, leases should not match.
+    lease1.hwaddr_[1] += 1;
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.hwaddr_ = lease2.hwaddr_;
+
+    // Chanage client id, leases should not match.
+    std::vector<uint8_t> client_id = lease1.client_id_->getClientId();
+    client_id[1] += 1;
+    lease1.client_id_.reset(new ClientId(client_id));
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.client_id_ = lease2.client_id_;
+
+    // Change ext_, leases should not match.
+    lease1.ext_ += 1;
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.ext_ = lease2.ext_;
+}
+
 /// @brief Lease4 Equality Test
 ///
 /// Checks that the operator==() correctly compares two leases for equality.
@@ -641,6 +711,59 @@ TEST(Lease6, Lease6ConstructorWithFQDN) {
                                          subnet_id)), InvalidOperation);
 }
 
+// This test verifies that the matches() function returns true if two leases
+// differ by values other than address, type, prefix length, IAID and DUID.
+TEST(Lease6, matches) {
+
+    // Create two matching leases.
+    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::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::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;
+
+    EXPECT_TRUE(lease1.matches(lease2));
+
+    // Modify each value used to match both leases, and make sure that
+    // leases don't match.
+
+    // Modify address.
+    lease1.addr_ = IOAddress("2001:db8:1::2");
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.addr_ = lease2.addr_;
+
+    // Modify lease type.
+    lease1.type_ = Lease6::TYPE_TA;
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.type_ = lease2.type_;
+
+    // Modify prefix length.
+    lease1.prefixlen_ += 1;
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.prefixlen_ = lease2.prefixlen_;
+
+    // Modify IAID.
+    lease1.iaid_ += 1;
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.iaid_ = lease2.iaid_;
+
+    // Modify DUID.
+    llt[1] += 1;
+    duid.reset(new DUID(llt, sizeof(llt)));
+    lease1.duid_ = duid;
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.duid_ = lease2.duid_;
+}
 
 /// @brief Lease6 Equality Test
 ///
diff --git a/src/lib/dhcpsrv/tests/lease_unittest.cc b/src/lib/dhcpsrv/tests/lease_unittest.cc
new file mode 100644
index 0000000..6f57f30
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/lease_unittest.cc
@@ -0,0 +1,71 @@
+// 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/duid.h>
+#include <dhcpsrv/lease.h>
+#include <gtest/gtest.h>
+#include <vector>
+
+using namespace isc;
+using namespace isc::dhcp;
+
+namespace {
+
+// @todo Currently this file contains tests for new functions which return DUID
+// or client identifier. Other tests for Lease objects must be implemented.
+// See http://bind10.isc.org/ticket/3240.
+
+// Verify that the client id can be returned as a vector object and if client
+// id is NULL the empty vector is returned.
+TEST(Lease4Test, getClientIdVector) {
+    // Create a lease.
+    Lease4 lease;
+    // By default, the lease should have client id set to NULL. If it doesn't,
+    // continuing the test makes no sense.
+    ASSERT_FALSE(lease.client_id_);
+    // When client id is NULL the vector returned should be empty.
+    EXPECT_TRUE(lease.getClientIdVector().empty());
+    // Now, let's set the non NULL client id. Fill it with the 8 bytes, each
+    // holding a value of 0x42.
+    std::vector<uint8_t> client_id_vec(8, 0x42);
+    lease.client_id_ = ClientIdPtr(new ClientId(client_id_vec));
+    // Check that the returned vector, encapsulating client id is equal to
+    // the one that has been used to set the client id for the lease.
+    std::vector<uint8_t> returned_vec = lease.getClientIdVector();
+    EXPECT_TRUE(returned_vec == client_id_vec);
+}
+
+// Verify that the DUID can be returned as a vector object and if DUID is NULL
+// the empty vector is returned.
+TEST(Lease6Test, getDuidVector) {
+    // Create a lease.
+    Lease6 lease;
+    // By default, the lease should have client id set to NULL. If it doesn't,
+    // continuing the test makes no sense.
+    ASSERT_FALSE(lease.duid_);
+    // When client id is NULL the vector returned should be empty.
+    EXPECT_TRUE(lease.getDuidVector().empty());
+    // Now, let's set the non NULL DUID. Fill it with the 8 bytes, each
+    // holding a value of 0x42.
+    std::vector<uint8_t> duid_vec(8, 0x42);
+    lease.duid_ = DuidPtr(new DUID(duid_vec));
+    // Check that the returned vector, encapsulating DUID is equal to
+    // the one that has been used to set the DUID for the lease.
+    std::vector<uint8_t> returned_vec = lease.getDuidVector();
+    EXPECT_TRUE(returned_vec == duid_vec);
+}
+
+
+}; // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
index a995f1a..85015f6 100644
--- a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/memfile_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
@@ -35,7 +35,14 @@ namespace {
 class MemfileLeaseMgrTest : public GenericLeaseMgrTest {
 public:
     MemfileLeaseMgrTest() {
+        const LeaseMgr::ParameterMap pmap;
+        lmptr_ = new Memfile_LeaseMgr(pmap);
     }
+
+    virtual ~MemfileLeaseMgrTest() {
+        delete lmptr_;
+    }
+
 };
 
 // This test checks if the LeaseMgr can be instantiated and that it
@@ -141,79 +148,22 @@ TEST_F(MemfileLeaseMgrTest, addGetDelete6) {
 
 // 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());
+    testGetLease4ClientId();
 }
 
 // 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());
+    testGetLease4NullClientId();
 }
 
 // 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());
+    testGetLease4HWAddr();
 }
 
 // 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());
+    testGetLease4ClientIdHWAddrSubnetId();
 }
 
 }; // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/subnet_unittest.cc b/src/lib/dhcpsrv/tests/subnet_unittest.cc
index 60142d4..d0dd57a 100644
--- a/src/lib/dhcpsrv/tests/subnet_unittest.cc
+++ b/src/lib/dhcpsrv/tests/subnet_unittest.cc
@@ -615,6 +615,76 @@ 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));
diff --git a/src/lib/dhcpsrv/tests/test_utils.cc b/src/lib/dhcpsrv/tests/test_utils.cc
index 9b4263f..e418c62 100644
--- a/src/lib/dhcpsrv/tests/test_utils.cc
+++ b/src/lib/dhcpsrv/tests/test_utils.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
@@ -98,9 +98,8 @@ detailCompareLease(const Lease6Ptr& first, const Lease6Ptr& second) {
     EXPECT_EQ(first->hostname_, second->hostname_);
 }
 
-
 GenericLeaseMgrTest::GenericLeaseMgrTest()
-    :lmptr_(NULL) {
+    : lmptr_(NULL) {
     // Initialize address strings and IOAddresses
     for (int i = 0; ADDRESS4[i] != NULL; ++i) {
         string addr(ADDRESS4[i]);
@@ -121,6 +120,11 @@ GenericLeaseMgrTest::GenericLeaseMgrTest()
     }
 }
 
+GenericLeaseMgrTest::~GenericLeaseMgrTest() {
+    // Does nothing. The derived classes are expected to clean up, i.e.
+    // remove the lmptr_ pointer.
+}
+
 /// @brief Initialize Lease4 Fields
 ///
 /// Returns a pointer to a Lease4 structure.  Different values are put into
@@ -217,7 +221,7 @@ GenericLeaseMgrTest::initializeLease4(std::string address) {
         lease->fqdn_rev_ = false;
         lease->fqdn_fwd_ = false;
         lease->hostname_ = "otherhost.example.com.";
-        } else if (address == straddress4_[6]) {
+    } else if (address == straddress4_[6]) {
         lease->hwaddr_ = vector<uint8_t>(6, 0x6e);
         // Same ClientId as straddress4_1
         lease->client_id_ = ClientIdPtr(
@@ -469,6 +473,111 @@ GenericLeaseMgrTest::createLeases6() {
     return (leases);
 }
 
+void
+GenericLeaseMgrTest::testGetLease4ClientId() {
+    // Let's initialize a specific lease ...
+    Lease4Ptr lease = initializeLease4(straddress4_[1]);
+    EXPECT_TRUE(lmptr_->addLease(lease));
+    Lease4Collection returned = lmptr_->getLease4(*lease->client_id_);
+
+    ASSERT_EQ(1, returned.size());
+    // We should retrieve our lease...
+    detailCompareLease(lease, *returned.begin());
+    lease = initializeLease4(straddress4_[2]);
+    returned = lmptr_->getLease4(*lease->client_id_);
+
+    ASSERT_EQ(0, returned.size());
+}
+
+void
+GenericLeaseMgrTest::testGetLease4NullClientId() {
+    // Let's initialize a specific lease ... But this time
+    // We keep its client id for further lookup and
+    // We clearly 'reset' it ...
+    Lease4Ptr leaseA = initializeLease4(straddress4_[4]);
+    ClientIdPtr client_id = leaseA->client_id_;
+    leaseA->client_id_ = ClientIdPtr();
+    ASSERT_TRUE(lmptr_->addLease(leaseA));
+
+    Lease4Collection returned = lmptr_->getLease4(*client_id);
+    // Shouldn't have our previous lease ...
+    ASSERT_TRUE(returned.empty());
+
+    // Add another lease with the non-NULL client id, and make sure that the
+    // lookup will not break due to existence of both leases with non-NULL and
+    // NULL client ids.
+    Lease4Ptr leaseB = initializeLease4(straddress4_[0]);
+    // Shouldn't throw any null pointer exception
+    ASSERT_TRUE(lmptr_->addLease(leaseB));
+    // Try to get the lease.
+    returned = lmptr_->getLease4(*client_id);
+    ASSERT_TRUE(returned.empty());
+
+    // Let's make it more interesting and add another lease with NULL client id.
+    Lease4Ptr leaseC = initializeLease4(straddress4_[5]);
+    leaseC->client_id_.reset();
+    ASSERT_TRUE(lmptr_->addLease(leaseC));
+    returned = lmptr_->getLease4(*client_id);
+    ASSERT_TRUE(returned.empty());
+
+    // But getting the lease with non-NULL client id should be successful.
+    returned = lmptr_->getLease4(*leaseB->client_id_);
+    ASSERT_EQ(1, returned.size());
+}
+
+void
+GenericLeaseMgrTest::testGetLease4HWAddr() {
+    // 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(lmptr_->addLease(leaseA));
+
+    // we should not have a lease, with this MAC Addr
+    Lease4Collection returned = lmptr_->getLease4(hwaddrB);
+    ASSERT_EQ(0, returned.size());
+
+    // But with this one
+    returned = lmptr_->getLease4(hwaddrA);
+    ASSERT_EQ(1, returned.size());
+}
+
+void
+GenericLeaseMgrTest::testGetLease4ClientIdHWAddrSubnetId() {
+    Lease4Ptr leaseA = initializeLease4(straddress4_[4]);
+    Lease4Ptr leaseB = initializeLease4(straddress4_[5]);
+    Lease4Ptr leaseC = initializeLease4(straddress4_[6]);
+    // Set NULL client id for one of the leases. This is to make sure that such
+    // a lease may coexist with other leases with non NULL client id.
+    leaseC->client_id_.reset();
+
+    HWAddr hwaddrA(leaseA->hwaddr_, HTYPE_ETHER);
+    HWAddr hwaddrB(leaseB->hwaddr_, HTYPE_ETHER);
+    HWAddr hwaddrC(leaseC->hwaddr_, HTYPE_ETHER);
+    EXPECT_TRUE(lmptr_->addLease(leaseA));
+    EXPECT_TRUE(lmptr_->addLease(leaseB));
+    EXPECT_TRUE(lmptr_->addLease(leaseC));
+    // First case we should retrieve our lease
+    Lease4Ptr lease = lmptr_->getLease4(*leaseA->client_id_, hwaddrA, leaseA->subnet_id_);
+    detailCompareLease(lease, leaseA);
+    // Retrieve the other lease.
+    lease = lmptr_->getLease4(*leaseB->client_id_, hwaddrB, leaseB->subnet_id_);
+    detailCompareLease(lease, leaseB);
+    // The last lease has NULL client id so we will use a different getLease4 function
+    // which doesn't require client id (just a hwaddr and subnet id).
+    lease = lmptr_->getLease4(hwaddrC, leaseC->subnet_id_);
+    detailCompareLease(lease, leaseC);
+
+    // An attempt to retrieve the lease with non matching lease parameters should
+    // result in NULL pointer being returned.
+    lease = lmptr_->getLease4(*leaseA->client_id_, hwaddrB, leaseA->subnet_id_);
+    EXPECT_FALSE(lease);
+    lease = lmptr_->getLease4(*leaseA->client_id_, hwaddrA, leaseB->subnet_id_);
+    EXPECT_FALSE(lease);
+}
+
+
 };
 };
 };
diff --git a/src/lib/dhcpsrv/tests/test_utils.h b/src/lib/dhcpsrv/tests/test_utils.h
index af10a31..6a86724 100644
--- a/src/lib/dhcpsrv/tests/test_utils.h
+++ b/src/lib/dhcpsrv/tests/test_utils.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,8 +49,13 @@ detailCompareLease(const Lease4Ptr& first, const Lease4Ptr& second);
 /// All concrete LeaseMgr test classes should be derived from it.
 class GenericLeaseMgrTest : public ::testing::Test {
 public:
+
+    /// @brief Default constructor.
     GenericLeaseMgrTest();
 
+    /// @brief Virtual destructor.
+    virtual ~GenericLeaseMgrTest();
+
     /// @brief Initialize Lease4 Fields
     ///
     /// Returns a pointer to a Lease4 structure.  Different values are put into
@@ -85,7 +90,7 @@ public:
     ///
     /// @param leases Vector of pointers to leases
     template <typename T>
-        void checkLeasesDifferent(const std::vector<T>& leases) const;
+    void checkLeasesDifferent(const std::vector<T>& leases) const;
 
     /// @brief Creates leases for the test
     ///
@@ -101,6 +106,18 @@ public:
     /// @return vector<Lease6Ptr> Vector of pointers to leases
     std::vector<Lease6Ptr> createLeases6();
 
+    /// @brief Test lease retrieval using client id.
+    void testGetLease4ClientId();
+
+    /// @brief Test lease retrieval when leases with NULL client id are present.
+    void testGetLease4NullClientId();
+
+    /// @brief Test lease retrieval using HW address.
+    void testGetLease4HWAddr();
+
+    /// @brief Test lease retrieval using client id, HW address and subnet id.
+    void testGetLease4ClientIdHWAddrSubnetId();
+
     // Member variables
     std::vector<std::string>  straddress4_;   ///< String forms of IPv4 addresses
     std::vector<isc::asiolink::IOAddress> ioaddress4_;  ///< IOAddress forms of IPv4 addresses
@@ -108,7 +125,7 @@ public:
     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
+    LeaseMgr* lmptr_;                     ///< Pointer to the lease manager
 };
 
 };
diff --git a/src/lib/dns/masterload.cc b/src/lib/dns/masterload.cc
index 53a2b8d..f583c4f 100644
--- a/src/lib/dns/masterload.cc
+++ b/src/lib/dns/masterload.cc
@@ -19,11 +19,13 @@
 #include <cctype>
 #include <cerrno>
 
+#include <boost/bind.hpp>
 #include <boost/scoped_ptr.hpp>
 
 #include <exceptions/exceptions.h>
 
 #include <dns/masterload.h>
+#include <dns/master_loader.h>
 #include <dns/name.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
@@ -31,6 +33,7 @@
 #include <dns/rrset.h>
 #include <dns/rrttl.h>
 #include <dns/rrtype.h>
+#include <dns/rrcollator.h>
 
 using namespace std;
 using namespace boost;
@@ -39,25 +42,45 @@ using namespace isc::dns::rdata;
 namespace isc {
 namespace dns {
 namespace {
-// A helper function that strips off any comment or whitespace at the end of
-// an RR.
-// This is an incomplete implementation, and cannot handle all such comments;
-// it's considered a short term workaround to deal with some real world
-// cases.
-string
-stripLine(string& s, const Exception& ex) {
-    // Find any ';' in the text data, and locate the position of the last
-    // occurrence.  Note that unless/until we support empty RDATA it
-    // shouldn't be placed at the beginning of the data.
-    const size_t pos_semicolon = s.rfind(';');
-    if (pos_semicolon == 0) {
-        throw ex;
-    } else if (pos_semicolon != string::npos) {
-        s.resize(pos_semicolon);
+void
+callbackWrapper(const RRsetPtr& rrset, MasterLoadCallback callback,
+                const Name* origin)
+{
+    // Origin related validation:
+    //  - reject out-of-zone data
+    //  - reject SOA whose owner is not at the top of zone
+    const NameComparisonResult cmp_result =
+        rrset->getName().compare(*origin);
+    if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
+        cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
+        isc_throw(MasterLoadError, "Out-of-zone data for " << *origin
+                  << "/" << rrset->getClass() << ": " << rrset->getName());
+    }
+    if (rrset->getType() == RRType::SOA() &&
+        cmp_result.getRelation() != NameComparisonResult::EQUAL) {
+        isc_throw(MasterLoadError, "SOA not at top of zone: "
+                  << *rrset);
+    }
+
+    callback(rrset);
+}
+
+template <typename InputType>
+void
+loadHelper(InputType input, const Name& origin,
+           const RRClass& zone_class, MasterLoadCallback callback)
+{
+    RRCollator rr_collator(boost::bind(callbackWrapper, _1,
+                                       callback, &origin));
+    MasterLoader loader(input, origin, zone_class,
+                        MasterLoaderCallbacks::getNullCallbacks(),
+                        rr_collator.getCallback());
+    try {
+        loader.load();
+    } catch (const MasterLoaderError& ex) {
+        isc_throw(MasterLoadError, ex.what());
     }
-    // Remove any trailing whitespace return the resulting text.
-    s.resize(s.find_last_not_of(" \t") + 1);
-    return (s);
+    rr_collator.flush();
 }
 }
 
@@ -69,154 +92,14 @@ masterLoad(const char* const filename, const Name& origin,
         isc_throw(MasterLoadError, "Name of master file must not be null");
     }
 
-    ifstream ifs;
-    ifs.open(filename, ios_base::in);
-    if (ifs.fail()) {
-        isc_throw(MasterLoadError, "Failed to open master file: " <<
-                  filename << ": " << strerror(errno));
-    }
-    masterLoad(ifs, origin, zone_class, callback, filename);
-    ifs.close();
+    loadHelper<const char*>(filename, origin, zone_class, callback);
 }
 
 void
 masterLoad(istream& input, const Name& origin, const RRClass& zone_class,
-           MasterLoadCallback callback, const char* source)
+           MasterLoadCallback callback, const char*)
 {
-    RRsetPtr rrset;
-    ConstRdataPtr prev_rdata;   // placeholder for special case of RRSIGs
-    string line;
-    unsigned int line_count = 1;
-
-    if (source == NULL) {
-        source = "<unknown>";
-    }
-
-    do {
-        getline(input, line);
-        if (input.bad() || (input.fail() && !input.eof())) {
-            isc_throw(MasterLoadError, "Unexpectedly failed to read a line in "
-                                       << source);
-        }
-
-        // blank/comment lines should be simply skipped.
-        if (line.empty() || line[0] == ';') {
-            continue;
-        }
-
-        // The line shouldn't have leading space (which means omitting the
-        // owner name).
-        if (isspace(line[0])) {
-            isc_throw(MasterLoadError, "Leading space in " << source
-                                       << " at line " << line_count);
-        }
-
-        // Parse a single RR
-        istringstream iss(line);
-        string owner_txt, ttl_txt, rrclass_txt, rrtype_txt;
-        stringbuf rdatabuf;
-        iss >> owner_txt >> ttl_txt >> rrclass_txt >> rrtype_txt >> &rdatabuf;
-        if (iss.bad() || iss.fail()) {
-            isc_throw(MasterLoadError, "Parse failure for a valid RR in "
-                      << source << " at line " << line_count);
-        }
-
-        // This simple version doesn't support relative owner names with a
-        // separate origin.
-        if (owner_txt.empty() || *(owner_txt.end() - 1) != '.') {
-            isc_throw(MasterLoadError, "Owner name is not absolute in "
-                      << source << " at line " << line_count);
-        }
-
-        // XXX: this part is a bit tricky (and less efficient).  We are going
-        // to validate the text for the RR parameters, and throw an exception
-        // if any of them is invalid by converting an underlying exception
-        // to MasterLoadError.  To do that, we need to define the corresponding
-        // variables used for RRset construction outside the try-catch block,
-        // but we don't like to use a temporary variable with a meaningless
-        // initial value.  So we define pointers outside the try block
-        // and allocate/initialize the actual objects within the block.
-        // To make it exception safe we use Boost.scoped_ptr.
-        scoped_ptr<const Name> owner;
-        scoped_ptr<const RRTTL> ttl;
-        scoped_ptr<const RRClass> rrclass;
-        scoped_ptr<const RRType> rrtype;
-        ConstRdataPtr rdata;
-        try {
-            owner.reset(new Name(owner_txt));
-            ttl.reset(new RRTTL(ttl_txt));
-            rrclass.reset(new RRClass(rrclass_txt));
-            rrtype.reset(new RRType(rrtype_txt));
-            string rdtext = rdatabuf.str();
-            try {
-                rdata = createRdata(*rrtype, *rrclass, rdtext);
-            } catch (const Exception& ex) {
-                // If the parse for the RDATA fails, check if it has comments
-                // or whitespace at the end, and if so, retry the conversion
-                // after stripping off the comment or whitespace
-                rdata = createRdata(*rrtype, *rrclass, stripLine(rdtext, ex));
-            }
-        } catch (const Exception& ex) {
-            isc_throw(MasterLoadError, "Invalid RR text in " << source <<
-                                       " at line " << line_count << ": "
-                                       << ex.what());
-        }
-
-        // Origin related validation:
-        //  - reject out-of-zone data
-        //  - reject SOA whose owner is not at the top of zone
-        const NameComparisonResult cmp_result = owner->compare(origin);
-        if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
-            cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
-            isc_throw(MasterLoadError, "Out-of-zone data for " << origin
-                                       << "/" << rrclass_txt << " in "
-                                       << source << " at line "
-                                       << line_count);
-        }
-        if (*rrtype == RRType::SOA() &&
-            cmp_result.getRelation() != NameComparisonResult::EQUAL) {
-            isc_throw(MasterLoadError, "SOA not at top of zone in "
-                      << source << " at line " << line_count);
-        }
-
-        // Reject RR class mismatching
-        if (*rrclass != zone_class) {
-            isc_throw(MasterLoadError, "RR class (" << rrclass_txt
-                      << ") does not match the zone class (" << zone_class
-                      << ") in " << source << " at line " << line_count);
-        }
-
-        // Everything is okay.  Now create/update RRset with the new RR.
-        // If this is the first RR or the RR type/name is new, we are seeing
-        // a new RRset.
-        bool new_rrset = false;
-        if (!rrset || rrset->getType() != *rrtype ||
-            rrset->getName() != *owner) {
-            new_rrset = true;
-        } else if (rrset->getType() == RRType::RRSIG()) {
-            // We are seeing two consecutive RRSIGs of the same name.
-            // They can be combined iff they have the same type covered.
-            if (dynamic_cast<const generic::RRSIG&>(*rdata).typeCovered() !=
-                dynamic_cast<const generic::RRSIG&>(*prev_rdata).typeCovered())
-            {
-                new_rrset = true;
-            }
-        }
-        if (new_rrset) {
-            // Commit the previous RRset, if any.
-            if (rrset) {
-                callback(rrset);
-            }
-            rrset = RRsetPtr(new RRset(*owner, *rrclass, *rrtype, *ttl));
-        }
-        rrset->addRdata(rdata);
-        prev_rdata = rdata;
-    } while (++line_count, !input.eof());
-
-    // Commit the last RRset, if any.
-    if (rrset) {
-        callback(rrset);
-    }
+    loadHelper<istream&>(input, origin, zone_class, callback);
 }
 
 } // namespace dns
diff --git a/src/lib/dns/masterload.h b/src/lib/dns/masterload.h
index 44b3d62..8ff45e0 100644
--- a/src/lib/dns/masterload.h
+++ b/src/lib/dns/masterload.h
@@ -64,86 +64,22 @@ typedef boost::function<void(RRsetPtr)> MasterLoadCallback;
 /// and this function never uses it once it is called.
 /// The callback can freely modify the passed \c RRset.
 ///
-/// This function performs minimum level of validation on the input:
-/// - Each RR is a valid textual representation per the DNS protocol.
-/// - The class of each RR must be identical to the specified RR class.
-/// - The owner name of each RR must be a subdomain of the origin name
-///   (that can be equal to the origin).
-/// - If an SOA RR is included, its owner name must be the origin name.
-/// If any of these validation checks fails, this function throws an
-/// exception of class \c MasterLoadError.
+/// This function internally uses the MasterLoader class, and basically
+/// accepts and rejects input that MasterLoader accepts and rejects,
+/// accordingly.  In addition, this function performs the following validation:
+/// if an SOA RR is included, its owner name must be the origin name.
 ///
 /// It does not perform other semantical checks, however.  For example,
 /// it doesn't check if an NS RR of the origin name is included or if
 /// there is more than one SOA RR.  Such further checks are the caller's
 /// (or the callback's) responsibility.
 ///
-/// <b>Acceptable Format</b>
-///
-/// The current implementation only supports a restricted form of master files
-/// for simplicity.  One easy way to ensure that a handwritten zone file is
-/// acceptable to this implementation is to preprocess it with BIND 9's
-/// named-compilezone tool with both the input and output formats being
-/// "text".
-/// Here is an example:
-/// \code % named-compilezone -f text -F text -o example.com.norm
-///      example.com example.com.zone
-/// \endcode
-/// where example.com.zone is the original zone file for the "example.com"
-/// zone.  The output file is example.com.norm, which should be acceptable
-/// by this implementation.
-///
-/// Below are specific restrictions that this implementation assumes.
-/// Basically, each RR must consist of exactly one line
-/// (so there shouldn't be a multi-line RR) in the following format:
-/// \code  <owner name> <TTL> <RRCLASS> <RRTYPE> <RDATA (single line)>
-/// \endcode
-/// Here are some more details about the restrictions:
-/// - No special directives such as $TTL are supported.
-/// - The owner name must be absolute, that is, it must end with a period.
-/// - "@" is not recognized as a valid owner name.
-/// - Owner names, TTL and RRCLASS cannot be omitted.
-/// - As a corollary, a non blank line must not begin with a space character.
-/// - The order of the RR parameters is fixed, for example, this is acceptable:
-/// \code example.com. 3600 IN A 192.0.2.1
-/// \endcode
-///  but this is not even though it's valid per RFC1035:
-/// \code example.com. IN 3600 A 192.0.2.1
-/// \endcode
-/// - "TTL", "RRCLASS", and "RRTYPE" must be recognizable by the \c RRTTL,
-///   RRClass and RRType class implementations of this library.  In particular,
-///   as of this writing TTL must be a decimal number (a convenient extension
-///   such as "1H" instead of 3600 cannot be used).  Not all standard RR
-///   classes and RR types are supported yet, so the mnemonics of them will
-///   be rejected, too.
-/// - RR TTLs of the same RRset must be the same; even if they are different,
-///   this implementation simply uses the TTL of the first RR.
-///
-/// Blank lines and lines beginning with a semi-colon are allowed, and will
-/// be simply ignored.  Comments cannot coexist with an RR line, however.
-/// For example, this will be rejected:
-/// \code example.com. 3600 IN A 192.0.2.1 ; this is a comment
-/// \endcode
-///
-/// This implementation assumes that RRs of a single RRset are not
-/// interleaved with RRs of a different RRset.
-/// That is, the following sequence shouldn't happen:
-/// \code example.com. 3600 IN A 192.0.2.1
-/// example.com. 3600 IN AAAA 2001:db8::1
-/// example.com. 3600 IN A 192.0.2.2
-/// \endcode
-/// But it does not consider this an error; it will simply regard each RR
-/// as a separate RRset and call the callback with them separately.
-/// It is up to the callback to merge multiple RRsets into one if possible
-/// and necessary.
-///
 /// <b>Exceptions</b>
 ///
 /// This function throws an exception of class \c MasterLoadError in the
 /// following cases:
-/// - Any of the validation checks fails (see the class description).
-/// - The input data is not in the acceptable format (see the details of
-///   the format above).
+/// - Any of the validation checks fails (see above).
+/// - The input data has a syntax error.
 /// - The specified file cannot be opened for loading.
 /// - An I/O error occurs during the loading.
 ///
@@ -196,16 +132,12 @@ typedef boost::function<void(RRsetPtr)> MasterLoadCallback;
 /// The current implementation is in a preliminary level and needs further
 /// extensions.  Some design decisions may also have to be reconsidered as
 /// we gain experiences.  Those include:
-/// - We should be more flexible about the input format.
 /// - We may want to allow optional conditions.  For example, we may want to
 ///   be generous about some validation failures and be able to continue
 ///   parsing.
 /// - Especially if we allow to be generous, we may also want to support
 ///   returning an error code instead of throwing an exception when we
 ///   encounter validation failure.
-/// - We may want to support incremental loading.
-/// - If we add these optional features we may want to introduce a class
-///   that encapsulates loading status and options.
 /// - RRSIGs are handled as separate RRsets, i.e. they are not included in
 ///   the RRset they cover.
 ///
@@ -227,14 +159,16 @@ void masterLoad(const char* const filename, const Name& origin,
 /// All descriptions of the other version apply to this version except those
 /// specific to file I/O.
 ///
+/// Note: The 'source' parameter is now ignored, but it was only used in
+/// exception messages on some error.  So the compatibility effect should be
+/// minimal.
+///
 /// \param input An input stream object that is to emit zone's RRs.
 /// \param origin The origin name of the zone.
 /// \param zone_class The RR class of the zone.
 /// \param callback A callback functor or function that is to be called for
 /// each RRset.
-/// \param source A string to use in error messages if zone content is bad
-/// (e.g. the file name when reading from a file). If this value is NULL,
-/// or left out, the error will use the string '<unknown>'
+/// \param source This parameter is now ignored but left for compatibility.
 void masterLoad(std::istream& input, const Name& origin,
                 const RRClass& zone_class, MasterLoadCallback callback,
                 const char* source = NULL);
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/tests/masterload_unittest.cc b/src/lib/dns/tests/masterload_unittest.cc
index dfd901a..51c999d 100644
--- a/src/lib/dns/tests/masterload_unittest.cc
+++ b/src/lib/dns/tests/masterload_unittest.cc
@@ -267,6 +267,15 @@ TEST_F(MasterLoadTest, loadRRNoComment) {
                                       "\"aaa;bbb\"")));
 }
 
+TEST_F(MasterLoadTest, nonAbsoluteOwner) {
+    // If the owner name is not absolute, the zone origin is assumed to be
+    // its origin.
+    rr_stream << "example.com 3600 IN A 192.0.2.1";
+    masterLoad(rr_stream, origin, zclass, callback);
+    EXPECT_EQ(1, results.size());
+    EXPECT_EQ(results[0]->getName(), Name("example.com.example.com"));
+}
+
 TEST_F(MasterLoadTest, loadRREmptyAndComment) {
     // There's no RDATA (invalid in this case) but a comment.  This position
     // shouldn't cause any disruption and should be treated as a normal error.
@@ -356,11 +365,6 @@ TEST_F(MasterLoadTest, loadBadRRText) {
     stringstream rr_stream6("example.com. 3600 IN A");
     EXPECT_THROW(masterLoad(rr_stream6, origin, zclass, callback),
                  MasterLoadError);
-
-    // owner name is not absolute
-    stringstream rr_stream7("example.com 3600 IN A 192.0.2.1");
-    EXPECT_THROW(masterLoad(rr_stream7, origin, zclass, callback),
-                 MasterLoadError);
 }
 
 // This is a helper callback to test the case the input stream becomes bad
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/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py
index 495d20b..e2b1357 100644
--- a/src/lib/python/isc/config/config_data.py
+++ b/src/lib/python/isc/config/config_data.py
@@ -24,6 +24,7 @@ import isc.cc.data
 import isc.config.module_spec
 import ast
 import copy
+import sys
 
 class ConfigDataError(Exception): pass
 
@@ -62,10 +63,16 @@ def check_type(spec_part, value):
     else:
         raise isc.cc.data.DataTypeError(str("Incorrect specification part for type checking"))
 
-    if data_type == "integer" and type(value) != int:
-        raise isc.cc.data.DataTypeError(str(value) + " is not an integer")
-    elif data_type == "real" and type(value) != float:
-        raise isc.cc.data.DataTypeError(str(value) + " is not a real")
+    if data_type == "integer":
+        if type(value) != int:
+            raise isc.cc.data.DataTypeError(str(value) + " is not an integer")
+        if value > sys.maxsize:
+            raise isc.cc.data.DataTypeError(str(value) + " is too large an integer")
+    elif data_type == "real":
+        if type(value) != float:
+            raise isc.cc.data.DataTypeError(str(value) + " is not a real")
+        if float(value) > sys.float_info.max:
+            raise isc.cc.data.DataTypeError(str(value) + " is too large a float")
     elif data_type == "boolean" and type(value) != bool:
         raise isc.cc.data.DataTypeError(str(value) + " is not a boolean")
     elif data_type == "string" and type(value) != str:
diff --git a/src/lib/python/isc/config/tests/config_data_test.py b/src/lib/python/isc/config/tests/config_data_test.py
index ddeabb6..83f22c9 100644
--- a/src/lib/python/isc/config/tests/config_data_test.py
+++ b/src/lib/python/isc/config/tests/config_data_test.py
@@ -47,6 +47,7 @@ class TestConfigData(unittest.TestCase):
         self.assertRaises(isc.cc.data.DataTypeError, check_type, spec_part, "a")
         self.assertRaises(isc.cc.data.DataTypeError, check_type, spec_part, [ 1, 2 ])
         self.assertRaises(isc.cc.data.DataTypeError, check_type, spec_part, { "a": 1 })
+        self.assertRaises(isc.cc.data.DataTypeError, check_type, spec_part, 10000000000000000000000)
 
         spec_part = find_spec_part(config_spec, "value2")
         check_type(spec_part, 1.1)
@@ -55,6 +56,7 @@ class TestConfigData(unittest.TestCase):
         self.assertRaises(isc.cc.data.DataTypeError, check_type, spec_part, "a")
         self.assertRaises(isc.cc.data.DataTypeError, check_type, spec_part, [ 1, 2 ])
         self.assertRaises(isc.cc.data.DataTypeError, check_type, spec_part, { "a": 1 })
+        self.assertRaises(isc.cc.data.DataTypeError, check_type, spec_part, 2.0000000e+308)
 
         spec_part = find_spec_part(config_spec, "value3")
         check_type(spec_part, True)
diff --git a/src/lib/resolve/Makefile.am b/src/lib/resolve/Makefile.am
index eae202e..13e37e1 100644
--- a/src/lib/resolve/Makefile.am
+++ b/src/lib/resolve/Makefile.am
@@ -46,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/resolve/tests/recursive_query_unittest_3.cc b/src/lib/resolve/tests/recursive_query_unittest_3.cc
index df48740..7803b88 100644
--- a/src/lib/resolve/tests/recursive_query_unittest_3.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest_3.cc
@@ -218,7 +218,8 @@ public:
     /// \param ec ASIO error code, completion code of asynchronous I/O issued
     ///        by the "server" to receive data.
     /// \param length Amount of data received.
-    void udpReceiveHandler(error_code ec = error_code(), size_t length = 0) {
+    void udpReceiveHandler(asio::error_code ec = asio::error_code(),
+                           size_t length = 0) {
         // Expected state should be one greater than the last state.
         EXPECT_EQ(static_cast<int>(expected_), static_cast<int>(last_) + 1);
         last_ = expected_;
@@ -281,7 +282,8 @@ public:
     ///
     /// \param ec Completion error code of the send.
     /// \param length Actual number of bytes sent.
-    void udpSendHandler(error_code ec = error_code(), size_t length = 0) {
+    void udpSendHandler(asio::error_code ec = asio::error_code(),
+                        size_t length = 0) {
         // Check send was OK
         EXPECT_EQ(0, ec.value());
         EXPECT_EQ(udp_length_, length);
@@ -301,7 +303,8 @@ public:
     ///
     /// \param socket Socket on which data will be received
     /// \param ec Boost error code, value should be zero.
-    void tcpAcceptHandler(error_code ec = error_code(), size_t length = 0) {
+    void tcpAcceptHandler(asio::error_code ec = asio::error_code(),
+                          size_t length = 0) {
         // Expect that the accept completed without a problem.
         EXPECT_EQ(0, ec.value());
 
@@ -323,7 +326,8 @@ public:
     /// \param ec ASIO error code, completion code of asynchronous I/O issued
     ///        by the "server" to receive data.
     /// \param length Amount of data received.
-    void tcpReceiveHandler(error_code ec = error_code(), size_t length = 0) {
+    void tcpReceiveHandler(asio::error_code ec = asio::error_code(),
+                           size_t length = 0) {
         // Expect that the receive completed without a problem.
         EXPECT_EQ(0, ec.value());
 
@@ -409,7 +413,7 @@ public:
     /// \param ec Boost error code, value should be zero.
     /// \param length Number of bytes sent.
     void tcpSendHandler(size_t expected_length = 0,
-                        error_code ec = error_code(),
+                        asio::error_code ec = asio::error_code(),
                         size_t length = 0)
     {
         EXPECT_EQ(0, ec.value());       // Expect no error
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/util/threads/tests/condvar_unittest.cc b/src/lib/util/threads/tests/condvar_unittest.cc
index 3bb7230..f804391 100644
--- a/src/lib/util/threads/tests/condvar_unittest.cc
+++ b/src/lib/util/threads/tests/condvar_unittest.cc
@@ -142,8 +142,13 @@ signalAndWait(CondVar* condvar, Mutex* mutex) {
     condvar->wait(*mutex);
 }
 
-#ifndef HAS_UNDEFINED_PTHREAD_BEHAVIOR
-TEST_F(CondVarTest, destroyWhileWait) {
+TEST_F(CondVarTest,
+#ifdef HAS_UNDEFINED_PTHREAD_BEHAVIOR
+       DISABLED_destroyWhileWait
+#else
+       destroyWhileWait
+#endif
+) {
     // We'll destroy a CondVar object while the thread is still waiting
     // on it.  This will trigger an assertion failure.
     if (!isc::util::unittests::runningOnValgrind()) {
@@ -155,7 +160,6 @@ TEST_F(CondVarTest, destroyWhileWait) {
             }, "");
     }
 }
-#endif // !HAS_UNDEFINED_PTHREAD_BEHAVIOR
 
 #ifdef ENABLE_DEBUG
 
diff --git a/src/lib/util/threads/tests/lock_unittest.cc b/src/lib/util/threads/tests/lock_unittest.cc
index c17999e..e72b92d 100644
--- a/src/lib/util/threads/tests/lock_unittest.cc
+++ b/src/lib/util/threads/tests/lock_unittest.cc
@@ -86,9 +86,14 @@ TEST(MutexTest, lockNonBlocking) {
 
 #endif // ENABLE_DEBUG
 
-#ifndef HAS_UNDEFINED_PTHREAD_BEHAVIOR
 // Destroying a locked mutex is a bad idea as well
-TEST(MutexTest, destroyLocked) {
+TEST(MutexTest,
+#ifdef HAS_UNDEFINED_PTHREAD_BEHAVIOR
+     DISABLED_destroyLocked
+#else
+     destroyLocked
+#endif
+) {
     if (!isc::util::unittests::runningOnValgrind()) {
         EXPECT_DEATH_IF_SUPPORTED({
             Mutex* mutex = new Mutex;
@@ -99,7 +104,6 @@ TEST(MutexTest, destroyLocked) {
         }, "");
     }
 }
-#endif // !HAS_UNDEFINED_PTHREAD_BEHAVIOR
 
 // In this test, we try to check if a mutex really locks. We could try that
 // with a deadlock, but that's not practical (the test would not end).
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 4822552..2f43dfd 100644
--- a/tests/lettuce/features/xfrin_notify_handling.feature
+++ b/tests/lettuce/features/xfrin_notify_handling.feature
@@ -118,6 +118,10 @@ Feature: Xfrin incoming notify handling
       | 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
@@ -226,6 +230,10 @@ Feature: Xfrin incoming notify handling
       | 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
@@ -338,6 +346,10 @@ Feature: Xfrin incoming notify handling
       | 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
@@ -450,6 +462,10 @@ Feature: Xfrin incoming notify handling
       | 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
@@ -613,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..4e2d22b 100644
--- a/tests/tools/perfdhcp/Makefile.am
+++ b/tests/tools/perfdhcp/Makefile.am
@@ -23,7 +23,9 @@ 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 += rate_control.cc rate_control.h
 perfdhcp_SOURCES += stats_mgr.h
 perfdhcp_SOURCES += test_control.cc test_control.h
 libb10_perfdhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
@@ -41,4 +43,4 @@ perfdhcp_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 
 
 # ... and the documentation
-EXTRA_DIST = perfdhcp_internals.dox
\ No newline at end of file
+EXTRA_DIST = perfdhcp_internals.dox
diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc
index 7df1ea4..26255e8 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;
@@ -111,6 +113,8 @@ CommandOptions::reset() {
     exchange_mode_ = DORA_SARR;
     lease_type_.set(LeaseType::ADDRESS);
     rate_ = 0;
+    renew_rate_ = 0;
+    release_rate_ = 0;
     report_delay_ = 0;
     clients_num_ = 0;
     mac_template_.assign(mac, mac + 6);
@@ -208,7 +212,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:e:")) != -1) {
+                        "s:iBc1T:X:O:E:S:I:x:w:e:f:F:")) != -1) {
         stream << " -" << static_cast<char>(opt);
         if (optarg) {
             stream << " " << optarg;
@@ -299,6 +303,17 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
                                              " 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 'F':
+            release_rate_ = positiveInteger("value of the release rate:"
+                                            " -F<release-rate> must be a"
+                                            " positive integer");
+            break;
+
         case 'h':
             usage();
             return (true);
@@ -680,6 +695,10 @@ 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((getIpVersion() != 6) && (getReleaseRate() != 0),
+          "-F<release-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),
@@ -692,44 +711,58 @@ CommandOptions::validate() const {
           "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) && (getReleaseRate() != 0),
+          "-F<release-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() + getReleaseRate() > getRate()),
+          "The sum of Renew rate (-f<renew-rate>) and Release rate"
+          " (-F<release-rate>) must not be greater than the exchange"
+          " 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((getRate() == 0) && (getReleaseRate() != 0),
+          "Release rate specified as -F<release-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>");
 
 }
 
@@ -737,6 +770,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);
     }
@@ -793,6 +828,12 @@ CommandOptions::printCommandLine() const {
     if (rate_ != 0) {
         std::cout << "rate[1/s]=" << rate_ <<  std::endl;
     }
+    if (getRenewRate() != 0) {
+        std::cout << "renew-rate[1/s]=" << getRenewRate() << std::endl;
+    }
+    if (getReleaseRate() != 0) {
+        std::cout << "release-rate[1/s]=" << getReleaseRate() << std::endl;
+    }
     if (report_delay_ != 0) {
         std::cout << "report[s]=" << report_delay_ << std::endl;
     }
@@ -875,13 +916,15 @@ CommandOptions::printCommandLine() const {
 void
 CommandOptions::usage() const {
     std::cout <<
-        "perfdhcp [-hv] [-4|-6] [-e<lease-type>] [-r<rate>] [-t<report>]\n"
-        "    [-R<range>] [-b<base>] [-n<num-request>] [-p<test-period>]\n"
-        "    [-d<drop-time>] [-D<max-drop>] [-l<local-addr|interface>]\n"
-        "    [-P<preload>] [-a<aggressivity>] [-L<local-port>] [-s<seed>]\n"
-        "    [-i] [-B] [-c] [-1] [-T<template-file>] [-X<xid-offset>]\n"
-        "    [-O<random-offset] [-E<time-offset>] [-S<srvid-offset>]\n"
-        "    [-I<ip-offset>] [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
+        "perfdhcp [-hv] [-4|-6] [-e<lease-type>] [-r<rate>] [-f<renew-rate>]\n"
+        "         [-F<release-rate>] [-t<report>] [-R<range>] [-b<base>]\n"
+        "         [-n<num-request>] [-p<test-period>] [-d<drop-time>]\n"
+        "         [-D<max-drop>] [-l<local-addr|interface>] [-P<preload>]\n"
+        "         [-a<aggressivity>] [-L<local-port>] [-s<seed>] [-i] [-B]\n"
+        "         [-c] [-1] [-T<template-file>] [-X<xid-offset>]\n"
+        "         [-O<random-offset] [-E<time-offset>] [-S<srvid-offset>]\n"
+        "         [-I<ip-offset>] [-x<diagnostic-selector>] [-w<wrapped>]\n"
+        "         [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"
@@ -971,6 +1014,16 @@ CommandOptions::usage() const {
         "\n"
         "DHCPv6 only options:\n"
         "-c: Add a rapid commit option (exchanges will be SA).\n"
+        "-f<renew-rate>: Rate at which IPv6 Renew requests are sent to\n"
+        "    a server. This value is only valid when used in conjunction with\n"
+        "    the exchange rate (given by -r<rate>).  Furthermore the sum of\n"
+        "    this value and the release-rate (given by -F<rate) must be equal\n"
+        "    to or less than the exchange rate.\n"
+        "-F<release-rate>: Rate at which IPv6 Release requests are sent to\n"
+        "    a server. This value is only valid when used in conjunction with\n"
+        "    the exchange rate (given by -r<rate>).  Furthermore the sum of\n"
+        "    this value and the renew-rate (given by -f<rate) must be equal\n"
+        "    to or less than the exchange rate.\n"
         "\n"
         "The remaining options are used only in conjunction with -r:\n"
         "\n"
diff --git a/tests/tools/perfdhcp/command_options.h b/tests/tools/perfdhcp/command_options.h
index 7431057..7395eae 100644
--- a/tests/tools/perfdhcp/command_options.h
+++ b/tests/tools/perfdhcp/command_options.h
@@ -1,4 +1,3 @@
-
 // Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
@@ -16,11 +15,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 {
 
@@ -155,6 +155,16 @@ public:
     /// \return exchange rate per second.
     int getRate() const { return rate_; }
 
+    /// \brief Returns a rate at which DHCPv6 Renew messages are sent.
+    ///
+    /// \return A rate at which IPv6 Renew messages are sent.
+    int getRenewRate() const { return (renew_rate_); }
+
+    /// \brief Returns a rate at which DHCPv6 Release messages are sent.
+    ///
+    /// \return A rate at which DHCPv6 Release messages are sent.
+    int getReleaseRate() const { return (release_rate_); }
+
     /// \brief Returns delay between two performance reports.
     ///
     /// \return delay between two consecutive performance reports.
@@ -461,6 +471,10 @@ private:
     LeaseType lease_type_;
     /// Rate in exchange per second
     int rate_;
+    /// A rate at which DHCPv6 Renew messages are sent.
+    int renew_rate_;
+    /// A rate at which DHCPv6 Release messages are sent.
+    int release_rate_;
     /// Delay between generation of two consecutive
     /// performance reports
     int report_delay_;
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/rate_control.cc b/tests/tools/perfdhcp/rate_control.cc
new file mode 100644
index 0000000..1c2a600
--- /dev/null
+++ b/tests/tools/perfdhcp/rate_control.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 "rate_control.h"
+
+namespace isc {
+namespace perfdhcp {
+
+using namespace boost::posix_time;
+
+RateControl::RateControl()
+    : send_due_(currentTime()), last_sent_(currentTime()),
+      aggressivity_(1), rate_(0), late_sent_(false) {
+}
+
+RateControl::RateControl(const int rate, const int aggressivity)
+    : send_due_(currentTime()), last_sent_(currentTime()),
+      aggressivity_(aggressivity), rate_(rate), late_sent_(false) {
+    if (aggressivity_ < 1) {
+        isc_throw(isc::BadValue, "invalid value of aggressivity "
+                  << aggressivity << ", expected value is greater than 0");
+    }
+    if (rate_ < 0) {
+        isc_throw(isc::BadValue, "invalid value of rate " << rate
+                  << ", expected non-negative value");
+    }
+}
+
+uint64_t
+RateControl::getOutboundMessageCount() {
+
+    // We need calculate the due time for sending next set of messages.
+    updateSendDue();
+
+    // Get current time. If we are behind due time, we have to calculate
+    // how many messages to send to catch up with the rate.
+    ptime now = currentTime();
+    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 (getRate() != 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.
+            double due_factor = duration.fractional_seconds() /
+                time_duration::ticks_per_second();
+            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 * getRate());
+            // We want to make sure that at least one packet goes out.
+            if (due_exchanges == 0) {
+                due_exchanges = 1;
+            }
+            // We should not exceed aggressivity as it could have been
+            // restricted from command line.
+            if (due_exchanges > getAggressivity()) {
+                due_exchanges = getAggressivity();
+            }
+        } else {
+            // Rate is not specified so we rely on aggressivity
+            // which is the number of packets to be sent in
+            // one chunk.
+            due_exchanges = getAggressivity();
+        }
+        return (due_exchanges);
+    }
+    return (0);
+}
+
+boost::posix_time::ptime
+RateControl::currentTime() {
+    return (microsec_clock::universal_time());
+}
+
+void
+RateControl::updateSendDue() {
+    // There is no sense to update due time if the current due time is in the
+    // future. The due time is calculated as a duration between the moment
+    // when the last message of the given type was sent and the time when
+    // next one is supposed to be sent based on a given rate. The former value
+    // will not change until we send the next message, which we don't do
+    // until we reach the due time.
+    if (send_due_ > currentTime()) {
+        return;
+    }
+    // This is initialized in the class constructor, so if it is not initialized
+    // it is a programmatic error.
+    if (last_sent_.is_not_a_date_time()) {
+        isc_throw(isc::Unexpected, "timestamp of the last sent packet not"
+                  " initialized");
+    }
+    // If rate was not specified we will wait just one clock tick to
+    // send next packet. This simulates best effort conditions.
+    long duration = 1;
+    if (getRate() != 0) {
+        // We use number of ticks instead of nanoseconds because
+        // nanosecond resolution may not be available on some
+        // machines. Number of ticks guarantees the highest possible
+        // timer resolution.
+        duration = time_duration::ticks_per_second() / getRate();
+    }
+    // Calculate due time to initiate next chunk of exchanges.
+    send_due_ = last_sent_ + time_duration(0, 0, 0, duration);
+    if (send_due_ > currentTime()) {
+        late_sent_ = true;
+    } else {
+        late_sent_ = false;
+    }
+}
+
+void
+RateControl::setAggressivity(const int aggressivity) {
+    if (aggressivity < 1) {
+        isc_throw(isc::BadValue, "invalid value of aggressivity "
+                  << aggressivity << ", expected value is greater than 0");
+    }
+    aggressivity_ = aggressivity;
+}
+
+void
+RateControl::setRate(const int rate) {
+    if (rate < 0) {
+        isc_throw(isc::BadValue, "invalid value of rate " << rate
+                  << ", expected non-negative value");
+    }
+    rate_ = rate;
+}
+
+void
+RateControl::setRelativeDue(const int offset) {
+    send_due_ = offset > 0 ?
+        currentTime() + seconds(abs(offset)) :
+        currentTime() - seconds(abs(offset));
+}
+
+void
+RateControl::updateSendTime() {
+    last_sent_ = currentTime();
+}
+
+} // namespace perfdhcp
+} // namespace isc
diff --git a/tests/tools/perfdhcp/rate_control.h b/tests/tools/perfdhcp/rate_control.h
new file mode 100644
index 0000000..cd49c2c
--- /dev/null
+++ b/tests/tools/perfdhcp/rate_control.h
@@ -0,0 +1,180 @@
+// 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 RATE_CONTROL_H
+#define RATE_CONTROL_H
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+namespace isc {
+namespace perfdhcp {
+
+/// \brief A message sending rate control class for perfdhcp.
+///
+/// This class provides the means to control the rate at which messages
+/// of the specific type are sent by perfdhcp. Each message type,
+/// for which the desired rate can be specified, has a corresponding
+/// \c RateControl object. So, the perfdhcp is using up to three objects
+/// of this type at the same time, to control the rate of the following
+/// messages being sent:
+/// - Discover(DHCPv4) or Solicit (DHCPv6)
+/// - Renew (DHCPv6) or Request (DHCPv4) to renew leases.
+/// - Release
+///
+/// The purpose of the RateControl class is to track the due time for
+/// sending next message (or bunch of messages) to keep outbound rate
+/// of particular messages at the desired level. The due time is calculated
+/// using the desired rate value and the timestamp when the last message of
+/// the particular type has been sent. That puts the responsibility on the
+/// \c TestControl class to invoke the \c RateControl::updateSendDue, every
+/// time the message is sent.
+///
+/// The \c RateControl object returns the number of messages to be sent at
+/// the time. The number returned is 0, if perfdhcp shouldn't send any messages
+/// yet, or 1 (sometimes more) if the send due time has been reached.
+class RateControl {
+public:
+
+    /// \brief Default constructor.
+    RateControl();
+
+    /// \brief Constructor which sets desired rate and aggressivity.
+    ///
+    /// \param rate A desired rate.
+    /// \param aggressivity A desired aggressivity.
+    RateControl(const int rate, const int aggressivity);
+
+    /// \brief Returns the value of aggressivity.
+    int getAggressivity() const {
+        return (aggressivity_);
+    }
+
+    /// \brief Returns current due time to send next message.
+    boost::posix_time::ptime getDue() const {
+        return (send_due_);
+    }
+
+    /// \brief Returns number of messages to be sent "now".
+    ///
+    /// This function calculates how many messages of the given type should
+    /// be sent immediately when the call to the function returns, to catch
+    /// up with the desired message rate.
+    ///
+    /// The value returned depends on the due time calculated with the
+    /// \c RateControl::updateSendDue function and the current time. If
+    /// the due time has been hit, the non-zero number of messages is returned.
+    /// If the due time hasn't been hit, the number returned is 0.
+    ///
+    /// If the rate is non-zero, the number of messages to be sent is calculated
+    /// as follows:
+    /// \code
+    ///          num = duration * rate
+    /// \endcode
+    /// where <b>duration</b> is a time period between the due time to send
+    /// next set of messages and current time. The duration is expressed in
+    /// seconds with the fractional part having 6 or 9 digits (depending on
+    /// the timer resolution). If the calculated value is equal to 0, it is
+    /// rounded to 1, so as at least one message is sent.
+    ///
+    /// The value of aggressivity limits the maximal number of messages to
+    /// be sent one after another. If the number of messages calculated with
+    /// the equation above exceeds the aggressivity, this function will return
+    /// the value equal to aggressivity.
+    ///
+    /// If the rate is not specified (equal to 0), the value calculated by
+    /// this function is equal to aggressivity.
+    ///
+    /// \return A number of messages to be sent immediately.
+    uint64_t getOutboundMessageCount();
+
+    /// \brief Returns the rate.
+    int getRate() const {
+        return (rate_);
+    }
+
+    /// \brief Returns the value of the late send flag.
+    ///
+    /// The flag returned by this function indicates whether the new due time
+    /// calculated by the \c RateControl::updateSendDue is in the past.
+    /// This value is used by the \c TestControl object to increment the counter
+    /// of the late sent messages in the \c StatsMgr.
+    bool isLateSent() const {
+        return (late_sent_);
+    }
+
+    /// \brief Sets the value of aggressivity.
+    ///
+    /// \param aggressivity A new value of aggressivity. This value must be
+    /// a positive integer.
+    /// \throw isc::BadValue if new value is not a positive integer.
+    void setAggressivity(const int aggressivity);
+
+    /// \brief Sets the new rate.
+    ///
+    /// \param rate A new value of rate. This value must not be negative.
+    /// \throw isc::BadValue if new rate is negative.
+    void setRate(const int rate);
+
+    /// \brief Sets the value of the due time.
+    ///
+    /// This function is intended for unit testing. It manipulates the value of
+    /// the due time. The parameter passed to this function specifies the
+    /// (positive or negative) number of seconds relative to current time.
+    ///
+    /// \param offset A number of seconds relative to current time which
+    /// constitutes the new due time.
+    void setRelativeDue(const int offset);
+
+    /// \brief Sets the timestamp of the last sent message to current time.
+    void updateSendTime();
+
+protected:
+
+    /// \brief Convenience function returning current time.
+    ///
+    /// \return current time.
+    static boost::posix_time::ptime currentTime();
+
+    /// \brief Calculates the send due.
+    ///
+    /// This function calculates the send due timestamp using the current time
+    /// and desired rate. The due timestamp is calculated as a sum of the
+    /// timestamp when the last message was sent and the reciprocal of the rate
+    /// in micro or nanoseconds (depending on the timer resolution). If the rate
+    /// is not specified, the duration between two consecutive sends is one
+    /// timer tick.
+    void updateSendDue();
+
+    /// \brief Holds a timestamp when the next message should be sent.
+    boost::posix_time::ptime send_due_;
+
+    /// \brief Holds a timestamp when the last message was sent.
+    boost::posix_time::ptime last_sent_;
+
+    /// \brief Holds an aggressivity value.
+    int aggressivity_;
+
+    /// \brief Holds a desired rate value.
+    int rate_;
+
+    /// \brief A flag which indicates that the calculated due time is in the
+    /// past.
+    bool late_sent_;
+
+};
+
+}
+}
+
+#endif
diff --git a/tests/tools/perfdhcp/stats_mgr.h b/tests/tools/perfdhcp/stats_mgr.h
index d614ad0..7486b9e 100644
--- a/tests/tools/perfdhcp/stats_mgr.h
+++ b/tests/tools/perfdhcp/stats_mgr.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,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,9 @@ 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
+        XCHG_RL   ///< DHCPv6 RELEASE-REPLY
     };
 
     /// \brief Exchange Statistics.
@@ -628,12 +633,19 @@ public:
         /// Method prints main statistics for particular exchange.
         /// Statistics includes: number of sent and received packets,
         /// number of dropped packets and number of orphans.
+        ///
+        /// \todo Currently the number of orphans is not displayed because
+        /// Reply messages received for Renew and Releases are counted as
+        /// orphans for the 4-way exchanges, which is wrong. We will need to
+        /// move the orphans counting out of the Statistics Manager so as
+        /// orphans counter is increased only if the particular message is
+        /// not identified as a reponse to any of the messages sent by perfdhcp.
         void printMainStats() const {
             using namespace std;
             cout << "sent packets: " << getSentPacketsNum() << endl
                  << "received packets: " << getRcvdPacketsNum() << endl
-                 << "drops: " << getDroppedPacketsNum() << endl
-                 << "orphans: " << getOrphans() << endl;
+                 << "drops: " << getDroppedPacketsNum() << endl;
+            //                 << "orphans: " << getOrphans() << endl;
         }
 
         /// \brief Print round trip time packets statistics.
@@ -867,6 +879,20 @@ public:
                                                boot_time_));
     }
 
+    /// \brief Check if the exchange type has been specified.
+    ///
+    /// This method checks if the \ref ExchangeStats object of a particular type
+    /// exists (has been added using \ref addExchangeStats function).
+    ///
+    /// \param xchg_type A type of the exchange being repersented by the
+    /// \ref ExchangeStats object.
+    ///
+    /// \return true if the \ref ExchangeStats object has been added for a
+    /// specified exchange type.
+    bool hasExchangeStats(const ExchangeType xchg_type) const {
+        return (exchanges_.find(xchg_type) != exchanges_.end());
+    }
+
     /// \brief Add named custom uint64 counter.
     ///
     /// Method creates new named counter and stores in counter's map under
@@ -1155,7 +1181,7 @@ public:
     ///
     /// \param xchg_type exchange type.
     /// \return string representing name of the exchange.
-    std::string exchangeToString(ExchangeType xchg_type) const {
+    static std::string exchangeToString(ExchangeType xchg_type) {
         switch(xchg_type) {
         case XCHG_DO:
             return("DISCOVER-OFFER");
@@ -1165,6 +1191,10 @@ public:
             return("SOLICIT-ADVERTISE");
         case XCHG_RR:
             return("REQUEST-REPLY");
+        case XCHG_RN:
+            return("RENEW-REPLY");
+        case XCHG_RL:
+            return("RELEASE-REPLY");
         default:
             return("Unknown exchange type");
         }
diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc
index a126d51..3751e2f 100644
--- a/tests/tools/perfdhcp/test_control.cc
+++ b/tests/tools/perfdhcp/test_control.cc
@@ -47,7 +47,7 @@ namespace perfdhcp {
 bool TestControl::interrupted_ = false;
 
 TestControl::TestControlSocket::TestControlSocket(const int socket) :
-    SocketInfo(socket, asiolink::IOAddress("127.0.0.1"), 0),
+    SocketInfo(asiolink::IOAddress("127.0.0.1"), 0, socket),
     ifindex_(0), valid_(true) {
     try {
         initSocketData();
@@ -98,6 +98,53 @@ TestControl::TestControl() {
 }
 
 void
+TestControl::checkLateMessages(RateControl& rate_control) {
+    // If diagnostics is disabled, there is no need to log late sent messages.
+    // If it is enabled and the rate control object indicates that the last
+    // sent message was late, bump up the counter in Stats Manager.
+    if (rate_control.isLateSent() && testDiags('i')) {
+        CommandOptions& options = CommandOptions::instance();
+        if (options.getIpVersion() == 4) {
+            stats_mgr4_->incrementCounter("latesend");
+        } else if (options.getIpVersion() == 6) {
+            stats_mgr6_->incrementCounter("latesend");
+        }
+    }
+}
+
+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"
@@ -283,6 +330,46 @@ TestControl::checkExitConditions() const {
     return (false);
 }
 
+Pkt6Ptr
+TestControl::createMessageFromReply(const uint16_t msg_type,
+                                    const dhcp::Pkt6Ptr& reply) {
+    // Restrict messages to Release and Renew.
+    if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
+        isc_throw(isc::BadValue, "invalid message type " << msg_type
+                  << " to be created from Reply, expected DHCPV6_RENEW or"
+                  " DHCPV6_RELEASE");
+    }
+    // Get the string representation of the message - to be used for error
+    // logging purposes.
+    const char* msg_type_str = (msg_type == DHCPV6_RENEW ? "Renew" : "Release");
+    // Reply message must be specified.
+    if (!reply) {
+        isc_throw(isc::BadValue, "Unable to create " << msg_type_str
+                  << " message from the Reply message because the instance of"
+                  " the Reply message is NULL");
+    }
+
+    Pkt6Ptr msg(new Pkt6(msg_type, generateTransid()));
+    // Client id.
+    OptionPtr opt_clientid = reply->getOption(D6O_CLIENTID);
+    if (!opt_clientid) {
+        isc_throw(isc::Unexpected, "failed to create " << msg_type_str
+                  << " message because client id option has not been found"
+                  " in the Reply message");
+    }
+    msg->addOption(opt_clientid);
+    // Server id.
+    OptionPtr opt_serverid = reply->getOption(D6O_SERVERID);
+    if (!opt_serverid) {
+        isc_throw(isc::Unexpected, "failed to create " << msg_type_str
+                  << " because server id option has not been found in the"
+                  " Reply message");
+    }
+    msg->addOption(opt_serverid);
+    copyIaOptions(reply, msg);
+    return (msg);
+}
+
 OptionPtr
 TestControl::factoryElapsedTime6(Option::Universe, uint16_t,
                                  const OptionBuffer& buf) {
@@ -426,6 +513,36 @@ 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 >= basic_rate_control_.getDue() ||
+        (options.getRenewRate() != 0 && now >= renew_rate_control_.getDue()) ||
+        (options.getReleaseRate() != 0 &&
+         now >= release_rate_control_.getDue())) {
+        return (0);
+    }
+
+    // Let's assume that the due time for Solicit is the soonest.
+    ptime due = basic_rate_control_.getDue();
+    // If we are sending Renews and due time for Renew occurs sooner,
+    // set the due time to Renew due time.
+    if ((options.getRenewRate()) != 0 && (renew_rate_control_.getDue() < due)) {
+        due = renew_rate_control_.getDue();
+    }
+    // If we are sending Releases and the due time for Release occurs
+    // sooner than the current due time, let's use the due for Releases.
+    if ((options.getReleaseRate() != 0) &&
+        (release_rate_control_.getDue() < due)) {
+        due = release_rate_control_.getDue();
+    }
+    // Return the timeout in microseconds.
+    return (time_period(now, due).length().total_microseconds());
+}
+
 int
 TestControl::getElapsedTimeOffset() const {
     int elp_offset = CommandOptions::instance().getIpVersion() == 4 ?
@@ -454,48 +571,6 @@ TestControl::getElapsedTime(const T& pkt1, const T& pkt2) {
     return(elapsed_period.length().total_milliseconds());
 }
 
-
-uint64_t
-TestControl::getNextExchangesNum() const {
-    CommandOptions& options = CommandOptions::instance();
-    // Get current time.
-    ptime now(microsec_clock::universal_time());
-    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);
-            time_duration duration = period.length();
-            // due_factor indicates the number of seconds that
-            // sending next chunk of packets will take.
-            double due_factor = duration.fractional_seconds() /
-                time_duration::ticks_per_second();
-            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());
-            // We want to make sure that at least one packet goes out.
-            if (due_exchanges == 0) {
-                due_exchanges = 1;
-            }
-            // We should not exceed aggressivity as it could have been
-            // restricted from command line.
-            if (due_exchanges > options.getAggressivity()) {
-                due_exchanges = options.getAggressivity();
-            }
-        } else {
-            // Rate is not specified so we rely on aggressivity
-            // which is the number of packets to be sent in
-            // one chunk.
-            due_exchanges = options.getAggressivity();
-        }
-        return (due_exchanges);
-    }
-    return (0);
-}
-
 int
 TestControl::getRandomOffset(const int arg_idx) const {
     int rand_offset = CommandOptions::instance().getIpVersion() == 4 ?
@@ -616,6 +691,12 @@ TestControl::initializeStatsMgr() {
             stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR,
                                           options.getDropTime()[1]);
         }
+        if (options.getRenewRate() != 0) {
+            stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RN);
+        }
+        if (options.getReleaseRate() != 0) {
+            stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RL);
+        }
     }
     if (testDiags('i')) {
         if (options.getIpVersion() == 4) {
@@ -769,6 +850,18 @@ TestControl::sendPackets(const TestControlSocket& socket,
     }
 }
 
+uint64_t
+TestControl::sendMultipleMessages6(const TestControlSocket& socket,
+                                   const uint32_t msg_type,
+                                   const uint64_t msg_num) {
+    for (uint64_t i = 0; i < msg_num; ++i) {
+        if (!sendMessageFromReply(msg_type, socket)) {
+            return (i);
+        }
+    }
+    return (msg_num);
+}
+
 void
 TestControl::printDiagnostics() const {
     CommandOptions& options = CommandOptions::instance();
@@ -1024,20 +1117,49 @@ TestControl::processReceivedPacket6(const TestControlSocket& socket,
             }
         }
     } else if (packet_type == DHCPV6_REPLY) {
-        stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6);
+        // If the received message is Reply, we have to find out which exchange
+        // type the Reply message belongs to. It is doable by matching the Reply
+        // transaction id with the transaction id of the sent Request, Renew
+        // or Release. First we start with the Request.
+        if (stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6)) {
+            // The Reply belongs to Request-Reply exchange type. So, we may need
+            // to keep this Reply in the storage if Renews or/and Releases are
+            // being sent. Note that, Reply messages hold the information about
+            // leases assigned. We use this information to construct Renew and
+            // Release messages.
+            if (stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RN) ||
+                stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RL)) {
+                // Renew or Release messages are sent, because StatsMgr has the
+                // specific exchange type specified. Let's append the Reply
+                // message to a storage.
+                reply_storage_.append(pkt6);
+            }
+        // The Reply message is not a server's response to the Request message
+        // sent within the 4-way exchange. It may be a response to the Renew
+        // or Release message. In the if clause we first check if StatsMgr
+        // has exchange type for Renew specified, and if it has, if there is
+        // a corresponding Renew message for the received Reply. If not,
+        // we check that StatsMgr has exchange type for Release specified,
+        // as possibly the Reply has been sent in response to Release.
+        } else if (!(stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RN) &&
+                     stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RN, pkt6)) &&
+                   stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RL)) {
+            // At this point, it is only possible that the Reply has been sent
+            // in response to a Release. Try to match the Reply with Release.
+            stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RL, 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;
@@ -1055,7 +1177,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;
@@ -1153,10 +1275,16 @@ TestControl::registerOptionFactories() const {
 
 void
 TestControl::reset() {
-    send_due_ = microsec_clock::universal_time();
-    last_sent_ = send_due_;
-    last_report_ = send_due_;
+    CommandOptions& options = CommandOptions::instance();
+    basic_rate_control_.setAggressivity(options.getAggressivity());
+    basic_rate_control_.setRate(options.getRate());
+    renew_rate_control_.setAggressivity(options.getAggressivity());
+    renew_rate_control_.setRate(options.getRenewRate());
+    release_rate_control_.setAggressivity(options.getAggressivity());
+    release_rate_control_.setRate(options.getReleaseRate());
+
     transid_gen_.reset();
+    last_report_ = microsec_clock::universal_time();
     // Actual generators will have to be set later on because we need to
     // get command line parameters first.
     setTransidGenerator(NumberGeneratorPtr());
@@ -1222,11 +1350,10 @@ TestControl::run() {
     // Initialize Statistics Manager. Release previous if any.
     initializeStatsMgr();
     for (;;) {
-        // Calculate send due based on when last exchange was initiated.
-        updateSendDue();
         // Calculate number of packets to be sent to stay
         // catch up with rate.
-        uint64_t packets_due = getNextExchangesNum();
+        uint64_t packets_due = basic_rate_control_.getOutboundMessageCount();
+        checkLateMessages(basic_rate_control_);
         if ((packets_due == 0) && testDiags('i')) {
             if (options.getIpVersion() == 4) {
                 stats_mgr4_->incrementCounter("shortwait");
@@ -1249,11 +1376,39 @@ 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)) {
+            uint64_t renew_packets_due =
+                renew_rate_control_.getOutboundMessageCount();
+            checkLateMessages(renew_rate_control_);
+            // Send Renew messages.
+            sendMultipleMessages6(socket, DHCPV6_RENEW, renew_packets_due);
+        }
+
+        // If -F<release-rate> option was specified we have to check how many
+        // Release messages should be sent to catch up with a desired rate.
+        if ((options.getIpVersion() == 6) && (options.getReleaseRate() != 0)) {
+            uint64_t release_packets_due =
+                release_rate_control_.getOutboundMessageCount();
+            checkLateMessages(release_rate_control_);
+            // Send Release messages.
+            sendMultipleMessages6(socket, DHCPV6_RELEASE, release_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();
 
@@ -1334,7 +1489,7 @@ TestControl::saveFirstPacket(const Pkt6Ptr& pkt) {
 void
 TestControl::sendDiscover4(const TestControlSocket& socket,
                            const bool preload /*= false*/) {
-    last_sent_ = microsec_clock::universal_time();
+    basic_rate_control_.updateSendTime();
     // Generate the MAC address to be passed in the packet.
     uint8_t randomized = 0;
     std::vector<uint8_t> mac_address = generateMacAddress(randomized);
@@ -1379,9 +1534,7 @@ void
 TestControl::sendDiscover4(const TestControlSocket& socket,
                            const std::vector<uint8_t>& template_buf,
                            const bool preload /* = false */) {
-    // last_sent_ has to be updated for each function that initiates
-    // new transaction. The packet exchange synchronization relies on this.
-    last_sent_ = microsec_clock::universal_time();
+    basic_rate_control_.updateSendTime();
     // Get the first argument if mulitple the same arguments specified
     // in the command line. First one refers to DISCOVER packets.
     const uint8_t arg_idx = 0;
@@ -1429,6 +1582,39 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
     saveFirstPacket(pkt4);
 }
 
+bool
+TestControl::sendMessageFromReply(const uint16_t msg_type,
+                                  const TestControlSocket& socket) {
+    // We only permit Release or Renew messages to be sent using this function.
+    if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
+        isc_throw(isc::BadValue, "invalid message type " << msg_type
+                  << " to be sent, expected DHCPV6_RENEW or DHCPV6_RELEASE");
+    }
+    // We track the timestamp of last Release and Renew in different variables.
+    if (msg_type == DHCPV6_RENEW) {
+        renew_rate_control_.updateSendTime();
+    } else {
+        release_rate_control_.updateSendTime();
+    }
+    Pkt6Ptr reply = reply_storage_.getRandom();
+    if (!reply) {
+        return (false);
+    }
+    // Prepare the message of the specified type.
+    Pkt6Ptr msg = createMessageFromReply(msg_type, reply);
+    setDefaults6(socket, msg);
+    msg->pack();
+    // And send it.
+    IfaceMgr::instance().send(msg);
+    if (!stats_mgr6_) {
+        isc_throw(Unexpected, "Statistics Manager for DHCPv6 "
+                  "hasn't been initialized");
+    }
+    stats_mgr6_->passSentPacket((msg_type == DHCPV6_RENEW ? StatsMgr6::XCHG_RN
+                                 : StatsMgr6::XCHG_RL), msg);
+    return (true);
+}
+
 void
 TestControl::sendRequest4(const TestControlSocket& socket,
                           const dhcp::Pkt4Ptr& discover_pkt4,
@@ -1767,7 +1953,7 @@ TestControl::sendRequest6(const TestControlSocket& socket,
 void
 TestControl::sendSolicit6(const TestControlSocket& socket,
                           const bool preload /*= false*/) {
-    last_sent_ = microsec_clock::universal_time();
+    basic_rate_control_.updateSendTime();
     // Generate DUID to be passed to the packet
     uint8_t randomized = 0;
     std::vector<uint8_t> duid = generateDuid(randomized);
@@ -1816,7 +2002,7 @@ void
 TestControl::sendSolicit6(const TestControlSocket& socket,
                           const std::vector<uint8_t>& template_buf,
                           const bool preload /*= false*/) {
-    last_sent_ = microsec_clock::universal_time();
+    basic_rate_control_.updateSendTime();
     const int arg_idx = 0;
     // Get transaction id offset.
     size_t transid_offset = getTransactionIdOffset(arg_idx);
@@ -1915,46 +2101,5 @@ TestControl::testDiags(const char diag) const {
     return (false);
 }
 
-void
-TestControl::updateSendDue() {
-    // 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()) {
-        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;
-    if (rate != 0) {
-        // We use number of ticks instead of nanoseconds because
-        // nanosecond resolution may not be available on some
-        // machines. Number of ticks guarantees the highest possible
-        // timer resolution.
-        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);
-    // 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 (testDiags('i')) {
-            if (options.getIpVersion() == 4) {
-                stats_mgr4_->incrementCounter("latesend");
-            } else if (options.getIpVersion() == 6) {
-                stats_mgr6_->incrementCounter("latesend");
-            }
-        }
-    }
-}
-
-
 } // namespace perfdhcp
 } // namespace isc
diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h
index 93509e6..5a7ef48 100644
--- a/tests/tools/perfdhcp/test_control.h
+++ b/tests/tools/perfdhcp/test_control.h
@@ -15,20 +15,22 @@
 #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 "rate_control.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 {
@@ -299,6 +301,37 @@ 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 DHCPv6 message from the Reply packet.
+    ///
+    /// This function creates DHCPv6 Renew or Release message using the
+    /// data from the Reply message by copying options from the Reply
+    /// message.
+    ///
+    /// \param msg_type A type of the message to be createad.
+    /// \param reply An instance of the Reply packet which contents should
+    /// be used to create an instance of the new message.
+    ///
+    /// \return created Release or Renew message
+    /// \throw isc::BadValue if the msg_type is neither DHCPV6_RENEW nor
+    /// DHCPV6_RELEASE or if the reply is NULL.
+    /// \throw isc::Unexpected if mandatory options are missing in the
+    /// Reply message.
+    dhcp::Pkt6Ptr createMessageFromReply(const uint16_t msg_type,
+                                         const dhcp::Pkt6Ptr& reply);
+
     /// \brief Factory function to create DHCPv6 ELAPSED_TIME option.
     ///
     /// This factory function creates DHCPv6 ELAPSED_TIME option instance.
@@ -450,15 +483,14 @@ protected:
         return (transid_gen_->generate());
     }
 
-    /// \brief Returns number of exchanges to be started.
+    /// \brief Returns a timeout for packet reception.
     ///
-    /// Method returns number of new exchanges to be started as soon
-    /// as possible to satisfy expected rate. Calculation used here
-    /// is based on current time, due time calculated with
-    /// \ref updateSendDue function and expected rate.
+    /// 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 number of exchanges to be started immediately.
-    uint64_t getNextExchangesNum() const;
+    /// \return A current timeout in microseconds.
+    uint32_t getCurrentTimeout() const;
 
     /// \brief Return template buffer.
     ///
@@ -702,6 +734,33 @@ protected:
                      const uint64_t packets_num,
                      const bool preload = false);
 
+    /// \brief Send number of DHCPv6 Renew or Release messages to the server.
+    ///
+    /// \param socket An object representing socket to be used to send packets.
+    /// \param msg_type A type of the messages to be sent (DHCPV6_RENEW or
+    /// DHCPV6_RELEASE).
+    /// \param msg_num A number of messages to be sent.
+    ///
+    /// \return A number of messages actually sent.
+    uint64_t sendMultipleMessages6(const TestControlSocket& socket,
+                                   const uint32_t msg_type,
+                                   const uint64_t msg_num);
+
+    /// \brief Send DHCPv6 Renew or Release message using specified socket.
+    ///
+    /// This method will select an existing lease from the Reply packet cache
+    /// If there is no lease that can be renewed or released this method will
+    /// return false.
+    ///
+    /// \param msg_type A type of the message to be sent (DHCPV6_RENEW or
+    /// DHCPV6_RELEASE).
+    /// \param socket An object encapsulating socket to be used to send
+    /// a packet.
+    ///
+    /// \return true if the message has been sent, false otherwise.
+    bool sendMessageFromReply(const uint16_t msg_type,
+                              const TestControlSocket& socket);
+
     /// \brief Send DHCPv4 REQUEST message.
     ///
     /// Method creates and sends DHCPv4 REQUEST message to the server.
@@ -843,14 +902,18 @@ protected:
     /// \return true if diagnostics flag has been set.
     bool testDiags(const char diag) const;
 
-    /// \brief Update due time to initiate next chunk of exchanges.
-    ///
-    /// 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();
+protected:
 
-private:
+    /// \brief Increments counter of late sent messages if required.
+    ///
+    /// This function checks if the message or set of messages of a given type,
+    /// were sent later than their due time. If they were sent late, it is
+    /// an indication that the perfdhcp doesn't catch up with the desired rate
+    /// for sending messages.
+    ///
+    /// \param rate_control An object tracking due times for a particular
+    /// type of messages.
+    void checkLateMessages(RateControl& rate_control);
 
     /// \brief Copies IA_NA or IA_PD option from one packet to another.
     ///
@@ -880,7 +943,7 @@ private:
 
     /// \brief Calculate elapsed time between two packets.
     ///
-    /// \param T Pkt4Ptr or Pkt6Ptr class.
+    /// \tparam T Pkt4Ptr or Pkt6Ptr class.
     /// \param pkt1 first packet.
     /// \param pkt2 second packet.
     /// \throw InvalidOperation if packet timestamps are invalid.
@@ -993,16 +1056,20 @@ private:
     std::string vector2Hex(const std::vector<uint8_t>& vec,
                            const std::string& separator = "") const;
 
-    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.
+    /// \brief A rate control class for Discover and Solicit messages.
+    RateControl basic_rate_control_;
+    /// \brief A rate control class for Renew messages.
+    RateControl renew_rate_control_;
+    /// \brief A rate control class for Release messages.
+    RateControl release_rate_control_;
 
     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..6e808c6 100644
--- a/tests/tools/perfdhcp/tests/Makefile.am
+++ b/tests/tools/perfdhcp/tests/Makefile.am
@@ -1,6 +1,7 @@
 SUBDIRS = . testdata
 
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -I$(srcdir)/.. -I$(builddir)/..
 AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\"
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -25,6 +26,8 @@ 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 += rate_control_unittest.cc
 run_unittests_SOURCES += stats_mgr_unittest.cc
 run_unittests_SOURCES += test_control_unittest.cc
 run_unittests_SOURCES += command_options_helper.h
@@ -32,6 +35,7 @@ run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/command_options.cc
 run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/pkt_transform.cc
 run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt6.cc
 run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt4.cc
+run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/rate_control.cc
 run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/test_control.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
diff --git a/tests/tools/perfdhcp/tests/command_options_helper.h b/tests/tools/perfdhcp/tests/command_options_helper.h
index dbb5c42..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 {
diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc
index d323782..3d2ce2e 100644
--- a/tests/tools/perfdhcp/tests/command_options_unittest.cc
+++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc
@@ -168,6 +168,8 @@ protected:
         EXPECT_EQ(CommandOptions::DORA_SARR, opt.getExchangeMode());
         EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::ADDRESS));
         EXPECT_EQ(0, opt.getRate());
+        EXPECT_EQ(0, opt.getRenewRate());
+        EXPECT_EQ(0, opt.getReleaseRate());
         EXPECT_EQ(0, opt.getReportDelay());
         EXPECT_EQ(0, opt.getClientsNum());
 
@@ -334,6 +336,99 @@ 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 and -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 should not be greater than the rate.
+    EXPECT_THROW(process("perfdhcp -6 -r 10 -f 11 -l ethx all"),
+                 isc::InvalidParameter);
+    // The renew-rate of 0 is invalid.
+    EXPECT_THROW(process("perfdhcp -6 -r 10 -f 0 -l ethx all"),
+                 isc::InvalidParameter);
+    // The negative renew-rate is invalid.
+    EXPECT_THROW(process("perfdhcp -6 -r 10 -f -5 -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, ReleaseRate) {
+    CommandOptions& opt = CommandOptions::instance();
+    // If -F is specified together with -r the command line should
+    // be accepted and the release rate should be set.
+    EXPECT_NO_THROW(process("perfdhcp -6 -r 10 -F 10 -l ethx all"));
+    EXPECT_EQ(10, opt.getReleaseRate());
+    // Check that the release rate can be set to different value than
+    // rate specified as -r<rate>. Also, swap -F and -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.getReleaseRate());
+    // The release rate should not be greater than the rate.
+    EXPECT_THROW(process("perfdhcp -6 -r 10 -F 11 -l ethx all"),
+                 isc::InvalidParameter);
+    // The release-rate of 0 is invalid.
+    EXPECT_THROW(process("perfdhcp -6 -r 10 -F 0 -l ethx all"),
+                 isc::InvalidParameter);
+    // The negative rlease-rate is invalid.
+    EXPECT_THROW(process("perfdhcp -6 -r 10 -F -5 -l ethx all"),
+                 isc::InvalidParameter);
+    // If -r<rate> is not specified the -F<release-rate> should not
+    // be accepted.
+    EXPECT_THROW(process("perfdhcp -6 -F 10 -l ethx all"),
+                 isc::InvalidParameter);
+    // Currently the -F<release-rate> can be specified for IPv6 mode
+    // only.
+    EXPECT_THROW(process("perfdhcp -4 -r 10 -F 10 -l ethx all"),
+                 isc::InvalidParameter);
+    // Release 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, ReleaseRenew) {
+    CommandOptions& opt = CommandOptions::instance();
+    // It should be possible to specify the -F, -f and -r options.
+    EXPECT_NO_THROW(process("perfdhcp -6 -r 10 -F 3 -f 5 -l ethx all"));
+    EXPECT_EQ(10, opt.getRate());
+    EXPECT_EQ(3, opt.getReleaseRate());
+    EXPECT_EQ(5, opt.getRenewRate());
+    // It should be possible to specify the -F and -f with the values which
+    // sum is equal to the rate specified as -r<rate>.
+    EXPECT_NO_THROW(process("perfdhcp -6 -r 8 -F 3 -f 5 -l ethx all"));
+    EXPECT_EQ(8, opt.getRate());
+    EXPECT_EQ(3, opt.getReleaseRate());
+    EXPECT_EQ(5, opt.getRenewRate());
+    // Check that the sum of the release and renew rate is not greater
+    // than the rate specified as -r<rate>.
+    EXPECT_THROW(process("perfdhcp -6 -F 6 -f 5 -r 10 -l ethx 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/rate_control_unittest.cc b/tests/tools/perfdhcp/tests/rate_control_unittest.cc
new file mode 100644
index 0000000..829d37f
--- /dev/null
+++ b/tests/tools/perfdhcp/tests/rate_control_unittest.cc
@@ -0,0 +1,207 @@
+// 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 "rate_control.h"
+#include <gtest/gtest.h>
+
+
+using namespace isc;
+using namespace isc::perfdhcp;
+
+/// \brief A class which exposes protected methods and members of the
+/// RateControl class (under test).
+class NakedRateControl : public RateControl {
+public:
+
+    /// \brief Default constructor.
+    NakedRateControl()
+        : RateControl() {
+    }
+
+    /// \brief Constructor which sets up the rate and aggressivity.
+    ///
+    /// \param rate A rate at which messages are sent.
+    /// \param aggressivity A value of aggressivity. This value controls the
+    /// maximal number of messages sent in one chunk.
+    NakedRateControl(const int rate, const int aggressivity)
+        : RateControl(rate, aggressivity) {
+    }
+
+    using RateControl::currentTime;
+    using RateControl::updateSendTime;
+    using RateControl::updateSendDue;
+    using RateControl::send_due_;
+    using RateControl::last_sent_;
+    using RateControl::late_sent_;
+
+};
+
+// Test default constructor.
+TEST(RateControl, constructorDefault) {
+    NakedRateControl rc;
+    EXPECT_EQ(1, rc.getAggressivity());
+    EXPECT_EQ(0, rc.getRate());
+    EXPECT_FALSE(rc.getDue().is_not_a_date_time());
+    EXPECT_FALSE(rc.last_sent_.is_not_a_date_time());
+    EXPECT_FALSE(rc.isLateSent());
+}
+
+// Test the constructor which sets the rate and aggressivity.
+TEST(RateControl, constructor) {
+    // Call the constructor and verify that it sets the appropriate
+    // values.
+    NakedRateControl rc1(3, 2);
+    EXPECT_EQ(2, rc1.getAggressivity());
+    EXPECT_EQ(3, rc1.getRate());
+    EXPECT_FALSE(rc1.getDue().is_not_a_date_time());
+    EXPECT_FALSE(rc1.last_sent_.is_not_a_date_time());
+    EXPECT_FALSE(rc1.isLateSent());
+
+    // Call the constructor again and make sure that different values
+    // will be set correctly.
+    NakedRateControl rc2(5, 6);
+    EXPECT_EQ(6, rc2.getAggressivity());
+    EXPECT_EQ(5, rc2.getRate());
+    EXPECT_FALSE(rc2.getDue().is_not_a_date_time());
+    EXPECT_FALSE(rc2.last_sent_.is_not_a_date_time());
+    EXPECT_FALSE(rc2.isLateSent());
+
+    // The 0 value of aggressivity < 1 is not acceptable.
+    EXPECT_THROW(RateControl(3, 0), isc::BadValue);
+    // The negative value of rate is not acceptable.
+    EXPECT_THROW(RateControl(-1, 3), isc::BadValue);
+}
+
+// Check the aggressivity accessor.
+TEST(RateControl, getAggressivity) {
+    RateControl rc;
+    ASSERT_EQ(1, rc.getAggressivity());
+    rc.setAggressivity(5);
+    ASSERT_EQ(5, rc.getAggressivity());
+    rc.setAggressivity(10);
+    EXPECT_EQ(10, rc.getAggressivity());
+}
+
+// Check the due time accessor.
+TEST(RateControl, getDue) {
+    NakedRateControl rc;
+    ASSERT_FALSE(rc.getDue().is_not_a_date_time());
+    rc.send_due_ = NakedRateControl::currentTime();
+    EXPECT_TRUE(NakedRateControl::currentTime() >= rc.getDue());
+    rc.send_due_ = NakedRateControl::currentTime() +
+        boost::posix_time::seconds(10);
+    EXPECT_TRUE(NakedRateControl::currentTime() < rc.getDue());
+}
+
+// Check the rate accessor.
+TEST(RateControl, getRate) {
+    RateControl rc;
+    ASSERT_EQ(0, rc.getRate());
+    rc.setRate(5);
+    ASSERT_EQ(5, rc.getRate());
+    rc.setRate(10);
+    EXPECT_EQ(10, rc.getRate());
+}
+
+// Check if late send flag accessor.
+TEST(RateControl, isLateSent) {
+    NakedRateControl rc;
+    ASSERT_FALSE(rc.isLateSent());
+    rc.late_sent_ = true;
+    EXPECT_TRUE(rc.isLateSent());
+}
+
+// Check that the function returns the number of messages to be sent "now"
+// correctly.
+// @todo Possibly extend this test to cover more complex scenarios. Note that
+// it is quite hard to fully test this function as its behaviour strongly
+// depends on time.
+TEST(RateControl, getOutboundMessageCount) {
+    NakedRateControl rc1(1000, 1);
+    // Set the timestamp of the last sent message well to the past.
+    // The resulting due time will be in the past too.
+    rc1.last_sent_ =
+        NakedRateControl::currentTime() - boost::posix_time::seconds(5);
+    // The number of messages to be sent must be greater than 0.
+    uint64_t count;
+    ASSERT_NO_THROW(count = rc1.getOutboundMessageCount());
+    EXPECT_GT(count, 0);
+    // Now, don't specify the rate. In this case the aggressivity dictates
+    // how many messages to send.
+    NakedRateControl rc2(0, 3);
+    rc2.last_sent_ =
+        NakedRateControl::currentTime() - boost::posix_time::seconds(5);
+    ASSERT_NO_THROW(count = rc2.getOutboundMessageCount());
+    EXPECT_EQ(3, count);
+    // Specify the rate and set the timestamp of the last sent message well
+    // to the future. If the resulting due time is well in the future too,
+    // the number of messages to be sent must be 0.
+    NakedRateControl rc3(10, 3);
+    rc3.last_sent_ = NakedRateControl::currentTime() +
+        boost::posix_time::seconds(5);
+    ASSERT_NO_THROW(count = rc3.getOutboundMessageCount());
+    EXPECT_EQ(0, count);
+
+}
+
+// Test the aggressivity modifier for valid and invalid values.
+TEST(RateControl, setAggressivity) {
+    NakedRateControl rc;
+    ASSERT_NO_THROW(rc.setAggressivity(1));
+    EXPECT_THROW(rc.setAggressivity(0), isc::BadValue);
+    EXPECT_THROW(rc.setAggressivity(-1), isc::BadValue);
+}
+
+// Test the rate modifier for valid and invalid rate values.
+TEST(RateControl, setRate) {
+    NakedRateControl rc;
+    EXPECT_NO_THROW(rc.setRate(1));
+    EXPECT_NO_THROW(rc.setRate(0));
+    EXPECT_THROW(rc.setRate(-1), isc::BadValue);
+}
+
+// Test the function which calculates the due time to send next set of
+// messages.
+TEST(RateControl, updateSendDue) {
+    NakedRateControl rc;
+    // Set the send due timestamp to the value which is well in the future.
+    // If we don't hit the due time, the function should not modify the
+    // due time.
+    rc.send_due_ =
+        NakedRateControl::currentTime() + boost::posix_time::seconds(10);
+    boost::posix_time::ptime last_send_due = rc.send_due_;
+    ASSERT_NO_THROW(rc.updateSendDue());
+    EXPECT_TRUE(rc.send_due_ == last_send_due);
+    // Set the due time to the value which is already behind.
+    rc.send_due_ =
+        NakedRateControl::currentTime() - boost::posix_time::seconds(10);
+    last_send_due = rc.send_due_;
+    ASSERT_NO_THROW(rc.updateSendDue());
+    // The value should be modified to the new value.
+    EXPECT_TRUE(rc.send_due_ > last_send_due);
+
+}
+
+// Test that the message send time is updated to the current time.
+TEST(RateControl, updateSendTime) {
+    NakedRateControl rc;
+    // Set the timestamp to the future.
+    rc.last_sent_ =
+        NakedRateControl::currentTime() + boost::posix_time::seconds(5);
+    rc.updateSendTime();
+    // Updated timestamp should be set to now or to the past.
+    EXPECT_TRUE(rc.last_sent_ <= NakedRateControl::currentTime());
+
+}
diff --git a/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc b/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
index 41aac82..6147bf7 100644
--- a/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
+++ b/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
@@ -187,6 +187,8 @@ TEST_F(StatsMgrTest, Exchange) {
                                                       common_transid));
     // This is expected to throw because XCHG_DO was not yet
     // added to Stats Manager for tracking.
+    ASSERT_FALSE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_DO));
+    ASSERT_FALSE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_RA));
     EXPECT_THROW(
         stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet),
         BadValue
@@ -196,8 +198,11 @@ TEST_F(StatsMgrTest, Exchange) {
         BadValue
     );
 
+
     // Adding DISCOVER-OFFER exchanges to be tracked by Stats Manager.
     stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
+    ASSERT_TRUE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_DO));
+    ASSERT_FALSE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_RA));
     // The following two attempts are expected to throw because
     // invalid exchange types are passed (XCHG_RA instead of XCHG_DO)
     EXPECT_THROW(
@@ -259,6 +264,21 @@ TEST_F(StatsMgrTest, MultipleExchanges) {
               stats_mgr->getRcvdPacketsNum(StatsMgr6::XCHG_RR));
 }
 
+TEST_F(StatsMgrTest, ExchangeToString) {
+    // Test DHCPv4 specific exchange names.
+    EXPECT_EQ("DISCOVER-OFFER",
+              StatsMgr4::exchangeToString(StatsMgr4::XCHG_DO));
+    EXPECT_EQ("REQUEST-ACK", StatsMgr4::exchangeToString(StatsMgr4::XCHG_RA));
+
+    // Test DHCPv6 specific exchange names.
+    EXPECT_EQ("SOLICIT-ADVERTISE",
+              StatsMgr6::exchangeToString(StatsMgr6::XCHG_SA));
+    EXPECT_EQ("REQUEST-REPLY", StatsMgr6::exchangeToString(StatsMgr6::XCHG_RR));
+    EXPECT_EQ("RENEW-REPLY", StatsMgr6::exchangeToString(StatsMgr6::XCHG_RN));
+    EXPECT_EQ("RELEASE-REPLY", StatsMgr6::exchangeToString(StatsMgr6::XCHG_RL));
+
+}
+
 TEST_F(StatsMgrTest, SendReceiveSimple) {
     boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
     boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc
index c0b7231..b84d58d 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;
@@ -63,11 +64,42 @@ 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.
     };
 
+    /// \brief Sets the due times for sedning Solicit, Renew and Release.
+    ///
+    /// There are three class members that hold the due time for sending DHCP
+    /// messages:
+    /// - send_due_ - due time to send Solicit,
+    /// - renew_due_ - due time to send Renew,
+    /// - release_due_ - due time to send Release.
+    /// Some tests in this test suite need to modify these values relative to
+    /// the current time. This function modifies this values using time
+    /// offset values (positive or negative) specified as a difference in
+    /// seconds between current time and the due time.
+    ///
+    /// \param send_secs An offset of the due time for Solicit.
+    /// \param renew_secs An offset of the due time for Renew.
+    /// \param release_secs An offset of the due time for Release.
+    void setRelativeDueTimes(const int send_secs, const int renew_secs = 0,
+                             const int release_secs = 0) {
+        ptime now = microsec_clock::universal_time();
+        basic_rate_control_.setRelativeDue(send_secs);
+        renew_rate_control_.setRelativeDue(renew_secs);
+        release_rate_control_.setRelativeDue(release_secs);
+
+    }
+
     using TestControl::checkExitConditions;
+    using TestControl::createMessageFromReply;
     using TestControl::factoryElapsedTime6;
     using TestControl::factoryGeneric;
     using TestControl::factoryIana6;
@@ -76,7 +108,7 @@ public:
     using TestControl::factoryRequestList4;
     using TestControl::generateDuid;
     using TestControl::generateMacAddress;
-    using TestControl::getNextExchangesNum;
+    using TestControl::getCurrentTimeout;
     using TestControl::getTemplateBuffer;
     using TestControl::initPacketTemplates;
     using TestControl::initializeStatsMgr;
@@ -84,10 +116,22 @@ public:
     using TestControl::processReceivedPacket4;
     using TestControl::processReceivedPacket6;
     using TestControl::registerOptionFactories;
+    using TestControl::reset;
     using TestControl::sendDiscover4;
+    using TestControl::sendPackets;
+    using TestControl::sendMultipleMessages6;
+    using TestControl::sendRequest6;
     using TestControl::sendSolicit6;
     using TestControl::setDefaults4;
     using TestControl::setDefaults6;
+    using TestControl::basic_rate_control_;
+    using TestControl::renew_rate_control_;
+    using TestControl::release_rate_control_;
+    using TestControl::last_report_;
+    using TestControl::transid_gen_;
+    using TestControl::macaddr_gen_;
+    using TestControl::first_packet_serverid_;
+    using TestControl::interrupted_;
 
     NakedTestControl() : TestControl() {
         uint32_t clients_num = CommandOptions::instance().getClientsNum() == 0 ?
@@ -281,7 +325,7 @@ public:
         return (cnt);
     }
 
-    /// brief Test generation of mulitple DUIDs
+    /// \brief Test generation of mulitple DUIDs
     ///
     /// This method checks the generation of multiple DUIDs. Number
     /// of iterations depends on the number of simulated clients.
@@ -615,6 +659,167 @@ public:
         }
     }
 
+    /// \brief Test that the DHCPv4 Release or Renew message is created
+    /// correctly and comprises expected options.
+    ///
+    /// \param msg_type A type of the message to be tested: DHCPV6_RELEASE
+    /// or DHCPV6_RENEW.
+    void testCreateRenewRelease(const uint16_t msg_type) {
+        // This command line specifies that the Release/Renew messages should
+        // be sent with the same rate as the Solicit messages.
+        std::ostringstream s;
+        s << "perfdhcp -6 -l lo -r 10 ";
+        s << (msg_type == DHCPV6_RELEASE ? "-F" : "-f") << " 10 ";
+        s << "-R 10 -L 10547 -n 10 -e address-and-prefix ::1";
+        ASSERT_NO_THROW(processCmdLine(s.str()));
+        // Create a test controller class.
+        NakedTestControl tc;
+        // Set the transaction id generator which will be used by the
+        // createRenew or createRelease function to generate transaction id.
+        boost::shared_ptr<NakedTestControl::IncrementalGenerator>
+            generator(new NakedTestControl::IncrementalGenerator());
+        tc.setTransidGenerator(generator);
+
+        // Create a Reply packet. The createRelease or createReply function will
+        // need Reply packet to create a corresponding Release or Reply.
+        Pkt6Ptr reply = createReplyPkt6(1);
+
+        Pkt6Ptr msg;
+        // Check that the message is created.
+        ASSERT_NO_THROW(msg = tc.createMessageFromReply(msg_type, reply));
+
+        ASSERT_TRUE(msg);
+        // Check that the message type and transaction id is correct.
+        EXPECT_EQ(msg_type, msg->getType());
+        EXPECT_EQ(1, msg->getTransid());
+
+        // Check that the message has expected options. These are the same for
+        // Release and Renew.
+
+        // Client Identifier.
+        OptionPtr opt_clientid = msg->getOption(D6O_CLIENTID);
+        ASSERT_TRUE(opt_clientid);
+        EXPECT_TRUE(reply->getOption(D6O_CLIENTID)->getData() ==
+                    opt_clientid->getData());
+
+        // Server identifier
+        OptionPtr opt_serverid = msg->getOption(D6O_SERVERID);
+        ASSERT_TRUE(opt_serverid);
+        EXPECT_TRUE(reply->getOption(D6O_SERVERID)->getData() ==
+                opt_serverid->getData());
+
+        // IA_NA
+        OptionPtr opt_ia_na = msg->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 = msg->getOption(D6O_IA_PD);
+        ASSERT_TRUE(opt_ia_pd);
+        EXPECT_TRUE(reply->getOption(D6O_IA_PD)->getData() ==
+                    opt_ia_pd->getData());
+
+        // Make sure that exception is thrown if the Reply message is NULL.
+        EXPECT_THROW(tc.createMessageFromReply(msg_type, Pkt6Ptr()),
+                     isc::BadValue);
+
+    }
+
+    /// \brief Test sending DHCPv6 Releases or Renews.
+    ///
+    /// This function simulates acquiring 10 leases from the server. Returned
+    /// Reply messages are cached and used to send Renew or Release messages.
+    /// The maxmimal number of Renew or Release messages which can be sent is
+    /// equal to the number of leases acquired (10). This function also checks
+    /// that an attempt to send more Renew or Release messages than the number
+    /// of leases acquired will fail.
+    ///
+    /// \param msg_type A type of the message which is simulated to be sent
+    /// (DHCPV6_RENEW or DHCPV6_RELEASE).
+    void testSendRenewRelease(const uint16_t msg_type) {
+        std::string loopback_iface(getLocalLoopback());
+        if (loopback_iface.empty()) {
+            std::cout << "Skipping the test because loopback interface could"
+                " not be detected" << std::endl;
+            return;
+        }
+        // Build a command line. Depending on the message type, we will use
+        // -f<renew-rate> or -F<release-rate> parameter.
+        std::ostringstream s;
+        s << "perfdhcp -6 -l " << loopback_iface << " -r 10 ";
+        s << (msg_type == DHCPV6_RENEW ? "-f" : "-F");
+        s << " 10 -R 10 -L 10547 -n 10 ::1";
+        ASSERT_NO_THROW(processCmdLine(s.str()));
+        // 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 msg_num;
+        // Try to send 5 messages. It should be successful because 10 Reply
+        // messages has been received. For each of them we should be able to
+        // send Renew or Release.
+        ASSERT_NO_THROW(
+            msg_num = tc.sendMultipleMessages6(sock, msg_type, 5)
+        );
+        // Make sure that we have sent 5 messages.
+        EXPECT_EQ(5, msg_num);
+
+        // Try to do it again. We should still have 5 Reply packets for
+        // which Renews or Releases haven't been sent yet.
+        ASSERT_NO_THROW(
+            msg_num = tc.sendMultipleMessages6(sock, msg_type, 5)
+        );
+        EXPECT_EQ(5, msg_num);
+
+        // We used all the Reply packets (we sent Renew or Release for each of
+        // them already). Therefore, no further Renew or Release messages should
+        // be sent before we acquire new leases.
+        ASSERT_NO_THROW(
+            msg_num = tc.sendMultipleMessages6(sock, msg_type, 5)
+        );
+        // Make sure that no message has been sent.
+        EXPECT_EQ(0, msg_num);
+
+    }
+
     /// \brief Parse command line string with CommandOptions.
     ///
     /// \param cmdline command line string to be parsed.
@@ -624,7 +829,6 @@ public:
         CommandOptionsHelper::process(cmdline);
     }
 
-private:
     /// \brief Create DHCPv4 OFFER packet.
     ///
     /// \param transid transaction id.
@@ -645,8 +849,8 @@ private:
     ///
     /// \param transid transaction id.
     /// \return instance of the packet.
-    boost::shared_ptr<Pkt6>
-    createAdvertisePkt6(uint32_t transid) const {
+    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()
@@ -671,8 +875,54 @@ private:
         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);
+
+    }
+
 };
 
+// This test verifies that the class members are reset to expected values.
+TEST_F(TestControlTest, reset) {
+    ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l ethx -r 50 -f 30 -F 10 -a 3 all"));
+    NakedTestControl tc;
+    tc.reset();
+    EXPECT_EQ(3, tc.basic_rate_control_.getAggressivity());
+    EXPECT_EQ(3, tc.renew_rate_control_.getAggressivity());
+    EXPECT_EQ(3, tc.release_rate_control_.getAggressivity());
+    EXPECT_EQ(50, tc.basic_rate_control_.getRate());
+    EXPECT_EQ(30, tc.renew_rate_control_.getRate());
+    EXPECT_EQ(10, tc.release_rate_control_.getRate());
+    EXPECT_FALSE(tc.last_report_.is_not_a_date_time());
+    EXPECT_FALSE(tc.transid_gen_);
+    EXPECT_FALSE(tc.macaddr_gen_);
+    EXPECT_TRUE(tc.first_packet_serverid_.empty());
+    EXPECT_FALSE(tc.interrupted_);
+
+}
+
 TEST_F(TestControlTest, GenerateDuid) {
     // Simple command line that simulates one client only. Always the
     // same DUID will be generated.
@@ -1189,27 +1439,166 @@ TEST_F(TestControlTest, PacketTemplates) {
     EXPECT_THROW(tc.initPacketTemplates(), isc::BadValue);
 }
 
-TEST_F(TestControlTest, RateControl) {
-    // We don't specify the exchange rate here so the aggressivity
-    // value will determine how many packets are to be send each
-    // time we query the getNextExchangesNum.
-    ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 all"));
-    CommandOptions& options = CommandOptions::instance();
+TEST_F(TestControlTest, processRenew) {
+    testSendRenewRelease(DHCPV6_RENEW);
+}
 
-    NakedTestControl tc1;
-    uint64_t xchgs_num = tc1.getNextExchangesNum();
-    EXPECT_EQ(options.getAggressivity(), xchgs_num);
+TEST_F(TestControlTest, processRelease) {
+    testSendRenewRelease(DHCPV6_RELEASE);
+}
+
+// This test verifies that the DHCPV6 Renew message is created correctly
+// and that it comprises all required options.
+TEST_F(TestControlTest, createRenew) {
+    testCreateRenewRelease(DHCPV6_RENEW);
+}
+
+// This test verifies that the DHCPv6 Release message is created correctly
+// and that it comprises all required options.
+TEST_F(TestControlTest, createRelease) {
+    testCreateRenewRelease(DHCPV6_RELEASE);
+}
+
+// This test verifies that the current timeout value for waiting for
+// the server's responses is valid. The timeout value corresponds to the
+// time period between now and the next message to be sent from the
+// perfdhcp to a server.
+TEST_F(TestControlTest, getCurrentTimeout) {
+    // Process the command line: set the rate for Discovers to 10,
+    // and set Renew rate to 0 (-f flag absent).
+    ASSERT_NO_THROW(processCmdLine("perfdhcp -4 -l lo -r 10 ::1"));
+    NakedTestControl tc;
+    // Make sure that the renew rate is 0.
+    ASSERT_EQ(0, CommandOptions::instance().getRenewRate());
+    // Simulate the case when we are already behind the due time for
+    // the next Discover to be sent.
+    tc.setRelativeDueTimes(-3);
+    // Expected timeout value is 0, which means that perfdhcp should
+    // not wait for server's response but rather send the next
+    // message to a server immediately.
+    EXPECT_EQ(0, tc.getCurrentTimeout());
+    // Now, let's do set the due time to a value in the future. The returned
+    // timeout value should be somewhere between now and this time in the
+    // future. The value of ten seconds ahead should be safe and guarantee
+    // that the returned timeout value is non-zero, even though there is a
+    // delay between setting the send_due_ value and invoking the function.
+    tc.setRelativeDueTimes(10);
+    uint32_t timeout = tc.getCurrentTimeout();
+    EXPECT_GT(timeout, 0);
+    EXPECT_LE(timeout, 10000000);
+}
+
+// This test verifies that the current timeout value for waiting for the
+// server's responses is valid. In this case, we are simulating that perfdhcp
+// sends Renew requests to the server, apart from the regular 4-way exchanges.
+// The timeout value depends on both the due time to send next Solicit and the
+// due time to send Renew - the timeout should be ajusted to the due time that
+// occurs sooner.
+TEST_F(TestControlTest, getCurrentTimeoutRenew) {
+    // Set the Solicit rate to 10 and the Renew rate 5.
+    ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l lo -r 10 -f 5 ::1"));
+    NakedTestControl tc;
+
+    // Make sure, that the Renew rate has been set to 5.
+    ASSERT_EQ(5, CommandOptions::instance().getRenewRate());
+    // The send_due_ is in the past, the renew_due_ is in the future.
+    tc.setRelativeDueTimes(-3, 3);
+    EXPECT_EQ(0, tc.getCurrentTimeout());
+
+    // Swap the due times from the previous check. The effect should be the
+    // same.
+    tc.setRelativeDueTimes(3, -3);
+    EXPECT_EQ(0, tc.getCurrentTimeout());
+
+    // Set both due times to the future. The renew due time is to occur
+    // sooner. The timeout should be a value between now and the
+    // renew due time.
+    tc.setRelativeDueTimes(10, 5);
+    EXPECT_GT(tc.getCurrentTimeout(), 0);
+    EXPECT_LE(tc.getCurrentTimeout(), 5000000);
+
+    // Repeat the same check, but swap the due times.
+    tc.setRelativeDueTimes(5, 10);
+    EXPECT_GT(tc.getCurrentTimeout(), 0);
+    EXPECT_LE(tc.getCurrentTimeout(), 5000000);
+
+}
+
+// This test verifies that the current timeout value for waiting for the
+// server's responses is valid. In this case, we are simulating that perfdhcp
+// sends Release requests to the server, apart from the regular 4-way exchanges.
+TEST_F(TestControlTest, getCurrentTimeoutRelease) {
+    // Set the Solicit rate to 10 and the Release rate 5.
+    ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l lo -r 10 -F 5 ::1"));
+    NakedTestControl tc;
+
+    // Make sure, that the Release rate has been set to 5.
+    ASSERT_EQ(5, CommandOptions::instance().getReleaseRate());
+    // The send_due_ is in the past, the renew_due_ is in the future.
+    tc.setRelativeDueTimes(-3, 0, 3);
+    EXPECT_EQ(0, tc.getCurrentTimeout());
+
+    // Swap the due times from the previous check. The effect should be the
+    // same.
+    tc.setRelativeDueTimes(3, 0, -3);
+    EXPECT_EQ(0, tc.getCurrentTimeout());
+
+    // Set both due times to the future. The renew due time is to occur
+    // sooner. The timeout should be a value between now and the
+    // release due time.
+    tc.setRelativeDueTimes(10, 0, 5);
+    EXPECT_GT(tc.getCurrentTimeout(), 0);
+    EXPECT_LE(tc.getCurrentTimeout(), 5000000);
+
+    // Repeat the same check, but swap the due times.
+    tc.setRelativeDueTimes(5, 0, 10);
+    EXPECT_GT(tc.getCurrentTimeout(), 0);
+    EXPECT_LE(tc.getCurrentTimeout(), 5000000);
+
+}
+
+// This test verifies that the current timeout value for waiting for the
+// server's responses is valid. In this case, we are simulating that perfdhcp
+// sends both Renew and Release requests to the server, apart from the regular
+// 4-way exchanges.
+TEST_F(TestControlTest, getCurrentTimeoutRenewRelease) {
+    // Set the Solicit rate to 10 and, Renew rate to 5, Release rate to 3.
+    ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l lo -r 10 -f 5 -F 3 ::1"));
+    NakedTestControl tc;
+
+    // Make sure the Renew and Release rates has been set to a non-zero value.
+    ASSERT_EQ(5, CommandOptions::instance().getRenewRate());
+    ASSERT_EQ(3, CommandOptions::instance().getReleaseRate());
+
+    // If any of the due times is in the past, the timeout value should be 0,
+    // to indicate that the next message should be sent immediately.
+    tc.setRelativeDueTimes(-3, 3, 5);
+    EXPECT_EQ(0, tc.getCurrentTimeout());
+
+    tc.setRelativeDueTimes(-3, 5, 3);
+    EXPECT_EQ(0, tc.getCurrentTimeout());
+
+    tc.setRelativeDueTimes(3, -3, 5);
+    EXPECT_EQ(0, tc.getCurrentTimeout());
+
+    tc.setRelativeDueTimes(3, 2, -5);
+    EXPECT_EQ(0, tc.getCurrentTimeout());
+
+    tc.setRelativeDueTimes(-3, -2, -5);
+    EXPECT_EQ(0, tc.getCurrentTimeout());
+
+    // If due times are in the future, the timeout value should be aligned to
+    // the due time which occurs the soonest.
+    tc.setRelativeDueTimes(10, 9, 8);
+    EXPECT_GT(tc.getCurrentTimeout(), 0);
+    EXPECT_LE(tc.getCurrentTimeout(), 8000000);
+
+    tc.setRelativeDueTimes(10, 8, 9);
+    EXPECT_GT(tc.getCurrentTimeout(), 0);
+    EXPECT_LE(tc.getCurrentTimeout(), 8000000);
+
+    tc.setRelativeDueTimes(5, 8, 9);
+    EXPECT_GT(tc.getCurrentTimeout(), 0);
+    EXPECT_LE(tc.getCurrentTimeout(), 5000000);
 
-    // The exchange rate is now 1 per second. We don't know how many
-    // exchanges have to initiated exactly but for sure it has to be
-    // non-zero value. Also, since aggressivity is very high we expect
-    // that it will not be restricted by aggressivity.
-    ASSERT_NO_THROW(
-        processCmdLine("perfdhcp -l 127.0.0.1 -a 1000000 -r 1 all")
-    );
-    NakedTestControl tc2;
-    xchgs_num = tc2.getNextExchangesNum();
-    EXPECT_GT(xchgs_num, 0);
-    EXPECT_LT(xchgs_num, options.getAggressivity());
-    // @todo add more thorough checks for rate values.
 }



More information about the bind10-changes mailing list