BIND 10 trac3251, updated. 4063c0a8aa70cd0503fbf9d19a8b7f65ecd59064 [3251] Merge branch 'master' into trac3251
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu Jan 2 10:30:53 UTC 2014
The branch, trac3251 has been updated
via 4063c0a8aa70cd0503fbf9d19a8b7f65ecd59064 (commit)
via 7715d34151a84d87702c18ea0717fdb7f63affb0 (commit)
via 0660b7ceb2b998b765e5ab48568f6dfc8111defa (commit)
via 687beb26da60ed2ce8e6eb44cd1725aa53f29f55 (commit)
via 210c5d0939516e7ad8250e4ff056f132e4d8963d (commit)
via 35bee74e98a1d769ef7d028ca3ef95f3ac4081b0 (commit)
via e7cac9c491fc35f45d4593e309b27ce6a33973dd (commit)
via ca58ac00fce4cb5f46e534d7ffadb2db4e4ffaf3 (commit)
via 13d812dc84b6967638efe30dd1dd1a34efcbfa80 (commit)
via 59bd021c72b3e2d9538b534f3ba00ae4b410da62 (commit)
via 3ff790761f37375de1f9eff59872405fc26e0364 (commit)
via 0e2851da1d2f91263c2f474fecdfe14427cf5c34 (commit)
via 0b4b438f337361eeeb7f5e5dbaf9938c24353812 (commit)
via 88a4858db206dfcd53a227562198f308f7779a72 (commit)
via 2133cf13c8ce75c146c1f66179f9c7d9a9228ef8 (commit)
via d8045b5e1580a1d0b89a232fd61c10d25a95e769 (commit)
via 113fef862040f3c52e11071ae363e9016f30df0a (commit)
via 689bd411d1c25b3bd798a24567cadf4f867475c8 (commit)
via 8f96d784205c46e35f368e39e9233711639918b9 (commit)
via e5ee6e7893e6ecad5760759f85eb0650435f8dbf (commit)
via ddfaf375356cb49e1c784ae66c7f223c60537652 (commit)
via 70941f1375063c52a43a10831dc9707c6303f55a (commit)
via 39e902dc727aedf0fe5b949516ee746bad38607e (commit)
via 265d1dabd2c745fd1884fa2afbeb951068dd9168 (commit)
via fa547a4d7443e7407a8e6cbb02d3263133b24a0d (commit)
via c6158690c389d75686545459618ae0bf16f2cdb8 (commit)
via 03c2a0f0a538aa4f8bebe9678a23473e0a05ee3f (commit)
via 684524bc130080e4fa31b65edfd14d58eec37e50 (commit)
via ac0abc3cf7c512c87122e0f5ead4f0c2fca04890 (commit)
via 840afe672b9002ddecba485aec09ad085e09baeb (commit)
via df4334a7eb130bb0322d271253c4ffe15485a2b7 (commit)
via dc1071d7aa1204e731c0a5959d28dc3425aa7f3b (commit)
via 7a96b9bb8c38e0e45b44db09c5c2b015c2609cb6 (commit)
via 08b88147e8368d7f48a2e130deefbcb86cd645af (commit)
via 13cb26e18540a27e77a96736eb7d8029997f1ccf (commit)
via 3c33b14cb434d745187877e56dd3858648343281 (commit)
via 68ec7bd3331f98e87e5a0f59cc0894d4e6792a84 (commit)
via b25d9710abc79dd80179ea256793c1ac5556309a (commit)
via 19a8a4ef198d0b435f8f525cf5eaf215716084ae (commit)
via af2ab70ac7327f38f0e75f37175d190e0f77fa4d (commit)
via ae4c0644431f0da68e498b3334a9ea2e55825bf6 (commit)
via 13f795defaa2a330f4ccc637735f2f3a7d43a0cc (commit)
via 3ed35605cc7f7aeaec15d04e8c5f54a1b54369b6 (commit)
via 439951434df8bb361eaf9453a65f386a74700215 (commit)
via 9d3cf130a0c92c715b8ff1b7ce8f1743ab9c2bc7 (commit)
via 4a08e3662e7720ac6c96b7d585084bbe4033c29b (commit)
via 96fdb92a291b0f8897027e3b1c8b79918ae32483 (commit)
via 9fbb30d633080d5e6a65cef793b1a990f4f1d5e8 (commit)
via 92740b11ed7dc3a757e2082608596e0a3d173b51 (commit)
from cef44a4094cac6272f1a85c485b23fd047107fe0 (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 4063c0a8aa70cd0503fbf9d19a8b7f65ecd59064
Merge: cef44a4 7715d34
Author: Marcin Siodelski <marcin at isc.org>
Date: Thu Jan 2 11:01:05 2014 +0100
[3251] Merge branch 'master' into trac3251
-----------------------------------------------------------------------
Summary of changes:
.gitignore | 2 +-
ChangeLog | 38 +-
configure.ac | 440 ++---
doc/guide/bind10-guide.xml | 68 +-
src/bin/d2/Makefile.am | 1 +
src/bin/d2/d2_messages.mes | 95 +-
src/bin/d2/nc_add.cc | 70 +-
src/bin/d2/nc_add.h | 21 +-
src/bin/d2/{nc_add.cc => nc_remove.cc} | 368 ++--
src/bin/d2/{nc_add.h => nc_remove.h} | 293 ++-
src/bin/d2/nc_trans.cc | 24 +-
src/bin/d2/nc_trans.h | 8 +-
src/bin/d2/tests/Makefile.am | 2 +
src/bin/d2/tests/nc_add_unittests.cc | 44 +-
src/bin/d2/tests/nc_remove_unittests.cc | 1872 ++++++++++++++++++++
src/bin/d2/tests/nc_test_utils.cc | 274 ++-
src/bin/d2/tests/nc_test_utils.h | 101 +-
src/bin/d2/tests/nc_trans_unittests.cc | 33 +-
src/bin/dhcp4/config_parser.cc | 21 +-
src/bin/dhcp4/dhcp4.spec | 6 +
src/bin/dhcp4/dhcp4_srv.cc | 4 +-
src/bin/dhcp4/tests/config_parser_unittest.cc | 41 +
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc | 50 +
src/bin/dhcp4/tests/dhcp4_test_utils.cc | 23 +-
src/bin/dhcp4/tests/dhcp4_test_utils.h | 8 +-
src/lib/dhcp/iface_mgr.h | 7 +-
src/lib/dhcp/iface_mgr_bsd.cc | 98 +-
src/lib/dhcp/iface_mgr_linux.cc | 2 +-
src/lib/dhcp/iface_mgr_sun.cc | 104 +-
src/lib/dhcp/libdhcp++.cc | 4 +-
src/lib/dhcp/libdhcp++.dox | 5 +-
src/lib/dhcp/tests/iface_mgr_unittest.cc | 186 +-
src/lib/dhcp/tests/libdhcp++_unittest.cc | 5 +-
src/lib/dhcpsrv/cfgmgr.cc | 2 +-
src/lib/dhcpsrv/cfgmgr.h | 19 +
src/lib/dhcpsrv/tests/cfgmgr_unittest.cc | 17 +
tests/tools/perfdhcp/Makefile.am | 3 +-
tests/tools/perfdhcp/command_options.cc | 55 +-
tests/tools/perfdhcp/command_options.h | 10 +-
tests/tools/perfdhcp/rate_control.cc | 158 ++
tests/tools/perfdhcp/rate_control.h | 180 ++
tests/tools/perfdhcp/stats_mgr.h | 34 +-
tests/tools/perfdhcp/test_control.cc | 288 +--
tests/tools/perfdhcp/test_control.h | 98 +-
tests/tools/perfdhcp/tests/Makefile.am | 3 +
.../perfdhcp/tests/command_options_unittest.cc | 68 +-
.../tools/perfdhcp/tests/rate_control_unittest.cc | 207 +++
tests/tools/perfdhcp/tests/stats_mgr_unittest.cc | 20 +
.../tools/perfdhcp/tests/test_control_unittest.cc | 508 ++++--
49 files changed, 4926 insertions(+), 1062 deletions(-)
copy src/bin/d2/{nc_add.cc => nc_remove.cc} (62%)
copy src/bin/d2/{nc_add.h => nc_remove.h} (55%)
create mode 100644 src/bin/d2/tests/nc_remove_unittests.cc
create mode 100644 tests/tools/perfdhcp/rate_control.cc
create mode 100644 tests/tools/perfdhcp/rate_control.h
create mode 100644 tests/tools/perfdhcp/tests/rate_control_unittest.cc
-----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 45f9424..65efde8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,7 +13,7 @@ TAGS
*.log
*.trs
-
+config.h.in~
/aclocal.m4
/autom4te.cache/
/config.guess
diff --git a/ChangeLog b/ChangeLog
index 7767c1c..b12cf47 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,37 @@
+720. [func] tmark
+ Added the initial implementation of the class, NameAddTransaction,
+ to b10-dhcp-ddns. This class provides a state machine which implements
+ the logic required to remove forward and reverse DNS entries as described
+ in RFC 4703, section 5.5. This includes the ability to construct the
+ necessary DNS requests.
+ (Trac# 3088, git ca58ac00fce4cb5f46e534d7ffadb2db4e4ffaf3)
+
+719. [func] tomek
+ b10-dhcp4: Support for sending back client-id (RFC6842) has been
+ added now. Also a configuration parameter (echo-client-id) has
+ been added, so it is possible to enable backward compatibility
+ ("echo-client-id false").
+ (Trac #3210, git 88a4858db206dfcd53a227562198f308f7779a72)
+
+718. [func] dclink, tomek
+ libdhcp++: Interface detection implemented for FreeBSD, NetBSD,
+ OpenBSD, Mac OS X and Solaris 11. Thanks to David Carlier for
+ contributing a patch.
+ (Trac #2246, git d8045b5e1580a1d0b89a232fd61c10d25a95e769)
+
+717. [bug] marcin
+ Fixed the bug which incorrectly treated DHCPv4 option codes 224-254 as
+ standard options, barring them from being used as custom options.
+ (Trac #2772, git c6158690c389d75686545459618ae0bf16f2cdb8)
+
+716. [func] marcin
+ perfdhcp: added support for sending DHCPv6 Relese messages at the specified
+ rate and measure performance. The orphan messages counters are not
+ displayed for individual exchanges anymore. The following ticket: #3261
+ has been submitted to implement global orphan counting for all exchange
+ types.
+ (Trac #3181, git 684524bc130080e4fa31b65edfd14d58eec37e50)
+
715. [bug] marcin
libdhcp++: Used the CMSG_SPACE instead of CMSG_LEN macro to calculate
msg_controllen field of the DHCPv6 message. Use of CMSG_LEN causes
@@ -13,7 +47,7 @@
b10-dhcp-ddns. The class now generates all DNS update request variations
needed to fulfill it's state machine in compliance with RFC 4703, sections
5.3 and 5.4.
- (Trac# 3207, git dceca9554cb9410dd8d12371b68198b797cb6cfb)
+ (Trac# 3241, git dceca9554cb9410dd8d12371b68198b797cb6cfb)
712. [func] marcin,dclink
b10-dhcp4: If server fails to open a socket on one interface it
@@ -30,7 +64,7 @@
reverse DNS entries for a given FQDN. It does not yet construct
the actual DNS update requests, this will be added under Trac#
3241.
- (Trac# 3207, git 8f99da735a9f39d514c40d0a295f751dc8edfbcd)
+ (Trac# 3087, git 8f99da735a9f39d514c40d0a295f751dc8edfbcd)
710. [build] jinmei
Fixed various build time issues for MacOS X 10.9. Those include
diff --git a/configure.ac b/configure.ac
index 3f2b3fa..f508400 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1301,302 +1301,303 @@ AC_TRY_LINK(
AM_CONDITIONAL(HAVE_OPTRESET, test "x$var_optreset_exists" != "xno")
AM_COND_IF([HAVE_OPTRESET], [AC_DEFINE([HAVE_OPTRESET], [1], [Check for optreset?])])
-AC_CONFIG_FILES([Makefile
- doc/Makefile
- doc/guide/Makefile
- doc/design/Makefile
+AC_CONFIG_FILES([compatcheck/Makefile
+ dns++.pc
doc/design/datasrc/Makefile
- ext/Makefile
- ext/asio/Makefile
+ doc/design/Makefile
+ doc/guide/Makefile
+ doc/Makefile
+ doc/version.ent
ext/asio/asio/Makefile
- compatcheck/Makefile
- src/Makefile
- src/bin/Makefile
+ ext/asio/Makefile
+ ext/Makefile
+ m4macros/Makefile
+ Makefile
+ src/bin/auth/auth.spec.pre
+ src/bin/auth/benchmarks/Makefile
+ src/bin/auth/gen-statisticsitems.py.pre
+ src/bin/auth/Makefile
+ src/bin/auth/spec_config.h.pre
+ src/bin/auth/tests/Makefile
+ src/bin/auth/tests/testdata/example-base.zone
+ src/bin/auth/tests/testdata/example-nsec3.zone
+ src/bin/auth/tests/testdata/example.zone
+ src/bin/auth/tests/testdata/Makefile
src/bin/bind10/bind10
+ src/bin/bind10/init.py
src/bin/bind10/Makefile
+ src/bin/bind10/run_bind10.sh
+ src/bin/bind10/tests/init_test.py
src/bin/bind10/tests/Makefile
- src/bin/cmdctl/Makefile
- src/bin/cmdctl/tests/Makefile
+ src/bin/bindctl/bindctl_main.py
src/bin/bindctl/Makefile
+ src/bin/bindctl/run_bindctl.sh
+ src/bin/bindctl/tests/bindctl_test
src/bin/bindctl/tests/Makefile
- src/bin/cfgmgr/Makefile
+ src/bin/cfgmgr/b10-cfgmgr.py
src/bin/cfgmgr/local_plugins/Makefile
+ src/bin/cfgmgr/Makefile
+ src/bin/cfgmgr/plugins/datasrc.spec.pre
src/bin/cfgmgr/plugins/Makefile
src/bin/cfgmgr/plugins/tests/Makefile
+ src/bin/cfgmgr/tests/b10-cfgmgr_test.py
src/bin/cfgmgr/tests/Makefile
+ src/bin/cmdctl/cmdctl.py
+ src/bin/cmdctl/cmdctl.spec.pre
+ src/bin/cmdctl/Makefile
+ src/bin/cmdctl/run_b10-cmdctl.sh
+ src/bin/cmdctl/tests/cmdctl_test
+ src/bin/cmdctl/tests/Makefile
+ src/bin/d2/Makefile
+ src/bin/d2/spec_config.h.pre
+ src/bin/d2/tests/Makefile
+ src/bin/d2/tests/test_data_files_config.h
+ src/bin/dbutil/dbutil.py
src/bin/dbutil/Makefile
+ src/bin/dbutil/run_dbutil.sh
+ src/bin/dbutil/tests/dbutil_test.sh
src/bin/dbutil/tests/Makefile
src/bin/dbutil/tests/testdata/Makefile
+ src/bin/ddns/ddns.py
+ src/bin/ddns/Makefile
+ src/bin/ddns/tests/Makefile
+ src/bin/dhcp4/Makefile
+ src/bin/dhcp4/spec_config.h.pre
+ src/bin/dhcp4/tests/Makefile
+ src/bin/dhcp4/tests/marker_file.h
+ src/bin/dhcp4/tests/test_libraries.h
+ src/bin/dhcp6/Makefile
+ src/bin/dhcp6/spec_config.h.pre
+ src/bin/dhcp6/tests/Makefile
+ src/bin/dhcp6/tests/marker_file.h
+ src/bin/dhcp6/tests/test_data_files_config.h
+ src/bin/dhcp6/tests/test_libraries.h
+ src/bin/loadzone/loadzone.py
src/bin/loadzone/Makefile
- src/bin/loadzone/tests/Makefile
+ src/bin/loadzone/run_loadzone.sh
+ src/bin/loadzone/tests/correct/correct_test.sh
src/bin/loadzone/tests/correct/Makefile
+ src/bin/loadzone/tests/Makefile
+ src/bin/Makefile
src/bin/memmgr/Makefile
+ src/bin/memmgr/memmgr.py
+ src/bin/memmgr/memmgr.spec.pre
src/bin/memmgr/tests/Makefile
src/bin/msgq/Makefile
+ src/bin/msgq/msgq.py
+ src/bin/msgq/run_msgq.sh
src/bin/msgq/tests/Makefile
- src/bin/auth/Makefile
- src/bin/auth/tests/Makefile
- src/bin/auth/tests/testdata/Makefile
- src/bin/auth/benchmarks/Makefile
- src/bin/ddns/Makefile
- src/bin/ddns/tests/Makefile
- src/bin/dhcp6/Makefile
- src/bin/dhcp6/tests/Makefile
- src/bin/dhcp4/Makefile
- src/bin/dhcp4/tests/Makefile
- src/bin/d2/Makefile
- src/bin/d2/tests/Makefile
+ src/bin/resolver/bench/Makefile
src/bin/resolver/Makefile
+ src/bin/resolver/resolver.spec.pre
+ src/bin/resolver/spec_config.h.pre
src/bin/resolver/tests/Makefile
- src/bin/resolver/bench/Makefile
- src/bin/sysinfo/Makefile
src/bin/sockcreator/Makefile
src/bin/sockcreator/tests/Makefile
+ src/bin/stats/Makefile
+ src/bin/stats/stats_httpd.py
+ src/bin/stats/stats.py
+ src/bin/stats/tests/Makefile
+ src/bin/stats/tests/testdata/Makefile
+ src/bin/sysinfo/Makefile
+ src/bin/sysinfo/run_sysinfo.sh
+ src/bin/sysinfo/sysinfo.py
+ src/bin/tests/Makefile
+ src/bin/tests/process_rename_test.py
+ src/bin/usermgr/b10-cmdctl-usermgr.py
+ src/bin/usermgr/Makefile
+ src/bin/usermgr/run_b10-cmdctl-usermgr.sh
+ src/bin/usermgr/tests/Makefile
src/bin/xfrin/Makefile
+ src/bin/xfrin/run_b10-xfrin.sh
src/bin/xfrin/tests/Makefile
src/bin/xfrin/tests/testdata/Makefile
+ src/bin/xfrin/tests/xfrin_test
+ src/bin/xfrin/xfrin.py
src/bin/xfrout/Makefile
+ src/bin/xfrout/run_b10-xfrout.sh
src/bin/xfrout/tests/Makefile
+ src/bin/xfrout/tests/xfrout_test
+ src/bin/xfrout/tests/xfrout_test.py
+ src/bin/xfrout/xfrout.py
+ src/bin/xfrout/xfrout.spec.pre
src/bin/zonemgr/Makefile
+ src/bin/zonemgr/run_b10-zonemgr.sh
src/bin/zonemgr/tests/Makefile
- src/bin/stats/Makefile
- src/bin/stats/tests/Makefile
- src/bin/stats/tests/testdata/Makefile
- src/bin/usermgr/Makefile
- src/bin/usermgr/tests/Makefile
- src/bin/tests/Makefile
- src/hooks/Makefile
+ src/bin/zonemgr/tests/zonemgr_test
+ src/bin/zonemgr/zonemgr.py
+ src/bin/zonemgr/zonemgr.spec.pre
src/hooks/dhcp/Makefile
src/hooks/dhcp/user_chk/Makefile
src/hooks/dhcp/user_chk/tests/Makefile
- src/lib/Makefile
- src/lib/asiolink/Makefile
- src/lib/asiolink/tests/Makefile
+ src/hooks/dhcp/user_chk/tests/test_data_files_config.h
+ src/hooks/Makefile
+ src/lib/acl/Makefile
+ src/lib/acl/tests/Makefile
src/lib/asiodns/Makefile
src/lib/asiodns/tests/Makefile
- src/lib/bench/Makefile
+ src/lib/asiolink/Makefile
+ src/lib/asiolink/tests/Makefile
src/lib/bench/example/Makefile
+ src/lib/bench/Makefile
src/lib/bench/tests/Makefile
+ src/lib/cache/Makefile
+ src/lib/cache/tests/Makefile
src/lib/cc/Makefile
+ src/lib/cc/session_config.h.pre
src/lib/cc/tests/Makefile
- src/lib/python/Makefile
- src/lib/python/isc/Makefile
+ src/lib/cc/tests/session_unittests_config.h
+ src/lib/config/Makefile
+ src/lib/config/tests/data_def_unittests_config.h
+ src/lib/config/tests/Makefile
+ src/lib/config/tests/testdata/Makefile
+ src/lib/cryptolink/Makefile
+ src/lib/cryptolink/tests/Makefile
+ src/lib/datasrc/datasrc_config.h.pre
+ src/lib/datasrc/Makefile
+ src/lib/datasrc/memory/benchmarks/Makefile
+ src/lib/datasrc/memory/Makefile
+ src/lib/datasrc/tests/Makefile
+ src/lib/datasrc/tests/memory/Makefile
+ src/lib/datasrc/tests/memory/testdata/Makefile
+ src/lib/datasrc/tests/testdata/Makefile
+ src/lib/dhcp_ddns/Makefile
+ src/lib/dhcp_ddns/tests/Makefile
+ src/lib/dhcp/Makefile
+ src/lib/dhcpsrv/Makefile
+ src/lib/dhcpsrv/tests/Makefile
+ src/lib/dhcpsrv/tests/test_libraries.h
+ src/lib/dhcp/tests/Makefile
+ src/lib/dns/benchmarks/Makefile
+ src/lib/dns/gen-rdatacode.py
+ src/lib/dns/Makefile
+ src/lib/dns/python/Makefile
+ src/lib/dns/python/tests/Makefile
+ src/lib/dns/tests/Makefile
+ src/lib/dns/tests/testdata/Makefile
+ src/lib/exceptions/Makefile
+ src/lib/exceptions/tests/Makefile
+ src/lib/hooks/Makefile
+ src/lib/hooks/tests/Makefile
+ src/lib/hooks/tests/marker_file.h
+ src/lib/hooks/tests/test_libraries.h
+ src/lib/log/compiler/Makefile
+ src/lib/log/interprocess/Makefile
+ src/lib/log/interprocess/tests/Makefile
+ src/lib/log/Makefile
+ src/lib/log/tests/buffer_logger_test.sh
+ src/lib/log/tests/console_test.sh
+ src/lib/log/tests/destination_test.sh
+ src/lib/log/tests/init_logger_test.sh
+ src/lib/log/tests/local_file_test.sh
+ src/lib/log/tests/logger_lock_test.sh
+ src/lib/log/tests/Makefile
+ src/lib/log/tests/severity_test.sh
+ src/lib/log/tests/tempdir.h
+ src/lib/Makefile
+ src/lib/nsas/Makefile
+ src/lib/nsas/tests/Makefile
+ src/lib/python/bind10_config.py
src/lib/python/isc/acl/Makefile
src/lib/python/isc/acl/tests/Makefile
- src/lib/python/isc/util/Makefile
- src/lib/python/isc/util/tests/Makefile
- src/lib/python/isc/util/cio/Makefile
- src/lib/python/isc/util/cio/tests/Makefile
- src/lib/python/isc/datasrc/Makefile
- src/lib/python/isc/datasrc/tests/Makefile
- src/lib/python/isc/datasrc/tests/testdata/Makefile
- src/lib/python/isc/dns/Makefile
- src/lib/python/isc/cc/Makefile
+ src/lib/python/isc/bind10/Makefile
+ src/lib/python/isc/bind10/tests/Makefile
src/lib/python/isc/cc/cc_generated/Makefile
+ src/lib/python/isc/cc/Makefile
+ src/lib/python/isc/cc/tests/cc_test
src/lib/python/isc/cc/tests/Makefile
src/lib/python/isc/config/Makefile
+ src/lib/python/isc/config/tests/config_test
src/lib/python/isc/config/tests/Makefile
+ src/lib/python/isc/datasrc/Makefile
+ src/lib/python/isc/datasrc/tests/Makefile
+ src/lib/python/isc/datasrc/tests/testdata/Makefile
+ src/lib/python/isc/ddns/Makefile
+ src/lib/python/isc/ddns/tests/Makefile
+ src/lib/python/isc/dns/Makefile
src/lib/python/isc/log/Makefile
- src/lib/python/isc/log/tests/Makefile
src/lib/python/isc/log_messages/Makefile
+ src/lib/python/isc/log_messages/work/__init__.py
src/lib/python/isc/log_messages/work/Makefile
+ src/lib/python/isc/log/tests/log_console.py
+ src/lib/python/isc/log/tests/Makefile
+ src/lib/python/isc/Makefile
+ src/lib/python/isc/memmgr/Makefile
+ src/lib/python/isc/memmgr/tests/Makefile
+ src/lib/python/isc/memmgr/tests/testdata/Makefile
src/lib/python/isc/net/Makefile
src/lib/python/isc/net/tests/Makefile
src/lib/python/isc/notify/Makefile
src/lib/python/isc/notify/tests/Makefile
- src/lib/python/isc/testutils/Makefile
- src/lib/python/isc/bind10/Makefile
- src/lib/python/isc/bind10/tests/Makefile
- src/lib/python/isc/ddns/Makefile
- src/lib/python/isc/ddns/tests/Makefile
- src/lib/python/isc/memmgr/Makefile
- src/lib/python/isc/memmgr/tests/Makefile
- src/lib/python/isc/memmgr/tests/testdata/Makefile
- src/lib/python/isc/xfrin/Makefile
- src/lib/python/isc/xfrin/tests/Makefile
+ src/lib/python/isc/notify/tests/notify_out_test
src/lib/python/isc/server_common/Makefile
src/lib/python/isc/server_common/tests/Makefile
- src/lib/python/isc/sysinfo/Makefile
- src/lib/python/isc/sysinfo/tests/Makefile
src/lib/python/isc/statistics/Makefile
src/lib/python/isc/statistics/tests/Makefile
- src/lib/config/Makefile
- src/lib/config/tests/Makefile
- src/lib/config/tests/testdata/Makefile
- src/lib/cryptolink/Makefile
- src/lib/cryptolink/tests/Makefile
- src/lib/dns/Makefile
- src/lib/dns/tests/Makefile
- src/lib/dns/tests/testdata/Makefile
- src/lib/dns/python/Makefile
- src/lib/dns/python/tests/Makefile
- src/lib/dns/benchmarks/Makefile
- src/lib/dhcp/Makefile
- src/lib/dhcp/tests/Makefile
- src/lib/dhcp_ddns/Makefile
- src/lib/dhcp_ddns/tests/Makefile
- src/lib/dhcpsrv/Makefile
- src/lib/dhcpsrv/tests/Makefile
- src/lib/exceptions/Makefile
- src/lib/exceptions/tests/Makefile
- src/lib/datasrc/Makefile
- src/lib/datasrc/memory/Makefile
- src/lib/datasrc/memory/benchmarks/Makefile
- src/lib/datasrc/tests/Makefile
- src/lib/datasrc/tests/testdata/Makefile
- src/lib/datasrc/tests/memory/Makefile
- src/lib/datasrc/tests/memory/testdata/Makefile
- src/lib/xfr/Makefile
- src/lib/xfr/tests/Makefile
- src/lib/hooks/Makefile
- src/lib/hooks/tests/Makefile
- src/lib/log/Makefile
- src/lib/log/interprocess/Makefile
- src/lib/log/interprocess/tests/Makefile
- src/lib/log/compiler/Makefile
- src/lib/log/tests/Makefile
+ src/lib/python/isc/sysinfo/Makefile
+ src/lib/python/isc/sysinfo/tests/Makefile
+ src/lib/python/isc/testutils/Makefile
+ src/lib/python/isc/util/cio/Makefile
+ src/lib/python/isc/util/cio/tests/Makefile
+ src/lib/python/isc/util/Makefile
+ src/lib/python/isc/util/tests/Makefile
+ src/lib/python/isc/xfrin/Makefile
+ src/lib/python/isc/xfrin/tests/Makefile
+ src/lib/python/Makefile
src/lib/resolve/Makefile
src/lib/resolve/tests/Makefile
- src/lib/testutils/Makefile
- src/lib/testutils/testdata/Makefile
- src/lib/nsas/Makefile
- src/lib/nsas/tests/Makefile
- src/lib/cache/Makefile
- src/lib/cache/tests/Makefile
src/lib/server_common/Makefile
+ src/lib/server_common/tests/data_path.h
src/lib/server_common/tests/Makefile
- src/lib/util/Makefile
+ src/lib/statistics/Makefile
+ src/lib/statistics/tests/Makefile
+ src/lib/testutils/Makefile
+ src/lib/testutils/testdata/Makefile
src/lib/util/io/Makefile
- src/lib/util/threads/Makefile
- src/lib/util/threads/tests/Makefile
- src/lib/util/unittests/Makefile
+ src/lib/util/Makefile
+ src/lib/util/python/doxygen2pydoc.py
+ src/lib/util/python/gen_wiredata.py
src/lib/util/python/Makefile
+ src/lib/util/python/mkpywrapper.py
src/lib/util/pyunittests/Makefile
src/lib/util/tests/Makefile
- src/lib/acl/Makefile
- src/lib/acl/tests/Makefile
- src/lib/statistics/Makefile
- src/lib/statistics/tests/Makefile
+ src/lib/util/threads/Makefile
+ src/lib/util/threads/tests/Makefile
+ src/lib/util/unittests/Makefile
+ src/lib/xfr/Makefile
+ src/lib/xfr/tests/Makefile
+ src/Makefile
+ tests/lettuce/Makefile
+ tests/lettuce/setup_intree_bind10.sh
tests/Makefile
- tests/tools/Makefile
tests/tools/badpacket/Makefile
tests/tools/badpacket/tests/Makefile
+ tests/tools/Makefile
tests/tools/perfdhcp/Makefile
tests/tools/perfdhcp/tests/Makefile
tests/tools/perfdhcp/tests/testdata/Makefile
- tests/lettuce/Makefile
- m4macros/Makefile
- dns++.pc
- ])
-AC_OUTPUT([doc/version.ent
- src/bin/cfgmgr/b10-cfgmgr.py
- src/bin/cfgmgr/tests/b10-cfgmgr_test.py
- src/bin/cfgmgr/plugins/datasrc.spec.pre
- src/bin/cmdctl/cmdctl.py
- src/bin/cmdctl/run_b10-cmdctl.sh
- src/bin/cmdctl/tests/cmdctl_test
- src/bin/cmdctl/cmdctl.spec.pre
- src/bin/dbutil/dbutil.py
- src/bin/dbutil/run_dbutil.sh
- src/bin/dbutil/tests/dbutil_test.sh
- src/bin/ddns/ddns.py
- src/bin/dhcp4/tests/marker_file.h
- src/bin/dhcp4/tests/test_libraries.h
- src/bin/dhcp6/tests/marker_file.h
- src/bin/dhcp6/tests/test_libraries.h
- src/bin/xfrin/tests/xfrin_test
- src/bin/xfrin/xfrin.py
- src/bin/xfrin/run_b10-xfrin.sh
- src/bin/xfrout/xfrout.py
- src/bin/xfrout/xfrout.spec.pre
- src/bin/xfrout/tests/xfrout_test
- src/bin/xfrout/tests/xfrout_test.py
- src/bin/xfrout/run_b10-xfrout.sh
- src/bin/resolver/resolver.spec.pre
- src/bin/resolver/spec_config.h.pre
- src/bin/zonemgr/zonemgr.py
- src/bin/zonemgr/zonemgr.spec.pre
- src/bin/zonemgr/tests/zonemgr_test
- src/bin/zonemgr/run_b10-zonemgr.sh
- src/bin/sysinfo/sysinfo.py
- src/bin/sysinfo/run_sysinfo.sh
- src/bin/stats/stats.py
- src/bin/stats/stats_httpd.py
- src/bin/bind10/init.py
- src/bin/bind10/run_bind10.sh
- src/bin/bind10/tests/init_test.py
- src/bin/bindctl/run_bindctl.sh
- src/bin/bindctl/bindctl_main.py
- src/bin/bindctl/tests/bindctl_test
- src/bin/loadzone/run_loadzone.sh
- src/bin/loadzone/tests/correct/correct_test.sh
- src/bin/loadzone/loadzone.py
- src/bin/usermgr/run_b10-cmdctl-usermgr.sh
- src/bin/usermgr/b10-cmdctl-usermgr.py
- src/bin/memmgr/memmgr.py
- src/bin/memmgr/memmgr.spec.pre
- src/bin/msgq/msgq.py
- src/bin/msgq/run_msgq.sh
- src/bin/auth/auth.spec.pre
- src/bin/auth/spec_config.h.pre
- src/bin/auth/tests/testdata/example.zone
- src/bin/auth/tests/testdata/example-base.zone
- src/bin/auth/tests/testdata/example-nsec3.zone
- src/bin/auth/gen-statisticsitems.py.pre
- src/bin/dhcp4/spec_config.h.pre
- src/bin/dhcp6/spec_config.h.pre
- src/bin/dhcp6/tests/test_data_files_config.h
- src/bin/d2/spec_config.h.pre
- src/bin/d2/tests/test_data_files_config.h
- src/bin/tests/process_rename_test.py
- src/hooks/dhcp/user_chk/tests/test_data_files_config.h
- src/lib/config/tests/data_def_unittests_config.h
- src/lib/dhcpsrv/tests/test_libraries.h
- src/lib/python/isc/config/tests/config_test
- src/lib/python/isc/cc/tests/cc_test
- src/lib/python/isc/notify/tests/notify_out_test
- src/lib/python/isc/log/tests/log_console.py
- src/lib/python/isc/log_messages/work/__init__.py
- src/lib/dns/gen-rdatacode.py
- src/lib/python/bind10_config.py
- src/lib/cc/session_config.h.pre
- src/lib/cc/tests/session_unittests_config.h
- src/lib/datasrc/datasrc_config.h.pre
- src/lib/hooks/tests/marker_file.h
- src/lib/hooks/tests/test_libraries.h
- src/lib/log/tests/console_test.sh
- src/lib/log/tests/destination_test.sh
- src/lib/log/tests/init_logger_test.sh
- src/lib/log/tests/buffer_logger_test.sh
- src/lib/log/tests/local_file_test.sh
- src/lib/log/tests/logger_lock_test.sh
- src/lib/log/tests/severity_test.sh
- src/lib/log/tests/tempdir.h
- src/lib/util/python/doxygen2pydoc.py
- src/lib/util/python/mkpywrapper.py
- src/lib/util/python/gen_wiredata.py
- src/lib/server_common/tests/data_path.h
- tests/lettuce/setup_intree_bind10.sh
- ], [
- chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
- chmod +x src/bin/xfrin/run_b10-xfrin.sh
- chmod +x src/bin/xfrout/run_b10-xfrout.sh
- chmod +x src/bin/zonemgr/run_b10-zonemgr.sh
+])
+
+ AC_CONFIG_COMMANDS([permissions], [
chmod +x src/bin/bind10/bind10
chmod +x src/bin/bind10/run_bind10.sh
+ chmod +x src/bin/bindctl/run_bindctl.sh
+ chmod +x src/bin/bindctl/tests/bindctl_test
+ chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
chmod +x src/bin/cmdctl/tests/cmdctl_test
chmod +x src/bin/dbutil/run_dbutil.sh
chmod +x src/bin/dbutil/tests/dbutil_test.sh
- chmod +x src/bin/xfrin/tests/xfrin_test
- chmod +x src/bin/xfrout/tests/xfrout_test
- chmod +x src/bin/zonemgr/tests/zonemgr_test
- chmod +x src/bin/bindctl/tests/bindctl_test
- chmod +x src/bin/bindctl/run_bindctl.sh
chmod +x src/bin/loadzone/run_loadzone.sh
chmod +x src/bin/loadzone/tests/correct/correct_test.sh
+ chmod +x src/bin/msgq/run_msgq.sh
chmod +x src/bin/sysinfo/run_sysinfo.sh
chmod +x src/bin/usermgr/run_b10-cmdctl-usermgr.sh
- chmod +x src/bin/msgq/run_msgq.sh
+ chmod +x src/bin/xfrin/run_b10-xfrin.sh
+ chmod +x src/bin/xfrin/tests/xfrin_test
+ chmod +x src/bin/xfrout/run_b10-xfrout.sh
+ chmod +x src/bin/xfrout/tests/xfrout_test
+ chmod +x src/bin/zonemgr/run_b10-zonemgr.sh
+ chmod +x src/bin/zonemgr/tests/zonemgr_test
chmod +x src/lib/dns/gen-rdatacode.py
chmod +x src/lib/log/tests/console_test.sh
chmod +x src/lib/log/tests/destination_test.sh
@@ -1604,11 +1605,12 @@ AC_OUTPUT([doc/version.ent
chmod +x src/lib/log/tests/local_file_test.sh
chmod +x src/lib/log/tests/logger_lock_test.sh
chmod +x src/lib/log/tests/severity_test.sh
+ chmod +x src/lib/python/isc/log/tests/log_console.py
chmod +x src/lib/util/python/doxygen2pydoc.py
- chmod +x src/lib/util/python/mkpywrapper.py
chmod +x src/lib/util/python/gen_wiredata.py
- chmod +x src/lib/python/isc/log/tests/log_console.py
- ])
+ chmod +x src/lib/util/python/mkpywrapper.py
+])
+
AC_OUTPUT
dnl Print the results
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index 16fffa8..d915e27 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -4419,6 +4419,29 @@ Dhcp4/subnet4 [] list (default)
</section>
+ <section id="dhcp4-echo-client-id">
+ <title>Echoing client-id (RFC6842)</title>
+ <para>Original DHCPv4 spec (RFC2131) states that the DHCPv4
+ server must not send back client-id options when responding to
+ clients. However, in some cases that confused clients that did
+ not have MAC address or client-id. See RFC6842 for details. That
+ behavior has changed with the publication of RFC6842 which
+ updated RFC2131. That update now states that the server must
+ send client-id if client sent it. That is the default behaviour
+ that Kea offers. However, in some cases older devices that do
+ not support RFC6842 may refuse to accept responses that include
+ client-id option. To enable backward compatibility, an optional
+ configuration parameter has been introduced. To configure it,
+ use the following commands:</para>
+
+<screen>
+> <userinput>config add Dhcp4/echo-client-id</userinput>
+> <userinput>config set Dhcp4/echo-client-id False</userinput>
+> <userinput>config commit</userinput>
+</screen>
+
+ </section>
+
<section id="dhcp4-std">
<title>Supported Standards</title>
<para>The following standards and draft standards are currently
@@ -4429,7 +4452,8 @@ Dhcp4/subnet4 [] list (default)
REQUEST, RELEASE, ACK, and NAK.</simpara>
</listitem>
<listitem>
- <simpara><ulink url="http://tools.ietf.org/html/rfc2132">RFC 2132</ulink>: Supported options are: PAD (0),
+ <simpara><ulink url="http://tools.ietf.org/html/rfc2132">RFC 2132</ulink>:
+ Supported options are: PAD (0),
END(255), Message Type(53), DHCP Server Identifier (54),
Domain Name (15), DNS Servers (6), IP Address Lease Time
(51), Subnet mask (1), and Routers (3).</simpara>
@@ -4437,6 +4461,10 @@ Dhcp4/subnet4 [] list (default)
<listitem>
<simpara><ulink url="http://tools.ietf.org/html/rfc3046">RFC 3046</ulink>:
Relay Agent Information option is supported.</simpara>
+ <simpara><ulink url="http://tools.ietf.org/html/rfc6842">RFC 6842</ulink>:
+ Server by default sends back client-id option. That capability may be
+ disabled. See <xref linkend="dhcp4-echo-client-id"/> for details.
+ </simpara>
</listitem>
</itemizedlist>
</section>
@@ -4460,21 +4488,6 @@ Dhcp4/renew-timer 1000 integer (default)
> <userinput>config commit</userinput></screen>
</para>
</listitem>
- <listitem>
- <simpara>During the initial IPv4 node configuration, the
- server is expected to send packets to a node that does not
- have IPv4 address assigned yet. The server requires
- certain tricks (or hacks) to transmit such packets. This
- is not implemented yet, therefore DHCPv4 server supports
- relayed traffic only (that is, normal point to point
- communication).</simpara>
- </listitem>
-
- <listitem>
- <simpara>Upon start, the server will open sockets on all
- interfaces that are not loopback, are up and running and
- have IPv4 address.</simpara>
- </listitem>
<listitem>
<simpara>The DHCPv4 server does not support
@@ -4484,7 +4497,7 @@ Dhcp4/renew-timer 1000 integer (default)
available from <ulink url="http://www.isc.org/software/dhcp"/>.</simpara>
</listitem>
<listitem>
- <simpara>Interface detection is currently working on Linux
+ <simpara>Raw sockets operation is working on Linux
only. See <xref linkend="iface-detect"/> for details.</simpara>
</listitem>
<listitem>
@@ -5401,10 +5414,6 @@ Dhcp6/renew-timer 1000 integer (default)
<listitem>
<simpara>DNS Update is not supported.</simpara>
</listitem>
- <listitem>
- <simpara>Interface detection is currently working on Linux
- only. See <xref linkend="iface-detect"/> for details.</simpara>
- </listitem>
</itemizedlist>
</section>
@@ -5439,16 +5448,17 @@ Dhcp6/renew-timer 1000 integer (default)
<!-- TODO: point to doxygen docs -->
<section id="iface-detect">
- <title>Interface detection</title>
+ <title>Interface detection and Socket handling</title>
<para>Both the DHCPv4 and DHCPv6 components share network
interface detection routines. Interface detection is
- currently only supported on Linux systems.</para>
-
- <para>For non-Linux systems, there is currently a stub
- implementation provided. The interface manager detects loopback
- interfaces only as their name (lo or lo0) can be easily predicted.
- Please contact the BIND 10 development team if you are interested
- in running DHCP components on systems other than Linux.</para>
+ currently supported on Linux, all BSD family (FreeBSD, NetBSD,
+ OpenBSD), Mac OS X and Solaris 11 systems.</para>
+
+ <para>DHCPv4 requires special raw socket processing to send and receive
+ packets from hosts that do not have IPv4 address assigned yet. Support
+ for this operation is implemented on Linux only, so it is likely that
+ DHCPv4 component will not work in certain cases on systems other than
+ Linux.</para>
</section>
<!--
diff --git a/src/bin/d2/Makefile.am b/src/bin/d2/Makefile.am
index 43f3e39..1da489c 100644
--- a/src/bin/d2/Makefile.am
+++ b/src/bin/d2/Makefile.am
@@ -65,6 +65,7 @@ b10_dhcp_ddns_SOURCES += d2_zone.cc d2_zone.h
b10_dhcp_ddns_SOURCES += dns_client.cc dns_client.h
b10_dhcp_ddns_SOURCES += labeled_value.cc labeled_value.h
b10_dhcp_ddns_SOURCES += nc_add.cc nc_add.h
+b10_dhcp_ddns_SOURCES += nc_remove.cc nc_remove.h
b10_dhcp_ddns_SOURCES += nc_trans.cc nc_trans.h
b10_dhcp_ddns_SOURCES += state_model.cc state_model.h
diff --git a/src/bin/d2/d2_messages.mes b/src/bin/d2/d2_messages.mes
index c0370ea..d4ab054 100644
--- a/src/bin/d2/d2_messages.mes
+++ b/src/bin/d2/d2_messages.mes
@@ -325,19 +325,19 @@ message but the attempt to send it suffered a unexpected error. This is most
likely a programmatic error, rather than a communications issue. Some or all
of the DNS updates requested as part of this request did not succeed.
-% DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE A DNS update message to add a forward DNS entry could not be constructed for this request: %1, reason: %2
+% DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE DNS udpate message to add a forward DNS entry could not be constructed for this request: %1, reason: %2
This is an error message issued when an error occurs attempting to construct
the server bound packet requesting a forward address addition. This is due
to invalid data contained in the NameChangeRequest. The request will be aborted.
This is most likely a configuration issue.
-% DHCP_DDNS_FORWARD_REPLACE_BUILD_FAILURE A DNS update message to replace a forward DNS entry could not be constructed from this request: %1, reason: %2
+% DHCP_DDNS_FORWARD_REPLACE_BUILD_FAILURE DNS update message to replace a forward DNS entry could not be constructed from this request: %1, reason: %2
This is an error message issued when an error occurs attempting to construct
the server bound packet requesting a forward address replacement. This is
due to invalid data contained in the NameChangeRequest. The request will be
aborted. This is most likely a configuration issue.
-% DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE A DNS update message to replace a reverse DNS entry could not be constructed from this request: %1, reason: %2
+% DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE DNS update message to replace a reverse DNS entry could not be constructed from this request: %1, reason: %2
This is an error message issued when an error occurs attempting to construct
the server bound packet requesting a reverse PTR replacement. This is
due to invalid data contained in the NameChangeRequest. The request will be
@@ -347,7 +347,94 @@ aborted. This is most likely a configuration issue.
This is a debug message issued after DHCP_DDNS has submitted DNS mapping
additions which were received and accepted by an appropriate DNS server.
-% DHCP_DDNS_ADD_FAILED DHCP_DDNS failed attempting to make DNS mapping additions for this request: %1
+% DHCP_DDNS_ADD_FAILED DHCP_DDNS failed attempting to make DNS mapping additions for this request: %1, event: %2
This is an error message issued after DHCP_DDNS attempts to submit DNS mapping
entry additions have failed. The precise reason for the failure should be
documented in preceding log entries.
+
+% DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE DNS udpate message to remove a forward DNS Address entry could not be constructed for this request: %1, reason: %2
+This is an error message issued when an error occurs attempting to construct
+the server bound packet requesting a forward address (A or AAAA) removal. This
+is due to invalid data contained in the NameChangeRequest. The request will be
+aborted. This is most likely a configuration issue.
+
+% DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED DNS Server, %1, rejected a DNS update request to remove the forward address mapping for FQDN, %2, with an RCODE: %3
+This is an error message issued when an update was rejected by the DNS server
+it was sent to for the reason given by the RCODE. The rcode values are defined
+in RFC 2136.
+
+% DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR DHCP_DDNS encountered an IO error sending a forward mapping address removal for FQDN %1 to DNS server %2
+This is an error message issued when a communication error occurs while
+DHCP_DDNS is carrying out a forward address remove. The application will retry
+against the same server or others as appropriate.
+
+% DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT DHCP_DDNS received a corrupt response from the DNS server, %1, while removing forward address mapping for FQDN, %2
+This is an error message issued when the response received by DHCP_DDNS, to a
+update request to remove a forward address mapping, is mangled or malformed.
+The application will retry against the same server or others as appropriate.
+
+% DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS DHCP_DDNS received an unknown DNSClient status: %1, while removing a forward address mapping for FQDN %2 to DNS server %3
+This is an error message issued when DNSClient returns an unrecognized status
+while DHCP_DDNS was removing a forward address mapping. The request will be
+aborted. This is most likely a programmatic issue and should be reported.
+
+% DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE DNS udpate message to remove forward DNS RR entries could not be constructed for this request: %1, reason: %2
+This is an error message issued when an error occurs attempting to construct
+the server bound packet requesting forward RR (DHCID RR) removal. This is due
+to invalid data contained in the NameChangeRequest. The request will be aborted.This is most likely a configuration issue.
+
+% DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED DNS Server, %1, rejected a DNS update request to remove forward RR entries for FQDN, %2, with an RCODE: %3
+This is an error message issued when an update was rejected by the DNS server
+it was sent to for the reason given by the RCODE. The rcode values are defined
+in RFC 2136.
+
+% DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR DHCP_DDNS encountered an IO error sending a forward RR removal for FQDN %1 to DNS server %2
+This is an error message issued when a communication error occurs while
+DHCP_DDNS is carrying out a forward RR remove. The application will retry
+against the same server.
+
+% DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT DHCP_DDNS received a corrupt response from the DNS server, %1, while removing forward RRs for FQDN, %2
+This is an error message issued when the response received by DHCP_DDNS, to a
+update request to remove forward RRs mapping, is mangled or malformed.
+The application will retry against the same server or others as appropriate.
+
+% DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS DHCP_DDNS received an unknown DNSClient status: %1, while removing forward RRs for FQDN %2 to DNS server %3
+This is an error message issued when DNSClient returns an unrecognized status
+while DHCP_DDNS was removing forward RRs. The request will be aborted. This is
+most likely a programmatic issue and should be reported.
+
+% DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE DNS update message to remove a reverse DNS entry could not be constructed from this request: %1, reason: %2
+This is an error message issued when an error occurs attempting to construct
+the server bound packet requesting a reverse PTR removal. This is
+due to invalid data contained in the NameChangeRequest. The request will be
+aborted. This is most likely a configuration issue.
+
+% DHCP_DDNS_REVERSE_REMOVE_REJECTED DNS Server, %1, rejected a DNS update request to remove the reverse mapping for FQDN, %2, with an RCODE: %3
+This is an error message issued when an update was rejected by the DNS server
+it was sent to for the reason given by the RCODE. The rcode values are defined
+in RFC 2136.
+
+% DHCP_DDNS_REVERSE_REMOVE_IO_ERROR DHCP_DDNS encountered an IO error sending a reverse mapping remove for FQDN %1 to DNS server %2
+This is an error message issued when a communication error occurs while
+DHCP_DDNS is carrying out a reverse address update. The application will
+retry against the same server or others as appropriate.
+
+% DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT DHCP_DDNS received a corrupt response from the DNS server, %1, while removing reverse address mapping for FQDN, %2
+This is an error message issued when the response received by DHCP_DDNS, to a
+update request to remove a reverse address, is mangled or malformed.
+The application will retry against the same server or others as appropriate.
+
+% DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS DHCP_DDNS received an unknown DNSClient status: %1, while removing reverse address mapping for FQDN %2 to DNS server %3
+This is an error message issued when DNSClient returns an unrecognized status
+while DHCP_DDNS was removing a reverse address mapping. The request will be
+aborted. This is most likely a programmatic issue and should be reported.
+
+% DHCP_DDNS_REMOVE_SUCCEEDED DHCP_DDNS successfully removed the DNS mapping addition for this request: %1
+This is a debug message issued after DHCP_DDNS has submitted DNS mapping
+removals which were received and accepted by an appropriate DNS server.
+
+% DHCP_DDNS_REMOVE_FAILED DHCP_DDNS failed attempting to make DNS mapping removals for this request: %1, event: %2
+This is an error message issued after DHCP_DDNS attempts to submit DNS mapping
+entry removals have failed. The precise reason for the failure should be
+documented in preceding log entries.
+
diff --git a/src/bin/d2/nc_add.cc b/src/bin/d2/nc_add.cc
index b92ec56..da20763 100644
--- a/src/bin/d2/nc_add.cc
+++ b/src/bin/d2/nc_add.cc
@@ -54,17 +54,25 @@ NameAddTransaction::defineEvents() {
// Call superclass impl first.
NameChangeTransaction::defineEvents();
- // Define NCT events.
+ // Define NameAddTransaction events.
defineEvent(FQDN_IN_USE_EVT, "FQDN_IN_USE_EVT");
defineEvent(FQDN_NOT_IN_USE_EVT, "FQDN_NOT_IN_USE_EVT");
}
void
NameAddTransaction::verifyEvents() {
- // Call superclass impl first.
+ // Call superclass implementation first to verify its events. These are
+ // events common to all transactions, and they must be defined.
+ // SELECT_SERVER_EVT
+ // SERVER_SELECTED_EVT
+ // SERVER_IO_ERROR_EVT
+ // NO_MORE_SERVERS_EVT
+ // IO_COMPLETED_EVT
+ // UPDATE_OK_EVT
+ // UPDATE_FAILED_EVT
NameChangeTransaction::verifyEvents();
- // Verify NCT events.
+ // Verify NameAddTransaction events by attempting to fetch them.
getEvent(FQDN_IN_USE_EVT);
getEvent(FQDN_NOT_IN_USE_EVT);
}
@@ -74,7 +82,7 @@ NameAddTransaction::defineStates() {
// Call superclass impl first.
NameChangeTransaction::defineStates();
- // Define the states.
+ // Define NameAddTransaction states.
defineState(READY_ST, "READY_ST",
boost::bind(&NameAddTransaction::readyHandler, this));
@@ -102,10 +110,16 @@ NameAddTransaction::defineStates() {
}
void
NameAddTransaction::verifyStates() {
- // Call superclass impl first.
+ // Call superclass implementation first to verify its states. These are
+ // states common to all transactions, and they must be defined.
+ // READY_ST
+ // SELECTING_FWD_SERVER_ST
+ // SELECTING_REV_SERVER_ST
+ // PROCESS_TRANS_OK_ST
+ // PROCESS_TRANS_FAILED_ST
NameChangeTransaction::verifyStates();
- // Verify NCT states. This ensures that derivations provide the handlers.
+ // Verify NameAddTransaction states by attempting to fetch them.
getState(ADDING_FWD_ADDRS_ST);
getState(REPLACING_FWD_ADDRS_ST);
getState(REPLACING_REV_PTRS_ST);
@@ -541,7 +555,9 @@ void
NameAddTransaction::processAddFailedHandler() {
switch(getNextEvent()) {
case UPDATE_FAILED_EVT:
- LOG_ERROR(dctl_logger, DHCP_DDNS_ADD_FAILED).arg(getNcr()->toText());
+ case NO_MORE_SERVERS_EVT:
+ LOG_ERROR(dctl_logger, DHCP_DDNS_ADD_FAILED).arg(getNcr()->toText())
+ .arg(getContextStr());
setNcrStatus(dhcp_ddns::ST_FAILED);
endModel();
break;
@@ -560,27 +576,28 @@ NameAddTransaction::buildAddFwdAddressRequest() {
// Construct dns::Name from NCR fqdn.
dns::Name fqdn(dns::Name(getNcr()->getFqdn()));
+ // Content on this request is based on RFC 4703, section 5.3.1
// First build the Prerequisite Section.
- // Create 'FQDN Is Not In Use' prerequisite (RFC 2136, section 2.4.5)
- // Add the RR to prerequisite section.
+ // Create 'FQDN Is Not In Use' prerequisite and add it to the
+ // prerequisite section.
+ // Based on RFC 2136, section 2.4.5
dns::RRsetPtr prereq(new dns::RRset(fqdn, dns::RRClass::NONE(),
dns::RRType::ANY(), dns::RRTTL(0)));
request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
// Next build the Update Section.
- // Create the FQDN/IP 'add' RR (RFC 2136, section 2.5.1)
- // Set the message RData to lease address.
- // Add the RR to update section.
+ // Create the FQDN/IP 'add' RR and add it to the to update section.
+ // Based on RFC 2136, section 2.5.1
dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::IN(),
getAddressRRType(), dns::RRTTL(0)));
addLeaseAddressRdata(update);
request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
- // Now create the FQDN/DHCID 'add' RR per RFC 4701)
- // Set the message RData to DHCID.
- // Add the RR to update section.
+
+ // Now create the FQDN/DHCID 'add' RR and add it to update section.
+ // Based on RFC 2136, section 2.5.1
update.reset(new dns::RRset(fqdn, dns::RRClass::IN(),
dns::RRType::DHCID(), dns::RRTTL(0)));
addDhcidRdata(update);
@@ -598,17 +615,19 @@ NameAddTransaction::buildReplaceFwdAddressRequest() {
// Construct dns::Name from NCR fqdn.
dns::Name fqdn(dns::Name(getNcr()->getFqdn()));
+ // Content on this request is based on RFC 4703, section 5.3.2
// First build the Prerequisite Section.
- // Create an 'FQDN Is In Use' prerequisite (RFC 2136, section 2.4.4)
- // Add it to the pre-requisite section.
+ // Create an 'FQDN Is In Use' prerequisite and add it to the
+ // pre-requisite section.
+ // Based on RFC 2136, section 2.4.4
dns::RRsetPtr prereq(new dns::RRset(fqdn, dns::RRClass::ANY(),
dns::RRType::ANY(), dns::RRTTL(0)));
request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
- // Now create an DHCID matches prerequisite RR.
- // Set the RR's RData to DHCID.
- // Add it to the pre-requisite section.
+ // Create an DHCID matches prerequisite RR and add it to the
+ // pre-requisite section.
+ // Based on RFC 2136, section 2.4.2.
prereq.reset(new dns::RRset(fqdn, dns::RRClass::IN(),
dns::RRType::DHCID(), dns::RRTTL(0)));
addDhcidRdata(prereq);
@@ -616,16 +635,14 @@ NameAddTransaction::buildReplaceFwdAddressRequest() {
// Next build the Update Section.
- // Create the FQDN/IP 'delete' RR (RFC 2136, section 2.5.1)
- // Set the message RData to lease address.
- // Add the RR to update section.
+ // Create the FQDN/IP 'delete' RR and add it to the update section.
+ // Based on RFC 2136, section 2.5.2
dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::ANY(),
getAddressRRType(), dns::RRTTL(0)));
request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
- // Create the FQDN/IP 'add' RR (RFC 2136, section 2.5.1)
- // Set the message RData to lease address.
- // Add the RR to update section.
+ // Create the FQDN/IP 'add' RR and add it to the update section.
+ // Based on RFC 2136, section 2.5.1
update.reset(new dns::RRset(fqdn, dns::RRClass::IN(),
getAddressRRType(), dns::RRTTL(0)));
addLeaseAddressRdata(update);
@@ -644,6 +661,7 @@ NameAddTransaction::buildReplaceRevPtrsRequest() {
std::string rev_addr = D2CfgMgr::reverseIpAddress(getNcr()->getIpAddress());
dns::Name rev_ip(rev_addr);
+ // Content on this request is based on RFC 4703, section 5.4
// Reverse replacement has no prerequisites so straight on to
// building the Update section.
diff --git a/src/bin/d2/nc_add.h b/src/bin/d2/nc_add.h
index b5776b4..1fe167b 100644
--- a/src/bin/d2/nc_add.h
+++ b/src/bin/d2/nc_add.h
@@ -34,9 +34,10 @@ public:
/// @brief Embodies the "life-cycle" required to carry out a DDNS Add update.
///
/// NameAddTransaction implements a state machine for adding (or replacing) a
-/// forward DNS mapping. This state machine is based upon the processing logic
-/// described in RFC 4703, Sections 5.3 and 5.4. That logic may be paraphrased
-/// as follows:
+/// forward and/or reverse DNS mapping. This state machine is based upon the
+/// processing logic described in RFC 4703, Sections 5.3 and 5.4. That logic
+/// may be paraphrased as follows:
+///
/// @code
///
/// If the request includes a forward change:
@@ -109,7 +110,8 @@ protected:
/// @brief Validates the contents of the set of events.
///
/// Invokes NameChangeTransaction's implementation and then verifies the
- /// Add transaction's events.
+ /// Add transaction's. This tests that the needed events are in the event
+ /// dictionary.
///
/// @throw StateModelError if an event value is undefined.
virtual void verifyEvents();
@@ -125,7 +127,8 @@ protected:
/// @brief Validates the contents of the set of states.
///
/// Invokes NameChangeTransaction's implementation and then verifies the
- /// Add transaction's states.
+ /// Add transaction's states. This tests that the needed states are in the
+ /// state dictionary.
///
/// @throw StateModelError if an event value is undefined.
virtual void verifyStates();
@@ -166,7 +169,7 @@ protected:
/// handler simply attempts to select the next server.
///
/// Transitions to:
- /// - ADDING_REV_PTRS_ST with next event of SERVER_SELECTED upon successful
+ /// - ADDING_FWD_ADDRS_ST with next event of SERVER_SELECTED upon successful
/// server selection
///
/// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon
@@ -329,7 +332,7 @@ protected:
/// - PROCESS_TRANS_OK_ST with a next event of UPDATE_OK_EVT upon
/// successful replacement.
///
- /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_OK_EVT If the
+ /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT If the
/// DNS server rejected the update for any reason or the IO completed
/// with an unrecognized status.
///
@@ -365,8 +368,10 @@ protected:
/// @brief State handler for PROCESS_TRANS_FAILED_ST.
///
/// Entered from:
+ /// - SELECTING_FWD_SERVER_ST with a next event of NO_MORE_SERVERS
/// - ADDING_FWD_ADDRS_ST with a next event of UPDATE_FAILED_EVT
/// - REPLACING_FWD_ADDRS_ST with a next event of UPDATE_FAILED_EVT
+ /// - SELECTING_REV_SERVER_ST with a next event of NO_MORE_SERVERS
/// - REPLACING_REV_PTRS_ST with a next event of UPDATE_FAILED_EVT
///
/// Sets the transaction status to indicate failure and ends
@@ -438,7 +443,7 @@ protected:
void buildReplaceRevPtrsRequest();
};
-/// @brief Defines a pointer to a NameChangeTransaction.
+/// @brief Defines a pointer to a NameAddTransaction.
typedef boost::shared_ptr<NameAddTransaction> NameAddTransactionPtr;
} // namespace isc::d2
diff --git a/src/bin/d2/nc_remove.cc b/src/bin/d2/nc_remove.cc
new file mode 100644
index 0000000..d7135bd
--- /dev/null
+++ b/src/bin/d2/nc_remove.cc
@@ -0,0 +1,695 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <d2/d2_log.h>
+#include <d2/d2_cfg_mgr.h>
+#include <d2/nc_remove.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+
+namespace isc {
+namespace d2 {
+
+
+// NameRemoveTransaction states
+const int NameRemoveTransaction::REMOVING_FWD_ADDRS_ST;
+const int NameRemoveTransaction::REMOVING_FWD_RRS_ST;
+const int NameRemoveTransaction::REMOVING_REV_PTRS_ST;
+
+// NameRemoveTransaction events
+// Currently NameRemoveTransaction does not define any events.
+
+NameRemoveTransaction::
+NameRemoveTransaction(IOServicePtr& io_service,
+ dhcp_ddns::NameChangeRequestPtr& ncr,
+ DdnsDomainPtr& forward_domain,
+ DdnsDomainPtr& reverse_domain)
+ : NameChangeTransaction(io_service, ncr, forward_domain, reverse_domain) {
+ if (ncr->getChangeType() != isc::dhcp_ddns::CHG_REMOVE) {
+ isc_throw (NameRemoveTransactionError,
+ "NameRemoveTransaction, request type must be CHG_REMOVE");
+ }
+}
+
+NameRemoveTransaction::~NameRemoveTransaction(){
+}
+
+void
+NameRemoveTransaction::defineEvents() {
+ // Call superclass impl first.
+ NameChangeTransaction::defineEvents();
+
+ // Define NameRemoveTransaction events.
+ // Currently NameRemoveTransaction does not define any events.
+ // defineEvent(TBD_EVENT, "TBD_EVT");
+}
+
+void
+NameRemoveTransaction::verifyEvents() {
+ // Call superclass implementation first to verify its events. These are
+ // events common to all transactions, and they must be defined.
+ // SELECT_SERVER_EVT
+ // SERVER_SELECTED_EVT
+ // SERVER_IO_ERROR_EVT
+ // NO_MORE_SERVERS_EVT
+ // IO_COMPLETED_EVT
+ // UPDATE_OK_EVT
+ // UPDATE_FAILED_EVT
+ NameChangeTransaction::verifyEvents();
+
+ // Verify NameRemoveTransaction events by attempting to fetch them.
+ // Currently NameRemoveTransaction does not define any events.
+ // getEvent(TBD_EVENT);
+}
+
+void
+NameRemoveTransaction::defineStates() {
+ // Call superclass impl first.
+ NameChangeTransaction::defineStates();
+
+ // Define NameRemoveTransaction states.
+ defineState(READY_ST, "READY_ST",
+ boost::bind(&NameRemoveTransaction::readyHandler, this));
+
+ defineState(SELECTING_FWD_SERVER_ST, "SELECTING_FWD_SERVER_ST",
+ boost::bind(&NameRemoveTransaction::selectingFwdServerHandler,
+ this));
+
+ defineState(SELECTING_REV_SERVER_ST, "SELECTING_REV_SERVER_ST",
+ boost::bind(&NameRemoveTransaction::selectingRevServerHandler,
+ this));
+
+ defineState(REMOVING_FWD_ADDRS_ST, "REMOVING_FWD_ADDRS_ST",
+ boost::bind(&NameRemoveTransaction::removingFwdAddrsHandler,
+ this));
+
+ defineState(REMOVING_FWD_RRS_ST, "REMOVING_FWD_RRS_ST",
+ boost::bind(&NameRemoveTransaction::removingFwdRRsHandler,
+ this));
+
+ defineState(REMOVING_REV_PTRS_ST, "REMOVING_REV_PTRS_ST",
+ boost::bind(&NameRemoveTransaction::removingRevPtrsHandler,
+ this));
+
+ defineState(PROCESS_TRANS_OK_ST, "PROCESS_TRANS_OK_ST",
+ boost::bind(&NameRemoveTransaction::processRemoveOkHandler,
+ this));
+
+ defineState(PROCESS_TRANS_FAILED_ST, "PROCESS_TRANS_FAILED_ST",
+ boost::bind(&NameRemoveTransaction::processRemoveFailedHandler,
+ this));
+}
+
+void
+NameRemoveTransaction::verifyStates() {
+ // Call superclass implementation first to verify its states. These are
+ // states common to all transactions, and they must be defined.
+ // READY_ST
+ // SELECTING_FWD_SERVER_ST
+ // SELECTING_REV_SERVER_ST
+ // PROCESS_TRANS_OK_ST
+ // PROCESS_TRANS_FAILED_ST
+ NameChangeTransaction::verifyStates();
+
+ // Verify NameRemoveTransaction states by attempting to fetch them.
+ getState(REMOVING_FWD_ADDRS_ST);
+ getState(REMOVING_FWD_RRS_ST);
+ getState(REMOVING_REV_PTRS_ST);
+}
+
+void
+NameRemoveTransaction::readyHandler() {
+ switch(getNextEvent()) {
+ case START_EVT:
+ if (getForwardDomain()) {
+ // Request includes a forward change, do that first.
+ transition(SELECTING_FWD_SERVER_ST, SELECT_SERVER_EVT);
+ } else {
+ // Reverse change only, transition accordingly.
+ transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT);
+ }
+
+ break;
+ default:
+ // Event is invalid.
+ isc_throw(NameRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+void
+NameRemoveTransaction::selectingFwdServerHandler() {
+ switch(getNextEvent()) {
+ case SELECT_SERVER_EVT:
+ // First time through for this transaction, so initialize server
+ // selection.
+ initServerSelection(getForwardDomain());
+ break;
+ case SERVER_IO_ERROR_EVT:
+ // We failed to communicate with current server. Attempt to select
+ // another one below.
+ break;
+ default:
+ // Event is invalid.
+ isc_throw(NameRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+
+ // Select the next server from the list of forward servers.
+ if (selectNextServer()) {
+ // We have a server to try.
+ transition(REMOVING_FWD_ADDRS_ST, SERVER_SELECTED_EVT);
+ }
+ else {
+ // Server list is exhausted, so fail the transaction.
+ transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT);
+ }
+}
+
+void
+NameRemoveTransaction::removingFwdAddrsHandler() {
+ if (doOnEntry()) {
+ // Clear the request on initial transition. This allows us to reuse
+ // the request on retries if necessary.
+ clearDnsUpdateRequest();
+ }
+
+ switch(getNextEvent()) {
+ case SERVER_SELECTED_EVT:
+ if (!getDnsUpdateRequest()) {
+ // Request hasn't been constructed yet, so build it.
+ try {
+ buildRemoveFwdAddressRequest();
+ } catch (const std::exception& ex) {
+ // While unlikely, the build might fail if we have invalid
+ // data. Should that be the case, we need to fail the
+ // transaction.
+ LOG_ERROR(dctl_logger,
+ DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE)
+ .arg(getNcr()->toText())
+ .arg(ex.what());
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ break;
+ }
+ }
+
+ // Call sendUpdate() to initiate the async send. Note it also sets
+ // next event to NOP_EVT.
+ sendUpdate();
+ break;
+
+ case IO_COMPLETED_EVT: {
+ switch (getDnsUpdateStatus()) {
+ case DNSClient::SUCCESS: {
+ // We successfully received a response packet from the server.
+ const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
+ if ((rcode == dns::Rcode::NOERROR()) ||
+ (rcode == dns::Rcode::NXDOMAIN())) {
+ // We were able to remove it or it wasn't there, now we
+ // need to remove any other RRs for this FQDN.
+ transition(REMOVING_FWD_RRS_ST, UPDATE_OK_EVT);
+ } else {
+ // Per RFC4703 any other value means cease.
+ // If we get not authorized should we try the next server in
+ // the list? @todo This needs some discussion perhaps.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED)
+ .arg(getCurrentServer()->getIpAddress())
+ .arg(getNcr()->getFqdn())
+ .arg(rcode.getCode());
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ }
+
+ break;
+ }
+
+ case DNSClient::TIMEOUT:
+ case DNSClient::OTHER:
+ // We couldn't send to the current server, log it and set up
+ // to select the next server for a retry.
+ // @note For now we treat OTHER as an IO error like TIMEOUT. It
+ // is not entirely clear if this is accurate.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR)
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->getIpAddress());
+
+ retryTransition(SELECTING_FWD_SERVER_ST);
+ break;
+
+ case DNSClient::INVALID_RESPONSE:
+ // A response was received but was corrupt. Retry it like an IO
+ // error.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT)
+ .arg(getCurrentServer()->getIpAddress())
+ .arg(getNcr()->getFqdn());
+
+ retryTransition(SELECTING_FWD_SERVER_ST);
+ break;
+
+ default:
+ // Any other value and we will fail this transaction, something
+ // bigger is wrong.
+ LOG_ERROR(dctl_logger,
+ DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS)
+ .arg(getDnsUpdateStatus())
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->getIpAddress());
+
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ break;
+ } // end switch on dns_status
+
+ break;
+ } // end case IO_COMPLETE_EVT
+
+ default:
+ // Event is invalid.
+ isc_throw(NameRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+
+void
+NameRemoveTransaction::removingFwdRRsHandler() {
+ if (doOnEntry()) {
+ // Clear the request on initial transition. This allows us to reuse
+ // the request on retries if necessary.
+ clearDnsUpdateRequest();
+ }
+
+ switch(getNextEvent()) {
+ case UPDATE_OK_EVT:
+ case SERVER_SELECTED_EVT:
+ if (!getDnsUpdateRequest()) {
+ // Request hasn't been constructed yet, so build it.
+ try {
+ buildRemoveFwdRRsRequest();
+ } catch (const std::exception& ex) {
+ // While unlikely, the build might fail if we have invalid
+ // data. Should that be the case, we need to fail the
+ // transaction.
+ LOG_ERROR(dctl_logger,
+ DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE)
+ .arg(getNcr()->toText())
+ .arg(ex.what());
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ break;
+ }
+ }
+
+ // Call sendUpdate() to initiate the async send. Note it also sets
+ // next event to NOP_EVT.
+ sendUpdate();
+ break;
+
+ case IO_COMPLETED_EVT: {
+ switch (getDnsUpdateStatus()) {
+ case DNSClient::SUCCESS: {
+ // We successfully received a response packet from the server.
+ const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
+ // @todo Not sure if NXDOMAIN is ok here, but I think so.
+ // A Rcode of NXDOMAIN would mean there are no RRs for the FQDN,
+ // which is fine. We were asked to delete them, they are not there
+ // so all is well.
+ if ((rcode == dns::Rcode::NOERROR()) ||
+ (rcode == dns::Rcode::NXDOMAIN())) {
+ // We were able to remove the forward mapping. Mark it as done.
+ setForwardChangeCompleted(true);
+
+ // If request calls for reverse update then do that next,
+ // otherwise we can process ok.
+ if (getReverseDomain()) {
+ transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT);
+ } else {
+ transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT);
+ }
+ } else {
+ // Per RFC4703 any other value means cease.
+ // If we get not authorized should try the next server in
+ // the list? @todo This needs some discussion perhaps.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED)
+ .arg(getCurrentServer()->getIpAddress())
+ .arg(getNcr()->getFqdn())
+ .arg(rcode.getCode());
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ }
+
+ break;
+ }
+
+ case DNSClient::TIMEOUT:
+ case DNSClient::OTHER:
+ // We couldn't send to the current server, log it and set up
+ // to select the next server for a retry.
+ // @note For now we treat OTHER as an IO error like TIMEOUT. It
+ // is not entirely clear if this is accurate.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR)
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->getIpAddress());
+
+ // @note If we exhaust the IO retries for the current server
+ // due to IO failures, we will abort the remaining updates.
+ // The rational is that we are only in this state, if the remove
+ // of the forward address RR succeeded (removingFwdAddrsHandler)
+ // on the current server. Therefore we should not attempt another
+ // removal on a different server. This is perhaps a point
+ // for discussion.
+ // @todo Should we go ahead with the reverse remove?
+ retryTransition(PROCESS_TRANS_FAILED_ST);
+ break;
+
+ case DNSClient::INVALID_RESPONSE:
+ // A response was received but was corrupt. Retry it like an IO
+ // error.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT)
+ .arg(getCurrentServer()->getIpAddress())
+ .arg(getNcr()->getFqdn());
+
+ // If we are out of retries on this server abandon the transaction.
+ // (Same logic as the case for TIMEOUT above).
+ retryTransition(PROCESS_TRANS_FAILED_ST);
+ break;
+
+ default:
+ // Any other value and we will fail this transaction, something
+ // bigger is wrong.
+ LOG_ERROR(dctl_logger,
+ DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS)
+ .arg(getDnsUpdateStatus())
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->getIpAddress());
+
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ break;
+ } // end switch on dns_status
+
+ break;
+ } // end case IO_COMPLETE_EVT
+
+ default:
+ // Event is invalid.
+ isc_throw(NameRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+
+void
+NameRemoveTransaction::selectingRevServerHandler() {
+ switch(getNextEvent()) {
+ case SELECT_SERVER_EVT:
+ // First time through for this transaction, so initialize server
+ // selection.
+ initServerSelection(getReverseDomain());
+ break;
+ case SERVER_IO_ERROR_EVT:
+ // We failed to communicate with current server. Attempt to select
+ // another one below.
+ break;
+ default:
+ // Event is invalid.
+ isc_throw(NameRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+
+ // Select the next server from the list of forward servers.
+ if (selectNextServer()) {
+ // We have a server to try.
+ transition(REMOVING_REV_PTRS_ST, SERVER_SELECTED_EVT);
+ }
+ else {
+ // Server list is exhausted, so fail the transaction.
+ transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT);
+ }
+}
+
+
+void
+NameRemoveTransaction::removingRevPtrsHandler() {
+ if (doOnEntry()) {
+ // Clear the request on initial transition. This allows us to reuse
+ // the request on retries if necessary.
+ clearDnsUpdateRequest();
+ }
+
+ switch(getNextEvent()) {
+ case SERVER_SELECTED_EVT:
+ if (!getDnsUpdateRequest()) {
+ // Request hasn't been constructed yet, so build it.
+ try {
+ buildRemoveRevPtrsRequest();
+ } catch (const std::exception& ex) {
+ // While unlikely, the build might fail if we have invalid
+ // data. Should that be the case, we need to fail the
+ // transaction.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE)
+ .arg(getNcr()->toText())
+ .arg(ex.what());
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ break;
+ }
+ }
+
+ // Call sendUpdate() to initiate the async send. Note it also sets
+ // next event to NOP_EVT.
+ sendUpdate();
+ break;
+
+ case IO_COMPLETED_EVT: {
+ switch (getDnsUpdateStatus()) {
+ case DNSClient::SUCCESS: {
+ // We successfully received a response packet from the server.
+ const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
+ if ((rcode == dns::Rcode::NOERROR()) ||
+ (rcode == dns::Rcode::NXDOMAIN())) {
+ // We were able to update the reverse mapping. Mark it as done.
+ // @todo For now we are also treating NXDOMAIN as success.
+ setReverseChangeCompleted(true);
+ transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT);
+ } else {
+ // Per RFC4703 any other value means cease.
+ // If we get not authorized should try the next server in
+ // the list? @todo This needs some discussion perhaps.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REMOVE_REJECTED)
+ .arg(getCurrentServer()->getIpAddress())
+ .arg(getNcr()->getFqdn())
+ .arg(rcode.getCode());
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ }
+
+ break;
+ }
+
+ case DNSClient::TIMEOUT:
+ case DNSClient::OTHER:
+ // We couldn't send to the current server, log it and set up
+ // to select the next server for a retry.
+ // @note For now we treat OTHER as an IO error like TIMEOUT. It
+ // is not entirely clear if this is accurate.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REMOVE_IO_ERROR)
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->getIpAddress());
+
+ // If we are out of retries on this server, we go back and start
+ // all over on a new server.
+ retryTransition(SELECTING_REV_SERVER_ST);
+ break;
+
+ case DNSClient::INVALID_RESPONSE:
+ // A response was received but was corrupt. Retry it like an IO
+ // error.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT)
+ .arg(getCurrentServer()->getIpAddress())
+ .arg(getNcr()->getFqdn());
+
+ // If we are out of retries on this server, we go back and start
+ // all over on a new server.
+ retryTransition(SELECTING_REV_SERVER_ST);
+ break;
+
+ default:
+ // Any other value and we will fail this transaction, something
+ // bigger is wrong.
+ LOG_ERROR(dctl_logger,
+ DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS)
+ .arg(getDnsUpdateStatus())
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->getIpAddress());
+
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ break;
+ } // end switch on dns_status
+
+ break;
+ } // end case IO_COMPLETE_EVT
+
+ default:
+ // Event is invalid.
+ isc_throw(NameRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+
+void
+NameRemoveTransaction::processRemoveOkHandler() {
+ switch(getNextEvent()) {
+ case UPDATE_OK_EVT:
+ LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL, DHCP_DDNS_REMOVE_SUCCEEDED)
+ .arg(getNcr()->toText());
+ setNcrStatus(dhcp_ddns::ST_COMPLETED);
+ endModel();
+ break;
+ default:
+ // Event is invalid.
+ isc_throw(NameRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+void
+NameRemoveTransaction::processRemoveFailedHandler() {
+ switch(getNextEvent()) {
+ case UPDATE_FAILED_EVT:
+ case NO_MORE_SERVERS_EVT:
+ case SERVER_IO_ERROR_EVT:
+ LOG_ERROR(dctl_logger, DHCP_DDNS_REMOVE_FAILED).arg(getNcr()->toText())
+ .arg(getEventLabel(getNextEvent()));
+ setNcrStatus(dhcp_ddns::ST_FAILED);
+ endModel();
+ break;
+ default:
+ // Event is invalid.
+ isc_throw(NameRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+void
+NameRemoveTransaction::buildRemoveFwdAddressRequest() {
+ // Construct an empty request.
+ D2UpdateMessagePtr request = prepNewRequest(getForwardDomain());
+
+ // Content on this request is based on RFC 4703, section 5.5, paragraph 4.
+ // Construct dns::Name from NCR fqdn.
+ dns::Name fqdn(dns::Name(getNcr()->getFqdn()));
+ // First build the Prerequisite Section
+
+ // Create an DHCID matches prerequisite RR and add it to the
+ // pre-requisite section
+ // Based on RFC 2136, section 2.4.2.
+ dns::RRsetPtr prereq(new dns::RRset(fqdn, dns::RRClass::IN(),
+ dns::RRType::DHCID(), dns::RRTTL(0)));
+ addDhcidRdata(prereq);
+ request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
+
+ // Next build the Update Section
+
+ // Create the FQDN/IP 'delete' RR and add it to the update section.
+ // Add the RR to update section.
+ // Based on 2136 section 2.5.4
+ dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::NONE(),
+ getAddressRRType(), dns::RRTTL(0)));
+ addLeaseAddressRdata(update);
+ request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+ // Set the transaction's update request to the new request.
+ setDnsUpdateRequest(request);
+}
+
+void
+NameRemoveTransaction::buildRemoveFwdRRsRequest() {
+ // Construct an empty request.
+ D2UpdateMessagePtr request = prepNewRequest(getForwardDomain());
+
+ // Construct dns::Name from NCR fqdn.
+ dns::Name fqdn(dns::Name(getNcr()->getFqdn()));
+
+ // Content on this request is based on RFC 4703, section 5.5, paragraph 5.
+ // First build the Prerequisite Section.
+
+ // Now create an DHCID matches prerequisite RR.
+ // Set the RR's RData to DHCID.
+ // Add it to the pre-requisite section.
+ // Based on RFC 2136, section 2.4.2.
+ dns::RRsetPtr prereq(new dns::RRset(fqdn, dns::RRClass::IN(),
+ dns::RRType::DHCID(), dns::RRTTL(0)));
+ addDhcidRdata(prereq);
+ request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
+
+ // Create an assertion that there are no A RRs for the FQDN.
+ // Add it to the pre-reqs.
+ // Based on RFC 2136, section 2.4.3.
+ prereq.reset(new dns::RRset(fqdn, dns::RRClass::NONE(),
+ dns::RRType::A(), dns::RRTTL(0)));
+ request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
+
+ // Create an assertion that there are no A RRs for the FQDN.
+ // Add it to the pre-reqs.
+ // Based on RFC 2136, section 2.4.3.
+ prereq.reset(new dns::RRset(fqdn, dns::RRClass::NONE(),
+ dns::RRType::AAAA(), dns::RRTTL(0)));
+ request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
+
+ // Next build the Update Section.
+
+ // Create the 'delete' of all RRs for FQDN.
+ // Set the message RData to lease address.
+ // Add the RR to update section.
+ // Based on RFC 2136, section 2.5.3.
+ dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::ANY(),
+ dns::RRType::ANY(), dns::RRTTL(0)));
+ request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+ // Set the transaction's update request to the new request.
+ setDnsUpdateRequest(request);
+}
+
+void
+NameRemoveTransaction::buildRemoveRevPtrsRequest() {
+ // Construct an empty request.
+ D2UpdateMessagePtr request = prepNewRequest(getReverseDomain());
+
+ // Create the reverse IP address "FQDN".
+ std::string rev_addr = D2CfgMgr::reverseIpAddress(getNcr()->getIpAddress());
+ dns::Name rev_ip(rev_addr);
+
+ // Content on this request is based on RFC 4703, section 5.5, paragraph 2.
+ // First build the Prerequisite Section.
+ // (Note that per RFC 4703, section 5.4, there is no need to validate
+ // DHCID RR for PTR entries.)
+
+ // Create an assertion that the PTRDNAME in the PTR record matches the
+ // client's FQDN for the address that was released.
+ // Based on RFC 2136, section 3.2.3
+ dns::RRsetPtr prereq(new dns::RRset(rev_ip, dns::RRClass::IN(),
+ dns::RRType::PTR(), dns::RRTTL(0)));
+ addPtrRdata(prereq);
+ request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
+
+ // Now, build the Update section.
+
+ // Create a delete of any RRs for the FQDN and add it to update section.
+ // Based on RFC 2136, section 3.4.2.3
+ dns::RRsetPtr update(new dns::RRset(rev_ip, dns::RRClass::ANY(),
+ dns::RRType::ANY(), dns::RRTTL(0)));
+ request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+ // Set the transaction's update request to the new request.
+ setDnsUpdateRequest(request);
+}
+
+} // namespace isc::d2
+} // namespace isc
diff --git a/src/bin/d2/nc_remove.h b/src/bin/d2/nc_remove.h
new file mode 100644
index 0000000..f7b0dcc
--- /dev/null
+++ b/src/bin/d2/nc_remove.h
@@ -0,0 +1,435 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef NC_REMOVE_H
+#define NC_REMOVE_H
+
+/// @file nc_remove.h This file defines the class NameRemoveTransaction.
+
+#include <d2/nc_trans.h>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Thrown if the NameRemoveTransaction encounters a general error.
+class NameRemoveTransactionError : public isc::Exception {
+public:
+ NameRemoveTransactionError(const char* file, size_t line,
+ const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Embodies the "life-cycle" required to carry out a DDNS Remove update.
+///
+/// NameRemoveTransaction implements a state machine for removing a forward
+/// and/or reverse DNS mappings. This state machine is based upon the processing
+/// logic described in RFC 4703, Section 5.5. That logic may be paraphrased as
+/// follows:
+///
+/// @code
+///
+/// If the request includes a forward change:
+/// Select a forward server
+/// Send the server a request to remove client's specific forward address RR
+/// If it succeeds or the server responds with name no longer in use
+/// Send a server a request to delete any other RRs for that FQDN, such
+/// as the DHCID RR.
+/// otherwise
+/// abandon the update
+///
+/// If the request includes a reverse change:
+/// Select a reverse server
+/// Send a server a request to delete reverse entry (PTR RR)
+///
+/// @endcode
+///
+/// This class derives from NameChangeTransaction from which it inherits
+/// states, events, and methods common to NameChangeRequest processing.
+class NameRemoveTransaction : public NameChangeTransaction {
+public:
+
+ //@{ Additional states needed for NameRemove state model.
+ /// @brief State that attempts to remove specific forward address record.
+ static const int REMOVING_FWD_ADDRS_ST = NCT_DERIVED_STATE_MIN + 1;
+
+ /// @brief State that attempts to remove any other forward RRs for the DHCID
+ static const int REMOVING_FWD_RRS_ST = NCT_DERIVED_STATE_MIN + 2;
+
+ /// @brief State that attempts to remove reverse PTR records
+ static const int REMOVING_REV_PTRS_ST = NCT_DERIVED_STATE_MIN + 3;
+ //@}
+
+ //@{ Additional events needed for NameRemove state model.
+ /// @brief Event sent when replace attempt to fails with address not in use.
+ /// @todo Currently none have been identified.
+ //@}
+
+ /// @brief Constructor
+ ///
+ /// Instantiates an Remove transaction that is ready to be started.
+ ///
+ /// @param io_service IO service to be used for IO processing
+ /// @param ncr is the NameChangeRequest to fulfill
+ /// @param forward_domain is the domain to use for forward DNS updates
+ /// @param reverse_domain is the domain to use for reverse DNS updates
+ ///
+ /// @throw NameRemoveTransaction error if given request is not a CHG_REMOVE,
+ /// NameChangeTransaction error for base class construction errors.
+ NameRemoveTransaction(IOServicePtr& io_service,
+ dhcp_ddns::NameChangeRequestPtr& ncr,
+ DdnsDomainPtr& forward_domain,
+ DdnsDomainPtr& reverse_domain);
+
+ /// @brief Destructor
+ virtual ~NameRemoveTransaction();
+
+protected:
+ /// @brief Adds events defined by NameRemoveTransaction to the event set.
+ ///
+ /// Invokes NameChangeTransaction's implementation and then defines the
+ /// events unique to NCR Remove transaction processing.
+ ///
+ /// @throw StateModelError if an event definition is invalid or a duplicate.
+ virtual void defineEvents();
+
+ /// @brief Validates the contents of the set of events.
+ ///
+ /// Invokes NameChangeTransaction's implementation and then verifies the
+ /// Remove transaction's events. This tests that the needed events are in
+ /// the event dictionary.
+ ///
+ /// @throw StateModelError if an event value is undefined.
+ virtual void verifyEvents();
+
+ /// @brief Adds states defined by NameRemoveTransaction to the state set.
+ ///
+ /// Invokes NameChangeTransaction's implementation and then defines the
+ /// states unique to NCR Remove transaction processing.
+ ///
+ /// @throw StateModelError if an state definition is invalid or a duplicate.
+ virtual void defineStates();
+
+ /// @brief Validates the contents of the set of states.
+ ///
+ /// Invokes NameChangeTransaction's implementation and then verifies the
+ /// Remove transaction's states. This tests that the needed states are in
+ /// the state dictionary.
+ ///
+ /// @throw StateModelError if an event value is undefined.
+ virtual void verifyStates();
+
+ /// @brief State handler for READY_ST.
+ ///
+ /// Entered from:
+ /// - INIT_ST with next event of START_EVT
+ ///
+ /// The READY_ST is the state the model transitions into when the inherited
+ /// method, startTransaction() is invoked. This handler, therefore, is the
+ /// entry point into the state model execution. Its primary task is to
+ /// determine whether to start with a forward DNS change or a reverse DNS
+ /// change.
+ ///
+ /// Transitions to:
+ /// - SELECTING_FWD_SERVER_ST with next event of SERVER_SELECT_ST if request
+ /// includes a forward change.
+ ///
+ /// - SELECTING_REV_SERVER_ST with next event of SERVER_SELECT_ST if request
+ /// includes only a reverse change.
+ ///
+ /// @throw NameRemoveTransactionError if upon entry next event is not
+ /// START_EVT.
+ void readyHandler();
+
+ /// @brief State handler for SELECTING_FWD_SERVER_ST.
+ ///
+ /// Entered from:
+ /// - READY_ST with next event of SELECT_SERVER_EVT
+ /// - REMOVING_FWD_ADDRS_ST with next event of SERVER_IO_ERROR_EVT
+ ///
+ /// Selects the server to be used from the forward domain for the forward
+ /// DNS update. If next event is SELECT_SERVER_EVT the handler initializes
+ /// the forward domain's server selection mechanism and then attempts to
+ /// select the next server. If next event is SERVER_IO_ERROR_EVT then the
+ /// handler simply attempts to select the next server.
+ ///
+ /// Transitions to:
+ /// - REMOVING_FWD_ADDRS_ST with next event of SERVER_SELECTED upon
+ /// successful server selection
+ ///
+ /// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon
+ /// failure to select a server
+ ///
+ /// @throw NameRemoveTransactionError if upon entry next event is not
+ /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT.
+ void selectingFwdServerHandler();
+
+ /// @brief State handler for SELECTING_REV_SERVER_ST.
+ ///
+ /// Entered from:
+ /// - READY_ST with next event of SELECT_SERVER_EVT
+ /// - REMOVING_FWD_RRS_ST with next event of SELECT_SERVER_EVT
+ /// - REMOVING_REV_PTRS_ST with next event of SERVER_IO_ERROR_EVT
+ ///
+ /// Selects the server to be used from the reverse domain for the reverse
+ /// DNS update. If next event is SELECT_SERVER_EVT the handler initializes
+ /// the reverse domain's server selection mechanism and then attempts to
+ /// select the next server. If next event is SERVER_IO_ERROR_EVT then the
+ /// handler simply attempts to select the next server.
+ ///
+ /// Transitions to:
+ /// - REMOVING_REV_PTRS_ST with next event of SERVER_SELECTED upon
+ /// successful server selection
+ ///
+ /// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon
+ /// failure to select a server
+ ///
+ /// @throw NameRemoveTransactionError if upon entry next event is not
+ /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT.
+ void selectingRevServerHandler();
+
+ /// @brief State handler for REMOVING_FWD_ADDRS_ST.
+ ///
+ /// Entered from:
+ /// - SELECTING_FWD_SERVER with next event of SERVER_SELECTED_EVT
+ ///
+ /// Attempts to remove the forward DNS entry for a given FQDN, provided
+ /// a DHCID RR exists which matches the requesting DHCID. If this is
+ /// first invocation of the handler after transitioning into this state,
+ /// any previous update request context is deleted. If next event
+ /// is SERVER_SELECTED_EVT, the handler builds the forward remove request,
+ /// schedules an asynchronous send via sendUpdate(), and returns. Note
+ /// that sendUpdate will post NOP_EVT as next event.
+ ///
+ /// Posting the NOP_EVT will cause runModel() to suspend execution of
+ /// the state model thus affecting a "wait" for the update IO to complete.
+ /// Update completion occurs via the DNSClient callback operator() method
+ /// inherited from NameChangeTransaction. When invoked this callback will
+ /// post a next event of IO_COMPLETED_EVT and then invoke runModel which
+ /// resumes execution of the state model.
+ ///
+ /// When the handler is invoked with a next event of IO_COMPELTED_EVT,
+ /// the DNS update status is checked and acted upon accordingly:
+ ///
+ /// Transitions to:
+ /// - REMOVING_FWD_RRS_ST with next event of UPDATE_OK_EVT upon successful
+ /// removal or RCODE of indication FQDN is no longer in use (NXDOMAIN).
+ ///
+ /// - PROCESS_TRANS_FAILED_ST with next event of UPDATE_FAILED_EVT if the
+ /// DNS server rejected the update for any reason or the IO completed
+ /// with an unrecognized status.
+ ///
+ /// - RE-ENTER this state with next event of SERVER_SELECTED_EVT if
+ /// there was an IO error communicating with the server and the number of
+ /// per server retries has not been exhausted.
+ ///
+ /// - SELECTING_FWD_SERVER_ST with next event of SERVER_IO_ERROR_EVT if
+ /// there was an IO error communicating with the server and the number of
+ /// per server retries has been exhausted.
+ ///
+ /// @throw NameRemoveTransactionError if upon entry next event is not
+ /// SERVER_SELECTED_EVT or IO_COMPLETE_EVT
+ void removingFwdAddrsHandler();
+
+ /// @brief State handler for REMOVING_FWD_RRS_ST.
+ ///
+ /// Entered from:
+ /// - REMOVING_FWD_ADDRS_ST with next event of UPDATE_OK_EVT
+ ///
+ /// Attempts to delete any remaining RRs associated with the given FQDN
+ /// such as the DHCID RR. If this is first invocation of the handler after
+ /// transitioning into this state, any previous update request context is
+ /// deleted and the handler builds the forward remove request. It then
+ /// schedules an asynchronous send via sendUpdate(),
+ /// and returns. Note that sendUpdate will post NOP_EVT as the next event.
+ ///
+ /// Posting the NOP_EVT will cause runModel() to suspend execution of
+ /// the state model thus affecting a "wait" for the update IO to complete.
+ /// Update completion occurs via the DNSClient callback operator() method
+ /// inherited from NameChangeTransaction. When invoked this callback will
+ /// post a next event of IO_COMPLETED_EVT and then invoke runModel which
+ /// resumes execution of the state model.
+ ///
+ /// When the handler is invoked with a next event of IO_COMPELTED_EVT,
+ /// the DNS update status is checked and acted upon accordingly:
+ ///
+ /// Transitions to:
+ /// - SELECTING_REV_SERVER_ST with a next event of SELECT_SERVER_EVT upon
+ /// successful completion and the request includes a reverse DNS update.
+ ///
+ /// - PROCESS_TRANS_OK_ST with next event of UPDATE_OK_EVT upon successful
+ /// completion and the request does not include a reverse DNS update.
+ ///
+ /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT if the
+ /// DNS server rejected the update for any other reason or the IO completed
+ /// with an unrecognized status.
+ ///
+ /// - RE-ENTER this state with a next event of SERVER_SELECTED_EVT if
+ /// there was an IO error communicating with the server and the number of
+ /// per server retries has not been exhausted.
+ ///
+ /// - PROCESS_TRANS_FAILED_ST with a next event of SERVER_IO_ERROR_EVT if
+ /// there we have reached maximum number of retries without success on the
+ /// current server.
+ ///
+ /// @note If we exhaust the IO retries for the current server due to IO
+ /// failures, we will abort the remaining updates. The rational is that
+ /// we are only in this state, if the remove of the forward address RR
+ /// succeeded (removingFwdAddrsHandler) on the current server so we should
+ /// not attempt another removal on a different server. This is perhaps a
+ /// point for discussion. @todo Should we go ahead with the reverse remove?
+ ///
+ /// @throw NameRemoveTransactionError if upon entry next event is not:
+ /// UPDATE_OK_EVT or IO_COMPLETE_EVT
+ void removingFwdRRsHandler();
+
+ /// @brief State handler for REMOVING_REV_PTRS_ST.
+ ///
+ /// Entered from:
+ /// - SELECTING_REV_SERVER_ST with a next event of SERVER_SELECTED_EVT
+ ///
+ /// Attempts to delete a reverse DNS entry for a given FQDN. If this is
+ /// first invocation of the handler after transitioning into this state,
+ /// any previous update request context is deleted. If next event is
+ /// SERVER_SELECTED_EVT, the handler builds the reverse remove request,
+ /// schedules an asynchronous send via sendUpdate(), and then returns.
+ /// Note that sendUpdate will post NOP_EVT as next event.
+ ///
+ /// Posting the NOP_EVT will cause runModel() to suspend execution of
+ /// the state model thus affecting a "wait" for the update IO to complete.
+ /// Update completion occurs via the DNSClient callback operator() method
+ /// inherited from NameChangeTransaction. When invoked this callback will
+ /// post a next event of IO_COMPLETED_EVT and then invoke runModel which
+ /// resumes execution of the state model.
+ ///
+ /// When the handler is invoked with a next event of IO_COMPELTED_EVT,
+ /// the DNS update status is checked and acted upon accordingly:
+ ///
+ /// Transitions to:
+ /// - PROCESS_TRANS_OK_ST with a next event of UPDATE_OK_EVT upon
+ /// successful completion.
+ ///
+ /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT If the
+ /// DNS server rejected the update for any reason or the IO completed
+ /// with an unrecognized status.
+ ///
+ /// - RE-ENTER this state with a next event of SERVER_SELECTED_EVT if
+ /// there was an IO error communicating with the server and the number of
+ /// per server retries has not been exhausted.
+ ///
+ /// - SELECTING_REV_SERVER_ST with next event of SERVER_IO_ERROR_EVT if
+ /// there was an IO error communicating with the server and the number of
+ /// per server retries has been exhausted.
+ ///
+ /// @throw NameRemoveTransactionError if upon entry next event is not:
+ /// SERVER_SELECTED_EVT or IO_COMPLETED_EVT
+ void removingRevPtrsHandler();
+
+ /// @brief State handler for PROCESS_TRANS_OK_ST.
+ ///
+ /// Entered from:
+ /// - REMOVING_FWD_RRS_ST with a next event of UPDATE_OK_EVT
+ /// - REMOVING_REV_PTRS_ST with a next event of UPDATE_OK_EVT
+ ///
+ /// Sets the transaction action status to indicate success and ends
+ /// model execution.
+ ///
+ /// Transitions to:
+ /// - END_ST with a next event of END_EVT.
+ ///
+ /// @throw NameRemoveTransactionError if upon entry next event is not:
+ /// UPDATE_OK_EVT
+ void processRemoveOkHandler();
+
+ /// @brief State handler for PROCESS_TRANS_FAILED_ST.
+ ///
+ /// Entered from:
+ /// - SELECTING_FWD_SERVER_ST with a next event of NO_MORE_SERVERS
+ /// - REMOVING_FWD_ADDRS_ST with a next event of UPDATE_FAILED_EVT
+ /// - REMOVING_FWD_RRS_ST with a next event of UPDATE_FAILED_EVT
+ /// - REMOVING_FWD_RRS_ST with a next event of SERVER_IO_ERROR_EVT
+ /// - SELECTING_REV_SERVER_ST with a next event of NO_MORE_SERVERS
+ /// - REMOVING_REV_PTRS_ST with a next event of UPDATE_FAILED_EVT
+ ///
+ /// Sets the transaction status to indicate failure and ends
+ /// model execution.
+ ///
+ /// Transitions to:
+ /// - END_ST with a next event of FAIL_EVT.
+ ///
+ /// @throw NameRemoveTransactionError if upon entry next event is not:
+ /// UPDATE_FAILED_EVT
+ void processRemoveFailedHandler();
+
+ /// @brief Builds a DNS request to remove a forward DNS address for a FQDN.
+ ///
+ /// Constructs a DNS update request, based upon the NCR, for removing a
+ /// forward DNS address mapping. Once constructed, the request is stored as
+ /// the transaction's DNS update request.
+ ///
+ /// The request content is adherent to RFC 4703 section 5.5, paragraph 4.
+ ///
+ /// Prerequisite RRsets:
+ /// 1. An assertion that a matching DHCID RR exists
+ ///
+ /// Updates RRsets:
+ /// 1. A delete of the FQDN/IP RR (type A for IPv4, AAAA for IPv6)
+ ///
+ /// @throw This method does not throw but underlying methods may.
+ void buildRemoveFwdAddressRequest();
+
+ /// @brief Builds a DNS request to remove all forward DNS RRs for a FQDN.
+ ///
+ /// Constructs a DNS update request, based upon the NCR, for removing any
+ /// remaining forward DNS RRs, once all A or AAAA entries for the FQDN
+ /// have been removed. Once constructed, the request is stored as the
+ /// transaction's DNS update request.
+ ///
+ /// The request content is adherent to RFC 4703 section 5.5, paragraph 5.
+ ///
+ /// Prerequisite RRsets:
+ /// 1. An assertion that a matching DHCID RR exists
+ /// 2. An assertion that no A RRs for the FQDN exist
+ /// 3. An assertion that no AAAA RRs for the FQDN exist
+ ///
+ /// Updates RRsets:
+ /// 1. A delete of all RRs for the FQDN
+ ///
+ /// @throw This method does not throw but underlying methods may.
+ void buildRemoveFwdRRsRequest();
+
+ /// @brief Builds a DNS request to remove a reverse DNS entry for a FQDN
+ ///
+ /// Constructs a DNS update request, based upon the NCR, for removing a
+ /// reverse DNS mapping. Once constructed, the request is stored as
+ /// the transaction's DNS update request.
+ ///
+ /// The request content is adherent to RFC 4703 section 5.5, paragraph 2:
+ ///
+ /// Prerequisite RRsets:
+ /// 1. An assertion that a PTR record matching the client's FQDN exists.
+ ///
+ /// Updates RRsets:
+ /// 1. A delete of all RRs for the FQDN
+ ///
+ /// @throw This method does not throw but underlying methods may.
+ void buildRemoveRevPtrsRequest();
+};
+
+/// @brief Defines a pointer to a NameRemoveTransaction.
+typedef boost::shared_ptr<NameRemoveTransaction> NameRemoveTransactionPtr;
+
+
+} // namespace isc::d2
+} // namespace isc
+#endif
diff --git a/src/bin/d2/nc_trans.cc b/src/bin/d2/nc_trans.cc
index 3c604d2..1ad9b93 100644
--- a/src/bin/d2/nc_trans.cc
+++ b/src/bin/d2/nc_trans.cc
@@ -14,6 +14,7 @@
#include <d2/d2_log.h>
#include <d2/nc_trans.h>
+#include <dns/rdata.h>
namespace isc {
namespace d2 {
@@ -181,14 +182,14 @@ NameChangeTransaction::onModelFailure(const std::string& explanation) {
}
void
-NameChangeTransaction::retryTransition(const int server_sel_state) {
+NameChangeTransaction::retryTransition(const int fail_to_state) {
if (update_attempts_ < MAX_UPDATE_TRIES_PER_SERVER) {
// Re-enter the current state with same server selected.
transition(getCurrState(), SERVER_SELECTED_EVT);
} else {
- // Transition to given server selection state if we are out
+ // Transition to given fail_to_state state if we are out
// of retries.
- transition(server_sel_state, SERVER_IO_ERROR_EVT);
+ transition(fail_to_state, SERVER_IO_ERROR_EVT);
}
}
@@ -262,13 +263,13 @@ NameChangeTransaction::addLeaseAddressRdata(dns::RRsetPtr& rrset) {
try {
// Manufacture an RData from the lease address then add it to the RR.
+ dns::rdata::ConstRdataPtr rdata;
if (ncr_->isV4()) {
- dns::rdata::in::A a_rdata(ncr_->getIpAddress());
- rrset->addRdata(a_rdata);
+ rdata.reset(new dns::rdata::in::A(ncr_->getIpAddress()));
} else {
- dns::rdata::in::AAAA rdata(ncr_->getIpAddress());
- rrset->addRdata(rdata);
+ rdata.reset(new dns::rdata::in::AAAA(ncr_->getIpAddress()));
}
+ rrset->addRdata(rdata);
} catch (const std::exception& ex) {
isc_throw(NameChangeTransactionError, "Cannot add address rdata: "
<< ex.what());
@@ -285,12 +286,14 @@ NameChangeTransaction::addDhcidRdata(dns::RRsetPtr& rrset) {
try {
const std::vector<uint8_t>& ncr_dhcid = ncr_->getDhcid().getBytes();
util::InputBuffer buffer(ncr_dhcid.data(), ncr_dhcid.size());
- dns::rdata::in::DHCID rdata(buffer, ncr_dhcid.size());
+ dns::rdata::ConstRdataPtr rdata (new dns::rdata::in::
+ DHCID(buffer, ncr_dhcid.size()));
rrset->addRdata(rdata);
} catch (const std::exception& ex) {
isc_throw(NameChangeTransactionError, "Cannot add DCHID rdata: "
<< ex.what());
}
+
}
void
@@ -301,7 +304,8 @@ NameChangeTransaction::addPtrRdata(dns::RRsetPtr& rrset) {
}
try {
- dns::rdata::generic::PTR rdata(getNcr()->getFqdn());
+ dns::rdata::ConstRdataPtr rdata(new dns::rdata::generic::
+ PTR(getNcr()->getFqdn()));
rrset->addRdata(rdata);
} catch (const std::exception& ex) {
isc_throw(NameChangeTransactionError, "Cannot add PTR rdata: "
@@ -412,7 +416,7 @@ NameChangeTransaction::getUpdateAttempts() const {
const dns::RRType&
NameChangeTransaction::getAddressRRType() const {
- return (ncr_->isV4() ? dns::RRType::A(): dns::RRType::AAAA());
+ return (ncr_->isV4() ? dns::RRType::A() : dns::RRType::AAAA());
}
} // namespace isc::d2
diff --git a/src/bin/d2/nc_trans.h b/src/bin/d2/nc_trans.h
index b0f7c4a..86a89d7 100644
--- a/src/bin/d2/nc_trans.h
+++ b/src/bin/d2/nc_trans.h
@@ -277,10 +277,10 @@ protected:
/// If the maximum number of attempts has been reached, it will transition
/// to the given state with a next event of SERVER_IO_ERROR_EVT.
///
- /// @param server_sel_state State to transition to if maximum attempts
+ /// @param fail_to_state State to transition to if maximum attempts
/// have been tried.
///
- void retryTransition(const int server_sel_state);
+ void retryTransition(const int fail_to_state);
/// @brief Sets the update request packet to the given packet.
///
@@ -435,13 +435,13 @@ public:
/// @brief Fetches the forward DdnsDomain.
///
- /// @return A pointer reference to the forward DdnsDomain. If
+ /// @return A pointer reference to the forward DdnsDomain. If
/// the request does not include a forward change, the pointer will empty.
DdnsDomainPtr& getForwardDomain();
/// @brief Fetches the reverse DdnsDomain.
///
- /// @return A pointer reference to the reverse DdnsDomain. If
+ /// @return A pointer reference to the reverse DdnsDomain. If
/// the request does not include a reverse change, the pointer will empty.
DdnsDomainPtr& getReverseDomain();
diff --git a/src/bin/d2/tests/Makefile.am b/src/bin/d2/tests/Makefile.am
index d860ccc..8b5e351 100644
--- a/src/bin/d2/tests/Makefile.am
+++ b/src/bin/d2/tests/Makefile.am
@@ -67,6 +67,7 @@ d2_unittests_SOURCES += ../d2_zone.cc ../d2_zone.h
d2_unittests_SOURCES += ../dns_client.cc ../dns_client.h
d2_unittests_SOURCES += ../labeled_value.cc ../labeled_value.h
d2_unittests_SOURCES += ../nc_add.cc ../nc_add.h
+d2_unittests_SOURCES += ../nc_remove.cc ../nc_remove.h
d2_unittests_SOURCES += ../nc_trans.cc ../nc_trans.h
d2_unittests_SOURCES += ../state_model.cc ../state_model.h
d2_unittests_SOURCES += d_test_stubs.cc d_test_stubs.h
@@ -83,6 +84,7 @@ d2_unittests_SOURCES += d2_zone_unittests.cc
d2_unittests_SOURCES += dns_client_unittests.cc
d2_unittests_SOURCES += labeled_value_unittests.cc
d2_unittests_SOURCES += nc_add_unittests.cc
+d2_unittests_SOURCES += nc_remove_unittests.cc
d2_unittests_SOURCES += nc_test_utils.cc nc_test_utils.h
d2_unittests_SOURCES += nc_trans_unittests.cc
d2_unittests_SOURCES += state_model_unittests.cc
diff --git a/src/bin/d2/tests/nc_add_unittests.cc b/src/bin/d2/tests/nc_add_unittests.cc
index c291a54..9aecda4 100644
--- a/src/bin/d2/tests/nc_add_unittests.cc
+++ b/src/bin/d2/tests/nc_add_unittests.cc
@@ -407,14 +407,14 @@ TEST_F(NameAddTransactionTest, buildForwardAdd) {
NameAddStubPtr name_add;
ASSERT_NO_THROW(name_add = makeTransaction4());
ASSERT_NO_THROW(name_add->buildAddFwdAddressRequest());
- checkForwardAddRequest(*name_add);
+ checkAddFwdAddressRequest(*name_add);
// Create a IPv6 forward add transaction.
// Verify the request builds without error.
// and then verify the request contents.
ASSERT_NO_THROW(name_add = makeTransaction6());
ASSERT_NO_THROW(name_add->buildAddFwdAddressRequest());
- checkForwardAddRequest(*name_add);
+ checkAddFwdAddressRequest(*name_add);
}
/// @brief Tests construction of a DNS update request for replacing a forward
@@ -426,14 +426,14 @@ TEST_F(NameAddTransactionTest, buildReplaceFwdAddressRequest) {
NameAddStubPtr name_add;
ASSERT_NO_THROW(name_add = makeTransaction4());
ASSERT_NO_THROW(name_add->buildReplaceFwdAddressRequest());
- checkForwardReplaceRequest(*name_add);
+ checkReplaceFwdAddressRequest(*name_add);
// Create a IPv6 forward replace transaction.
// Verify the request builds without error.
// and then verify the request contents.
ASSERT_NO_THROW(name_add = makeTransaction6());
ASSERT_NO_THROW(name_add->buildReplaceFwdAddressRequest());
- checkForwardReplaceRequest(*name_add);
+ checkReplaceFwdAddressRequest(*name_add);
}
/// @brief Tests the construction of a DNS update request for replacing a
@@ -445,14 +445,14 @@ TEST_F(NameAddTransactionTest, buildReplaceRevPtrsRequest) {
NameAddStubPtr name_add;
ASSERT_NO_THROW(name_add = makeTransaction4());
ASSERT_NO_THROW(name_add->buildReplaceRevPtrsRequest());
- checkReverseReplaceRequest(*name_add);
+ checkReplaceRevPtrsRequest(*name_add);
// Create a IPv6 reverse replace transaction.
// Verify the request builds without error.
// and then verify the request contents.
ASSERT_NO_THROW(name_add = makeTransaction6());
ASSERT_NO_THROW(name_add->buildReplaceRevPtrsRequest());
- checkReverseReplaceRequest(*name_add);
+ checkReplaceRevPtrsRequest(*name_add);
}
// Tests the readyHandler functionality.
@@ -632,7 +632,7 @@ TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_FwdOnlyAddOK) {
EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
// Verify that an update message was constructed properly.
- checkForwardAddRequest(*name_add);
+ checkAddFwdAddressRequest(*name_add);
// Verify that we are still in this state and next event is NOP_EVT.
// This indicates we "sent" the message and are waiting for IO completion.
@@ -943,7 +943,7 @@ TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_FwdOnlyAddOK) {
EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
// Verify that an update message was constructed properly.
- checkForwardReplaceRequest(*name_add);
+ checkReplaceFwdAddressRequest(*name_add);
// Verify that we are still in this state and next event is NOP_EVT.
// This indicates we "sent" the message and are waiting for IO completion.
@@ -1366,7 +1366,7 @@ TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_FwdOnlyAddOK) {
EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
// Verify that an update message was constructed properly.
- checkReverseReplaceRequest(*name_add);
+ checkReplaceRevPtrsRequest(*name_add);
// Verify that we are still in this state and next event is NOP_EVT.
// This indicates we "sent" the message and are waiting for IO completion.
@@ -1419,8 +1419,7 @@ TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_OtherRcode) {
name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
// Run replacingRevPtrsHandler again to process the response.
- //EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
- (name_add->replacingRevPtrsHandler());
+ EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
// Completion flags should still be false.
EXPECT_FALSE(name_add->getForwardChangeCompleted());
@@ -1634,6 +1633,29 @@ TEST_F(NameAddTransactionTest, processAddFailedHandler) {
EXPECT_THROW(name_add->processAddFailedHandler(), NameAddTransactionError);
}
+// Tests the processAddFailedHandler functionality.
+// It verifies behavior for posted event of NO_MORE_SERVERS_EVT.
+TEST_F(NameAddTransactionTest, processAddFailedHandler_NoMoreServers) {
+ NameAddStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::
+ NO_MORE_SERVERS_EVT));
+
+ // Run processAddFailedHandler.
+ EXPECT_NO_THROW(name_remove->processAddFailedHandler());
+
+ // Verify that a server was selected.
+ EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus());
+
+ // Verify that the model has ended. (Remember, the transaction failed NOT
+ // the model. The model should have ended normally.)
+ EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
+ EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
+}
+
// Tests addingFwdAddrsHandler with the following scenario:
//
// The request includes only a forward change.
diff --git a/src/bin/d2/tests/nc_remove_unittests.cc b/src/bin/d2/tests/nc_remove_unittests.cc
new file mode 100644
index 0000000..37efad6
--- /dev/null
+++ b/src/bin/d2/tests/nc_remove_unittests.cc
@@ -0,0 +1,1872 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <d2/d2_cfg_mgr.h>
+#include <d2/d2_cfg_mgr.h>
+#include <d2/nc_remove.h>
+#include <dns/messagerenderer.h>
+#include <nc_test_utils.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+
+namespace {
+
+/// @brief Test class derived from NameRemoveTransaction to provide visiblity
+// to protected methods.
+class NameRemoveStub : public NameRemoveTransaction {
+public:
+ NameRemoveStub(IOServicePtr& io_service,
+ dhcp_ddns::NameChangeRequestPtr& ncr,
+ DdnsDomainPtr& forward_domain,
+ DdnsDomainPtr& reverse_domain)
+ : NameRemoveTransaction(io_service, ncr, forward_domain,
+ reverse_domain),
+ simulate_send_exception_(false),
+ simulate_build_request_exception_(false) {
+ }
+
+ virtual ~NameRemoveStub() {
+ }
+
+ /// @brief Simulates sending update requests to the DNS server
+ ///
+ /// This method simulates the initiation of an asynchronous send of
+ /// a DNS update request. It overrides the actual sendUpdate method in
+ /// the base class, thus avoiding an actual send, yet still increments
+ /// the update attempt count and posts a next event of NOP_EVT.
+ ///
+ /// It will also simulate an exception-based failure of sendUpdate, if
+ /// the simulate_send_exception_ flag is true.
+ ///
+ /// @param use_tsig_ Parameter is unused, but present in the base class
+ /// method.
+ ///
+ virtual void sendUpdate(bool /* use_tsig_ = false */) {
+ if (simulate_send_exception_) {
+ // Make the flag a one-shot by resetting it.
+ simulate_send_exception_ = false;
+ // Transition to failed.
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ return;
+ }
+
+ // Update send attempt count and post a NOP_EVT.
+ setUpdateAttempts(getUpdateAttempts() + 1);
+ postNextEvent(StateModel::NOP_EVT);
+ }
+
+ /// @brief Prepares the initial D2UpdateMessage
+ ///
+ /// This method overrides the NameChangeTransactio implementation to
+ /// provide the ability to simulate an exception throw in the build
+ /// request logic.
+ /// If the one-shot flag, simulate_build_request_exception_ is true,
+ /// this method will throw an exception, otherwise it will invoke the
+ /// base class method, providing normal functionality.
+ ///
+ /// For parameter description see the NameChangeTransaction implementation.
+ virtual D2UpdateMessagePtr prepNewRequest(DdnsDomainPtr domain) {
+ if (simulate_build_request_exception_) {
+ simulate_build_request_exception_ = false;
+ isc_throw (NameRemoveTransactionError,
+ "Simulated build requests exception");
+ }
+
+ return (NameChangeTransaction::prepNewRequest(domain));
+ }
+
+ /// @brief Simulates receiving a response
+ ///
+ /// This method simulates the completion of a DNSClient send. This allows
+ /// the state handler logic devoted to dealing with IO completion to be
+ /// fully exercised without requiring any actual IO. The two primary
+ /// pieces of information gleaned from IO completion are the DNSClient
+ /// status which indicates whether or not the IO exchange was successful
+ /// and the rcode, which indicates the server's reaction to the request.
+ ///
+ /// This method updates the transaction's DNS status value to that of the
+ /// given parameter, and then constructs and DNS update response message
+ /// with the given rcode value. To complete the simulation it then posts
+ /// a next event of IO_COMPLETED_EVT.
+ ///
+ /// @param status simulated DNSClient status
+ /// @param rcode simulated server response code
+ void fakeResponse(const DNSClient::Status& status,
+ const dns::Rcode& rcode) {
+ // Set the DNS update status. This is normally set in
+ // DNSClient IO completion handler.
+ setDnsUpdateStatus(status);
+
+ // Construct an empty message with the given Rcode.
+ D2UpdateMessagePtr msg(new D2UpdateMessage(D2UpdateMessage::OUTBOUND));
+ msg->setRcode(rcode);
+
+ // Set the update response to the message.
+ setDnsUpdateResponse(msg);
+
+ // Post the IO completion event.
+ postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+ }
+
+ /// @brief Selects the first forward server.
+ /// Some state handlers require a server to have been selected.
+ /// This selects a server without going through the state
+ /// transition(s) to do so.
+ bool selectFwdServer() {
+ if (getForwardDomain()) {
+ initServerSelection(getForwardDomain());
+ selectNextServer();
+ return (getCurrentServer());
+ }
+
+ return (false);
+ }
+
+ /// @brief Selects the first reverse server.
+ /// Some state handlers require a server to have been selected.
+ /// This selects a server without going through the state
+ /// transition(s) to do so.
+ bool selectRevServer() {
+ if (getReverseDomain()) {
+ initServerSelection(getReverseDomain());
+ selectNextServer();
+ return (getCurrentServer());
+ }
+
+ return (false);
+ }
+
+ /// @brief One-shot flag which will simulate sendUpdate failure if true.
+ bool simulate_send_exception_;
+
+ /// @brief One-shot flag which will simulate an exception when sendUpdate
+ /// failure if true.
+ bool simulate_build_request_exception_;
+
+ using StateModel::postNextEvent;
+ using StateModel::setState;
+ using StateModel::initDictionaries;
+ using NameRemoveTransaction::defineEvents;
+ using NameRemoveTransaction::verifyEvents;
+ using NameRemoveTransaction::defineStates;
+ using NameRemoveTransaction::verifyStates;
+ using NameRemoveTransaction::readyHandler;
+ using NameRemoveTransaction::selectingFwdServerHandler;
+ using NameRemoveTransaction::getCurrentServer;
+ using NameRemoveTransaction::removingFwdAddrsHandler;
+ using NameRemoveTransaction::setDnsUpdateStatus;
+ using NameRemoveTransaction::removingFwdRRsHandler;
+ using NameRemoveTransaction::selectingRevServerHandler;
+ using NameRemoveTransaction::removingRevPtrsHandler;
+ using NameRemoveTransaction::processRemoveOkHandler;
+ using NameRemoveTransaction::processRemoveFailedHandler;
+ using NameRemoveTransaction::buildRemoveFwdAddressRequest;
+ using NameRemoveTransaction::buildRemoveFwdRRsRequest;
+ using NameRemoveTransaction::buildRemoveRevPtrsRequest;
+};
+
+typedef boost::shared_ptr<NameRemoveStub> NameRemoveStubPtr;
+
+/// @brief Test fixture for testing NameRemoveTransaction
+///
+/// Note this class uses NameRemoveStub class to exercise non-public
+/// aspects of NameRemoveTransaction.
+class NameRemoveTransactionTest : public TransactionTest {
+public:
+ NameRemoveTransactionTest() {
+ }
+
+ virtual ~NameRemoveTransactionTest() {
+ }
+
+ /// @brief Creates a transaction which requests an IPv4 DNS update.
+ ///
+ /// The transaction is constructed around a predefined (i.e. "canned")
+ /// IPv4 NameChangeRequest. The request has both forward and reverse DNS
+ /// changes requested. Based upon the change mask, the transaction
+ /// will have either the forward, reverse, or both domains populated.
+ ///
+ /// @param change_mask determines which change directions are requested
+ NameRemoveStubPtr makeTransaction4(int change_mask) {
+ // Creates IPv4 remove request, forward, and reverse domains.
+ setupForIPv4Transaction(dhcp_ddns::CHG_REMOVE, change_mask);
+
+ // Now create the test transaction as would occur in update manager.
+ return (NameRemoveStubPtr(new NameRemoveStub(io_service_, ncr_,
+ forward_domain_,
+ reverse_domain_)));
+ }
+
+ /// @brief Creates a transaction which requests an IPv6 DNS update.
+ ///
+ /// The transaction is constructed around a predefined (i.e. "canned")
+ /// IPv6 NameChangeRequest. The request has both forward and reverse DNS
+ /// changes requested. Based upon the change mask, the transaction
+ /// will have either the forward, reverse, or both domains populated.
+ ///
+ /// @param change_mask determines which change directions are requested
+ NameRemoveStubPtr makeTransaction6(int change_mask) {
+ // Creates IPv6 remove request, forward, and reverse domains.
+ setupForIPv6Transaction(dhcp_ddns::CHG_REMOVE, change_mask);
+
+ // Now create the test transaction as would occur in update manager.
+ return (NameRemoveStubPtr(new NameRemoveStub(io_service_, ncr_,
+ forward_domain_,
+ reverse_domain_)));
+ }
+
+ /// @brief Create a test transaction at a known point in the state model.
+ ///
+ /// Method prepares a new test transaction and sets its state and next
+ /// event values to those given. This makes the transaction appear to
+ /// be at that point in the state model without having to transition it
+ /// through prerequisite states. It also provides the ability to set
+ /// which change directions are requested: forward change only, reverse
+ /// change only, or both.
+ ///
+ /// @param state value to set as the current state
+ /// @param event value to post as the next event
+ /// @param change_mask determines which change directions are requested
+ /// @param family selects between an IPv4 (AF_INET) and IPv6 (AF_INET6)
+ /// transaction.
+ NameRemoveStubPtr prepHandlerTest(unsigned int state, unsigned int event,
+ unsigned int change_mask
+ = FWD_AND_REV_CHG,
+ short family = AF_INET) {
+ NameRemoveStubPtr name_remove = (family == AF_INET ?
+ makeTransaction4(change_mask) :
+ makeTransaction6(change_mask));
+ name_remove->initDictionaries();
+ name_remove->postNextEvent(event);
+ name_remove->setState(state);
+ return (name_remove);
+ }
+
+};
+
+/// @brief Tests NameRemoveTransaction construction.
+/// This test verifies that:
+/// 1. Construction with invalid type of request
+/// 2. Valid construction functions properly
+TEST(NameRemoveTransaction, construction) {
+ IOServicePtr io_service(new isc::asiolink::IOService());
+
+ const char* msg_str =
+ "{"
+ " \"change_type\" : 0 , "
+ " \"forward_change\" : true , "
+ " \"reverse_change\" : true , "
+ " \"fqdn\" : \"example.com.\" , "
+ " \"ip_address\" : \"192.168.2.1\" , "
+ " \"dhcid\" : \"0102030405060708\" , "
+ " \"lease_expires_on\" : \"20130121132405\" , "
+ " \"lease_length\" : 1300 "
+ "}";
+
+ dhcp_ddns::NameChangeRequestPtr ncr;
+ DnsServerInfoStoragePtr servers;
+ DdnsDomainPtr forward_domain;
+ DdnsDomainPtr reverse_domain;
+ DdnsDomainPtr empty_domain;
+
+ ASSERT_NO_THROW(ncr = dhcp_ddns::NameChangeRequest::fromJSON(msg_str));
+ ASSERT_NO_THROW(forward_domain.reset(new DdnsDomain("*", "", servers)));
+ ASSERT_NO_THROW(reverse_domain.reset(new DdnsDomain("*", "", servers)));
+
+ // Verify that construction with wrong change type fails.
+ EXPECT_THROW(NameRemoveTransaction(io_service, ncr,
+ forward_domain, reverse_domain),
+ NameRemoveTransactionError);
+
+ // Verify that a valid construction attempt works.
+ ncr->setChangeType(isc::dhcp_ddns::CHG_REMOVE);
+ EXPECT_NO_THROW(NameRemoveTransaction(io_service, ncr,
+ forward_domain, reverse_domain));
+}
+
+/// @brief Tests event and state dictionary construction and verification.
+TEST_F(NameRemoveTransactionTest, dictionaryCheck) {
+ NameRemoveStubPtr name_remove;
+ ASSERT_NO_THROW(name_remove = makeTransaction4(FWD_AND_REV_CHG));
+ // Verify that the event and state dictionary validation fails prior
+ // dictionary construction.
+ ASSERT_THROW(name_remove->verifyEvents(), StateModelError);
+ ASSERT_THROW(name_remove->verifyStates(), StateModelError);
+
+ // Construct both dictionaries.
+ ASSERT_NO_THROW(name_remove->defineEvents());
+ ASSERT_NO_THROW(name_remove->defineStates());
+
+ // Verify both event and state dictionaries now pass validation.
+ ASSERT_NO_THROW(name_remove->verifyEvents());
+ ASSERT_NO_THROW(name_remove->verifyStates());
+}
+
+
+/// @brief Tests construction of a DNS update request for removing forward
+/// DNS address RRs.
+TEST_F(NameRemoveTransactionTest, buildRemoveFwdAddressRequest) {
+ // Create a IPv4 forward add transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ NameRemoveStubPtr name_remove;
+ ASSERT_NO_THROW(name_remove = makeTransaction4(FORWARD_CHG));
+ ASSERT_NO_THROW(name_remove->buildRemoveFwdAddressRequest());
+ checkRemoveFwdAddressRequest(*name_remove);
+
+ // Create a IPv6 forward add transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ ASSERT_NO_THROW(name_remove = makeTransaction6(FORWARD_CHG));
+ ASSERT_NO_THROW(name_remove->buildRemoveFwdAddressRequest());
+ checkRemoveFwdAddressRequest(*name_remove);
+}
+
+/// @brief Tests construction of a DNS update request for removing forward
+/// dns RR entries.
+TEST_F(NameRemoveTransactionTest, buildRemoveFwdRRsRequest) {
+ // Create a IPv4 forward replace transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ NameRemoveStubPtr name_remove;
+ ASSERT_NO_THROW(name_remove = makeTransaction4(FORWARD_CHG));
+ ASSERT_NO_THROW(name_remove->buildRemoveFwdRRsRequest());
+ checkRemoveFwdRRsRequest(*name_remove);
+
+ // Create a IPv6 forward replace transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ ASSERT_NO_THROW(name_remove = makeTransaction6(FORWARD_CHG));
+ ASSERT_NO_THROW(name_remove->buildRemoveFwdRRsRequest());
+ checkRemoveFwdRRsRequest(*name_remove);
+}
+
+/// @brief Tests the construction of a DNS update request for removing a
+/// reverse dns entry.
+TEST_F(NameRemoveTransactionTest, buildRemoveRevPtrsRequest) {
+ // Create a IPv4 reverse replace transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ NameRemoveStubPtr name_remove;
+ ASSERT_NO_THROW(name_remove = makeTransaction4(REVERSE_CHG));
+ ASSERT_NO_THROW(name_remove->buildRemoveRevPtrsRequest());
+ checkRemoveRevPtrsRequest(*name_remove);
+
+ // Create a IPv6 reverse replace transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ ASSERT_NO_THROW(name_remove = makeTransaction6(REVERSE_CHG));
+ ASSERT_NO_THROW(name_remove->buildRemoveRevPtrsRequest());
+ checkRemoveRevPtrsRequest(*name_remove);
+}
+
+// Tests the readyHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is START_EVT and request includes only a forward change
+// 2. Posted event is START_EVT and request includes both a forward and a
+// reverse change
+// 3. Posted event is START_EVT and request includes only a reverse change
+// 4. Posted event is invalid
+//
+TEST_F(NameRemoveTransactionTest, readyHandler) {
+ NameRemoveStubPtr name_remove;
+
+ // Create a transaction which includes only a forward change.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::READY_ST,
+ StateModel::START_EVT, FORWARD_CHG));
+ // Run readyHandler.
+ EXPECT_NO_THROW(name_remove->readyHandler());
+
+ // Verify that a request requiring only a forward change, transitions to
+ // selecting a forward server.
+ EXPECT_EQ(NameChangeTransaction::SELECTING_FWD_SERVER_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+ name_remove->getNextEvent());
+
+ // Create a transaction which includes both a forward and a reverse change.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::READY_ST,
+ StateModel::START_EVT, FWD_AND_REV_CHG));
+ // Run readyHandler.
+ EXPECT_NO_THROW(name_remove->readyHandler());
+
+ // Verify that a request requiring both forward and reverse, starts with
+ // the forward change by transitioning to selecting a forward server.
+ EXPECT_EQ(NameChangeTransaction::SELECTING_FWD_SERVER_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+ name_remove->getNextEvent());
+
+ // Create and prep a reverse only transaction.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::READY_ST,
+ StateModel::START_EVT, REVERSE_CHG));
+ // Run readyHandler.
+ EXPECT_NO_THROW(name_remove->readyHandler());
+
+ // Verify that a request requiring only a reverse change, transitions to
+ // selecting a reverse server.
+ EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+ name_remove->getNextEvent());
+
+ // Create and prep transaction, poised to run the handler but with an
+ // invalid event.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::READY_ST,
+ StateModel::NOP_EVT));
+
+ // Running the readyHandler should throw.
+ EXPECT_THROW(name_remove->readyHandler(), NameRemoveTransactionError);
+}
+
+
+// Tests the selectingFwdServerHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is SELECT_SERVER_EVT
+// 2. Posted event is SERVER_IO_ERROR_EVT
+// 3. Posted event is invalid
+//
+TEST_F(NameRemoveTransactionTest, selectingFwdServerHandler) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ SELECTING_FWD_SERVER_ST,
+ NameChangeTransaction::SELECT_SERVER_EVT));
+
+ // Call selectingFwdServerHandler enough times to select all of the
+ // servers in it's current domain. The first time, it will be with
+ // next event of SELECT_SERVER_EVT. Thereafter it will be with a next
+ // event of SERVER_IO_ERROR_EVT.
+ int num_servers = name_remove->getForwardDomain()->getServers()->size();
+ for (int i = 0; i < num_servers; ++i) {
+ // Run selectingFwdServerHandler.
+ ASSERT_NO_THROW(name_remove->selectingFwdServerHandler())
+ << " num_servers: " << num_servers
+ << " selections: " << i;
+
+ // Verify that a server was selected.
+ ASSERT_TRUE(name_remove->getCurrentServer())
+ << " num_servers: " << num_servers << " selections: " << i;
+
+ // Verify that we transitioned correctly.
+ ASSERT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
+ name_remove->getCurrState())
+ << " num_servers: " << num_servers << " selections: " << i;
+ ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_remove->getNextEvent())
+ << " num_servers: " << num_servers << " selections: " << i;
+
+ // Post a server IO error event. This simulates an IO error occuring
+ // and a need to select the new server.
+ ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction::
+ SERVER_IO_ERROR_EVT))
+ << " num_servers: " << num_servers
+ << " selections: " << i;
+ }
+
+ // We should have exhausted the list of servers. Processing another
+ // SERVER_IO_ERROR_EVT should transition us to failure.
+ EXPECT_NO_THROW(name_remove->selectingFwdServerHandler());
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT,
+ name_remove->getNextEvent());
+
+ // Create and prep transaction, poised to run the handler but with an
+ // invalid event.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ SELECTING_FWD_SERVER_ST,
+ StateModel::NOP_EVT));
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_remove->selectingFwdServerHandler(),
+ NameRemoveTransactionError);
+}
+
+// ************************ addingFwdAddrHandler Tests *****************
+
+// Tests that removingFwdAddrsHandler rejects invalid events.
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_InvalidEvent) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler but with
+ // an invalid event.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ StateModel::NOP_EVT));
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_remove->removingFwdAddrsHandler(),
+ NameRemoveTransactionError);
+}
+
+
+// Tests addingFwdAddrsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent without error.
+// A server response is received which indicates successful update
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_FwdOnlyOK) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, FORWARD_CHG));
+
+ // Should not be an update message yet.
+ D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest();
+ ASSERT_FALSE(update_msg);
+
+ // At this point completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Run removingFwdAddrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Verify that an update message was constructed properly.
+ checkRemoveFwdAddressRequest(*name_remove);
+
+ // Verify that we are still in this state and next event is NOP_EVT.
+ // This indicates we "sent" the message and are waiting for IO completion.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::NOP_EVT,
+ name_remove->getNextEvent());
+
+ // Simulate receiving a successful update response.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+ // Run removingFwdAddrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Completion flags should both still be false, as we are only partly
+ // done with forward updates.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since we succeeded, we should now attempt to remove any remaining
+ // forward RRs.
+ // Verify that we transitioned correctly.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameRemoveTransaction::UPDATE_OK_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests addingFwdAddrsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent without error.
+// A server response is received which indicates FQDN is not in use.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_FqdnNotInUse) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, FORWARD_CHG));
+
+ // Run removingFwdAddrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Simulate receiving a FQDN not in use response.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXDOMAIN());
+
+ // Run removingFwdAddrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Completion flags should both still be false, as we are only partly
+ // done with forward updates.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // There was no address RR to remove, but we will still make sure there
+ // are no other RRs for this FQDN.
+ // Verify that we transitioned correctly.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameRemoveTransaction::UPDATE_OK_EVT,
+ name_remove->getNextEvent());
+}
+
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+// The request includes a forward and reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent without error.
+// A server response is received which indicates the update was rejected.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_OtherRcode) {
+ NameRemoveStubPtr name_remove;
+
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectFwdServer());
+
+ // Run removingFwdAddrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Simulate receiving server rejection response. Per RFC, anything other
+ // than no error or FQDN not in use is failure. Arbitrarily choosing
+ // refused.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+ // Run removingFwdAddrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Completion flags should still be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // We should have failed the transaction. Verify that we transitioned
+ // correctly.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+// The request includes a forward and reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request send times out MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_Timeout) {
+ NameRemoveStubPtr name_remove;
+
+ // Create and prep a transaction, poised to run the handler.
+ // The log message issued when this test succeeds, displays the
+ // selected server, so we need to select a server before running this
+ // test.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectFwdServer());
+
+ // Verify that we can make maximum number of update attempts permitted
+ // and then transition to selecting a new server.
+ int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+ for (int i = 1; i <= max_tries; ++i) {
+ const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();
+
+ // Run removingFwdAddrsHandler to send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
+ if (i == 1) {
+ // First time around we should build the message.
+ EXPECT_FALSE(prev_msg);
+ EXPECT_TRUE(curr_msg);
+ } else {
+ // Subsequent passes should reuse the request. We are only
+ // looking to check that we have not replaced the pointer value
+ // with a new pointer. This tests the on_entry() logic which
+ // clears the request ONLY upon initial entry into the state.
+ EXPECT_TRUE(prev_msg == curr_msg);
+ }
+
+ // Simulate a server IO timeout.
+ name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT);
+ name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+ // Run removingFwdAddrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ if (i < max_tries) {
+ // We should be ready to try again.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_remove->getNextEvent());
+ } else {
+ // Server retries should be exhausted, time for a new server.
+ EXPECT_EQ(NameRemoveTransaction::SELECTING_FWD_SERVER_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+ name_remove->getNextEvent());
+ }
+ }
+}
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+// The request includes a forward and reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent but a corrupt response is received, this occurs
+// MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_InvalidResponse) {
+ NameRemoveStubPtr name_remove;
+
+ // Create and prep a transaction, poised to run the handler.
+ // The log message issued when this test succeeds, displays the
+ // selected server, so we need to select a server before running this
+ // test.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectFwdServer());
+
+ // Verify that we can make maximum number of update attempts permitted
+ // and then transition to selecting a new server.
+ int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+ for (int i = 1; i <= max_tries; ++i) {
+ // Run removingFwdAddrsHandler to construct send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Simulate a corrupt server response.
+ name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+ name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+ // Run removingFwdAddrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ if (i < max_tries) {
+ // We should be ready to try again.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_remove->getNextEvent());
+ } else {
+ // Server retries should be exhausted, time for a new server.
+ EXPECT_EQ(NameRemoveTransaction::SELECTING_FWD_SERVER_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+ name_remove->getNextEvent());
+ }
+ }
+
+}
+
+// ************************ removingFwdRRsHandler Tests *****************
+
+// Tests that removingFwdRRsHandler rejects invalid events.
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_InvalidEvent) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler but with
+ // an invalid event.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_RRS_ST,
+ StateModel::NOP_EVT));
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_remove->removingFwdRRsHandler(),
+ NameRemoveTransactionError);
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is UPDATE_OK_EVT.
+// The update request is sent without error.
+// A server response is received which indicates successful update.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FwdOnlyOK) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ UPDATE_OK_EVT, FORWARD_CHG));
+
+ // Should not be an update message yet.
+ D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest();
+ ASSERT_FALSE(update_msg);
+
+ // At this point completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Run removingFwdRRsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Verify that an update message was constructed properly.
+ checkRemoveFwdRRsRequest(*name_remove);
+
+ // Verify that we are still in this state and next event is NOP_EVT.
+ // This indicates we "sent" the message and are waiting for IO completion.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::NOP_EVT,
+ name_remove->getNextEvent());
+
+ // Simulate receiving a successful update response.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+ // Run removingFwdRRsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Forward completion should be true, reverse should be false.
+ EXPECT_TRUE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since it is a forward only change, we should be done.
+ // Verify that we transitioned correctly.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent without error.
+// A server response is received which indicates successful update.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FwdOnlyOK2) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, FORWARD_CHG));
+
+ // Run removingFwdRRsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Simulate receiving a successful update response.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+ // Run removingFwdRRsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Forward completion should be true, reverse should be false.
+ EXPECT_TRUE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since it is a forward only change, we should be done.
+ // Verify that we transitioned correctly.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+ name_remove->getNextEvent());
+}
+
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+// The request includes a forward and reverse change.
+// Initial posted event is UPDATE_OK_EVT.
+// The update request is sent without error.
+// A server response is received which indicates successful update.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FwdAndRevOK) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ UPDATE_OK_EVT, FWD_AND_REV_CHG));
+
+ // Run removingFwdRRsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Simulate receiving a successful update response.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+ // Run removingFwdRRsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Forward change completion should be true, reverse flag should be false.
+ EXPECT_TRUE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since the request also includes a reverse change we should
+ // be poised to start it. Verify that we transitioned correctly.
+ EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+// The request includes a forward and reverse change.
+// Initial posted event is UPDATE_OK_EVT.
+// The update request is sent without error.
+// A server response is received which indicates the FQDN is NOT in use.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FqdnNotInUse) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ UPDATE_OK_EVT, FORWARD_CHG));
+
+ // Run removingFwdRRsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Simulate receiving a FQDN not in use response.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXDOMAIN());
+
+ // Run removingFwdRRsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Forwad completion flag should be true, reverse should still be false.
+ EXPECT_TRUE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // The FQDN is no longer in use, RFC is unclear about this,
+ // but we will treat this as success.
+ // Since it is a forward only change, we should be done.
+ // Verify that we transitioned correctly.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+// The request includes a forward and reverse change.
+// The update request is sent without error.
+// A server response is received which indicates the update was rejected.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_OtherRcode) {
+ NameRemoveStubPtr name_remove;
+ // Create the transaction.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ UPDATE_OK_EVT, FWD_AND_REV_CHG));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectFwdServer());
+
+ // Run removingFwdRRsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Simulate receiving server rejection response. Per RFC, anything other
+ // than no error is failure (we are also treating FQDN not in use is
+ // success). Arbitrarily choosing refused.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+ // Run removingFwdRRsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Completion flags should still be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // We should have failed the transaction. Verifiy that we transitioned
+ // correctly.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+// The request includes a forward and reverse change.
+// Initial posted event is UPDATE_OK_EVT.
+// The update request send times out MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_Timeout) {
+ NameRemoveStubPtr name_remove;
+
+ // Create the transaction.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_RRS_ST,
+ NameChangeTransaction::
+ UPDATE_OK_EVT, FWD_AND_REV_CHG));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectFwdServer());
+
+ // Verify that we can make maximum number of update attempts permitted
+ // and then transition to selecting a new server.
+ int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+ for (int i = 1; i <= max_tries; ++i) {
+ const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();
+
+ // Run removingFwdRRsHandler to send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
+ if (i == 1) {
+ // First time around we should build the message.
+ EXPECT_FALSE(prev_msg);
+ EXPECT_TRUE(curr_msg);
+ } else {
+ // Subsequent passes should reuse the request. We are only
+ // looking to check that we have not replaced the pointer value
+ // with a new pointer. This tests the on_entry() logic which
+ // clears the request ONLY upon initial entry into the state.
+ EXPECT_TRUE(prev_msg == curr_msg);
+ }
+
+ // Simulate a server IO timeout.
+ name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT);
+ name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+ // Run removingFwdRRsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ if (i < max_tries) {
+ // We should be ready to try again.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_remove->getNextEvent());
+ } else {
+ // Server retries should be exhausted.
+ // We should abandon the transaction.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+ name_remove->getNextEvent());
+ }
+ }
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+// The request includes a forward and reverse change.
+// Initial posted event is UPDATE_OK_EVT.
+// The update request is sent but a corrupt response is received, this occurs
+// MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_InvalidResponse) {
+ NameRemoveStubPtr name_remove;
+
+ // Create the transaction.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_RRS_ST,
+ NameChangeTransaction::
+ UPDATE_OK_EVT, FWD_AND_REV_CHG));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectFwdServer());
+
+ // Verify that we can make maximum number of update attempts permitted
+ // and then transition to selecting a new server.
+ int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+ for (int i = 1; i <= max_tries; ++i) {
+ const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();
+
+ // Run removingFwdRRsHandler to send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
+ if (i == 1) {
+ // First time around we should build the message.
+ EXPECT_FALSE(prev_msg);
+ EXPECT_TRUE(curr_msg);
+ } else {
+ // Subsequent passes should reuse the request. We are only
+ // looking to check that we have not replaced the pointer value
+ // with a new pointer. This tests the on_entry() logic which
+ // clears the request ONLY upon initial entry into the state.
+ EXPECT_TRUE(prev_msg == curr_msg);
+ }
+
+ // Simulate a corrupt server response.
+ name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+ name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+ // Run removingFwdRRsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ if (i < max_tries) {
+ // We should be ready to try again.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_remove->getNextEvent());
+ } else {
+ // Server retries should be exhausted.
+ // We should abandon the transaction.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+ name_remove->getNextEvent());
+ }
+ }
+}
+
+
+// Tests the selectingRevServerHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is SELECT_SERVER_EVT
+// 2. Posted event is SERVER_IO_ERROR_EVT
+// 3. Posted event is invalid
+//
+TEST_F(NameRemoveTransactionTest, selectingRevServerHandler) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ SELECTING_REV_SERVER_ST,
+ NameChangeTransaction::SELECT_SERVER_EVT));
+
+ // Call selectingRevServerHandler enough times to select all of the
+ // servers in it's current domain. The first time, it will be with
+ // next event of SELECT_SERVER_EVT. Thereafter it will be with a next
+ // event of SERVER_IO_ERROR_EVT.
+ int num_servers = name_remove->getReverseDomain()->getServers()->size();
+ for (int i = 0; i < num_servers; ++i) {
+ // Run selectingRevServerHandler.
+ ASSERT_NO_THROW(name_remove->selectingRevServerHandler())
+ << " num_servers: " << num_servers
+ << " selections: " << i;
+
+ // Verify that a server was selected.
+ ASSERT_TRUE(name_remove->getCurrentServer())
+ << " num_servers: " << num_servers
+ << " selections: " << i;
+
+ // Verify that we transitioned correctly.
+ ASSERT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
+ name_remove->getCurrState())
+ << " num_servers: " << num_servers << " selections: " << i;
+ ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_remove->getNextEvent())
+ << " num_servers: " << num_servers << " selections: " << i;
+
+ // Post a server IO error event. This simulates an IO error occuring
+ // and a need to select the new server.
+ ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction::
+ SERVER_IO_ERROR_EVT))
+ << " num_servers: " << num_servers
+ << " selections: " << i;
+ }
+
+ // We should have exhausted the list of servers. Processing another
+ // SERVER_IO_ERROR_EVT should transition us to failure.
+ EXPECT_NO_THROW(name_remove->selectingRevServerHandler());
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT,
+ name_remove->getNextEvent());
+
+ // Create and prep transaction, poised to run the handler but with an
+ // invalid event.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ SELECTING_REV_SERVER_ST,
+ StateModel::NOP_EVT));
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_remove->selectingRevServerHandler(),
+ NameRemoveTransactionError);
+}
+
+//************************** removingRevPtrsHandler tests *****************
+
+// Tests that removingRevPtrsHandler rejects invalid events.
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_InvalidEvent) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler but with
+ // an invalid event.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_REV_PTRS_ST,
+ StateModel::NOP_EVT));
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_remove->removingRevPtrsHandler(),
+ NameRemoveTransactionError);
+}
+
+// Tests removingRevPtrsHandler with the following scenario:
+//
+// The request includes only a reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent without error.
+// A server response is received which indicates successful update.
+//
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_RevOnlyOK) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, REVERSE_CHG));
+
+ // Should not be an update message yet.
+ D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest();
+ ASSERT_FALSE(update_msg);
+
+ // At this point completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Run removingRevPtrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Verify that an update message was constructed properly.
+ checkRemoveRevPtrsRequest(*name_remove);
+
+ // Verify that we are still in this state and next event is NOP_EVT.
+ // This indicates we "sent" the message and are waiting for IO completion.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(StateModel::NOP_EVT,
+ name_remove->getNextEvent());
+
+ // Simulate receiving a successful update response.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+ // Run removingRevPtrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Forward completion should be false, reverse should be true.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_TRUE(name_remove->getReverseChangeCompleted());
+
+ // Since it is a reverse change, we should be done.
+ // Verify that we transitioned correctly.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingRevPtrsHandler with the following scenario:
+//
+// The request includes only a reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent without error.
+// A server response is received which indicates FQDN is NOT in use.
+//
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_FqdnNotInUse) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, REVERSE_CHG));
+
+ // Should not be an update message yet.
+ D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest();
+ ASSERT_FALSE(update_msg);
+
+ // At this point completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Run removingRevPtrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Verify that an update message was constructed properly.
+ checkRemoveRevPtrsRequest(*name_remove);
+
+ // Verify that we are still in this state and next event is NOP_EVT.
+ // This indicates we "sent" the message and are waiting for IO completion.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(StateModel::NOP_EVT,
+ name_remove->getNextEvent());
+
+ // Simulate receiving a FQDN not in use response.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXDOMAIN());
+
+ // Run removingRevPtrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Forward completion should be false, reverse should be true.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_TRUE(name_remove->getReverseChangeCompleted());
+
+ // Since it is a reverse change, we should be done.
+ // Verify that we transitioned correctly.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingRevPtrsHandler with the following scenario:
+//
+// The request includes only a reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent without error.
+// A server response is received which indicates the update was rejected.
+//
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_OtherRcode) {
+ NameRemoveStubPtr name_remove;
+ // Create the transaction.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, REVERSE_CHG));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectRevServer());
+
+ // Run removingRevPtrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Simulate receiving server rejection response. Per RFC, anything other
+ // than no error is failure. Arbitrarily choosing refused.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+ // Run removingRevPtrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Completion flags should still be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // We should have failed the transaction. Verify that we transitioned
+ // correctly.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingRevPtrsHandler with the following scenario:
+//
+// The request includes only a reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request send times out MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_Timeout) {
+ NameRemoveStubPtr name_remove;
+ // Create the transaction.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, REVERSE_CHG));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectRevServer());
+
+ // Verify that we can make maximum number of update attempts permitted
+ // and then transition to selecting a new server.
+ int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+ for (int i = 1; i <= max_tries; ++i) {
+ const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();
+
+ // Run removingRevPtrsHandler to send the request.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
+ if (i == 1) {
+ // First time around we should build the message.
+ EXPECT_FALSE(prev_msg);
+ EXPECT_TRUE(curr_msg);
+ } else {
+ // Subsequent passes should reuse the request. We are only
+ // looking to check that we have not replaced the pointer value
+ // with a new pointer. This tests the on_entry() logic which
+ // clears the request ONLY upon initial entry into the state.
+ EXPECT_TRUE(prev_msg == curr_msg);
+ }
+
+ // Simulate a server IO timeout.
+ name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT);
+ name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+ // Run removingRevPtrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ if (i < max_tries) {
+ // We should be ready to try again.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_remove->getNextEvent());
+ } else {
+ // Server retries should be exhausted, time for a new server.
+ EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+ name_remove->getNextEvent());
+ }
+ }
+}
+
+
+// Tests removingRevPtrsHandler with the following scenario:
+//
+// The request includes only a reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent but a corrupt response is received, this occurs
+// MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_CorruptResponse) {
+ NameRemoveStubPtr name_remove;
+ // Create the transaction.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, REVERSE_CHG));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectRevServer());
+
+ // Verify that we can make maximum number of update attempts permitted
+ // and then transition to selecting a new server.
+ int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+ for (int i = 1; i <= max_tries; ++i) {
+ const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();
+
+ // Run removingRevPtrsHandler to send the request.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
+ if (i == 1) {
+ // First time around we should build the message.
+ EXPECT_FALSE(prev_msg);
+ EXPECT_TRUE(curr_msg);
+ } else {
+ // Subsequent passes should reuse the request. We are only
+ // looking to check that we have not replaced the pointer value
+ // with a new pointer. This tests the on_entry() logic which
+ // clears the request ONLY upon initial entry into the state.
+ EXPECT_TRUE(prev_msg == curr_msg);
+ }
+
+ // Simulate a server corrupt response.
+ name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+ name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+ // Run removingRevPtrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ if (i < max_tries) {
+ // We should be ready to try again.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_remove->getNextEvent());
+ } else {
+ // Server retries should be exhausted, time for a new server.
+ EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+ name_remove->getNextEvent());
+ }
+ }
+}
+
+// Tests the processRemoveOkHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is UPDATE_OK_EVT
+// 2. Posted event is invalid
+//
+TEST_F(NameRemoveTransactionTest, processRemoveOkHandler) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ NameChangeTransaction::UPDATE_OK_EVT));
+ // Run processRemoveOkHandler.
+ EXPECT_NO_THROW(name_remove->processRemoveOkHandler());
+
+ // Verify that a server was selected.
+ EXPECT_EQ(dhcp_ddns::ST_COMPLETED, name_remove->getNcrStatus());
+
+ // Verify that the model has ended.
+ EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
+ EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
+
+
+ // Create and prep transaction, poised to run the handler but with an
+ // invalid event.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ StateModel::NOP_EVT));
+ // Running the handler should throw.
+ EXPECT_THROW(name_remove->processRemoveOkHandler(),
+ NameRemoveTransactionError);
+}
+
+// Tests the processRemoveFailedHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is UPDATE_FAILED_EVT
+// 2. Posted event is invalid
+//
+TEST_F(NameRemoveTransactionTest, processRemoveFailedHandler) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::UPDATE_FAILED_EVT));
+ // Run processRemoveFailedHandler.
+ EXPECT_NO_THROW(name_remove->processRemoveFailedHandler());
+
+ // Verify that a server was selected.
+ EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus());
+
+ // Verify that the model has ended. (Remember, the transaction failed NOT
+ // the model. The model should have ended normally.)
+ EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
+ EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
+
+
+ // Create and prep transaction, poised to run the handler but with an
+ // invalid event.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ PROCESS_TRANS_FAILED_ST,
+ StateModel::NOP_EVT));
+ // Running the handler should throw.
+ EXPECT_THROW(name_remove->processRemoveFailedHandler(),
+ NameRemoveTransactionError);
+}
+
+// Tests the processRemoveFailedHandler functionality.
+// It verifies behavior for posted event of NO_MORE_SERVERS_EVT.
+TEST_F(NameRemoveTransactionTest, processRemoveFailedHandler_NoMoreServers) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::
+ NO_MORE_SERVERS_EVT));
+
+ // Run processRemoveFailedHandler.
+ EXPECT_NO_THROW(name_remove->processRemoveFailedHandler());
+
+ // Verify that a server was selected.
+ EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus());
+
+ // Verify that the model has ended. (Remember, the transaction failed NOT
+ // the model. The model should have ended normally.)
+ EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
+ EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
+}
+
+// Tests the processRemoveFailedHandler functionality.
+// It verifies behavior for posted event of SERVER_IO_ERROR_EVT.
+TEST_F(NameRemoveTransactionTest, processRemoveFailedHandler_ServerIOError) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::
+ SERVER_IO_ERROR_EVT));
+
+ // Run processRemoveFailedHandler.
+ EXPECT_NO_THROW(name_remove->processRemoveFailedHandler());
+
+ // Verify that a server was selected.
+ EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus());
+
+ // Verify that the model has ended. (Remember, the transaction failed NOT
+ // the model. The model should have ended normally.)
+ EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
+ EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
+}
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The send update request fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_sendUpdateException) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, FORWARD_CHG));
+
+ name_remove->simulate_send_exception_ = true;
+
+ // Run removingFwdAddrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since IO exceptions should be gracefully handled, any that occur
+ // are unanticipated, and deemed unrecoverable, so the transaction should
+ // be transitioned to failure.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The send update request fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_SendUpdateException) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_RRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, FORWARD_CHG));
+
+ name_remove->simulate_send_exception_ = true;
+
+ // Run removingFwdRRsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since IO exceptions should be gracefully handled, any that occur
+ // are unanticipated, and deemed unrecoverable, so the transaction should
+ // be transitioned to failure.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingRevPtrHandler with the following scenario:
+//
+// The request includes only a reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The send update request fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_SendUpdateException) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, REVERSE_CHG));
+
+ name_remove->simulate_send_exception_ = true;
+
+ // Run removingRevPtrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since IO exceptions should be gracefully handled, any that occur
+ // are unanticipated, and deemed unrecoverable, so the transaction should
+ // be transitioned to failure.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The request build fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest,
+ removingFwdAddrsHandler_BuildRequestException) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, FORWARD_CHG));
+
+ // Set the one-shot exception simulation flag.
+ name_remove->simulate_build_request_exception_ = true;
+
+ // Run removingFwdAddrsHandler to construct and send the request.
+ // This should fail with a build request throw which should be caught
+ // in the state handler.
+ ASSERT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Verify we did not attempt to send anything.
+ EXPECT_EQ(0, name_remove->getUpdateAttempts());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since IO exceptions should be gracefully handled, any that occur
+ // are unanticipated, and deemed unrecoverable, so the transaction should
+ // be transitioned to failure.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The request build fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest,
+ removingFwdRRsHandler_BuildRequestException) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_RRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, FORWARD_CHG));
+
+ // Set the one-shot exception simulation flag.
+ name_remove->simulate_build_request_exception_ = true;
+
+ // Run removingFwdRRsHandler to construct and send the request.
+ // This should fail with a build request throw which should be caught
+ // in the state handler.
+ ASSERT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Verify we did not attempt to send anything.
+ EXPECT_EQ(0, name_remove->getUpdateAttempts());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since IO exceptions should be gracefully handled, any that occur
+ // are unanticipated, and deemed unrecoverable, so the transaction should
+ // be transitioned to failure.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingRevPTRsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The request build fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest,
+ removingRevPTRsHandler_BuildRequestException) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, FORWARD_CHG));
+
+ // Set the one-shot exception simulation flag.
+ name_remove->simulate_build_request_exception_ = true;
+
+ // Run removingRevPtrsHandler to construct and send the request.
+ // This should fail with a build request throw which should be caught
+ // in the state handler.
+ ASSERT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Verify we did not attempt to send anything.
+ EXPECT_EQ(0, name_remove->getUpdateAttempts());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since IO exceptions should be gracefully handled, any that occur
+ // are unanticipated, and deemed unrecoverable, so the transaction should
+ // be transitioned to failure.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+}
diff --git a/src/bin/d2/tests/nc_test_utils.cc b/src/bin/d2/tests/nc_test_utils.cc
index 2df18e9..bebc06c 100644
--- a/src/bin/d2/tests/nc_test_utils.cc
+++ b/src/bin/d2/tests/nc_test_utils.cc
@@ -29,6 +29,11 @@ namespace d2 {
const char* TEST_DNS_SERVER_IP = "127.0.0.1";
size_t TEST_DNS_SERVER_PORT = 5301;
+const bool HAS_RDATA = true;
+const bool NO_RDATA = false;
+
+//*************************** FauxServer class ***********************
+
FauxServer::FauxServer(asiolink::IOService& io_service,
asiolink::IOAddress& address, size_t port)
:io_service_(io_service), address_(address), port_(port),
@@ -136,6 +141,135 @@ FauxServer::requestHandler(const asio::error_code& error,
}
}
+//********************** TransactionTest class ***********************
+
+const unsigned int TransactionTest::FORWARD_CHG = 0x01;
+const unsigned int TransactionTest::REVERSE_CHG = 0x02;
+const unsigned int TransactionTest::FWD_AND_REV_CHG = REVERSE_CHG | FORWARD_CHG;
+
+TransactionTest::TransactionTest()
+ : io_service_(new isc::asiolink::IOService()), ncr_(),
+ timer_(*io_service_), run_time_(0) {
+}
+
+TransactionTest::~TransactionTest() {
+}
+
+void
+TransactionTest::runTimedIO(int run_time) {
+ run_time_ = run_time;
+ timer_.setup(boost::bind(&TransactionTest::timesUp, this), run_time_);
+ io_service_->run();
+}
+
+void
+TransactionTest::timesUp() {
+ io_service_->stop();
+ FAIL() << "Test Time: " << run_time_ << " expired";
+}
+
+void
+TransactionTest::setupForIPv4Transaction(dhcp_ddns::NameChangeType chg_type,
+ int change_mask) {
+ const char* msg_str =
+ "{"
+ " \"change_type\" : 0 , "
+ " \"forward_change\" : true , "
+ " \"reverse_change\" : true , "
+ " \"fqdn\" : \"my.forward.example.com.\" , "
+ " \"ip_address\" : \"192.168.2.1\" , "
+ " \"dhcid\" : \"0102030405060708\" , "
+ " \"lease_expires_on\" : \"20130121132405\" , "
+ " \"lease_length\" : 1300 "
+ "}";
+
+ // Create NameChangeRequest from JSON string.
+ ncr_ = dhcp_ddns::NameChangeRequest::fromJSON(msg_str);
+
+ // Set the change type.
+ ncr_->setChangeType(chg_type);
+
+ // If the change mask does not include a forward change clear the
+ // forward domain; otherwise create the domain and its servers.
+ if (!(change_mask & FORWARD_CHG)) {
+ ncr_->setForwardChange(false);
+ forward_domain_.reset();
+ } else {
+ // Create the forward domain and then its servers.
+ forward_domain_ = makeDomain("example.com.");
+ addDomainServer(forward_domain_, "forward.example.com",
+ "127.0.0.1", 5301);
+ addDomainServer(forward_domain_, "forward2.example.com",
+ "127.0.0.1", 5302);
+ }
+
+ // If the change mask does not include a reverse change clear the
+ // reverse domain; otherwise create the domain and its servers.
+ if (!(change_mask & REVERSE_CHG)) {
+ ncr_->setReverseChange(false);
+ reverse_domain_.reset();
+ } else {
+ // Create the reverse domain and its server.
+ reverse_domain_ = makeDomain("2.168.192.in.addr.arpa.");
+ addDomainServer(reverse_domain_, "reverse.example.com",
+ "127.0.0.1", 5301);
+ addDomainServer(reverse_domain_, "reverse2.example.com",
+ "127.0.0.1", 5302);
+ }
+}
+
+void
+TransactionTest::setupForIPv6Transaction(dhcp_ddns::NameChangeType chg_type,
+ int change_mask) {
+ const char* msg_str =
+ "{"
+ " \"change_type\" : 0 , "
+ " \"forward_change\" : true , "
+ " \"reverse_change\" : true , "
+ " \"fqdn\" : \"my6.forward.example.com.\" , "
+ " \"ip_address\" : \"2001:1::100\" , "
+ " \"dhcid\" : \"0102030405060708\" , "
+ " \"lease_expires_on\" : \"20130121132405\" , "
+ " \"lease_length\" : 1300 "
+ "}";
+
+ // Create NameChangeRequest from JSON string.
+ ncr_ = makeNcrFromString(msg_str);
+
+ // Set the change type.
+ ncr_->setChangeType(chg_type);
+
+ // If the change mask does not include a forward change clear the
+ // forward domain; otherwise create the domain and its servers.
+ if (!(change_mask & FORWARD_CHG)) {
+ ncr_->setForwardChange(false);
+ forward_domain_.reset();
+ } else {
+ // Create the forward domain and then its servers.
+ forward_domain_ = makeDomain("example.com.");
+ addDomainServer(forward_domain_, "fwd6-server.example.com",
+ "::1", 5301);
+ addDomainServer(forward_domain_, "fwd6-server2.example.com",
+ "::1", 5302);
+ }
+
+ // If the change mask does not include a reverse change clear the
+ // reverse domain; otherwise create the domain and its servers.
+ if (!(change_mask & REVERSE_CHG)) {
+ ncr_->setReverseChange(false);
+ reverse_domain_.reset();
+ } else {
+ // Create the reverse domain and its server.
+ reverse_domain_ = makeDomain("1.2001.ip6.arpa.");
+ addDomainServer(reverse_domain_, "rev6-server.example.com",
+ "::1", 5301);
+ addDomainServer(reverse_domain_, "rev6-server2.example.com",
+ "::1", 5302);
+ }
+}
+
+
+//********************** Functions ****************************
void
checkRRCount(const D2UpdateMessagePtr& request,
@@ -158,13 +292,15 @@ checkZone(const D2UpdateMessagePtr& request, const std::string& exp_zone_name) {
void
checkRR(dns::RRsetPtr rrset, const std::string& exp_name,
const dns::RRClass& exp_class, const dns::RRType& exp_type,
- unsigned int exp_ttl, dhcp_ddns::NameChangeRequestPtr ncr) {
+ unsigned int exp_ttl, dhcp_ddns::NameChangeRequestPtr ncr,
+ bool has_rdata) {
// Verify the FQDN/DHCID RR fields.
EXPECT_EQ(exp_name, rrset->getName().toText());
EXPECT_EQ(exp_class.getCode(), rrset->getClass().getCode());
EXPECT_EQ(exp_type.getCode(), rrset->getType().getCode());
EXPECT_EQ(exp_ttl, rrset->getTTL().getValue());
- if (exp_type == dns::RRType::ANY() || exp_class == dns::RRClass::ANY()) {
+ if ((!has_rdata) ||
+ (exp_type == dns::RRType::ANY() || exp_class == dns::RRClass::ANY())) {
// ANY types do not have RData
ASSERT_EQ(0, rrset->getRdataCount());
return;
@@ -229,7 +365,7 @@ void addDomainServer(DdnsDomainPtr& domain, const std::string& name,
// Verifies that the contents of the given transaction's DNS update request
// is correct for adding a forward DNS entry
-void checkForwardAddRequest(NameChangeTransaction& tran) {
+void checkAddFwdAddressRequest(NameChangeTransaction& tran) {
const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
ASSERT_TRUE(request);
@@ -276,7 +412,7 @@ void checkForwardAddRequest(NameChangeTransaction& tran) {
// Verifies that the contents of the given transaction's DNS update request
// is correct for replacing a forward DNS entry
-void checkForwardReplaceRequest(NameChangeTransaction& tran) {
+void checkReplaceFwdAddressRequest(NameChangeTransaction& tran) {
const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
ASSERT_TRUE(request);
@@ -332,7 +468,7 @@ void checkForwardReplaceRequest(NameChangeTransaction& tran) {
// Verifies that the contents of the given transaction's DNS update request
// is correct for replacing a reverse DNS entry
-void checkReverseReplaceRequest(NameChangeTransaction& tran) {
+void checkReplaceRevPtrsRequest(NameChangeTransaction& tran) {
const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
ASSERT_TRUE(request);
@@ -390,5 +526,133 @@ void checkReverseReplaceRequest(NameChangeTransaction& tran) {
ASSERT_NO_THROW(request->toWire(renderer));
}
+void checkRemoveFwdAddressRequest(NameChangeTransaction& tran) {
+ const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
+ ASSERT_TRUE(request);
+
+ // Safety check.
+ dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr();
+ ASSERT_TRUE(ncr);
+
+ std::string exp_zone_name = tran.getForwardDomain()->getName();
+ std::string exp_fqdn = ncr->getFqdn();
+
+ // Verify the zone section.
+ checkZone(request, exp_zone_name);
+
+ // Verify there is 1 RR in the PREREQUISITE Section.
+ checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 1);
+
+ // Verify the DHCID matching assertion RR.
+ dns::RRsetPtr rrset;
+ ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+ SECTION_PREREQUISITE, 0));
+ checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(),
+ 0, ncr);
+
+ // Verify there is 1 RR in the UPDATE Section.
+ checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 1);
+
+ // Verify the FQDN/IP delete RR.
+ const dns::RRType& exp_ip_rr_type = tran.getAddressRRType();
+ ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+ SECTION_UPDATE, 0));
+ checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), exp_ip_rr_type,
+ 0, ncr);
+
+ // Verify that it will render toWire without throwing.
+ dns::MessageRenderer renderer;
+ ASSERT_NO_THROW(request->toWire(renderer));
+}
+
+void checkRemoveFwdRRsRequest(NameChangeTransaction& tran) {
+ const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
+ ASSERT_TRUE(request);
+
+ // Safety check.
+ dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr();
+ ASSERT_TRUE(ncr);
+
+ std::string exp_zone_name = tran.getForwardDomain()->getName();
+ std::string exp_fqdn = ncr->getFqdn();
+
+ // Verify the zone section.
+ checkZone(request, exp_zone_name);
+
+ // Verify there is 1 RR in the PREREQUISITE Section.
+ checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 3);
+
+ // Verify the DHCID matches assertion.
+ dns::RRsetPtr rrset;
+ ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+ SECTION_PREREQUISITE, 0));
+ checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(),
+ 0, ncr);
+
+ // Verify the NO A RRs assertion.
+ ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+ SECTION_PREREQUISITE, 1));
+ checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), dns::RRType::A(),
+ 0, ncr, NO_RDATA);
+
+ // Verify the NO AAAA RRs assertion.
+ ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+ SECTION_PREREQUISITE, 2));
+ checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), dns::RRType::AAAA(),
+ 0, ncr, NO_RDATA);
+
+ // Verify there is 1 RR in the UPDATE Section.
+ checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 1);
+
+ // Verify the delete all for the FQDN RR.
+ ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+ SECTION_UPDATE, 0));
+ checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::ANY(),
+ 0, ncr);
+
+ // Verify that it will render toWire without throwing.
+ dns::MessageRenderer renderer;
+ ASSERT_NO_THROW(request->toWire(renderer));
+}
+
+void checkRemoveRevPtrsRequest(NameChangeTransaction& tran) {
+ const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
+ ASSERT_TRUE(request);
+
+ // Safety check.
+ dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr();
+ ASSERT_TRUE(ncr);
+
+ std::string exp_zone_name = tran.getReverseDomain()->getName();
+ std::string exp_rev_addr = D2CfgMgr::reverseIpAddress(ncr->getIpAddress());
+
+ // Verify the zone section.
+ checkZone(request, exp_zone_name);
+
+ // Verify there is 1 RR in the PREREQUISITE Section.
+ checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 1);
+
+ // Verify the FQDN-PTRNAME assertion RR.
+ dns::RRsetPtr rrset;
+ ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+ SECTION_PREREQUISITE, 0));
+ checkRR(rrset, exp_rev_addr, dns::RRClass::IN(), dns::RRType::PTR(),
+ 0, ncr);
+
+ // Verify there is 1 RR in the UPDATE Section.
+ checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 1);
+
+ // Verify the delete all for the FQDN RR.
+ ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+ SECTION_UPDATE, 0));
+ checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::ANY(),
+ 0, ncr);
+
+ // Verify that it will render toWire without throwing.
+ dns::MessageRenderer renderer;
+ ASSERT_NO_THROW(request->toWire(renderer));
+}
+
+
}; // namespace isc::d2
}; // namespace isc
diff --git a/src/bin/d2/tests/nc_test_utils.h b/src/bin/d2/tests/nc_test_utils.h
index 7cffd05..9b96b2e 100644
--- a/src/bin/d2/tests/nc_test_utils.h
+++ b/src/bin/d2/tests/nc_test_utils.h
@@ -21,6 +21,7 @@
#include <asio/ip/udp.hpp>
#include <asio/socket_base.hpp>
+#include <gtest/gtest.h>
namespace isc {
namespace d2 {
@@ -94,6 +95,68 @@ public:
const dns::Rcode& response_rcode);
};
+/// @brief Base class Test fixture for testing transactions.
+class TransactionTest : public ::testing::Test {
+public:
+ IOServicePtr io_service_;
+ dhcp_ddns::NameChangeRequestPtr ncr_;
+ DdnsDomainPtr forward_domain_;
+ DdnsDomainPtr reverse_domain_;
+ asiolink::IntervalTimer timer_;
+ int run_time_;
+
+ /// #brief constants used to specify change directions for a transaction.
+ static const unsigned int FORWARD_CHG; // Only forward change.
+ static const unsigned int REVERSE_CHG; // Only reverse change.
+ static const unsigned int FWD_AND_REV_CHG; // Both forward and reverse.
+
+ TransactionTest();
+ virtual ~TransactionTest();
+
+ /// @brief Run the IO service for no more than a given amount of time.
+ ///
+ /// Uses an IntervalTimer to interrupt the invocation of IOService run(),
+ /// after the given number of milliseconds elapse. The timer executes
+ /// the timesUp() method if it expires.
+ ///
+ /// @param run_time amount of time in milliseconds to allow run to execute.
+ void runTimedIO(int run_time);
+
+ /// @brief IO Timer expiration handler
+ ///
+ /// Stops the IOSerivce and fails the current test.
+ virtual void timesUp();
+
+ /// @brief Creates a transaction which requests an IPv4 DNS update.
+ ///
+ /// The transaction is constructed around a predefined (i.e. "canned")
+ /// IPv4 NameChangeRequest. The request has both forward and reverse DNS
+ /// changes requested. Based upon the change mask, the transaction
+ /// will have either the forward, reverse, or both domains populated.
+ ///
+ /// @param change_type selects the type of change requested, CHG_ADD or
+ /// CHG_REMOVE.
+ /// @param change_mask determines which change directions are requested
+ /// FORWARD_CHG, REVERSE_CHG, or FWD_AND_REV_CHG.
+ void setupForIPv4Transaction(dhcp_ddns::NameChangeType change_type,
+ int change_mask);
+
+ /// @brief Creates a transaction which requests an IPv6 DNS update.
+ ///
+ /// The transaction is constructed around a predefined (i.e. "canned")
+ /// IPv6 NameChangeRequest. The request has both forward and reverse DNS
+ /// changes requested. Based upon the change mask, the transaction
+ /// will have either the forward, reverse, or both domains populated.
+ ///
+ /// @param change_type selects the type of change requested, CHG_ADD or
+ /// CHG_REMOVE.
+ /// @param change_mask determines which change directions are requested
+ /// FORWARD_CHG, REVERSE_CHG, or FWD_AND_REV_CHG.
+ void setupForIPv6Transaction(dhcp_ddns::NameChangeType change_type,
+ int change_mask);
+};
+
+
/// @brief Tests the number of RRs in a request section against a given count.
///
/// This function actually returns the number of RRsetPtrs in a section. Since
@@ -124,9 +187,14 @@ extern void checkZone(const D2UpdateMessagePtr& request,
/// @param exp_typ expected RRType value of RRset
/// @param exp_ttl expected TTL value of RRset
/// @param ncr NameChangeRequest on which the RRset is based
+/// @param has_rdata if true, RRset's rdata will be checked based on it's
+/// RRType. Set this to false if the RRset's type supports Rdata but it does
+/// not contain it. For instance, prerequisites of type NONE have no Rdata
+/// where udpates of type NONE may.
extern void checkRR(dns::RRsetPtr rrset, const std::string& exp_name,
const dns::RRClass& exp_class, const dns::RRType& exp_type,
- unsigned int exp_ttl, dhcp_ddns::NameChangeRequestPtr ncr);
+ unsigned int exp_ttl, dhcp_ddns::NameChangeRequestPtr ncr,
+ bool has_rdata=true);
/// @brief Fetches an RR(set) from a given section of a request
///
@@ -163,7 +231,7 @@ extern dns::RRsetPtr getRRFromSection(const D2UpdateMessagePtr& request,
/// adding a forward DNS mapping.
///
/// @param tran Transaction containing the request to be verified.
-extern void checkForwardAddRequest(NameChangeTransaction& tran);
+extern void checkAddFwdAddressRequest(NameChangeTransaction& tran);
/// @brief Verifies a forward mapping replacement DNS update request
///
@@ -171,7 +239,7 @@ extern void checkForwardAddRequest(NameChangeTransaction& tran);
/// replacing a forward DNS mapping.
///
/// @param tran Transaction containing the request to be verified.
-extern void checkForwardReplaceRequest(NameChangeTransaction& tran);
+extern void checkReplaceFwdAddressRequest(NameChangeTransaction& tran);
/// @brief Verifies a reverse mapping replacement DNS update request
///
@@ -179,7 +247,32 @@ extern void checkForwardReplaceRequest(NameChangeTransaction& tran);
/// replacing a reverse DNS mapping.
///
/// @param tran Transaction containing the request to be verified.
-extern void checkReverseReplaceRequest(NameChangeTransaction& tran);
+extern void checkReplaceRevPtrsRequest(NameChangeTransaction& tran);
+
+/// @brief Verifies a forward address removal DNS update request
+///
+/// Tests that the DNS Update request for a given transaction, is correct for
+/// removing the forward address DNS entry.
+///
+/// @param tran Transaction containing the request to be verified.
+extern void checkRemoveFwdAddressRequest(NameChangeTransaction& tran);
+
+/// @brief Verifies a forward RR removal DNS update request
+///
+/// Tests that the DNS Update request for a given transaction, is correct for
+/// removing forward RR DNS entries.
+///
+/// @param tran Transaction containing the request to be verified.
+extern void checkRemoveFwdRRsRequest(NameChangeTransaction& tran);
+
+/// @brief Verifies a reverse mapping removal DNS update request
+///
+/// Tests that the DNS Update request for a given transaction, is correct for
+/// removing a reverse DNS mapping.
+///
+/// @param tran Transaction containing the request to be verified.
+extern void checkRemoveRevPtrsRequest(NameChangeTransaction& tran);
+
/// @brief Creates a NameChangeRequest from JSON string.
///
diff --git a/src/bin/d2/tests/nc_trans_unittests.cc b/src/bin/d2/tests/nc_trans_unittests.cc
index 6e1c3fc..b235ef1 100644
--- a/src/bin/d2/tests/nc_trans_unittests.cc
+++ b/src/bin/d2/tests/nc_trans_unittests.cc
@@ -997,19 +997,10 @@ TEST_F(NameChangeTransactionTest, addLeaseAddressRData) {
ASSERT_NO_THROW(name_change = makeCannedTransaction());
dhcp_ddns::NameChangeRequestPtr ncr = name_change->getNcr();
- // Test a lease rdata add failure.
- // As you cannot stuff an invalid address into an NCR, the only failure
- // that can be induced is a mismatch between the RData and the RRset.
- // Attempt to add a lease address Rdata, this should fail.
- // Create an Any class/Any type RRset, they are not allowed to contain
- // rdata.
- dns::RRsetPtr rrset(new dns::RRset(dns::Name("bs"), dns::RRClass::ANY(),
- dns::RRType::ANY(), dns::RRTTL(0)));
- ASSERT_THROW(name_change->addLeaseAddressRdata(rrset), std::exception);
-
// Verify we can add a lease RData to an valid RRset.
- rrset.reset(new dns::RRset(dns::Name("bs"), dns::RRClass::IN(),
- name_change->getAddressRRType(), dns::RRTTL(0)));
+ dns::RRsetPtr rrset(new dns::RRset(dns::Name("bs"), dns::RRClass::IN(),
+ name_change->getAddressRRType(),
+ dns::RRTTL(0)));
ASSERT_NO_THROW(name_change->addLeaseAddressRdata(rrset));
// Verify the Rdata was added and the value is correct.
@@ -1026,14 +1017,9 @@ TEST_F(NameChangeTransactionTest, addDhcidRdata) {
ASSERT_NO_THROW(name_change = makeCannedTransaction());
dhcp_ddns::NameChangeRequestPtr ncr = name_change->getNcr();
- // Test a DHCID rdata add failure.
- dns::RRsetPtr rrset(new dns::RRset(dns::Name("bs"), dns::RRClass::ANY(),
- dns::RRType::ANY(), dns::RRTTL(0)));
- ASSERT_THROW(name_change->addDhcidRdata(rrset), std::exception);
-
// Verify we can add a lease RData to an valid RRset.
- rrset.reset(new dns::RRset(dns::Name("bs"), dns::RRClass::IN(),
- dns::RRType::DHCID(), dns::RRTTL(0)));
+ dns::RRsetPtr rrset(new dns::RRset(dns::Name("bs"), dns::RRClass::IN(),
+ dns::RRType::DHCID(), dns::RRTTL(0)));
ASSERT_NO_THROW(name_change->addDhcidRdata(rrset));
// Verify the Rdata was added and the value is correct.
@@ -1053,14 +1039,9 @@ TEST_F(NameChangeTransactionTest, addPtrRdata) {
ASSERT_NO_THROW(name_change = makeCannedTransaction());
dhcp_ddns::NameChangeRequestPtr ncr = name_change->getNcr();
- // Test a PTR rdata add failure.
- dns::RRsetPtr rrset(new dns::RRset(dns::Name("bs"), dns::RRClass::ANY(),
- dns::RRType::ANY(), dns::RRTTL(0)));
- ASSERT_THROW(name_change->addPtrRdata(rrset), std::exception);
-
// Verify we can add a PTR RData to an valid RRset.
- rrset.reset(new dns::RRset(dns::Name("bs"), dns::RRClass::IN(),
- dns::RRType::PTR(), dns::RRTTL(0)));
+ dns::RRsetPtr rrset (new dns::RRset(dns::Name("bs"), dns::RRClass::IN(),
+ dns::RRType::PTR(), dns::RRTTL(0)));
ASSERT_NO_THROW(name_change->addPtrRdata(rrset));
// Verify the Rdata was added and the value is correct.
diff --git a/src/bin/dhcp4/config_parser.cc b/src/bin/dhcp4/config_parser.cc
index a5acac0..777cce5 100644
--- a/src/bin/dhcp4/config_parser.cc
+++ b/src/bin/dhcp4/config_parser.cc
@@ -216,7 +216,6 @@ protected:
return (parser);
}
-
/// @brief Determines if the given option space name and code describe
/// a standard option for the DCHP4 server.
///
@@ -395,6 +394,8 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
parser = new DbAccessParser(config_id);
} else if (config_id.compare("hooks-libraries") == 0) {
parser = new HooksLibrariesParser(config_id);
+ } else if (config_id.compare("echo-client-id") == 0) {
+ parser = new BooleanParser(config_id, globalContext()->boolean_values_);
} else {
isc_throw(NotImplemented,
"Parser error: Global configuration parameter not supported: "
@@ -404,6 +405,20 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
return (parser);
}
+void commitGlobalOptions() {
+ // Although the function is modest for now, it is certain that the number
+ // of global switches will increase over time, hence the name.
+
+ // Set whether v4 server is supposed to echo back client-id (yes = RFC6842
+ // compatible, no = backward compatibility)
+ try {
+ bool echo_client_id = globalContext()->boolean_values_->getParam("echo-client-id");
+ CfgMgr::instance().echoClientId(echo_client_id);
+ } catch (...) {
+ // Ignore errors. This flag is optional
+ }
+}
+
isc::data::ConstElementPtr
configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
if (!config_set) {
@@ -536,6 +551,9 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
iface_parser->commit();
}
+ // Apply global options
+ commitGlobalOptions();
+
// This occurs last as if it succeeds, there is no easy way
// revert it. As a result, the failure to commit a subsequent
// change causes problems when trying to roll back.
@@ -579,4 +597,3 @@ ParserContextPtr& globalContext() {
}; // end of isc::dhcp namespace
}; // end of isc namespace
-
diff --git a/src/bin/dhcp4/dhcp4.spec b/src/bin/dhcp4/dhcp4.spec
index b507159..9372dfd 100644
--- a/src/bin/dhcp4/dhcp4.spec
+++ b/src/bin/dhcp4/dhcp4.spec
@@ -54,6 +54,12 @@
"item_default": ""
},
+ { "item_name": "echo-client-id",
+ "item_type": "boolean",
+ "item_optional": true,
+ "item_default": true
+ },
+
{ "item_name": "option-def",
"item_type": "list",
"item_optional": false,
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index 329d604..5e5572b 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -653,8 +653,10 @@ Dhcpv4Srv::copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer) {
answer->setGiaddr(question->getGiaddr());
// Let's copy client-id to response. See RFC6842.
+ // It is possible to disable RFC6842 to keep backward compatibility
+ bool echo = CfgMgr::instance().echoClientId();
OptionPtr client_id = question->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
- if (client_id) {
+ if (client_id && echo) {
answer->addOption(client_id);
}
diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc
index 6e53d4d..1d9ccb7 100644
--- a/src/bin/dhcp4/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp4/tests/config_parser_unittest.cc
@@ -557,6 +557,47 @@ TEST_F(Dhcp4ParserTest, nextServerOverride) {
EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText());
}
+// Check whether it is possible to configure echo-client-id
+TEST_F(Dhcp4ParserTest, echoClientId) {
+
+ ConstElementPtr status;
+
+ string config_false = "{ \"interfaces\": [ \"*\" ],"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"echo-client-id\": false,"
+ "\"subnet4\": [ { "
+ " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+ " \"subnet\": \"192.0.2.0/24\" } ],"
+ "\"valid-lifetime\": 4000 }";
+
+ string config_true = "{ \"interfaces\": [ \"*\" ],"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"echo-client-id\": true,"
+ "\"subnet4\": [ { "
+ " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+ " \"subnet\": \"192.0.2.0/24\" } ],"
+ "\"valid-lifetime\": 4000 }";
+
+ ElementPtr json_false = Element::fromJSON(config_false);
+ ElementPtr json_true = Element::fromJSON(config_true);
+
+ // Let's check the default. It should be true
+ ASSERT_TRUE(CfgMgr::instance().echoClientId());
+
+ // Now check that "false" configuration is really applied.
+ EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json_false));
+ ASSERT_FALSE(CfgMgr::instance().echoClientId());
+
+ // Now check that "true" configuration is really applied.
+ EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json_true));
+ ASSERT_TRUE(CfgMgr::instance().echoClientId());
+
+ // In any case revert back to the default value (true)
+ CfgMgr::instance().echoClientId(true);
+}
+
// This test checks if it is possible to override global values
// on a per subnet basis.
TEST_F(Dhcp4ParserTest, subnetLocal) {
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
index a15bbad..3787149 100644
--- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -608,6 +608,32 @@ TEST_F(Dhcpv4SrvTest, ManyDiscovers) {
cout << "Offered address to client3=" << addr3.toText() << endl;
}
+// Checks whether echoing back client-id is controllable, i.e.
+// whether the server obeys echo-client-id and sends (or not)
+// client-id
+TEST_F(Dhcpv4SrvTest, discoverEchoClientId) {
+ NakedDhcpv4Srv srv(0);
+
+ Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+ dis->setRemoteAddr(IOAddress("192.0.2.1"));
+ OptionPtr clientid = generateClientId();
+ dis->addOption(clientid);
+
+ // Pass it to the server and get an offer
+ Pkt4Ptr offer = srv.processDiscover(dis);
+
+ // Check if we get response at all
+ checkResponse(offer, DHCPOFFER, 1234);
+ checkClientId(offer, clientid);
+
+ CfgMgr::instance().echoClientId(false);
+ offer = srv.processDiscover(dis);
+
+ // Check if we get response at all
+ checkResponse(offer, DHCPOFFER, 1234);
+ checkClientId(offer, clientid);
+}
+
// This test verifies that incoming REQUEST can be handled properly, that an
// ACK is generated, that the response has an address and that address
// really belongs to the configured pool.
@@ -750,6 +776,30 @@ TEST_F(Dhcpv4SrvTest, ManyRequests) {
cout << "Offered address to client3=" << addr3.toText() << endl;
}
+// Checks whether echoing back client-id is controllable
+TEST_F(Dhcpv4SrvTest, requestEchoClientId) {
+ NakedDhcpv4Srv srv(0);
+
+ Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+ dis->setRemoteAddr(IOAddress("192.0.2.1"));
+ OptionPtr clientid = generateClientId();
+ dis->addOption(clientid);
+
+ // Pass it to the server and get ACK
+ Pkt4Ptr ack = srv.processRequest(dis);
+
+ // Check if we get response at all
+ checkResponse(ack, DHCPACK, 1234);
+ checkClientId(ack, clientid);
+
+ CfgMgr::instance().echoClientId(false);
+ ack = srv.processDiscover(dis);
+
+ // Check if we get response at all
+ checkResponse(ack, DHCPOFFER, 1234);
+ checkClientId(ack, clientid);
+}
+
// This test verifies that incoming (positive) REQUEST/Renewing can be handled properly, that a
// REPLY is generated, that the response has an address and that address
diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.cc b/src/bin/dhcp4/tests/dhcp4_test_utils.cc
index e29c31d..8590927 100644
--- a/src/bin/dhcp4/tests/dhcp4_test_utils.cc
+++ b/src/bin/dhcp4/tests/dhcp4_test_utils.cc
@@ -67,6 +67,12 @@ Dhcpv4SrvTest::Dhcpv4SrvTest()
valid_iface_ = ifaces.begin()->getName();
}
+Dhcpv4SrvTest::~Dhcpv4SrvTest() {
+
+ // Make sure that we revert to default value
+ CfgMgr::instance().echoClientId(true);
+}
+
void Dhcpv4SrvTest::addPrlOption(Pkt4Ptr& pkt) {
OptionUint8ArrayPtr option_prl =
@@ -332,12 +338,21 @@ void Dhcpv4SrvTest::checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_
}
void Dhcpv4SrvTest::checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid) {
+
+ bool include_clientid = CfgMgr::instance().echoClientId();
+
// check that server included our own client-id
OptionPtr opt = rsp->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
- ASSERT_TRUE(opt);
- EXPECT_EQ(expected_clientid->getType(), opt->getType());
- EXPECT_EQ(expected_clientid->len(), opt->len());
- EXPECT_TRUE(expected_clientid->getData() == opt->getData());
+ if (include_clientid) {
+ // Normal mode: echo back (see RFC6842)
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(expected_clientid->getType(), opt->getType());
+ EXPECT_EQ(expected_clientid->len(), opt->len());
+ EXPECT_TRUE(expected_clientid->getData() == opt->getData());
+ } else {
+ // Backward compatibility mode for pre-RFC6842 devices
+ ASSERT_FALSE(opt);
+ }
}
::testing::AssertionResult
diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h
index c448a2f..26ed87d 100644
--- a/src/bin/dhcp4/tests/dhcp4_test_utils.h
+++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h
@@ -81,8 +81,7 @@ public:
Dhcpv4SrvTest();
/// @brief destructor
- virtual ~Dhcpv4SrvTest() {
- }
+ virtual ~Dhcpv4SrvTest();
/// @brief Add 'Parameter Request List' option to the packet.
///
@@ -189,6 +188,11 @@ public:
void checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid);
/// @brief Checks if server response (OFFER, ACK, NAK) includes proper client-id
+ ///
+ /// This method follows values reported by CfgMgr in echoClientId() method.
+ /// Depending on its configuration, the client-id is either mandatory or
+ /// forbidden to appear in the response.
+ ///
/// @param rsp response packet to be validated
/// @param expected_clientid expected value of client-id
void checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid);
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
index 2a220b8..0ac845d 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -208,9 +208,10 @@ public:
///
/// @note Implementation of this method is OS-dependent as bits have
/// different meaning on each OS.
+ /// We need to make it 64 bits, because Solaris uses 64, not 32 bits.
///
/// @param flags bitmask value returned by OS in interface detection
- void setFlags(uint32_t flags);
+ void setFlags(uint64_t flags);
/// @brief Returns interface index.
///
@@ -364,7 +365,9 @@ public:
/// Interface flags (this value is as is returned by OS,
/// it may mean different things on different OSes).
- uint32_t flags_;
+ /// Solaris based os have unsigned long flags field (64 bits).
+ /// It is usually 32 bits, though.
+ uint64_t flags_;
/// Indicates that IPv4 sockets should (true) or should not (false)
/// be opened on this interface.
diff --git a/src/lib/dhcp/iface_mgr_bsd.cc b/src/lib/dhcp/iface_mgr_bsd.cc
index 2293877..be73bcf 100644
--- a/src/lib/dhcp/iface_mgr_bsd.cc
+++ b/src/lib/dhcp/iface_mgr_bsd.cc
@@ -20,6 +20,12 @@
#include <dhcp/pkt_filter_inet.h>
#include <exceptions/exceptions.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if_dl.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+
using namespace std;
using namespace isc;
using namespace isc::asiolink;
@@ -28,11 +34,97 @@ using namespace isc::dhcp;
namespace isc {
namespace dhcp {
+/// This is a BSD specific interface detection method.
void
IfaceMgr::detectIfaces() {
- /// @todo do the actual detection on BSDs. Currently just calling
- /// stub implementation.
- stubDetectIfaces();
+ struct ifaddrs* iflist = 0;// The whole interface list
+ struct ifaddrs* ifptr = 0; // The interface we're processing now
+
+ // Gets list of ifaddrs struct
+ if(getifaddrs(&iflist) != 0) {
+ isc_throw(Unexpected, "Network interfaces detection failed.");
+ }
+
+ typedef map<string, Iface> ifaceLst;
+ ifaceLst::iterator iface_iter;
+ ifaceLst ifaces;
+
+ // First lookup for getting interfaces ...
+ for (ifptr = iflist; ifptr != 0; ifptr = ifptr->ifa_next) {
+ const char * ifname = ifptr->ifa_name;
+ uint ifindex = 0;
+
+ if (!(ifindex = if_nametoindex(ifname))) {
+ // Interface name does not have corresponding index ...
+ freeifaddrs(iflist);
+ isc_throw(Unexpected, "Interface " << ifname << " has no index");
+ }
+
+ if ((iface_iter = ifaces.find(ifname)) != ifaces.end()) {
+ continue;
+ }
+
+ Iface iface(ifname, ifindex);
+ iface.setFlags(ifptr->ifa_flags);
+ ifaces.insert(pair<string, Iface>(ifname, iface));
+ }
+
+ // Second lookup to get MAC and IP addresses
+ for (ifptr = iflist; ifptr != 0; ifptr = ifptr->ifa_next) {
+ if ((iface_iter = ifaces.find(ifptr->ifa_name)) == ifaces.end()) {
+ continue;
+ }
+ // Common byte pointer for following data
+ const uint8_t * ptr = 0;
+ if(ifptr->ifa_addr->sa_family == AF_LINK) {
+ // HWAddr
+ struct sockaddr_dl * ldata =
+ reinterpret_cast<struct sockaddr_dl *>(ifptr->ifa_addr);
+ ptr = reinterpret_cast<uint8_t *>(LLADDR(ldata));
+
+ iface_iter->second.setHWType(ldata->sdl_type);
+ iface_iter->second.setMac(ptr, ldata->sdl_alen);
+ } else if(ifptr->ifa_addr->sa_family == AF_INET6) {
+ // IPv6 Addr
+ struct sockaddr_in6 * adata =
+ reinterpret_cast<struct sockaddr_in6 *>(ifptr->ifa_addr);
+ ptr = reinterpret_cast<uint8_t *>(&adata->sin6_addr);
+
+ IOAddress a = IOAddress::fromBytes(AF_INET6, ptr);
+ iface_iter->second.addAddress(a);
+ } else {
+ // IPv4 Addr
+ struct sockaddr_in * adata =
+ reinterpret_cast<struct sockaddr_in *>(ifptr->ifa_addr);
+ ptr = reinterpret_cast<uint8_t *>(&adata->sin_addr);
+
+ IOAddress a = IOAddress::fromBytes(AF_INET, ptr);
+ iface_iter->second.addAddress(a);
+ }
+ }
+
+ freeifaddrs(iflist);
+
+ // Interfaces registering
+ for(ifaceLst::const_iterator iface_iter = ifaces.begin();
+ iface_iter != ifaces.end(); ++iface_iter) {
+ ifaces_.push_back(iface_iter->second);
+ }
+}
+
+/// @brief sets flag_*_ fields
+///
+/// Like Linux version, os specific flags
+///
+/// @params flags
+void Iface::setFlags(uint64_t flags) {
+ flags_ = flags;
+
+ flag_loopback_ = flags & IFF_LOOPBACK;
+ flag_up_ = flags & IFF_UP;
+ flag_running_ = flags & IFF_RUNNING;
+ flag_multicast_ = flags & IFF_MULTICAST;
+ flag_broadcast_ = flags & IFF_BROADCAST;
}
void IfaceMgr::os_send4(struct msghdr& /*m*/,
diff --git a/src/lib/dhcp/iface_mgr_linux.cc b/src/lib/dhcp/iface_mgr_linux.cc
index f31c353..dddeb52 100644
--- a/src/lib/dhcp/iface_mgr_linux.cc
+++ b/src/lib/dhcp/iface_mgr_linux.cc
@@ -502,7 +502,7 @@ void IfaceMgr::detectIfaces() {
/// on different OSes.
///
/// @param flags flags bitfield read from OS
-void Iface::setFlags(uint32_t flags) {
+void Iface::setFlags(uint64_t flags) {
flags_ = flags;
flag_loopback_ = flags & IFF_LOOPBACK;
diff --git a/src/lib/dhcp/iface_mgr_sun.cc b/src/lib/dhcp/iface_mgr_sun.cc
index 46c4a97..fe2b0b3 100644
--- a/src/lib/dhcp/iface_mgr_sun.cc
+++ b/src/lib/dhcp/iface_mgr_sun.cc
@@ -20,6 +20,12 @@
#include <dhcp/pkt_filter_inet.h>
#include <exceptions/exceptions.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if_dl.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+
using namespace std;
using namespace isc;
using namespace isc::asiolink;
@@ -28,11 +34,100 @@ using namespace isc::dhcp;
namespace isc {
namespace dhcp {
+/// This is a Solaris specific interface detection code. It works on Solaris 11
+/// only, as earlier versions did not support getifaddrs() API.
void
IfaceMgr::detectIfaces() {
- /// @todo do the actual detection on Solaris. Currently just calling
- /// stub implementation.
- stubDetectIfaces();
+ struct ifaddrs * iflist = 0, * ifptr = 0;
+
+ // Gets list of ifaddrs struct
+ if(getifaddrs(& iflist) != 0) {
+ isc_throw(Unexpected, "Network interfaces detection failed.");
+ }
+
+ typedef std::map<string, Iface> ifaceLst;
+ ifaceLst::iterator iface_iter;
+ ifaceLst ifaces;
+
+ // First lookup for getting interfaces ...
+ for(ifptr = iflist; ifptr != 0; ifptr = ifptr->ifa_next) {
+ const char * ifname = ifptr->ifa_name;
+ uint ifindex = 0;
+
+ if (!(ifindex = if_nametoindex(ifname))) {
+ // Interface name does not have corresponding index ...
+ freeifaddrs(iflist);
+ isc_throw(Unexpected, "Interface " << ifname << " has no index");
+ }
+
+ iface_iter = ifaces.find(ifname);
+ if (iface_iter != ifaces.end()) {
+ continue;
+ }
+
+ Iface iface(ifname, ifindex);
+ iface.setFlags(ifptr->ifa_flags);
+ ifaces.insert(pair<string, Iface>(ifname, iface));
+ }
+
+ // Second lookup to get MAC and IP addresses
+ for (ifptr = iflist; ifptr != 0; ifptr = ifptr->ifa_next) {
+
+ iface_iter = ifaces.find(ifptr->ifa_name);
+ if (iface_iter == ifaces.end()) {
+ continue;
+ }
+ // Common byte pointer for following data
+ const uint8_t * ptr = 0;
+ if (ifptr->ifa_addr->sa_family == AF_LINK) {
+ // HWAddr
+ struct sockaddr_dl * ldata =
+ reinterpret_cast<struct sockaddr_dl *>(ifptr->ifa_addr);
+ ptr = reinterpret_cast<uint8_t *>(LLADDR(ldata));
+
+ iface_iter->second.setHWType(ldata->sdl_type);
+ iface_iter->second.setMac(ptr, ldata->sdl_alen);
+ } else if (ifptr->ifa_addr->sa_family == AF_INET6) {
+ // IPv6 Addr
+ struct sockaddr_in6 * adata =
+ reinterpret_cast<struct sockaddr_in6 *>(ifptr->ifa_addr);
+ ptr = reinterpret_cast<uint8_t *>(& adata->sin6_addr);
+
+ IOAddress a = IOAddress::fromBytes(AF_INET6, ptr);
+ iface_iter->second.addAddress(a);
+ } else {
+ // IPv4 Addr
+ struct sockaddr_in * adata =
+ reinterpret_cast<struct sockaddr_in *>(ifptr->ifa_addr);
+ ptr = reinterpret_cast<uint8_t *>(& adata->sin_addr);
+
+ IOAddress a = IOAddress::fromBytes(AF_INET, ptr);
+ iface_iter->second.addAddress(a);
+ }
+ }
+
+ freeifaddrs(iflist);
+
+ // Interfaces registering
+ for (ifaceLst::const_iterator iface_iter = ifaces.begin();
+ iface_iter != ifaces.end(); ++iface_iter) {
+ ifaces_.push_back(iface_iter->second);
+ }
+}
+
+/// @brief sets flag_*_ fields
+///
+/// Like Linux version, os specific flags
+///
+/// @params flags
+void Iface::setFlags(uint64_t flags) {
+ flags_ = flags;
+
+ flag_loopback_ = flags & IFF_LOOPBACK;
+ flag_up_ = flags & IFF_UP;
+ flag_running_ = flags & IFF_RUNNING;
+ flag_multicast_ = flags & IFF_MULTICAST;
+ flag_broadcast_ = flags & IFF_BROADCAST;
}
void IfaceMgr::os_send4(struct msghdr& /*m*/,
@@ -40,7 +135,8 @@ void IfaceMgr::os_send4(struct msghdr& /*m*/,
size_t /*control_buf_len*/,
const Pkt4Ptr& /*pkt*/) {
// @todo: Are there any specific actions required before sending IPv4 packet
- // on BSDs? See iface_mgr_linux.cc for working Linux implementation.
+ // on Solaris based systems? See iface_mgr_linux.cc
+ // for working Linux implementation.
}
bool IfaceMgr::os_receive4(struct msghdr& /*m*/, Pkt4Ptr& /*pkt*/) {
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index 4bc4a92..4577c64 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -163,9 +163,9 @@ LibDHCP::isStandardOption(const Option::Universe u, const uint16_t code) {
code == 126 ||
code == 127 ||
(code > 146 && code < 150) ||
- (code > 177 && code < 208) ||
+ (code > 177 && code < 208) ||
(code > 213 && code < 220) ||
- (code > 221 && code < 224))) {
+ (code > 221 && code < 255))) {
return (true);
}
diff --git a/src/lib/dhcp/libdhcp++.dox b/src/lib/dhcp/libdhcp++.dox
index 8ec0ea1..e1d7920 100644
--- a/src/lib/dhcp/libdhcp++.dox
+++ b/src/lib/dhcp/libdhcp++.dox
@@ -19,8 +19,9 @@
libdhcp++ is an all-purpose DHCP-manipulation library, written in
C++. It offers packet parsing and assembly, DHCPv4 and DHCPv6
-options parsing and ssembly, interface detection (currently on
-Linux systems only) and socket operations. It is a generic purpose library that
+options parsing and assembly, interface detection (currently on
+Linux, FreeBSD, NetBSD, OpenBSD, Max OS X, and Solaris 11) and socket operations.
+It is a generic purpose library that
can be used by server, client, relay, performance tools and other DHCP-related
tools. For server specific library, see \ref libdhcpsrv. Please do not
add any server-specific code to libdhcp++ and use \ref libdhcpsrv instead.
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index 0469b86..eaedacd 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -2158,7 +2158,7 @@ void parse_ifconfig(const std::string& textFile, IfaceMgr::IfaceCollection& ifac
// TODO: temporarily disabled, see ticket #1529
TEST_F(IfaceMgrTest, DISABLED_detectIfaces_linux) {
- NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+ scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
IfaceMgr::IfaceCollection& detectedIfaces = ifacemgr->getIfacesLst();
const std::string textFile = "ifconfig.txt";
@@ -2263,10 +2263,192 @@ TEST_F(IfaceMgrTest, DISABLED_detectIfaces_linux) {
FAIL();
}
}
+}
+#endif
- delete ifacemgr;
+#if defined(OS_BSD)
+#include <net/if_dl.h>
+#endif
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+
+/// @brief Checks the index of this interface
+/// @param iface Kea interface structure to be checked
+/// @param ifptr original structure returned by getifaddrs
+/// @return true if index is returned properly
+bool
+checkIfIndex(const Iface & iface,
+ struct ifaddrs *& ifptr) {
+ return (iface.getIndex() == if_nametoindex(ifptr->ifa_name));
+}
+
+/// @brief Checks if the interface has proper flags set
+/// @param iface Kea interface structure to be checked
+/// @param ifptr original structure returned by getifaddrs
+/// @return true if flags are set properly
+bool
+checkIfFlags(const Iface & iface,
+ struct ifaddrs *& ifptr) {
+ bool flag_loopback_ = ifptr->ifa_flags & IFF_LOOPBACK;
+ bool flag_up_ = ifptr->ifa_flags & IFF_UP;
+ bool flag_running_ = ifptr->ifa_flags & IFF_RUNNING;
+ bool flag_multicast_ = ifptr->ifa_flags & IFF_MULTICAST;
+
+ return ((iface.flag_loopback_ == flag_loopback_) &&
+ (iface.flag_up_ == flag_up_) &&
+ (iface.flag_running_ == flag_running_) &&
+ (iface.flag_multicast_ == flag_multicast_));
}
+
+/// @brief Checks if MAC Address/IP Addresses are properly well formed
+/// @param iface Kea interface structure to be checked
+/// @param ifptr original structure returned by getifaddrs
+/// @return true if addresses are returned properly
+bool
+checkIfAddrs(const Iface & iface, struct ifaddrs *& ifptr) {
+ const unsigned char * p = 0;
+#if defined(OS_LINUX)
+ // Workaround for Linux ...
+ if(ifptr->ifa_data != 0) {
+ // We avoid localhost as it has no MAC Address
+ if (!strncmp(iface.getName().c_str(), "lo", 2)) {
+ return (true);
+ }
+
+ struct ifreq ifr;
+ memset(& ifr.ifr_name, 0, sizeof ifr.ifr_name);
+ strncpy(ifr.ifr_name, iface.getName().c_str(), sizeof ifr.ifr_name);
+
+ int s = -1; // Socket descriptor
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ isc_throw(Unexpected, "Cannot create AF_INET socket");
+ }
+
+ if (ioctl(s, SIOCGIFHWADDR, & ifr) < 0) {
+ close(s);
+ isc_throw(Unexpected, "Cannot set SIOCGIFHWADDR flag");
+ }
+
+ const uint8_t * p =
+ reinterpret_cast<uint8_t *>(ifr.ifr_ifru.ifru_hwaddr.sa_data);
+
+ close(s);
+
+ /// @todo: Check MAC address length. For some interfaces it is
+ /// different than 6. Some have 0, while some exotic ones (like
+ /// infiniband) have 20.
+ return (!memcmp(p, iface.getMac(), iface.getMacLen()));
+ }
+#endif
+
+ if(!ifptr->ifa_addr) {
+ return (false);
+ }
+
+ switch(ifptr->ifa_addr->sa_family) {
+#if defined(OS_BSD)
+ case AF_LINK: {
+ // We avoid localhost as it has no MAC Address
+ if (!strncmp(iface.getName().c_str(), "lo", 2)) {
+ return (true);
+ }
+
+ struct sockaddr_dl * hwdata =
+ reinterpret_cast<struct sockaddr_dl *>(ifptr->ifa_addr);
+ p = reinterpret_cast<uint8_t *>(LLADDR(hwdata));
+
+ // Extract MAC address length
+ if (hwdata->sdl_alen != iface.getMacLen()) {
+ return (false);
+ }
+
+ return (!memcmp(p, iface.getMac(), hwdata->sdl_alen));
+ }
#endif
+ case AF_INET: {
+ struct sockaddr_in * v4data =
+ reinterpret_cast<struct sockaddr_in *>(ifptr->ifa_addr);
+ p = reinterpret_cast<uint8_t *>(& v4data->sin_addr);
+
+ IOAddress addrv4 = IOAddress::fromBytes(AF_INET, p);
+
+ for (Iface::AddressCollection::const_iterator a =
+ iface.getAddresses().begin();
+ a != iface.getAddresses().end(); ++ a) {
+ if(a->isV4() && (*a) == addrv4) {
+ return (true);
+ }
+ }
+
+ return (false);
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 * v6data =
+ reinterpret_cast<struct sockaddr_in6 *>(ifptr->ifa_addr);
+ p = reinterpret_cast<uint8_t *>(& v6data->sin6_addr);
+
+ IOAddress addrv6 = IOAddress::fromBytes(AF_INET6, p);
+
+ for(Iface::AddressCollection::const_iterator a =
+ iface.getAddresses().begin();
+ a != iface.getAddresses().end(); ++ a) {
+ if(a->isV6() && (*a) == addrv6) {
+ return (true);
+ }
+ }
+
+ return (false);
+ }
+ default:
+ return (true);
+ }
+}
+
+TEST_F(IfaceMgrTest, detectIfaces) {
+
+ NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+ IfaceMgr::IfaceCollection& detectedIfaces = ifacemgr->getIfacesLst();
+
+ // We are using struct ifaddrs as it is the only good portable one
+ // ifreq and ioctls are far from portabe. For BSD ifreq::ifa_flags field
+ // is only a short which, nowadays, can be negative
+ struct ifaddrs * iflist = 0, * ifptr = 0;
+
+ if(getifaddrs(& iflist) != 0) {
+ isc_throw(Unexpected, "Cannot detect interfaces");
+ }
+
+ for (ifptr = iflist; ifptr != 0; ifptr = ifptr->ifa_next) {
+ for (IfaceMgr::IfaceCollection::const_iterator i = detectedIfaces.begin();
+ i != detectedIfaces.end(); ++ i) {
+ if (!strncmp(ifptr->ifa_name, (*i).getName().c_str(),
+ (*i).getName().size())) {
+
+ // Typically unit-tests try to be silent. But interface detection
+ // is tricky enough to warrant additional prints.
+ std::cout << "Checking interface " << i->getName() << std::endl;
+
+ // Check if interface index is reported properly
+ EXPECT_TRUE(checkIfIndex(*i, ifptr));
+
+ // Check if flags are reported properly
+ EXPECT_TRUE(checkIfFlags(*i, ifptr));
+
+ // Check if addresses are reported properly
+ EXPECT_TRUE(checkIfAddrs(*i, ifptr));
+
+ }
+ }
+ }
+
+ freeifaddrs(iflist);
+ iflist = 0;
+
+ delete ifacemgr;
+}
volatile bool callback_ok;
diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc
index 92f7baf..ec9dd9f 100644
--- a/src/lib/dhcp/tests/libdhcp++_unittest.cc
+++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc
@@ -643,7 +643,10 @@ TEST_F(LibDhcpTest, isStandardOption4) {
187, 188, 189, 190, 191, 192, 193, 194, 195,
196, 197, 198, 199, 200, 201, 202, 203, 204,
205, 206, 207, 214, 215, 216, 217, 218, 219,
- 222, 223 };
+ 222, 223, 224, 225, 226, 227, 228, 229, 230,
+ 231, 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247, 248,
+ 249, 250, 251, 252, 253, 254 };
const size_t unassigned_num = sizeof(unassigned_codes) / sizeof(unassigned_codes[0]);
// Try all possible option codes.
diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc
index b4fb11d..7789c74 100644
--- a/src/lib/dhcpsrv/cfgmgr.cc
+++ b/src/lib/dhcpsrv/cfgmgr.cc
@@ -350,7 +350,7 @@ CfgMgr::getUnicast(const std::string& iface) const {
CfgMgr::CfgMgr()
: datadir_(DHCP_DATA_DIR),
- all_ifaces_active_(false) {
+ all_ifaces_active_(false), echo_v4_client_id_(true) {
// DHCP_DATA_DIR must be set set with -DDHCP_DATA_DIR="..." in Makefile.am
// Note: the definition of DHCP_DATA_DIR needs to include quotation marks
// See AM_CPPFLAGS definition in Makefile.am
diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h
index 7b5ead3..cda59f2 100644
--- a/src/lib/dhcpsrv/cfgmgr.h
+++ b/src/lib/dhcpsrv/cfgmgr.h
@@ -316,6 +316,22 @@ public:
const isc::asiolink::IOAddress*
getUnicast(const std::string& iface) const;
+ /// @brief Sets whether server should send back client-id in DHCPv4
+ ///
+ /// This is a compatibility flag. The default (true) is compliant with
+ /// RFC6842. False is for backward compatibility.
+ ///
+ /// @param echo should the client-id be sent or not
+ void echoClientId(const bool echo) {
+ echo_v4_client_id_ = echo;
+ }
+
+ /// @brief Returns whether server should send back client-id in DHCPv4.
+ /// @return true if client-id should be returned, false otherwise.
+ bool echoClientId() const {
+ return (echo_v4_client_id_);
+ }
+
protected:
/// @brief Protected constructor.
@@ -392,6 +408,9 @@ private:
/// A flag which indicates that server should listen on all available
/// interfaces.
bool all_ifaces_active_;
+
+ /// Indicates whether v4 server should send back client-id
+ bool echo_v4_client_id_;
};
} // namespace isc::dhcp
diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
index 94f78c3..08ce768 100644
--- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
@@ -653,6 +653,23 @@ TEST_F(CfgMgrTest, activateAllIfaces) {
EXPECT_FALSE(cfg_mgr.isActiveIface("eth2"));
}
+// This test verifies that RFC6842 (echo client-id) compatibility may be
+// configured.
+TEST_F(CfgMgrTest, echoClientId) {
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+
+ // Check that the default is true
+ EXPECT_TRUE(cfg_mgr.echoClientId());
+
+ // Check that it can be modified to false
+ cfg_mgr.echoClientId(false);
+ EXPECT_FALSE(cfg_mgr.echoClientId());
+
+ // Check that the default value can be restored
+ cfg_mgr.echoClientId(true);
+ EXPECT_TRUE(cfg_mgr.echoClientId());
+}
+
/// @todo Add unit-tests for testing:
/// - addActiveIface() with invalid interface name
/// - addActiveIface() with the same interface twice
diff --git a/tests/tools/perfdhcp/Makefile.am b/tests/tools/perfdhcp/Makefile.am
index 31e5a31..4e2d22b 100644
--- a/tests/tools/perfdhcp/Makefile.am
+++ b/tests/tools/perfdhcp/Makefile.am
@@ -25,6 +25,7 @@ perfdhcp_SOURCES += perf_pkt6.cc perf_pkt6.h
perfdhcp_SOURCES += perf_pkt4.cc perf_pkt4.h
perfdhcp_SOURCES += packet_storage.h
perfdhcp_SOURCES += pkt_transform.cc pkt_transform.h
+perfdhcp_SOURCES += rate_control.cc rate_control.h
perfdhcp_SOURCES += stats_mgr.h
perfdhcp_SOURCES += test_control.cc test_control.h
libb10_perfdhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
@@ -42,4 +43,4 @@ perfdhcp_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
# ... and the documentation
-EXTRA_DIST = perfdhcp_internals.dox
\ No newline at end of file
+EXTRA_DIST = perfdhcp_internals.dox
diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc
index 7edb84b..26255e8 100644
--- a/tests/tools/perfdhcp/command_options.cc
+++ b/tests/tools/perfdhcp/command_options.cc
@@ -114,6 +114,7 @@ CommandOptions::reset() {
lease_type_.set(LeaseType::ADDRESS);
rate_ = 0;
renew_rate_ = 0;
+ release_rate_ = 0;
report_delay_ = 0;
clients_num_ = 0;
mac_template_.assign(mac, mac + 6);
@@ -211,7 +212,7 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
// In this section we collect argument values from command line
// they will be tuned and validated elsewhere
while((opt = getopt(argc, argv, "hv46r:t:R:b:n:p:d:D:l:P:a:L:"
- "s:iBc1T:X:O:E:S:I:x:w:e:f:")) != -1) {
+ "s:iBc1T:X:O:E:S:I:x:w:e:f:F:")) != -1) {
stream << " -" << static_cast<char>(opt);
if (optarg) {
stream << " " << optarg;
@@ -307,6 +308,12 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
" must be a positive integer");
break;
+ case 'F':
+ release_rate_ = positiveInteger("value of the release rate:"
+ " -F<release-rate> must be a"
+ " positive integer");
+ break;
+
case 'h':
usage();
return (true);
@@ -690,6 +697,8 @@ CommandOptions::validate() const {
"-6 (IPv6) must be set to use -c");
check((getIpVersion() != 6) && (getRenewRate() !=0),
"-f<renew-rate> may be used with -6 (IPv6) only");
+ check((getIpVersion() != 6) && (getReleaseRate() != 0),
+ "-F<release-rate> may be used with -6 (IPv6) only");
check((getExchangeMode() == DO_SA) && (getNumRequests().size() > 1),
"second -n<num-request> is not compatible with -i");
check((getIpVersion() == 4) && !getLeaseType().is(LeaseType::ADDRESS),
@@ -719,6 +728,8 @@ CommandOptions::validate() const {
"-I<ip-offset> is not compatible with -i");
check((getExchangeMode() == DO_SA) && (getRenewRate() != 0),
"-f<renew-rate> is not compatible with -i");
+ check((getExchangeMode() == DO_SA) && (getReleaseRate() != 0),
+ "-F<release-rate> is not compatible with -i");
check((getExchangeMode() != DO_SA) && (isRapidCommit() != 0),
"-i must be set to use -c");
check((getRate() == 0) && (getReportDelay() != 0),
@@ -730,12 +741,16 @@ CommandOptions::validate() const {
check((getRate() == 0) &&
((getMaxDrop().size() > 0) || getMaxDropPercentage().size() > 0),
"-r<rate> must be set to use -D<max-drop>");
- check((getRate() != 0) && (getRenewRate() > getRate()),
- "Renew rate specified as -f<renew-rate> must not be greater than"
- " the rate specified as -r<rate>");
+ check((getRate() != 0) && (getRenewRate() + getReleaseRate() > getRate()),
+ "The sum of Renew rate (-f<renew-rate>) and Release rate"
+ " (-F<release-rate>) must not be greater than the exchange"
+ " rate specified as -r<rate>");
check((getRate() == 0) && (getRenewRate() != 0),
"Renew rate specified as -f<renew-rate> must not be specified"
" when -r<rate> parameter is not specified");
+ check((getRate() == 0) && (getReleaseRate() != 0),
+ "Release rate specified as -F<release-rate> must not be specified"
+ " when -r<rate> parameter is not specified");
check((getTemplateFiles().size() < getTransactionIdOffset().size()),
"-T<template-file> must be set to use -X<xid-offset>");
check((getTemplateFiles().size() < getRandomOffset().size()),
@@ -816,6 +831,9 @@ CommandOptions::printCommandLine() const {
if (getRenewRate() != 0) {
std::cout << "renew-rate[1/s]=" << getRenewRate() << std::endl;
}
+ if (getReleaseRate() != 0) {
+ std::cout << "release-rate[1/s]=" << getReleaseRate() << std::endl;
+ }
if (report_delay_ != 0) {
std::cout << "report[s]=" << report_delay_ << std::endl;
}
@@ -899,13 +917,14 @@ void
CommandOptions::usage() const {
std::cout <<
"perfdhcp [-hv] [-4|-6] [-e<lease-type>] [-r<rate>] [-f<renew-rate>]\n"
- " [-t<report>] [-R<range>] [-b<base>] [-n<num-request>]\n"
- " [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
- " [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
- " [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
- " [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
- " [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
- " [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
+ " [-F<release-rate>] [-t<report>] [-R<range>] [-b<base>]\n"
+ " [-n<num-request>] [-p<test-period>] [-d<drop-time>]\n"
+ " [-D<max-drop>] [-l<local-addr|interface>] [-P<preload>]\n"
+ " [-a<aggressivity>] [-L<local-port>] [-s<seed>] [-i] [-B]\n"
+ " [-c] [-1] [-T<template-file>] [-X<xid-offset>]\n"
+ " [-O<random-offset] [-E<time-offset>] [-S<srvid-offset>]\n"
+ " [-I<ip-offset>] [-x<diagnostic-selector>] [-w<wrapped>]\n"
+ " [server]\n"
"\n"
"The [server] argument is the name/address of the DHCP server to\n"
"contact. For DHCPv4 operation, exchanges are initiated by\n"
@@ -948,10 +967,6 @@ CommandOptions::usage() const {
"-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
" elapsed-time option in the (second/request) template.\n"
" The value 0 disables it.\n"
- "-f<renew-rate>: A rate at which IPv6 Renew requests are sent to\n"
- " a server. This value must not be equal or lower than the rate\n"
- " specified as -r<rate>. If -r<rate> is not specified, this\n"
- " parameter must not be specified too.\n"
"-h: Print this help.\n"
"-i: Do only the initial part of an exchange: DO or SA, depending on\n"
" whether -6 is given.\n"
@@ -999,6 +1014,16 @@ CommandOptions::usage() const {
"\n"
"DHCPv6 only options:\n"
"-c: Add a rapid commit option (exchanges will be SA).\n"
+ "-f<renew-rate>: Rate at which IPv6 Renew requests are sent to\n"
+ " a server. This value is only valid when used in conjunction with\n"
+ " the exchange rate (given by -r<rate>). Furthermore the sum of\n"
+ " this value and the release-rate (given by -F<rate) must be equal\n"
+ " to or less than the exchange rate.\n"
+ "-F<release-rate>: Rate at which IPv6 Release requests are sent to\n"
+ " a server. This value is only valid when used in conjunction with\n"
+ " the exchange rate (given by -r<rate>). Furthermore the sum of\n"
+ " this value and the renew-rate (given by -f<rate) must be equal\n"
+ " to or less than the exchange rate.\n"
"\n"
"The remaining options are used only in conjunction with -r:\n"
"\n"
diff --git a/tests/tools/perfdhcp/command_options.h b/tests/tools/perfdhcp/command_options.h
index 42a31c9..7395eae 100644
--- a/tests/tools/perfdhcp/command_options.h
+++ b/tests/tools/perfdhcp/command_options.h
@@ -1,4 +1,3 @@
-
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
@@ -156,11 +155,16 @@ public:
/// \return exchange rate per second.
int getRate() const { return rate_; }
- /// \brief Returns a rate at which IPv6 Renew messages are sent.
+ /// \brief Returns a rate at which DHCPv6 Renew messages are sent.
///
/// \return A rate at which IPv6 Renew messages are sent.
int getRenewRate() const { return (renew_rate_); }
+ /// \brief Returns a rate at which DHCPv6 Release messages are sent.
+ ///
+ /// \return A rate at which DHCPv6 Release messages are sent.
+ int getReleaseRate() const { return (release_rate_); }
+
/// \brief Returns delay between two performance reports.
///
/// \return delay between two consecutive performance reports.
@@ -469,6 +473,8 @@ private:
int rate_;
/// A rate at which DHCPv6 Renew messages are sent.
int renew_rate_;
+ /// A rate at which DHCPv6 Release messages are sent.
+ int release_rate_;
/// Delay between generation of two consecutive
/// performance reports
int report_delay_;
diff --git a/tests/tools/perfdhcp/rate_control.cc b/tests/tools/perfdhcp/rate_control.cc
new file mode 100644
index 0000000..1c2a600
--- /dev/null
+++ b/tests/tools/perfdhcp/rate_control.cc
@@ -0,0 +1,158 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+#include "rate_control.h"
+
+namespace isc {
+namespace perfdhcp {
+
+using namespace boost::posix_time;
+
+RateControl::RateControl()
+ : send_due_(currentTime()), last_sent_(currentTime()),
+ aggressivity_(1), rate_(0), late_sent_(false) {
+}
+
+RateControl::RateControl(const int rate, const int aggressivity)
+ : send_due_(currentTime()), last_sent_(currentTime()),
+ aggressivity_(aggressivity), rate_(rate), late_sent_(false) {
+ if (aggressivity_ < 1) {
+ isc_throw(isc::BadValue, "invalid value of aggressivity "
+ << aggressivity << ", expected value is greater than 0");
+ }
+ if (rate_ < 0) {
+ isc_throw(isc::BadValue, "invalid value of rate " << rate
+ << ", expected non-negative value");
+ }
+}
+
+uint64_t
+RateControl::getOutboundMessageCount() {
+
+ // We need calculate the due time for sending next set of messages.
+ updateSendDue();
+
+ // Get current time. If we are behind due time, we have to calculate
+ // how many messages to send to catch up with the rate.
+ ptime now = currentTime();
+ if (now >= send_due_) {
+ // Reset number of exchanges.
+ uint64_t due_exchanges = 0;
+ // If rate is specified from the command line we have to
+ // synchornize with it.
+ if (getRate() != 0) {
+ time_period period(send_due_, now);
+ time_duration duration = period.length();
+ // due_factor indicates the number of seconds that
+ // sending next chunk of packets will take.
+ double due_factor = duration.fractional_seconds() /
+ time_duration::ticks_per_second();
+ due_factor += duration.total_seconds();
+ // Multiplying due_factor by expected rate gives the number
+ // of exchanges to be initiated.
+ due_exchanges = static_cast<uint64_t>(due_factor * getRate());
+ // We want to make sure that at least one packet goes out.
+ if (due_exchanges == 0) {
+ due_exchanges = 1;
+ }
+ // We should not exceed aggressivity as it could have been
+ // restricted from command line.
+ if (due_exchanges > getAggressivity()) {
+ due_exchanges = getAggressivity();
+ }
+ } else {
+ // Rate is not specified so we rely on aggressivity
+ // which is the number of packets to be sent in
+ // one chunk.
+ due_exchanges = getAggressivity();
+ }
+ return (due_exchanges);
+ }
+ return (0);
+}
+
+boost::posix_time::ptime
+RateControl::currentTime() {
+ return (microsec_clock::universal_time());
+}
+
+void
+RateControl::updateSendDue() {
+ // There is no sense to update due time if the current due time is in the
+ // future. The due time is calculated as a duration between the moment
+ // when the last message of the given type was sent and the time when
+ // next one is supposed to be sent based on a given rate. The former value
+ // will not change until we send the next message, which we don't do
+ // until we reach the due time.
+ if (send_due_ > currentTime()) {
+ return;
+ }
+ // This is initialized in the class constructor, so if it is not initialized
+ // it is a programmatic error.
+ if (last_sent_.is_not_a_date_time()) {
+ isc_throw(isc::Unexpected, "timestamp of the last sent packet not"
+ " initialized");
+ }
+ // If rate was not specified we will wait just one clock tick to
+ // send next packet. This simulates best effort conditions.
+ long duration = 1;
+ if (getRate() != 0) {
+ // We use number of ticks instead of nanoseconds because
+ // nanosecond resolution may not be available on some
+ // machines. Number of ticks guarantees the highest possible
+ // timer resolution.
+ duration = time_duration::ticks_per_second() / getRate();
+ }
+ // Calculate due time to initiate next chunk of exchanges.
+ send_due_ = last_sent_ + time_duration(0, 0, 0, duration);
+ if (send_due_ > currentTime()) {
+ late_sent_ = true;
+ } else {
+ late_sent_ = false;
+ }
+}
+
+void
+RateControl::setAggressivity(const int aggressivity) {
+ if (aggressivity < 1) {
+ isc_throw(isc::BadValue, "invalid value of aggressivity "
+ << aggressivity << ", expected value is greater than 0");
+ }
+ aggressivity_ = aggressivity;
+}
+
+void
+RateControl::setRate(const int rate) {
+ if (rate < 0) {
+ isc_throw(isc::BadValue, "invalid value of rate " << rate
+ << ", expected non-negative value");
+ }
+ rate_ = rate;
+}
+
+void
+RateControl::setRelativeDue(const int offset) {
+ send_due_ = offset > 0 ?
+ currentTime() + seconds(abs(offset)) :
+ currentTime() - seconds(abs(offset));
+}
+
+void
+RateControl::updateSendTime() {
+ last_sent_ = currentTime();
+}
+
+} // namespace perfdhcp
+} // namespace isc
diff --git a/tests/tools/perfdhcp/rate_control.h b/tests/tools/perfdhcp/rate_control.h
new file mode 100644
index 0000000..cd49c2c
--- /dev/null
+++ b/tests/tools/perfdhcp/rate_control.h
@@ -0,0 +1,180 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef RATE_CONTROL_H
+#define RATE_CONTROL_H
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+namespace isc {
+namespace perfdhcp {
+
+/// \brief A message sending rate control class for perfdhcp.
+///
+/// This class provides the means to control the rate at which messages
+/// of the specific type are sent by perfdhcp. Each message type,
+/// for which the desired rate can be specified, has a corresponding
+/// \c RateControl object. So, the perfdhcp is using up to three objects
+/// of this type at the same time, to control the rate of the following
+/// messages being sent:
+/// - Discover(DHCPv4) or Solicit (DHCPv6)
+/// - Renew (DHCPv6) or Request (DHCPv4) to renew leases.
+/// - Release
+///
+/// The purpose of the RateControl class is to track the due time for
+/// sending next message (or bunch of messages) to keep outbound rate
+/// of particular messages at the desired level. The due time is calculated
+/// using the desired rate value and the timestamp when the last message of
+/// the particular type has been sent. That puts the responsibility on the
+/// \c TestControl class to invoke the \c RateControl::updateSendDue, every
+/// time the message is sent.
+///
+/// The \c RateControl object returns the number of messages to be sent at
+/// the time. The number returned is 0, if perfdhcp shouldn't send any messages
+/// yet, or 1 (sometimes more) if the send due time has been reached.
+class RateControl {
+public:
+
+ /// \brief Default constructor.
+ RateControl();
+
+ /// \brief Constructor which sets desired rate and aggressivity.
+ ///
+ /// \param rate A desired rate.
+ /// \param aggressivity A desired aggressivity.
+ RateControl(const int rate, const int aggressivity);
+
+ /// \brief Returns the value of aggressivity.
+ int getAggressivity() const {
+ return (aggressivity_);
+ }
+
+ /// \brief Returns current due time to send next message.
+ boost::posix_time::ptime getDue() const {
+ return (send_due_);
+ }
+
+ /// \brief Returns number of messages to be sent "now".
+ ///
+ /// This function calculates how many messages of the given type should
+ /// be sent immediately when the call to the function returns, to catch
+ /// up with the desired message rate.
+ ///
+ /// The value returned depends on the due time calculated with the
+ /// \c RateControl::updateSendDue function and the current time. If
+ /// the due time has been hit, the non-zero number of messages is returned.
+ /// If the due time hasn't been hit, the number returned is 0.
+ ///
+ /// If the rate is non-zero, the number of messages to be sent is calculated
+ /// as follows:
+ /// \code
+ /// num = duration * rate
+ /// \endcode
+ /// where <b>duration</b> is a time period between the due time to send
+ /// next set of messages and current time. The duration is expressed in
+ /// seconds with the fractional part having 6 or 9 digits (depending on
+ /// the timer resolution). If the calculated value is equal to 0, it is
+ /// rounded to 1, so as at least one message is sent.
+ ///
+ /// The value of aggressivity limits the maximal number of messages to
+ /// be sent one after another. If the number of messages calculated with
+ /// the equation above exceeds the aggressivity, this function will return
+ /// the value equal to aggressivity.
+ ///
+ /// If the rate is not specified (equal to 0), the value calculated by
+ /// this function is equal to aggressivity.
+ ///
+ /// \return A number of messages to be sent immediately.
+ uint64_t getOutboundMessageCount();
+
+ /// \brief Returns the rate.
+ int getRate() const {
+ return (rate_);
+ }
+
+ /// \brief Returns the value of the late send flag.
+ ///
+ /// The flag returned by this function indicates whether the new due time
+ /// calculated by the \c RateControl::updateSendDue is in the past.
+ /// This value is used by the \c TestControl object to increment the counter
+ /// of the late sent messages in the \c StatsMgr.
+ bool isLateSent() const {
+ return (late_sent_);
+ }
+
+ /// \brief Sets the value of aggressivity.
+ ///
+ /// \param aggressivity A new value of aggressivity. This value must be
+ /// a positive integer.
+ /// \throw isc::BadValue if new value is not a positive integer.
+ void setAggressivity(const int aggressivity);
+
+ /// \brief Sets the new rate.
+ ///
+ /// \param rate A new value of rate. This value must not be negative.
+ /// \throw isc::BadValue if new rate is negative.
+ void setRate(const int rate);
+
+ /// \brief Sets the value of the due time.
+ ///
+ /// This function is intended for unit testing. It manipulates the value of
+ /// the due time. The parameter passed to this function specifies the
+ /// (positive or negative) number of seconds relative to current time.
+ ///
+ /// \param offset A number of seconds relative to current time which
+ /// constitutes the new due time.
+ void setRelativeDue(const int offset);
+
+ /// \brief Sets the timestamp of the last sent message to current time.
+ void updateSendTime();
+
+protected:
+
+ /// \brief Convenience function returning current time.
+ ///
+ /// \return current time.
+ static boost::posix_time::ptime currentTime();
+
+ /// \brief Calculates the send due.
+ ///
+ /// This function calculates the send due timestamp using the current time
+ /// and desired rate. The due timestamp is calculated as a sum of the
+ /// timestamp when the last message was sent and the reciprocal of the rate
+ /// in micro or nanoseconds (depending on the timer resolution). If the rate
+ /// is not specified, the duration between two consecutive sends is one
+ /// timer tick.
+ void updateSendDue();
+
+ /// \brief Holds a timestamp when the next message should be sent.
+ boost::posix_time::ptime send_due_;
+
+ /// \brief Holds a timestamp when the last message was sent.
+ boost::posix_time::ptime last_sent_;
+
+ /// \brief Holds an aggressivity value.
+ int aggressivity_;
+
+ /// \brief Holds a desired rate value.
+ int rate_;
+
+ /// \brief A flag which indicates that the calculated due time is in the
+ /// past.
+ bool late_sent_;
+
+};
+
+}
+}
+
+#endif
diff --git a/tests/tools/perfdhcp/stats_mgr.h b/tests/tools/perfdhcp/stats_mgr.h
index d0af943..7486b9e 100644
--- a/tests/tools/perfdhcp/stats_mgr.h
+++ b/tests/tools/perfdhcp/stats_mgr.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -124,7 +124,8 @@ public:
XCHG_RA, ///< DHCPv4 REQUEST-ACK
XCHG_SA, ///< DHCPv6 SOLICIT-ADVERTISE
XCHG_RR, ///< DHCPv6 REQUEST-REPLY
- XCHG_RN ///< DHCPv6 RENEW-REPLY
+ XCHG_RN, ///< DHCPv6 RENEW-REPLY
+ XCHG_RL ///< DHCPv6 RELEASE-REPLY
};
/// \brief Exchange Statistics.
@@ -632,12 +633,19 @@ public:
/// Method prints main statistics for particular exchange.
/// Statistics includes: number of sent and received packets,
/// number of dropped packets and number of orphans.
+ ///
+ /// \todo Currently the number of orphans is not displayed because
+ /// Reply messages received for Renew and Releases are counted as
+ /// orphans for the 4-way exchanges, which is wrong. We will need to
+ /// move the orphans counting out of the Statistics Manager so as
+ /// orphans counter is increased only if the particular message is
+ /// not identified as a reponse to any of the messages sent by perfdhcp.
void printMainStats() const {
using namespace std;
cout << "sent packets: " << getSentPacketsNum() << endl
<< "received packets: " << getRcvdPacketsNum() << endl
- << "drops: " << getDroppedPacketsNum() << endl
- << "orphans: " << getOrphans() << endl;
+ << "drops: " << getDroppedPacketsNum() << endl;
+ // << "orphans: " << getOrphans() << endl;
}
/// \brief Print round trip time packets statistics.
@@ -871,6 +879,20 @@ public:
boot_time_));
}
+ /// \brief Check if the exchange type has been specified.
+ ///
+ /// This method checks if the \ref ExchangeStats object of a particular type
+ /// exists (has been added using \ref addExchangeStats function).
+ ///
+ /// \param xchg_type A type of the exchange being repersented by the
+ /// \ref ExchangeStats object.
+ ///
+ /// \return true if the \ref ExchangeStats object has been added for a
+ /// specified exchange type.
+ bool hasExchangeStats(const ExchangeType xchg_type) const {
+ return (exchanges_.find(xchg_type) != exchanges_.end());
+ }
+
/// \brief Add named custom uint64 counter.
///
/// Method creates new named counter and stores in counter's map under
@@ -1159,7 +1181,7 @@ public:
///
/// \param xchg_type exchange type.
/// \return string representing name of the exchange.
- std::string exchangeToString(ExchangeType xchg_type) const {
+ static std::string exchangeToString(ExchangeType xchg_type) {
switch(xchg_type) {
case XCHG_DO:
return("DISCOVER-OFFER");
@@ -1171,6 +1193,8 @@ public:
return("REQUEST-REPLY");
case XCHG_RN:
return("RENEW-REPLY");
+ case XCHG_RL:
+ return("RELEASE-REPLY");
default:
return("Unknown exchange type");
}
diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc
index 7ffe9fa..3751e2f 100644
--- a/tests/tools/perfdhcp/test_control.cc
+++ b/tests/tools/perfdhcp/test_control.cc
@@ -98,6 +98,21 @@ TestControl::TestControl() {
}
void
+TestControl::checkLateMessages(RateControl& rate_control) {
+ // If diagnostics is disabled, there is no need to log late sent messages.
+ // If it is enabled and the rate control object indicates that the last
+ // sent message was late, bump up the counter in Stats Manager.
+ if (rate_control.isLateSent() && testDiags('i')) {
+ CommandOptions& options = CommandOptions::instance();
+ if (options.getIpVersion() == 4) {
+ stats_mgr4_->incrementCounter("latesend");
+ } else if (options.getIpVersion() == 6) {
+ stats_mgr6_->incrementCounter("latesend");
+ }
+ }
+}
+
+void
TestControl::cleanCachedPackets() {
CommandOptions& options = CommandOptions::instance();
// When Renews are not sent, Reply packets are not cached so there
@@ -316,28 +331,43 @@ TestControl::checkExitConditions() const {
}
Pkt6Ptr
-TestControl::createRenew(const Pkt6Ptr& reply) {
+TestControl::createMessageFromReply(const uint16_t msg_type,
+ const dhcp::Pkt6Ptr& reply) {
+ // Restrict messages to Release and Renew.
+ if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
+ isc_throw(isc::BadValue, "invalid message type " << msg_type
+ << " to be created from Reply, expected DHCPV6_RENEW or"
+ " DHCPV6_RELEASE");
+ }
+ // Get the string representation of the message - to be used for error
+ // logging purposes.
+ const char* msg_type_str = (msg_type == DHCPV6_RENEW ? "Renew" : "Release");
+ // Reply message must be specified.
if (!reply) {
- isc_throw(isc::BadValue,"Unable to create Renew packet from the Reply packet"
- " because the instance of the Reply is NULL");
+ isc_throw(isc::BadValue, "Unable to create " << msg_type_str
+ << " message from the Reply message because the instance of"
+ " the Reply message is NULL");
}
- Pkt6Ptr renew(new Pkt6(DHCPV6_RENEW, generateTransid()));
+
+ Pkt6Ptr msg(new Pkt6(msg_type, generateTransid()));
// Client id.
OptionPtr opt_clientid = reply->getOption(D6O_CLIENTID);
if (!opt_clientid) {
- isc_throw(isc::Unexpected, "failed to create Renew packet because client id"
- " option has not been found in the Reply from the server");
+ isc_throw(isc::Unexpected, "failed to create " << msg_type_str
+ << " message because client id option has not been found"
+ " in the Reply message");
}
- renew->addOption(opt_clientid);
+ msg->addOption(opt_clientid);
// Server id.
OptionPtr opt_serverid = reply->getOption(D6O_SERVERID);
if (!opt_serverid) {
- isc_throw(isc::Unexpected, "failed to create Renew packet because server id"
- " option has not been found in the Reply from the server");
+ isc_throw(isc::Unexpected, "failed to create " << msg_type_str
+ << " because server id option has not been found in the"
+ " Reply message");
}
- renew->addOption(opt_serverid);
- copyIaOptions(reply, renew);
- return (renew);
+ msg->addOption(opt_serverid);
+ copyIaOptions(reply, msg);
+ return (msg);
}
OptionPtr
@@ -489,16 +519,28 @@ TestControl::getCurrentTimeout() const {
ptime now(microsec_clock::universal_time());
// Check that we haven't passed the moment to send the next set of
// packets.
- if (now >= send_due_ ||
- (options.getRenewRate() != 0 && now >= renew_due_)) {
+ if (now >= basic_rate_control_.getDue() ||
+ (options.getRenewRate() != 0 && now >= renew_rate_control_.getDue()) ||
+ (options.getReleaseRate() != 0 &&
+ now >= release_rate_control_.getDue())) {
return (0);
}
- // There is a due time to send Solicit and Renew. We should adjust
- // the timeout to the due time which occurs sooner.
- ptime due = send_due_ > renew_due_ ? renew_due_ : send_due_;
- time_period due_period(now, due);
- return (due_period.length().total_microseconds());
+ // Let's assume that the due time for Solicit is the soonest.
+ ptime due = basic_rate_control_.getDue();
+ // If we are sending Renews and due time for Renew occurs sooner,
+ // set the due time to Renew due time.
+ if ((options.getRenewRate()) != 0 && (renew_rate_control_.getDue() < due)) {
+ due = renew_rate_control_.getDue();
+ }
+ // If we are sending Releases and the due time for Release occurs
+ // sooner than the current due time, let's use the due for Releases.
+ if ((options.getReleaseRate() != 0) &&
+ (release_rate_control_.getDue() < due)) {
+ due = release_rate_control_.getDue();
+ }
+ // Return the timeout in microseconds.
+ return (time_period(now, due).length().total_microseconds());
}
int
@@ -529,49 +571,6 @@ TestControl::getElapsedTime(const T& pkt1, const T& pkt2) {
return(elapsed_period.length().total_milliseconds());
}
-
-uint64_t
-TestControl::getNextExchangesNum(const boost::posix_time::ptime& send_due,
- const int rate) {
- CommandOptions& options = CommandOptions::instance();
- // Get current time.
- ptime now(microsec_clock::universal_time());
- if (now >= send_due) {
- // Reset number of exchanges.
- uint64_t due_exchanges = 0;
- // If rate is specified from the command line we have to
- // synchornize with it.
- if (rate != 0) {
- time_period period(send_due, now);
- time_duration duration = period.length();
- // due_factor indicates the number of seconds that
- // sending next chunk of packets will take.
- double due_factor = duration.fractional_seconds() /
- time_duration::ticks_per_second();
- due_factor += duration.total_seconds();
- // Multiplying due_factor by expected rate gives the number
- // of exchanges to be initiated.
- due_exchanges = static_cast<uint64_t>(due_factor * rate);
- // We want to make sure that at least one packet goes out.
- if (due_exchanges == 0) {
- due_exchanges = 1;
- }
- // We should not exceed aggressivity as it could have been
- // restricted from command line.
- if (due_exchanges > options.getAggressivity()) {
- due_exchanges = options.getAggressivity();
- }
- } else {
- // Rate is not specified so we rely on aggressivity
- // which is the number of packets to be sent in
- // one chunk.
- due_exchanges = options.getAggressivity();
- }
- return (due_exchanges);
- }
- return (0);
-}
-
int
TestControl::getRandomOffset(const int arg_idx) const {
int rand_offset = CommandOptions::instance().getIpVersion() == 4 ?
@@ -695,6 +694,9 @@ TestControl::initializeStatsMgr() {
if (options.getRenewRate() != 0) {
stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RN);
}
+ if (options.getReleaseRate() != 0) {
+ stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RL);
+ }
}
if (testDiags('i')) {
if (options.getIpVersion() == 4) {
@@ -849,14 +851,15 @@ TestControl::sendPackets(const TestControlSocket& socket,
}
uint64_t
-TestControl::sendRenewPackets(const TestControlSocket& socket,
- const uint64_t packets_num) {
- for (uint64_t i = 0; i < packets_num; ++i) {
- if (!sendRenew(socket)) {
+TestControl::sendMultipleMessages6(const TestControlSocket& socket,
+ const uint32_t msg_type,
+ const uint64_t msg_num) {
+ for (uint64_t i = 0; i < msg_num; ++i) {
+ if (!sendMessageFromReply(msg_type, socket)) {
return (i);
}
}
- return (packets_num);
+ return (msg_num);
}
void
@@ -1114,14 +1117,36 @@ TestControl::processReceivedPacket6(const TestControlSocket& socket,
}
}
} else if (packet_type == DHCPV6_REPLY) {
- Pkt6Ptr sent_packet = stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR,
- pkt6);
- if (sent_packet) {
- if (CommandOptions::instance().getRenewRate() != 0) {
+ // If the received message is Reply, we have to find out which exchange
+ // type the Reply message belongs to. It is doable by matching the Reply
+ // transaction id with the transaction id of the sent Request, Renew
+ // or Release. First we start with the Request.
+ if (stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6)) {
+ // The Reply belongs to Request-Reply exchange type. So, we may need
+ // to keep this Reply in the storage if Renews or/and Releases are
+ // being sent. Note that, Reply messages hold the information about
+ // leases assigned. We use this information to construct Renew and
+ // Release messages.
+ if (stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RN) ||
+ stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RL)) {
+ // Renew or Release messages are sent, because StatsMgr has the
+ // specific exchange type specified. Let's append the Reply
+ // message to a storage.
reply_storage_.append(pkt6);
}
- } else {
- stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RN, pkt6);
+ // The Reply message is not a server's response to the Request message
+ // sent within the 4-way exchange. It may be a response to the Renew
+ // or Release message. In the if clause we first check if StatsMgr
+ // has exchange type for Renew specified, and if it has, if there is
+ // a corresponding Renew message for the received Reply. If not,
+ // we check that StatsMgr has exchange type for Release specified,
+ // as possibly the Reply has been sent in response to Release.
+ } else if (!(stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RN) &&
+ stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RN, pkt6)) &&
+ stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RL)) {
+ // At this point, it is only possible that the Reply has been sent
+ // in response to a Release. Try to match the Reply with Release.
+ stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RL, pkt6);
}
}
}
@@ -1250,12 +1275,16 @@ TestControl::registerOptionFactories() const {
void
TestControl::reset() {
- send_due_ = microsec_clock::universal_time();
- last_sent_ = send_due_;
- last_report_ = send_due_;
- renew_due_ = send_due_;
- last_renew_ = send_due_;
+ CommandOptions& options = CommandOptions::instance();
+ basic_rate_control_.setAggressivity(options.getAggressivity());
+ basic_rate_control_.setRate(options.getRate());
+ renew_rate_control_.setAggressivity(options.getAggressivity());
+ renew_rate_control_.setRate(options.getRenewRate());
+ release_rate_control_.setAggressivity(options.getAggressivity());
+ release_rate_control_.setRate(options.getReleaseRate());
+
transid_gen_.reset();
+ last_report_ = microsec_clock::universal_time();
// Actual generators will have to be set later on because we need to
// get command line parameters first.
setTransidGenerator(NumberGeneratorPtr());
@@ -1321,11 +1350,10 @@ TestControl::run() {
// Initialize Statistics Manager. Release previous if any.
initializeStatsMgr();
for (;;) {
- // Calculate send due based on when last exchange was initiated.
- updateSendDue(last_sent_, options.getRate(), send_due_);
// Calculate number of packets to be sent to stay
// catch up with rate.
- uint64_t packets_due = getNextExchangesNum(send_due_, options.getRate());
+ uint64_t packets_due = basic_rate_control_.getOutboundMessageCount();
+ checkLateMessages(basic_rate_control_);
if ((packets_due == 0) && testDiags('i')) {
if (options.getIpVersion() == 4) {
stats_mgr4_->incrementCounter("shortwait");
@@ -1351,11 +1379,21 @@ TestControl::run() {
// If -f<renew-rate> option was specified we have to check how many
// Renew packets should be sent to catch up with a desired rate.
if ((options.getIpVersion() == 6) && (options.getRenewRate() != 0)) {
- updateSendDue(last_renew_, options.getRenewRate(), renew_due_);
uint64_t renew_packets_due =
- getNextExchangesNum(renew_due_, options.getRenewRate());
- // Send renew packets.
- sendRenewPackets(socket, renew_packets_due);
+ renew_rate_control_.getOutboundMessageCount();
+ checkLateMessages(renew_rate_control_);
+ // Send Renew messages.
+ sendMultipleMessages6(socket, DHCPV6_RENEW, renew_packets_due);
+ }
+
+ // If -F<release-rate> option was specified we have to check how many
+ // Release messages should be sent to catch up with a desired rate.
+ if ((options.getIpVersion() == 6) && (options.getReleaseRate() != 0)) {
+ uint64_t release_packets_due =
+ release_rate_control_.getOutboundMessageCount();
+ checkLateMessages(release_rate_control_);
+ // Send Release messages.
+ sendMultipleMessages6(socket, DHCPV6_RELEASE, release_packets_due);
}
// Report delay means that user requested printing number
@@ -1451,7 +1489,7 @@ TestControl::saveFirstPacket(const Pkt6Ptr& pkt) {
void
TestControl::sendDiscover4(const TestControlSocket& socket,
const bool preload /*= false*/) {
- last_sent_ = microsec_clock::universal_time();
+ basic_rate_control_.updateSendTime();
// Generate the MAC address to be passed in the packet.
uint8_t randomized = 0;
std::vector<uint8_t> mac_address = generateMacAddress(randomized);
@@ -1496,9 +1534,7 @@ void
TestControl::sendDiscover4(const TestControlSocket& socket,
const std::vector<uint8_t>& template_buf,
const bool preload /* = false */) {
- // last_sent_ has to be updated for each function that initiates
- // new transaction. The packet exchange synchronization relies on this.
- last_sent_ = microsec_clock::universal_time();
+ basic_rate_control_.updateSendTime();
// Get the first argument if mulitple the same arguments specified
// in the command line. First one refers to DISCOVER packets.
const uint8_t arg_idx = 0;
@@ -1547,21 +1583,35 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
}
bool
-TestControl::sendRenew(const TestControlSocket& socket) {
- last_renew_ = microsec_clock::universal_time();
+TestControl::sendMessageFromReply(const uint16_t msg_type,
+ const TestControlSocket& socket) {
+ // We only permit Release or Renew messages to be sent using this function.
+ if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
+ isc_throw(isc::BadValue, "invalid message type " << msg_type
+ << " to be sent, expected DHCPV6_RENEW or DHCPV6_RELEASE");
+ }
+ // We track the timestamp of last Release and Renew in different variables.
+ if (msg_type == DHCPV6_RENEW) {
+ renew_rate_control_.updateSendTime();
+ } else {
+ release_rate_control_.updateSendTime();
+ }
Pkt6Ptr reply = reply_storage_.getRandom();
if (!reply) {
return (false);
}
- Pkt6Ptr renew = createRenew(reply);
- setDefaults6(socket, renew);
- renew->pack();
- IfaceMgr::instance().send(renew);
+ // Prepare the message of the specified type.
+ Pkt6Ptr msg = createMessageFromReply(msg_type, reply);
+ setDefaults6(socket, msg);
+ msg->pack();
+ // And send it.
+ IfaceMgr::instance().send(msg);
if (!stats_mgr6_) {
isc_throw(Unexpected, "Statistics Manager for DHCPv6 "
"hasn't been initialized");
}
- stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RN, renew);
+ stats_mgr6_->passSentPacket((msg_type == DHCPV6_RENEW ? StatsMgr6::XCHG_RN
+ : StatsMgr6::XCHG_RL), msg);
return (true);
}
@@ -1903,7 +1953,7 @@ TestControl::sendRequest6(const TestControlSocket& socket,
void
TestControl::sendSolicit6(const TestControlSocket& socket,
const bool preload /*= false*/) {
- last_sent_ = microsec_clock::universal_time();
+ basic_rate_control_.updateSendTime();
// Generate DUID to be passed to the packet
uint8_t randomized = 0;
std::vector<uint8_t> duid = generateDuid(randomized);
@@ -1952,7 +2002,7 @@ void
TestControl::sendSolicit6(const TestControlSocket& socket,
const std::vector<uint8_t>& template_buf,
const bool preload /*= false*/) {
- last_sent_ = microsec_clock::universal_time();
+ basic_rate_control_.updateSendTime();
const int arg_idx = 0;
// Get transaction id offset.
size_t transid_offset = getTransactionIdOffset(arg_idx);
@@ -2051,47 +2101,5 @@ TestControl::testDiags(const char diag) const {
return (false);
}
-void
-TestControl::updateSendDue(const boost::posix_time::ptime& last_sent,
- const int rate,
- boost::posix_time::ptime& send_due) {
- // If default constructor was called, this should not happen but
- // if somebody has changed default constructor it is better to
- // keep this check.
- if (last_sent.is_not_a_date_time()) {
- isc_throw(Unexpected, "time of last sent packet not initialized");
- }
- // Get the expected exchange rate.
- CommandOptions& options = CommandOptions::instance();
- // If rate was not specified we will wait just one clock tick to
- // send next packet. This simulates best effort conditions.
- long duration = 1;
- if (rate != 0) {
- // We use number of ticks instead of nanoseconds because
- // nanosecond resolution may not be available on some
- // machines. Number of ticks guarantees the highest possible
- // timer resolution.
- duration = time_duration::ticks_per_second() / rate;
- }
- // Calculate due time to initiate next chunk of exchanges.
- send_due = last_sent + time_duration(0, 0, 0, duration);
- // Check if it is already due.
- ptime now(microsec_clock::universal_time());
- // \todo verify if this condition is not too tight. In other words
- // verify if this will not produce too many late sends.
- // We might want to look at this once we are done implementing
- // microsecond timeouts in IfaceMgr.
- if (now > send_due) {
- if (testDiags('i')) {
- if (options.getIpVersion() == 4) {
- stats_mgr4_->incrementCounter("latesend");
- } else if (options.getIpVersion() == 6) {
- stats_mgr6_->incrementCounter("latesend");
- }
- }
- }
-}
-
-
} // namespace perfdhcp
} // namespace isc
diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h
index 26adf9f..5a7ef48 100644
--- a/tests/tools/perfdhcp/test_control.h
+++ b/tests/tools/perfdhcp/test_control.h
@@ -16,6 +16,7 @@
#define TEST_CONTROL_H
#include "packet_storage.h"
+#include "rate_control.h"
#include "stats_mgr.h"
#include <dhcp/iface_mgr.h>
@@ -313,13 +314,23 @@ protected:
/// has been reached.
void cleanCachedPackets();
- /// \brief Creates IPv6 packet using options from Reply packet.
+ /// \brief Creates DHCPv6 message from the Reply packet.
///
+ /// This function creates DHCPv6 Renew or Release message using the
+ /// data from the Reply message by copying options from the Reply
+ /// message.
+ ///
+ /// \param msg_type A type of the message to be createad.
/// \param reply An instance of the Reply packet which contents should
- /// be used to create an instance of the Renew packet.
+ /// be used to create an instance of the new message.
///
- /// \return created Renew packet.
- dhcp::Pkt6Ptr createRenew(const dhcp::Pkt6Ptr& reply);
+ /// \return created Release or Renew message
+ /// \throw isc::BadValue if the msg_type is neither DHCPV6_RENEW nor
+ /// DHCPV6_RELEASE or if the reply is NULL.
+ /// \throw isc::Unexpected if mandatory options are missing in the
+ /// Reply message.
+ dhcp::Pkt6Ptr createMessageFromReply(const uint16_t msg_type,
+ const dhcp::Pkt6Ptr& reply);
/// \brief Factory function to create DHCPv6 ELAPSED_TIME option.
///
@@ -481,21 +492,6 @@ protected:
/// \return A current timeout in microseconds.
uint32_t getCurrentTimeout() const;
- /// \brief Returns number of exchanges to be started.
- ///
- /// Method returns number of new exchanges to be started as soon
- /// as possible to satisfy expected rate. Calculation used here
- /// is based on current time, due time calculated with
- /// \ref updateSendDue function and expected rate.
- ///
- /// \param send_due Due time to initiate next chunk set exchanges.
- /// \param rate A rate at which exchanges are initiated.
- ///
- /// \return number of exchanges to be started immediately.
- static uint64_t
- getNextExchangesNum(const boost::posix_time::ptime& send_due,
- const int rate);
-
/// \brief Return template buffer.
///
/// Method returns template buffer at specified index.
@@ -738,25 +734,32 @@ protected:
const uint64_t packets_num,
const bool preload = false);
- /// \brief Send number of DHCPv6 Renew packets to the server.
+ /// \brief Send number of DHCPv6 Renew or Release messages to the server.
///
/// \param socket An object representing socket to be used to send packets.
- /// \param packets_num A number of Renew packets to be send.
+ /// \param msg_type A type of the messages to be sent (DHCPV6_RENEW or
+ /// DHCPV6_RELEASE).
+ /// \param msg_num A number of messages to be sent.
///
- /// \return A number of packets actually sent.
- uint64_t sendRenewPackets(const TestControlSocket& socket,
- const uint64_t packets_num);
+ /// \return A number of messages actually sent.
+ uint64_t sendMultipleMessages6(const TestControlSocket& socket,
+ const uint32_t msg_type,
+ const uint64_t msg_num);
- /// \brief Send a renew message using provided socket.
+ /// \brief Send DHCPv6 Renew or Release message using specified socket.
///
/// This method will select an existing lease from the Reply packet cache
- /// If there is no lease that can be renewed this method will return false.
+ /// If there is no lease that can be renewed or released this method will
+ /// return false.
///
+ /// \param msg_type A type of the message to be sent (DHCPV6_RENEW or
+ /// DHCPV6_RELEASE).
/// \param socket An object encapsulating socket to be used to send
/// a packet.
///
- /// \return true if packet has been sent, false otherwise.
- bool sendRenew(const TestControlSocket& socket);
+ /// \return true if the message has been sent, false otherwise.
+ bool sendMessageFromReply(const uint16_t msg_type,
+ const TestControlSocket& socket);
/// \brief Send DHCPv4 REQUEST message.
///
@@ -899,21 +902,18 @@ protected:
/// \return true if diagnostics flag has been set.
bool testDiags(const char diag) const;
- /// \brief Update due time to initiate next chunk of exchanges.
+protected:
+
+ /// \brief Increments counter of late sent messages if required.
///
- /// Method updates due time to initiate next chunk of exchanges.
- /// Function takes current time, last sent packet's time and
- /// expected rate in its calculations.
+ /// This function checks if the message or set of messages of a given type,
+ /// were sent later than their due time. If they were sent late, it is
+ /// an indication that the perfdhcp doesn't catch up with the desired rate
+ /// for sending messages.
///
- /// \param last_sent A time when the last exchange was initiated.
- /// \param rate A rate at which exchangesa re initiated
- /// \param [out] send_due A reference to the time object to be updated
- /// with the next due time.
- void updateSendDue(const boost::posix_time::ptime& last_sent,
- const int rate,
- boost::posix_time::ptime& send_due);
-
-private:
+ /// \param rate_control An object tracking due times for a particular
+ /// type of messages.
+ void checkLateMessages(RateControl& rate_control);
/// \brief Copies IA_NA or IA_PD option from one packet to another.
///
@@ -943,7 +943,7 @@ private:
/// \brief Calculate elapsed time between two packets.
///
- /// \param T Pkt4Ptr or Pkt6Ptr class.
+ /// \tparam T Pkt4Ptr or Pkt6Ptr class.
/// \param pkt1 first packet.
/// \param pkt2 second packet.
/// \throw InvalidOperation if packet timestamps are invalid.
@@ -1056,14 +1056,12 @@ private:
std::string vector2Hex(const std::vector<uint8_t>& vec,
const std::string& separator = "") const;
- boost::posix_time::ptime send_due_; ///< Due time to initiate next chunk
- ///< of exchanges.
- boost::posix_time::ptime last_sent_; ///< Indicates when the last exchange
- ///< was initiated.
- boost::posix_time::ptime renew_due_; ///< Due time to send next set of
- ///< Renew requests.
- boost::posix_time::ptime last_renew_; ///< Indicates when the last Renew
- ///< was attempted.
+ /// \brief A rate control class for Discover and Solicit messages.
+ RateControl basic_rate_control_;
+ /// \brief A rate control class for Renew messages.
+ RateControl renew_rate_control_;
+ /// \brief A rate control class for Release messages.
+ RateControl release_rate_control_;
boost::posix_time::ptime last_report_; ///< Last intermediate report time.
diff --git a/tests/tools/perfdhcp/tests/Makefile.am b/tests/tools/perfdhcp/tests/Makefile.am
index e48757a..6e808c6 100644
--- a/tests/tools/perfdhcp/tests/Makefile.am
+++ b/tests/tools/perfdhcp/tests/Makefile.am
@@ -1,6 +1,7 @@
SUBDIRS = . testdata
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -I$(srcdir)/.. -I$(builddir)/..
AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\"
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -26,6 +27,7 @@ run_unittests_SOURCES += perf_pkt6_unittest.cc
run_unittests_SOURCES += perf_pkt4_unittest.cc
run_unittests_SOURCES += localized_option_unittest.cc
run_unittests_SOURCES += packet_storage_unittest.cc
+run_unittests_SOURCES += rate_control_unittest.cc
run_unittests_SOURCES += stats_mgr_unittest.cc
run_unittests_SOURCES += test_control_unittest.cc
run_unittests_SOURCES += command_options_helper.h
@@ -33,6 +35,7 @@ run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/command_options.cc
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/pkt_transform.cc
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt6.cc
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt4.cc
+run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/rate_control.cc
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/test_control.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc
index 3431b87..3d2ce2e 100644
--- a/tests/tools/perfdhcp/tests/command_options_unittest.cc
+++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc
@@ -168,6 +168,8 @@ protected:
EXPECT_EQ(CommandOptions::DORA_SARR, opt.getExchangeMode());
EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::ADDRESS));
EXPECT_EQ(0, opt.getRate());
+ EXPECT_EQ(0, opt.getRenewRate());
+ EXPECT_EQ(0, opt.getReleaseRate());
EXPECT_EQ(0, opt.getReportDelay());
EXPECT_EQ(0, opt.getClientsNum());
@@ -341,12 +343,18 @@ TEST_F(CommandOptionsTest, RenewRate) {
EXPECT_NO_THROW(process("perfdhcp -6 -r 10 -f 10 -l ethx all"));
EXPECT_EQ(10, opt.getRenewRate());
// Check that the release rate can be set to different value than
- // rate specified as -r<rate>. Also, swap -f na d-r to make sure
+ // rate specified as -r<rate>. Also, swap -f and -r to make sure
// that order doesn't matter.
EXPECT_NO_THROW(process("perfdhcp -6 -f 5 -r 10 -l ethx all"));
EXPECT_EQ(5, opt.getRenewRate());
+ // The renew rate should not be greater than the rate.
+ EXPECT_THROW(process("perfdhcp -6 -r 10 -f 11 -l ethx all"),
+ isc::InvalidParameter);
// The renew-rate of 0 is invalid.
- EXPECT_THROW(process("perfdhcp -6 -r 10 -f 0 - l ethx all"),
+ EXPECT_THROW(process("perfdhcp -6 -r 10 -f 0 -l ethx all"),
+ isc::InvalidParameter);
+ // The negative renew-rate is invalid.
+ EXPECT_THROW(process("perfdhcp -6 -r 10 -f -5 -l ethx all"),
isc::InvalidParameter);
// If -r<rate> is not specified the -f<renew-rate> should not
// be accepted.
@@ -365,6 +373,62 @@ TEST_F(CommandOptionsTest, RenewRate) {
isc::InvalidParameter);
}
+TEST_F(CommandOptionsTest, ReleaseRate) {
+ CommandOptions& opt = CommandOptions::instance();
+ // If -F is specified together with -r the command line should
+ // be accepted and the release rate should be set.
+ EXPECT_NO_THROW(process("perfdhcp -6 -r 10 -F 10 -l ethx all"));
+ EXPECT_EQ(10, opt.getReleaseRate());
+ // Check that the release rate can be set to different value than
+ // rate specified as -r<rate>. Also, swap -F and -r to make sure
+ // that order doesn't matter.
+ EXPECT_NO_THROW(process("perfdhcp -6 -F 5 -r 10 -l ethx all"));
+ EXPECT_EQ(5, opt.getReleaseRate());
+ // The release rate should not be greater than the rate.
+ EXPECT_THROW(process("perfdhcp -6 -r 10 -F 11 -l ethx all"),
+ isc::InvalidParameter);
+ // The release-rate of 0 is invalid.
+ EXPECT_THROW(process("perfdhcp -6 -r 10 -F 0 -l ethx all"),
+ isc::InvalidParameter);
+ // The negative rlease-rate is invalid.
+ EXPECT_THROW(process("perfdhcp -6 -r 10 -F -5 -l ethx all"),
+ isc::InvalidParameter);
+ // If -r<rate> is not specified the -F<release-rate> should not
+ // be accepted.
+ EXPECT_THROW(process("perfdhcp -6 -F 10 -l ethx all"),
+ isc::InvalidParameter);
+ // Currently the -F<release-rate> can be specified for IPv6 mode
+ // only.
+ EXPECT_THROW(process("perfdhcp -4 -r 10 -F 10 -l ethx all"),
+ isc::InvalidParameter);
+ // Release rate should be specified.
+ EXPECT_THROW(process("perfdhcp -6 -r 10 -F -l ethx all"),
+ isc::InvalidParameter);
+
+ // -F and -i are mutually exclusive
+ EXPECT_THROW(process("perfdhcp -6 -r 10 -F 10 -l ethx -i all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, ReleaseRenew) {
+ CommandOptions& opt = CommandOptions::instance();
+ // It should be possible to specify the -F, -f and -r options.
+ EXPECT_NO_THROW(process("perfdhcp -6 -r 10 -F 3 -f 5 -l ethx all"));
+ EXPECT_EQ(10, opt.getRate());
+ EXPECT_EQ(3, opt.getReleaseRate());
+ EXPECT_EQ(5, opt.getRenewRate());
+ // It should be possible to specify the -F and -f with the values which
+ // sum is equal to the rate specified as -r<rate>.
+ EXPECT_NO_THROW(process("perfdhcp -6 -r 8 -F 3 -f 5 -l ethx all"));
+ EXPECT_EQ(8, opt.getRate());
+ EXPECT_EQ(3, opt.getReleaseRate());
+ EXPECT_EQ(5, opt.getRenewRate());
+ // Check that the sum of the release and renew rate is not greater
+ // than the rate specified as -r<rate>.
+ EXPECT_THROW(process("perfdhcp -6 -F 6 -f 5 -r 10 -l ethx all"),
+ isc::InvalidParameter);
+}
+
TEST_F(CommandOptionsTest, ReportDelay) {
CommandOptions& opt = CommandOptions::instance();
EXPECT_NO_THROW(process("perfdhcp -r 100 -t 17 -l ethx all"));
diff --git a/tests/tools/perfdhcp/tests/rate_control_unittest.cc b/tests/tools/perfdhcp/tests/rate_control_unittest.cc
new file mode 100644
index 0000000..829d37f
--- /dev/null
+++ b/tests/tools/perfdhcp/tests/rate_control_unittest.cc
@@ -0,0 +1,207 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+#include "rate_control.h"
+#include <gtest/gtest.h>
+
+
+using namespace isc;
+using namespace isc::perfdhcp;
+
+/// \brief A class which exposes protected methods and members of the
+/// RateControl class (under test).
+class NakedRateControl : public RateControl {
+public:
+
+ /// \brief Default constructor.
+ NakedRateControl()
+ : RateControl() {
+ }
+
+ /// \brief Constructor which sets up the rate and aggressivity.
+ ///
+ /// \param rate A rate at which messages are sent.
+ /// \param aggressivity A value of aggressivity. This value controls the
+ /// maximal number of messages sent in one chunk.
+ NakedRateControl(const int rate, const int aggressivity)
+ : RateControl(rate, aggressivity) {
+ }
+
+ using RateControl::currentTime;
+ using RateControl::updateSendTime;
+ using RateControl::updateSendDue;
+ using RateControl::send_due_;
+ using RateControl::last_sent_;
+ using RateControl::late_sent_;
+
+};
+
+// Test default constructor.
+TEST(RateControl, constructorDefault) {
+ NakedRateControl rc;
+ EXPECT_EQ(1, rc.getAggressivity());
+ EXPECT_EQ(0, rc.getRate());
+ EXPECT_FALSE(rc.getDue().is_not_a_date_time());
+ EXPECT_FALSE(rc.last_sent_.is_not_a_date_time());
+ EXPECT_FALSE(rc.isLateSent());
+}
+
+// Test the constructor which sets the rate and aggressivity.
+TEST(RateControl, constructor) {
+ // Call the constructor and verify that it sets the appropriate
+ // values.
+ NakedRateControl rc1(3, 2);
+ EXPECT_EQ(2, rc1.getAggressivity());
+ EXPECT_EQ(3, rc1.getRate());
+ EXPECT_FALSE(rc1.getDue().is_not_a_date_time());
+ EXPECT_FALSE(rc1.last_sent_.is_not_a_date_time());
+ EXPECT_FALSE(rc1.isLateSent());
+
+ // Call the constructor again and make sure that different values
+ // will be set correctly.
+ NakedRateControl rc2(5, 6);
+ EXPECT_EQ(6, rc2.getAggressivity());
+ EXPECT_EQ(5, rc2.getRate());
+ EXPECT_FALSE(rc2.getDue().is_not_a_date_time());
+ EXPECT_FALSE(rc2.last_sent_.is_not_a_date_time());
+ EXPECT_FALSE(rc2.isLateSent());
+
+ // The 0 value of aggressivity < 1 is not acceptable.
+ EXPECT_THROW(RateControl(3, 0), isc::BadValue);
+ // The negative value of rate is not acceptable.
+ EXPECT_THROW(RateControl(-1, 3), isc::BadValue);
+}
+
+// Check the aggressivity accessor.
+TEST(RateControl, getAggressivity) {
+ RateControl rc;
+ ASSERT_EQ(1, rc.getAggressivity());
+ rc.setAggressivity(5);
+ ASSERT_EQ(5, rc.getAggressivity());
+ rc.setAggressivity(10);
+ EXPECT_EQ(10, rc.getAggressivity());
+}
+
+// Check the due time accessor.
+TEST(RateControl, getDue) {
+ NakedRateControl rc;
+ ASSERT_FALSE(rc.getDue().is_not_a_date_time());
+ rc.send_due_ = NakedRateControl::currentTime();
+ EXPECT_TRUE(NakedRateControl::currentTime() >= rc.getDue());
+ rc.send_due_ = NakedRateControl::currentTime() +
+ boost::posix_time::seconds(10);
+ EXPECT_TRUE(NakedRateControl::currentTime() < rc.getDue());
+}
+
+// Check the rate accessor.
+TEST(RateControl, getRate) {
+ RateControl rc;
+ ASSERT_EQ(0, rc.getRate());
+ rc.setRate(5);
+ ASSERT_EQ(5, rc.getRate());
+ rc.setRate(10);
+ EXPECT_EQ(10, rc.getRate());
+}
+
+// Check if late send flag accessor.
+TEST(RateControl, isLateSent) {
+ NakedRateControl rc;
+ ASSERT_FALSE(rc.isLateSent());
+ rc.late_sent_ = true;
+ EXPECT_TRUE(rc.isLateSent());
+}
+
+// Check that the function returns the number of messages to be sent "now"
+// correctly.
+// @todo Possibly extend this test to cover more complex scenarios. Note that
+// it is quite hard to fully test this function as its behaviour strongly
+// depends on time.
+TEST(RateControl, getOutboundMessageCount) {
+ NakedRateControl rc1(1000, 1);
+ // Set the timestamp of the last sent message well to the past.
+ // The resulting due time will be in the past too.
+ rc1.last_sent_ =
+ NakedRateControl::currentTime() - boost::posix_time::seconds(5);
+ // The number of messages to be sent must be greater than 0.
+ uint64_t count;
+ ASSERT_NO_THROW(count = rc1.getOutboundMessageCount());
+ EXPECT_GT(count, 0);
+ // Now, don't specify the rate. In this case the aggressivity dictates
+ // how many messages to send.
+ NakedRateControl rc2(0, 3);
+ rc2.last_sent_ =
+ NakedRateControl::currentTime() - boost::posix_time::seconds(5);
+ ASSERT_NO_THROW(count = rc2.getOutboundMessageCount());
+ EXPECT_EQ(3, count);
+ // Specify the rate and set the timestamp of the last sent message well
+ // to the future. If the resulting due time is well in the future too,
+ // the number of messages to be sent must be 0.
+ NakedRateControl rc3(10, 3);
+ rc3.last_sent_ = NakedRateControl::currentTime() +
+ boost::posix_time::seconds(5);
+ ASSERT_NO_THROW(count = rc3.getOutboundMessageCount());
+ EXPECT_EQ(0, count);
+
+}
+
+// Test the aggressivity modifier for valid and invalid values.
+TEST(RateControl, setAggressivity) {
+ NakedRateControl rc;
+ ASSERT_NO_THROW(rc.setAggressivity(1));
+ EXPECT_THROW(rc.setAggressivity(0), isc::BadValue);
+ EXPECT_THROW(rc.setAggressivity(-1), isc::BadValue);
+}
+
+// Test the rate modifier for valid and invalid rate values.
+TEST(RateControl, setRate) {
+ NakedRateControl rc;
+ EXPECT_NO_THROW(rc.setRate(1));
+ EXPECT_NO_THROW(rc.setRate(0));
+ EXPECT_THROW(rc.setRate(-1), isc::BadValue);
+}
+
+// Test the function which calculates the due time to send next set of
+// messages.
+TEST(RateControl, updateSendDue) {
+ NakedRateControl rc;
+ // Set the send due timestamp to the value which is well in the future.
+ // If we don't hit the due time, the function should not modify the
+ // due time.
+ rc.send_due_ =
+ NakedRateControl::currentTime() + boost::posix_time::seconds(10);
+ boost::posix_time::ptime last_send_due = rc.send_due_;
+ ASSERT_NO_THROW(rc.updateSendDue());
+ EXPECT_TRUE(rc.send_due_ == last_send_due);
+ // Set the due time to the value which is already behind.
+ rc.send_due_ =
+ NakedRateControl::currentTime() - boost::posix_time::seconds(10);
+ last_send_due = rc.send_due_;
+ ASSERT_NO_THROW(rc.updateSendDue());
+ // The value should be modified to the new value.
+ EXPECT_TRUE(rc.send_due_ > last_send_due);
+
+}
+
+// Test that the message send time is updated to the current time.
+TEST(RateControl, updateSendTime) {
+ NakedRateControl rc;
+ // Set the timestamp to the future.
+ rc.last_sent_ =
+ NakedRateControl::currentTime() + boost::posix_time::seconds(5);
+ rc.updateSendTime();
+ // Updated timestamp should be set to now or to the past.
+ EXPECT_TRUE(rc.last_sent_ <= NakedRateControl::currentTime());
+
+}
diff --git a/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc b/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
index 41aac82..6147bf7 100644
--- a/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
+++ b/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
@@ -187,6 +187,8 @@ TEST_F(StatsMgrTest, Exchange) {
common_transid));
// This is expected to throw because XCHG_DO was not yet
// added to Stats Manager for tracking.
+ ASSERT_FALSE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_DO));
+ ASSERT_FALSE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_RA));
EXPECT_THROW(
stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet),
BadValue
@@ -196,8 +198,11 @@ TEST_F(StatsMgrTest, Exchange) {
BadValue
);
+
// Adding DISCOVER-OFFER exchanges to be tracked by Stats Manager.
stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
+ ASSERT_TRUE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_DO));
+ ASSERT_FALSE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_RA));
// The following two attempts are expected to throw because
// invalid exchange types are passed (XCHG_RA instead of XCHG_DO)
EXPECT_THROW(
@@ -259,6 +264,21 @@ TEST_F(StatsMgrTest, MultipleExchanges) {
stats_mgr->getRcvdPacketsNum(StatsMgr6::XCHG_RR));
}
+TEST_F(StatsMgrTest, ExchangeToString) {
+ // Test DHCPv4 specific exchange names.
+ EXPECT_EQ("DISCOVER-OFFER",
+ StatsMgr4::exchangeToString(StatsMgr4::XCHG_DO));
+ EXPECT_EQ("REQUEST-ACK", StatsMgr4::exchangeToString(StatsMgr4::XCHG_RA));
+
+ // Test DHCPv6 specific exchange names.
+ EXPECT_EQ("SOLICIT-ADVERTISE",
+ StatsMgr6::exchangeToString(StatsMgr6::XCHG_SA));
+ EXPECT_EQ("REQUEST-REPLY", StatsMgr6::exchangeToString(StatsMgr6::XCHG_RR));
+ EXPECT_EQ("RENEW-REPLY", StatsMgr6::exchangeToString(StatsMgr6::XCHG_RN));
+ EXPECT_EQ("RELEASE-REPLY", StatsMgr6::exchangeToString(StatsMgr6::XCHG_RL));
+
+}
+
TEST_F(StatsMgrTest, SendReceiveSimple) {
boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc
index 0d45c88..b84d58d 100644
--- a/tests/tools/perfdhcp/tests/test_control_unittest.cc
+++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc
@@ -74,8 +74,32 @@ public:
uint32_t transid_; ///< Last generated transaction id.
};
+ /// \brief Sets the due times for sedning Solicit, Renew and Release.
+ ///
+ /// There are three class members that hold the due time for sending DHCP
+ /// messages:
+ /// - send_due_ - due time to send Solicit,
+ /// - renew_due_ - due time to send Renew,
+ /// - release_due_ - due time to send Release.
+ /// Some tests in this test suite need to modify these values relative to
+ /// the current time. This function modifies this values using time
+ /// offset values (positive or negative) specified as a difference in
+ /// seconds between current time and the due time.
+ ///
+ /// \param send_secs An offset of the due time for Solicit.
+ /// \param renew_secs An offset of the due time for Renew.
+ /// \param release_secs An offset of the due time for Release.
+ void setRelativeDueTimes(const int send_secs, const int renew_secs = 0,
+ const int release_secs = 0) {
+ ptime now = microsec_clock::universal_time();
+ basic_rate_control_.setRelativeDue(send_secs);
+ renew_rate_control_.setRelativeDue(renew_secs);
+ release_rate_control_.setRelativeDue(release_secs);
+
+ }
+
using TestControl::checkExitConditions;
- using TestControl::createRenew;
+ using TestControl::createMessageFromReply;
using TestControl::factoryElapsedTime6;
using TestControl::factoryGeneric;
using TestControl::factoryIana6;
@@ -84,7 +108,7 @@ public:
using TestControl::factoryRequestList4;
using TestControl::generateDuid;
using TestControl::generateMacAddress;
- using TestControl::getNextExchangesNum;
+ using TestControl::getCurrentTimeout;
using TestControl::getTemplateBuffer;
using TestControl::initPacketTemplates;
using TestControl::initializeStatsMgr;
@@ -92,13 +116,22 @@ public:
using TestControl::processReceivedPacket4;
using TestControl::processReceivedPacket6;
using TestControl::registerOptionFactories;
+ using TestControl::reset;
using TestControl::sendDiscover4;
using TestControl::sendPackets;
- using TestControl::sendRenewPackets;
+ using TestControl::sendMultipleMessages6;
using TestControl::sendRequest6;
using TestControl::sendSolicit6;
using TestControl::setDefaults4;
using TestControl::setDefaults6;
+ using TestControl::basic_rate_control_;
+ using TestControl::renew_rate_control_;
+ using TestControl::release_rate_control_;
+ using TestControl::last_report_;
+ using TestControl::transid_gen_;
+ using TestControl::macaddr_gen_;
+ using TestControl::first_packet_serverid_;
+ using TestControl::interrupted_;
NakedTestControl() : TestControl() {
uint32_t clients_num = CommandOptions::instance().getClientsNum() == 0 ?
@@ -626,6 +659,167 @@ public:
}
}
+ /// \brief Test that the DHCPv4 Release or Renew message is created
+ /// correctly and comprises expected options.
+ ///
+ /// \param msg_type A type of the message to be tested: DHCPV6_RELEASE
+ /// or DHCPV6_RENEW.
+ void testCreateRenewRelease(const uint16_t msg_type) {
+ // This command line specifies that the Release/Renew messages should
+ // be sent with the same rate as the Solicit messages.
+ std::ostringstream s;
+ s << "perfdhcp -6 -l lo -r 10 ";
+ s << (msg_type == DHCPV6_RELEASE ? "-F" : "-f") << " 10 ";
+ s << "-R 10 -L 10547 -n 10 -e address-and-prefix ::1";
+ ASSERT_NO_THROW(processCmdLine(s.str()));
+ // Create a test controller class.
+ NakedTestControl tc;
+ // Set the transaction id generator which will be used by the
+ // createRenew or createRelease function to generate transaction id.
+ boost::shared_ptr<NakedTestControl::IncrementalGenerator>
+ generator(new NakedTestControl::IncrementalGenerator());
+ tc.setTransidGenerator(generator);
+
+ // Create a Reply packet. The createRelease or createReply function will
+ // need Reply packet to create a corresponding Release or Reply.
+ Pkt6Ptr reply = createReplyPkt6(1);
+
+ Pkt6Ptr msg;
+ // Check that the message is created.
+ ASSERT_NO_THROW(msg = tc.createMessageFromReply(msg_type, reply));
+
+ ASSERT_TRUE(msg);
+ // Check that the message type and transaction id is correct.
+ EXPECT_EQ(msg_type, msg->getType());
+ EXPECT_EQ(1, msg->getTransid());
+
+ // Check that the message has expected options. These are the same for
+ // Release and Renew.
+
+ // Client Identifier.
+ OptionPtr opt_clientid = msg->getOption(D6O_CLIENTID);
+ ASSERT_TRUE(opt_clientid);
+ EXPECT_TRUE(reply->getOption(D6O_CLIENTID)->getData() ==
+ opt_clientid->getData());
+
+ // Server identifier
+ OptionPtr opt_serverid = msg->getOption(D6O_SERVERID);
+ ASSERT_TRUE(opt_serverid);
+ EXPECT_TRUE(reply->getOption(D6O_SERVERID)->getData() ==
+ opt_serverid->getData());
+
+ // IA_NA
+ OptionPtr opt_ia_na = msg->getOption(D6O_IA_NA);
+ ASSERT_TRUE(opt_ia_na);
+ EXPECT_TRUE(reply->getOption(D6O_IA_NA)->getData() ==
+ opt_ia_na->getData());
+
+ // IA_PD
+ OptionPtr opt_ia_pd = msg->getOption(D6O_IA_PD);
+ ASSERT_TRUE(opt_ia_pd);
+ EXPECT_TRUE(reply->getOption(D6O_IA_PD)->getData() ==
+ opt_ia_pd->getData());
+
+ // Make sure that exception is thrown if the Reply message is NULL.
+ EXPECT_THROW(tc.createMessageFromReply(msg_type, Pkt6Ptr()),
+ isc::BadValue);
+
+ }
+
+ /// \brief Test sending DHCPv6 Releases or Renews.
+ ///
+ /// This function simulates acquiring 10 leases from the server. Returned
+ /// Reply messages are cached and used to send Renew or Release messages.
+ /// The maxmimal number of Renew or Release messages which can be sent is
+ /// equal to the number of leases acquired (10). This function also checks
+ /// that an attempt to send more Renew or Release messages than the number
+ /// of leases acquired will fail.
+ ///
+ /// \param msg_type A type of the message which is simulated to be sent
+ /// (DHCPV6_RENEW or DHCPV6_RELEASE).
+ void testSendRenewRelease(const uint16_t msg_type) {
+ std::string loopback_iface(getLocalLoopback());
+ if (loopback_iface.empty()) {
+ std::cout << "Skipping the test because loopback interface could"
+ " not be detected" << std::endl;
+ return;
+ }
+ // Build a command line. Depending on the message type, we will use
+ // -f<renew-rate> or -F<release-rate> parameter.
+ std::ostringstream s;
+ s << "perfdhcp -6 -l " << loopback_iface << " -r 10 ";
+ s << (msg_type == DHCPV6_RENEW ? "-f" : "-F");
+ s << " 10 -R 10 -L 10547 -n 10 ::1";
+ ASSERT_NO_THROW(processCmdLine(s.str()));
+ // Create a test controller class.
+ NakedTestControl tc;
+ tc.initializeStatsMgr();
+ // Set the transaction id generator to sequential to control to
+ // guarantee that transaction ids are predictable.
+ boost::shared_ptr<NakedTestControl::IncrementalGenerator>
+ generator(new NakedTestControl::IncrementalGenerator());
+ tc.setTransidGenerator(generator);
+ // Socket has to be created so as we can actually send packets.
+ int sock_handle = 0;
+ ASSERT_NO_THROW(sock_handle = tc.openSocket());
+ TestControl::TestControlSocket sock(sock_handle);
+
+ // Send a number of Solicit messages. Each generated Solicit will be
+ // assigned a different transaction id, starting from 1 to 10.
+ tc.sendPackets(sock, 10);
+
+ // Simulate Advertise responses from the server. Each advertise is
+ // assigned a transaction id from the range of 1 to 10, so as they
+ // match the transaction ids from the Solicit messages.
+ for (int i = generator->getNext() - 10; i < generator->getNext(); ++i) {
+ Pkt6Ptr advertise(createAdvertisePkt6(i));
+ // If Advertise is matched with the Solicit the call below will
+ // trigger a corresponding Request. They will be assigned
+ // transaction ids from the range from 11 to 20 (the range of
+ // 1 to 10 has been used by Solicit-Advertise).
+ ASSERT_NO_THROW(tc.processReceivedPacket6(sock, advertise));
+ }
+
+ // Requests have been sent, so now let's simulate responses from the
+ // server. Generate corresponding Reply messages with the transaction
+ // ids from the range from 11 to 20.
+ for (int i = generator->getNext() - 10; i < generator->getNext(); ++i) {
+ Pkt6Ptr reply(createReplyPkt6(i));
+ // Each Reply packet corresponds to the new lease acquired. Since
+ // -f<renew-rate> option has been specified, received Reply
+ // messages are held so as Renew messages can be sent for
+ // existing leases.
+ ASSERT_NO_THROW(tc.processReceivedPacket6(sock, reply));
+ }
+
+ uint64_t msg_num;
+ // Try to send 5 messages. It should be successful because 10 Reply
+ // messages has been received. For each of them we should be able to
+ // send Renew or Release.
+ ASSERT_NO_THROW(
+ msg_num = tc.sendMultipleMessages6(sock, msg_type, 5)
+ );
+ // Make sure that we have sent 5 messages.
+ EXPECT_EQ(5, msg_num);
+
+ // Try to do it again. We should still have 5 Reply packets for
+ // which Renews or Releases haven't been sent yet.
+ ASSERT_NO_THROW(
+ msg_num = tc.sendMultipleMessages6(sock, msg_type, 5)
+ );
+ EXPECT_EQ(5, msg_num);
+
+ // We used all the Reply packets (we sent Renew or Release for each of
+ // them already). Therefore, no further Renew or Release messages should
+ // be sent before we acquire new leases.
+ ASSERT_NO_THROW(
+ msg_num = tc.sendMultipleMessages6(sock, msg_type, 5)
+ );
+ // Make sure that no message has been sent.
+ EXPECT_EQ(0, msg_num);
+
+ }
+
/// \brief Parse command line string with CommandOptions.
///
/// \param cmdline command line string to be parsed.
@@ -710,6 +904,25 @@ public:
};
+// This test verifies that the class members are reset to expected values.
+TEST_F(TestControlTest, reset) {
+ ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l ethx -r 50 -f 30 -F 10 -a 3 all"));
+ NakedTestControl tc;
+ tc.reset();
+ EXPECT_EQ(3, tc.basic_rate_control_.getAggressivity());
+ EXPECT_EQ(3, tc.renew_rate_control_.getAggressivity());
+ EXPECT_EQ(3, tc.release_rate_control_.getAggressivity());
+ EXPECT_EQ(50, tc.basic_rate_control_.getRate());
+ EXPECT_EQ(30, tc.renew_rate_control_.getRate());
+ EXPECT_EQ(10, tc.release_rate_control_.getRate());
+ EXPECT_FALSE(tc.last_report_.is_not_a_date_time());
+ EXPECT_FALSE(tc.transid_gen_);
+ EXPECT_FALSE(tc.macaddr_gen_);
+ EXPECT_TRUE(tc.first_packet_serverid_.empty());
+ EXPECT_FALSE(tc.interrupted_);
+
+}
+
TEST_F(TestControlTest, GenerateDuid) {
// Simple command line that simulates one client only. Always the
// same DUID will be generated.
@@ -1226,157 +1439,166 @@ TEST_F(TestControlTest, PacketTemplates) {
EXPECT_THROW(tc.initPacketTemplates(), isc::BadValue);
}
-TEST_F(TestControlTest, RateControl) {
- // We don't specify the exchange rate here so the aggressivity
- // value will determine how many packets are to be send each
- // time we query the getNextExchangesNum.
- ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 all"));
- CommandOptions& options = CommandOptions::instance();
+TEST_F(TestControlTest, processRenew) {
+ testSendRenewRelease(DHCPV6_RENEW);
+}
- NakedTestControl tc1;
- uint64_t xchgs_num = tc1.getNextExchangesNum(microsec_clock::universal_time(),
- options.getRate());
- EXPECT_EQ(options.getAggressivity(), xchgs_num);
+TEST_F(TestControlTest, processRelease) {
+ testSendRenewRelease(DHCPV6_RELEASE);
+}
- // The exchange rate is now 1 per second. We don't know how many
- // exchanges have to initiated exactly but for sure it has to be
- // non-zero value. Also, since aggressivity is very high we expect
- // that it will not be restricted by aggressivity.
- ASSERT_NO_THROW(
- processCmdLine("perfdhcp -l 127.0.0.1 -a 1000000 -r 1 all")
- );
- NakedTestControl tc2;
- xchgs_num = tc2.getNextExchangesNum(microsec_clock::universal_time(),
- options.getRate());
- EXPECT_GT(xchgs_num, 0);
- EXPECT_LT(xchgs_num, options.getAggressivity());
- // @todo add more thorough checks for rate values.
+// This test verifies that the DHCPV6 Renew message is created correctly
+// and that it comprises all required options.
+TEST_F(TestControlTest, createRenew) {
+ testCreateRenewRelease(DHCPV6_RENEW);
}
-TEST_F(TestControlTest, processRenew) {
- std::string loopback_iface(getLocalLoopback());
- if (loopback_iface.empty()) {
- std::cout << "Skipping the test because loopback interface could"
- " not be detected" << std::endl;
- return;
- }
- // This command line specifies that the Renew messages should be sent
- // with the same rate as the Solicit messages.
- ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l " + loopback_iface +
- " -r 10 -f 10 -R 10 -L 10547 -n 10 ::1"));
- // Create a test controller class.
+// This test verifies that the DHCPv6 Release message is created correctly
+// and that it comprises all required options.
+TEST_F(TestControlTest, createRelease) {
+ testCreateRenewRelease(DHCPV6_RELEASE);
+}
+
+// This test verifies that the current timeout value for waiting for
+// the server's responses is valid. The timeout value corresponds to the
+// time period between now and the next message to be sent from the
+// perfdhcp to a server.
+TEST_F(TestControlTest, getCurrentTimeout) {
+ // Process the command line: set the rate for Discovers to 10,
+ // and set Renew rate to 0 (-f flag absent).
+ ASSERT_NO_THROW(processCmdLine("perfdhcp -4 -l lo -r 10 ::1"));
NakedTestControl tc;
- tc.initializeStatsMgr();
- // Set the transaction id generator to sequential to control to guarantee
- // that transaction ids are predictable.
- boost::shared_ptr<NakedTestControl::IncrementalGenerator>
- generator(new NakedTestControl::IncrementalGenerator());
- tc.setTransidGenerator(generator);
- // Socket has to be created so as we can actually send packets.
- int sock_handle = 0;
- ASSERT_NO_THROW(sock_handle = tc.openSocket());
- TestControl::TestControlSocket sock(sock_handle);
-
- // Send a number of Solicit messages. Each generated Solicit will be
- // assigned a different transaction id, starting from 1 to 10.
- tc.sendPackets(sock, 10);
-
- // Simulate Advertise responses from the server. Each advertise is assigned
- // a transaction id from the range of 1 to 10, so as they match the
- // transaction ids from the Solicit messages.
- for (int i = generator->getNext() - 10; i < generator->getNext(); ++i) {
- Pkt6Ptr advertise(createAdvertisePkt6(i));
- // If Advertise is matched with the Solicit the call below will
- // trigger a corresponding Request. They will be assigned
- // transaction ids from the range from 11 to 20 (the range of
- // 1 to 10 has been used by Solicit-Advertise).
- ASSERT_NO_THROW(tc.processReceivedPacket6(sock, advertise));
- }
+ // Make sure that the renew rate is 0.
+ ASSERT_EQ(0, CommandOptions::instance().getRenewRate());
+ // Simulate the case when we are already behind the due time for
+ // the next Discover to be sent.
+ tc.setRelativeDueTimes(-3);
+ // Expected timeout value is 0, which means that perfdhcp should
+ // not wait for server's response but rather send the next
+ // message to a server immediately.
+ EXPECT_EQ(0, tc.getCurrentTimeout());
+ // Now, let's do set the due time to a value in the future. The returned
+ // timeout value should be somewhere between now and this time in the
+ // future. The value of ten seconds ahead should be safe and guarantee
+ // that the returned timeout value is non-zero, even though there is a
+ // delay between setting the send_due_ value and invoking the function.
+ tc.setRelativeDueTimes(10);
+ uint32_t timeout = tc.getCurrentTimeout();
+ EXPECT_GT(timeout, 0);
+ EXPECT_LE(timeout, 10000000);
+}
- // Requests have been sent, so now let's simulate responses from the server.
- // Generate corresponding Reply messages with the transaction ids from the
- // range from 11 to 20.
- for (int i = generator->getNext() - 10; i < generator->getNext(); ++i) {
- Pkt6Ptr reply(createReplyPkt6(i));
- // Each Reply packet corresponds to the new lease acquired. Since
- // -f<renew-rate> option has been specified, received Reply
- // messages are held so as Renew messages can be sent for
- // existing leases.
- ASSERT_NO_THROW(tc.processReceivedPacket6(sock, reply));
- }
+// This test verifies that the current timeout value for waiting for the
+// server's responses is valid. In this case, we are simulating that perfdhcp
+// sends Renew requests to the server, apart from the regular 4-way exchanges.
+// The timeout value depends on both the due time to send next Solicit and the
+// due time to send Renew - the timeout should be ajusted to the due time that
+// occurs sooner.
+TEST_F(TestControlTest, getCurrentTimeoutRenew) {
+ // Set the Solicit rate to 10 and the Renew rate 5.
+ ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l lo -r 10 -f 5 ::1"));
+ NakedTestControl tc;
+
+ // Make sure, that the Renew rate has been set to 5.
+ ASSERT_EQ(5, CommandOptions::instance().getRenewRate());
+ // The send_due_ is in the past, the renew_due_ is in the future.
+ tc.setRelativeDueTimes(-3, 3);
+ EXPECT_EQ(0, tc.getCurrentTimeout());
+
+ // Swap the due times from the previous check. The effect should be the
+ // same.
+ tc.setRelativeDueTimes(3, -3);
+ EXPECT_EQ(0, tc.getCurrentTimeout());
+
+ // Set both due times to the future. The renew due time is to occur
+ // sooner. The timeout should be a value between now and the
+ // renew due time.
+ tc.setRelativeDueTimes(10, 5);
+ EXPECT_GT(tc.getCurrentTimeout(), 0);
+ EXPECT_LE(tc.getCurrentTimeout(), 5000000);
+
+ // Repeat the same check, but swap the due times.
+ tc.setRelativeDueTimes(5, 10);
+ EXPECT_GT(tc.getCurrentTimeout(), 0);
+ EXPECT_LE(tc.getCurrentTimeout(), 5000000);
- uint64_t renew_num;
- // Try to send 5 Renew packets. It should be successful because
- // 10 Reply messages has been received. For each of them we should
- // be able to send Renew.
- ASSERT_NO_THROW(renew_num = tc.sendRenewPackets(sock, 5));
- // Make sure that we have sent 5 packets.
- EXPECT_EQ(5, renew_num);
-
- // Try to do it again. We should still have 5 Reply packets for
- // which Renews haven't been sent yet.
- ASSERT_NO_THROW(renew_num = tc.sendRenewPackets(sock, 5));
- EXPECT_EQ(5, renew_num);
-
- // We used all the Reply packets (we sent Renew for each of them
- // already). Therefore, no further Renew packets should be sent before
- // We acquire new leases.
- ASSERT_NO_THROW(renew_num = tc.sendRenewPackets(sock, 5));
- // Make sure that no Renew has been sent.
- EXPECT_EQ(0, renew_num);
}
-TEST_F(TestControlTest, createRenew) {
- // This command line specifies that the Renew messages should be sent
- // with the same rate as the Solicit messages.
- ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l lo -r 10 -f 10 -R 10"
- " -L 10547 -n 10 -e address-and-prefix"
- " ::1"));
- // Create a test controller class.
+// This test verifies that the current timeout value for waiting for the
+// server's responses is valid. In this case, we are simulating that perfdhcp
+// sends Release requests to the server, apart from the regular 4-way exchanges.
+TEST_F(TestControlTest, getCurrentTimeoutRelease) {
+ // Set the Solicit rate to 10 and the Release rate 5.
+ ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l lo -r 10 -F 5 ::1"));
NakedTestControl tc;
- // Set the transaction id generator because createRenew function requires
- // it to generate the transaction id for the Renew packet.
- boost::shared_ptr<NakedTestControl::IncrementalGenerator>
- generator(new NakedTestControl::IncrementalGenerator());
- tc.setTransidGenerator(generator);
-
- // Create a Reply packet. The createRenew function will need Reply
- // packet to create a corresponding Renew.
- Pkt6Ptr reply = createReplyPkt6(1);
- Pkt6Ptr renew;
- // Check that Renew is created.
- ASSERT_NO_THROW(renew = tc.createRenew(reply));
- ASSERT_TRUE(renew);
- EXPECT_EQ(DHCPV6_RENEW, renew->getType());
- EXPECT_EQ(1, renew->getTransid());
-
- // Now check that the Renew packet created, has expected options. The
- // payload of these options should be the same as the payload of the
- // options in the Reply.
-
- // Client Identifier
- OptionPtr opt_clientid = renew->getOption(D6O_CLIENTID);
- ASSERT_TRUE(opt_clientid);
- EXPECT_TRUE(reply->getOption(D6O_CLIENTID)->getData() ==
- opt_clientid->getData());
-
- // Server identifier
- OptionPtr opt_serverid = renew->getOption(D6O_SERVERID);
- ASSERT_TRUE(opt_serverid);
- EXPECT_TRUE(reply->getOption(D6O_SERVERID)->getData() ==
- opt_serverid->getData());
-
- // IA_NA
- OptionPtr opt_ia_na = renew->getOption(D6O_IA_NA);
- ASSERT_TRUE(opt_ia_na);
- EXPECT_TRUE(reply->getOption(D6O_IA_NA)->getData() ==
- opt_ia_na->getData());
- // IA_PD
- OptionPtr opt_ia_pd = renew->getOption(D6O_IA_PD);
- ASSERT_TRUE(opt_ia_pd);
- EXPECT_TRUE(reply->getOption(D6O_IA_PD)->getData() ==
- opt_ia_pd->getData());
+ // Make sure, that the Release rate has been set to 5.
+ ASSERT_EQ(5, CommandOptions::instance().getReleaseRate());
+ // The send_due_ is in the past, the renew_due_ is in the future.
+ tc.setRelativeDueTimes(-3, 0, 3);
+ EXPECT_EQ(0, tc.getCurrentTimeout());
+
+ // Swap the due times from the previous check. The effect should be the
+ // same.
+ tc.setRelativeDueTimes(3, 0, -3);
+ EXPECT_EQ(0, tc.getCurrentTimeout());
+
+ // Set both due times to the future. The renew due time is to occur
+ // sooner. The timeout should be a value between now and the
+ // release due time.
+ tc.setRelativeDueTimes(10, 0, 5);
+ EXPECT_GT(tc.getCurrentTimeout(), 0);
+ EXPECT_LE(tc.getCurrentTimeout(), 5000000);
+
+ // Repeat the same check, but swap the due times.
+ tc.setRelativeDueTimes(5, 0, 10);
+ EXPECT_GT(tc.getCurrentTimeout(), 0);
+ EXPECT_LE(tc.getCurrentTimeout(), 5000000);
}
+// This test verifies that the current timeout value for waiting for the
+// server's responses is valid. In this case, we are simulating that perfdhcp
+// sends both Renew and Release requests to the server, apart from the regular
+// 4-way exchanges.
+TEST_F(TestControlTest, getCurrentTimeoutRenewRelease) {
+ // Set the Solicit rate to 10 and, Renew rate to 5, Release rate to 3.
+ ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l lo -r 10 -f 5 -F 3 ::1"));
+ NakedTestControl tc;
+
+ // Make sure the Renew and Release rates has been set to a non-zero value.
+ ASSERT_EQ(5, CommandOptions::instance().getRenewRate());
+ ASSERT_EQ(3, CommandOptions::instance().getReleaseRate());
+
+ // If any of the due times is in the past, the timeout value should be 0,
+ // to indicate that the next message should be sent immediately.
+ tc.setRelativeDueTimes(-3, 3, 5);
+ EXPECT_EQ(0, tc.getCurrentTimeout());
+
+ tc.setRelativeDueTimes(-3, 5, 3);
+ EXPECT_EQ(0, tc.getCurrentTimeout());
+
+ tc.setRelativeDueTimes(3, -3, 5);
+ EXPECT_EQ(0, tc.getCurrentTimeout());
+
+ tc.setRelativeDueTimes(3, 2, -5);
+ EXPECT_EQ(0, tc.getCurrentTimeout());
+
+ tc.setRelativeDueTimes(-3, -2, -5);
+ EXPECT_EQ(0, tc.getCurrentTimeout());
+
+ // If due times are in the future, the timeout value should be aligned to
+ // the due time which occurs the soonest.
+ tc.setRelativeDueTimes(10, 9, 8);
+ EXPECT_GT(tc.getCurrentTimeout(), 0);
+ EXPECT_LE(tc.getCurrentTimeout(), 8000000);
+
+ tc.setRelativeDueTimes(10, 8, 9);
+ EXPECT_GT(tc.getCurrentTimeout(), 0);
+ EXPECT_LE(tc.getCurrentTimeout(), 8000000);
+
+ tc.setRelativeDueTimes(5, 8, 9);
+ EXPECT_GT(tc.getCurrentTimeout(), 0);
+ EXPECT_LE(tc.getCurrentTimeout(), 5000000);
+
+}
More information about the bind10-changes
mailing list