BIND 10 trac1470, updated. ee21a81dbcfc6dc3587a9ca8ae95175c3b52bfea Merge branch 'master' into trac1470

BIND 10 source code commits bind10-changes at lists.isc.org
Mon Dec 19 14:04:55 UTC 2011


The branch, trac1470 has been updated
       via  ee21a81dbcfc6dc3587a9ca8ae95175c3b52bfea (commit)
       via  4feacbc10b4f22b6d8b82230ce88c95a391b82da (commit)
       via  2f0aa20b44604b671e6bde78815db39381e563bf (commit)
       via  8cabb13b990bd2536af98d5a73edaa31b0928f9d (commit)
       via  7b122af8489acf0f28f935a19eca2c5509a3677f (commit)
       via  f3db2f5329ae957c463e486d7fe4ab09aa0a30f0 (commit)
       via  884644953f7c356cc53ca975a61502ef77d6dcbe (commit)
       via  196b58b907f9069c004fbf2ac140bcfd59f960bc (commit)
       via  4d6719f99efecca28f65c792ac9933c5424c963a (commit)
       via  11b8542e2d8e8a2ffd03fcb10a4278b399e9353b (commit)
       via  2c4dcf9b5974e5b1e23b8670b984e0b4b4e6a625 (commit)
       via  740d33cd94f12b8c88e5dcb1e1640e87a97a499f (commit)
       via  3525f88d298f4e877c8fe6e59f5ce3f2f1ff9b47 (commit)
       via  bdd00552a920331dc3cf1f2e6a7644112caa4b72 (commit)
       via  ebb759a07c67f015305019aa34ff20f69218a094 (commit)
       via  b015cf738dc87f33f5f9e3c16bed1f19b1a18fc4 (commit)
       via  a243ee281b5bc65c750925e20dfdca87f8914e2a (commit)
       via  d394e64f4c44f16027b1e62b4ac34e054b49221d (commit)
       via  d75d622a266d7f0b8bddb9c4319acadcf0edac13 (commit)
       via  4d9485ae50a13a37c1c1c4bb1dd43998c83d5c69 (commit)
       via  8eb7bcd370b766d260cd69001f119e4dea1e87cd (commit)
       via  a5dbe4197bf7fc362784fd2d8227fd108d08602c (commit)
       via  e20572f93d2db7db1bfb3efaf08a068ca4416dc9 (commit)
       via  7ad41d91b28fa38bfa5dc07458da24b8459fdc2a (commit)
       via  5749a7ada244fbb7113883b4150c94b20d20d1e6 (commit)
       via  53bc298c40ab5386e88d751cf4c997df7c0eec96 (commit)
       via  e14cc5ec115df8f053ee0b4324e9be9b73feebe5 (commit)
       via  8cb640551a6fc4a101bdc4c6469630d0cbeb7076 (commit)
       via  ff81c97c2ad006e5e28f33e67d3a8bde1dccc523 (commit)
       via  afddaf4c5718c2a0cc31f2eee79c4e0cc625499f (commit)
       via  2f4433fb15900481e4fb976a93692e68084c3925 (commit)
       via  2d0aacaedbdc31e8afa5075ed6d77ea9a1db8920 (commit)
       via  d6e33479365c8f8f62ef2b9aa5548efe6b194601 (commit)
       via  497d53517cb412c6d5b90779bd06070e58f90df1 (commit)
       via  1ae5ff8616b9a2c97bfe8c3a9f5f7c65fcd7fc00 (commit)
       via  1655bed624866a766311a01214597db01b4c7cec (commit)
       via  23350205fb9b09ed188be0898b3f95efb34f74b1 (commit)
       via  ebfebb1be1968d33fb0054fe6df65aa90a085612 (commit)
       via  b14866797ef758fd4e3a920b8ca9eab50053e120 (commit)
       via  a5c940634fbc5dffd80ff2c0cfd98294322583f7 (commit)
       via  28f5b7df990e502695635c112d61ce5bbe088efd (commit)
       via  64546f4f97bf4032f7b97f768649ba1024503fc0 (commit)
       via  936855eb6fd5304128b737b72ca464747061a476 (commit)
       via  07d2c46d78d35556a3e83f490eaa1aee9563e494 (commit)
       via  f84f26374fd7f359eafd98a00c9065d2c1aaa924 (commit)
       via  7a5214fa8a316a8096509226ff498ee1c39ec2de (commit)
       via  697341a7b1e73b508d8142b8afc07767ea46f3f3 (commit)
       via  1cd21e93810b18a4aa90d7d20afa6dbc3c0a8861 (commit)
       via  7515af0e50af796b0b936e9a966eea5bff82dfe9 (commit)
       via  292db243f0d4e4036b265da8b9e5c01db2929f58 (commit)
       via  89601ee181490433adf058011d920befc5e38061 (commit)
       via  d8cd199a66645341270081a7f409c557e596099b (commit)
       via  d873ad02831294bdc9c23ebf3178fa0532f8b8c0 (commit)
       via  2e58ef22ff8fb2b2ef21d481205b4cf197aa1092 (commit)
       via  f27e984224d7dbb033c09205c2dd8e1e6579408f (commit)
       via  e1a683babf04b28b75599c797a00b8a277191844 (commit)
       via  582b0cc873a5cd5333236c2a58f30ca1e6562f5d (commit)
       via  932e38a12ca938afa18563e89efb5931dbd63576 (commit)
       via  55e89adf8bff6709a86b3331b809a47662b7a05b (commit)
       via  073cae7e8f0c72040eef17f49cce5593023237bc (commit)
       via  0f4dd0cf9c1ca4cc397954d639692a8946edb284 (commit)
       via  eb2e8615ae2ed35f9d70e632e970c42729853a19 (commit)
       via  ea709c77cdab1d2d91a923b913af869f865477bd (commit)
       via  7e2204f4a69fe0f1ce24ce36152577828c8ded79 (commit)
      from  ad1fd67bc72d8a050b58496fab79c4aeeeec1f44 (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 ee21a81dbcfc6dc3587a9ca8ae95175c3b52bfea
Merge: ad1fd67bc72d8a050b58496fab79c4aeeeec1f44 4feacbc10b4f22b6d8b82230ce88c95a391b82da
Author: Stephen Morris <stephen at isc.org>
Date:   Mon Dec 19 13:58:05 2011 +0000

    Merge branch 'master' into trac1470

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

Summary of changes:
 ChangeLog                                          |   48 ++-
 configure.ac                                       |    7 +-
 doc/guide/bind10-guide.xml                         |   69 ++-
 src/bin/Makefile.am                                |    2 +-
 src/bin/auth/Makefile.am                           |    1 +
 src/bin/auth/auth_srv.cc                           |    6 +-
 src/bin/auth/auth_srv.h                            |    2 +-
 src/bin/auth/benchmarks/Makefile.am                |    1 +
 src/bin/auth/statistics.cc                         |   61 +-
 src/bin/auth/statistics.h                          |   22 +-
 src/bin/auth/tests/Makefile.am                     |    1 +
 src/bin/auth/tests/auth_srv_unittest.cc            |   16 +-
 src/bin/auth/tests/statistics_unittest.cc          |   39 +-
 src/bin/cfgmgr/plugins/logging.spec                |    2 +-
 src/bin/dhcp4/Makefile.am                          |   43 ++
 src/bin/dhcp4/b10-dhcp4.8                          |   60 ++
 src/bin/dhcp4/b10-dhcp4.xml                        |   98 +++
 src/bin/dhcp4/dhcp4.spec                           |   14 +
 src/bin/dhcp4/dhcp4_srv.cc                         |  154 +++++
 src/bin/dhcp4/dhcp4_srv.h                          |  137 ++++
 src/bin/dhcp4/main.cc                              |  112 +++
 src/bin/dhcp4/spec_config.h.pre.in                 |   15 +
 src/bin/dhcp4/tests/Makefile.am                    |   46 ++
 src/bin/dhcp4/tests/dhcp4_srv_unittest.cc          |  161 +++++
 src/bin/dhcp4/tests/dhcp4_unittests.cc             |   28 +
 src/bin/dhcp6/Makefile.am                          |    5 +-
 src/bin/dhcp6/dhcp6.spec                           |    2 +-
 src/bin/dhcp6/dhcp6_srv.cc                         |    2 +-
 src/bin/dhcp6/iface_mgr.cc                         |  725 --------------------
 src/bin/dhcp6/iface_mgr.h                          |  414 -----------
 src/bin/dhcp6/tests/Makefile.am                    |    6 +-
 src/bin/dhcp6/tests/dhcp6_srv_unittest.cc          |    6 +-
 src/bin/dhcp6/tests/dhcp6_test.py                  |    8 +-
 src/bin/dhcp6/tests/iface_mgr_unittest.cc          |  518 --------------
 src/bin/xfrout/b10-xfrout.xml                      |   22 +-
 src/bin/zonemgr/b10-zonemgr.xml                    |   26 +-
 src/bin/zonemgr/tests/Makefile.am                  |    1 +
 src/bin/zonemgr/tests/zonemgr_test.py              |  136 +++--
 src/bin/zonemgr/zonemgr.py.in                      |   51 ++-
 src/lib/Makefile.am                                |    2 +-
 src/lib/cryptolink/tests/Makefile.am               |    2 +-
 src/lib/datasrc/database.cc                        |    2 +-
 src/lib/datasrc/tests/Makefile.am                  |   39 +-
 src/lib/dhcp/Makefile.am                           |   29 +-
 src/lib/dhcp/README                                |    4 +-
 src/lib/dhcp/iface_mgr.cc                          |  725 ++++++++++++++++++++
 src/lib/dhcp/iface_mgr.h                           |  413 +++++++++++
 src/lib/dhcp/libdhcp++.cc                          |  180 +++++
 src/lib/dhcp/{libdhcp.h => libdhcp++.h}            |    0 
 src/lib/dhcp/libdhcp.cc                            |  180 -----
 src/lib/dhcp/option.cc                             |    2 +-
 src/lib/dhcp/option6_addrlst.cc                    |    2 +-
 src/lib/dhcp/option6_ia.cc                         |    2 +-
 src/lib/dhcp/option6_iaaddr.cc                     |    2 +-
 src/lib/dhcp/pkt4.cc                               |    4 +-
 src/lib/dhcp/pkt4.h                                |   82 ++-
 src/lib/dhcp/pkt6.cc                               |    2 +-
 src/lib/dhcp/tests/Makefile.am                     |   48 +-
 src/lib/dhcp/tests/iface_mgr_unittest.cc           |  524 ++++++++++++++
 src/lib/dhcp/tests/libdhcp++_unittest.cc           |  234 +++++++
 src/lib/dhcp/tests/libdhcp_unittest.cc             |  234 -------
 src/lib/dhcp/tests/pkt4_unittest.cc                |   18 +-
 src/lib/log/output_option.h                        |    2 +-
 src/lib/log/tests/output_option_unittest.cc        |    2 +-
 src/lib/resolve/recursive_query.cc                 |    3 +
 src/lib/statistics/Makefile.am                     |   24 +
 src/lib/statistics/counter.cc                      |   68 ++
 src/lib/statistics/counter.h                       |   55 ++
 src/lib/statistics/counter_dict.cc                 |  251 +++++++
 src/lib/statistics/counter_dict.h                  |  145 ++++
 src/lib/statistics/tests/Makefile.am               |   47 ++
 src/lib/statistics/tests/counter_dict_unittest.cc  |  174 +++++
 src/lib/statistics/tests/counter_unittest.cc       |   85 +++
 src/lib/statistics/tests/run_unittests.cc          |   25 +
 tests/lettuce/README.tutorial                      |    6 +-
 .../configurations/ixfr-out/testset1-config.db     |    1 +
 tests/lettuce/data/ixfr-out/zones.slite3           |  Bin 0 -> 246784 bytes
 tests/lettuce/features/ixfr_out_bind10.feature     |  195 ++++++
 tests/lettuce/features/terrain/bind10_control.py   |   14 +
 tests/lettuce/features/terrain/querying.py         |    2 +-
 tests/lettuce/features/terrain/transfer.py         |  138 ++++
 tools/reorder_message_file.py                      |    2 +-
 82 files changed, 4675 insertions(+), 2361 deletions(-)
 create mode 100644 src/bin/dhcp4/Makefile.am
 create mode 100644 src/bin/dhcp4/b10-dhcp4.8
 create mode 100644 src/bin/dhcp4/b10-dhcp4.xml
 create mode 100644 src/bin/dhcp4/dhcp4.spec
 create mode 100644 src/bin/dhcp4/dhcp4_srv.cc
 create mode 100644 src/bin/dhcp4/dhcp4_srv.h
 create mode 100644 src/bin/dhcp4/main.cc
 create mode 100644 src/bin/dhcp4/spec_config.h.pre.in
 create mode 100644 src/bin/dhcp4/tests/Makefile.am
 create mode 100644 src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
 create mode 100644 src/bin/dhcp4/tests/dhcp4_unittests.cc
 delete mode 100644 src/bin/dhcp6/iface_mgr.cc
 delete mode 100644 src/bin/dhcp6/iface_mgr.h
 delete mode 100644 src/bin/dhcp6/tests/iface_mgr_unittest.cc
 create mode 100644 src/lib/dhcp/iface_mgr.cc
 create mode 100644 src/lib/dhcp/iface_mgr.h
 create mode 100644 src/lib/dhcp/libdhcp++.cc
 rename src/lib/dhcp/{libdhcp.h => libdhcp++.h} (100%)
 delete mode 100644 src/lib/dhcp/libdhcp.cc
 create mode 100644 src/lib/dhcp/tests/iface_mgr_unittest.cc
 create mode 100644 src/lib/dhcp/tests/libdhcp++_unittest.cc
 delete mode 100644 src/lib/dhcp/tests/libdhcp_unittest.cc
 create mode 100644 src/lib/statistics/Makefile.am
 create mode 100644 src/lib/statistics/counter.cc
 create mode 100644 src/lib/statistics/counter.h
 create mode 100644 src/lib/statistics/counter_dict.cc
 create mode 100644 src/lib/statistics/counter_dict.h
 create mode 100644 src/lib/statistics/tests/Makefile.am
 create mode 100644 src/lib/statistics/tests/counter_dict_unittest.cc
 create mode 100644 src/lib/statistics/tests/counter_unittest.cc
 create mode 100644 src/lib/statistics/tests/run_unittests.cc
 create mode 100644 tests/lettuce/configurations/ixfr-out/testset1-config.db
 create mode 100644 tests/lettuce/data/ixfr-out/zones.slite3
 create mode 100644 tests/lettuce/features/ixfr_out_bind10.feature
 create mode 100644 tests/lettuce/features/terrain/transfer.py

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index c59c9ec..44421f2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,42 @@
+348.	[bug]		stephen
+	By default the logging output stream is now flushed after each write.
+	This fixes a problem seen on some systems where the log output from
+	different processes was jumbled up.  Flushing can be disabled by setting
+	the appropriate option in the logging configuration.
+	(Trac #1405, git 2f0aa20b44604b671e6bde78815db39381e563bf)
+
+347.	[bug]		jelte
+	Fixed a bug where adding Zonemgr/secondary_zones without explicitely
+	setting the class value of the added zone resulted in a cryptic
+	error in bindctl ("Error: class"). It will now correctly default to
+	IN if not set. This also adds better checks on the name and class
+	values, and better errors if they are bad.
+	(Trac #1414, git 7b122af8489acf0f28f935a19eca2c5509a3677f)
+
+346.	[build]*		jreed
+	Renamed libdhcp to libdhcp++.
+	(Trac #1446, git d394e64f4c44f16027b1e62b4ac34e054b49221d)
+
+345.	[func]		tomek
+	dhcp4: Dummy DHCPv4 component implemented. Currently it does
+	nothing useful, except providing skeleton implementation that can
+	be expanded in the future.
+	(Trac #992, git d6e33479365c8f8f62ef2b9aa5548efe6b194601)
+
+344.	[func]		y-aharen
+	src/lib/statistics: Added statistics counter library for entire server
+	items and per zone items. Also, modified b10-auth to use it. It is
+	also intended to use in the other modules such as b10-resolver.
+	(Trac #510, git afddaf4c5718c2a0cc31f2eee79c4e0cc625499f)
+
+343.	[func]		jelte
+	Added IXFR-out system tests, based on the first two test sets of
+	http://bind10.isc.org/wiki/IxfrSystemTests.
+	(Trac #1314, git 1655bed624866a766311a01214597db01b4c7cec)
+
 342.	[bug]		stephen
 	In the resolver, a FORMERR received from an upstream nameserver
-	now rsults in a SERVFAIL being returned as a response to the original
+	now results in a SERVFAIL being returned as a response to the original
 	query.  Additional debug messages added to distinguish between
 	different errors in packets received from upstream nameservers.
 	(Trac #1383, git 9b2b249d23576c999a65d8c338e008cabe45f0c9)
@@ -69,12 +105,12 @@
 	potential problems and were fixed.
 	(Trac #1389, git 3fdce88046bdad392bd89ea656ec4ac3c858ca2f)
 
-333.    [bug]		dvv
-	Solaris needs "-z now" to force non-lazy binding and prevent g++ static
-	initialization code from deadlocking.
+333.	[bug]		dvv
+	Solaris needs "-z now" to force non-lazy binding and prevent
+	g++ static initialization code from deadlocking.
 	(Trac #1439, git c789138250b33b6b08262425a08a2a0469d90433)
 
-332.    [bug]		vorner
+332.	[bug]		vorner
 	C++ exceptions in the isc.dns.Rdata wrapper are now converted
 	to python ones instead of just aborting the interpretter.
 	(Trac #1407, git 5b64e839be2906b8950f5b1e42a3fadd72fca033)
@@ -109,7 +145,7 @@ bind10-devel-20111128 released on November 28, 2011
 	always respond to IXFR requests according to RFC1995).
 	(Trac #1371 and #1372, git 80c131f5b0763753d199b0fb9b51f10990bcd92b)
 
-326.	[build]*	jinmei
+326.	[build]*		jinmei
 	Added a check script for the SQLite3 schema version.  It will be
 	run at the beginning of 'make install', and if it detects an old
 	version of schema, installation will stop.  You'll then need to
diff --git a/configure.ac b/configure.ac
index e370e21..671a9b6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -730,7 +730,7 @@ then
 				GTEST_FOUND="true"
 				# There is no gtest-config script on this
 				# system, which is supposed to inform us
-				# whether we need pthreads as well (a 
+				# whether we need pthreads as well (a
 				# gtest compile-time option). So we still
 				# need to test that manually.
 				CPPFLAGS_SAVED="$CPPFLAGS"
@@ -892,6 +892,8 @@ AC_CONFIG_FILES([Makefile
                  src/bin/auth/benchmarks/Makefile
                  src/bin/dhcp6/Makefile
                  src/bin/dhcp6/tests/Makefile
+		 src/bin/dhcp4/Makefile
+		 src/bin/dhcp4/tests/Makefile
                  src/bin/resolver/Makefile
                  src/bin/resolver/tests/Makefile
                  src/bin/sockcreator/Makefile
@@ -984,6 +986,8 @@ AC_CONFIG_FILES([Makefile
                  src/lib/util/tests/Makefile
                  src/lib/acl/Makefile
                  src/lib/acl/tests/Makefile
+                 src/lib/statistics/Makefile
+                 src/lib/statistics/tests/Makefile
                  tests/Makefile
                  tests/system/Makefile
                  tests/tools/Makefile
@@ -1031,6 +1035,7 @@ AC_OUTPUT([doc/version.ent
            src/bin/msgq/run_msgq.sh
            src/bin/auth/auth.spec.pre
            src/bin/auth/spec_config.h.pre
+           src/bin/dhcp4/spec_config.h.pre
            src/bin/dhcp6/spec_config.h.pre
            src/bin/tests/process_rename_test.py
            src/lib/config/tests/data_def_unittests_config.h
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index e61725f..b186111 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -1502,6 +1502,49 @@ what if a NOTIFY is sent?
 
 -->
 
+    <section id="zonemgr">
+      <title>Secondary Manager</title>
+
+      <para>
+        The <command>b10-zonemgr</command> process is started by
+        <command>bind10</command>.
+        It keeps track of SOA refresh, retry, and expire timers
+        and other details for BIND 10 to perform as a slave.
+        When the <command>b10-auth</command> authoritative DNS server
+        receives a NOTIFY message, <command>b10-zonemgr</command>
+        may tell <command>b10-xfrin</command> to do a refresh
+        to start an inbound zone transfer.
+        The secondary manager resets its counters when a new zone is
+        transferred in.
+      </para>
+
+      <note><simpara>
+        Access control (such as allowing notifies) is not yet provided.
+        The primary/secondary service is not yet complete.
+      </simpara></note>
+
+      <para>
+        The following example shows using <command>bindctl</command>
+        to configure the server to be a secondary for the example zone:
+
+      <screen>> <userinput>config add Zonemgr/secondary_zones</userinput>
+> <userinput>config set Zonemgr/secondary_zones[0]/name "<option>example.com</option>"</userinput>
+> <userinput>config set Zonemgr/secondary_zones[0]/class "<option>IN</option>"</userinput>
+> <userinput>config commit</userinput></screen>
+
+<!-- TODO: remove the IN class example above when it is the default -->
+
+      </para>
+
+      <para>
+        If the zone does not exist in the data source already
+        (i.e. no SOA record for it), <command>b10-zonemgr</command>
+        will automatically tell <command>b10-xfrin</command>
+        to transfer the zone in.
+      </para>
+
+    </section>
+
     <section>
       <title>Trigger an Incoming Zone Transfer Manually</title>
 
@@ -1514,7 +1557,6 @@ what if a NOTIFY is sent?
       </para>
     </section>
 
-
 <!-- TODO: can that retransfer be used to identify a new zone? -->
 <!-- TODO: what if doesn't exist at that master IP? -->
 
@@ -1606,31 +1648,6 @@ what is XfroutClient xfr_client??
 
   </chapter>
 
-  <chapter id="zonemgr">
-    <title>Secondary Manager</title>
-
-    <para>
-      The <command>b10-zonemgr</command> process is started by
-      <command>bind10</command>.
-      It keeps track of SOA refresh, retry, and expire timers
-      and other details for BIND 10 to perform as a slave.
-      When the <command>b10-auth</command> authoritative DNS server
-      receives a NOTIFY message, <command>b10-zonemgr</command>
-      may tell <command>b10-xfrin</command> to do a refresh
-      to start an inbound zone transfer.
-      The secondary manager resets its counters when a new zone is
-      transferred in.
-    </para>
-
-    <note><simpara>
-     Access control (such as allowing notifies) is not yet provided.
-     The primary/secondary service is not yet complete.
-    </simpara></note>
-
-<!-- TODO: lots to describe for zonemgr -->
-
-  </chapter>
-
   <chapter id="resolverserver">
     <title>Recursive Name Server</title>
 
diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am
index 06d8df2..ba7ae81 100644
--- a/src/bin/Makefile.am
+++ b/src/bin/Makefile.am
@@ -1,4 +1,4 @@
 SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout \
-	usermgr zonemgr stats tests resolver sockcreator dhcp6 
+	usermgr zonemgr stats tests resolver sockcreator dhcp6 dhcp4
 
 check-recursive: all-recursive
diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am
index 4d8ec83..3d60432 100644
--- a/src/bin/auth/Makefile.am
+++ b/src/bin/auth/Makefile.am
@@ -71,6 +71,7 @@ b10_auth_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 b10_auth_LDADD += $(top_builddir)/src/lib/log/liblog.la
 b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 b10_auth_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
+b10_auth_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
 b10_auth_LDADD += $(SQLITE_LIBS)
 
 # TODO: config.h.in is wrong because doesn't honor pkgdatadir
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index d9eda8d..9c5e594 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -671,9 +671,9 @@ void
 AuthSrvImpl::incCounter(const int protocol) {
     // Increment query counter.
     if (protocol == IPPROTO_UDP) {
-        counters_.inc(AuthCounters::COUNTER_UDP_QUERY);
+        counters_.inc(AuthCounters::SERVER_UDP_QUERY);
     } else if (protocol == IPPROTO_TCP) {
-        counters_.inc(AuthCounters::COUNTER_TCP_QUERY);
+        counters_.inc(AuthCounters::SERVER_TCP_QUERY);
     } else {
         // unknown protocol
         isc_throw(Unexpected, "Unknown protocol: " << protocol);
@@ -766,7 +766,7 @@ bool AuthSrv::submitStatistics() const {
 }
 
 uint64_t
-AuthSrv::getCounter(const AuthCounters::CounterType type) const {
+AuthSrv::getCounter(const AuthCounters::ServerCounterType type) const {
     return (impl_->counters_.getCounter(type));
 }
 
diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h
index f2259a2..a50e427 100644
--- a/src/bin/auth/auth_srv.h
+++ b/src/bin/auth/auth_srv.h
@@ -343,7 +343,7 @@ public:
     /// \param type Type of a counter to get the value of
     ///
     /// \return the value of the counter.
-    uint64_t getCounter(const AuthCounters::CounterType type) const;
+    uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
 
     /**
      * \brief Set and get the addresses we listen on.
diff --git a/src/bin/auth/benchmarks/Makefile.am b/src/bin/auth/benchmarks/Makefile.am
index dd00ea5..da6a5c8 100644
--- a/src/bin/auth/benchmarks/Makefile.am
+++ b/src/bin/auth/benchmarks/Makefile.am
@@ -35,5 +35,6 @@ query_bench_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 query_bench_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 query_bench_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
+query_bench_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
 query_bench_LDADD += $(SQLITE_LIBS)
 
diff --git a/src/bin/auth/statistics.cc b/src/bin/auth/statistics.cc
index e62719f..7397a50 100644
--- a/src/bin/auth/statistics.cc
+++ b/src/bin/auth/statistics.cc
@@ -18,48 +18,67 @@
 #include <cc/data.h>
 #include <cc/session.h>
 
+#include <statistics/counter.h>
+#include <statistics/counter_dict.h>
+
 #include <sstream>
 #include <iostream>
 
+#include <boost/noncopyable.hpp>
+
 using namespace isc::auth;
+using namespace isc::statistics;
 
 // TODO: We need a namespace ("auth_server"?) to hold
 // AuthSrv and AuthCounters.
 
-class AuthCountersImpl {
-private:
-    // prohibit copy
-    AuthCountersImpl(const AuthCountersImpl& source);
-    AuthCountersImpl& operator=(const AuthCountersImpl& source);
+// TODO: Make use of wrappers like isc::dns::Opcode
+// for counter item type.
+
+class AuthCountersImpl : boost::noncopyable {
 public:
     AuthCountersImpl();
     ~AuthCountersImpl();
-    void inc(const AuthCounters::CounterType type);
+    void inc(const AuthCounters::ServerCounterType type);
+    void inc(const std::string& zone,
+             const AuthCounters::PerZoneCounterType type);
     bool submitStatistics() const;
     void setStatisticsSession(isc::cc::AbstractSession* statistics_session);
     void registerStatisticsValidator
     (AuthCounters::validator_type validator);
     // Currently for testing purpose only
-    uint64_t getCounter(const AuthCounters::CounterType type) const;
+    uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
 private:
-    std::vector<uint64_t> counters_;
+    Counter server_counter_;
+    CounterDictionary per_zone_counter_;
     isc::cc::AbstractSession* statistics_session_;
     AuthCounters::validator_type validator_;
 };
 
 AuthCountersImpl::AuthCountersImpl() :
     // initialize counter
-    // size: AuthCounters::COUNTER_TYPES, initial value: 0
-    counters_(AuthCounters::COUNTER_TYPES, 0),
+    // size of server_counter_: AuthCounters::SERVER_COUNTER_TYPES
+    // size of per_zone_counter_: AuthCounters::PER_ZONE_COUNTER_TYPES
+    server_counter_(AuthCounters::SERVER_COUNTER_TYPES),
+    per_zone_counter_(AuthCounters::PER_ZONE_COUNTER_TYPES),
     statistics_session_(NULL)
-{}
+{
+    per_zone_counter_.addElement("_SERVER_");
+}
 
 AuthCountersImpl::~AuthCountersImpl()
 {}
 
 void
-AuthCountersImpl::inc(const AuthCounters::CounterType type) {
-    ++counters_.at(type);
+AuthCountersImpl::inc(const AuthCounters::ServerCounterType type) {
+    server_counter_.inc(type);
+}
+
+void
+AuthCountersImpl::inc(const std::string& zone,
+                      const AuthCounters::PerZoneCounterType type)
+{
+    per_zone_counter_[zone].inc(type);
 }
 
 bool
@@ -73,9 +92,9 @@ AuthCountersImpl::submitStatistics() const {
                       <<   "{ \"owner\": \"Auth\","
                       <<   "  \"data\":"
                       <<     "{ \"queries.udp\": "
-                      <<     counters_.at(AuthCounters::COUNTER_UDP_QUERY)
+                      <<     server_counter_.get(AuthCounters::SERVER_UDP_QUERY)
                       <<     ", \"queries.tcp\": "
-                      <<     counters_.at(AuthCounters::COUNTER_TCP_QUERY)
+                      <<     server_counter_.get(AuthCounters::SERVER_TCP_QUERY)
                       <<   " }"
                       <<   "}"
                       << "]}";
@@ -126,19 +145,17 @@ AuthCountersImpl::registerStatisticsValidator
 
 // Currently for testing purpose only
 uint64_t
-AuthCountersImpl::getCounter(const AuthCounters::CounterType type) const {
-    return (counters_.at(type));
+AuthCountersImpl::getCounter(const AuthCounters::ServerCounterType type) const {
+    return (server_counter_.get(type));
 }
 
 AuthCounters::AuthCounters() : impl_(new AuthCountersImpl())
 {}
 
-AuthCounters::~AuthCounters() {
-    delete impl_;
-}
+AuthCounters::~AuthCounters() {}
 
 void
-AuthCounters::inc(const AuthCounters::CounterType type) {
+AuthCounters::inc(const AuthCounters::ServerCounterType type) {
     impl_->inc(type);
 }
 
@@ -155,7 +172,7 @@ AuthCounters::setStatisticsSession
 }
 
 uint64_t
-AuthCounters::getCounter(const AuthCounters::CounterType type) const {
+AuthCounters::getCounter(const AuthCounters::ServerCounterType type) const {
     return (impl_->getCounter(type));
 }
 
diff --git a/src/bin/auth/statistics.h b/src/bin/auth/statistics.h
index c930414..280b4a5 100644
--- a/src/bin/auth/statistics.h
+++ b/src/bin/auth/statistics.h
@@ -17,6 +17,7 @@
 
 #include <cc/session.h>
 #include <stdint.h>
+#include <boost/scoped_ptr.hpp>
 
 class AuthCountersImpl;
 
@@ -51,13 +52,18 @@ class AuthCountersImpl;
 /// \todo Consider overhead of \c AuthCounters::inc()
 class AuthCounters {
 private:
-    AuthCountersImpl* impl_;
+    boost::scoped_ptr<AuthCountersImpl> impl_;
 public:
     // Enum for the type of counter
-    enum CounterType {
-        COUNTER_UDP_QUERY = 0,  ///< COUNTER_UDP_QUERY: counter for UDP queries
-        COUNTER_TCP_QUERY = 1,  ///< COUNTER_TCP_QUERY: counter for TCP queries
-        COUNTER_TYPES = 2 ///< The number of defined counters
+    enum ServerCounterType {
+        SERVER_UDP_QUERY,       ///< SERVER_UDP_QUERY: counter for UDP queries
+        SERVER_TCP_QUERY,       ///< SERVER_TCP_QUERY: counter for TCP queries
+        SERVER_COUNTER_TYPES    ///< The number of defined counters
+    };
+    enum PerZoneCounterType {
+        ZONE_UDP_QUERY,         ///< ZONE_UDP_QUERY: counter for UDP queries
+        ZONE_TCP_QUERY,         ///< ZONE_TCP_QUERY: counter for TCP queries
+        PER_ZONE_COUNTER_TYPES  ///< The number of defined counters
     };
     /// The constructor.
     ///
@@ -77,9 +83,9 @@ public:
     ///
     /// \throw std::out_of_range \a type is unknown.
     ///
-    /// usage: counter.inc(CounterType::COUNTER_UDP_QUERY);
+    /// usage: counter.inc(AuthCounters::SERVER_UDP_QUERY);
     /// 
-    void inc(const CounterType type);
+    void inc(const ServerCounterType type);
 
     /// \brief Submit statistics counters to statistics module.
     ///
@@ -130,7 +136,7 @@ public:
     ///
     /// \return the value of the counter specified by \a type.
     ///
-    uint64_t getCounter(const AuthCounters::CounterType type) const;
+    uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
 
     /// \brief A type of validation function for the specification in
     /// isc::config::ModuleSpec.
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am
index d27386e..b5b96d7 100644
--- a/src/bin/auth/tests/Makefile.am
+++ b/src/bin/auth/tests/Makefile.am
@@ -65,6 +65,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index d938df9..329a2dc 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -778,7 +778,7 @@ TEST_F(AuthSrvTest, cacheSlots) {
 // Submit UDP normal query and check query counter
 TEST_F(AuthSrvTest, queryCounterUDPNormal) {
     // The counter should be initialized to 0.
-    EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
+    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
     // Create UDP message and process.
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                        default_qid, Name("example.com"),
@@ -787,13 +787,13 @@ TEST_F(AuthSrvTest, queryCounterUDPNormal) {
     server.processMessage(*io_message, parse_message, response_obuffer,
                           &dnsserv);
     // After processing UDP query, the counter should be 1.
-    EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
+    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
 }
 
 // Submit TCP normal query and check query counter
 TEST_F(AuthSrvTest, queryCounterTCPNormal) {
     // The counter should be initialized to 0.
-    EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
     // Create TCP message and process.
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                        default_qid, Name("example.com"),
@@ -802,13 +802,13 @@ TEST_F(AuthSrvTest, queryCounterTCPNormal) {
     server.processMessage(*io_message, parse_message, response_obuffer,
                           &dnsserv);
     // After processing TCP query, the counter should be 1.
-    EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
 }
 
 // Submit TCP AXFR query and check query counter
 TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
     // The counter should be initialized to 0.
-    EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
     UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                          Name("example.com"), RRClass::IN(), RRType::AXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
@@ -817,13 +817,13 @@ TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
     server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
     // After processing TCP AXFR query, the counter should be 1.
-    EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
 }
 
 // Submit TCP IXFR query and check query counter
 TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
     // The counter should be initialized to 0.
-    EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
     UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                          Name("example.com"), RRClass::IN(), RRType::IXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
@@ -832,7 +832,7 @@ TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
     server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
     // After processing TCP IXFR query, the counter should be 1.
-    EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
 }
 
 // class for queryCounterUnexpected test
diff --git a/src/bin/auth/tests/statistics_unittest.cc b/src/bin/auth/tests/statistics_unittest.cc
index 98e573b..3f19f91 100644
--- a/src/bin/auth/tests/statistics_unittest.cc
+++ b/src/bin/auth/tests/statistics_unittest.cc
@@ -150,25 +150,24 @@ AuthCountersTest::MockSession::setThrowSessionTimeout(bool flag) {
 
 TEST_F(AuthCountersTest, incrementUDPCounter) {
     // The counter should be initialized to 0.
-    EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY));
-    EXPECT_NO_THROW(counters.inc(AuthCounters::COUNTER_UDP_QUERY));
+    EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
+    EXPECT_NO_THROW(counters.inc(AuthCounters::SERVER_UDP_QUERY));
     // After increment, the counter should be 1.
-    EXPECT_EQ(1, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY));
+    EXPECT_EQ(1, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
 }
 
 TEST_F(AuthCountersTest, incrementTCPCounter) {
     // The counter should be initialized to 0.
-    EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY));
-    EXPECT_NO_THROW(counters.inc(AuthCounters::COUNTER_TCP_QUERY));
+    EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
+    EXPECT_NO_THROW(counters.inc(AuthCounters::SERVER_TCP_QUERY));
     // After increment, the counter should be 1.
-    EXPECT_EQ(1, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+    EXPECT_EQ(1, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
 }
 
 TEST_F(AuthCountersTest, incrementInvalidCounter) {
-    // Expect to throw isc::InvalidParameter if the type of the counter is
-    // invalid.
-    EXPECT_THROW(counters.inc(AuthCounters::COUNTER_TYPES),
-                 std::out_of_range);
+    // Expect to throw an isc::OutOfRange
+    EXPECT_THROW(counters.inc(AuthCounters::SERVER_COUNTER_TYPES),
+                 isc::OutOfRange);
 }
 
 TEST_F(AuthCountersTest, submitStatisticsWithoutSession) {
@@ -195,14 +194,14 @@ TEST_F(AuthCountersTest, submitStatisticsWithoutValidator) {
     // Validate if it submits correct data.
 
     // Counters should be initialized to 0.
-    EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY));
-    EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+    EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
+    EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
 
     // UDP query counter is set to 2.
-    counters.inc(AuthCounters::COUNTER_UDP_QUERY);
-    counters.inc(AuthCounters::COUNTER_UDP_QUERY);
+    counters.inc(AuthCounters::SERVER_UDP_QUERY);
+    counters.inc(AuthCounters::SERVER_UDP_QUERY);
     // TCP query counter is set to 1.
-    counters.inc(AuthCounters::COUNTER_TCP_QUERY);
+    counters.inc(AuthCounters::SERVER_TCP_QUERY);
     counters.submitStatistics();
 
     // Destination is "Stats".
@@ -237,14 +236,14 @@ TEST_F(AuthCountersTest, submitStatisticsWithValidator) {
     counters.registerStatisticsValidator(validator);
 
     // Counters should be initialized to 0.
-    EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY));
-    EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+    EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
+    EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
 
     // UDP query counter is set to 2.
-    counters.inc(AuthCounters::COUNTER_UDP_QUERY);
-    counters.inc(AuthCounters::COUNTER_UDP_QUERY);
+    counters.inc(AuthCounters::SERVER_UDP_QUERY);
+    counters.inc(AuthCounters::SERVER_UDP_QUERY);
     // TCP query counter is set to 1.
-    counters.inc(AuthCounters::COUNTER_TCP_QUERY);
+    counters.inc(AuthCounters::SERVER_TCP_QUERY);
 
     // checks the value returned by submitStatistics
     EXPECT_TRUE(counters.submitStatistics());
diff --git a/src/bin/cfgmgr/plugins/logging.spec b/src/bin/cfgmgr/plugins/logging.spec
index e377b0e..da20ce5 100644
--- a/src/bin/cfgmgr/plugins/logging.spec
+++ b/src/bin/cfgmgr/plugins/logging.spec
@@ -57,7 +57,7 @@
                       { "item_name": "flush",
                         "item_type": "boolean",
                         "item_optional": false,
-                        "item_default": false
+                        "item_default": true
                       },
                       { "item_name": "maxsize",
                         "item_type": "integer",
diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am
new file mode 100644
index 0000000..513ae1c
--- /dev/null
+++ b/src/bin/dhcp4/Makefile.am
@@ -0,0 +1,43 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+
+CLEANFILES = spec_config.h
+
+man_MANS = b10-dhcp4.8
+EXTRA_DIST = $(man_MANS) dhcp4.spec
+
+if ENABLE_MAN
+
+b10-dhcp4.8: b10-dhcp4.xml
+	xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-dhcp4.xml
+
+endif
+
+spec_config.h: spec_config.h.pre
+	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
+
+BUILT_SOURCES = spec_config.h
+pkglibexec_PROGRAMS = b10-dhcp4
+
+b10_dhcp4_SOURCES = main.cc dhcp4_srv.cc dhcp4_srv.h
+
+b10_dhcp4_LDADD = $(top_builddir)/src/lib/dhcp/libdhcp++.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/liblog.la
+
+# TODO: config.h.in is wrong because doesn't honor pkgdatadir
+# and can't use @datadir@ because doesn't expand default ${prefix}
+b10_dhcp4dir = $(pkgdatadir)
+b10_dhcp4_DATA = dhcp4.spec
diff --git a/src/bin/dhcp4/b10-dhcp4.8 b/src/bin/dhcp4/b10-dhcp4.8
new file mode 100644
index 0000000..97bdeb8
--- /dev/null
+++ b/src/bin/dhcp4/b10-dhcp4.8
@@ -0,0 +1,60 @@
+'\" t
+.\"     Title: b10-dhcp4
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
+.\"      Date: October 27, 2011
+.\"    Manual: BIND10
+.\"    Source: BIND10
+.\"  Language: English
+.\"
+.TH "B10\-DHCP4" "8" "October 27, 2011" "BIND10" "BIND10"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+b10-dhcp4 \- DHCPv4 server in BIND 10 architecture
+.SH "SYNOPSIS"
+.HP \w'\fBb10\-dhcp4\fR\ 'u
+\fBb10\-dhcp4\fR [\fB\-v\fR]
+.SH "DESCRIPTION"
+.PP
+The
+\fBb10\-dhcp4\fR
+daemon will provide the DHCPv4 server implementation when it becomes functional\&.
+.SH "ARGUMENTS"
+.PP
+The arguments are as follows:
+.PP
+\fB\-v\fR
+.RS 4
+Enable verbose mode\&.
+.RE
+.SH "SEE ALSO"
+.PP
+
+\fBbind10\fR(8)\&.
+.SH "HISTORY"
+.PP
+The
+\fBb10\-dhcp4\fR
+daemon was first coded in November 2011 by Tomek Mrugalski\&.
+.SH "COPYRIGHT"
+.br
+Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC")
+.br
diff --git a/src/bin/dhcp4/b10-dhcp4.xml b/src/bin/dhcp4/b10-dhcp4.xml
new file mode 100644
index 0000000..370fa04
--- /dev/null
+++ b/src/bin/dhcp4/b10-dhcp4.xml
@@ -0,0 +1,98 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+	       [<!ENTITY mdash "—">]>
+<!--
+ - Copyright (C) 2011  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.
+-->
+
+<refentry>
+
+  <refentryinfo>
+    <date>October 27, 2011</date>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>b10-dhcp4</refentrytitle>
+    <manvolnum>8</manvolnum>
+    <refmiscinfo>BIND10</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>b10-dhcp4</refname>
+    <refpurpose>DHCPv4 server in BIND 10 architecture</refpurpose>
+  </refnamediv>
+
+  <docinfo>
+    <copyright>
+      <year>2011</year>
+      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+    </copyright>
+  </docinfo>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>b10-dhcp4</command>
+      <arg><option>-v</option></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+    <para>
+      The <command>b10-dhcp4</command> daemon will provide the
+       DHCPv4 server implementation when it becomes functional.
+    </para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>ARGUMENTS</title>
+
+    <para>The arguments are as follows:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><option>-v</option></term>
+        <listitem><para>
+          Enable verbose mode.
+<!-- TODO: what does this do? -->
+        </para></listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+    <para>
+      <citerefentry>
+        <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>HISTORY</title>
+    <para>
+      The <command>b10-dhcp4</command> daemon was first coded in
+      November 2011 by Tomek Mrugalski.
+    </para>
+  </refsect1>
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->
diff --git a/src/bin/dhcp4/dhcp4.spec b/src/bin/dhcp4/dhcp4.spec
new file mode 100644
index 0000000..8061fd2
--- /dev/null
+++ b/src/bin/dhcp4/dhcp4.spec
@@ -0,0 +1,14 @@
+{
+  "module_spec": {
+    "module_name": "dhcp4",
+    "module_description": "DHCPv4 server daemon",
+    "config_data": [
+      { "item_name": "interface",
+        "item_type": "string",
+        "item_optional": false,
+        "item_default": "eth0"
+      }
+    ],
+    "commands": []
+  }
+}
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
new file mode 100644
index 0000000..9686a35
--- /dev/null
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -0,0 +1,154 @@
+// Copyright (C) 2011  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/pkt4.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp4/dhcp4_srv.h>
+#include <asiolink/io_address.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+Dhcpv4Srv::Dhcpv4Srv(uint16_t port) {
+    cout << "Initialization: opening sockets on port " << port << endl;
+
+    // first call to instance() will create IfaceMgr (it's a singleton)
+    // it may throw something if things go wrong
+    IfaceMgr::instance();
+
+    /// @todo: instantiate LeaseMgr here once it is imlpemented.
+
+    setServerID();
+
+    shutdown_ = false;
+}
+
+Dhcpv4Srv::~Dhcpv4Srv() {
+    cout << "DHCPv4 server shutdown." << endl;
+}
+
+bool
+Dhcpv4Srv::run() {
+    while (!shutdown_) {
+        boost::shared_ptr<Pkt4> query; // client's message
+        boost::shared_ptr<Pkt4> rsp;   // server's response
+
+#if 0
+        // uncomment this once ticket 1239 is merged.
+        query = IfaceMgr::instance().receive4();
+#endif
+
+        if (query) {
+            if (!query->unpack()) {
+                cout << "Failed to parse incoming packet" << endl;
+                continue;
+            }
+            switch (query->getType()) {
+            case DHCPDISCOVER:
+                rsp = processDiscover(query);
+                break;
+            case DHCPREQUEST:
+                rsp = processRequest(query);
+                break;
+            case DHCPRELEASE:
+                processRelease(query);
+                break;
+            case DHCPDECLINE:
+                processDecline(query);
+                break;
+            case DHCPINFORM:
+                processInform(query);
+                break;
+            default:
+                cout << "Unknown pkt type received:"
+                     << query->getType() << endl;
+            }
+
+            cout << "Received " << query->len() << " bytes packet type="
+                 << query->getType() << endl;
+
+            // TODO: print out received packets only if verbose (or debug)
+            // mode is enabled
+            cout << query->toText();
+
+            if (rsp) {
+                rsp->setRemoteAddr(query->getRemoteAddr());
+                rsp->setLocalAddr(query->getLocalAddr());
+                rsp->setRemotePort(DHCP4_CLIENT_PORT);
+                rsp->setLocalPort(DHCP4_SERVER_PORT);
+                rsp->setIface(query->getIface());
+                rsp->setIndex(query->getIndex());
+
+                cout << "Replying with:" << rsp->getType() << endl;
+                cout << rsp->toText();
+                cout << "----" << endl;
+                if (rsp->pack()) {
+                    cout << "Packet assembled correctly." << endl;
+                }
+#if 0
+                // uncomment this once ticket 1240 is merged.
+                IfaceMgr::instance().send4(rsp);
+#endif
+            }
+        }
+
+        // TODO add support for config session (see src/bin/auth/main.cc)
+        //      so this daemon can be controlled from bob
+    }
+
+    return (true);
+}
+
+void
+Dhcpv4Srv::setServerID() {
+    /// TODO implement this for real once interface detection (ticket 1237)
+    /// is done. Use hardcoded server-id for now.
+
+#if 0
+    // uncomment this once ticket 1350 is merged.
+    IOAddress srvId("127.0.0.1");
+    serverid_ = boost::shared_ptr<Option>(
+      new Option4AddrLst(Option::V4, DHO_DHCP_SERVER_IDENTIFIER, srvId));
+#endif
+}
+
+boost::shared_ptr<Pkt4>
+Dhcpv4Srv::processDiscover(boost::shared_ptr<Pkt4>& discover) {
+    /// TODO: Currently implemented echo mode. Implement this for real
+    return (discover);
+}
+
+boost::shared_ptr<Pkt4>
+Dhcpv4Srv::processRequest(boost::shared_ptr<Pkt4>& request) {
+    /// TODO: Currently implemented echo mode. Implement this for real
+    return (request);
+}
+
+void Dhcpv4Srv::processRelease(boost::shared_ptr<Pkt4>& release) {
+    /// TODO: Implement this.
+    cout << "Received RELEASE on " << release->getIface() << " interface." << endl;
+}
+
+void Dhcpv4Srv::processDecline(boost::shared_ptr<Pkt4>& decline) {
+    /// TODO: Implement this.
+    cout << "Received DECLINE on " << decline->getIface() << " interface." << endl;
+}
+
+boost::shared_ptr<Pkt4> Dhcpv4Srv::processInform(boost::shared_ptr<Pkt4>& inform) {
+    /// TODO: Currently implemented echo mode. Implement this for real
+    return (inform);
+}
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
new file mode 100644
index 0000000..033ac5a
--- /dev/null
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -0,0 +1,137 @@
+// Copyright (C) 2011  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 DHCPV4_SRV_H
+#define DHCPV4_SRV_H
+
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+#include <dhcp/dhcp4.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/option.h>
+#include <iostream>
+
+namespace isc {
+
+namespace dhcp {
+/// @brief DHCPv4 server service.
+///
+/// This singleton class represents DHCPv4 server. It contains all
+/// top-level methods and routines necessary for server operation.
+/// In particular, it instantiates IfaceMgr, loads or generates DUID
+/// that is going to be used as server-identifier, receives incoming
+/// packets, processes them, manages leases assignment and generates
+/// appropriate responses.
+class Dhcpv4Srv : public boost::noncopyable {
+
+    public:
+    /// @brief Default constructor.
+    ///
+    /// Instantiates necessary services, required to run DHCPv6 server.
+    /// In particular, creates IfaceMgr that will be responsible for
+    /// network interaction. Will instantiate lease manager, and load
+    /// old or create new DUID. It is possible to specify alternate
+    /// port on which DHCPv4 server will listen on. That is mostly useful
+    /// for testing purposes.
+    ///
+    /// @param port specifies port number to listen on
+    Dhcpv4Srv(uint16_t port = DHCP4_SERVER_PORT);
+
+    /// @brief Destructor. Used during DHCPv6 service shutdown.
+    ~Dhcpv4Srv();
+
+    /// @brief Main server processing loop.
+    ///
+    /// Main server processing loop. Receives incoming packets, verifies
+    /// their correctness, generates appropriate answer (if needed) and
+    /// transmits respones.
+    ///
+    /// @return true, if being shut down gracefully, fail if experienced
+    ///         critical error.
+    bool run();
+
+protected:
+    /// @brief Processes incoming DISCOVER and returns response.
+    ///
+    /// Processes received DISCOVER message and verifies that its sender
+    /// should be served. In particular, a lease is selected and sent
+    /// as an offer to a client if it should be served.
+    ///
+    /// @param solicit DISCOVER message received from client
+    ///
+    /// @return OFFER message or NULL
+    boost::shared_ptr<Pkt4>
+    processDiscover(boost::shared_ptr<Pkt4>& discover);
+
+    /// @brief Processes incoming REQUEST and returns REPLY response.
+    ///
+    /// Processes incoming REQUEST message and verifies that its sender
+    /// should be served. In particular, verifies that requested lease
+    /// is valid, not expired, not reserved, not used by other client and
+    /// that requesting client is allowed to use it.
+    ///
+    /// Returns ACK message, NACK message, or NULL
+    ///
+    /// @param request a message received from client
+    ///
+    /// @return ACK or NACK message
+    boost::shared_ptr<Pkt4> processRequest(boost::shared_ptr<Pkt4>& request);
+
+    /// @brief Stub function that will handle incoming RELEASE messages.
+    ///
+    /// In DHCPv4, server does not respond to RELEASE messages, therefore
+    /// this function does not return anything.
+    ///
+    /// @param release message received from client
+    void processRelease(boost::shared_ptr<Pkt4>& release);
+
+    /// @brief Stub function that will handle incoming DHCPDECLINE messages.
+    ///
+    /// @param decline message received from client
+    void processDecline(boost::shared_ptr<Pkt4>& decline);
+
+    /// @brief Stub function that will handle incoming INFORM messages.
+    ///
+    /// @param infRequest message received from client
+    boost::shared_ptr<Pkt4> processInform(boost::shared_ptr<Pkt4>& inform);
+
+    /// @brief Returns server-intentifier option
+    ///
+    /// @return server-id option
+    boost::shared_ptr<isc::dhcp::Option>
+    getServerID() { return serverid_; }
+
+    /// @brief Sets server-identifier.
+    ///
+    /// This method attempts to set server-identifier DUID. It tries to
+    /// load previously stored IP from configuration. If there is no previously
+    /// stored server identifier, it will pick up one address from configured
+    /// and supported network interfaces.
+    ///
+    /// @throws isc::Unexpected Failed to obtain server identifier (i.e. no
+    //          previously stored configuration and no network interfaces available)
+    void setServerID();
+
+    /// server DUID (to be sent in server-identifier option)
+    boost::shared_ptr<isc::dhcp::Option> serverid_;
+
+    /// indicates if shutdown is in progress. Setting it to true will
+    /// initiate server shutdown procedure.
+    volatile bool shutdown_;
+};
+
+}; // namespace isc::dhcp
+}; // namespace isc
+
+#endif // DHCP4_SRV_H
diff --git a/src/bin/dhcp4/main.cc b/src/bin/dhcp4/main.cc
new file mode 100644
index 0000000..ee40295
--- /dev/null
+++ b/src/bin/dhcp4/main.cc
@@ -0,0 +1,112 @@
+// Copyright (C) 2009-2011  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 <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <cassert>
+#include <iostream>
+
+#include <exceptions/exceptions.h>
+#if 0
+// TODO cc is not used yet. It should be eventually
+#include <cc/session.h>
+#include <config/ccsession.h>
+#endif
+
+#include <util/buffer.h>
+#include <log/dummylog.h>
+
+#include <dhcp4/spec_config.h>
+#include <dhcp4/dhcp4_srv.h>
+
+using namespace std;
+using namespace isc::util;
+
+using namespace isc;
+using namespace isc::dhcp;
+
+namespace {
+
+bool verbose_mode = false;
+
+void
+usage() {
+    cerr << "Usage:  b10-dhcp4 [-v]"
+         << endl;
+    cerr << "\t-v: verbose output" << endl;
+    exit(1);
+}
+} // end of anonymous namespace
+
+int
+main(int argc, char* argv[]) {
+    int ch;
+
+    while ((ch = getopt(argc, argv, ":v")) != -1) {
+        switch (ch) {
+        case 'v':
+            verbose_mode = true;
+            isc::log::denabled = true;
+            break;
+        case ':':
+        default:
+            usage();
+        }
+    }
+
+    cout << "My pid=" << getpid() << endl;
+
+    if (argc - optind > 0) {
+        usage();
+    }
+
+    int ret = 0;
+
+    // TODO remainder of auth to dhcp4 code copy. We need to enable this in
+    //      dhcp4 eventually
+#if 0
+    Session* cc_session = NULL;
+    Session* statistics_session = NULL;
+    ModuleCCSession* config_session = NULL;
+#endif
+    try {
+        string specfile;
+        if (getenv("B10_FROM_BUILD")) {
+            specfile = string(getenv("B10_FROM_BUILD")) +
+                "/src/bin/auth/dhcp4.spec";
+        } else {
+            specfile = string(DHCP4_SPECFILE_LOCATION);
+        }
+
+        cout << "[b10-dhcp4] Initiating DHCPv4 server operation." << endl;
+
+        Dhcpv4Srv* srv = new Dhcpv4Srv();
+
+        srv->run();
+
+    } catch (const std::exception& ex) {
+        cerr << "[b10-dhcp4] Server failed: " << ex.what() << endl;
+        ret = 1;
+    }
+
+    return (ret);
+}
diff --git a/src/bin/dhcp4/spec_config.h.pre.in b/src/bin/dhcp4/spec_config.h.pre.in
new file mode 100644
index 0000000..17623ed
--- /dev/null
+++ b/src/bin/dhcp4/spec_config.h.pre.in
@@ -0,0 +1,15 @@
+// Copyright (C) 2011  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.
+
+#define DHCP4_SPECFILE_LOCATION "@prefix@/share/@PACKAGE@/dhcp4.spec"
diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
new file mode 100644
index 0000000..dcda356
--- /dev/null
+++ b/src/bin/dhcp4/tests/Makefile.am
@@ -0,0 +1,46 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_builddir)/src/bin # for generated spec_config.h header
+AM_CPPFLAGS += -I$(top_srcdir)/src/bin
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_top_srcdir)/src/lib/testutils/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/dhcp6/tests\"
+AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
+
+CLEANFILES = $(builddir)/interfaces.txt
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+TESTS =
+if HAVE_GTEST
+
+TESTS += dhcp4_unittests
+
+dhcp4_unittests_SOURCES = ../dhcp4_srv.h ../dhcp4_srv.cc
+dhcp4_unittests_SOURCES += dhcp4_unittests.cc
+dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc
+
+dhcp4_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+dhcp4_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+dhcp4_unittests_LDADD = $(GTEST_LDADD)
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
new file mode 100644
index 0000000..c1976e1
--- /dev/null
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -0,0 +1,161 @@
+// Copyright (C) 2011  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 <iostream>
+#include <sstream>
+
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+#include <dhcp/dhcp4.h>
+#include <dhcp4/dhcp4_srv.h>
+#include <dhcp/option.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+
+namespace {
+
+class NakedDhcpv4Srv: public Dhcpv4Srv {
+    // "naked" DHCPv4 server, exposes internal fields
+public:
+    NakedDhcpv4Srv() { }
+
+    boost::shared_ptr<Pkt4> processDiscover(boost::shared_ptr<Pkt4>& discover) {
+        return Dhcpv4Srv::processDiscover(discover);
+    }
+    boost::shared_ptr<Pkt4> processRequest(boost::shared_ptr<Pkt4>& request) {
+        return Dhcpv4Srv::processRequest(request);
+    }
+    void processRelease(boost::shared_ptr<Pkt4>& release) {
+        return Dhcpv4Srv::processRelease(release);
+    }
+    void processDecline(boost::shared_ptr<Pkt4>& decline) {
+        Dhcpv4Srv::processDecline(decline);
+    }
+    boost::shared_ptr<Pkt4> processInform(boost::shared_ptr<Pkt4>& inform) {
+        return Dhcpv4Srv::processInform(inform);
+    }
+};
+
+class Dhcpv4SrvTest : public ::testing::Test {
+public:
+    Dhcpv4SrvTest() {
+    }
+
+    ~Dhcpv4SrvTest() {
+    };
+};
+
+TEST_F(Dhcpv4SrvTest, basic) {
+    // nothing to test. DHCPv4_srv instance is created
+    // in test fixture. It is destroyed in destructor
+
+    Dhcpv4Srv* srv = NULL;
+    ASSERT_NO_THROW({
+        srv = new Dhcpv4Srv();
+    });
+
+    delete srv;
+}
+
+TEST_F(Dhcpv4SrvTest, processDiscover) {
+    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+
+    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDISCOVER, 1234));
+
+    // should not throw
+    EXPECT_NO_THROW(
+        srv->processDiscover(pkt);
+    );
+
+    // should return something
+    EXPECT_TRUE(srv->processDiscover(pkt));
+
+    // TODO: Implement more reasonable tests before starting
+    // work on processSomething() method.
+    delete srv;
+}
+
+TEST_F(Dhcpv4SrvTest, processRequest) {
+    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+
+    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPREQUEST, 1234));
+
+    // should not throw
+    EXPECT_NO_THROW(
+        srv->processRequest(pkt);
+    );
+
+    // should return something
+    EXPECT_TRUE(srv->processRequest(pkt));
+
+    // TODO: Implement more reasonable tests before starting
+    // work on processSomething() method.
+    delete srv;
+}
+
+TEST_F(Dhcpv4SrvTest, processRelease) {
+    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+
+    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPRELEASE, 1234));
+
+    // should not throw
+    EXPECT_NO_THROW(
+        srv->processRelease(pkt);
+    );
+
+    // TODO: Implement more reasonable tests before starting
+    // work on processSomething() method.
+
+    delete srv;
+}
+
+TEST_F(Dhcpv4SrvTest, processDecline) {
+    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+
+    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDECLINE, 1234));
+
+    // should not throw
+    EXPECT_NO_THROW(
+        srv->processDecline(pkt);
+    );
+
+    // TODO: Implement more reasonable tests before starting
+    // work on processSomething() method.
+    delete srv;
+}
+
+TEST_F(Dhcpv4SrvTest, processInform) {
+    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+
+    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPINFORM, 1234));
+
+    // should not throw
+    EXPECT_NO_THROW(
+        srv->processInform(pkt);
+    );
+
+    // should return something
+    EXPECT_TRUE(srv->processInform(pkt));
+
+    // TODO: Implement more reasonable tests before starting
+    // work on processSomething() method.
+
+    delete srv;
+}
+
+} // end of anonymous namespace
diff --git a/src/bin/dhcp4/tests/dhcp4_unittests.cc b/src/bin/dhcp4/tests/dhcp4_unittests.cc
new file mode 100644
index 0000000..ebc72fb
--- /dev/null
+++ b/src/bin/dhcp4/tests/dhcp4_unittests.cc
@@ -0,0 +1,28 @@
+// Copyright (C) 2011 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 <stdio.h>
+#include <gtest/gtest.h>
+#include <log/logger_support.h>
+
+int
+main(int argc, char* argv[]) {
+
+    ::testing::InitGoogleTest(&argc, argv);
+    isc::log::initLogger();
+
+    int result = RUN_ALL_TESTS();
+
+    return (result);
+}
diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
index b0f8cd9..0e93924 100644
--- a/src/bin/dhcp6/Makefile.am
+++ b/src/bin/dhcp6/Makefile.am
@@ -32,10 +32,9 @@ spec_config.h: spec_config.h.pre
 BUILT_SOURCES = spec_config.h
 pkglibexec_PROGRAMS = b10-dhcp6
 
-b10_dhcp6_SOURCES = main.cc iface_mgr.cc dhcp6_srv.cc
-b10_dhcp6_SOURCES += iface_mgr.h dhcp6_srv.h
+b10_dhcp6_SOURCES = main.cc dhcp6_srv.cc dhcp6_srv.h
 
-b10_dhcp6_LDADD = $(top_builddir)/src/lib/dhcp/libdhcp.la
+b10_dhcp6_LDADD = $(top_builddir)/src/lib/dhcp/libdhcp++.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/liblog.la
diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec
index 0e7e852..05c3529 100644
--- a/src/bin/dhcp6/dhcp6.spec
+++ b/src/bin/dhcp6/dhcp6.spec
@@ -1,7 +1,7 @@
 {
   "module_spec": {
     "module_name": "dhcp6",
-    "module_description": "DHCPv6 daemon",
+    "module_description": "DHCPv6 server daemon",
     "config_data": [
       { "item_name": "interface",
         "item_type": "string",
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index d5a969f..6bc7194 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -14,7 +14,7 @@
 
 #include <dhcp/dhcp6.h>
 #include <dhcp/pkt6.h>
-#include <dhcp6/iface_mgr.h>
+#include <dhcp/iface_mgr.h>
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
diff --git a/src/bin/dhcp6/iface_mgr.cc b/src/bin/dhcp6/iface_mgr.cc
deleted file mode 100644
index de2b93c..0000000
--- a/src/bin/dhcp6/iface_mgr.cc
+++ /dev/null
@@ -1,725 +0,0 @@
-// Copyright (C) 2011  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 <sstream>
-#include <fstream>
-#include <string.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <dhcp/dhcp6.h>
-#include <dhcp6/iface_mgr.h>
-#include <exceptions/exceptions.h>
-
-using namespace std;
-using namespace isc;
-using namespace isc::asiolink;
-using namespace isc::dhcp;
-
-namespace isc {
-
-/// IfaceMgr is a singleton implementation
-IfaceMgr* IfaceMgr::instance_ = 0;
-
-void
-IfaceMgr::instanceCreate() {
-    if (instance_) {
-        // no need to do anything. Instance is already created.
-        // Who called it again anyway? Uh oh. Had to be us, as
-        // this is private method.
-        return;
-    }
-    instance_ = new IfaceMgr();
-}
-
-IfaceMgr&
-IfaceMgr::instance() {
-    if (instance_ == 0) {
-        instanceCreate();
-    }
-    return (*instance_);
-}
-
-IfaceMgr::Iface::Iface(const std::string& name, int ifindex)
-    :name_(name), ifindex_(ifindex), mac_len_(0) {
-
-    memset(mac_, 0, sizeof(mac_));
-}
-
-std::string
-IfaceMgr::Iface::getFullName() const {
-    ostringstream tmp;
-    tmp << name_ << "/" << ifindex_;
-    return (tmp.str());
-}
-
-std::string
-IfaceMgr::Iface::getPlainMac() const {
-    ostringstream tmp;
-    tmp.fill('0');
-    tmp << hex;
-    for (int i = 0; i < mac_len_; i++) {
-        tmp.width(2);
-        tmp << mac_[i];
-        if (i < mac_len_-1) {
-            tmp << ":";
-        }
-    }
-    return (tmp.str());
-}
-
-bool IfaceMgr::Iface::delAddress(const isc::asiolink::IOAddress& addr) {
-
-    // Let's delete all addresses that match. It really shouldn't matter
-    // if we delete first or all, as the OS should allow to add a single
-    // address to an interface only once. If OS allows multiple instances
-    // of the same address added, we are in deep problems anyway.
-    size_t size = addrs_.size();
-    addrs_.erase(remove(addrs_.begin(), addrs_.end(), addr), addrs_.end());
-    return (addrs_.size() < size);
-}
-
-bool IfaceMgr::Iface::delSocket(uint16_t sockfd) {
-    list<SocketInfo>::iterator sock = sockets_.begin();
-    while (sock!=sockets_.end()) {
-        if (sock->sockfd_ == sockfd) {
-            close(sockfd);
-            sockets_.erase(sock);
-            return (true); //socket found
-        }
-        ++sock;
-    }
-    return (false); // socket not found
-}
-
-IfaceMgr::IfaceMgr()
-    :control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
-     control_buf_(new char[control_buf_len_])
-{
-
-    cout << "IfaceMgr initialization." << endl;
-
-    try {
-        // required for sending/receiving packets
-        // let's keep it in front, just in case someone
-        // wants to send anything during initialization
-
-        // control_buf_ = boost::scoped_array<char>();
-
-        detectIfaces();
-
-    } catch (const std::exception& ex) {
-        cout << "IfaceMgr creation failed:" << ex.what() << endl;
-
-        // TODO Uncomment this (or call LOG_FATAL) once
-        // interface detection is implemented. Otherwise
-        // it is not possible to run tests in a portable
-        // way (see detectIfaces() method).
-        // throw ex;
-    }
-}
-
-void IfaceMgr::closeSockets() {
-    for (IfaceCollection::iterator iface = ifaces_.begin();
-         iface != ifaces_.end(); ++iface) {
-
-        for (SocketCollection::iterator sock = iface->sockets_.begin();
-             sock != iface->sockets_.end(); ++sock) {
-            cout << "Closing socket " << sock->sockfd_ << endl;
-            close(sock->sockfd_);
-        }
-        iface->sockets_.clear();
-    }
-
-}
-
-IfaceMgr::~IfaceMgr() {
-    closeSockets();
-
-    // control_buf_ is deleted automatically (scoped_ptr)
-    control_buf_len_ = 0;
-}
-
-void
-IfaceMgr::detectIfaces() {
-    string ifaceName, linkLocal;
-
-    // TODO do the actual detection. Currently interface detection is faked
-    //      by reading a text file.
-
-    cout << "Interface detection is not implemented yet. "
-         << "Reading interfaces.txt file instead." << endl;
-    cout << "Please use format: interface-name link-local-address" << endl;
-
-    try {
-        ifstream interfaces("interfaces.txt");
-
-        if (!interfaces.good()) {
-            cout << "Failed to read interfaces.txt file." << endl;
-            isc_throw(Unexpected, "Failed to read interfaces.txt");
-        }
-        interfaces >> ifaceName;
-        interfaces >> linkLocal;
-
-        cout << "Detected interface " << ifaceName << "/" << linkLocal << endl;
-
-        Iface iface(ifaceName, if_nametoindex( ifaceName.c_str() ) );
-        IOAddress addr(linkLocal);
-        iface.addAddress(addr);
-        addInterface(iface);
-        interfaces.close();
-    } catch (const std::exception& ex) {
-        // TODO: deallocate whatever memory we used
-        // not that important, since this function is going to be
-        // thrown away as soon as we get proper interface detection
-        // implemented
-
-        // TODO Do LOG_FATAL here
-        std::cerr << "Interface detection failed." << std::endl;
-        throw ex;
-    }
-}
-
-void
-IfaceMgr::openSockets(uint16_t port) {
-    int sock1, sock2;
-
-    for (IfaceCollection::iterator iface = ifaces_.begin();
-         iface != ifaces_.end(); ++iface) {
-
-        AddressCollection addrs = iface->getAddresses();
-
-        for (AddressCollection::iterator addr = addrs.begin();
-             addr != addrs.end();
-             ++addr) {
-
-            sock1 = openSocket(iface->getName(), *addr, port);
-            if (sock1 < 0) {
-                isc_throw(Unexpected, "Failed to open unicast socket on "
-                          << " interface " << iface->getFullName());
-            }
-
-            if ( !joinMcast(sock1, iface->getName(),
-                             string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
-                close(sock1);
-                isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
-                          << " multicast group.");
-            }
-
-            // this doesn't work too well on NetBSD
-            sock2 = openSocket(iface->getName(),
-                               IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
-                               port);
-            if (sock2 < 0) {
-                isc_throw(Unexpected, "Failed to open multicast socket on "
-                          << " interface " << iface->getFullName());
-                iface->delSocket(sock1); // delete previously opened socket
-            }
-        }
-    }
-}
-
-void
-IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
-    for (IfaceCollection::const_iterator iface = ifaces_.begin();
-         iface != ifaces_.end(); ++iface) {
-        out << "Detected interface " << iface->getFullName() << endl;
-        out << "  " << iface->getAddresses().size() << " addr(s):" << endl;
-        const AddressCollection addrs = iface->getAddresses();
-
-        for (AddressCollection::const_iterator addr = addrs.begin();
-             addr != addrs.end(); ++addr) {
-            out << "  " << addr->toText() << endl;
-        }
-        out << "  mac: " << iface->getPlainMac() << endl;
-    }
-}
-
-IfaceMgr::Iface*
-IfaceMgr::getIface(int ifindex) {
-    for (IfaceCollection::iterator iface = ifaces_.begin();
-         iface != ifaces_.end(); ++iface) {
-        if (iface->getIndex() == ifindex) {
-            return (&(*iface));
-        }
-    }
-
-    return (NULL); // not found
-}
-
-IfaceMgr::Iface*
-IfaceMgr::getIface(const std::string& ifname) {
-    for (IfaceCollection::iterator iface = ifaces_.begin();
-         iface != ifaces_.end(); ++iface) {
-        if (iface->getName() == ifname) {
-            return (&(*iface));
-        }
-    }
-
-    return (NULL); // not found
-}
-
-int
-IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr,
-                     int port) {
-    Iface* iface = getIface(ifname);
-    if (!iface) {
-        isc_throw(BadValue, "There is no " << ifname << " interface present.");
-    }
-    switch (addr.getFamily()) {
-    case AF_INET:
-        return openSocket4(*iface, addr, port);
-    case AF_INET6:
-        return openSocket6(*iface, addr, port);
-    default:
-        isc_throw(BadValue, "Failed to detect family of address: "
-                  << addr.toText());
-    }
-}
-
-int
-IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, int port) {
-
-    cout << "Creating UDP4 socket on " << iface.getFullName()
-         << " " << addr.toText() << "/port=" << port << endl;
-
-    struct sockaddr_in addr4;
-    memset(&addr4, 0, sizeof(sockaddr));
-    addr4.sin_family = AF_INET;
-    addr4.sin_port = htons(port);
-    memcpy(&addr4.sin_addr, addr.getAddress().to_v4().to_bytes().data(),
-           sizeof(addr4.sin_addr));
-
-    int sock = socket(AF_INET, SOCK_DGRAM, 0);
-    if (sock < 0) {
-        isc_throw(Unexpected, "Failed to create UDP6 socket.");
-    }
-
-    if (bind(sock, (struct sockaddr *)&addr4, sizeof(addr4)) < 0) {
-        close(sock);
-        isc_throw(Unexpected, "Failed to bind socket " << sock << " to " << addr.toText()
-                  << "/port=" << port);
-    }
-
-    // If there is no support for IP_PKTINFO, we are really out of luck.
-    // It will be difficult to understand, where this packet came from.
-#if defined(IP_PKTINFO)
-    int flag = 1;
-    if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &flag, sizeof(flag)) != 0) {
-        close(sock);
-        isc_throw(Unexpected, "setsockopt: IP_PKTINFO: failed.");
-    }
-#endif
-
-    cout << "Created socket " << sock << " on " << iface.getName() << "/" <<
-        addr.toText() << "/port=" << port << endl;
-
-    iface.addSocket(SocketInfo(sock, addr, port));
-
-    return (sock);
-}
-
-int
-IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
-
-    cout << "Creating UDP6 socket on " << iface.getFullName()
-         << " " << addr.toText() << "/port=" << port << endl;
-
-    struct sockaddr_in6 addr6;
-    memset(&addr6, 0, sizeof(addr6));
-    addr6.sin6_family = AF_INET6;
-    addr6.sin6_port = htons(port);
-    if (addr.toText() != "::1")
-      addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str());
-
-    memcpy(&addr6.sin6_addr,
-           addr.getAddress().to_v6().to_bytes().data(),
-           sizeof(addr6.sin6_addr));
-#ifdef HAVE_SA_LEN
-    addr6->sin6_len = sizeof(addr6);
-#endif
-
-    // TODO: use sockcreator once it becomes available
-
-    // make a socket
-    int sock = socket(AF_INET6, SOCK_DGRAM, 0);
-    if (sock < 0) {
-        isc_throw(Unexpected, "Failed to create UDP6 socket.");
-    }
-
-    // Set the REUSEADDR option so that we don't fail to start if
-    // we're being restarted.
-    int flag = 1;
-    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
-                   (char *)&flag, sizeof(flag)) < 0) {
-        close(sock);
-        isc_throw(Unexpected, "Can't set SO_REUSEADDR option on dhcpv6 socket.");
-    }
-
-    if (bind(sock, (struct sockaddr *)&addr6, sizeof(addr6)) < 0) {
-        close(sock);
-        isc_throw(Unexpected, "Failed to bind socket " << sock << " to " << addr.toText()
-                  << "/port=" << port);
-    }
-#ifdef IPV6_RECVPKTINFO
-    // RFC3542 - a new way
-    if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
-                   &flag, sizeof(flag)) != 0) {
-        close(sock);
-        isc_throw(Unexpected, "setsockopt: IPV6_RECVPKTINFO failed.");
-    }
-#else
-    // RFC2292 - an old way
-    if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
-                   &flag, sizeof(flag)) != 0) {
-        close(sock);
-        isc_throw(Unexpected, "setsockopt: IPV6_PKTINFO: failed.");
-    }
-#endif
-
-    // multicast stuff
-    if (addr.getAddress().to_v6().is_multicast()) {
-        // both mcast (ALL_DHCP_RELAY_AGENTS_AND_SERVERS and ALL_DHCP_SERVERS)
-        // are link and site-scoped, so there is no sense to join those groups
-        // with global addresses.
-
-        if ( !joinMcast( sock, iface.getName(),
-                         string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
-            close(sock);
-            isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
-                      << " multicast group.");
-        }
-    }
-
-    cout << "Created socket " << sock << " on " << iface.getName() << "/" <<
-        addr.toText() << "/port=" << port << endl;
-
-    iface.addSocket(SocketInfo(sock, addr, port));
-
-    return (sock);
-}
-
-bool
-IfaceMgr::joinMcast(int sock, const std::string& ifname,
-const std::string & mcast) {
-
-    struct ipv6_mreq mreq;
-
-    if (inet_pton(AF_INET6, mcast.c_str(),
-                  &mreq.ipv6mr_multiaddr) <= 0) {
-        cout << "Failed to convert " << ifname
-             << " to IPv6 multicast address." << endl;
-        return (false);
-    }
-
-    mreq.ipv6mr_interface = if_nametoindex(ifname.c_str());
-    if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
-                   &mreq, sizeof(mreq)) < 0) {
-        cout << "Failed to join " << mcast << " multicast group." << endl;
-        return (false);
-    }
-
-    cout << "Joined multicast " << mcast << " group." << endl;
-
-    return (true);
-}
-
-bool
-IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
-    struct msghdr m;
-    struct iovec v;
-    int result;
-    struct in6_pktinfo *pktinfo;
-    struct cmsghdr *cmsg;
-
-    Iface* iface = getIface(pkt->iface_);
-    if (!iface) {
-        isc_throw(BadValue, "Unable to send Pkt6. Invalid interface ("
-                  << pkt->iface_ << ") specified.");
-    }
-
-    memset(&control_buf_[0], 0, control_buf_len_);
-
-    // Initialize our message header structure.
-    memset(&m, 0, sizeof(m));
-
-    // Set the target address we're sending to.
-    sockaddr_in6 to;
-    memset(&to, 0, sizeof(to));
-    to.sin6_family = AF_INET6;
-    to.sin6_port = htons(pkt->remote_port_);
-    memcpy(&to.sin6_addr,
-           pkt->remote_addr_.getAddress().to_v6().to_bytes().data(),
-           16);
-    to.sin6_scope_id = pkt->ifindex_;
-
-    m.msg_name = &to;
-    m.msg_namelen = sizeof(to);
-
-    // Set the data buffer we're sending. (Using this wacky
-    // "scatter-gather" stuff... we only have a single chunk
-    // of data to send, so we declare a single vector entry.)
-    v.iov_base = (char *) &pkt->data_[0];
-    v.iov_len = pkt->data_len_;
-    m.msg_iov = &v;
-    m.msg_iovlen = 1;
-
-    // Setting the interface is a bit more involved.
-    //
-    // We have to create a "control message", and set that to
-    // define the IPv6 packet information. We could set the
-    // source address if we wanted, but we can safely let the
-    // kernel decide what that should be.
-    m.msg_control = &control_buf_[0];
-    m.msg_controllen = control_buf_len_;
-    cmsg = CMSG_FIRSTHDR(&m);
-    cmsg->cmsg_level = IPPROTO_IPV6;
-    cmsg->cmsg_type = IPV6_PKTINFO;
-    cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
-    pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
-    memset(pktinfo, 0, sizeof(*pktinfo));
-    pktinfo->ipi6_ifindex = pkt->ifindex_;
-    m.msg_controllen = cmsg->cmsg_len;
-
-    result = sendmsg(getSocket(*pkt), &m, 0);
-    if (result < 0) {
-        cout << "Send packet failed." << endl;
-    }
-    cout << "Sent " << pkt->data_len_ << " bytes over socket " << getSocket(*pkt)
-         << " on " << iface->getFullName() << " interface: "
-         << " dst=" << pkt->remote_addr_.toText()
-         << ", src=" << pkt->local_addr_.toText()
-         << endl;
-
-    return (result);
-}
-
-bool
-IfaceMgr::send(boost::shared_ptr<Pkt4>& )
-{
-    /// TODO: Implement this (ticket #1240)
-    isc_throw(NotImplemented, "Pkt4 send not implemented yet.");
-}
-
-
-boost::shared_ptr<Pkt4>
-IfaceMgr::receive4() {
-    isc_throw(NotImplemented, "Pkt4 reception not implemented yet.");
-
-    // TODO: To be implemented (ticket #1239)
-    return (boost::shared_ptr<Pkt4>()); // NULL
-}
-
-boost::shared_ptr<Pkt6>
-IfaceMgr::receive6() {
-    struct msghdr m;
-    struct iovec v;
-    int result;
-    struct cmsghdr* cmsg;
-    struct in6_pktinfo* pktinfo;
-    struct sockaddr_in6 from;
-    struct in6_addr to_addr;
-    boost::shared_ptr<Pkt6> pkt;
-    char addr_str[INET6_ADDRSTRLEN];
-
-    try {
-        // RFC3315 states that server responses may be
-        // fragmented if they are over MTU. There is no
-        // text whether client's packets may be larger
-        // than 1500. Nevertheless to be on the safe side
-        // we use larger buffer. This buffer limit is checked
-        // during reception (see iov_len below), so we are
-        // safe
-        pkt = boost::shared_ptr<Pkt6>(new Pkt6(65536));
-    } catch (const std::exception& ex) {
-        cout << "Failed to create new packet." << endl;
-        return (boost::shared_ptr<Pkt6>()); // NULL
-    }
-
-    memset(&control_buf_[0], 0, control_buf_len_);
-
-    memset(&from, 0, sizeof(from));
-    memset(&to_addr, 0, sizeof(to_addr));
-
-    // Initialize our message header structure.
-    memset(&m, 0, sizeof(m));
-
-    // Point so we can get the from address.
-    m.msg_name = &from;
-    m.msg_namelen = sizeof(from);
-
-    // Set the data buffer we're receiving. (Using this wacky
-    // "scatter-gather" stuff... but we that doesn't really make
-    // sense for us, so we use a single vector entry.)
-    v.iov_base = (void*)&pkt->data_[0];
-    v.iov_len = pkt->data_len_;
-    m.msg_iov = &v;
-    m.msg_iovlen = 1;
-
-    // Getting the interface is a bit more involved.
-    //
-    // We set up some space for a "control message". We have
-    // previously asked the kernel to give us packet
-    // information (when we initialized the interface), so we
-    // should get the destination address from that.
-    m.msg_control = &control_buf_[0];
-    m.msg_controllen = control_buf_len_;
-
-    /// TODO: Need to move to select() and pool over
-    /// all available sockets. For now, we just take the
-    /// first interface and use first socket from it.
-    IfaceCollection::const_iterator iface = ifaces_.begin();
-    if (iface == ifaces_.end()) {
-        isc_throw(Unexpected, "No interfaces detected. Can't receive anything.");
-    }
-    SocketCollection::const_iterator s = iface->sockets_.begin();
-    const SocketInfo* candidate = 0;
-    while (s != iface->sockets_.end()) {
-        if (s->addr_.getAddress().to_v6().is_multicast()) {
-            candidate = &(*s);
-            break;
-        }
-        if (!candidate) {
-            candidate = &(*s); // it's not multicast, but it's better than none
-        }
-        ++s;
-    }
-    if (!candidate) {
-        isc_throw(Unexpected, "Interface " << iface->getFullName()
-                  << " does not have any sockets open.");
-    }
-
-    cout << "Trying to receive over socket " << candidate->sockfd_ << " bound to "
-         << candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
-         << iface->getFullName() << endl;
-    result = recvmsg(candidate->sockfd_, &m, 0);
-
-    if (result >= 0) {
-        // If we did read successfully, then we need to loop
-        // through the control messages we received and
-        // find the one with our destination address.
-        //
-        // We also keep a flag to see if we found it. If we
-        // didn't, then we consider this to be an error.
-        int found_pktinfo = 0;
-        cmsg = CMSG_FIRSTHDR(&m);
-        while (cmsg != NULL) {
-            if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
-                (cmsg->cmsg_type == IPV6_PKTINFO)) {
-                pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsg);
-                to_addr = pktinfo->ipi6_addr;
-                pkt->ifindex_ = pktinfo->ipi6_ifindex;
-                found_pktinfo = 1;
-            }
-            cmsg = CMSG_NXTHDR(&m, cmsg);
-        }
-        if (!found_pktinfo) {
-            cout << "Unable to find pktinfo" << endl;
-            return (boost::shared_ptr<Pkt6>()); // NULL
-        }
-    } else {
-        cout << "Failed to receive data." << endl;
-        return (boost::shared_ptr<Pkt6>()); // NULL
-    }
-
-    // That's ugly.
-    // TODO add IOAddress constructor that will take struct in6_addr*
-    // TODO: there's from_bytes() method added in IOAddress. Use it!
-    inet_ntop(AF_INET6, &to_addr, addr_str,INET6_ADDRSTRLEN);
-    pkt->local_addr_ = IOAddress(string(addr_str));
-
-    // TODO: there's from_bytes() method added in IOAddress. Use it!
-    inet_ntop(AF_INET6, &from.sin6_addr, addr_str, INET6_ADDRSTRLEN);
-    pkt->remote_addr_ = IOAddress(string(addr_str));
-
-    pkt->remote_port_ = ntohs(from.sin6_port);
-
-    Iface* received = getIface(pkt->ifindex_);
-    if (received) {
-        pkt->iface_ = received->getName();
-    } else {
-        cout << "Received packet over unknown interface (ifindex="
-             << pkt->ifindex_ << ")." << endl;
-        return (boost::shared_ptr<Pkt6>()); // NULL
-    }
-
-    pkt->data_len_ = result;
-
-    // TODO Move this to LOG_DEBUG
-    cout << "Received " << pkt->data_len_ << " bytes over "
-         << pkt->iface_ << "/" << pkt->ifindex_ << " interface: "
-         << " src=" << pkt->remote_addr_.toText()
-         << ", dst=" << pkt->local_addr_.toText()
-         << endl;
-
-    return (pkt);
-}
-
-uint16_t
-IfaceMgr::getSocket(isc::dhcp::Pkt6 const& pkt) {
-    Iface* iface = getIface(pkt.iface_);
-    if (!iface) {
-        isc_throw(BadValue, "Tried to find socket for non-existent interface "
-                  << pkt.iface_);
-    }
-
-    SocketCollection::const_iterator s;
-    for (s = iface->sockets_.begin(); s != iface->sockets_.end(); ++s) {
-        if (s->family_ != AF_INET6) {
-            // don't use IPv4 sockets
-            continue;
-        }
-        if (s->addr_.getAddress().to_v6().is_multicast()) {
-            // don't use IPv6 sockets bound to multicast address
-            continue;
-        }
-        /// TODO: Add more checks here later. If remote address is
-        /// not link-local, we can't use link local bound socket
-        /// to send data.
-
-        return (s->sockfd_);
-    }
-
-    isc_throw(Unexpected, "Interface " << iface->getFullName()
-              << " does not have any suitable IPv6 sockets open.");
-}
-
-uint16_t
-IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
-    Iface* iface = getIface(pkt.getIface());
-    if (!iface) {
-        isc_throw(BadValue, "Tried to find socket for non-existent interface "
-                  << pkt.getIface());
-    }
-
-    SocketCollection::const_iterator s;
-    for (s = iface->sockets_.begin(); s != iface->sockets_.end(); ++s) {
-        if (s->family_ != AF_INET) {
-            // don't use IPv4 sockets
-            continue;
-        }
-        /// TODO: Add more checks here later. If remote address is
-        /// not link-local, we can't use link local bound socket
-        /// to send data.
-
-        return (s->sockfd_);
-    }
-
-    isc_throw(Unexpected, "Interface " << iface->getFullName()
-              << " does not have any suitable IPv4 sockets open.");
-}
-
-
-
-}
diff --git a/src/bin/dhcp6/iface_mgr.h b/src/bin/dhcp6/iface_mgr.h
deleted file mode 100644
index 0aa2592..0000000
--- a/src/bin/dhcp6/iface_mgr.h
+++ /dev/null
@@ -1,414 +0,0 @@
-// Copyright (C) 2011  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 IFACE_MGR_H
-#define IFACE_MGR_H
-
-#include <list>
-#include <boost/shared_ptr.hpp>
-#include <boost/scoped_array.hpp>
-#include <boost/noncopyable.hpp>
-#include <asiolink/io_address.h>
-#include <dhcp/pkt4.h>
-#include <dhcp/pkt6.h>
-
-namespace isc {
-
-namespace dhcp {
-/// @brief handles network interfaces, transmission and reception
-///
-/// IfaceMgr is an interface manager class that detects available network
-/// interfaces, configured addresses, link-local addresses, and provides
-/// API for using sockets.
-///
-class IfaceMgr : public boost::noncopyable {
-public:
-    /// type that defines list of addresses
-    typedef std::vector<isc::asiolink::IOAddress> AddressCollection;
-
-    /// maximum MAC address length (Infiniband uses 20 bytes)
-    static const unsigned int MAX_MAC_LEN = 20;
-
-    /// 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 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()) { }
-    };
-
-    /// type that holds a list of socket informations
-    typedef std::list<SocketInfo> SocketCollection;
-
-    /// @brief represents a single network interface
-    ///
-    /// Iface structure represents network interface with all useful
-    /// information, like name, interface index, MAC address and
-    /// list of assigned addresses
-    class Iface {
-    public:
-        /// @brief Iface constructor.
-        ///
-        /// Creates Iface object that represents network interface.
-        ///
-        /// @param name name of the interface
-        /// @param ifindex interface index (unique integer identifier)
-        Iface(const std::string& name, int ifindex);
-
-        /// @brief Returns full interface name as "ifname/ifindex" string.
-        ///
-        /// @return string with interface name
-        std::string getFullName() const;
-
-        /// @brief Returns link-layer address a plain text.
-        ///
-        /// @return MAC address as a plain text (string)
-        std::string getPlainMac() const;
-
-        /// @brief Returns interface index.
-        ///
-        /// @return interface index
-        uint16_t getIndex() const { return ifindex_; }
-
-        /// @brief Returns interface name.
-        ///
-        /// @return interface name
-        std::string getName() const { return name_; };
-
-        /// @brief Returns all interfaces available on an interface.
-        ///
-        /// Care should be taken to not use this collection after Iface object
-        /// ceases to exist. That is easy in most cases as Iface objects are
-        /// created by IfaceMgr that is a singleton an is expected to be
-        /// available at all time. We may revisit this if we ever decide to
-        /// implement dynamic interface detection, but such fancy feature would
-        /// mostly be useful for clients with wifi/vpn/virtual interfaces.
-        ///
-        /// @return collection of addresses
-        const AddressCollection& getAddresses() const { return addrs_; }
-
-        /// @brief Adds an address to an interface.
-        ///
-        /// This only adds an address to collection, it does not physically
-        /// configure address on actual network interface.
-        ///
-        /// @param addr address to be added
-        void addAddress(const isc::asiolink::IOAddress& addr) {
-            addrs_.push_back(addr);
-        }
-
-        /// @brief Deletes an address from an interface.
-        ///
-        /// This only deletes address from collection, it does not physically
-        /// remove address configuration from actual network interface.
-        ///
-        /// @param addr address to be removed.
-        ///
-        /// @return true if removal was successful (address was in collection),
-        ///         false otherwise
-        bool delAddress(const isc::asiolink::IOAddress& addr);
-
-        /// @brief Adds socket descriptor to an interface.
-        ///
-        /// @param socket SocketInfo structure that describes socket.
-        void addSocket(const SocketInfo& sock)
-            { sockets_.push_back(sock); }
-
-        /// @brief Closes socket.
-        ///
-        /// Closes socket and removes corresponding SocketInfo structure
-        /// from an interface.
-        ///
-        /// @param socket descriptor to be closed/removed.
-        /// @return true if there was such socket, false otherwise
-        bool delSocket(uint16_t sockfd);
-
-        /// socket used to sending data
-        /// TODO: this should be protected
-        SocketCollection sockets_;
-
-    protected:
-        /// network interface name
-        std::string name_;
-
-        /// interface index (a value that uniquely indentifies an interface)
-        int ifindex_;
-
-        /// list of assigned addresses
-        AddressCollection addrs_;
-
-        /// link-layer address
-        uint8_t mac_[MAX_MAC_LEN];
-
-        /// length of link-layer address (usually 6)
-        int mac_len_;
-    };
-
-    // TODO performance improvement: we may change this into
-    //      2 maps (ifindex-indexed and name-indexed) and
-    //      also hide it (make it public make tests easier for now)
-
-    /// type that holds a list of interfaces
-    typedef std::list<Iface> IfaceCollection;
-
-    /// IfaceMgr is a singleton class. This method returns reference
-    /// to its sole instance.
-    ///
-    /// @return the only existing instance of interface manager
-    static IfaceMgr& instance();
-
-    /// @brief Returns interface with specified interface index
-    ///
-    /// @param ifindex index of searched interface
-    ///
-    /// @return interface with requested index (or NULL if no such
-    ///         interface is present)
-    ///
-    Iface*
-    getIface(int ifindex);
-
-    /// @brief Returns interface with specified interface name
-    ///
-    /// @param ifname name of searched interface
-    ///
-    /// @return interface with requested name (or NULL if no such
-    ///         interface is present)
-    ///
-    Iface*
-    getIface(const std::string& ifname);
-
-    /// @brief Return most suitable socket for transmitting specified IPv6 packet.
-    ///
-    /// This method takes Pkt6 (see overloaded implementation that takes
-    /// Pkt4) and chooses appropriate socket to send it. This method
-    /// may throw BadValue if specified packet does not have outbound
-    /// interface specified, no such interface exists, or specified
-    /// interface does not have any appropriate sockets open.
-    ///
-    /// @param pkt a packet to be transmitted
-    ///
-    /// @return a socket descriptor
-    uint16_t getSocket(const isc::dhcp::Pkt6& pkt);
-
-    /// @brief Return most suitable socket for transmitting specified IPv6 packet.
-    ///
-    /// This method takes Pkt4 (see overloaded implementation that takes
-    /// Pkt6) and chooses appropriate socket to send it. This method
-    /// may throw BadValue if specified packet does not have outbound
-    /// interface specified, no such interface exists, or specified
-    /// interface does not have any appropriate sockets open.
-    ///
-    /// @param pkt a packet to be transmitted
-    ///
-    /// @return a socket descriptor
-    uint16_t getSocket(const isc::dhcp::Pkt4& pkt);
-
-    /// debugging method that prints out all available interfaces
-    ///
-    /// @param out specifies stream to print list of interfaces to
-    void
-    printIfaces(std::ostream& out = std::cout);
-
-    /// @brief Sends an IPv6 packet.
-    ///
-    /// Sends an IPv6 packet. All parameters for actual transmission are specified in
-    /// Pkt6 structure itself. That includes destination address, src/dst port
-    /// and interface over which data will be sent.
-    ///
-    /// @param pkt packet to be sent
-    ///
-    /// @return true if sending was successful
-    bool send(boost::shared_ptr<Pkt6>& pkt);
-
-    /// @brief Sends an IPv4 packet.
-    ///
-    /// Sends an IPv4 packet. All parameters for actual transmission are specified
-    /// in Pkt4 structure itself. That includes destination address, src/dst
-    /// port and interface over which data will be sent.
-    ///
-    /// @param pkt a packet to be sent
-    ///
-    /// @return true if sending was successful
-    bool send(boost::shared_ptr<Pkt4>& pkt);
-
-    /// @brief Tries to receive IPv6 packet over open IPv6 sockets.
-    ///
-    /// Attempts to receive a single IPv6 packet of any of the open IPv6 sockets.
-    /// If reception is successful and all information about its sender
-    /// are obtained, Pkt6 object is created and returned.
-    ///
-    /// TODO Start using select() and add timeout to be able
-    /// to not wait infinitely, but rather do something useful
-    /// (e.g. remove expired leases)
-    ///
-    /// @return Pkt6 object representing received packet (or NULL)
-    boost::shared_ptr<Pkt6> receive6();
-
-    /// @brief Tries to receive IPv4 packet over open IPv4 sockets.
-    ///
-    /// Attempts to receive a single IPv4 packet of any of the open IPv4 sockets.
-    /// If reception is successful and all information about its sender
-    /// are obtained, Pkt4 object is created and returned.
-    ///
-    /// TODO Start using select() and add timeout to be able
-    /// to not wait infinitely, but rather do something useful
-    /// (e.g. remove expired leases)
-    ///
-    /// @return Pkt4 object representing received packet (or NULL)
-    boost::shared_ptr<Pkt4> receive4();
-
-    /// Opens UDP/IP socket and binds it to address, interface and port.
-    ///
-    /// Specific type of socket (UDP/IPv4 or UDP/IPv6) depends on passed addr
-    /// family.
-    ///
-    /// @param ifname name of the interface
-    /// @param addr address to be bound.
-    /// @param port UDP port.
-    ///
-    /// Method will throw if socket creation, socket binding or multicast
-    /// join fails.
-    ///
-    /// @return socket descriptor, if socket creation, binding and multicast
-    /// group join were all successful.
-    int openSocket(const std::string& ifname,
-                   const isc::asiolink::IOAddress& addr, int port);
-
-    /// Opens IPv6 sockets on detected interfaces.
-    ///
-    /// Will throw exception if socket creation fails.
-    ///
-    /// @param port specifies port number (usually DHCP6_SERVER_PORT)
-    void openSockets(uint16_t port);
-
-
-    /// @brief Closes all open sockets.
-    /// Is used in destructor, but also from Dhcpv4_srv and Dhcpv6_srv classes.
-    void closeSockets();
-
-    // don't use private, we need derived classes in tests
-protected:
-
-    /// @brief Protected constructor.
-    ///
-    /// Protected constructor. This is a singleton class. We don't want
-    /// anyone to create instances of IfaceMgr. Use instance() method instead.
-    IfaceMgr();
-
-    ~IfaceMgr();
-
-    /// @brief Opens IPv4 socket.
-    ///
-    /// Please do not use this method directly. Use openSocket instead.
-    ///
-    /// This method may throw exception if socket creation fails.
-    ///
-    /// @param iface reference to interface structure.
-    /// @param addr an address the created socket should be bound to
-    /// @param port a port that created socket should be bound to
-    ///
-    /// @return socket descriptor
-    int openSocket4(Iface& iface, const isc::asiolink::IOAddress& addr, int port);
-
-    /// @brief Opens IPv6 socket.
-    ///
-    /// Please do not use this method directly. Use openSocket instead.
-    ///
-    /// This method may throw exception if socket creation fails.
-    ///
-    /// @param iface reference to interface structure.
-    /// @param addr an address the created socket should be bound to
-    /// @param port a port that created socket should be bound to
-    ///
-    /// @return socket descriptor
-    int openSocket6(Iface& iface, const isc::asiolink::IOAddress& addr, int 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
-    /// it offers stub implementation. First interface name and link-local
-    /// IPv6 address is read from intefaces.txt file.
-    void
-    detectIfaces();
-
-    // TODO: having 2 maps (ifindex->iface and ifname->iface would)
-    //      probably be better for performance reasons
-
-    /// List of available interfaces
-    IfaceCollection ifaces_;
-
-    /// a pointer to a sole instance of this class (a singleton)
-    static IfaceMgr * instance_;
-
-    // TODO: Also keep this interface on Iface once interface detection
-    // is implemented. We may need it e.g. to close all sockets on
-    // specific interface
-    //int recvsock_; // TODO: should be fd_set eventually, but we have only
-    //int sendsock_; // 2 sockets for now. Will do for until next release
-
-    // we can't use the same socket, as receiving socket
-    // is bound to multicast address. And we all know what happens
-    // to people who try to use multicast as source address.
-
-    /// length of the control_buf_ array
-    int control_buf_len_;
-
-    /// control-buffer, used in transmission and reception
-    boost::scoped_array<char> control_buf_;
-
-private:
-
-    /// creates a single instance of this class (a singleton implementation)
-    static void
-    instanceCreate();
-
-    /// @brief Joins IPv6 multicast group on a socket.
-    ///
-    /// Socket must be created and bound to an address. Note that this
-    /// address is different than the multicast address. For example DHCPv6
-    /// server should bind its socket to link-local address (fe80::1234...)
-    /// and later join ff02::1:2 multicast group.
-    ///
-    /// @param sock socket fd (socket must be bound)
-    /// @param ifname interface name (for link-scoped multicast groups)
-    /// @param mcast multicast address to join (e.g. "ff02::1:2")
-    ///
-    /// @return true if multicast join was successful
-    ///
-    bool
-    joinMcast(int sock, const std::string& ifname,
-              const std::string& mcast);
-
-};
-
-}; // namespace isc::dhcp
-}; // namespace isc
-
-#endif
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index f37194c..6a0844f 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -43,10 +43,8 @@ if HAVE_GTEST
 
 TESTS += dhcp6_unittests
 
-dhcp6_unittests_SOURCES = ../iface_mgr.h ../iface_mgr.cc
-dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc
+dhcp6_unittests_SOURCES = ../dhcp6_srv.h ../dhcp6_srv.cc
 dhcp6_unittests_SOURCES += dhcp6_unittests.cc
-dhcp6_unittests_SOURCES += iface_mgr_unittest.cc
 dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
 
 dhcp6_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
@@ -54,7 +52,7 @@ dhcp6_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 dhcp6_unittests_LDADD = $(GTEST_LDADD)
 dhcp6_unittests_LDADD += $(SQLITE_LIBS)
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 endif
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 50f37af..092dd2e 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -57,7 +57,7 @@ TEST_F(Dhcpv6SrvTest, basic) {
     // interfaces.txt instead. It will pretend to have detected
     // fe80::1234 link-local address on eth0 interface. Obviously
     // an attempt to bind this socket will fail.
-    Dhcpv6Srv* srv = 0;
+    Dhcpv6Srv* srv = NULL;
     ASSERT_NO_THROW( {
         // open an unpriviledged port
         srv = new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000);
@@ -67,7 +67,7 @@ TEST_F(Dhcpv6SrvTest, basic) {
 }
 
 TEST_F(Dhcpv6SrvTest, Solicit_basic) {
-    NakedDhcpv6Srv * srv = 0;
+    NakedDhcpv6Srv* srv = NULL;
     ASSERT_NO_THROW( srv = new NakedDhcpv6Srv(); );
 
     // a dummy content for client-id
@@ -116,7 +116,7 @@ TEST_F(Dhcpv6SrvTest, Solicit_basic) {
     boost::shared_ptr<Option> tmp = reply->getOption(D6O_IA_NA);
     ASSERT_TRUE( tmp );
 
-    Option6IA * reply_ia = dynamic_cast<Option6IA*> ( tmp.get() );
+    Option6IA* reply_ia = dynamic_cast<Option6IA*> ( tmp.get() );
     EXPECT_EQ( 234, reply_ia->getIAID() );
 
     // check that there's an address included
diff --git a/src/bin/dhcp6/tests/dhcp6_test.py b/src/bin/dhcp6/tests/dhcp6_test.py
index 5ae1f5e..d63e04d 100644
--- a/src/bin/dhcp6/tests/dhcp6_test.py
+++ b/src/bin/dhcp6/tests/dhcp6_test.py
@@ -59,7 +59,13 @@ class TestDhcpv6Daemon(unittest.TestCase):
         # kill this process
         # XXX: b10-dhcp6 is too dumb to understand 'shutdown' command for now,
         #      so let's just kill the bastard
-        os.kill(pi.pid, signal.SIGTERM)
+
+        # TODO: Ignore errors for now. This test will be more thorough once ticket #1503
+        # (passing port number to b10-dhcp6 daemon) is implemented.
+        try:
+            os.kill(pi.pid, signal.SIGTERM)
+        except OSError:
+            print("Ignoring failed kill attempt. Process is dead already.")
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/src/bin/dhcp6/tests/iface_mgr_unittest.cc b/src/bin/dhcp6/tests/iface_mgr_unittest.cc
deleted file mode 100644
index 0c54780..0000000
--- a/src/bin/dhcp6/tests/iface_mgr_unittest.cc
+++ /dev/null
@@ -1,518 +0,0 @@
-// Copyright (C) 2011  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 <iostream>
-#include <fstream>
-#include <sstream>
-
-#include <arpa/inet.h>
-#include <gtest/gtest.h>
-
-#include <asiolink/io_address.h>
-#include <dhcp/pkt6.h>
-#include <dhcp6/iface_mgr.h>
-#include <dhcp/dhcp4.h>
-
-using namespace std;
-using namespace isc;
-using namespace isc::asiolink;
-using namespace isc::dhcp;
-
-// name of loopback interface detection
-char LOOPBACK[32] = "lo";
-
-namespace {
-const char* const INTERFACE_FILE = TEST_DATA_BUILDDIR "/interfaces.txt";
-
-class NakedIfaceMgr: public IfaceMgr {
-    // "naked" Interface Manager, exposes internal fields
-public:
-    NakedIfaceMgr() { }
-    IfaceCollection & getIfacesLst() { return ifaces_; }
-};
-
-// dummy class for now, but this will be expanded when needed
-class IfaceMgrTest : public ::testing::Test {
-public:
-    IfaceMgrTest() {
-    }
-
-    void createLoInterfacesTxt() {
-        unlink(INTERFACE_FILE);
-        fstream fakeifaces(INTERFACE_FILE, ios::out|ios::trunc);
-        fakeifaces << LOOPBACK << " ::1";
-        fakeifaces.close();
-    }
-};
-
-// 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.
-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) {
-        cout << "This is Linux, using lo as loopback." << endl;
-        sprintf(LOOPBACK, "lo");
-    } else if (if_nametoindex("lo0")>0) {
-        cout << "This is BSD, using lo0 as loopback." << endl;
-        sprintf(LOOPBACK, "lo0");
-    } else {
-        cout << "Failed to detect loopback interface. Neither "
-             << "lo or lo0 worked. I give up." << endl;
-        ASSERT_TRUE(false);
-    }
-}
-
-// uncomment this test to create packet writer. It will
-// write incoming DHCPv6 packets as C arrays. That is useful
-// for generating test sequences based on actual traffic
-//
-// TODO: this potentially should be moved to a separate tool
-//
-
-#if 0
-TEST_F(IfaceMgrTest, dhcp6Sniffer) {
-    // testing socket operation in a portable way is tricky
-    // without interface detection implemented
-
-    unlink("interfaces.txt");
-
-    ofstream interfaces("interfaces.txt", ios::ate);
-    interfaces << "eth0 fe80::21e:8cff:fe9b:7349";
-    interfaces.close();
-
-    NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
-
-    Pkt6 * pkt = 0;
-    int cnt = 0;
-    cout << "---8X-----------------------------------------" << endl;
-    while (true) {
-        pkt = ifacemgr->receive();
-
-        cout << "// this code is autogenerated. Do NOT edit." << endl;
-        cout << "// Received " << pkt->data_len_ << " bytes packet:" << endl;
-        cout << "Pkt6 *capture" << cnt++ << "() {" << endl;
-        cout << "    Pkt6* pkt;" << endl;
-        cout << "    pkt = new Pkt6(" << pkt->data_len_ << ");" << endl;
-        cout << "    pkt->remote_port_ = " << pkt-> remote_port_ << ";" << endl;
-        cout << "    pkt->remote_addr_ = IOAddress(\""
-             << pkt->remote_addr_.toText() << "\");" << endl;
-        cout << "    pkt->local_port_ = " << pkt-> local_port_ << ";" << endl;
-        cout << "    pkt->local_addr_ = IOAddress(\""
-             << pkt->local_addr_.toText() << "\");" << endl;
-        cout << "    pkt->ifindex_ = " << pkt->ifindex_ << ";" << endl;
-        cout << "    pkt->iface_ = \"" << pkt->iface_ << "\";" << endl;
-
-        // TODO it is better to declare an array and then memcpy it to
-        // packet.
-        for (int i=0; i< pkt->data_len_; i++) {
-            cout << "    pkt->data_[" << i << "]="
-                 << (int)(unsigned char)pkt->data_[i] << "; ";
-            if (!(i%4))
-                cout << endl;
-        }
-        cout << endl;
-        cout << "    return (pkt);" << endl;
-        cout << "}" << endl << endl;
-
-        delete pkt;
-    }
-    cout << "---8X-----------------------------------------" << endl;
-
-    // never happens. Infinite loop is infinite
-    delete pkt;
-    delete ifacemgr;
-}
-#endif
-
-TEST_F(IfaceMgrTest, basic) {
-    // checks that IfaceManager can be instantiated
-
-    IfaceMgr & ifacemgr = IfaceMgr::instance();
-    ASSERT_TRUE(&ifacemgr != 0);
-}
-
-TEST_F(IfaceMgrTest, ifaceClass) {
-    // basic tests for Iface inner class
-
-    IfaceMgr::Iface * iface = new IfaceMgr::Iface("eth5", 7);
-
-    EXPECT_STREQ("eth5/7", iface->getFullName().c_str());
-
-    delete iface;
-
-}
-
-// TODO: Implement getPlainMac() test as soon as interface detection
-// is implemented.
-TEST_F(IfaceMgrTest, getIface) {
-
-    cout << "Interface checks. Please ignore socket binding errors." << endl;
-    NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
-
-    // interface name, ifindex
-    IfaceMgr::Iface iface1("lo1", 1);
-    IfaceMgr::Iface iface2("eth5", 2);
-    IfaceMgr::Iface iface3("en3", 5);
-    IfaceMgr::Iface iface4("e1000g0", 3);
-
-    // note: real interfaces may be detected as well
-    ifacemgr->getIfacesLst().push_back(iface1);
-    ifacemgr->getIfacesLst().push_back(iface2);
-    ifacemgr->getIfacesLst().push_back(iface3);
-    ifacemgr->getIfacesLst().push_back(iface4);
-
-    cout << "There are " << ifacemgr->getIfacesLst().size()
-         << " interfaces." << endl;
-    for (IfaceMgr::IfaceCollection::iterator iface=ifacemgr->getIfacesLst().begin();
-         iface != ifacemgr->getIfacesLst().end();
-         ++iface) {
-        cout << "  " << iface->getFullName() << endl;
-    }
-
-
-    // check that interface can be retrieved by ifindex
-    IfaceMgr::Iface * tmp = ifacemgr->getIface(5);
-    // ASSERT_NE(NULL, tmp); is not supported. hmmmm.
-    ASSERT_TRUE( tmp != NULL );
-
-    EXPECT_EQ( "en3", tmp->getName() );
-    EXPECT_EQ(5, tmp->getIndex());
-
-    // check that interface can be retrieved by name
-    tmp = ifacemgr->getIface("lo1");
-    ASSERT_TRUE( tmp != NULL );
-
-    EXPECT_EQ( "lo1", tmp->getName() );
-    EXPECT_EQ(1, tmp->getIndex());
-
-    // check that non-existing interfaces are not returned
-    EXPECT_EQ(static_cast<void*>(NULL), ifacemgr->getIface("wifi0") );
-
-    delete ifacemgr;
-}
-
-TEST_F(IfaceMgrTest, detectIfaces) {
-
-    // test detects that interfaces can be detected
-    // there is no code for that now, but interfaces are
-    // read from file
-    fstream fakeifaces(INTERFACE_FILE, ios::out|ios::trunc);
-    fakeifaces << "eth0 fe80::1234";
-    fakeifaces.close();
-
-    // this is not usable on systems that don't have eth0
-    // interfaces. Nevertheless, this fake interface should
-    // be on list, but if_nametoindex() will fail.
-
-    NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
-
-    ASSERT_TRUE( ifacemgr->getIface("eth0") != NULL );
-
-    IfaceMgr::Iface * eth0 = ifacemgr->getIface("eth0");
-
-    // there should be one address
-    IfaceMgr::AddressCollection addrs = eth0->getAddresses();
-    ASSERT_EQ(1, addrs.size());
-
-    IOAddress addr = *addrs.begin();
-
-    EXPECT_STREQ( "fe80::1234", addr.toText().c_str() );
-
-    delete ifacemgr;
-}
-
-TEST_F(IfaceMgrTest, sockets6) {
-    // testing socket operation in a portable way is tricky
-    // without interface detection implemented
-
-    createLoInterfacesTxt();
-
-    NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
-
-    IOAddress loAddr("::1");
-
-    Pkt6 pkt6(128);
-    pkt6.iface_ = LOOPBACK;
-
-    // bind multicast socket to port 10547
-    int socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
-    EXPECT_GT(socket1, 0); // socket > 0
-
-    EXPECT_EQ(socket1, ifacemgr->getSocket(pkt6));
-
-    // bind unicast socket to port 10548
-    int socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10548);
-    EXPECT_GT(socket2, 0);
-
-    // removed code for binding socket twice to the same address/port
-    // as it caused problems on some platforms (e.g. Mac OS X)
-
-    close(socket1);
-    close(socket2);
-
-    delete ifacemgr;
-}
-
-// TODO: disabled due to other naming on various systems
-// (lo in Linux, lo0 in BSD systems)
-TEST_F(IfaceMgrTest, DISABLED_sockets6Mcast) {
-    // testing socket operation in a portable way is tricky
-    // without interface detection implemented
-
-    NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
-
-    IOAddress loAddr("::1");
-    IOAddress mcastAddr("ff02::1:2");
-
-    // bind multicast socket to port 10547
-    int socket1 = ifacemgr->openSocket(LOOPBACK, mcastAddr, 10547);
-    EXPECT_GT(socket1, 0); // socket > 0
-
-    // expect success. This address/port is already bound, but
-    // we are using SO_REUSEADDR, so we can bind it twice
-    int socket2 = ifacemgr->openSocket(LOOPBACK, mcastAddr, 10547);
-    EXPECT_GT(socket2, 0);
-
-    // there's no good way to test negative case here.
-    // we would need non-multicast interface. We will be able
-    // to iterate thru available interfaces and check if there
-    // are interfaces without multicast-capable flag.
-
-    close(socket1);
-    close(socket2);
-
-    delete ifacemgr;
-}
-
-TEST_F(IfaceMgrTest, sendReceive6) {
-
-    // testing socket operation in a portable way is tricky
-    // without interface detection implemented
-    createLoInterfacesTxt();
-
-    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
-
-    // let's assume that every supported OS have lo interface
-    IOAddress loAddr("::1");
-    int socket1 = 0, socket2 = 0;
-    EXPECT_NO_THROW(
-        socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
-        socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10546);
-    );
-
-    EXPECT_GT(socket1, 0);
-    EXPECT_GT(socket2, 0);
-
-    boost::shared_ptr<Pkt6> sendPkt(new Pkt6(128) );
-
-    // prepare dummy payload
-    for (int i=0;i<128; i++) {
-        sendPkt->data_[i] = i;
-    }
-
-    sendPkt->remote_port_ = 10547;
-    sendPkt->remote_addr_ = IOAddress("::1");
-    sendPkt->ifindex_ = 1;
-    sendPkt->iface_ = LOOPBACK;
-
-    boost::shared_ptr<Pkt6> rcvPkt;
-
-    EXPECT_EQ(true, ifacemgr->send(sendPkt));
-
-    rcvPkt = ifacemgr->receive6();
-
-    ASSERT_TRUE( rcvPkt ); // received our own packet
-
-    // let's check that we received what was sent
-    EXPECT_EQ(sendPkt->data_len_, rcvPkt->data_len_);
-    EXPECT_EQ(0, memcmp(&sendPkt->data_[0], &rcvPkt->data_[0],
-                        rcvPkt->data_len_) );
-
-    EXPECT_EQ(sendPkt->remote_addr_.toText(), rcvPkt->remote_addr_.toText());
-
-    // since we opened 2 sockets on the same interface and none of them is multicast,
-    // none is preferred over the other for sending data, so we really should not
-    // assume the one or the other will always be choosen for sending data. Therefore
-    // we should accept both values as source ports.
-    EXPECT_TRUE( (rcvPkt->remote_port_ == 10546) || (rcvPkt->remote_port_ == 10547) );
-
-    delete ifacemgr;
-}
-
-TEST_F(IfaceMgrTest, socket4) {
-
-    createLoInterfacesTxt();
-    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
-
-    // Let's assume that every supported OS have lo interface.
-    IOAddress loAddr("127.0.0.1");
-    // Use unprivileged port (it's convenient for running tests as non-root).
-    int socket1 = 0;
-
-    EXPECT_NO_THROW(
-        socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, DHCP4_SERVER_PORT + 10000);
-    );
-
-    EXPECT_GT(socket1, 0);
-
-    Pkt4 pkt(DHCPDISCOVER, 1234);
-    pkt.setIface(LOOPBACK);
-
-    // Expect that we get the socket that we just opened.
-    EXPECT_EQ(socket1, ifacemgr->getSocket(pkt));
-
-    close(socket1);
-
-    delete ifacemgr;
-}
-
-// Test the Iface structure itself
-TEST_F(IfaceMgrTest, iface) {
-    IfaceMgr::Iface* iface = 0;
-    EXPECT_NO_THROW(
-        iface = new IfaceMgr::Iface("eth0",1);
-    );
-
-    EXPECT_EQ("eth0", iface->getName());
-    EXPECT_EQ(1, iface->getIndex());
-    EXPECT_EQ("eth0/1", iface->getFullName());
-
-    // Let's make a copy of this address collection.
-    IfaceMgr::AddressCollection addrs = iface->getAddresses();
-
-    EXPECT_EQ(0, addrs.size());
-
-    IOAddress addr1("192.0.2.6");
-    iface->addAddress(addr1);
-
-    addrs = iface->getAddresses();
-    ASSERT_EQ(1, addrs.size());
-    EXPECT_EQ("192.0.2.6", addrs.at(0).toText());
-
-    // No such address, should return false.
-    EXPECT_FALSE(iface->delAddress(IOAddress("192.0.8.9")));
-
-    // This address is present, delete it!
-    EXPECT_TRUE(iface->delAddress(IOAddress("192.0.2.6")));
-
-    // Not really necessary, previous reference still points to the same
-    // collection. Let's do it anyway, as test code may serve as example
-    // usage code as well.
-    addrs = iface->getAddresses();
-
-    EXPECT_EQ(0, addrs.size());
-
-    EXPECT_NO_THROW(
-        delete iface;
-    );
-}
-
-TEST_F(IfaceMgrTest, socketInfo) {
-
-    // check that socketinfo for IPv4 socket is functional
-    IfaceMgr::SocketInfo sock1(7, IOAddress("192.0.2.56"), DHCP4_SERVER_PORT + 7);
-    EXPECT_EQ(7, sock1.sockfd_);
-    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 socketinfo for IPv6 socket is functional
-    IfaceMgr::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_);
-
-    // now let's test if IfaceMgr handles socket info properly
-    createLoInterfacesTxt();
-    NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
-    IfaceMgr::Iface* loopback = ifacemgr->getIface(LOOPBACK);
-    ASSERT_TRUE(loopback);
-    loopback->addSocket(sock1);
-    loopback->addSocket(sock2);
-
-    Pkt6 pkt6(100);
-
-    // pkt6 dos not have interface set yet
-    EXPECT_THROW(
-        ifacemgr->getSocket(pkt6),
-        BadValue
-    );
-
-    // try to send over non-existing interface
-    pkt6.iface_ = "nosuchinterface45";
-    EXPECT_THROW(
-        ifacemgr->getSocket(pkt6),
-        BadValue
-    );
-
-    // this will work
-    pkt6.iface_ = LOOPBACK;
-    EXPECT_EQ(9, ifacemgr->getSocket(pkt6));
-
-    bool deleted = false;
-    EXPECT_NO_THROW(
-        deleted = ifacemgr->getIface(LOOPBACK)->delSocket(9);
-    );
-    EXPECT_EQ(true, deleted);
-
-    // it should throw again, there's no usable socket anymore
-    EXPECT_THROW(
-        ifacemgr->getSocket(pkt6),
-        Unexpected
-    );
-
-    // repeat for pkt4
-    Pkt4 pkt4(DHCPDISCOVER, 1);
-
-    // pkt4 does not have interface set yet.
-    EXPECT_THROW(
-        ifacemgr->getSocket(pkt4),
-        BadValue
-    );
-
-    // Try to send over non-existing interface.
-    pkt4.setIface("nosuchinterface45");
-    EXPECT_THROW(
-        ifacemgr->getSocket(pkt4),
-        BadValue
-    );
-
-    // Socket info is set, packet has well defined interface. It should work.
-    pkt4.setIface(LOOPBACK);
-    EXPECT_EQ(7, ifacemgr->getSocket(pkt4));
-
-    EXPECT_NO_THROW(
-        ifacemgr->getIface(LOOPBACK)->delSocket(7);
-    );
-
-    // It should throw again, there's no usable socket anymore.
-    EXPECT_THROW(
-        ifacemgr->getSocket(pkt4),
-        Unexpected
-    );
-
-    delete ifacemgr;
-}
-
-}
diff --git a/src/bin/xfrout/b10-xfrout.xml b/src/bin/xfrout/b10-xfrout.xml
index 4f6a7fa..87d0267 100644
--- a/src/bin/xfrout/b10-xfrout.xml
+++ b/src/bin/xfrout/b10-xfrout.xml
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>December 1, 2010</date>
+    <date>December 15, 2011</date>
   </refentryinfo>
 
   <refmeta>
@@ -52,7 +52,7 @@
   <refsect1>
     <title>DESCRIPTION</title>
     <para>The <command>b10-xfrout</command> daemon provides the BIND 10
-      outgoing DNS zone transfer service.
+      outgoing DNS zone transfer service using AXFR or IXFR.
       It is also used to send outgoing NOTIFY messages.
       Normally it is started by the
       <citerefentry><refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum></citerefentry>
@@ -67,13 +67,13 @@
  process?, and then the socket and xfr request is sent to xfrout.
 -->
 
+<!-- TODO: IXFR from differences, DDNS, UDP socket passing -->
     <note><simpara>
-      This development prototype release only supports AXFR.
-      IXFR is not implemented.
+      Currently IXFR only works if it gets the zone via
+      <command>b10-xfrin</command> and only on TCP.
     </simpara></note>
 
     <para>
-<!-- TODO: does it really use msgq? what for? -->
       This daemon communicates with BIND 10 over a
       <citerefentry><refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum></citerefentry>
       C-Channel connection.  If this connection is not established,
@@ -100,15 +100,15 @@
     <para>
       <varname>tsig_key_ring</varname>
       A list of TSIG keys (each of which is in the form of
-      name:base64-key[:algorithm]) used for access control on transfer
-      requests.
+      <replaceable>name:base64-key[:algorithm]</replaceable>)
+      used for access control on transfer requests.
       The default is an empty list.
     </para>
     <para>
       <varname>transfer_acl</varname>
       A list of ACL elements that apply to all transfer requests by
-      default (unless overridden in zone_config).  See the BIND 10
-      guide for configuration examples.
+      default (unless overridden in <varname>zone_config</varname>).
+      See the <citetitle>BIND 10 Guide</citetitle> for configuration examples.
       The default is an element that allows any transfer requests.
     </para>
     <para>
@@ -117,9 +117,9 @@
       configuration concerning <command>b10-xfrout</command>.
       The supported names of each object are "origin" (the origin
       name of the zone), "class" (the RR class of the zone, optional,
-      default to "IN"), and "acl_element" (ACL only applicable to
+      default to "IN"), and "transfer_acl" (ACL only applicable to
       transfer requests for that zone).
-      See the BIND 10 guide for configuration examples.
+      See the <citetitle>BIND 10 Guide</citetitle> for configuration examples.
       The default is an empty list, that is, no zone specific configuration.
     </para>
     <para>
diff --git a/src/bin/zonemgr/b10-zonemgr.xml b/src/bin/zonemgr/b10-zonemgr.xml
index 00f5d04..5ea6041 100644
--- a/src/bin/zonemgr/b10-zonemgr.xml
+++ b/src/bin/zonemgr/b10-zonemgr.xml
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>May 19, 2011</date>
+    <date>December 8, 2011</date>
   </refentryinfo>
 
   <refmeta>
@@ -107,15 +107,20 @@
 
     <para>
       <varname>refresh_jitter</varname>
+      is used to provide a time range for randomizing the refresh
+      and retry timers to help avoid many zones needing to do a refresh
+      or retry at the same time.
       This value is a real number.
-      The maximum amount is 0.5.
-      The default is 0.25.
+      The maximum amount is 0.5 (the new timer will be within
+      half the original time).
+      The default is 0.25 (up to a quarter sooner).
+      Set to 0 to disable this jitter.
     </para>
-<!-- TODO: needs to be documented -->
-<!-- TODO:      Set to 0 to disable the jitter.   -->
 
     <para>
       <varname>reload_jitter</varname>
+<!--      is used to provide a slight random variation -->
+<!-- TODO: ask what the purpose of this is and why 0.75. -->
       This value is a real number.
       The default is 0.75.
     </para>
@@ -224,14 +229,6 @@
 
   </refsect1>
 -->
-<!--
-  <refsect1>
-    <title>FILES</title>
-    <para>
-    <filename>/tmp/auth_xfrout_conn</filename>
-    </para>
-  </refsect1>
--->
 
   <refsect1>
     <title>SEE ALSO</title>
@@ -249,9 +246,6 @@
         <refentrytitle>b10-xfrin</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citerefentry>
-        <refentrytitle>b10-xfrout</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
         <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citetitle>BIND 10 Guide</citetitle>.
diff --git a/src/bin/zonemgr/tests/Makefile.am b/src/bin/zonemgr/tests/Makefile.am
index 769d332..8c6b904 100644
--- a/src/bin/zonemgr/tests/Makefile.am
+++ b/src/bin/zonemgr/tests/Makefile.am
@@ -20,6 +20,7 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
+	B10_FROM_BUILD=$(abs_top_builddir) \
 	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/zonemgr:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/bin/zonemgr/tests/zonemgr_test.py b/src/bin/zonemgr/tests/zonemgr_test.py
index 80e41b3..600453d 100644
--- a/src/bin/zonemgr/tests/zonemgr_test.py
+++ b/src/bin/zonemgr/tests/zonemgr_test.py
@@ -48,28 +48,16 @@ class MySession():
     def group_recvmsg(self, nonblock, seq):
         return None, None
 
-class FakeConfig:
+class FakeCCSession(isc.config.ConfigData):
     def __init__(self):
-        self.zone_list = []
-        self.set_zone_list_from_name_classes([ZONE_NAME_CLASS1_IN,
-                                              ZONE_NAME_CLASS2_CH])
-    def set_zone_list_from_name_classes(self, zones):
-        self.zone_list = map(lambda nc: {"name": nc[0], "class": nc[1]}, zones)
-    def get(self, name):
-        if name == 'lowerbound_refresh':
-            return LOWERBOUND_REFRESH
-        elif name == 'lowerbound_retry':
-            return LOWERBOUND_RETRY
-        elif name == 'max_transfer_timeout':
-            return MAX_TRANSFER_TIMEOUT
-        elif name == 'refresh_jitter':
-            return REFRESH_JITTER
-        elif name == 'reload_jitter':
-            return RELOAD_JITTER
-        elif name == 'secondary_zones':
-            return self.zone_list
+        module_spec = isc.config.module_spec_from_file(SPECFILE_LOCATION)
+        ConfigData.__init__(self, module_spec)
+
+    def get_remote_config_value(self, module_name, identifier):
+        if module_name == "Auth" and identifier == "database_file":
+            return "initdb.file", False
         else:
-            raise ValueError('Uknown config option')
+            return "unknown", False
 
 class MyZonemgrRefresh(ZonemgrRefresh):
     def __init__(self):
@@ -92,7 +80,7 @@ class MyZonemgrRefresh(ZonemgrRefresh):
         sqlite3_ds.get_zone_soa = get_zone_soa
 
         ZonemgrRefresh.__init__(self, MySession(), "initdb.file",
-            self._slave_socket, FakeConfig())
+                                self._slave_socket, FakeCCSession())
         current_time = time.time()
         self._zonemgr_refresh_info = {
          ('example.net.', 'IN'): {
@@ -112,6 +100,7 @@ class TestZonemgrRefresh(unittest.TestCase):
         self.stderr_backup = sys.stderr
         sys.stderr = open(os.devnull, 'w')
         self.zone_refresh = MyZonemgrRefresh()
+        self.cc_session = FakeCCSession()
 
     def test_random_jitter(self):
         max = 100025.120
@@ -458,7 +447,23 @@ class TestZonemgrRefresh(unittest.TestCase):
                     "secondary_zones": [ { "name": "example.net.",
                                            "class": "IN" } ]
                 }
-        self.zone_refresh.update_config_data(config_data)
+        self.zone_refresh.update_config_data(config_data, self.cc_session)
+        self.assertTrue(("example.net.", "IN") in
+                        self.zone_refresh._zonemgr_refresh_info)
+
+        # make sure it does fail if we don't provide a name
+        config_data = {
+                    "secondary_zones": [ { "class": "IN" } ]
+                }
+        self.assertRaises(ZonemgrException,
+                          self.zone_refresh.update_config_data,
+                          config_data, self.cc_session)
+
+        # But not if we don't provide a class
+        config_data = {
+                    "secondary_zones": [ { "name": "example.net." } ]
+                }
+        self.zone_refresh.update_config_data(config_data, self.cc_session)
         self.assertTrue(("example.net.", "IN") in
                         self.zone_refresh._zonemgr_refresh_info)
 
@@ -471,7 +476,7 @@ class TestZonemgrRefresh(unittest.TestCase):
                     "reload_jitter" : 0.75,
                     "secondary_zones": []
                 }
-        self.zone_refresh.update_config_data(config_data)
+        self.zone_refresh.update_config_data(config_data, self.cc_session)
         self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
         self.assertEqual(30, self.zone_refresh._lowerbound_retry)
         self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
@@ -482,7 +487,7 @@ class TestZonemgrRefresh(unittest.TestCase):
         config_data = {
                     "reload_jitter" : 0.35,
                 }
-        self.zone_refresh.update_config_data(config_data)
+        self.zone_refresh.update_config_data(config_data, self.cc_session)
         self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
         self.assertEqual(30, self.zone_refresh._lowerbound_retry)
         self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
@@ -500,7 +505,7 @@ class TestZonemgrRefresh(unittest.TestCase):
                     "secondary_zones": [ { "name": "doesnotexist",
                                            "class": "IN" } ]
                 }
-        self.zone_refresh.update_config_data(config_data)
+        self.zone_refresh.update_config_data(config_data, self.cc_session)
         name_class = ("doesnotexist.", "IN")
         self.assertTrue(self.zone_refresh._zonemgr_refresh_info[name_class]["zone_soa_rdata"]
                         is None)
@@ -520,7 +525,7 @@ class TestZonemgrRefresh(unittest.TestCase):
                     "reload_jitter" : 0.75,
                     "secondary_zones": []
                 }
-        self.zone_refresh.update_config_data(config_data)
+        self.zone_refresh.update_config_data(config_data, self.cc_session)
         self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
         self.assertEqual(30, self.zone_refresh._lowerbound_retry)
         self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
@@ -536,45 +541,67 @@ class TestZonemgrRefresh(unittest.TestCase):
         self.assertFalse(listener.is_alive())
 
     def test_secondary_zones(self):
+        def zone_list_from_name_classes(zones):
+            return map(lambda nc: {"name": nc[0], "class": nc[1]}, zones)
+
         """Test that we can modify the list of secondary zones"""
-        config = FakeConfig()
-        config.zone_list = []
+        config = self.cc_session.get_full_config()
+        config['secondary_zones'] = []
         # First, remove everything
-        self.zone_refresh.update_config_data(config)
+        self.zone_refresh.update_config_data(config, self.cc_session)
         self.assertEqual(self.zone_refresh._zonemgr_refresh_info, {})
         # Put something in
-        config.set_zone_list_from_name_classes([ZONE_NAME_CLASS1_IN])
-        self.zone_refresh.update_config_data(config)
+        config['secondary_zones'] = \
+            zone_list_from_name_classes([ZONE_NAME_CLASS1_IN])
+        self.zone_refresh.update_config_data(config, self.cc_session)
         self.assertTrue(("example.net.", "IN") in
                         self.zone_refresh._zonemgr_refresh_info)
-        # This one does not exist
-        config.set_zone_list_from_name_classes(["example.net", "CH"])
-        self.zone_refresh.update_config_data(config)
-        self.assertFalse(("example.net.", "CH") in
-                        self.zone_refresh._zonemgr_refresh_info)
-        # Simply skip loading soa for the zone, the other configs should be updated successful
+        # Reset the data, set to use a different class, and make sure
+        # it does not get set to IN
+        config['secondary_zones'] = \
+            zone_list_from_name_classes([ZONE_NAME_CLASS1_CH])
+        self.zone_refresh.update_config_data(config, self.cc_session)
         self.assertFalse(("example.net.", "IN") in
-                        self.zone_refresh._zonemgr_refresh_info)
+                         self.zone_refresh._zonemgr_refresh_info)
         # Make sure it works even when we "accidentally" forget the final dot
-        config.set_zone_list_from_name_classes([("example.net", "IN")])
-        self.zone_refresh.update_config_data(config)
+        config['secondary_zones'] = \
+            zone_list_from_name_classes([("example.net", "IN")])
+        self.zone_refresh.update_config_data(config, self.cc_session)
         self.assertTrue(("example.net.", "IN") in
                         self.zone_refresh._zonemgr_refresh_info)
 
-    def tearDown(self):
-        sys.stderr= self.stderr_backup
-
+        # and with case-insensitive checking
+        config['secondary_zones'] = \
+            zone_list_from_name_classes([("Example.NeT.", "in")])
+        self.zone_refresh.update_config_data(config, self.cc_session)
+        self.assertTrue(("example.net.", "IN") in
+                        self.zone_refresh._zonemgr_refresh_info)
 
-class MyCCSession():
-    def __init__(self):
-        pass
-
-    def get_remote_config_value(self, module_name, identifier):
-        if module_name == "Auth" and identifier == "database_file":
-            return "initdb.file", False
-        else:
-            return "unknown", False
+        # Try some bad names
+        config['secondary_zones'] = \
+            zone_list_from_name_classes([("example..net", "IN")])
+        self.assertRaises(ZonemgrException,
+                          self.zone_refresh.update_config_data,
+                          config, self.cc_session)
+        config['secondary_zones'] = \
+            zone_list_from_name_classes([("", "IN")])
+        self.assertRaises(ZonemgrException,
+                          self.zone_refresh.update_config_data,
+                          config, self.cc_session)
+        # Try a bad class
+        config['secondary_zones'] = \
+            zone_list_from_name_classes([("example.net", "BADCLASS")])
+        self.assertRaises(ZonemgrException,
+                          self.zone_refresh.update_config_data,
+                          config, self.cc_session)
+        config['secondary_zones'] = \
+            zone_list_from_name_classes([("example.net", "")])
+        self.assertRaises(ZonemgrException,
+                          self.zone_refresh.update_config_data,
+                          config, self.cc_session)
 
+    def tearDown(self):
+        sys.stderr= self.stderr_backup
 
 class MyZonemgr(Zonemgr):
 
@@ -583,7 +610,7 @@ class MyZonemgr(Zonemgr):
         self._zone_refresh = None
         self._shutdown_event = threading.Event()
         self._cc = MySession()
-        self._module_cc = MyCCSession()
+        self._module_cc = FakeCCSession()
         self._config_data = {
                     "lowerbound_refresh" : 10,
                     "lowerbound_retry" : 5,
@@ -622,7 +649,7 @@ class TestZonemgr(unittest.TestCase):
         self.assertEqual(0.5, self.zonemgr._config_data.get("refresh_jitter"))
         # The zone doesn't exist in database, simply skip loading soa for it and log an warning
         self.zonemgr._zone_refresh = ZonemgrRefresh(None, "initdb.file", None,
-                                                    config_data1)
+                                                    FakeCCSession())
         config_data1["secondary_zones"] = [{"name": "nonexistent.example",
                                             "class": "IN"}]
         self.assertEqual(self.zonemgr.config_handler(config_data1),
@@ -660,4 +687,5 @@ class TestZonemgr(unittest.TestCase):
         pass
 
 if __name__== "__main__":
+    isc.log.resetUnitTestRootLogger()
     unittest.main()
diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in
index 5bdb765..4060bb5 100755
--- a/src/bin/zonemgr/zonemgr.py.in
+++ b/src/bin/zonemgr/zonemgr.py.in
@@ -28,6 +28,7 @@ import os
 import time
 import signal
 import isc
+import isc.dns
 import random
 import threading
 import select
@@ -98,7 +99,7 @@ class ZonemgrRefresh:
     can be stopped by calling shutdown() in another thread.
     """
 
-    def __init__(self, cc, db_file, slave_socket, config_data):
+    def __init__(self, cc, db_file, slave_socket, module_cc_session):
         self._cc = cc
         self._check_sock = slave_socket
         self._db_file = db_file
@@ -108,7 +109,8 @@ class ZonemgrRefresh:
         self._max_transfer_timeout = None
         self._refresh_jitter = None
         self._reload_jitter = None
-        self.update_config_data(config_data)
+        self.update_config_data(module_cc_session.get_full_config(),
+                                module_cc_session)
         self._running = False
 
     def _random_jitter(self, max, jitter):
@@ -424,7 +426,7 @@ class ZonemgrRefresh:
         self._read_sock = None
         self._write_sock = None
 
-    def update_config_data(self, new_config):
+    def update_config_data(self, new_config, module_cc_session):
         """ update ZonemgrRefresh config """
         # Get a new value, but only if it is defined (commonly used below)
         # We don't use "value or default", because if value would be
@@ -456,11 +458,42 @@ class ZonemgrRefresh:
             if secondary_zones is not None:
                 # Add new zones
                 for secondary_zone in new_config.get('secondary_zones'):
+                    if 'name' not in secondary_zone:
+                        raise ZonemgrException("Secondary zone specified "
+                                               "without a name")
                     name = secondary_zone['name']
-                    # Be tolerant to sclerotic users who forget the final dot
-                    if name[-1] != '.':
-                        name = name + '.'
-                    name_class = (name, secondary_zone['class'])
+
+                    # Convert to Name and back (both to check and to normalize)
+                    try:
+                        name = isc.dns.Name(name, True).to_text()
+                    # Name() can raise a number of different exceptions, just
+                    # catch 'em all.
+                    except Exception as isce:
+                        raise ZonemgrException("Bad zone name '" + name +
+                                               "': " + str(isce))
+
+                    # Currently we use an explicit get_default_value call
+                    # in case the class hasn't been set. Alternatively, we
+                    # could use
+                    # module_cc_session.get_value('secondary_zones[INDEX]/class')
+                    # To get either the value that was set, or the default if
+                    # it wasn't set.
+                    # But the real solution would be to make new_config a type
+                    # that contains default values itself
+                    # (then this entire method can be simplified a lot, and we
+                    # wouldn't need direct access to the ccsession object)
+                    if 'class' in secondary_zone:
+                        rr_class = secondary_zone['class']
+                    else:
+                        rr_class = module_cc_session.get_default_value(
+                                        'secondary_zones/class')
+                    # Convert rr_class to and from RRClass to check its value
+                    try:
+                        name_class = (name, isc.dns.RRClass(rr_class).to_text())
+                    except isc.dns.InvalidRRClass:
+                        raise ZonemgrException("Bad RR class '" +
+                                               rr_class +
+                                               "' for zone " + name)
                     required[name_class] = True
                     # Add it only if it isn't there already
                     if not name_class in self._zonemgr_refresh_info:
@@ -485,7 +518,7 @@ class Zonemgr:
         self._db_file = self.get_db_file()
         # Create socket pair for communicating between main thread and zonemgr timer thread
         self._master_socket, self._slave_socket = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
-        self._zone_refresh = ZonemgrRefresh(self._cc, self._db_file, self._slave_socket, self._config_data)
+        self._zone_refresh = ZonemgrRefresh(self._cc, self._db_file, self._slave_socket, self._module_cc)
         self._zone_refresh.run_timer()
 
         self._lock = threading.Lock()
@@ -540,7 +573,7 @@ class Zonemgr:
         self._config_data_check(complete)
         if self._zone_refresh is not None:
             try:
-                self._zone_refresh.update_config_data(complete)
+                self._zone_refresh.update_config_data(complete, self._module_cc)
             except Exception as e:
                 answer = create_answer(1, str(e))
                 ok = False
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index a569ea7..9ebd541 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -1,3 +1,3 @@
 SUBDIRS = exceptions util log cryptolink dns cc config acl xfr bench \
           asiolink asiodns nsas cache resolve testutils datasrc \
-          server_common python dhcp
+          server_common python dhcp statistics
diff --git a/src/lib/cryptolink/tests/Makefile.am b/src/lib/cryptolink/tests/Makefile.am
index 6ac6fdf..fbe1bad 100644
--- a/src/lib/cryptolink/tests/Makefile.am
+++ b/src/lib/cryptolink/tests/Makefile.am
@@ -16,7 +16,7 @@ TESTS += run_unittests
 run_unittests_SOURCES = run_unittests.cc
 run_unittests_SOURCES += crypto_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS =  $(BOTAN_LDFLAGS) $(GTEST_LDFLAGS) $(AM_LDFLAGS) 
+run_unittests_LDFLAGS =  $(BOTAN_LDFLAGS) $(GTEST_LDFLAGS) $(AM_LDFLAGS)
 run_unittests_LDADD = $(GTEST_LDADD) $(BOTAN_LIBS)
 run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libcryptolink.la
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 4af6aea..6620c85 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -411,7 +411,7 @@ DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
     // cut but the match we find for the glue is a wildcard match.  In that
     // case, we return the delegation instead (see RFC 1034, section 4.3.3).
     // To save a new search, we record the location of the delegation cut when
-    // we encounter it here. 
+    // we encounter it here.
     isc::dns::ConstRRsetPtr first_ns;
 
     // We want to search from the apex down.  We are given the full domain
diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am
index 6dd6b0a..113d03c 100644
--- a/src/lib/datasrc/tests/Makefile.am
+++ b/src/lib/datasrc/tests/Makefile.am
@@ -17,6 +17,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 
 TESTS =
+noinst_PROGRAMS =
 if HAVE_GTEST
 TESTS += run_unittests run_unittests_sqlite3 run_unittests_memory
 
@@ -84,25 +85,7 @@ run_unittests_memory_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 
 run_unittests_memory_LDADD = $(common_ldadd)
 
-endif
-
-noinst_PROGRAMS = $(TESTS)
-
-EXTRA_DIST =  testdata/brokendb.sqlite3
-EXTRA_DIST += testdata/example.com.signed
-EXTRA_DIST += testdata/example.org
-EXTRA_DIST += testdata/example.org.sqlite3
-EXTRA_DIST += testdata/example2.com
-EXTRA_DIST += testdata/example2.com.sqlite3
-EXTRA_DIST += testdata/mkbrokendb.c
-EXTRA_DIST += testdata/root.zone
-EXTRA_DIST += testdata/sql1.example.com.signed
-EXTRA_DIST += testdata/sql2.example.com.signed
-EXTRA_DIST += testdata/test-root.sqlite3
-EXTRA_DIST += testdata/test.sqlite3
-EXTRA_DIST += testdata/test.sqlite3.nodiffs
-EXTRA_DIST += testdata/rwtest.sqlite3
-EXTRA_DIST += testdata/diffs.sqlite3
+noinst_PROGRAMS+= $(TESTS)
 
 # For the factory unit tests, we need to specify that we want
 # the loadable backend libraries from the build tree, and not from 
@@ -121,3 +104,21 @@ run_unittests_factory_LDADD = $(common_ldadd)
 check-local:
 	B10_FROM_BUILD=${abs_top_builddir} ./run_unittests_factory
 endif
+
+endif
+
+EXTRA_DIST =  testdata/brokendb.sqlite3
+EXTRA_DIST += testdata/example.com.signed
+EXTRA_DIST += testdata/example.org
+EXTRA_DIST += testdata/example.org.sqlite3
+EXTRA_DIST += testdata/example2.com
+EXTRA_DIST += testdata/example2.com.sqlite3
+EXTRA_DIST += testdata/mkbrokendb.c
+EXTRA_DIST += testdata/root.zone
+EXTRA_DIST += testdata/sql1.example.com.signed
+EXTRA_DIST += testdata/sql2.example.com.signed
+EXTRA_DIST += testdata/test-root.sqlite3
+EXTRA_DIST += testdata/test.sqlite3
+EXTRA_DIST += testdata/test.sqlite3.nodiffs
+EXTRA_DIST += testdata/rwtest.sqlite3
+EXTRA_DIST += testdata/diffs.sqlite3
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index 3991033..99ee112 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -7,21 +7,22 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 CLEANFILES = *.gcno *.gcda
 
-lib_LTLIBRARIES = libdhcp.la
-libdhcp_la_SOURCES  =
-libdhcp_la_SOURCES += libdhcp.cc libdhcp.h
-libdhcp_la_SOURCES += option.cc option.h
-libdhcp_la_SOURCES += option6_ia.cc option6_ia.h
-libdhcp_la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
-libdhcp_la_SOURCES += option6_addrlst.cc option6_addrlst.h
-libdhcp_la_SOURCES += option4_addrlst.cc option4_addrlst.h
-libdhcp_la_SOURCES += dhcp6.h dhcp4.h
-libdhcp_la_SOURCES += pkt6.cc pkt6.h
-libdhcp_la_SOURCES += pkt4.cc pkt4.h
+lib_LTLIBRARIES = libdhcp++.la
+libdhcp___la_SOURCES  =
+libdhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
+libdhcp___la_SOURCES += iface_mgr.cc iface_mgr.h
+libdhcp___la_SOURCES += option.cc option.h
+libdhcp___la_SOURCES += option6_ia.cc option6_ia.h
+libdhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
+libdhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h
+libdhcp___la_SOURCES += option4_addrlst.cc option4_addrlst.h
+libdhcp___la_SOURCES += dhcp6.h dhcp4.h
+libdhcp___la_SOURCES += pkt6.cc pkt6.h
+libdhcp___la_SOURCES += pkt4.cc pkt4.h
 
 EXTRA_DIST  = README
 #EXTRA_DIST += log_messages.mes
 
-libdhcp_la_CXXFLAGS = $(AM_CXXFLAGS)
-libdhcp_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
-libdhcp_la_LIBADD   = $(top_builddir)/src/lib/util/libutil.la
+libdhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
+libdhcp___la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libdhcp___la_LIBADD   = $(top_builddir)/src/lib/util/libutil.la
diff --git a/src/lib/dhcp/README b/src/lib/dhcp/README
index 6c5353d..6bd6384 100644
--- a/src/lib/dhcp/README
+++ b/src/lib/dhcp/README
@@ -1,4 +1,4 @@
-This directory holds implementation for libdhcp.
+This directory holds implementation for libdhcp++.
 
 
 Basic Ideas
@@ -8,4 +8,4 @@ Basic Ideas
 Notes
 =====
 This work just begun. Don't expect to see much useful code here.
-We are working on it.
\ No newline at end of file
+We are working on it.
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
new file mode 100644
index 0000000..25999a0
--- /dev/null
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -0,0 +1,725 @@
+// Copyright (C) 2011  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 <sstream>
+#include <fstream>
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/iface_mgr.h>
+#include <exceptions/exceptions.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+namespace isc {
+
+/// IfaceMgr is a singleton implementation
+IfaceMgr* IfaceMgr::instance_ = 0;
+
+void
+IfaceMgr::instanceCreate() {
+    if (instance_) {
+        // no need to do anything. Instance is already created.
+        // Who called it again anyway? Uh oh. Had to be us, as
+        // this is private method.
+        return;
+    }
+    instance_ = new IfaceMgr();
+}
+
+IfaceMgr&
+IfaceMgr::instance() {
+    if (instance_ == 0) {
+        instanceCreate();
+    }
+    return (*instance_);
+}
+
+IfaceMgr::Iface::Iface(const std::string& name, int ifindex)
+    :name_(name), ifindex_(ifindex), mac_len_(0) {
+
+    memset(mac_, 0, sizeof(mac_));
+}
+
+std::string
+IfaceMgr::Iface::getFullName() const {
+    ostringstream tmp;
+    tmp << name_ << "/" << ifindex_;
+    return (tmp.str());
+}
+
+std::string
+IfaceMgr::Iface::getPlainMac() const {
+    ostringstream tmp;
+    tmp.fill('0');
+    tmp << hex;
+    for (int i = 0; i < mac_len_; i++) {
+        tmp.width(2);
+        tmp << mac_[i];
+        if (i < mac_len_-1) {
+            tmp << ":";
+        }
+    }
+    return (tmp.str());
+}
+
+bool IfaceMgr::Iface::delAddress(const isc::asiolink::IOAddress& addr) {
+
+    // Let's delete all addresses that match. It really shouldn't matter
+    // if we delete first or all, as the OS should allow to add a single
+    // address to an interface only once. If OS allows multiple instances
+    // of the same address added, we are in deep problems anyway.
+    size_t size = addrs_.size();
+    addrs_.erase(remove(addrs_.begin(), addrs_.end(), addr), addrs_.end());
+    return (addrs_.size() < size);
+}
+
+bool IfaceMgr::Iface::delSocket(uint16_t sockfd) {
+    list<SocketInfo>::iterator sock = sockets_.begin();
+    while (sock!=sockets_.end()) {
+        if (sock->sockfd_ == sockfd) {
+            close(sockfd);
+            sockets_.erase(sock);
+            return (true); //socket found
+        }
+        ++sock;
+    }
+    return (false); // socket not found
+}
+
+IfaceMgr::IfaceMgr()
+    :control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
+     control_buf_(new char[control_buf_len_])
+{
+
+    cout << "IfaceMgr initialization." << endl;
+
+    try {
+        // required for sending/receiving packets
+        // let's keep it in front, just in case someone
+        // wants to send anything during initialization
+
+        // control_buf_ = boost::scoped_array<char>();
+
+        detectIfaces();
+
+    } catch (const std::exception& ex) {
+        cout << "IfaceMgr creation failed:" << ex.what() << endl;
+
+        // TODO Uncomment this (or call LOG_FATAL) once
+        // interface detection is implemented. Otherwise
+        // it is not possible to run tests in a portable
+        // way (see detectIfaces() method).
+        // throw ex;
+    }
+}
+
+void IfaceMgr::closeSockets() {
+    for (IfaceCollection::iterator iface = ifaces_.begin();
+         iface != ifaces_.end(); ++iface) {
+
+        for (SocketCollection::iterator sock = iface->sockets_.begin();
+             sock != iface->sockets_.end(); ++sock) {
+            cout << "Closing socket " << sock->sockfd_ << endl;
+            close(sock->sockfd_);
+        }
+        iface->sockets_.clear();
+    }
+
+}
+
+IfaceMgr::~IfaceMgr() {
+    closeSockets();
+
+    // control_buf_ is deleted automatically (scoped_ptr)
+    control_buf_len_ = 0;
+}
+
+void
+IfaceMgr::detectIfaces() {
+    string ifaceName, linkLocal;
+
+    // TODO do the actual detection. Currently interface detection is faked
+    //      by reading a text file.
+
+    cout << "Interface detection is not implemented yet. "
+         << "Reading interfaces.txt file instead." << endl;
+    cout << "Please use format: interface-name link-local-address" << endl;
+
+    try {
+        ifstream interfaces("interfaces.txt");
+
+        if (!interfaces.good()) {
+            cout << "Failed to read interfaces.txt file." << endl;
+            isc_throw(Unexpected, "Failed to read interfaces.txt");
+        }
+        interfaces >> ifaceName;
+        interfaces >> linkLocal;
+
+        cout << "Detected interface " << ifaceName << "/" << linkLocal << endl;
+
+        Iface iface(ifaceName, if_nametoindex( ifaceName.c_str() ) );
+        IOAddress addr(linkLocal);
+        iface.addAddress(addr);
+        addInterface(iface);
+        interfaces.close();
+    } catch (const std::exception& ex) {
+        // TODO: deallocate whatever memory we used
+        // not that important, since this function is going to be
+        // thrown away as soon as we get proper interface detection
+        // implemented
+
+        // TODO Do LOG_FATAL here
+        std::cerr << "Interface detection failed." << std::endl;
+        throw ex;
+    }
+}
+
+void
+IfaceMgr::openSockets(uint16_t port) {
+    int sock1, sock2;
+
+    for (IfaceCollection::iterator iface = ifaces_.begin();
+         iface != ifaces_.end(); ++iface) {
+
+        AddressCollection addrs = iface->getAddresses();
+
+        for (AddressCollection::iterator addr = addrs.begin();
+             addr != addrs.end();
+             ++addr) {
+
+            sock1 = openSocket(iface->getName(), *addr, port);
+            if (sock1 < 0) {
+                isc_throw(Unexpected, "Failed to open unicast socket on "
+                          << " interface " << iface->getFullName());
+            }
+
+            if ( !joinMcast(sock1, iface->getName(),
+                             string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
+                close(sock1);
+                isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
+                          << " multicast group.");
+            }
+
+            // this doesn't work too well on NetBSD
+            sock2 = openSocket(iface->getName(),
+                               IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
+                               port);
+            if (sock2 < 0) {
+                isc_throw(Unexpected, "Failed to open multicast socket on "
+                          << " interface " << iface->getFullName());
+                iface->delSocket(sock1); // delete previously opened socket
+            }
+        }
+    }
+}
+
+void
+IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
+    for (IfaceCollection::const_iterator iface = ifaces_.begin();
+         iface != ifaces_.end(); ++iface) {
+        out << "Detected interface " << iface->getFullName() << endl;
+        out << "  " << iface->getAddresses().size() << " addr(s):" << endl;
+        const AddressCollection addrs = iface->getAddresses();
+
+        for (AddressCollection::const_iterator addr = addrs.begin();
+             addr != addrs.end(); ++addr) {
+            out << "  " << addr->toText() << endl;
+        }
+        out << "  mac: " << iface->getPlainMac() << endl;
+    }
+}
+
+IfaceMgr::Iface*
+IfaceMgr::getIface(int ifindex) {
+    for (IfaceCollection::iterator iface = ifaces_.begin();
+         iface != ifaces_.end(); ++iface) {
+        if (iface->getIndex() == ifindex) {
+            return (&(*iface));
+        }
+    }
+
+    return (NULL); // not found
+}
+
+IfaceMgr::Iface*
+IfaceMgr::getIface(const std::string& ifname) {
+    for (IfaceCollection::iterator iface = ifaces_.begin();
+         iface != ifaces_.end(); ++iface) {
+        if (iface->getName() == ifname) {
+            return (&(*iface));
+        }
+    }
+
+    return (NULL); // not found
+}
+
+int
+IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr,
+                     int port) {
+    Iface* iface = getIface(ifname);
+    if (!iface) {
+        isc_throw(BadValue, "There is no " << ifname << " interface present.");
+    }
+    switch (addr.getFamily()) {
+    case AF_INET:
+        return openSocket4(*iface, addr, port);
+    case AF_INET6:
+        return openSocket6(*iface, addr, port);
+    default:
+        isc_throw(BadValue, "Failed to detect family of address: "
+                  << addr.toText());
+    }
+}
+
+int
+IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, int port) {
+
+    cout << "Creating UDP4 socket on " << iface.getFullName()
+         << " " << addr.toText() << "/port=" << port << endl;
+
+    struct sockaddr_in addr4;
+    memset(&addr4, 0, sizeof(sockaddr));
+    addr4.sin_family = AF_INET;
+    addr4.sin_port = htons(port);
+    memcpy(&addr4.sin_addr, addr.getAddress().to_v4().to_bytes().data(),
+           sizeof(addr4.sin_addr));
+
+    int sock = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sock < 0) {
+        isc_throw(Unexpected, "Failed to create UDP6 socket.");
+    }
+
+    if (bind(sock, (struct sockaddr *)&addr4, sizeof(addr4)) < 0) {
+        close(sock);
+        isc_throw(Unexpected, "Failed to bind socket " << sock << " to " << addr.toText()
+                  << "/port=" << port);
+    }
+
+    // If there is no support for IP_PKTINFO, we are really out of luck.
+    // It will be difficult to understand, where this packet came from.
+#if defined(IP_PKTINFO)
+    int flag = 1;
+    if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &flag, sizeof(flag)) != 0) {
+        close(sock);
+        isc_throw(Unexpected, "setsockopt: IP_PKTINFO: failed.");
+    }
+#endif
+
+    cout << "Created socket " << sock << " on " << iface.getName() << "/" <<
+        addr.toText() << "/port=" << port << endl;
+
+    iface.addSocket(SocketInfo(sock, addr, port));
+
+    return (sock);
+}
+
+int
+IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
+
+    cout << "Creating UDP6 socket on " << iface.getFullName()
+         << " " << addr.toText() << "/port=" << port << endl;
+
+    struct sockaddr_in6 addr6;
+    memset(&addr6, 0, sizeof(addr6));
+    addr6.sin6_family = AF_INET6;
+    addr6.sin6_port = htons(port);
+    if (addr.toText() != "::1")
+      addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str());
+
+    memcpy(&addr6.sin6_addr,
+           addr.getAddress().to_v6().to_bytes().data(),
+           sizeof(addr6.sin6_addr));
+#ifdef HAVE_SA_LEN
+    addr6->sin6_len = sizeof(addr6);
+#endif
+
+    // TODO: use sockcreator once it becomes available
+
+    // make a socket
+    int sock = socket(AF_INET6, SOCK_DGRAM, 0);
+    if (sock < 0) {
+        isc_throw(Unexpected, "Failed to create UDP6 socket.");
+    }
+
+    // Set the REUSEADDR option so that we don't fail to start if
+    // we're being restarted.
+    int flag = 1;
+    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+                   (char *)&flag, sizeof(flag)) < 0) {
+        close(sock);
+        isc_throw(Unexpected, "Can't set SO_REUSEADDR option on dhcpv6 socket.");
+    }
+
+    if (bind(sock, (struct sockaddr *)&addr6, sizeof(addr6)) < 0) {
+        close(sock);
+        isc_throw(Unexpected, "Failed to bind socket " << sock << " to " << addr.toText()
+                  << "/port=" << port);
+    }
+#ifdef IPV6_RECVPKTINFO
+    // RFC3542 - a new way
+    if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+                   &flag, sizeof(flag)) != 0) {
+        close(sock);
+        isc_throw(Unexpected, "setsockopt: IPV6_RECVPKTINFO failed.");
+    }
+#else
+    // RFC2292 - an old way
+    if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
+                   &flag, sizeof(flag)) != 0) {
+        close(sock);
+        isc_throw(Unexpected, "setsockopt: IPV6_PKTINFO: failed.");
+    }
+#endif
+
+    // multicast stuff
+    if (addr.getAddress().to_v6().is_multicast()) {
+        // both mcast (ALL_DHCP_RELAY_AGENTS_AND_SERVERS and ALL_DHCP_SERVERS)
+        // are link and site-scoped, so there is no sense to join those groups
+        // with global addresses.
+
+        if ( !joinMcast( sock, iface.getName(),
+                         string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
+            close(sock);
+            isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
+                      << " multicast group.");
+        }
+    }
+
+    cout << "Created socket " << sock << " on " << iface.getName() << "/" <<
+        addr.toText() << "/port=" << port << endl;
+
+    iface.addSocket(SocketInfo(sock, addr, port));
+
+    return (sock);
+}
+
+bool
+IfaceMgr::joinMcast(int sock, const std::string& ifname,
+const std::string & mcast) {
+
+    struct ipv6_mreq mreq;
+
+    if (inet_pton(AF_INET6, mcast.c_str(),
+                  &mreq.ipv6mr_multiaddr) <= 0) {
+        cout << "Failed to convert " << ifname
+             << " to IPv6 multicast address." << endl;
+        return (false);
+    }
+
+    mreq.ipv6mr_interface = if_nametoindex(ifname.c_str());
+    if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+                   &mreq, sizeof(mreq)) < 0) {
+        cout << "Failed to join " << mcast << " multicast group." << endl;
+        return (false);
+    }
+
+    cout << "Joined multicast " << mcast << " group." << endl;
+
+    return (true);
+}
+
+bool
+IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
+    struct msghdr m;
+    struct iovec v;
+    int result;
+    struct in6_pktinfo *pktinfo;
+    struct cmsghdr *cmsg;
+
+    Iface* iface = getIface(pkt->iface_);
+    if (!iface) {
+        isc_throw(BadValue, "Unable to send Pkt6. Invalid interface ("
+                  << pkt->iface_ << ") specified.");
+    }
+
+    memset(&control_buf_[0], 0, control_buf_len_);
+
+    // Initialize our message header structure.
+    memset(&m, 0, sizeof(m));
+
+    // Set the target address we're sending to.
+    sockaddr_in6 to;
+    memset(&to, 0, sizeof(to));
+    to.sin6_family = AF_INET6;
+    to.sin6_port = htons(pkt->remote_port_);
+    memcpy(&to.sin6_addr,
+           pkt->remote_addr_.getAddress().to_v6().to_bytes().data(),
+           16);
+    to.sin6_scope_id = pkt->ifindex_;
+
+    m.msg_name = &to;
+    m.msg_namelen = sizeof(to);
+
+    // Set the data buffer we're sending. (Using this wacky
+    // "scatter-gather" stuff... we only have a single chunk
+    // of data to send, so we declare a single vector entry.)
+    v.iov_base = (char *) &pkt->data_[0];
+    v.iov_len = pkt->data_len_;
+    m.msg_iov = &v;
+    m.msg_iovlen = 1;
+
+    // Setting the interface is a bit more involved.
+    //
+    // We have to create a "control message", and set that to
+    // define the IPv6 packet information. We could set the
+    // source address if we wanted, but we can safely let the
+    // kernel decide what that should be.
+    m.msg_control = &control_buf_[0];
+    m.msg_controllen = control_buf_len_;
+    cmsg = CMSG_FIRSTHDR(&m);
+    cmsg->cmsg_level = IPPROTO_IPV6;
+    cmsg->cmsg_type = IPV6_PKTINFO;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
+    pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+    memset(pktinfo, 0, sizeof(*pktinfo));
+    pktinfo->ipi6_ifindex = pkt->ifindex_;
+    m.msg_controllen = cmsg->cmsg_len;
+
+    result = sendmsg(getSocket(*pkt), &m, 0);
+    if (result < 0) {
+        cout << "Send packet failed." << endl;
+    }
+    cout << "Sent " << pkt->data_len_ << " bytes over socket " << getSocket(*pkt)
+         << " on " << iface->getFullName() << " interface: "
+         << " dst=" << pkt->remote_addr_.toText()
+         << ", src=" << pkt->local_addr_.toText()
+         << endl;
+
+    return (result);
+}
+
+bool
+IfaceMgr::send(boost::shared_ptr<Pkt4>& )
+{
+    /// TODO: Implement this (ticket #1240)
+    isc_throw(NotImplemented, "Pkt4 send not implemented yet.");
+}
+
+
+boost::shared_ptr<Pkt4>
+IfaceMgr::receive4() {
+    isc_throw(NotImplemented, "Pkt4 reception not implemented yet.");
+
+    // TODO: To be implemented (ticket #1239)
+    return (boost::shared_ptr<Pkt4>()); // NULL
+}
+
+boost::shared_ptr<Pkt6>
+IfaceMgr::receive6() {
+    struct msghdr m;
+    struct iovec v;
+    int result;
+    struct cmsghdr* cmsg;
+    struct in6_pktinfo* pktinfo;
+    struct sockaddr_in6 from;
+    struct in6_addr to_addr;
+    boost::shared_ptr<Pkt6> pkt;
+    char addr_str[INET6_ADDRSTRLEN];
+
+    try {
+        // RFC3315 states that server responses may be
+        // fragmented if they are over MTU. There is no
+        // text whether client's packets may be larger
+        // than 1500. Nevertheless to be on the safe side
+        // we use larger buffer. This buffer limit is checked
+        // during reception (see iov_len below), so we are
+        // safe
+        pkt = boost::shared_ptr<Pkt6>(new Pkt6(65536));
+    } catch (const std::exception& ex) {
+        cout << "Failed to create new packet." << endl;
+        return (boost::shared_ptr<Pkt6>()); // NULL
+    }
+
+    memset(&control_buf_[0], 0, control_buf_len_);
+
+    memset(&from, 0, sizeof(from));
+    memset(&to_addr, 0, sizeof(to_addr));
+
+    // Initialize our message header structure.
+    memset(&m, 0, sizeof(m));
+
+    // Point so we can get the from address.
+    m.msg_name = &from;
+    m.msg_namelen = sizeof(from);
+
+    // Set the data buffer we're receiving. (Using this wacky
+    // "scatter-gather" stuff... but we that doesn't really make
+    // sense for us, so we use a single vector entry.)
+    v.iov_base = (void*)&pkt->data_[0];
+    v.iov_len = pkt->data_len_;
+    m.msg_iov = &v;
+    m.msg_iovlen = 1;
+
+    // Getting the interface is a bit more involved.
+    //
+    // We set up some space for a "control message". We have
+    // previously asked the kernel to give us packet
+    // information (when we initialized the interface), so we
+    // should get the destination address from that.
+    m.msg_control = &control_buf_[0];
+    m.msg_controllen = control_buf_len_;
+
+    /// TODO: Need to move to select() and pool over
+    /// all available sockets. For now, we just take the
+    /// first interface and use first socket from it.
+    IfaceCollection::const_iterator iface = ifaces_.begin();
+    if (iface == ifaces_.end()) {
+        isc_throw(Unexpected, "No interfaces detected. Can't receive anything.");
+    }
+    SocketCollection::const_iterator s = iface->sockets_.begin();
+    const SocketInfo* candidate = 0;
+    while (s != iface->sockets_.end()) {
+        if (s->addr_.getAddress().to_v6().is_multicast()) {
+            candidate = &(*s);
+            break;
+        }
+        if (!candidate) {
+            candidate = &(*s); // it's not multicast, but it's better than none
+        }
+        ++s;
+    }
+    if (!candidate) {
+        isc_throw(Unexpected, "Interface " << iface->getFullName()
+                  << " does not have any sockets open.");
+    }
+
+    cout << "Trying to receive over socket " << candidate->sockfd_ << " bound to "
+         << candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
+         << iface->getFullName() << endl;
+    result = recvmsg(candidate->sockfd_, &m, 0);
+
+    if (result >= 0) {
+        // If we did read successfully, then we need to loop
+        // through the control messages we received and
+        // find the one with our destination address.
+        //
+        // We also keep a flag to see if we found it. If we
+        // didn't, then we consider this to be an error.
+        int found_pktinfo = 0;
+        cmsg = CMSG_FIRSTHDR(&m);
+        while (cmsg != NULL) {
+            if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
+                (cmsg->cmsg_type == IPV6_PKTINFO)) {
+                pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsg);
+                to_addr = pktinfo->ipi6_addr;
+                pkt->ifindex_ = pktinfo->ipi6_ifindex;
+                found_pktinfo = 1;
+            }
+            cmsg = CMSG_NXTHDR(&m, cmsg);
+        }
+        if (!found_pktinfo) {
+            cout << "Unable to find pktinfo" << endl;
+            return (boost::shared_ptr<Pkt6>()); // NULL
+        }
+    } else {
+        cout << "Failed to receive data." << endl;
+        return (boost::shared_ptr<Pkt6>()); // NULL
+    }
+
+    // That's ugly.
+    // TODO add IOAddress constructor that will take struct in6_addr*
+    // TODO: there's from_bytes() method added in IOAddress. Use it!
+    inet_ntop(AF_INET6, &to_addr, addr_str,INET6_ADDRSTRLEN);
+    pkt->local_addr_ = IOAddress(string(addr_str));
+
+    // TODO: there's from_bytes() method added in IOAddress. Use it!
+    inet_ntop(AF_INET6, &from.sin6_addr, addr_str, INET6_ADDRSTRLEN);
+    pkt->remote_addr_ = IOAddress(string(addr_str));
+
+    pkt->remote_port_ = ntohs(from.sin6_port);
+
+    Iface* received = getIface(pkt->ifindex_);
+    if (received) {
+        pkt->iface_ = received->getName();
+    } else {
+        cout << "Received packet over unknown interface (ifindex="
+             << pkt->ifindex_ << ")." << endl;
+        return (boost::shared_ptr<Pkt6>()); // NULL
+    }
+
+    pkt->data_len_ = result;
+
+    // TODO Move this to LOG_DEBUG
+    cout << "Received " << pkt->data_len_ << " bytes over "
+         << pkt->iface_ << "/" << pkt->ifindex_ << " interface: "
+         << " src=" << pkt->remote_addr_.toText()
+         << ", dst=" << pkt->local_addr_.toText()
+         << endl;
+
+    return (pkt);
+}
+
+uint16_t
+IfaceMgr::getSocket(isc::dhcp::Pkt6 const& pkt) {
+    Iface* iface = getIface(pkt.iface_);
+    if (!iface) {
+        isc_throw(BadValue, "Tried to find socket for non-existent interface "
+                  << pkt.iface_);
+    }
+
+    SocketCollection::const_iterator s;
+    for (s = iface->sockets_.begin(); s != iface->sockets_.end(); ++s) {
+        if (s->family_ != AF_INET6) {
+            // don't use IPv4 sockets
+            continue;
+        }
+        if (s->addr_.getAddress().to_v6().is_multicast()) {
+            // don't use IPv6 sockets bound to multicast address
+            continue;
+        }
+        /// TODO: Add more checks here later. If remote address is
+        /// not link-local, we can't use link local bound socket
+        /// to send data.
+
+        return (s->sockfd_);
+    }
+
+    isc_throw(Unexpected, "Interface " << iface->getFullName()
+              << " does not have any suitable IPv6 sockets open.");
+}
+
+uint16_t
+IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
+    Iface* iface = getIface(pkt.getIface());
+    if (!iface) {
+        isc_throw(BadValue, "Tried to find socket for non-existent interface "
+                  << pkt.getIface());
+    }
+
+    SocketCollection::const_iterator s;
+    for (s = iface->sockets_.begin(); s != iface->sockets_.end(); ++s) {
+        if (s->family_ != AF_INET) {
+            // don't use IPv4 sockets
+            continue;
+        }
+        /// TODO: Add more checks here later. If remote address is
+        /// not link-local, we can't use link local bound socket
+        /// to send data.
+
+        return (s->sockfd_);
+    }
+
+    isc_throw(Unexpected, "Interface " << iface->getFullName()
+              << " does not have any suitable IPv4 sockets open.");
+}
+
+
+
+}
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
new file mode 100644
index 0000000..759d657
--- /dev/null
+++ b/src/lib/dhcp/iface_mgr.h
@@ -0,0 +1,413 @@
+// Copyright (C) 2011  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 IFACE_MGR_H
+#define IFACE_MGR_H
+
+#include <list>
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_array.hpp>
+#include <boost/noncopyable.hpp>
+#include <asiolink/io_address.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
+
+namespace isc {
+
+namespace dhcp {
+/// @brief handles network interfaces, transmission and reception
+///
+/// IfaceMgr is an interface manager class that detects available network
+/// interfaces, configured addresses, link-local addresses, and provides
+/// API for using sockets.
+///
+class IfaceMgr : public boost::noncopyable {
+public:
+    /// type that defines list of addresses
+    typedef std::vector<isc::asiolink::IOAddress> AddressCollection;
+
+    /// maximum MAC address length (Infiniband uses 20 bytes)
+    static const unsigned int MAX_MAC_LEN = 20;
+
+    /// 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 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()) { }
+    };
+
+    /// type that holds a list of socket informations
+    typedef std::list<SocketInfo> SocketCollection;
+
+    /// @brief represents a single network interface
+    ///
+    /// Iface structure represents network interface with all useful
+    /// information, like name, interface index, MAC address and
+    /// list of assigned addresses
+    class Iface {
+    public:
+        /// @brief Iface constructor.
+        ///
+        /// Creates Iface object that represents network interface.
+        ///
+        /// @param name name of the interface
+        /// @param ifindex interface index (unique integer identifier)
+        Iface(const std::string& name, int ifindex);
+
+        /// @brief Returns full interface name as "ifname/ifindex" string.
+        ///
+        /// @return string with interface name
+        std::string getFullName() const;
+
+        /// @brief Returns link-layer address a plain text.
+        ///
+        /// @return MAC address as a plain text (string)
+        std::string getPlainMac() const;
+
+        /// @brief Returns interface index.
+        ///
+        /// @return interface index
+        uint16_t getIndex() const { return ifindex_; }
+
+        /// @brief Returns interface name.
+        ///
+        /// @return interface name
+        std::string getName() const { return name_; };
+
+        /// @brief Returns all interfaces available on an interface.
+        ///
+        /// Care should be taken to not use this collection after Iface object
+        /// ceases to exist. That is easy in most cases as Iface objects are
+        /// created by IfaceMgr that is a singleton an is expected to be
+        /// available at all time. We may revisit this if we ever decide to
+        /// implement dynamic interface detection, but such fancy feature would
+        /// mostly be useful for clients with wifi/vpn/virtual interfaces.
+        ///
+        /// @return collection of addresses
+        const AddressCollection& getAddresses() const { return addrs_; }
+
+        /// @brief Adds an address to an interface.
+        ///
+        /// This only adds an address to collection, it does not physically
+        /// configure address on actual network interface.
+        ///
+        /// @param addr address to be added
+        void addAddress(const isc::asiolink::IOAddress& addr) {
+            addrs_.push_back(addr);
+        }
+
+        /// @brief Deletes an address from an interface.
+        ///
+        /// This only deletes address from collection, it does not physically
+        /// remove address configuration from actual network interface.
+        ///
+        /// @param addr address to be removed.
+        ///
+        /// @return true if removal was successful (address was in collection),
+        ///         false otherwise
+        bool delAddress(const isc::asiolink::IOAddress& addr);
+
+        /// @brief Adds socket descriptor to an interface.
+        ///
+        /// @param socket SocketInfo structure that describes socket.
+        void addSocket(const SocketInfo& sock)
+            { sockets_.push_back(sock); }
+
+        /// @brief Closes socket.
+        ///
+        /// Closes socket and removes corresponding SocketInfo structure
+        /// from an interface.
+        ///
+        /// @param socket descriptor to be closed/removed.
+        /// @return true if there was such socket, false otherwise
+        bool delSocket(uint16_t sockfd);
+
+        /// socket used to sending data
+        /// TODO: this should be protected
+        SocketCollection sockets_;
+
+    protected:
+        /// network interface name
+        std::string name_;
+
+        /// interface index (a value that uniquely indentifies an interface)
+        int ifindex_;
+
+        /// list of assigned addresses
+        AddressCollection addrs_;
+
+        /// link-layer address
+        uint8_t mac_[MAX_MAC_LEN];
+
+        /// length of link-layer address (usually 6)
+        int mac_len_;
+    };
+
+    // TODO performance improvement: we may change this into
+    //      2 maps (ifindex-indexed and name-indexed) and
+    //      also hide it (make it public make tests easier for now)
+
+    /// type that holds a list of interfaces
+    typedef std::list<Iface> IfaceCollection;
+
+    /// IfaceMgr is a singleton class. This method returns reference
+    /// to its sole instance.
+    ///
+    /// @return the only existing instance of interface manager
+    static IfaceMgr& instance();
+
+    /// @brief Returns interface with specified interface index
+    ///
+    /// @param ifindex index of searched interface
+    ///
+    /// @return interface with requested index (or NULL if no such
+    ///         interface is present)
+    ///
+    Iface* getIface(int ifindex);
+
+    /// @brief Returns interface with specified interface name
+    ///
+    /// @param ifname name of searched interface
+    ///
+    /// @return interface with requested name (or NULL if no such
+    ///         interface is present)
+    ///
+    Iface*
+    getIface(const std::string& ifname);
+
+    /// @brief Return most suitable socket for transmitting specified IPv6 packet.
+    ///
+    /// This method takes Pkt6 (see overloaded implementation that takes
+    /// Pkt4) and chooses appropriate socket to send it. This method
+    /// may throw BadValue if specified packet does not have outbound
+    /// interface specified, no such interface exists, or specified
+    /// interface does not have any appropriate sockets open.
+    ///
+    /// @param pkt a packet to be transmitted
+    ///
+    /// @return a socket descriptor
+    uint16_t getSocket(const isc::dhcp::Pkt6& pkt);
+
+    /// @brief Return most suitable socket for transmitting specified IPv6 packet.
+    ///
+    /// This method takes Pkt4 (see overloaded implementation that takes
+    /// Pkt6) and chooses appropriate socket to send it. This method
+    /// may throw BadValue if specified packet does not have outbound
+    /// interface specified, no such interface exists, or specified
+    /// interface does not have any appropriate sockets open.
+    ///
+    /// @param pkt a packet to be transmitted
+    ///
+    /// @return a socket descriptor
+    uint16_t getSocket(const isc::dhcp::Pkt4& pkt);
+
+    /// debugging method that prints out all available interfaces
+    ///
+    /// @param out specifies stream to print list of interfaces to
+    void
+    printIfaces(std::ostream& out = std::cout);
+
+    /// @brief Sends an IPv6 packet.
+    ///
+    /// Sends an IPv6 packet. All parameters for actual transmission are specified in
+    /// Pkt6 structure itself. That includes destination address, src/dst port
+    /// and interface over which data will be sent.
+    ///
+    /// @param pkt packet to be sent
+    ///
+    /// @return true if sending was successful
+    bool send(boost::shared_ptr<Pkt6>& pkt);
+
+    /// @brief Sends an IPv4 packet.
+    ///
+    /// Sends an IPv4 packet. All parameters for actual transmission are specified
+    /// in Pkt4 structure itself. That includes destination address, src/dst
+    /// port and interface over which data will be sent.
+    ///
+    /// @param pkt a packet to be sent
+    ///
+    /// @return true if sending was successful
+    bool send(boost::shared_ptr<Pkt4>& pkt);
+
+    /// @brief Tries to receive IPv6 packet over open IPv6 sockets.
+    ///
+    /// Attempts to receive a single IPv6 packet of any of the open IPv6 sockets.
+    /// If reception is successful and all information about its sender
+    /// are obtained, Pkt6 object is created and returned.
+    ///
+    /// TODO Start using select() and add timeout to be able
+    /// to not wait infinitely, but rather do something useful
+    /// (e.g. remove expired leases)
+    ///
+    /// @return Pkt6 object representing received packet (or NULL)
+    boost::shared_ptr<Pkt6> receive6();
+
+    /// @brief Tries to receive IPv4 packet over open IPv4 sockets.
+    ///
+    /// Attempts to receive a single IPv4 packet of any of the open IPv4 sockets.
+    /// If reception is successful and all information about its sender
+    /// are obtained, Pkt4 object is created and returned.
+    ///
+    /// TODO Start using select() and add timeout to be able
+    /// to not wait infinitely, but rather do something useful
+    /// (e.g. remove expired leases)
+    ///
+    /// @return Pkt4 object representing received packet (or NULL)
+    boost::shared_ptr<Pkt4> receive4();
+
+    /// Opens UDP/IP socket and binds it to address, interface and port.
+    ///
+    /// Specific type of socket (UDP/IPv4 or UDP/IPv6) depends on passed addr
+    /// family.
+    ///
+    /// @param ifname name of the interface
+    /// @param addr address to be bound.
+    /// @param port UDP port.
+    ///
+    /// Method will throw if socket creation, socket binding or multicast
+    /// join fails.
+    ///
+    /// @return socket descriptor, if socket creation, binding and multicast
+    /// group join were all successful.
+    int openSocket(const std::string& ifname,
+                   const isc::asiolink::IOAddress& addr, int port);
+
+    /// Opens IPv6 sockets on detected interfaces.
+    ///
+    /// Will throw exception if socket creation fails.
+    ///
+    /// @param port specifies port number (usually DHCP6_SERVER_PORT)
+    void openSockets(uint16_t port);
+
+
+    /// @brief Closes all open sockets.
+    /// Is used in destructor, but also from Dhcpv4_srv and Dhcpv6_srv classes.
+    void closeSockets();
+
+    // don't use private, we need derived classes in tests
+protected:
+
+    /// @brief Protected constructor.
+    ///
+    /// Protected constructor. This is a singleton class. We don't want
+    /// anyone to create instances of IfaceMgr. Use instance() method instead.
+    IfaceMgr();
+
+    ~IfaceMgr();
+
+    /// @brief Opens IPv4 socket.
+    ///
+    /// Please do not use this method directly. Use openSocket instead.
+    ///
+    /// This method may throw exception if socket creation fails.
+    ///
+    /// @param iface reference to interface structure.
+    /// @param addr an address the created socket should be bound to
+    /// @param port a port that created socket should be bound to
+    ///
+    /// @return socket descriptor
+    int openSocket4(Iface& iface, const isc::asiolink::IOAddress& addr, int port);
+
+    /// @brief Opens IPv6 socket.
+    ///
+    /// Please do not use this method directly. Use openSocket instead.
+    ///
+    /// This method may throw exception if socket creation fails.
+    ///
+    /// @param iface reference to interface structure.
+    /// @param addr an address the created socket should be bound to
+    /// @param port a port that created socket should be bound to
+    ///
+    /// @return socket descriptor
+    int openSocket6(Iface& iface, const isc::asiolink::IOAddress& addr, int 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
+    /// it offers stub implementation. First interface name and link-local
+    /// IPv6 address is read from intefaces.txt file.
+    void
+    detectIfaces();
+
+    // TODO: having 2 maps (ifindex->iface and ifname->iface would)
+    //      probably be better for performance reasons
+
+    /// List of available interfaces
+    IfaceCollection ifaces_;
+
+    /// a pointer to a sole instance of this class (a singleton)
+    static IfaceMgr * instance_;
+
+    // TODO: Also keep this interface on Iface once interface detection
+    // is implemented. We may need it e.g. to close all sockets on
+    // specific interface
+    //int recvsock_; // TODO: should be fd_set eventually, but we have only
+    //int sendsock_; // 2 sockets for now. Will do for until next release
+
+    // we can't use the same socket, as receiving socket
+    // is bound to multicast address. And we all know what happens
+    // to people who try to use multicast as source address.
+
+    /// length of the control_buf_ array
+    int control_buf_len_;
+
+    /// control-buffer, used in transmission and reception
+    boost::scoped_array<char> control_buf_;
+
+private:
+
+    /// creates a single instance of this class (a singleton implementation)
+    static void
+    instanceCreate();
+
+    /// @brief Joins IPv6 multicast group on a socket.
+    ///
+    /// Socket must be created and bound to an address. Note that this
+    /// address is different than the multicast address. For example DHCPv6
+    /// server should bind its socket to link-local address (fe80::1234...)
+    /// and later join ff02::1:2 multicast group.
+    ///
+    /// @param sock socket fd (socket must be bound)
+    /// @param ifname interface name (for link-scoped multicast groups)
+    /// @param mcast multicast address to join (e.g. "ff02::1:2")
+    ///
+    /// @return true if multicast join was successful
+    ///
+    bool
+    joinMcast(int sock, const std::string& ifname,
+              const std::string& mcast);
+
+};
+
+}; // namespace isc::dhcp
+}; // namespace isc
+
+#endif
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
new file mode 100644
index 0000000..c727be2
--- /dev/null
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -0,0 +1,180 @@
+// Copyright (C) 2011  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 <boost/shared_array.hpp>
+#include <boost/shared_ptr.hpp>
+#include <util/buffer.h>
+#include <dhcp/libdhcp++.h>
+#include "config.h"
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/option6_iaaddr.h>
+
+using namespace std;
+using namespace isc::dhcp;
+using namespace isc::util;
+
+// static array with factories for options
+std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
+
+unsigned int
+LibDHCP::unpackOptions6(const boost::shared_array<uint8_t> buf,
+                        unsigned int buf_len,
+                        unsigned int offset, unsigned int parse_len,
+                        isc::dhcp::Option::OptionCollection& options) {
+    if (offset + parse_len > buf_len) {
+        isc_throw(OutOfRange, "Option parse failed. Tried to parse "
+                  << parse_len << " bytes at offset " << offset
+                  << ":  out of buffer");
+    }
+    unsigned int end = offset + parse_len;
+
+    while (offset +4 <= end) {
+        uint16_t opt_type = buf[offset]*256 + buf[offset+1];
+        offset += 2;
+        uint16_t opt_len = buf[offset]*256 + buf[offset+1];
+        offset += 2;
+
+        if (offset + opt_len > end ) {
+            cout << "Option " << opt_type << " truncated." << endl;
+            return (offset);
+        }
+        boost::shared_ptr<Option> opt;
+        switch (opt_type) {
+        case D6O_IA_NA:
+        case D6O_IA_PD:
+            // cout << "Creating Option6IA" << endl;
+            opt = boost::shared_ptr<Option>(new Option6IA(opt_type,
+                                                          buf, buf_len,
+                                                          offset,
+                                                          opt_len));
+            break;
+        case D6O_IAADDR:
+            // cout << "Creating Option6IAAddr" << endl;
+            opt = boost::shared_ptr<Option>(new Option6IAAddr(opt_type,
+                                                              buf, buf_len,
+                                                              offset, opt_len));
+            break;
+        default:
+            // cout << "Creating Option" << endl;
+            opt = boost::shared_ptr<Option>(new Option(Option::V6,
+                                                       opt_type,
+                                                       buf,
+                                                       offset,
+                                                       opt_len));
+            break;
+        }
+        // add option to options
+        options.insert(pair<int, boost::shared_ptr<Option> >(opt_type, opt));
+        offset += opt_len;
+    }
+
+    return (offset);
+}
+
+void
+LibDHCP::unpackOptions4(const std::vector<uint8_t>& buf,
+                        isc::dhcp::Option::OptionCollection& options) {
+    size_t offset = 0;
+
+    // 2 - header of DHCPv4 option
+    while (offset + 1 <= buf.size()) {
+        uint8_t opt_type = buf[offset++];
+        if (offset + 1 == buf.size()) {
+            if (opt_type == DHO_END)
+                return; // just return. Don't need to add DHO_END option
+            else {
+                isc_throw(OutOfRange, "Attempt to parse truncated option "
+                          << 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.");
+        }
+
+        boost::shared_ptr<Option> opt;
+        switch(opt_type) {
+        default:
+            opt = boost::shared_ptr<Option>(new Option(Option::V4, opt_type,
+                                                       buf.begin()+offset,
+                                                       buf.begin()+offset+opt_len));
+        }
+
+        options.insert(pair<int, boost::shared_ptr<Option> >(opt_type, opt));
+        offset += opt_len;
+    }
+}
+
+unsigned int
+LibDHCP::packOptions6(boost::shared_array<uint8_t> data,
+                      unsigned int data_len,
+                      unsigned int offset,
+                      const isc::dhcp::Option::OptionCollection& options) {
+    try {
+        for (Option::OptionCollection::const_iterator it = options.begin();
+             it != options.end();
+             ++it) {
+            unsigned short opt_len = (*it).second->len();
+            if (offset + opt_len > data_len) {
+                isc_throw(OutOfRange, "Failed to build option " <<
+                          (*it).first << ": out of buffer");
+            }
+            offset = it->second->pack(data, data_len, offset);
+        }
+    }
+    catch (const Exception&) {
+        cout << "Packet build failed (Option build failed)." << endl;
+        throw;
+    }
+    return (offset);
+}
+
+void
+LibDHCP::packOptions(isc::util::OutputBuffer& buf,
+                     const Option::OptionCollection& options) {
+    for (Option::OptionCollection::const_iterator it = options.begin();
+         it != options.end();
+         ++it) {
+        it->second->pack4(buf);
+    }
+}
+
+
+bool
+LibDHCP::OptionFactoryRegister(Option::Universe u,
+                               unsigned short opt_type,
+                               Option::Factory * factory) {
+    switch (u) {
+    case Option::V6: {
+        if (v6factories_.find(opt_type)!=v6factories_.end()) {
+            isc_throw(BadValue, "There is already DHCPv6 factory registered "
+                     << "for option type "  << opt_type);
+        }
+        v6factories_[opt_type]=factory;
+        return true;
+    }
+    case Option::V4:
+    default:{
+        isc_throw(BadValue, "This universe type is not supported yet.");
+        return false; // never happens
+    }
+    }
+
+}
diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h
new file mode 100644
index 0000000..468e6bb
--- /dev/null
+++ b/src/lib/dhcp/libdhcp++.h
@@ -0,0 +1,103 @@
+// Copyright (C) 2011  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 LIBDHCP_H_
+#define LIBDHCP_H_
+
+#include <iostream>
+#include <util/buffer.h>
+#include <dhcp/pkt6.h>
+
+namespace isc {
+namespace dhcp {
+
+class LibDHCP {
+
+public:
+    /// Builds collection of options.
+    ///
+    /// Builds raw (on-wire) data for provided collection of options.
+    ///
+    /// @param buf shared pointer to buffer. Data will be stored there.
+    /// @param buf_len buffer length. Used for buffer overflow protection.
+    /// @param offset Offset from beginning of the buffer, where store options
+    /// @param options collection of options to store to
+    ///
+    /// @return offset to the first unused byte in buffer (next one after last
+    ///         used byte)
+    ///
+    static unsigned int
+    packOptions6(boost::shared_array<uint8_t> buf, unsigned int buf_len,
+                 unsigned int offset,
+                 const isc::dhcp::Option::OptionCollection& options);
+
+
+    /// @brief Stores options in a buffer.
+    ///
+    /// Stores all options defined in options containers in a on-wire
+    /// format in output buffer specified by buf.
+    ///
+    /// May throw different exceptions if option assembly fails. There
+    /// may be different reasons (option too large, option malformed,
+    /// too many options etc.)
+    ///
+    /// @param buf
+    /// @param options
+    static void
+    packOptions(isc::util::OutputBuffer& buf,
+                const isc::dhcp::Option::OptionCollection& options);
+
+    static void
+    unpackOptions4(const std::vector<uint8_t>& buf,
+                   isc::dhcp::Option::OptionCollection& options);
+    ///
+    /// Parses provided buffer and creates Option objects.
+    ///
+    /// Parses provided buf array and stores created Option objects
+    /// in options container.
+    ///
+    /// @param buf Buffer to be parsed.
+    /// @param offset Specifies offset for the first option.
+    /// @param options Reference to option container. Options will be
+    ///        put here.
+    ///
+    /// @return offset to first byte after last parsed option
+    ///
+    static unsigned int
+    unpackOptions6(const boost::shared_array<uint8_t> buf, unsigned int buf_len,
+                   unsigned int offset, unsigned int parse_len,
+                   isc::dhcp::Option::OptionCollection& options_);
+
+    ///
+    /// Registers factory method that produces options of specific option types.
+    ///
+    /// @param u universe of the option (V4 or V6)
+    /// @param opt_type option-type
+    /// @param factory function pointer
+    ///
+    /// @return true, if registration was successful, false otherwise
+    ///
+    static bool
+    OptionFactoryRegister(Option::Universe u,
+                          unsigned short type,
+                          Option::Factory * factory);
+protected:
+    // pointers to factories that produce DHCPv6 options
+    static std::map<unsigned short, Option::Factory*> v6factories_;
+};
+
+}
+}
+
+#endif
diff --git a/src/lib/dhcp/libdhcp.cc b/src/lib/dhcp/libdhcp.cc
deleted file mode 100644
index dd86de9..0000000
--- a/src/lib/dhcp/libdhcp.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright (C) 2011  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 <boost/shared_array.hpp>
-#include <boost/shared_ptr.hpp>
-#include <util/buffer.h>
-#include <dhcp/libdhcp.h>
-#include "config.h"
-#include <dhcp/dhcp4.h>
-#include <dhcp/dhcp6.h>
-#include <dhcp/option.h>
-#include <dhcp/option6_ia.h>
-#include <dhcp/option6_iaaddr.h>
-
-using namespace std;
-using namespace isc::dhcp;
-using namespace isc::util;
-
-// static array with factories for options
-std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
-
-unsigned int
-LibDHCP::unpackOptions6(const boost::shared_array<uint8_t> buf,
-                        unsigned int buf_len,
-                        unsigned int offset, unsigned int parse_len,
-                        isc::dhcp::Option::OptionCollection& options) {
-    if (offset + parse_len > buf_len) {
-        isc_throw(OutOfRange, "Option parse failed. Tried to parse "
-                  << parse_len << " bytes at offset " << offset
-                  << ":  out of buffer");
-    }
-    unsigned int end = offset + parse_len;
-
-    while (offset +4 <= end) {
-        uint16_t opt_type = buf[offset]*256 + buf[offset+1];
-        offset += 2;
-        uint16_t opt_len = buf[offset]*256 + buf[offset+1];
-        offset += 2;
-
-        if (offset + opt_len > end ) {
-            cout << "Option " << opt_type << " truncated." << endl;
-            return (offset);
-        }
-        boost::shared_ptr<Option> opt;
-        switch (opt_type) {
-        case D6O_IA_NA:
-        case D6O_IA_PD:
-            // cout << "Creating Option6IA" << endl;
-            opt = boost::shared_ptr<Option>(new Option6IA(opt_type,
-                                                          buf, buf_len,
-                                                          offset,
-                                                          opt_len));
-            break;
-        case D6O_IAADDR:
-            // cout << "Creating Option6IAAddr" << endl;
-            opt = boost::shared_ptr<Option>(new Option6IAAddr(opt_type,
-                                                              buf, buf_len,
-                                                              offset, opt_len));
-            break;
-        default:
-            // cout << "Creating Option" << endl;
-            opt = boost::shared_ptr<Option>(new Option(Option::V6,
-                                                       opt_type,
-                                                       buf,
-                                                       offset,
-                                                       opt_len));
-            break;
-        }
-        // add option to options
-        options.insert(pair<int, boost::shared_ptr<Option> >(opt_type, opt));
-        offset += opt_len;
-    }
-
-    return (offset);
-}
-
-void
-LibDHCP::unpackOptions4(const std::vector<uint8_t>& buf,
-                        isc::dhcp::Option::OptionCollection& options) {
-    size_t offset = 0;
-
-    // 2 - header of DHCPv4 option
-    while (offset + 1 <= buf.size()) {
-        uint8_t opt_type = buf[offset++];
-        if (offset + 1 == buf.size()) {
-            if (opt_type == DHO_END)
-                return; // just return. Don't need to add DHO_END option
-            else {
-                isc_throw(OutOfRange, "Attempt to parse truncated option "
-                          << 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.");
-        }
-
-        boost::shared_ptr<Option> opt;
-        switch(opt_type) {
-        default:
-            opt = boost::shared_ptr<Option>(new Option(Option::V4, opt_type,
-                                                       buf.begin()+offset,
-                                                       buf.begin()+offset+opt_len));
-        }
-
-        options.insert(pair<int, boost::shared_ptr<Option> >(opt_type, opt));
-        offset += opt_len;
-    }
-}
-
-unsigned int
-LibDHCP::packOptions6(boost::shared_array<uint8_t> data,
-                      unsigned int data_len,
-                      unsigned int offset,
-                      const isc::dhcp::Option::OptionCollection& options) {
-    try {
-        for (Option::OptionCollection::const_iterator it = options.begin();
-             it != options.end();
-             ++it) {
-            unsigned short opt_len = (*it).second->len();
-            if (offset + opt_len > data_len) {
-                isc_throw(OutOfRange, "Failed to build option " <<
-                          (*it).first << ": out of buffer");
-            }
-            offset = it->second->pack(data, data_len, offset);
-        }
-    }
-    catch (const Exception&) {
-        cout << "Packet build failed (Option build failed)." << endl;
-        throw;
-    }
-    return (offset);
-}
-
-void
-LibDHCP::packOptions(isc::util::OutputBuffer& buf,
-                     const Option::OptionCollection& options) {
-    for (Option::OptionCollection::const_iterator it = options.begin();
-         it != options.end();
-         ++it) {
-        it->second->pack4(buf);
-    }
-}
-
-
-bool
-LibDHCP::OptionFactoryRegister(Option::Universe u,
-                               unsigned short opt_type,
-                               Option::Factory * factory) {
-    switch (u) {
-    case Option::V6: {
-        if (v6factories_.find(opt_type)!=v6factories_.end()) {
-            isc_throw(BadValue, "There is already DHCPv6 factory registered "
-                     << "for option type "  << opt_type);
-        }
-        v6factories_[opt_type]=factory;
-        return true;
-    }
-    case Option::V4:
-    default:{
-        isc_throw(BadValue, "This universe type is not supported yet.");
-        return false; // never happens
-    }
-    }
-
-}
diff --git a/src/lib/dhcp/libdhcp.h b/src/lib/dhcp/libdhcp.h
deleted file mode 100644
index 468e6bb..0000000
--- a/src/lib/dhcp/libdhcp.h
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright (C) 2011  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 LIBDHCP_H_
-#define LIBDHCP_H_
-
-#include <iostream>
-#include <util/buffer.h>
-#include <dhcp/pkt6.h>
-
-namespace isc {
-namespace dhcp {
-
-class LibDHCP {
-
-public:
-    /// Builds collection of options.
-    ///
-    /// Builds raw (on-wire) data for provided collection of options.
-    ///
-    /// @param buf shared pointer to buffer. Data will be stored there.
-    /// @param buf_len buffer length. Used for buffer overflow protection.
-    /// @param offset Offset from beginning of the buffer, where store options
-    /// @param options collection of options to store to
-    ///
-    /// @return offset to the first unused byte in buffer (next one after last
-    ///         used byte)
-    ///
-    static unsigned int
-    packOptions6(boost::shared_array<uint8_t> buf, unsigned int buf_len,
-                 unsigned int offset,
-                 const isc::dhcp::Option::OptionCollection& options);
-
-
-    /// @brief Stores options in a buffer.
-    ///
-    /// Stores all options defined in options containers in a on-wire
-    /// format in output buffer specified by buf.
-    ///
-    /// May throw different exceptions if option assembly fails. There
-    /// may be different reasons (option too large, option malformed,
-    /// too many options etc.)
-    ///
-    /// @param buf
-    /// @param options
-    static void
-    packOptions(isc::util::OutputBuffer& buf,
-                const isc::dhcp::Option::OptionCollection& options);
-
-    static void
-    unpackOptions4(const std::vector<uint8_t>& buf,
-                   isc::dhcp::Option::OptionCollection& options);
-    ///
-    /// Parses provided buffer and creates Option objects.
-    ///
-    /// Parses provided buf array and stores created Option objects
-    /// in options container.
-    ///
-    /// @param buf Buffer to be parsed.
-    /// @param offset Specifies offset for the first option.
-    /// @param options Reference to option container. Options will be
-    ///        put here.
-    ///
-    /// @return offset to first byte after last parsed option
-    ///
-    static unsigned int
-    unpackOptions6(const boost::shared_array<uint8_t> buf, unsigned int buf_len,
-                   unsigned int offset, unsigned int parse_len,
-                   isc::dhcp::Option::OptionCollection& options_);
-
-    ///
-    /// Registers factory method that produces options of specific option types.
-    ///
-    /// @param u universe of the option (V4 or V6)
-    /// @param opt_type option-type
-    /// @param factory function pointer
-    ///
-    /// @return true, if registration was successful, false otherwise
-    ///
-    static bool
-    OptionFactoryRegister(Option::Universe u,
-                          unsigned short type,
-                          Option::Factory * factory);
-protected:
-    // pointers to factories that produce DHCPv6 options
-    static std::map<unsigned short, Option::Factory*> v6factories_;
-};
-
-}
-}
-
-#endif
diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc
index 20dd97a..96077b7 100644
--- a/src/lib/dhcp/option.cc
+++ b/src/lib/dhcp/option.cc
@@ -22,7 +22,7 @@
 #include "util/io_utilities.h"
 
 #include "dhcp/option.h"
-#include "dhcp/libdhcp.h"
+#include "dhcp/libdhcp++.h"
 
 using namespace std;
 using namespace isc::dhcp;
diff --git a/src/lib/dhcp/option6_addrlst.cc b/src/lib/dhcp/option6_addrlst.cc
index 9be3810..fb082fa 100644
--- a/src/lib/dhcp/option6_addrlst.cc
+++ b/src/lib/dhcp/option6_addrlst.cc
@@ -19,7 +19,7 @@
 
 #include "asiolink/io_address.h"
 #include "util/io_utilities.h"
-#include "dhcp/libdhcp.h"
+#include "dhcp/libdhcp++.h"
 #include "dhcp/option6_addrlst.h"
 #include "dhcp/dhcp6.h"
 
diff --git a/src/lib/dhcp/option6_ia.cc b/src/lib/dhcp/option6_ia.cc
index 209f500..cd55553 100644
--- a/src/lib/dhcp/option6_ia.cc
+++ b/src/lib/dhcp/option6_ia.cc
@@ -17,7 +17,7 @@
 #include <sstream>
 #include "exceptions/exceptions.h"
 
-#include "dhcp/libdhcp.h"
+#include "dhcp/libdhcp++.h"
 #include "dhcp/option6_ia.h"
 #include "dhcp/dhcp6.h"
 #include "util/io_utilities.h"
diff --git a/src/lib/dhcp/option6_iaaddr.cc b/src/lib/dhcp/option6_iaaddr.cc
index fd3bca4..70c2948 100644
--- a/src/lib/dhcp/option6_iaaddr.cc
+++ b/src/lib/dhcp/option6_iaaddr.cc
@@ -17,7 +17,7 @@
 #include <sstream>
 #include "exceptions/exceptions.h"
 
-#include "dhcp/libdhcp.h"
+#include "dhcp/libdhcp++.h"
 #include "dhcp/option6_iaaddr.h"
 #include "dhcp/dhcp6.h"
 #include "asiolink/io_address.h"
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index bea93fc..a2e886a 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -13,7 +13,7 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <dhcp/pkt4.h>
-#include <dhcp/libdhcp.h>
+#include <dhcp/libdhcp++.h>
 #include <dhcp/dhcp4.h>
 #include <exceptions/exceptions.h>
 #include <asiolink/io_address.h>
@@ -59,7 +59,7 @@ Pkt4::Pkt4(const uint8_t* data, size_t len)
      :local_addr_(DEFAULT_ADDRESS),
       remote_addr_(DEFAULT_ADDRESS),
       iface_(""),
-      ifindex_(-1),
+      ifindex_(0),
       local_port_(DHCP4_SERVER_PORT),
       remote_port_(DHCP4_CLIENT_PORT),
       op_(BOOTREQUEST),
diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
index 189d95d..9b6f726 100644
--- a/src/lib/dhcp/pkt4.h
+++ b/src/lib/dhcp/pkt4.h
@@ -302,17 +302,79 @@ public:
     boost::shared_ptr<Option>
     getOption(uint8_t opt_type);
 
+    /// @brief Returns interface name.
+    ///
+    /// Returns interface name over which packet was received or is
+    /// going to be transmitted.
+    ///
+    /// @return interface name
+    std::string getIface() const { return iface_; };
+
+    /// @brief Sets interface name.
+    ///
+    /// Sets interface name over which packet was received or is
+    /// going to be transmitted.
+    ///
+    /// @return interface name
+    void setIface(const std::string& iface ) { iface_ = iface; };
+
+    /// @brief Sets interface index.
+    ///
+    /// @param ifindex specifies interface index.
+    void setIndex(uint32_t ifindex) { ifindex_ = ifindex; };
+
+    /// @brief Returns interface index.
+    ///
+    /// @return interface index
+    uint32_t getIndex() const { return (ifindex_); };
+
+    /// @brief Sets remote address.
+    ///
+    /// @params remote specifies remote address
+    void setRemoteAddr(const isc::asiolink::IOAddress& remote) {
+        remote_addr_ = remote;
+    }
+
+    /// @brief Returns remote address
+    ///
+    /// @return remote address
+    const isc::asiolink::IOAddress& getRemoteAddr() {
+        return (remote_addr_);
+    }
+
+    /// @brief Sets local address.
+    ///
+    /// @params local specifies local address
+    void setLocalAddr(const isc::asiolink::IOAddress& local) {
+        local_addr_ = local;
+    }
+
+    /// @brief Returns local address.
+    ///
+    /// @return local address
+    const isc::asiolink::IOAddress& getLocalAddr() {
+        return (local_addr_);
+    }
+
+    /// @brief Sets local port.
+    ///
+    /// @params local specifies local port
+    void setLocalPort(uint16_t local) { local_port_ = local; }
+
+    /// @brief Returns local port.
+    ///
+    /// @return local port
+    uint16_t getLocalPort() { return (local_port_); }
 
-    /// @brief set interface over which packet should be sent
+    /// @brief Sets remote port.
     ///
-    /// @param interface defines outbound interface
-    void setIface(const std::string& interface){ iface_ = interface; }
+    /// @params remote specifies remote port
+    void setRemotePort(uint16_t remote) { remote_port_ = remote; }
 
-    /// @brief gets interface over which packet was received or
-    ///        will be transmitted
+    /// @brief Returns remote port.
     ///
-    /// @return name of the interface
-    std::string getIface() const { return iface_; }
+    /// @return remote port
+    uint16_t getRemotePort() { return (remote_port_); }
 
 protected:
 
@@ -338,13 +400,13 @@ protected:
     /// Each network interface has assigned unique ifindex. It is functional
     /// equvalent of name, but sometimes more useful, e.g. when using crazy
     /// systems that allow spaces in interface names e.g. MS Windows)
-    int ifindex_;
+    uint32_t ifindex_;
 
     /// local UDP port
-    int local_port_;
+    uint16_t local_port_;
 
     /// remote UDP port
-    int remote_port_;
+    uint16_t remote_port_;
 
     /// @brief message operation code
     ///
diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc
index 84c5729..ff27d5e 100644
--- a/src/lib/dhcp/pkt6.cc
+++ b/src/lib/dhcp/pkt6.cc
@@ -15,7 +15,7 @@
 
 #include "dhcp/dhcp6.h"
 #include "dhcp/pkt6.h"
-#include "dhcp/libdhcp.h"
+#include "dhcp/libdhcp++.h"
 #include "exceptions/exceptions.h"
 #include <iostream>
 #include <sstream>
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index 176992f..ef849f1 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -2,6 +2,10 @@ SUBDIRS = .
 
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_top_srcdir)/src/lib/testutils/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/dhcp/tests\"
+AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
+
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 if USE_STATIC_LINK
@@ -12,31 +16,33 @@ CLEANFILES = *.gcno *.gcda
 
 TESTS =
 if HAVE_GTEST
-TESTS += libdhcp_unittests
-libdhcp_unittests_SOURCES  = run_unittests.cc
-libdhcp_unittests_SOURCES += ../libdhcp.h ../libdhcp.cc libdhcp_unittest.cc
-libdhcp_unittests_SOURCES += ../option6_iaaddr.h ../option6_iaaddr.cc option6_iaaddr_unittest.cc
-libdhcp_unittests_SOURCES += ../option6_ia.h ../option6_ia.cc option6_ia_unittest.cc
-libdhcp_unittests_SOURCES += ../option6_addrlst.h ../option6_addrlst.cc option6_addrlst_unittest.cc
-libdhcp_unittests_SOURCES += ../option4_addrlst.cc ../option4_addrlst.h option4_addrlst_unittest.cc
-libdhcp_unittests_SOURCES += ../option.h ../option.cc option_unittest.cc
-libdhcp_unittests_SOURCES += ../pkt6.h ../pkt6.cc pkt6_unittest.cc
-libdhcp_unittests_SOURCES += ../pkt4.h ../pkt4.cc pkt4_unittest.cc
-
-libdhcp_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
-libdhcp_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
-
-libdhcp_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+TESTS += libdhcp++_unittests
+libdhcp___unittests_SOURCES  = run_unittests.cc
+libdhcp___unittests_SOURCES += ../libdhcp++.h ../libdhcp++.cc
+libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
+libdhcp___unittests_SOURCES += ../iface_mgr.cc ../iface_mgr.h iface_mgr_unittest.cc
+libdhcp___unittests_SOURCES += ../option6_iaaddr.h ../option6_iaaddr.cc option6_iaaddr_unittest.cc
+libdhcp___unittests_SOURCES += ../option6_ia.h ../option6_ia.cc option6_ia_unittest.cc
+libdhcp___unittests_SOURCES += ../option6_addrlst.h ../option6_addrlst.cc option6_addrlst_unittest.cc
+libdhcp___unittests_SOURCES += ../option4_addrlst.cc ../option4_addrlst.h option4_addrlst_unittest.cc
+libdhcp___unittests_SOURCES += ../option.h ../option.cc option_unittest.cc
+libdhcp___unittests_SOURCES += ../pkt6.h ../pkt6.cc pkt6_unittest.cc
+libdhcp___unittests_SOURCES += ../pkt4.h ../pkt4.cc pkt4_unittest.cc
+
+libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
+libdhcp___unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
+
+libdhcp___unittests_CXXFLAGS = $(AM_CXXFLAGS)
 if USE_CLANGPP
 # This is to workaround unused variables tcout and tcerr in
 # log4cplus's streams.h.
-libdhcp_unittests_CXXFLAGS += -Wno-unused-variable
+libdhcp___unittests_CXXFLAGS += -Wno-unused-variable
 endif
-libdhcp_unittests_LDADD  = $(GTEST_LDADD)
-libdhcp_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-libdhcp_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
-libdhcp_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-libdhcp_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+libdhcp___unittests_LDADD  = $(GTEST_LDADD)
+libdhcp___unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+libdhcp___unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+libdhcp___unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+libdhcp___unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
new file mode 100644
index 0000000..b647c18
--- /dev/null
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -0,0 +1,524 @@
+// Copyright (C) 2011  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 <iostream>
+#include <fstream>
+#include <sstream>
+
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+#include <asiolink/io_address.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/dhcp4.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+// name of loopback interface detection
+const size_t buf_size = 32;
+char LOOPBACK[buf_size] = "lo";
+
+namespace {
+const char* const INTERFACE_FILE = TEST_DATA_BUILDDIR "/interfaces.txt";
+
+class NakedIfaceMgr: public IfaceMgr {
+    // "naked" Interface Manager, exposes internal fields
+public:
+    NakedIfaceMgr() { }
+    IfaceCollection & getIfacesLst() { return ifaces_; }
+};
+
+// dummy class for now, but this will be expanded when needed
+class IfaceMgrTest : public ::testing::Test {
+public:
+    IfaceMgrTest() {
+    }
+
+    void createLoInterfacesTxt() {
+        unlink(INTERFACE_FILE);
+        fstream fakeifaces(INTERFACE_FILE, ios::out|ios::trunc);
+        fakeifaces << LOOPBACK << " ::1";
+        fakeifaces.close();
+    }
+};
+
+// 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.
+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) {
+        cout << "This is Linux, using lo as loopback." << endl;
+        snprintf(LOOPBACK, buf_size - 1, "lo");
+    } else if (if_nametoindex("lo0") > 0) {
+        cout << "This is BSD, using lo0 as loopback." << endl;
+        snprintf(LOOPBACK, buf_size - 1, "lo0");
+    } else {
+        cout << "Failed to detect loopback interface. Neither "
+             << "lo nor lo0 worked. I give up." << endl;
+        FAIL();
+    }
+}
+
+// uncomment this test to create packet writer. It will
+// write incoming DHCPv6 packets as C arrays. That is useful
+// for generating test sequences based on actual traffic
+//
+// TODO: this potentially should be moved to a separate tool
+//
+
+#if 0
+TEST_F(IfaceMgrTest, dhcp6Sniffer) {
+    // testing socket operation in a portable way is tricky
+    // without interface detection implemented
+
+    unlink("interfaces.txt");
+
+    ofstream interfaces("interfaces.txt", ios::ate);
+    interfaces << "eth0 fe80::21e:8cff:fe9b:7349";
+    interfaces.close();
+
+    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+
+    Pkt6* pkt = NULL;
+    int cnt = 0;
+    cout << "---8X-----------------------------------------" << endl;
+    while (true) {
+        pkt = ifacemgr->receive();
+
+        cout << "// this code is autogenerated. Do NOT edit." << endl;
+        cout << "// Received " << pkt->data_len_ << " bytes packet:" << endl;
+        cout << "Pkt6 *capture" << cnt++ << "() {" << endl;
+        cout << "    Pkt6* pkt;" << endl;
+        cout << "    pkt = new Pkt6(" << pkt->data_len_ << ");" << endl;
+        cout << "    pkt->remote_port_ = " << pkt-> remote_port_ << ";" << endl;
+        cout << "    pkt->remote_addr_ = IOAddress(\""
+             << pkt->remote_addr_.toText() << "\");" << endl;
+        cout << "    pkt->local_port_ = " << pkt-> local_port_ << ";" << endl;
+        cout << "    pkt->local_addr_ = IOAddress(\""
+             << pkt->local_addr_.toText() << "\");" << endl;
+        cout << "    pkt->ifindex_ = " << pkt->ifindex_ << ";" << endl;
+        cout << "    pkt->iface_ = \"" << pkt->iface_ << "\";" << endl;
+
+        // TODO it is better to declare an array and then memcpy it to
+        // packet.
+        for (int i=0; i< pkt->data_len_; i++) {
+            cout << "    pkt->data_[" << i << "]="
+                 << (int)(unsigned char)pkt->data_[i] << "; ";
+            if (!(i%4))
+                cout << endl;
+        }
+        cout << endl;
+        cout << "    return (pkt);" << endl;
+        cout << "}" << endl << endl;
+
+        delete pkt;
+    }
+    cout << "---8X-----------------------------------------" << endl;
+
+    // never happens. Infinite loop is infinite
+    delete pkt;
+    delete ifacemgr;
+}
+#endif
+
+TEST_F(IfaceMgrTest, basic) {
+    // checks that IfaceManager can be instantiated
+
+    IfaceMgr & ifacemgr = IfaceMgr::instance();
+    ASSERT_TRUE(&ifacemgr != 0);
+}
+
+TEST_F(IfaceMgrTest, ifaceClass) {
+    // basic tests for Iface inner class
+
+    IfaceMgr::Iface* iface = new IfaceMgr::Iface("eth5", 7);
+
+    EXPECT_STREQ("eth5/7", iface->getFullName().c_str());
+
+    delete iface;
+
+}
+
+// TODO: Implement getPlainMac() test as soon as interface detection
+// is implemented.
+TEST_F(IfaceMgrTest, getIface) {
+
+    cout << "Interface checks. Please ignore socket binding errors." << endl;
+    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+
+    // interface name, ifindex
+    IfaceMgr::Iface iface1("lo1", 1);
+    IfaceMgr::Iface iface2("eth5", 2);
+    IfaceMgr::Iface iface3("en3", 5);
+    IfaceMgr::Iface iface4("e1000g0", 3);
+
+    // note: real interfaces may be detected as well
+    ifacemgr->getIfacesLst().push_back(iface1);
+    ifacemgr->getIfacesLst().push_back(iface2);
+    ifacemgr->getIfacesLst().push_back(iface3);
+    ifacemgr->getIfacesLst().push_back(iface4);
+
+    cout << "There are " << ifacemgr->getIfacesLst().size()
+         << " interfaces." << endl;
+    for (IfaceMgr::IfaceCollection::iterator iface=ifacemgr->getIfacesLst().begin();
+         iface != ifacemgr->getIfacesLst().end();
+         ++iface) {
+        cout << "  " << iface->getFullName() << endl;
+    }
+
+
+    // check that interface can be retrieved by ifindex
+    IfaceMgr::Iface* tmp = ifacemgr->getIface(5);
+    // ASSERT_NE(NULL, tmp); is not supported. hmmmm.
+    ASSERT_TRUE( tmp != NULL );
+
+    EXPECT_EQ( "en3", tmp->getName() );
+    EXPECT_EQ(5, tmp->getIndex());
+
+    // check that interface can be retrieved by name
+    tmp = ifacemgr->getIface("lo1");
+    ASSERT_TRUE( tmp != NULL );
+
+    EXPECT_EQ( "lo1", tmp->getName() );
+    EXPECT_EQ(1, tmp->getIndex());
+
+    // check that non-existing interfaces are not returned
+    EXPECT_EQ(static_cast<void*>(NULL), ifacemgr->getIface("wifi0") );
+
+    delete ifacemgr;
+}
+
+TEST_F(IfaceMgrTest, detectIfaces) {
+
+    // test detects that interfaces can be detected
+    // there is no code for that now, but interfaces are
+    // read from file
+    fstream fakeifaces(INTERFACE_FILE, ios::out|ios::trunc);
+    fakeifaces << "eth0 fe80::1234";
+    fakeifaces.close();
+
+    // this is not usable on systems that don't have eth0
+    // interfaces. Nevertheless, this fake interface should
+    // be on list, but if_nametoindex() will fail.
+
+    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+
+    ASSERT_TRUE( ifacemgr->getIface("eth0") != NULL );
+
+    IfaceMgr::Iface* eth0 = ifacemgr->getIface("eth0");
+
+    // there should be one address
+    IfaceMgr::AddressCollection addrs = eth0->getAddresses();
+    ASSERT_EQ(1, addrs.size());
+
+    IOAddress addr = *addrs.begin();
+
+    EXPECT_STREQ( "fe80::1234", addr.toText().c_str() );
+
+    delete ifacemgr;
+    unlink(INTERFACE_FILE);
+}
+
+TEST_F(IfaceMgrTest, sockets6) {
+    // testing socket operation in a portable way is tricky
+    // without interface detection implemented
+
+    createLoInterfacesTxt();
+
+    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+
+    IOAddress loAddr("::1");
+
+    Pkt6 pkt6(128);
+    pkt6.iface_ = LOOPBACK;
+
+    // bind multicast socket to port 10547
+    int socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
+    EXPECT_GT(socket1, 0); // socket > 0
+
+    EXPECT_EQ(socket1, ifacemgr->getSocket(pkt6));
+
+    // bind unicast socket to port 10548
+    int socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10548);
+    EXPECT_GT(socket2, 0);
+
+    // removed code for binding socket twice to the same address/port
+    // as it caused problems on some platforms (e.g. Mac OS X)
+
+    close(socket1);
+    close(socket2);
+
+    delete ifacemgr;
+    unlink(INTERFACE_FILE);
+}
+
+// TODO: disabled due to other naming on various systems
+// (lo in Linux, lo0 in BSD systems)
+TEST_F(IfaceMgrTest, DISABLED_sockets6Mcast) {
+    // testing socket operation in a portable way is tricky
+    // without interface detection implemented
+
+    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+
+    IOAddress loAddr("::1");
+    IOAddress mcastAddr("ff02::1:2");
+
+    // bind multicast socket to port 10547
+    int socket1 = ifacemgr->openSocket(LOOPBACK, mcastAddr, 10547);
+    EXPECT_GT(socket1, 0); // socket > 0
+
+    // expect success. This address/port is already bound, but
+    // we are using SO_REUSEADDR, so we can bind it twice
+    int socket2 = ifacemgr->openSocket(LOOPBACK, mcastAddr, 10547);
+    EXPECT_GT(socket2, 0);
+
+    // there's no good way to test negative case here.
+    // we would need non-multicast interface. We will be able
+    // to iterate thru available interfaces and check if there
+    // are interfaces without multicast-capable flag.
+
+    close(socket1);
+    close(socket2);
+
+    delete ifacemgr;
+}
+
+TEST_F(IfaceMgrTest, sendReceive6) {
+
+    // testing socket operation in a portable way is tricky
+    // without interface detection implemented
+    createLoInterfacesTxt();
+
+    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+
+    // let's assume that every supported OS have lo interface
+    IOAddress loAddr("::1");
+    int socket1 = 0, socket2 = 0;
+    EXPECT_NO_THROW(
+        socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
+        socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10546);
+    );
+
+    EXPECT_GT(socket1, 0);
+    EXPECT_GT(socket2, 0);
+
+    boost::shared_ptr<Pkt6> sendPkt(new Pkt6(128) );
+
+    // prepare dummy payload
+    for (int i=0;i<128; i++) {
+        sendPkt->data_[i] = i;
+    }
+
+    sendPkt->remote_port_ = 10547;
+    sendPkt->remote_addr_ = IOAddress("::1");
+    sendPkt->ifindex_ = 1;
+    sendPkt->iface_ = LOOPBACK;
+
+    boost::shared_ptr<Pkt6> rcvPkt;
+
+    EXPECT_EQ(true, ifacemgr->send(sendPkt));
+
+    rcvPkt = ifacemgr->receive6();
+
+    ASSERT_TRUE( rcvPkt ); // received our own packet
+
+    // let's check that we received what was sent
+    EXPECT_EQ(sendPkt->data_len_, rcvPkt->data_len_);
+    EXPECT_EQ(0, memcmp(&sendPkt->data_[0], &rcvPkt->data_[0],
+                        rcvPkt->data_len_) );
+
+    EXPECT_EQ(sendPkt->remote_addr_.toText(), rcvPkt->remote_addr_.toText());
+
+    // since we opened 2 sockets on the same interface and none of them is multicast,
+    // none is preferred over the other for sending data, so we really should not
+    // assume the one or the other will always be choosen for sending data. Therefore
+    // we should accept both values as source ports.
+    EXPECT_TRUE( (rcvPkt->remote_port_ == 10546) || (rcvPkt->remote_port_ == 10547) );
+
+    delete ifacemgr;
+    unlink(INTERFACE_FILE);
+}
+
+TEST_F(IfaceMgrTest, socket4) {
+
+    createLoInterfacesTxt();
+    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+
+    // Let's assume that every supported OS have lo interface.
+    IOAddress loAddr("127.0.0.1");
+    // Use unprivileged port (it's convenient for running tests as non-root).
+    int socket1 = 0;
+
+    EXPECT_NO_THROW(
+        socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, DHCP4_SERVER_PORT + 10000);
+    );
+
+    EXPECT_GT(socket1, 0);
+
+    Pkt4 pkt(DHCPDISCOVER, 1234);
+    pkt.setIface(LOOPBACK);
+
+    // Expect that we get the socket that we just opened.
+    EXPECT_EQ(socket1, ifacemgr->getSocket(pkt));
+
+    close(socket1);
+
+    delete ifacemgr;
+    unlink(INTERFACE_FILE);
+}
+
+// Test the Iface structure itself
+TEST_F(IfaceMgrTest, iface) {
+    IfaceMgr::Iface* iface = NULL;
+    EXPECT_NO_THROW(
+        iface = new IfaceMgr::Iface("eth0",1);
+    );
+
+    EXPECT_EQ("eth0", iface->getName());
+    EXPECT_EQ(1, iface->getIndex());
+    EXPECT_EQ("eth0/1", iface->getFullName());
+
+    // Let's make a copy of this address collection.
+    IfaceMgr::AddressCollection addrs = iface->getAddresses();
+
+    EXPECT_EQ(0, addrs.size());
+
+    IOAddress addr1("192.0.2.6");
+    iface->addAddress(addr1);
+
+    addrs = iface->getAddresses();
+    ASSERT_EQ(1, addrs.size());
+    EXPECT_EQ("192.0.2.6", addrs.at(0).toText());
+
+    // No such address, should return false.
+    EXPECT_FALSE(iface->delAddress(IOAddress("192.0.8.9")));
+
+    // This address is present, delete it!
+    EXPECT_TRUE(iface->delAddress(IOAddress("192.0.2.6")));
+
+    // Not really necessary, previous reference still points to the same
+    // collection. Let's do it anyway, as test code may serve as example
+    // usage code as well.
+    addrs = iface->getAddresses();
+
+    EXPECT_EQ(0, addrs.size());
+
+    EXPECT_NO_THROW(
+        delete iface;
+    );
+}
+
+TEST_F(IfaceMgrTest, socketInfo) {
+
+    // check that socketinfo for IPv4 socket is functional
+    IfaceMgr::SocketInfo sock1(7, IOAddress("192.0.2.56"), DHCP4_SERVER_PORT + 7);
+    EXPECT_EQ(7, sock1.sockfd_);
+    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 socketinfo for IPv6 socket is functional
+    IfaceMgr::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_);
+
+    // now let's test if IfaceMgr handles socket info properly
+    createLoInterfacesTxt();
+    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+    IfaceMgr::Iface* loopback = ifacemgr->getIface(LOOPBACK);
+    ASSERT_TRUE(loopback);
+    loopback->addSocket(sock1);
+    loopback->addSocket(sock2);
+
+    Pkt6 pkt6(100);
+
+    // pkt6 dos not have interface set yet
+    EXPECT_THROW(
+        ifacemgr->getSocket(pkt6),
+        BadValue
+    );
+
+    // try to send over non-existing interface
+    pkt6.iface_ = "nosuchinterface45";
+    EXPECT_THROW(
+        ifacemgr->getSocket(pkt6),
+        BadValue
+    );
+
+    // this will work
+    pkt6.iface_ = LOOPBACK;
+    EXPECT_EQ(9, ifacemgr->getSocket(pkt6));
+
+    bool deleted = false;
+    EXPECT_NO_THROW(
+        deleted = ifacemgr->getIface(LOOPBACK)->delSocket(9);
+    );
+    EXPECT_EQ(true, deleted);
+
+    // it should throw again, there's no usable socket anymore
+    EXPECT_THROW(
+        ifacemgr->getSocket(pkt6),
+        Unexpected
+    );
+
+    // repeat for pkt4
+    Pkt4 pkt4(DHCPDISCOVER, 1);
+
+    // pkt4 does not have interface set yet.
+    EXPECT_THROW(
+        ifacemgr->getSocket(pkt4),
+        BadValue
+    );
+
+    // Try to send over non-existing interface.
+    pkt4.setIface("nosuchinterface45");
+    EXPECT_THROW(
+        ifacemgr->getSocket(pkt4),
+        BadValue
+    );
+
+    // Socket info is set, packet has well defined interface. It should work.
+    pkt4.setIface(LOOPBACK);
+    EXPECT_EQ(7, ifacemgr->getSocket(pkt4));
+
+    EXPECT_NO_THROW(
+        ifacemgr->getIface(LOOPBACK)->delSocket(7);
+    );
+
+    // It should throw again, there's no usable socket anymore.
+    EXPECT_THROW(
+        ifacemgr->getSocket(pkt4),
+        Unexpected
+    );
+
+    delete ifacemgr;
+    unlink(INTERFACE_FILE);
+}
+
+}
diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc
new file mode 100644
index 0000000..ee3b873
--- /dev/null
+++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc
@@ -0,0 +1,234 @@
+// Copyright (C) 2011  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 <iostream>
+#include <sstream>
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+#include <util/buffer.h>
+#include <dhcp/libdhcp++.h>
+#include "config.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::util;
+
+namespace {
+class LibDhcpTest : public ::testing::Test {
+public:
+    LibDhcpTest() {
+    }
+};
+
+static const uint8_t packed[] = {
+    0, 12, 0, 5, 100, 101, 102, 103, 104, // opt1 (9 bytes)
+    0, 13, 0, 3, 105, 106, 107, // opt2 (7 bytes)
+    0, 14, 0, 2, 108, 109, // opt3 (6 bytes)
+    1,  0, 0, 4, 110, 111, 112, 113, // opt4 (8 bytes)
+    1,  1, 0, 1, 114 // opt5 (5 bytes)
+};
+
+TEST(LibDhcpTest, packOptions6) {
+    boost::shared_array<uint8_t> buf(new uint8_t[512]);
+    isc::dhcp::Option::OptionCollection opts; // list of options
+
+    // generate content for options
+    for (int i = 0; i < 64; i++) {
+        buf[i]=i+100;
+    }
+
+    boost::shared_ptr<Option> opt1(new Option(Option::V6, 12, buf, 0, 5));
+    boost::shared_ptr<Option> opt2(new Option(Option::V6, 13, buf, 5, 3));
+    boost::shared_ptr<Option> opt3(new Option(Option::V6, 14, buf, 8, 2));
+    boost::shared_ptr<Option> opt4(new Option(Option::V6,256, buf,10, 4));
+    boost::shared_ptr<Option> opt5(new Option(Option::V6,257, buf,14, 1));
+
+    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt1));
+    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt2));
+    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt3));
+    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt4));
+    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt5));
+
+    unsigned int offset;
+    EXPECT_NO_THROW ({
+         offset = LibDHCP::packOptions6(buf, 512, 100, opts);
+    });
+    EXPECT_EQ(135, offset); // options should take 35 bytes
+    EXPECT_EQ(0, memcmp(&buf[100], packed, 35) );
+}
+
+TEST(LibDhcpTest, unpackOptions6) {
+
+    // just couple of random options
+    // Option is used as a simple option implementation
+    // More advanced uses are validated in tests dedicated for
+    // specific derived classes.
+    isc::dhcp::Option::OptionCollection options; // list of options
+
+    // we can't use packed directly, as shared_array would try to
+    // free it eventually
+    boost::shared_array<uint8_t> buf(new uint8_t[512]);
+    memcpy(&buf[0], packed, 35);
+
+    unsigned int offset;
+    EXPECT_NO_THROW ({
+        offset = LibDHCP::unpackOptions6(buf, 512, 0, 35, options);
+    });
+
+    EXPECT_EQ(35, offset); // parsed first 35 bytes (offset 0..34)
+    EXPECT_EQ(options.size(), 5); // there should be 5 options
+
+    isc::dhcp::Option::OptionCollection::const_iterator x = options.find(12);
+    ASSERT_FALSE(x == options.end()); // option 1 should exist
+    EXPECT_EQ(12, x->second->getType());  // this should be option 12
+    ASSERT_EQ(9, x->second->len()); // it should be of length 9
+    EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+4, 5)); // data len=5
+
+    x = options.find(13);
+    ASSERT_FALSE(x == options.end()); // option 13 should exist
+    EXPECT_EQ(13, x->second->getType());  // this should be option 13
+    ASSERT_EQ(7, x->second->len()); // it should be of length 7
+    EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+13, 3)); // data len=3
+
+    x = options.find(14);
+    ASSERT_FALSE(x == options.end()); // option 3 should exist
+    EXPECT_EQ(14, x->second->getType());  // this should be option 14
+    ASSERT_EQ(6, x->second->len()); // it should be of length 6
+    EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+20, 2)); // data len=2
+
+    x = options.find(256);
+    ASSERT_FALSE(x == options.end()); // option 256 should exist
+    EXPECT_EQ(256, x->second->getType());  // this should be option 256
+    ASSERT_EQ(8, x->second->len()); // it should be of length 7
+    EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+26, 4)); // data len=4
+
+    x = options.find(257);
+    ASSERT_FALSE(x == options.end()); // option 257 should exist
+    EXPECT_EQ(257, x->second->getType());  // this should be option 257
+    ASSERT_EQ(5, x->second->len()); // it should be of length 5
+    EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+34, 1)); // data len=1
+
+    x = options.find(0);
+    EXPECT_TRUE(x == options.end()); // option 0 not found
+
+    x = options.find(1); // 1 is htons(256) on little endians. Worth checking
+    EXPECT_TRUE(x == options.end()); // option 1 not found
+
+    x = options.find(2);
+    EXPECT_TRUE(x == options.end()); // option 2 not found
+
+    x = options.find(32000);
+    EXPECT_TRUE(x == options.end()); // option 32000 not found
+}
+
+
+static uint8_t v4Opts[] = {
+    12,  3, 0,   1,  2,
+    13,  3, 10, 11, 12,
+    14,  3, 20, 21, 22,
+    254, 3, 30, 31, 32,
+    128, 3, 40, 41, 42
+};
+
+TEST(LibDhcpTest, packOptions4) {
+
+    vector<uint8_t> payload[5];
+    for (int i = 0; i < 5; i++) {
+        payload[i].resize(3);
+        payload[i][0] = i*10;
+        payload[i][1] = i*10+1;
+        payload[i][2] = i*10+2;
+    }
+
+    boost::shared_ptr<Option> opt1(new Option(Option::V4, 12, payload[0]));
+    boost::shared_ptr<Option> opt2(new Option(Option::V4, 13, payload[1]));
+    boost::shared_ptr<Option> opt3(new Option(Option::V4, 14, payload[2]));
+    boost::shared_ptr<Option> opt4(new Option(Option::V4,254, payload[3]));
+    boost::shared_ptr<Option> opt5(new Option(Option::V4,128, payload[4]));
+
+    isc::dhcp::Option::OptionCollection opts; // list of options
+    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt1));
+    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt2));
+    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt3));
+    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt4));
+    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt5));
+
+    vector<uint8_t> expVect(v4Opts, v4Opts + sizeof(v4Opts));
+
+    OutputBuffer buf(100);
+    EXPECT_NO_THROW (
+        LibDHCP::packOptions(buf, opts);
+    );
+    ASSERT_EQ(buf.getLength(), sizeof(v4Opts));
+    EXPECT_EQ(0, memcmp(v4Opts, buf.getData(), sizeof(v4Opts)));
+
+}
+
+TEST(LibDhcpTest, unpackOptions4) {
+
+    vector<uint8_t> packed(v4Opts, v4Opts + sizeof(v4Opts));
+    isc::dhcp::Option::OptionCollection options; // list of options
+
+    ASSERT_NO_THROW(
+        LibDHCP::unpackOptions4(packed, options);
+    );
+
+    isc::dhcp::Option::OptionCollection::const_iterator x = options.find(12);
+    ASSERT_FALSE(x == options.end()); // option 1 should exist
+    EXPECT_EQ(12, x->second->getType());  // this should be option 12
+    ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
+    EXPECT_EQ(5, x->second->len()); // total option length 5
+    EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+2, 3)); // data len=3
+
+    x = options.find(13);
+    ASSERT_FALSE(x == options.end()); // option 1 should exist
+    EXPECT_EQ(13, x->second->getType());  // this should be option 13
+    ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
+    EXPECT_EQ(5, x->second->len()); // total option length 5
+    EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+7, 3)); // data len=3
+
+    x = options.find(14);
+    ASSERT_FALSE(x == options.end()); // option 3 should exist
+    EXPECT_EQ(14, x->second->getType());  // this should be option 14
+    ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
+    EXPECT_EQ(5, x->second->len()); // total option length 5
+    EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+12, 3)); // data len=3
+
+    x = options.find(254);
+    ASSERT_FALSE(x == options.end()); // option 3 should exist
+    EXPECT_EQ(254, x->second->getType());  // this should be option 254
+    ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
+    EXPECT_EQ(5, x->second->len()); // total option length 5
+    EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+17, 3)); // data len=3
+
+    x = options.find(128);
+    ASSERT_FALSE(x == options.end()); // option 3 should exist
+    EXPECT_EQ(128, x->second->getType());  // this should be option 254
+    ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
+    EXPECT_EQ(5, x->second->len()); // total option length 5
+    EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+22, 3)); // data len=3
+
+    x = options.find(0);
+    EXPECT_TRUE(x == options.end()); // option 0 not found
+
+    x = options.find(1);
+    EXPECT_TRUE(x == options.end()); // option 1 not found
+
+    x = options.find(2);
+    EXPECT_TRUE(x == options.end()); // option 2 not found
+}
+
+}
diff --git a/src/lib/dhcp/tests/libdhcp_unittest.cc b/src/lib/dhcp/tests/libdhcp_unittest.cc
deleted file mode 100644
index 11b618c..0000000
--- a/src/lib/dhcp/tests/libdhcp_unittest.cc
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright (C) 2011  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 <iostream>
-#include <sstream>
-#include <arpa/inet.h>
-#include <gtest/gtest.h>
-#include <util/buffer.h>
-#include <dhcp/libdhcp.h>
-#include "config.h"
-
-using namespace std;
-using namespace isc;
-using namespace isc::dhcp;
-using namespace isc::util;
-
-namespace {
-class LibDhcpTest : public ::testing::Test {
-public:
-    LibDhcpTest() {
-    }
-};
-
-static const uint8_t packed[] = {
-    0, 12, 0, 5, 100, 101, 102, 103, 104, // opt1 (9 bytes)
-    0, 13, 0, 3, 105, 106, 107, // opt2 (7 bytes)
-    0, 14, 0, 2, 108, 109, // opt3 (6 bytes)
-    1,  0, 0, 4, 110, 111, 112, 113, // opt4 (8 bytes)
-    1,  1, 0, 1, 114 // opt5 (5 bytes)
-};
-
-TEST(LibDhcpTest, packOptions6) {
-    boost::shared_array<uint8_t> buf(new uint8_t[512]);
-    isc::dhcp::Option::OptionCollection opts; // list of options
-
-    // generate content for options
-    for (int i = 0; i < 64; i++) {
-        buf[i]=i+100;
-    }
-
-    boost::shared_ptr<Option> opt1(new Option(Option::V6, 12, buf, 0, 5));
-    boost::shared_ptr<Option> opt2(new Option(Option::V6, 13, buf, 5, 3));
-    boost::shared_ptr<Option> opt3(new Option(Option::V6, 14, buf, 8, 2));
-    boost::shared_ptr<Option> opt4(new Option(Option::V6,256, buf,10, 4));
-    boost::shared_ptr<Option> opt5(new Option(Option::V6,257, buf,14, 1));
-
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt1));
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt2));
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt3));
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt4));
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt5));
-
-    unsigned int offset;
-    EXPECT_NO_THROW ({
-         offset = LibDHCP::packOptions6(buf, 512, 100, opts);
-    });
-    EXPECT_EQ(135, offset); // options should take 35 bytes
-    EXPECT_EQ(0, memcmp(&buf[100], packed, 35) );
-}
-
-TEST(LibDhcpTest, unpackOptions6) {
-
-    // just couple of random options
-    // Option is used as a simple option implementation
-    // More advanced uses are validated in tests dedicated for
-    // specific derived classes.
-    isc::dhcp::Option::OptionCollection options; // list of options
-
-    // we can't use packed directly, as shared_array would try to
-    // free it eventually
-    boost::shared_array<uint8_t> buf(new uint8_t[512]);
-    memcpy(&buf[0], packed, 35);
-
-    unsigned int offset;
-    EXPECT_NO_THROW ({
-        offset = LibDHCP::unpackOptions6(buf, 512, 0, 35, options);
-    });
-
-    EXPECT_EQ(35, offset); // parsed first 35 bytes (offset 0..34)
-    EXPECT_EQ(options.size(), 5); // there should be 5 options
-
-    isc::dhcp::Option::OptionCollection::const_iterator x = options.find(12);
-    ASSERT_FALSE(x == options.end()); // option 1 should exist
-    EXPECT_EQ(12, x->second->getType());  // this should be option 12
-    ASSERT_EQ(9, x->second->len()); // it should be of length 9
-    EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+4, 5)); // data len=5
-
-    x = options.find(13);
-    ASSERT_FALSE(x == options.end()); // option 13 should exist
-    EXPECT_EQ(13, x->second->getType());  // this should be option 13
-    ASSERT_EQ(7, x->second->len()); // it should be of length 7
-    EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+13, 3)); // data len=3
-
-    x = options.find(14);
-    ASSERT_FALSE(x == options.end()); // option 3 should exist
-    EXPECT_EQ(14, x->second->getType());  // this should be option 14
-    ASSERT_EQ(6, x->second->len()); // it should be of length 6
-    EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+20, 2)); // data len=2
-
-    x = options.find(256);
-    ASSERT_FALSE(x == options.end()); // option 256 should exist
-    EXPECT_EQ(256, x->second->getType());  // this should be option 256
-    ASSERT_EQ(8, x->second->len()); // it should be of length 7
-    EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+26, 4)); // data len=4
-
-    x = options.find(257);
-    ASSERT_FALSE(x == options.end()); // option 257 should exist
-    EXPECT_EQ(257, x->second->getType());  // this should be option 257
-    ASSERT_EQ(5, x->second->len()); // it should be of length 5
-    EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+34, 1)); // data len=1
-
-    x = options.find(0);
-    EXPECT_TRUE(x == options.end()); // option 0 not found
-
-    x = options.find(1); // 1 is htons(256) on little endians. Worth checking
-    EXPECT_TRUE(x == options.end()); // option 1 not found
-
-    x = options.find(2);
-    EXPECT_TRUE(x == options.end()); // option 2 not found
-
-    x = options.find(32000);
-    EXPECT_TRUE(x == options.end()); // option 32000 not found
-}
-
-
-static uint8_t v4Opts[] = {
-    12,  3, 0,   1,  2,
-    13,  3, 10, 11, 12,
-    14,  3, 20, 21, 22,
-    254, 3, 30, 31, 32,
-    128, 3, 40, 41, 42
-};
-
-TEST(LibDhcpTest, packOptions4) {
-
-    vector<uint8_t> payload[5];
-    for (int i = 0; i < 5; i++) {
-        payload[i].resize(3);
-        payload[i][0] = i*10;
-        payload[i][1] = i*10+1;
-        payload[i][2] = i*10+2;
-    }
-
-    boost::shared_ptr<Option> opt1(new Option(Option::V4, 12, payload[0]));
-    boost::shared_ptr<Option> opt2(new Option(Option::V4, 13, payload[1]));
-    boost::shared_ptr<Option> opt3(new Option(Option::V4, 14, payload[2]));
-    boost::shared_ptr<Option> opt4(new Option(Option::V4,254, payload[3]));
-    boost::shared_ptr<Option> opt5(new Option(Option::V4,128, payload[4]));
-
-    isc::dhcp::Option::OptionCollection opts; // list of options
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt1));
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt2));
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt3));
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt4));
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt5));
-
-    vector<uint8_t> expVect(v4Opts, v4Opts + sizeof(v4Opts));
-
-    OutputBuffer buf(100);
-    EXPECT_NO_THROW (
-        LibDHCP::packOptions(buf, opts);
-    );
-    ASSERT_EQ(buf.getLength(), sizeof(v4Opts));
-    EXPECT_EQ(0, memcmp(v4Opts, buf.getData(), sizeof(v4Opts)));
-
-}
-
-TEST(LibDhcpTest, unpackOptions4) {
-
-    vector<uint8_t> packed(v4Opts, v4Opts + sizeof(v4Opts));
-    isc::dhcp::Option::OptionCollection options; // list of options
-
-    ASSERT_NO_THROW(
-        LibDHCP::unpackOptions4(packed, options);
-    );
-
-    isc::dhcp::Option::OptionCollection::const_iterator x = options.find(12);
-    ASSERT_FALSE(x == options.end()); // option 1 should exist
-    EXPECT_EQ(12, x->second->getType());  // this should be option 12
-    ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
-    EXPECT_EQ(5, x->second->len()); // total option length 5
-    EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+2, 3)); // data len=3
-
-    x = options.find(13);
-    ASSERT_FALSE(x == options.end()); // option 1 should exist
-    EXPECT_EQ(13, x->second->getType());  // this should be option 13
-    ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
-    EXPECT_EQ(5, x->second->len()); // total option length 5
-    EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+7, 3)); // data len=3
-
-    x = options.find(14);
-    ASSERT_FALSE(x == options.end()); // option 3 should exist
-    EXPECT_EQ(14, x->second->getType());  // this should be option 14
-    ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
-    EXPECT_EQ(5, x->second->len()); // total option length 5
-    EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+12, 3)); // data len=3
-
-    x = options.find(254);
-    ASSERT_FALSE(x == options.end()); // option 3 should exist
-    EXPECT_EQ(254, x->second->getType());  // this should be option 254
-    ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
-    EXPECT_EQ(5, x->second->len()); // total option length 5
-    EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+17, 3)); // data len=3
-
-    x = options.find(128);
-    ASSERT_FALSE(x == options.end()); // option 3 should exist
-    EXPECT_EQ(128, x->second->getType());  // this should be option 254
-    ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
-    EXPECT_EQ(5, x->second->len()); // total option length 5
-    EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+22, 3)); // data len=3
-
-    x = options.find(0);
-    EXPECT_TRUE(x == options.end()); // option 0 not found
-
-    x = options.find(1);
-    EXPECT_TRUE(x == options.end()); // option 1 not found
-
-    x = options.find(2);
-    EXPECT_TRUE(x == options.end()); // option 2 not found
-}
-
-}
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index 0e14654..c5965d1 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -506,7 +506,7 @@ TEST(Pkt4Test, unpackOptions) {
 
     vector<uint8_t> expectedFormat = generateTestPacket2();
 
-    for (int i=0; i < sizeof(v4Opts); i++) {
+    for (int i = 0; i < sizeof(v4Opts); i++) {
         expectedFormat.push_back(v4Opts[i]);
     }
 
@@ -563,15 +563,19 @@ TEST(Pkt4Test, unpackOptions) {
 
 // This test verifies methods that are used for manipulating meta fields
 // i.e. fields that are not part of DHCPv4 (e.g. interface name).
-TEST(Pkt4Ttest, metaFields) {
-    Pkt4 pkt(DHCPDISCOVER, 1234);
+TEST(Pkt4Test, metaFields) {
 
-    pkt.setIface("lo0");
+    Pkt4* pkt = new Pkt4(DHCPOFFER, 1234);
+    pkt->setIface("loooopback");
+    pkt->setIndex(42);
+    pkt->setRemoteAddr(IOAddress("1.2.3.4"));
+    pkt->setLocalAddr(IOAddress("4.3.2.1"));
 
-    EXPECT_EQ("lo0", pkt.getIface());
+    EXPECT_EQ("loooopback", pkt->getIface());
+    EXPECT_EQ(42, pkt->getIndex());
+    EXPECT_EQ("1.2.3.4", pkt->getRemoteAddr().toText());
+    EXPECT_EQ("4.3.2.1", pkt->getLocalAddr().toText());
 
-    /// TODO: Expand this test once additonal getters/setters are
-    /// implemented.
 }
 
 } // end of anonymous namespace
diff --git a/src/lib/log/output_option.h b/src/lib/log/output_option.h
index cbb7e95..8dfdd70 100644
--- a/src/lib/log/output_option.h
+++ b/src/lib/log/output_option.h
@@ -60,7 +60,7 @@ struct OutputOption {
 
     /// \brief Constructor
     OutputOption() : destination(DEST_CONSOLE), stream(STR_STDERR),
-                     flush(false), facility("LOCAL0"), filename(""),
+                     flush(true), facility("LOCAL0"), filename(""),
                      maxsize(0), maxver(0)
     {}
 
diff --git a/src/lib/log/tests/output_option_unittest.cc b/src/lib/log/tests/output_option_unittest.cc
index 8f0e0de..ce00bb6 100644
--- a/src/lib/log/tests/output_option_unittest.cc
+++ b/src/lib/log/tests/output_option_unittest.cc
@@ -29,7 +29,7 @@ TEST(OutputOptionTest, Initialization) {
 
     EXPECT_EQ(OutputOption::DEST_CONSOLE, option.destination);
     EXPECT_EQ(OutputOption::STR_STDERR, option.stream);
-    EXPECT_FALSE(option.flush);
+    EXPECT_TRUE(option.flush);
     EXPECT_EQ(string("LOCAL0"), option.facility);
     EXPECT_EQ(string(""), option.filename);
     EXPECT_EQ(0, option.maxsize);
diff --git a/src/lib/resolve/recursive_query.cc b/src/lib/resolve/recursive_query.cc
index 5446974..c6ef331 100644
--- a/src/lib/resolve/recursive_query.cc
+++ b/src/lib/resolve/recursive_query.cc
@@ -633,12 +633,15 @@ private:
 
             case ResponseClassifier::NOTSINGLE:
                 message_id = RESLIB_NOTSINGLE_RESPONSE;
+                break;
 
             case ResponseClassifier::OPCODE:
                 message_id = RESLIB_OPCODE_RESPONSE;
+                break;
 
             default:
                 message_id = RESLIB_ERROR_RESPONSE;
+                break;
             }
             LOG_DEBUG(logger, RESLIB_DBG_RESULTS, message_id).
                       arg(questionText(question_));
diff --git a/src/lib/statistics/Makefile.am b/src/lib/statistics/Makefile.am
new file mode 100644
index 0000000..6c7b910
--- /dev/null
+++ b/src/lib/statistics/Makefile.am
@@ -0,0 +1,24 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/statistics -I$(top_builddir)/src/lib/statistics
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+if USE_CLANGPP
+# clang++ complains about unused function parameters in some boost header
+# files.
+AM_CXXFLAGS += -Wno-unused-parameter
+endif
+
+lib_LTLIBRARIES = libstatistics.la
+libstatistics_la_SOURCES  = counter.h counter.cc
+libstatistics_la_SOURCES  += counter_dict.h counter_dict.cc
+
+CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/statistics/counter.cc b/src/lib/statistics/counter.cc
new file mode 100644
index 0000000..9cb1a6f
--- /dev/null
+++ b/src/lib/statistics/counter.cc
@@ -0,0 +1,68 @@
+#include <vector>
+
+#include <boost/noncopyable.hpp>
+
+#include <statistics/counter.h>
+
+namespace {
+const unsigned int InitialValue = 0;
+} // namespace
+
+namespace isc {
+namespace statistics {
+
+class CounterImpl : boost::noncopyable {
+    private:
+        std::vector<Counter::Value> counters_;
+    public:
+        CounterImpl(const size_t nelements);
+        ~CounterImpl();
+        void inc(const Counter::Type&);
+        const Counter::Value& get(const Counter::Type&) const;
+};
+
+CounterImpl::CounterImpl(const size_t items) :
+    counters_(items, InitialValue)
+{
+    if (items == 0) {
+        isc_throw(isc::InvalidParameter, "Items must not be 0");
+    }
+}
+
+CounterImpl::~CounterImpl() {}
+
+void
+CounterImpl::inc(const Counter::Type& type) {
+    if(type >= counters_.size()) {
+        isc_throw(isc::OutOfRange, "Counter type is out of range");
+    }
+    ++counters_.at(type);
+    return;
+}
+
+const Counter::Value&
+CounterImpl::get(const Counter::Type& type) const {
+    if(type >= counters_.size()) {
+        isc_throw(isc::OutOfRange, "Counter type is out of range");
+    }
+    return (counters_.at(type));
+}
+
+Counter::Counter(const size_t items) : impl_(new CounterImpl(items))
+{}
+
+Counter::~Counter() {}
+
+void
+Counter::inc(const Type& type) {
+    impl_->inc(type);
+    return;
+}
+
+const Counter::Value&
+Counter::get(const Type& type) const {
+    return (impl_->get(type));
+}
+
+}   // namespace statistics
+}   // namespace isc
diff --git a/src/lib/statistics/counter.h b/src/lib/statistics/counter.h
new file mode 100644
index 0000000..b077616
--- /dev/null
+++ b/src/lib/statistics/counter.h
@@ -0,0 +1,55 @@
+#ifndef __COUNTER_H
+#define __COUNTER_H 1
+
+#include <boost/noncopyable.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <exceptions/exceptions.h>
+
+namespace isc {
+namespace statistics {
+
+// forward declaration for pImpl idiom
+class CounterImpl;
+
+class Counter : boost::noncopyable {
+private:
+    boost::scoped_ptr<CounterImpl> impl_;
+public:
+    typedef unsigned int Type;
+    typedef unsigned int Value;
+
+    /// The constructor.
+    ///
+    /// This constructor is mostly exception free. But it may still throw
+    /// a standard exception if memory allocation fails inside the method.
+    ///
+    /// \param items A number of counter items to hold (greater than 0)
+    ///
+    /// \throw isc::InvalidParameter \a items is 0
+    Counter(const size_t items);
+
+    /// The destructor.
+    ///
+    /// This method never throws an exception.
+    ~Counter();
+
+    /// \brief Increment a counter item specified with \a type.
+    ///
+    /// \param type %Counter item to increment
+    ///
+    /// \throw isc::OutOfRange \a type is invalid
+    void inc(const Type& type);
+
+    /// \brief Get the value of a counter item specified with \a type.
+    ///
+    /// \param type %Counter item to get the value of
+    ///
+    /// \throw isc::OutOfRange \a type is invalid
+    const Value& get(const Type& type) const;
+};
+
+}   // namespace statistics
+}   // namespace isc
+
+#endif
diff --git a/src/lib/statistics/counter_dict.cc b/src/lib/statistics/counter_dict.cc
new file mode 100644
index 0000000..da6aace
--- /dev/null
+++ b/src/lib/statistics/counter_dict.cc
@@ -0,0 +1,251 @@
+#include <cassert>
+#include <stdexcept>
+#include <iterator>
+#include <map>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <statistics/counter_dict.h>
+
+namespace {
+typedef boost::shared_ptr<isc::statistics::Counter> CounterPtr;
+typedef std::map<std::string, CounterPtr> DictionaryMap;
+}
+
+namespace isc {
+namespace statistics {
+
+// Implementation detail class for CounterDictionary::ConstIterator
+class CounterDictionaryConstIteratorImpl;
+
+class CounterDictionaryImpl : boost::noncopyable {
+private:
+    DictionaryMap dictionary_;
+    std::vector<std::string> elements_;
+    const size_t items_;
+    // Default constructor is forbidden; number of counter items must be
+    // specified at the construction of this class.
+    CounterDictionaryImpl();
+public:
+    CounterDictionaryImpl(const size_t items);
+    ~CounterDictionaryImpl();
+    void addElement(const std::string& name);
+    void deleteElement(const std::string& name);
+    Counter& getElement(const std::string& name);
+public:
+    CounterDictionaryConstIteratorImpl begin() const;
+    CounterDictionaryConstIteratorImpl end() const;
+};
+
+// Constructor with number of items
+CounterDictionaryImpl::CounterDictionaryImpl(const size_t items) :
+    items_(items)
+{
+    // The number of items must not be 0
+    if (items == 0) {
+        isc_throw(isc::InvalidParameter, "Items must not be 0");
+    }
+}
+
+// Destructor
+CounterDictionaryImpl::~CounterDictionaryImpl() {}
+
+void
+CounterDictionaryImpl::addElement(const std::string& name) {
+    // throw if the element already exists
+    if (dictionary_.count(name) != 0) {
+        isc_throw(isc::InvalidParameter,
+                  "Element " << name << " already exists");
+    }
+    assert(items_ != 0);
+    // Create a new Counter and add to the map
+    dictionary_.insert(
+        DictionaryMap::value_type(name, CounterPtr(new Counter(items_))));
+}
+
+void
+CounterDictionaryImpl::deleteElement(const std::string& name) {
+    size_t result = dictionary_.erase(name);
+    if (result != 1) {
+        // If an element with specified name does not exist, throw
+        // isc::OutOfRange.
+        isc_throw(isc::OutOfRange, "Element " << name << " does not exist");
+    }
+}
+
+Counter&
+CounterDictionaryImpl::getElement(const std::string& name) {
+    DictionaryMap::const_iterator i = dictionary_.find(name);
+    if (i != dictionary_.end()) {
+        // the key was found. return the element.
+        return (*(i->second));
+    } else {
+        // If an element with specified name does not exist, throw
+        // isc::OutOfRange.
+        isc_throw(isc::OutOfRange, "Element " << name << " does not exist");
+    }
+}
+
+// Constructor
+// Initialize impl_
+CounterDictionary::CounterDictionary(const size_t items) :
+    impl_(new CounterDictionaryImpl(items))
+{}
+
+// Destructor
+// impl_ will be freed automatically with scoped_ptr
+CounterDictionary::~CounterDictionary() {}
+
+void
+CounterDictionary::addElement(const std::string& name) {
+    impl_->addElement(name);
+}
+
+void
+CounterDictionary::deleteElement(const std::string& name) {
+    impl_->deleteElement(name);
+}
+
+Counter&
+CounterDictionary::getElement(const std::string& name) const {
+    return (impl_->getElement(name));
+}
+
+Counter&
+CounterDictionary::operator[](const std::string& name) const {
+    return (impl_->getElement(name));
+}
+
+// Implementation detail class for CounterDictionary::ConstIterator
+class CounterDictionaryConstIteratorImpl {
+    public:
+        CounterDictionaryConstIteratorImpl();
+        ~CounterDictionaryConstIteratorImpl();
+        CounterDictionaryConstIteratorImpl(
+            const CounterDictionaryConstIteratorImpl &other);
+        CounterDictionaryConstIteratorImpl &operator=(
+            const CounterDictionaryConstIteratorImpl &source);
+        CounterDictionaryConstIteratorImpl(
+            DictionaryMap::const_iterator iterator);
+    public:
+        void increment();
+        const CounterDictionary::ConstIterator::value_type&
+            dereference() const;
+        bool equal(const CounterDictionaryConstIteratorImpl& other) const;
+    private:
+        DictionaryMap::const_iterator iterator_;
+};
+
+CounterDictionaryConstIteratorImpl::CounterDictionaryConstIteratorImpl() {}
+
+CounterDictionaryConstIteratorImpl::~CounterDictionaryConstIteratorImpl() {}
+
+// Copy constructor: deep copy of iterator_
+CounterDictionaryConstIteratorImpl::CounterDictionaryConstIteratorImpl(
+    const CounterDictionaryConstIteratorImpl &other) :
+    iterator_(other.iterator_)
+{}
+
+// Assignment operator: deep copy of iterator_
+CounterDictionaryConstIteratorImpl &
+CounterDictionaryConstIteratorImpl::operator=(
+    const CounterDictionaryConstIteratorImpl &source)
+{
+    iterator_ = source.iterator_;
+    return (*this);
+}
+
+// Constructor from implementation detail DictionaryMap::const_iterator
+CounterDictionaryConstIteratorImpl::CounterDictionaryConstIteratorImpl(
+    DictionaryMap::const_iterator iterator) :
+    iterator_(iterator)
+{}
+
+CounterDictionaryConstIteratorImpl
+CounterDictionaryImpl::begin() const {
+    return (CounterDictionaryConstIteratorImpl(dictionary_.begin()));
+}
+
+CounterDictionaryConstIteratorImpl
+CounterDictionaryImpl::end() const {
+    return (CounterDictionaryConstIteratorImpl(dictionary_.end()));
+}
+
+void
+CounterDictionaryConstIteratorImpl::increment() {
+    ++iterator_;
+    return;
+}
+
+const CounterDictionary::ConstIterator::value_type&
+CounterDictionaryConstIteratorImpl::dereference() const {
+    return (iterator_->first);
+}
+
+bool
+CounterDictionaryConstIteratorImpl::equal(
+    const CounterDictionaryConstIteratorImpl& other) const
+{
+    return (iterator_ == other.iterator_);
+}
+
+CounterDictionary::ConstIterator
+CounterDictionary::begin() const {
+    return (CounterDictionary::ConstIterator(
+               CounterDictionaryConstIteratorImpl(impl_->begin())));
+}
+
+CounterDictionary::ConstIterator
+CounterDictionary::end() const {
+    return (CounterDictionary::ConstIterator(
+               CounterDictionaryConstIteratorImpl(impl_->end())));
+}
+
+CounterDictionary::ConstIterator::ConstIterator() :
+    impl_(new CounterDictionaryConstIteratorImpl())
+{}
+
+CounterDictionary::ConstIterator::~ConstIterator() {}
+
+// Copy constructor: deep copy of impl_
+CounterDictionary::ConstIterator::ConstIterator(
+    const CounterDictionary::ConstIterator& source) :
+    impl_(new CounterDictionaryConstIteratorImpl(*(source.impl_)))
+{}
+
+// Assignment operator: deep copy of impl_
+CounterDictionary::ConstIterator &
+CounterDictionary::ConstIterator::operator=(
+    const CounterDictionary::ConstIterator &source)
+{
+    *impl_ = *source.impl_;
+    return (*this);
+}
+
+// The constructor from implementation detail
+CounterDictionary::ConstIterator::ConstIterator(
+    const CounterDictionaryConstIteratorImpl& source) :
+    impl_(new CounterDictionaryConstIteratorImpl(source))
+{}
+
+const CounterDictionary::ConstIterator::value_type&
+CounterDictionary::ConstIterator::dereference() const
+{
+    return (impl_->dereference());
+}
+
+bool
+CounterDictionary::ConstIterator::equal(
+    CounterDictionary::ConstIterator const& other) const
+{
+    return (impl_->equal(*(other.impl_)));
+}
+
+void
+CounterDictionary::ConstIterator::increment() {
+    impl_->increment();
+    return;
+}
+
+}   // namespace statistics
+}   // namespace isc
diff --git a/src/lib/statistics/counter_dict.h b/src/lib/statistics/counter_dict.h
new file mode 100644
index 0000000..4a4cab1
--- /dev/null
+++ b/src/lib/statistics/counter_dict.h
@@ -0,0 +1,145 @@
+#ifndef __COUNTER_DICT_H
+#define __COUNTER_DICT_H 1
+
+#include <string>
+#include <vector>
+#include <utility>
+#include <boost/noncopyable.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/iterator/iterator_facade.hpp>
+
+#include <exceptions/exceptions.h>
+#include <statistics/counter.h>
+
+namespace isc {
+namespace statistics {
+
+class CounterDictionaryImpl;
+class CounterDictionaryConstIteratorImpl;
+
+class CounterDictionary : boost::noncopyable {
+private:
+    boost::scoped_ptr<CounterDictionaryImpl> impl_;
+    // Default constructor is forbidden; number of counter items must be
+    // specified at the construction of this class.
+    CounterDictionary();
+public:
+    /// The constructor.
+    /// This constructor is mostly exception free. But it may still throw
+    /// a standard exception if memory allocation fails inside the method.
+    ///
+    /// \param items A number of counter items to hold (greater than 0)
+    ///
+    /// \throw isc::InvalidParameter \a items is 0
+    CounterDictionary(const size_t items);
+
+    /// The destructor.
+    ///
+    /// This method never throws an exception.
+    ~CounterDictionary();
+
+    /// \brief Add an element
+    ///
+    /// \throw isc::InvalidParameter \a element already exists.
+    ///
+    /// \param name A name of the element to append
+    void addElement(const std::string& name);
+
+    /// \brief Delete
+    ///
+    /// \throw isc::OutOfRange \a element does not exist.
+    ///
+    /// \param name A name of the element to delete
+    void deleteElement(const std::string& name);
+
+    /// \brief Lookup
+    ///
+    /// \throw isc::OutOfRange \a element does not exist.
+    ///
+    /// \param name A name of the element to get the counters
+    Counter& getElement(const std::string &name) const;
+
+    /// Same as getElement()
+    Counter& operator[](const std::string &name) const;
+
+    /// \brief \c ConstIterator is a constant iterator that provides an
+    /// interface for enumerating name of zones stored in CounterDictionary.
+    ///
+    /// This class is derived from boost::iterator_facade and uses pImpl
+    /// idiom not to expose implementation detail of
+    /// CounterDictionary::iterator.
+    ///
+    /// It is intended to walk through the elements when sending the
+    /// counters to statistics module.
+    class ConstIterator :
+        public boost::iterator_facade<ConstIterator,
+                                const std::string,
+                                boost::forward_traversal_tag>
+    {
+        private:
+            boost::scoped_ptr<CounterDictionaryConstIteratorImpl> impl_;
+        public:
+            /// The constructor.
+            ///
+            /// This constructor is mostly exception free. But it may still
+            /// throw a standard exception if memory allocation fails
+            /// inside the method.
+            ConstIterator();
+            /// The destructor.
+            ///
+            /// This method never throws an exception.
+            ~ConstIterator();
+            /// The assignment operator.
+            ///
+            /// This method is mostly exception free. But it may still
+            /// throw a standard exception if memory allocation fails
+            /// inside the method.
+            ConstIterator& operator=(const ConstIterator &source);
+            /// The copy constructor.
+            ///
+            /// This constructor is mostly exception free. But it may still
+            /// throw a standard exception if memory allocation fails
+            /// inside the method.
+            ConstIterator(const ConstIterator& source);
+            /// The constructor from implementation detail.
+            ///
+            /// This method is used to create an instance of ConstIterator
+            /// by CounterDict::begin() and CounterDict::end().
+            ///
+            /// This constructor is mostly exception free. But it may still
+            /// throw a standard exception if memory allocation fails
+            /// inside the method.
+            ConstIterator(
+                const CounterDictionaryConstIteratorImpl& source);
+        private:
+            /// \brief An internal method to increment this iterator.
+            void increment();
+            /// \brief An internal method to check equality.
+            bool equal(const ConstIterator& other) const;
+            /// \brief An internal method to dereference this iterator.
+            const value_type& dereference() const;
+        private:
+            friend class boost::iterator_core_access;
+    };
+
+    typedef ConstIterator const_iterator;
+
+    /// \brief Return an iterator corresponding to the beginning of the
+    /// elements stored in CounterDictionary.
+    ///
+    /// This method is mostly exception free. But it may still throw a
+    /// standard exception if memory allocation fails inside the method.
+    const_iterator begin() const;
+
+    /// \brief Return an iterator corresponding to the end of the elements
+    /// stored in CounterDictionary.
+    ///
+    /// This method is mostly exception free. But it may still throw a
+    /// standard exception if memory allocation fails inside the method.
+    const_iterator end() const;
+};
+
+}   // namespace statistics
+}   // namespace isc
+
+#endif
diff --git a/src/lib/statistics/tests/Makefile.am b/src/lib/statistics/tests/Makefile.am
new file mode 100644
index 0000000..d66acdf
--- /dev/null
+++ b/src/lib/statistics/tests/Makefile.am
@@ -0,0 +1,47 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES  = run_unittests.cc
+run_unittests_SOURCES += counter_unittest.cc
+run_unittests_SOURCES += counter_dict_unittest.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+
+run_unittests_LDADD  = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
+# B10_CXXFLAGS)
+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/statistics/tests/counter_dict_unittest.cc b/src/lib/statistics/tests/counter_dict_unittest.cc
new file mode 100644
index 0000000..2578b46
--- /dev/null
+++ b/src/lib/statistics/tests/counter_dict_unittest.cc
@@ -0,0 +1,174 @@
+// Copyright (C) 2011  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 <gtest/gtest.h>
+
+#include <set>
+
+#include <boost/foreach.hpp>
+
+#include <statistics/counter_dict.h>
+
+enum CounterItems {
+    ITEM1 = 0,
+    ITEM2 = 1,
+    ITEM3 = 2,
+    NUMBER_OF_ITEMS = 3
+};
+
+using namespace isc::statistics;
+
+TEST(CounterDictionaryCreateTest, invalidCounterSize) {
+    // Creating counter with 0 elements will cause an isc::InvalidParameter
+    // exception
+    EXPECT_THROW(CounterDictionary counters(0), isc::InvalidParameter);
+}
+
+// This fixture is for testing CounterDictionary.
+class CounterDictionaryTest : public ::testing::Test {
+protected:
+    CounterDictionaryTest() : counters(NUMBER_OF_ITEMS) {
+        counters.addElement("test");
+        counters.addElement("sub.test");
+    }
+    ~CounterDictionaryTest() {}
+
+    CounterDictionary counters;
+};
+
+TEST_F(CounterDictionaryTest, initializeCheck) {
+    // Check if the all counters are initialized with 0
+    EXPECT_EQ(counters["test"].get(ITEM1), 0);
+    EXPECT_EQ(counters["test"].get(ITEM2), 0);
+    EXPECT_EQ(counters["test"].get(ITEM3), 0);
+}
+
+TEST_F(CounterDictionaryTest, getElement) {
+    // Another member function to get counters for the element
+    EXPECT_EQ(counters.getElement("test").get(ITEM1), 0);
+    EXPECT_EQ(counters.getElement("test").get(ITEM2), 0);
+    EXPECT_EQ(counters.getElement("test").get(ITEM3), 0);
+}
+
+TEST_F(CounterDictionaryTest, incrementCounterItem) {
+    // Increment counters
+    counters["test"].inc(ITEM1);
+    counters["test"].inc(ITEM2);
+    counters["test"].inc(ITEM2);
+    counters["test"].inc(ITEM3);
+    counters["test"].inc(ITEM3);
+    counters["test"].inc(ITEM3);
+    // Check if the counters have expected values
+    EXPECT_EQ(counters["test"].get(ITEM1), 1);
+    EXPECT_EQ(counters["test"].get(ITEM2), 2);
+    EXPECT_EQ(counters["test"].get(ITEM3), 3);
+    EXPECT_EQ(counters["sub.test"].get(ITEM1), 0);
+    EXPECT_EQ(counters["sub.test"].get(ITEM2), 0);
+    EXPECT_EQ(counters["sub.test"].get(ITEM3), 0);
+}
+
+TEST_F(CounterDictionaryTest, deleteElement) {
+    // Ensure the element is accessible
+    EXPECT_EQ(counters["test"].get(ITEM1), 0);
+    EXPECT_EQ(counters["test"].get(ITEM2), 0);
+    EXPECT_EQ(counters["test"].get(ITEM3), 0);
+    // Delete the element
+    counters.deleteElement("test");
+    // Accessing to the deleted element will cause an isc::OutOfRange exception
+    EXPECT_THROW(counters["test"].get(ITEM1), isc::OutOfRange);
+    // Deleting an element which does not exist will cause an isc::OutOfRange
+    //  exception
+    EXPECT_THROW(counters.deleteElement("test"), isc::OutOfRange);
+}
+
+TEST_F(CounterDictionaryTest, invalidCounterItem) {
+    // Incrementing out-of-bound counter will cause an isc::OutOfRange
+    // exception
+    EXPECT_THROW(counters["test"].inc(NUMBER_OF_ITEMS), isc::OutOfRange);
+}
+
+TEST_F(CounterDictionaryTest, uniquenessCheck) {
+    // Adding an element which already exists will cause an isc::OutOfRange
+    //  exception
+    EXPECT_THROW(counters.addElement("test"), isc::InvalidParameter);
+}
+
+TEST_F(CounterDictionaryTest, iteratorTest) {
+    // Increment counters
+    counters["test"].inc(ITEM1);
+    counters["sub.test"].inc(ITEM2);
+    counters["sub.test"].inc(ITEM2);
+
+    // boolean values to check all of the elements can be accessed through
+    // the iterator
+    bool element_test_visited = false;
+    bool element_sub_test_visited = false;
+    // Walk through the elements with iterator
+    // Check if the elements "test" and "sub.test" appears only once
+    //  and the counters have expected value
+    for (CounterDictionary::ConstIterator i = counters.begin(),
+                                          e = counters.end();
+         i != e;
+         ++i
+         )
+    {
+        const std::string& zone = *i;
+        if (zone == "test" && element_test_visited == false) {
+            element_test_visited = true;
+            // Check if the counters have expected value
+            EXPECT_EQ(counters[zone].get(ITEM1), 1);
+            EXPECT_EQ(counters[zone].get(ITEM2), 0);
+        } else if (zone == "sub.test" &&
+                   element_sub_test_visited == false) {
+            element_sub_test_visited = true;
+            // Check if the counters have expected value
+            EXPECT_EQ(counters[zone].get(ITEM1), 0);
+            EXPECT_EQ(counters[zone].get(ITEM2), 2);
+        } else {
+            // Test fails when reaches here: the element is not expected or
+            //  the element appeared twice
+            FAIL() << "Unexpected iterator value";
+        }
+    }
+    // Check if the "test" and "sub.test" is accessible
+    EXPECT_TRUE(element_test_visited);
+    EXPECT_TRUE(element_sub_test_visited);
+}
+
+TEST_F(CounterDictionaryTest, iteratorCopyTest) {
+    // Increment counters
+    counters["test"].inc(ITEM1);
+    counters["sub.test"].inc(ITEM2);
+    counters["sub.test"].inc(ITEM2);
+
+    CounterDictionary::ConstIterator i1 = counters.begin();
+    CounterDictionary::ConstIterator i2(i1);
+    CounterDictionary::ConstIterator i3;
+    i3 = i1;
+
+    EXPECT_TRUE(i1 == i2);
+    EXPECT_TRUE(i1 == i3);
+    EXPECT_TRUE(i2 == i3);
+
+    ++i2;
+    EXPECT_TRUE(i1 != i2);
+    EXPECT_TRUE(i1 == i3);
+    EXPECT_TRUE(i2 != i3);
+
+    ++i3;
+    EXPECT_TRUE(i1 != i2);
+    EXPECT_TRUE(i1 != i3);
+    EXPECT_TRUE(i2 == i3);
+}
diff --git a/src/lib/statistics/tests/counter_unittest.cc b/src/lib/statistics/tests/counter_unittest.cc
new file mode 100644
index 0000000..e0d29ac
--- /dev/null
+++ b/src/lib/statistics/tests/counter_unittest.cc
@@ -0,0 +1,85 @@
+// Copyright (C) 2011  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 <gtest/gtest.h>
+
+#include <statistics/counter.h>
+
+namespace {
+enum CounterItems {
+    ITEM1 = 0,
+    ITEM2 = 1,
+    ITEM3 = 2,
+    NUMBER_OF_ITEMS = 3
+};
+}
+
+using namespace isc::statistics;
+
+TEST(CounterCreateTest, invalidCounterSize) {
+    // Creating counter with 0 elements will cause an isc::InvalidParameter
+    // exception
+    EXPECT_THROW(Counter counter(0), isc::InvalidParameter);
+}
+
+// This fixture is for testing Counter.
+class CounterTest : public ::testing::Test {
+protected:
+    CounterTest() : counter(NUMBER_OF_ITEMS) {}
+    ~CounterTest() {}
+
+    Counter counter;
+};
+
+TEST_F(CounterTest, createCounter) {
+    // Check if the all counters are initialized with 0
+    EXPECT_EQ(counter.get(ITEM1), 0);
+    EXPECT_EQ(counter.get(ITEM2), 0);
+    EXPECT_EQ(counter.get(ITEM3), 0);
+}
+
+TEST_F(CounterTest, incrementCounterItem) {
+    // Increment counters
+    counter.inc(ITEM1);
+    counter.inc(ITEM2);
+    counter.inc(ITEM2);
+    counter.inc(ITEM3);
+    counter.inc(ITEM3);
+    counter.inc(ITEM3);
+    // Check if the counters have expected values
+    EXPECT_EQ(counter.get(ITEM1), 1);
+    EXPECT_EQ(counter.get(ITEM2), 2);
+    EXPECT_EQ(counter.get(ITEM3), 3);
+    // Increment counters once more
+    counter.inc(ITEM1);
+    counter.inc(ITEM2);
+    counter.inc(ITEM2);
+    counter.inc(ITEM3);
+    counter.inc(ITEM3);
+    counter.inc(ITEM3);
+    // Check if the counters have expected values
+    EXPECT_EQ(counter.get(ITEM1), 2);
+    EXPECT_EQ(counter.get(ITEM2), 4);
+    EXPECT_EQ(counter.get(ITEM3), 6);
+}
+
+TEST_F(CounterTest, invalidCounterItem) {
+    // Incrementing out-of-bound counter will cause an isc::OutOfRange
+    // exception
+    EXPECT_THROW(counter.inc(NUMBER_OF_ITEMS), isc::OutOfRange);
+    // Trying to get out-of-bound counter will cause an isc::OutOfRange
+    // exception
+    EXPECT_THROW(counter.get(NUMBER_OF_ITEMS), isc::OutOfRange);
+}
diff --git a/src/lib/statistics/tests/run_unittests.cc b/src/lib/statistics/tests/run_unittests.cc
new file mode 100644
index 0000000..38a299e
--- /dev/null
+++ b/src/lib/statistics/tests/run_unittests.cc
@@ -0,0 +1,25 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+#include <log/logger_support.h>
+
+int
+main(int argc, char* argv[])
+{
+    ::testing::InitGoogleTest(&argc, argv);         // Initialize Google test
+    isc::log::initLogger();
+    return (isc::util::unittests::run_all());
+}
diff --git a/tests/lettuce/README.tutorial b/tests/lettuce/README.tutorial
index 18c94cf..c7d3cd7 100644
--- a/tests/lettuce/README.tutorial
+++ b/tests/lettuce/README.tutorial
@@ -50,7 +50,7 @@ will need to expand these, but we will look at them shortly.
 This file defines a feature, just under the feature name we can
 provide a description of the feature.
 
-The one scenario we have no has no steps, so if we run it we should
+The one scenario we have has no steps, so if we run it we should
 see something like:
 
 -- output
@@ -84,7 +84,7 @@ So let's add a step that starts bind10.
         When I start bind10 with configuration example.org.config
 --
 
-This is not good enough; it will fire of the process, but setting up
+This is not good enough; it will start the process, but setting up
 b10-auth may take a few moments, so we need to add a step to wait for
 it to be started before we continue.
 
@@ -121,7 +121,7 @@ regular expression itself), so if the step is defined with "do foo bar", the
 scenario can add words for readability "When I do foo bar".
 
 Each captured group will be passed as an argument to the function we define.
-For bind10, i defined a configuration file, a cmdctl port, and a process
+For bind10, I defined a configuration file, a cmdctl port, and a process
 name. The first two should be self-evident, and the process name is an
 optional name we give it, should we want to address it in the rest of the
 tests. This is most useful if we want to start multiple instances. In the
diff --git a/tests/lettuce/configurations/ixfr-out/testset1-config.db b/tests/lettuce/configurations/ixfr-out/testset1-config.db
new file mode 100644
index 0000000..c5fc165
--- /dev/null
+++ b/tests/lettuce/configurations/ixfr-out/testset1-config.db
@@ -0,0 +1 @@
+{"Xfrin": {"zones": [{"use_ixfr": true, "class": "IN", "name": "example.com.", "master_addr": "178.18.82.80"}]}, "version": 2, "Logging": {"loggers": [{"debuglevel": 99, "severity": "DEBUG", "output_options": [{"output": "stderr", "flush": true}], "name": "*"}]}, "Auth": {"database_file": "data/ixfr-out/zones.slite3", "listen_on": [{"port": 47806, "address": "::"}, {"port": 47806, "address": "0.0.0.0"}]}}
diff --git a/tests/lettuce/data/ixfr-out/zones.slite3 b/tests/lettuce/data/ixfr-out/zones.slite3
new file mode 100644
index 0000000..a2b2dbd
Binary files /dev/null and b/tests/lettuce/data/ixfr-out/zones.slite3 differ
diff --git a/tests/lettuce/features/ixfr_out_bind10.feature b/tests/lettuce/features/ixfr_out_bind10.feature
new file mode 100644
index 0000000..e84ad8c
--- /dev/null
+++ b/tests/lettuce/features/ixfr_out_bind10.feature
@@ -0,0 +1,195 @@
+Feature: IXFR out
+    Tests for IXFR-out, specific for BIND 10 behaviour.
+    These are (part of) the tests as described on
+    http://bind10.isc.org/wiki/IxfrSystemTests
+
+    # A lot of these tests test specific UDP behaviour.
+    #
+    # Where possible, we use the TCP equivalent. Some of the behaviour
+    # tested is UDP-specific though. In either case, a comment above
+    # the test shows how and why it differs from the test specification,
+    # or why it is commented out for now.
+    # When we do implement UDP IXFR, we should probably keep the TCP
+    # tests, and add them to the test specification, so we still have a
+    # 1-to-1 mapping between these tests and the specification document.
+    #
+    # These tests use a zone with just a few records, the first serial
+    # is 2, and it is incremented in steps of 2, up to serial 22.
+    # Each updates either deletes or adds the www.example.com A record.
+    # Version 2 has the record, then the update to version 4 deletes it,
+    # the update to 6 adds it again, and so on, until version 22 (where
+    # the last update has added it again)
+    #
+    # Some of the tests (scenario 1 tests 3 and 4, and scenario 2 tests 1 and
+    # 2 may still not work if we replicate BIND 9's behaviour; it always
+    # responds to UDP IXFR requests with just the SOA, and it does not do
+    # AXFR-style IXFR if the number of changes exceeds the size of the zone)
+    #
+    # So in effect, there is only one test that is currently active (scenario
+    # 1 test 7)
+
+
+    Scenario: Test Set 1
+        Given I have bind10 running with configuration ixfr-out/testset1-config.db
+        Then wait for bind10 xfrout to start
+        The SOA serial for example.com should be 22
+
+        #
+        # Test 1
+        #
+        # We don't support UDP yet, and for TCP we currently return full zone,
+        # so this test is currently skipped
+        #
+        #When I do an IXFR transfer of example.com 123 over udp
+        #The transfer result should have 1 RRs
+        #The full result of the last transfer should be
+        #"""
+        #example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        #"""
+
+        #
+        # Test 2
+        #
+        # Original test specification was for UDP, using TCP for now
+        #
+        #When I do an IXFR transfer of example.com 22 over udp
+        When I do an IXFR transfer of example.com 22 over tcp
+        The transfer result should have 1 RRs
+        The full result of the last transfer should be
+        """
+        example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        """
+
+        #
+        # Test 3
+        #
+        # Original test specification was for UDP, using TCP for now
+        #
+        #When I do an IXFR transfer of example.com 20 over udp
+        When I do an IXFR transfer of example.com 20 over tcp
+        The transfer result should have 5 RRs
+        The full result of the last transfer should be
+        """
+        example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        example.com. 3600 IN SOA ns.example.com. admin.example.com. 20 28800 7200 604800 18000
+        example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        www.example.com. 3600 IN A 192.0.2.1
+        example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        """
+
+        #
+        # Test 4
+        #
+        # Original test specification was for UDP, using TCP for now
+        #
+        #When I do an IXFR transfer of example.com 18 over udp
+        When I do an IXFR transfer of example.com 18 over tcp
+        The transfer result should have 8 RRs
+        The full result of the last transfer should be
+        """
+        example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        example.com. 3600 IN SOA ns.example.com. admin.example.com. 18 28800 7200 604800 18000
+        www.example.com. 3600 IN A 192.0.2.1
+        example.com. 3600 IN SOA ns.example.com. admin.example.com. 20 28800 7200 604800 18000
+        example.com. 3600 IN SOA ns.example.com. admin.example.com. 20 28800 7200 604800 18000
+        example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        www.example.com. 3600 IN A 192.0.2.1
+        example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        """
+
+        #
+        # Test 5
+        #
+        # This test does not have a TCP equivalent, so it is skipped.
+        #
+        #When I do an IXFR transfer of example.com 2 over udp
+        #The transfer result should have 1 RRs
+        #The full result of the last transfer should be
+        #"""
+        #example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        #"""
+
+        #
+        # Test 6
+        #
+        # This test does not have a TCP equivalent, so it is skipped.
+        #
+        #When I do an IXFR transfer of example.com 5 over udp
+        #The transfer result should have 1 RRs
+        #The full result of the last transfer should be
+        #"""
+        #example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        #"""
+
+        #
+        # Test 7
+        #
+        When I do an IXFR transfer of example.com 14 over tcp
+        The transfer result should have 14 RRs
+        The full result of the last transfer should be
+        """
+        example.com.            3600    IN      SOA     ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        example.com.            3600    IN      SOA     ns.example.com. admin.example.com. 14 28800 7200 604800 18000
+        www.example.com.        3600    IN      A       192.0.2.1
+        example.com.            3600    IN      SOA     ns.example.com. admin.example.com. 16 28800 7200 604800 18000
+        example.com.            3600    IN      SOA     ns.example.com. admin.example.com. 16 28800 7200 604800 18000
+        example.com.            3600    IN      SOA     ns.example.com. admin.example.com. 18 28800 7200 604800 18000
+        www.example.com.        3600    IN      A       192.0.2.1
+        example.com.            3600    IN      SOA     ns.example.com. admin.example.com. 18 28800 7200 604800 18000
+        www.example.com.        3600    IN      A       192.0.2.1
+        example.com.            3600    IN      SOA     ns.example.com. admin.example.com. 20 28800 7200 604800 18000
+        example.com.            3600    IN      SOA     ns.example.com. admin.example.com. 20 28800 7200 604800 18000
+        example.com.            3600    IN      SOA     ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        www.example.com.        3600    IN      A       192.0.2.1
+        example.com.            3600    IN      SOA     ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        """
+
+    Scenario: Test Set 2
+        Given I have bind10 running with configuration ixfr-out/testset1-config.db
+        Then wait for bind10 xfrout to start
+        The SOA serial for example.com should be 22
+
+        #
+        # Test 1
+        #
+        # Original test specification was for UDP, using TCP for now
+        #
+        #When I do an IXFR transfer of example.com 19 over udp
+        When I do an IXFR transfer of example.com 19 over tcp
+        The transfer result should have 5 RRs
+        The full result of the last transfer should be
+        """
+        example.com.            3600    IN      SOA     ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        example.com.            3600    IN      NS      ns.example.com.
+        ns.example.com.         3600    IN      A       192.0.2.1
+        www.example.com.        3600    IN      A       192.0.2.1
+        example.com.            3600    IN      SOA     ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        """
+
+        #
+        # Test 2
+        #
+        # This test has no TCP equivalent
+        #
+        #When I do an IXFR transfer of example.com 6 over udp
+        #The transfer result should have 5 RRs
+        #The full result of the last transfer should be
+        #"""
+        #example.com.            3600    IN      SOA     ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        #example.com.            3600    IN      NS      ns.example.com.
+        #ns.example.com.         3600    IN      A       192.0.2.1
+        #www.example.com.        3600    IN      A       192.0.2.1
+        #example.com.            3600    IN      SOA     ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        #"""
+
+        #
+        # Test 3
+        #
+        # This test has no TCP equivalent
+        #
+        #When I do an IXFR transfer of example.com 2 over udp
+        #The transfer result should have 1 RRs
+        #The full result of the last transfer should be
+        #"""
+        #example.com.            3600    IN      SOA     ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+        #"""
diff --git a/tests/lettuce/features/terrain/bind10_control.py b/tests/lettuce/features/terrain/bind10_control.py
index 5248316..fdc419b 100644
--- a/tests/lettuce/features/terrain/bind10_control.py
+++ b/tests/lettuce/features/terrain/bind10_control.py
@@ -79,6 +79,20 @@ def wait_for_auth(step, process_name):
     world.processes.wait_for_stderr_str(process_name, ['AUTH_SERVER_STARTED'],
                                         False)
 
+ at step('wait for bind10 xfrout (?:of (\w+) )?to start')
+def wait_for_xfrout(step, process_name):
+    """Wait for b10-xfrout to run. This is done by blocking until the message
+       XFROUT_NEW_CONFIG_DONE is logged.
+       Parameters:
+       process_name ('of <name', optional): The name of the BIND 10 instance
+                    to wait for. Defaults to 'bind10'.
+    """
+    if process_name is None:
+        process_name = "bind10"
+    world.processes.wait_for_stderr_str(process_name,
+                                        ['XFROUT_NEW_CONFIG_DONE'],
+                                        False)
+
 @step('have bind10 running(?: with configuration ([\S]+))?' +\
       '(?: with cmdctl port (\d+))?' +\
       '(?: as ([\S]+))?')
diff --git a/tests/lettuce/features/terrain/querying.py b/tests/lettuce/features/terrain/querying.py
index ea89b18..b132512 100644
--- a/tests/lettuce/features/terrain/querying.py
+++ b/tests/lettuce/features/terrain/querying.py
@@ -179,7 +179,7 @@ class QueryResult(object):
         """
         pass
 
- at step('A query for ([\w.]+) (?:type ([A-Z]+) )?(?:class ([A-Z]+) )?' +
+ at step('A query for ([\w.]+) (?:type ([A-Z0-9]+) )?(?:class ([A-Z]+) )?' +
       '(?:to ([^:]+)(?::([0-9]+))? )?should have rcode ([\w.]+)')
 def query(step, query_name, qtype, qclass, addr, port, rcode):
     """
diff --git a/tests/lettuce/features/terrain/transfer.py b/tests/lettuce/features/terrain/transfer.py
new file mode 100644
index 0000000..305e677
--- /dev/null
+++ b/tests/lettuce/features/terrain/transfer.py
@@ -0,0 +1,138 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# This script provides transfer (ixfr/axfr) test functionality
+# It provides steps to perform the client side of a transfer,
+# and inspect the results.
+#
+# Like querying.py, it uses dig to do the transfers, and
+# places its output in a result structure
+#
+# This is done in a different file with different steps than
+# querying, because the format of dig's output is
+# very different than that of normal queries
+
+from lettuce import *
+import subprocess
+import re
+
+class TransferResult(object):
+    """This object stores transfer results, which is essentially simply
+       a list of RR strings. These are stored, as read from dig's output,
+       in the list 'records'. So for an IXFR transfer it contains
+       the exact result as returned by the server.
+       If this list is empty, the transfer failed for some reason (dig
+       does not really show error results well, unfortunately).
+       We may add some smarter inspection functionality to this class
+       later.
+    """
+    def __init__(self, args):
+        """Perform the transfer by calling dig, and store the results.
+           args is the array of arguments to pass to Popen(), this
+           is passed as is since for IXFR and AXFR there can be very
+           different options"""
+        self.records = []
+
+        # Technically, using a pipe here can fail; since we don't expect
+        # large output right now, this works, but should we get a test
+        # where we do have a lot of output, this could block, and we will
+        # need to read the output in a different way.
+        dig_process = subprocess.Popen(args, 1, None, None, subprocess.PIPE,
+                                       None)
+        result = dig_process.wait()
+        assert result == 0
+        for l in dig_process.stdout:
+            line = l.strip()
+            if len(line) > 0 and line[0] != ';':
+                self.records.append(line)
+
+ at step('An AXFR transfer of ([\w.]+)(?: from ([^:]+)(?::([0-9]+))?)?')
+def perform_axfr(step, zone_name, address, port):
+    """
+    Perform an AXFR transfer, and store the result as an instance of
+    TransferResult in world.transfer_result.
+
+    Step definition:
+    An AXFR transfer of <zone_name> [from <address>:<port>]
+
+    Address defaults to 127.0.0.1
+    Port defaults to 47806
+    """
+    if address is None:
+        address = "127.0.0.1"
+    if port is None:
+        port = 47806
+    args = [ 'dig', 'AXFR', '@' + str(address), '-p', str(port), zone_name ]
+    world.transfer_result = TransferResult(args)
+
+ at step('An IXFR transfer of ([\w.]+) (\d+)(?: from ([^:]+)(?::([0-9]+))?)?(?: over (tcp|udp))?')
+def perform_ixfr(step, zone_name, serial, address, port, protocol):
+    """
+    Perform an IXFR transfer, and store the result as an instance of
+    TransferResult in world.transfer_result.
+
+    Step definition:
+    An IXFR transfer of <zone_name> <serial> [from <address>:port] [over <tcp|udp>]
+
+    Address defaults to 127.0.0.1
+    Port defaults to 47806
+    If either tcp or udp is specified, only this protocol will be used.
+    """
+    if address is None:
+        address = "127.0.0.1"
+    if port is None:
+        port = 47806
+    args = [ 'dig', 'IXFR=' + str(serial), '@' + str(address), '-p', str(port), zone_name ]
+    if protocol is not None:
+        assert protocol == 'tcp' or protocol == 'udp', "Unknown protocol: " + protocol
+        if protocol == 'tcp':
+            args.append('+tcp')
+        elif protocol == 'udp':
+            args.append('+notcp')
+    world.transfer_result = TransferResult(args)
+
+ at step('transfer result should have (\d+) rrs?')
+def check_transfer_result_count(step, number_of_rrs):
+    """
+    Check the number of rrs in the transfer result object created by
+    the AXFR transfer or IXFR transfer step.
+
+    Step definition:
+    transfer result should have <number> rr[s]
+
+    Fails if the number of RRs is not equal to number
+    """
+    assert int(number_of_rrs) == len(world.transfer_result.records),\
+        "Got " + str(len(world.transfer_result.records)) +\
+        " records, expected " + str(number_of_rrs)
+
+ at step('full result of the last transfer should be')
+def check_full_transfer_result(step):
+    """
+    Check the complete output from the last transfer call.
+
+    Step definition:
+    full result of the last transfer should be <multiline value>
+
+    Whitespace is normalized in both the multiline value and the
+    output, but the order of the output is not.
+    Fails if there is any difference between the two. Prints
+    full output and expected value upon failure.
+    """
+    records_string = "\n".join(world.transfer_result.records)
+    records_string = re.sub("[ \t]+", " ", records_string)
+    expect = re.sub("[ \t]+", " ", step.multiline)
+    assert records_string.strip() == expect.strip(),\
+        "Got:\n'" + records_string + "'\nExpected:\n'" + expect + "'"
diff --git a/tools/reorder_message_file.py b/tools/reorder_message_file.py
index 31f4941..2ba4d7c 100644
--- a/tools/reorder_message_file.py
+++ b/tools/reorder_message_file.py
@@ -191,6 +191,6 @@ if __name__ == "__main__":
 
     # Read the files and load the data
     if len(sys.argv) != 2:
-        print "Usage: python reorder.py message_file"
+        print("Usage: python reorder.py message_file")
     else:
         process_file(sys.argv[1])




More information about the bind10-changes mailing list