Browse Source

[4277] Pull 4275 changes

    Merge branch 'master' into trac4277
Thomas Markwalder 9 years ago
parent
commit
7e11527e91
34 changed files with 1581 additions and 465 deletions
  1. 10 0
      ChangeLog
  2. 1 0
      configure.ac
  3. 4 4
      src/bin/admin/tests/data/pgsql.lease6_dump_test.reference.csv
  4. 112 23
      src/bin/admin/tests/pgsql_tests.sh.in
  5. 1 1
      src/bin/dhcp4/Makefile.am
  6. 2 2
      src/bin/dhcp4/ctrl_dhcp4_srv.cc
  7. 0 75
      src/bin/dhcp4/dhcp4_dhcp4o6_ipc.cc
  8. 84 0
      src/bin/dhcp4/dhcp4to6_ipc.cc
  9. 12 20
      src/bin/dhcp4/dhcp4_dhcp4o6_ipc.h
  10. 1 0
      src/bin/dhcp4/tests/Makefile.am
  11. 176 0
      src/bin/dhcp4/tests/dhcp4to6_ipc_unittest.cc
  12. 1 1
      src/bin/dhcp6/Makefile.am
  13. 2 2
      src/bin/dhcp6/ctrl_dhcp6_srv.cc
  14. 0 75
      src/bin/dhcp6/dhcp6_dhcp4o6_ipc.cc
  15. 0 61
      src/bin/dhcp6/dhcp6_dhcp4o6_ipc.h
  16. 2 2
      src/bin/dhcp6/dhcp6_srv.cc
  17. 75 0
      src/bin/dhcp6/dhcp6to4_ipc.cc
  18. 53 0
      src/bin/dhcp6/dhcp6to4_ipc.h
  19. 1 1
      src/bin/dhcp6/tests/Makefile.am
  20. 135 0
      src/bin/dhcp6/tests/dhcp6to4_ipc_unittest.cc
  21. 112 95
      src/lib/dhcpsrv/dhcp4o6_ipc.cc
  22. 78 28
      src/lib/dhcpsrv/dhcp4o6_ipc.h
  23. 8 0
      src/lib/dhcpsrv/pgsql_lease_mgr.cc
  24. 1 1
      src/lib/dhcpsrv/pgsql_lease_mgr.h
  25. 1 0
      src/lib/dhcpsrv/tests/Makefile.am
  26. 36 65
      src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc
  27. 2 1
      src/lib/dhcpsrv/testutils/Makefile.am
  28. 41 0
      src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.cc
  29. 90 0
      src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.h
  30. 1 0
      src/share/database/scripts/pgsql/.gitignore
  31. 1 0
      src/share/database/scripts/pgsql/Makefile.am
  32. 260 8
      src/share/database/scripts/pgsql/dhcpdb_create.pgsql
  33. 7 0
      src/share/database/scripts/pgsql/dhcpdb_drop.pgsql
  34. 271 0
      src/share/database/scripts/pgsql/upgrade_2.0_to_3.0.sh.in

+ 10 - 0
ChangeLog

@@ -1,3 +1,13 @@
+1132.	[func]		tmark
+	The Postgresql schema has been updated to support host reservations and its 
+	version number has been bumped to 3.0.  This schema is content equivlent to
+	the MySQL schema version 4.2.
+	(Trac #4275, git 6f8e646bee9de22c921ed26409f3d1856ebce9e6)
+
+1131.	[func]		fdupont
+	Update the DHCPv4-over-DHCPv6 inter-process communication code.
+	(Trac #4106, git 1ce1cba00af5d81822d5bbe26ddd28b718f51d54)
+
 1130.	[func]		marcin
 1130.	[func]		marcin
 	DHCPv6 server assigns DHCP options specified for hosts. Host
 	DHCPv6 server assigns DHCP options specified for hosts. Host
 	specific options take precedence over class specific options,
 	specific options take precedence over class specific options,

+ 1 - 0
configure.ac

@@ -1485,6 +1485,7 @@ AC_CONFIG_FILES([compatcheck/Makefile
                  src/share/database/scripts/mysql/upgrade_4.0_to_4.1.sh
                  src/share/database/scripts/mysql/upgrade_4.0_to_4.1.sh
                  src/share/database/scripts/pgsql/Makefile
                  src/share/database/scripts/pgsql/Makefile
                  src/share/database/scripts/pgsql/upgrade_1.0_to_2.0.sh
                  src/share/database/scripts/pgsql/upgrade_1.0_to_2.0.sh
+                 src/share/database/scripts/pgsql/upgrade_2.0_to_3.0.sh
                  tools/Makefile
                  tools/Makefile
                  tools/path_replacer.sh
                  tools/path_replacer.sh
 ])
 ])

+ 4 - 4
src/bin/admin/tests/data/pgsql.lease6_dump_test.reference.csv

@@ -1,4 +1,4 @@
-address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,state
+address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,state,hwaddr,hwtype,hwaddr_source
-12,21,30,<timestamp3>,40,50,IA_TA,60,70,1,1,three.example.com,expired-reclaimed
+10,20,30,<timestamp1>,40,50,IA_TA,60,70,1,1,one.example.com,default,80,90,HWADDR_SOURCE_REMOTE_ID
-11,,30,<timestamp2>,40,50,IA_TA,60,70,1,1,,declined
+11,,30,<timestamp2>,40,50,IA_TA,60,70,1,1,,declined,80,90,HWADDR_SOURCE_RAW
-10,20,30,<timestamp1>,40,50,IA_TA,60,70,1,1,one.example.com,default
+12,21,30,<timestamp3>,40,50,IA_TA,60,70,1,1,three.example.com,expired-reclaimed,80,90,HWADDR_SOURCE_DUID

+ 112 - 23
src/bin/admin/tests/pgsql_tests.sh.in

@@ -88,7 +88,7 @@ pgsql_lease_version_test() {
 
 
     # Verfiy that kea-admin lease-version returns the correct version
     # Verfiy that kea-admin lease-version returns the correct version
     version=$(${keaadmin} lease-version pgsql -u $db_user -p $db_password -n $db_name)
     version=$(${keaadmin} lease-version pgsql -u $db_user -p $db_password -n $db_name)
-    assert_str_eq "2.0" ${version} "Expected kea-admin to return %s, returned value was %s"
+    assert_str_eq "3.0" ${version} "Expected kea-admin to return %s, returned value was %s"
 
 
     # Let's wipe the whole database
     # Let's wipe the whole database
     pgsql_wipe
     pgsql_wipe
@@ -96,30 +96,18 @@ pgsql_lease_version_test() {
     test_finish 0
     test_finish 0
 }
 }
 
 
-pgsql_upgrade_test() {
+pgsql_upgrade_1_0_to_2_0() {
-    test_start "pgsql.upgrade-test"
+    # Added state column to lease4
-
+    output=`pgsql_execute "select state from lease4;"`
-    # Wipe the whole database
-    pgsql_wipe
-
-    # Initialize database to schema 1.0.
-    pgsql_execute_script @abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.pgsql
-    assert_eq 0 $? "cannot initialize the database, expected exit code: %d, actual: %d"
-
-    ${keaadmin} lease-upgrade pgsql -u $db_user -p $db_password -n $db_name -d $db_scripts_dir
-    assert_eq 0 $? "lease-upgrade failed, expected exit code: %d, actual: %d"
-
-    #table: state column added to lease4 (upgrade 1.0 -> 2.0)
-    output=`pgsql_execute "SELECT state from lease4;"`
     ERRCODE=$?
     ERRCODE=$?
     assert_eq 0 $ERRCODE "lease4 is missing state column. (returned status code %d, expected %d)"
     assert_eq 0 $ERRCODE "lease4 is missing state column. (returned status code %d, expected %d)"
 
 
-    #table: state column added to lease6 (upgrade 1.0 -> 2.0)
+    # Added state column to lease6
-    output=`pgsql_execute "SELECT state from lease6;"`
+    output=`pgsql_execute "select state from lease6;"`
     ERRCODE=$?
     ERRCODE=$?
     assert_eq 0 $ERRCODE "lease6 is missing state column. (returned status code %d, expected %d)"
     assert_eq 0 $ERRCODE "lease6 is missing state column. (returned status code %d, expected %d)"
 
 
-    #table: stored procedures for lease dumps added (upgrade 1.0 -> 2.0)
+    # Added stored procedures for lease dumps
     output=`pgsql_execute "select lease4DumpHeader from lease4DumpHeader();"`
     output=`pgsql_execute "select lease4DumpHeader from lease4DumpHeader();"`
     assert_eq 0 $ERRCODE "function lease4DumpHeader() broken or missing. (returned status code %d, expected %d)"
     assert_eq 0 $ERRCODE "function lease4DumpHeader() broken or missing. (returned status code %d, expected %d)"
 
 
@@ -131,6 +119,107 @@ pgsql_upgrade_test() {
 
 
     output=`pgsql_execute "select address from lease6DumpData();"`
     output=`pgsql_execute "select address from lease6DumpData();"`
     assert_eq 0 $ERRCODE "function lease6DumpData() broken or missing. (returned status code %d, expected %d)"
     assert_eq 0 $ERRCODE "function lease6DumpData() broken or missing. (returned status code %d, expected %d)"
+}
+
+pgsql_upgrade_2_0_to_3_0() {
+    # Added hwaddr, hwtype, and hwaddr_source columns to lease6 table
+    output=`pgsql_execute "select hwaddr, hwtype, hwaddr_source from lease6;"`
+    ERRCODE=$?
+    assert_eq 0 $ERRCODE "lease6 table not upgraded to 3.0 (returned status code %d, expected %d)"
+
+    # Added lease_hwaddr_source table
+    output=`pgsql_execute "select hwaddr_source, name from lease_hwaddr_source;"`
+    ERRCODE=$?
+    assert_eq 0 $ERRCODE "lease_hwaddr_source table is missing or broken. (returned status code %d, expected %d)"
+
+    # Added hosts table
+    output=`pgsql_execute "select host_id, dhcp_identifier, dhcp_identifier_type, dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, dhcp4_client_classes, dhcp6_client_classes from hosts;"`
+    ERRCODE=$?
+    assert_eq 0 $ERRCODE "hosts table is missing or broken. (returned status code %d, expected %d)"
+
+    # Added ipv6_reservations table
+    output=`pgsql_execute "select reservation_id, address, prefix_len, type, dhcp6_iaid, host_id from ipv6_reservations;"`
+    ERRCODE=$?
+    assert_eq 0 $ERRCODE "ipv6_reservations table is missing or broken. (returned status code %d, expected %d)"
+
+    # Added dhcp4_options table
+    output=`pgsql_execute "select option_id, code, value, formatted_value, space, persistent, dhcp_client_class, dhcp4_subnet_id, host_id, scope_id from dhcp4_options;"`
+    ERRCODE=$?
+    assert_eq 0 $ERRCODE "dhcp4_options table is missing or broken. (returned status code %d, expected %d)"
+
+    # Added dhcp6_options table
+    output=`pgsql_execute "select option_id, code, value, formatted_value, space, persistent, dhcp_client_class, dhcp6_subnet_id, host_id,scope_id from dhcp6_options;"`
+    ERRCODE=$?
+    assert_eq 0 $ERRCODE "dhcp6_options table is missing or broken. (returned status code %d, expected %d)"
+
+    # Added  host_identifier_type table
+    output=`pgsql_execute "select type, name from host_identifier_type;"`
+    ERRCODE=$?
+    assert_eq 0 $ERRCODE "host_identifier_type table is missing or broken. (returned status code %d, expected %d)"
+
+    # Added dhcp_option_scope table
+    output=`pgsql_execute "select scope_id, scope_name from dhcp_option_scope;"`
+    ERRCODE=$?
+    assert_eq 0 $ERRCODE "dhcp_option_scope table is missing or broken. (returned status code %d, expected %d)"
+
+    # Added dhcp6_options table
+    output=`pgsql_execute "select option_id, code, value, formatted_value, space, persistent, dhcp_client_class, dhcp6_subnet_id, host_id,scope_id from dhcp6_options;"`
+    ERRCODE=$?
+    assert_eq 0 $ERRCODE "dhcp6_options table is missing or broken. (returned status code %d, expected %d)"
+
+    # Added order by clause to lease4DumpData
+    output=`pgsql_execute "select address from lease4DumpData();"`
+    assert_eq 0 $ERRCODE "function lease4DumpData() broken or missing. (returned status code %d, expected %d)"
+    output=`pgsql_execute "\sf lease4DumpData"`
+    assert_eq 0 $ERRCODE "\sf of lease4DumpData failed. (returned status code %d, expected %d)"
+    count=`echo $output | grep -ic "order by l\.address"`
+    assert_eq 1 $count "lease4DumpData is missing order by clause"
+
+    # Added hwaddr columns to lease6DumpHeader
+    output=`pgsql_execute "select lease6DumpHeader from lease6DumpHeader();"`
+    assert_eq 0 $ERRCODE "function lease6DumpHeader() broken or missing. (returned status code %d, expected %d)"
+    count=`echo $output | grep -ic "hwaddr,hwtype,hwaddr_source"`
+    assert_eq 1 $count "lease6DumpHeader is missing hwaddr columns"
+
+    # Added hwaddr columns to lease6DumpData
+    output=`pgsql_execute "select hwaddr,hwttype,hwaddr_source from lease6DumpData();"`
+    assert_eq 0 $ERRCODE "function lease6DumpData() broken or missing. (returned status code %d, expected %d)"
+
+    # Added order by clause to lease6DumpData
+    output=`pgsql_execute "\sf lease4DumpData"`
+    assert_eq 0 $ERRCODE "\sf of lease4DumpData failed. (returned status code %d, expected %d)"
+    count=`echo $output | grep -ic "order by l\.address"`
+    assert_eq 1 $count "lease4DumpData is missing order by clause"
+
+    # lease_hardware_source should have row for source = 0
+    output=`pgsql_execute "select count(hwaddr_source) from lease_hwaddr_source where hwaddr_source = 0 and name='HWADDR_SOURCE_UNKNOWN';"`
+    ERRCODE=$?
+    assert_eq 0 $ERRCODE "select from lease_hwaddr_source failed. (returned status code %d, expected %d)"
+    assert_eq 1 "$output" "lease_hwaddr_source does not contain entry for HWADDR_SOURCE_UKNOWN. (record count %d, expected %d)"
+
+    # Verify upgraded schemd reports version 3.0.
+    version=$(${keaadmin} lease-version pgsql -u $db_user -p $db_password -n $db_name -d $db_scripts_dir)
+    assert_str_eq "3.0" ${version} "Expected kea-admin to return %s, returned value was %s"
+}
+
+pgsql_upgrade_test() {
+    test_start "pgsql.upgrade-test"
+
+    # Wipe the whole database
+    pgsql_wipe
+
+    # Initialize database to schema 1.0.
+    pgsql_execute_script @abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.pgsql
+    assert_eq 0 $? "cannot initialize the database, expected exit code: %d, actual: %d"
+
+    ${keaadmin} lease-upgrade pgsql -u $db_user -p $db_password -n $db_name -d $db_scripts_dir
+    assert_eq 0 $? "lease-upgrade failed, expected exit code: %d, actual: %d"
+
+    # Check 1.0 to 2.0 upgrade
+    pgsql_upgrade_1_0_to_2_0
+
+    # Check 2.0 to 3.0 upgrade
+    pgsql_upgrade_2_0_to_3_0
 
 
     # Let's wipe the whole database
     # Let's wipe the whole database
     pgsql_wipe
     pgsql_wipe
@@ -145,7 +234,7 @@ get_local_time() {
 
 
     # Expiration field is a "timestamp with timezone" so we need a reference
     # Expiration field is a "timestamp with timezone" so we need a reference
     # time for the machine/DB this test is running upon.
     # time for the machine/DB this test is running upon.
-    ref_timestamp=`pgsql_execute "SELECT timestamptz '$1';"`
+    ref_timestamp=`pgsql_execute "select timestamptz '$1';"`
     ERRCODE=$?
     ERRCODE=$?
     assert_eq 0 $ERRCODE "reference time query failed for [$timestamp], expected exit code %d, actual %d"
     assert_eq 0 $ERRCODE "reference time query failed for [$timestamp], expected exit code %d, actual %d"
     echo $ref_timestamp
     echo $ref_timestamp
@@ -297,9 +386,9 @@ pgsql_lease6_dump_test() {
 
 
     # Insert the reference records
     # Insert the reference records
     insert_sql="\
     insert_sql="\
-insert into lease6 values(10,E'\\x20',30,'$timestamp1',40,50,1,60,70,'t','t','one.example.com', 0);\
+insert into lease6 values(10,E'\\x20',30,'$timestamp1',40,50,1,60,70,'t','t','one.example.com', 0,decode('80','hex'),90,16);\
-insert into lease6 values(11,'',30,'$timestamp2',40,50,1,60,70,'t','t','', 1);\
+insert into lease6 values(11,'',30,'$timestamp2',40,50,1,60,70,'t','t','', 1,decode('80','hex'),90,1);\
-insert into lease6 values(12,E'\\x21',30,'$timestamp3',40,50,1,60,70,'t','t','three.example.com', 2);"
+insert into lease6 values(12,E'\\x21',30,'$timestamp3',40,50,1,60,70,'t','t','three.example.com', 2,decode('80','hex'),90,4);"
 
 
     pgsql_execute "$insert_sql"
     pgsql_execute "$insert_sql"
     ERRCODE=$?
     ERRCODE=$?

+ 1 - 1
src/bin/dhcp4/Makefile.am

@@ -62,7 +62,7 @@ libdhcp4_la_SOURCES += ctrl_dhcp4_srv.cc ctrl_dhcp4_srv.h
 libdhcp4_la_SOURCES += json_config_parser.cc json_config_parser.h
 libdhcp4_la_SOURCES += json_config_parser.cc json_config_parser.h
 libdhcp4_la_SOURCES += dhcp4_log.cc dhcp4_log.h
 libdhcp4_la_SOURCES += dhcp4_log.cc dhcp4_log.h
 libdhcp4_la_SOURCES += dhcp4_srv.cc dhcp4_srv.h
 libdhcp4_la_SOURCES += dhcp4_srv.cc dhcp4_srv.h
-libdhcp4_la_SOURCES += dhcp4_dhcp4o6_ipc.cc dhcp4_dhcp4o6_ipc.h
+libdhcp4_la_SOURCES += dhcp4to6_ipc.cc dhcp4to6_ipc.h
 
 
 libdhcp4_la_SOURCES += kea_controller.cc
 libdhcp4_la_SOURCES += kea_controller.cc
 
 

+ 2 - 2
src/bin/dhcp4/ctrl_dhcp4_srv.cc

@@ -8,7 +8,7 @@
 #include <cc/data.h>
 #include <cc/data.h>
 #include <dhcp4/ctrl_dhcp4_srv.h>
 #include <dhcp4/ctrl_dhcp4_srv.h>
 #include <dhcp4/dhcp4_log.h>
 #include <dhcp4/dhcp4_log.h>
-#include <dhcp4/dhcp4_dhcp4o6_ipc.h>
+#include <dhcp4/dhcp4to6_ipc.h>
 #include <hooks/hooks_manager.h>
 #include <hooks/hooks_manager.h>
 #include <dhcp4/json_config_parser.h>
 #include <dhcp4/json_config_parser.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
@@ -192,7 +192,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
 
 
     // Setup DHCPv4-over-DHCPv6 IPC
     // Setup DHCPv4-over-DHCPv6 IPC
     try {
     try {
-        Dhcp4o6Ipc::instance().open();
+        Dhcp4to6Ipc::instance().open();
     } catch (const std::exception& ex) {
     } catch (const std::exception& ex) {
         std::ostringstream err;
         std::ostringstream err;
         err << "error starting DHCPv4-over-DHCPv6 IPC "
         err << "error starting DHCPv4-over-DHCPv6 IPC "

+ 0 - 75
src/bin/dhcp4/dhcp4_dhcp4o6_ipc.cc

@@ -1,75 +0,0 @@
-// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <config.h>
-
-#include <util/buffer.h>
-#include <dhcp/iface_mgr.h>
-#include <dhcpsrv/cfgmgr.h>
-#include <dhcp4/dhcp4_dhcp4o6_ipc.h>
-
-using namespace std;
-
-namespace isc {
-namespace dhcp {
-
-Dhcp4o6Ipc::Dhcp4o6Ipc() : Dhcp4o6IpcBase() {}
-
-Dhcp4o6Ipc& Dhcp4o6Ipc::instance() {
-    static Dhcp4o6Ipc dhcp4o6_ipc;
-    return (dhcp4o6_ipc);
-}
-
-void Dhcp4o6Ipc::open() {
-    uint32_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port();
-    if (port == 0) {
-        Dhcp4o6IpcBase::close();
-        return;
-    }
-    if (port > 65534) {
-        isc_throw(OutOfRange, "DHCP4o6 port " << port);
-    }
-
-    int old_fd = socket_fd_;
-    socket_fd_ = Dhcp4o6IpcBase::open(static_cast<uint16_t>(port), 4);
-    if ((old_fd == -1) && (socket_fd_ != old_fd)) {
-        IfaceMgr::instance().addExternalSocket(socket_fd_, Dhcp4o6Ipc::handler);
-    }
-}
-
-void Dhcp4o6Ipc::handler() {
-    Dhcp4o6Ipc& ipc = Dhcp4o6Ipc::instance();
-    Pkt6Ptr pkt = ipc.receive();
-    if (!pkt) {
-        return;
-    }
-
-    OptionCollection msgs = pkt->getOptions(D6O_DHCPV4_MSG);
-    if (msgs.size() != 1) {
-        return;
-    }
-    OptionPtr msg = pkt->getOption(D6O_DHCPV4_MSG);
-    if (!msg) {
-        isc_throw(Unexpected, "Can't get DHCPv4 message option");
-    }
-    instance().received_.reset(new Pkt4o6(msg->getData(), pkt));
-}
-
-Pkt4o6Ptr& Dhcp4o6Ipc::getReceived() {
-    return (received_);
-}
-
-};  // namespace dhcp
-
-};  // namespace isc

+ 84 - 0
src/bin/dhcp4/dhcp4to6_ipc.cc

@@ -0,0 +1,84 @@
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <util/buffer.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcp4/dhcp4to6_ipc.h>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+Dhcp4to6Ipc::Dhcp4to6Ipc() : Dhcp4o6IpcBase() {}
+
+Dhcp4to6Ipc& Dhcp4to6Ipc::instance() {
+    static Dhcp4to6Ipc dhcp4to6_ipc;
+    return (dhcp4to6_ipc);
+}
+
+void Dhcp4to6Ipc::open() {
+    uint32_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port();
+    if (port == 0) {
+        Dhcp4o6IpcBase::close();
+        return;
+    }
+    if (port > 65534) {
+        isc_throw(OutOfRange, "DHCP4o6 port " << port);
+    }
+
+    int old_fd = socket_fd_;
+    socket_fd_ = Dhcp4o6IpcBase::open(static_cast<uint16_t>(port),
+                                      ENDPOINT_TYPE_V4);
+    if ((old_fd == -1) && (socket_fd_ != old_fd)) {
+        IfaceMgr::instance().addExternalSocket(socket_fd_,
+                                               Dhcp4to6Ipc::handler);
+    }
+}
+
+void Dhcp4to6Ipc::handler() {
+    Dhcp4to6Ipc& ipc = Dhcp4to6Ipc::instance();
+
+    // Reset received message in case we return from this method before the
+    // received message pointer is updated.
+    ipc.received_.reset();
+
+    // Receive message from the IPC socket.
+    Pkt6Ptr pkt = ipc.receive();
+    if (!pkt) {
+        return;
+    }
+
+    // Each message must contain option holding DHCPv4 message.
+    OptionCollection msgs = pkt->getOptions(D6O_DHCPV4_MSG);
+    if (msgs.empty()) {
+        isc_throw(Dhcp4o6IpcError, "DHCPv4 message option not present in the"
+                  " DHCPv4o6 message received by the DHCPv4 server");
+    } else if (msgs.size() > 1) {
+        isc_throw(Dhcp4o6IpcError, "expected exactly one DHCPv4 message within"
+                  " DHCPv4 message option received by the DHCPv4 server");
+    }
+
+    OptionPtr msg = msgs.begin()->second;
+    if (!msg) {
+        isc_throw(Dhcp4o6IpcError, "null DHCPv4 message option in the"
+                  " DHCPv4o6 message received by the DHCPv4 server");
+    }
+
+    // Record this message.
+    ipc.received_.reset(new Pkt4o6(msg->getData(), pkt));
+}
+
+Pkt4o6Ptr& Dhcp4to6Ipc::getReceived() {
+    return (received_);
+}
+
+};  // namespace dhcp
+
+};  // namespace isc

+ 12 - 20
src/bin/dhcp4/dhcp4_dhcp4o6_ipc.h

@@ -1,21 +1,13 @@
 // Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 // Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
-// Permission to use, copy, modify, and/or distribute this software for any
+// This Source Code Form is subject to the terms of the Mozilla Public
-// purpose with or without fee is hereby granted, provided that the above
+// License, v. 2.0. If a copy of the MPL was not distributed with this
-// copyright notice and this permission notice appear in all copies.
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-//
-// 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 DHCP4_DHCP4O6_IPC_H
+#ifndef DHCP4TO6_IPC_H
-#define DHCP4_DHCP4O6_IPC_H
+#define DHCP4TO6_IPC_H
 
 
-/// @file dhcp4_dhcp4o6_ipc.h Defines the Dhcp4o6Ipc class.
+/// @file dhcp4to6_ipc.h Defines the Dhcp4o6Ipc class.
 /// This file defines the class Kea uses to act as the DHCPv4 server
 /// This file defines the class Kea uses to act as the DHCPv4 server
 /// side of DHCPv4-over-DHCPv6 communication between servers.
 /// side of DHCPv4-over-DHCPv6 communication between servers.
 ///
 ///
@@ -28,23 +20,23 @@ namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
 /// @brief Handles DHCPv4-over-DHCPv6 IPC on the DHCPv4 server side
 /// @brief Handles DHCPv4-over-DHCPv6 IPC on the DHCPv4 server side
-class Dhcp4o6Ipc : public Dhcp4o6IpcBase {
+class Dhcp4to6Ipc : public Dhcp4o6IpcBase {
 protected:
 protected:
     /// @brief Constructor
     /// @brief Constructor
     ///
     ///
     /// Default constructor
     /// Default constructor
-    Dhcp4o6Ipc();
+    Dhcp4to6Ipc();
 
 
     /// @brief Destructor.
     /// @brief Destructor.
-    virtual ~Dhcp4o6Ipc() { }
+    virtual ~Dhcp4to6Ipc() { }
 
 
 public:
 public:
-    /// @brief Returns pointer to the sole instance of Dhcp4o6Ipc
+    /// @brief Returns pointer to the sole instance of Dhcp4to6Ipc
     ///
     ///
-    /// Dhcp4o6Ipc is a singleton class
+    /// Dhcp4to6Ipc is a singleton class
     ///
     ///
     /// @return the only existing instance of DHCP4o6 IPC
     /// @return the only existing instance of DHCP4o6 IPC
-    static Dhcp4o6Ipc& instance();
+    static Dhcp4to6Ipc& instance();
 
 
     /// @brief Open communication socket
     /// @brief Open communication socket
     ///
     ///

+ 1 - 0
src/bin/dhcp4/tests/Makefile.am

@@ -94,6 +94,7 @@ dhcp4_unittests_SOURCES += release_unittest.cc
 dhcp4_unittests_SOURCES += out_of_range_unittest.cc
 dhcp4_unittests_SOURCES += out_of_range_unittest.cc
 dhcp4_unittests_SOURCES += decline_unittest.cc
 dhcp4_unittests_SOURCES += decline_unittest.cc
 dhcp4_unittests_SOURCES += kea_controller_unittest.cc
 dhcp4_unittests_SOURCES += kea_controller_unittest.cc
+dhcp4_unittests_SOURCES += dhcp4to6_ipc_unittest.cc
 
 
 nodist_dhcp4_unittests_SOURCES = marker_file.h test_libraries.h
 nodist_dhcp4_unittests_SOURCES = marker_file.h test_libraries.h
 
 

+ 176 - 0
src/bin/dhcp4/tests/dhcp4to6_ipc_unittest.cc

@@ -0,0 +1,176 @@
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/io_address.h>
+#include <dhcp/pkt4o6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcp4/dhcp4to6_ipc.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/testutils/dhcp4o6_test_ipc.h>
+#include <gtest/gtest.h>
+#include <stdint.h>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace isc::util;
+
+namespace {
+
+/// @brief Port number used in tests.
+const uint16_t TEST_PORT = 32000;
+
+/// @brief Define short name for the test IPC.
+typedef Dhcp4o6TestIpc TestIpc;
+
+/// @brief Test fixture class for DHCPv4 endpoint of DHCPv4o6 IPC.
+class Dhcp4to6IpcTest : public ::testing::Test {
+public:
+
+    /// @brief Constructor
+    ///
+    /// Configures IPC to use a test port. It also provides a fake
+    /// configuration of interfaces.
+    Dhcp4to6IpcTest()
+        : iface_mgr_test_config_(true) {
+        configurePort(TEST_PORT);
+    }
+
+    /// @brief Configure DHCP4o6 port.
+    ///
+    /// @param port New port.
+    void configurePort(uint16_t port);
+
+    /// @brief Creates an instance of the DHCPv4o6 Message option.
+    ///
+    /// The option will contain an empty DHCPREQUEST message, with
+    /// just the Message Type option inside and nothing else.
+    ///
+    /// @return Pointer to the instance of the DHCPv4-query Message option.
+    OptionPtr createDHCPv4MsgOption() const;
+
+private:
+
+    /// @brief Provides fake configuration of interfaces.
+    IfaceMgrTestConfig iface_mgr_test_config_;
+
+};
+
+void
+Dhcp4to6IpcTest::configurePort(uint16_t port) {
+    CfgMgr::instance().getStagingCfg()->setDhcp4o6Port(port);
+}
+
+OptionPtr
+Dhcp4to6IpcTest::createDHCPv4MsgOption() const {
+    // Create the DHCPv4 message.
+    Pkt4Ptr pkt(new Pkt4(DHCPREQUEST, 1234));
+    // Make a wire representation of the DHCPv4 message.
+    pkt->pack();
+    OutputBuffer& output_buffer = pkt->getBuffer();
+    const uint8_t* data = static_cast<const uint8_t*>(output_buffer.getData());
+    OptionBuffer option_buffer(data, data + output_buffer.getLength());
+
+    // Create the DHCPv4 Message option holding the created message.
+    OptionPtr opt_msg(new Option(Option::V6, D6O_DHCPV4_MSG, option_buffer));
+    return (opt_msg);
+}
+
+// This test verifies that the IPC returns an error when trying to bind
+// to the out of range port.
+TEST_F(Dhcp4to6IpcTest, invalidPortError) {
+    // Create instance of the IPC endpoint under test with out-of-range port.
+    configurePort(65535);
+    Dhcp4to6Ipc& ipc = Dhcp4to6Ipc::instance();
+    EXPECT_THROW(ipc.open(), isc::OutOfRange);
+}
+
+// This test verifies that the DHCPv4 endpoint of the DHCPv4o6 IPC can
+// receive messages.
+TEST_F(Dhcp4to6IpcTest, receive) {
+    // Create instance of the IPC endpoint under test.
+    Dhcp4to6Ipc& ipc = Dhcp4to6Ipc::instance();
+    // Create instance of the IPC endpoint being used as a source of messages.
+    TestIpc src_ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V6);
+
+    // Open both endpoints.
+    ASSERT_NO_THROW(ipc.open());
+    ASSERT_NO_THROW(src_ipc.open());
+
+    // Create message to be sent over IPC.
+    Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_QUERY, 1234));
+    pkt->addOption(createDHCPv4MsgOption());
+    pkt->setIface("eth0");
+    pkt->setRemoteAddr(IOAddress("2001:db8:1::123"));
+    ASSERT_NO_THROW(pkt->pack());
+
+    // Send and wait up to 1 second to receive it.
+    ASSERT_NO_THROW(src_ipc.send(pkt));
+    ASSERT_NO_THROW(IfaceMgr::instance().receive6(1, 0));
+
+    // Make sure that the message has been received.
+    Pkt4o6Ptr pkt_received = ipc.getReceived();
+    ASSERT_TRUE(pkt_received);
+    Pkt6Ptr pkt6_received = pkt_received->getPkt6();
+    ASSERT_TRUE(pkt6_received);
+    EXPECT_EQ("eth0", pkt6_received->getIface());
+    EXPECT_EQ("2001:db8:1::123", pkt6_received->getRemoteAddr().toText());
+}
+
+// This test verifies that message with multiple DHCPv4 query options
+// is rejected.
+TEST_F(Dhcp4to6IpcTest, receiveMultipleQueries) {
+    // Create instance of the IPC endpoint under test.
+    Dhcp4to6Ipc& ipc = Dhcp4to6Ipc::instance();
+    // Create instance of the IPC endpoint being used as a source of messages.
+    TestIpc src_ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V6);
+
+    // Open both endpoints.
+    ASSERT_NO_THROW(ipc.open());
+    ASSERT_NO_THROW(src_ipc.open());
+
+    // Create message to be sent over IPC.
+    Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_QUERY, 1234));
+    // Add two DHCPv4 query options.
+    pkt->addOption(createDHCPv4MsgOption());
+    pkt->addOption(createDHCPv4MsgOption());
+    pkt->setIface("eth0");
+    pkt->setRemoteAddr(IOAddress("2001:db8:1::123"));
+    ASSERT_NO_THROW(pkt->pack());
+
+    // Send message.
+    ASSERT_NO_THROW(src_ipc.send(pkt));
+    // Reception handler should throw exception.
+    EXPECT_THROW(IfaceMgr::instance().receive6(1, 0), Dhcp4o6IpcError);
+}
+
+// This test verifies that message with no DHCPv4 query options is rejected.
+TEST_F(Dhcp4to6IpcTest, receiveNoQueries) {
+    // Create instance of the IPC endpoint under test.
+    Dhcp4to6Ipc& ipc = Dhcp4to6Ipc::instance();
+    // Create instance of the IPC endpoint being used as a source of messages.
+    TestIpc src_ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V6);
+
+    // Open both endpoints.
+    ASSERT_NO_THROW(ipc.open());
+    ASSERT_NO_THROW(src_ipc.open());
+
+    // Create message to be sent over IPC without DHCPv4 query option.
+    Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_QUERY, 1234));
+    pkt->setIface("eth0");
+    pkt->setRemoteAddr(IOAddress("2001:db8:1::123"));
+    ASSERT_NO_THROW(pkt->pack());
+
+    // Send message.
+    ASSERT_NO_THROW(src_ipc.send(pkt));
+    // Reception handler should throw exception.
+    EXPECT_THROW(IfaceMgr::instance().receive6(1, 0), Dhcp4o6IpcError);
+}
+
+} // end of anonymous namespace

+ 1 - 1
src/bin/dhcp6/Makefile.am

@@ -63,7 +63,7 @@ libdhcp6_la_SOURCES += dhcp6_log.cc dhcp6_log.h
 libdhcp6_la_SOURCES += dhcp6_srv.cc dhcp6_srv.h
 libdhcp6_la_SOURCES += dhcp6_srv.cc dhcp6_srv.h
 libdhcp6_la_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h
 libdhcp6_la_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h
 libdhcp6_la_SOURCES += json_config_parser.cc json_config_parser.h
 libdhcp6_la_SOURCES += json_config_parser.cc json_config_parser.h
-libdhcp6_la_SOURCES += dhcp6_dhcp4o6_ipc.cc dhcp6_dhcp4o6_ipc.h
+libdhcp6_la_SOURCES += dhcp6to4_ipc.cc dhcp6to4_ipc.h
 
 
 libdhcp6_la_SOURCES += kea_controller.cc
 libdhcp6_la_SOURCES += kea_controller.cc
 
 

+ 2 - 2
src/bin/dhcp6/ctrl_dhcp6_srv.cc

@@ -11,7 +11,7 @@
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfg_db_access.h>
 #include <dhcpsrv/cfg_db_access.h>
 #include <dhcp6/ctrl_dhcp6_srv.h>
 #include <dhcp6/ctrl_dhcp6_srv.h>
-#include <dhcp6/dhcp6_dhcp4o6_ipc.h>
+#include <dhcp6/dhcp6to4_ipc.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/json_config_parser.h>
 #include <dhcp6/json_config_parser.h>
 #include <hooks/hooks_manager.h>
 #include <hooks/hooks_manager.h>
@@ -216,7 +216,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
 
 
     // Setup DHCPv4-over-DHCPv6 IPC
     // Setup DHCPv4-over-DHCPv6 IPC
     try {
     try {
-        Dhcp4o6Ipc::instance().open();
+        Dhcp6to4Ipc::instance().open();
     } catch (const std::exception& ex) {
     } catch (const std::exception& ex) {
         std::ostringstream err;
         std::ostringstream err;
         err << "error starting DHCPv4-over-DHCPv6 IPC "
         err << "error starting DHCPv4-over-DHCPv6 IPC "

+ 0 - 75
src/bin/dhcp6/dhcp6_dhcp4o6_ipc.cc

@@ -1,75 +0,0 @@
-// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <config.h>
-
-#include <util/buffer.h>
-#include <dhcp/iface_mgr.h>
-#include <dhcpsrv/cfgmgr.h>
-#include <dhcp6/dhcp6_dhcp4o6_ipc.h>
-
-using namespace std;
-
-namespace isc {
-namespace dhcp {
-
-Dhcp4o6Ipc::Dhcp4o6Ipc() : Dhcp4o6IpcBase() {}
-
-Dhcp4o6Ipc& Dhcp4o6Ipc::instance() {
-    static Dhcp4o6Ipc dhcp4o6_ipc;
-    return (dhcp4o6_ipc);
-}
-
-void Dhcp4o6Ipc::open() {
-    uint32_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port();
-    if (port == 0) {
-        Dhcp4o6IpcBase::close();
-        return;
-    }
-    if (port > 65534) {
-        isc_throw(OutOfRange, "DHCP4o6 port " << port);
-    }
-
-    int old_fd = socket_fd_;
-    socket_fd_ = Dhcp4o6IpcBase::open(static_cast<uint16_t>(port), 6);
-    if ((old_fd == -1) && (socket_fd_ != old_fd)) {
-        IfaceMgr::instance().addExternalSocket(socket_fd_, Dhcp4o6Ipc::handler);
-    }
-}
-
-void Dhcp4o6Ipc::handler() {
-    Dhcp4o6Ipc& ipc = Dhcp4o6Ipc::instance();
-    Pkt6Ptr pkt = ipc.receive();
-    if (!pkt) {
-        return;
-    }
-
-    isc::util::OutputBuffer& buf = pkt->getBuffer();
-    buf.clear();
-    pkt->pack();
-
-    uint8_t msg_type = buf[0];
-    if ((msg_type == DHCPV6_RELAY_FORW) || (msg_type == DHCPV6_RELAY_REPL)) {
-        pkt->setRemotePort(DHCP6_SERVER_PORT);
-    } else {
-        pkt->setRemotePort(DHCP6_CLIENT_PORT);
-    }
-
-    IfaceMgr::instance().send(pkt);
-    // processStatsSent(pkt);
-}
-
-};  // namespace dhcp
-
-};  // namespace isc

+ 0 - 61
src/bin/dhcp6/dhcp6_dhcp4o6_ipc.h

@@ -1,61 +0,0 @@
-// Copyright (C) 2015-2016 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 DHCP6_DHCP4O6_IPC_H
-#define DHCP6_DHCP4O6_IPC_H
-
-/// @file dhcp6_dhcp4o6_ipc.h Defines the Dhcp4o6Ipc class.
-/// This file defines the class Kea uses to act as the DHCPv6 server
-/// side of DHCPv4-over-DHCPv6 communication between servers.
-///
-#include <dhcpsrv/dhcp4o6_ipc.h>
-#include <boost/shared_ptr.hpp>
-
-namespace isc {
-namespace dhcp {
-
-/// @brief Handles DHCPv4-over-DHCPv6 IPC on the DHCPv6 server side
-class Dhcp4o6Ipc : public Dhcp4o6IpcBase {
-protected:
-    /// @brief Constructor
-    ///
-    /// Default constructor
-    Dhcp4o6Ipc();
-
-    /// @brief Destructor.
-    virtual ~Dhcp4o6Ipc() { }
-
-public:
-    /// @brief Returns pointer to the sole instance of Dhcp4o6Ipc
-    ///
-    /// Dhcp4o6Ipc is a singleton class
-    ///
-    /// @return the only existing instance of DHCP4o6 IPC
-    static Dhcp4o6Ipc& instance();
-
-    /// @brief Open communication socket
-    ///
-    /// Call base open method and sets the handler/callback when needed
-    virtual void open();
-
-    /// @brief On receive handler
-    ///
-    /// The handler sends the DHCPv6 packet back to the remote address
-    static void handler();
-};
-
-} // namespace isc
-} // namespace dhcp
-
-#endif

+ 2 - 2
src/bin/dhcp6/dhcp6_srv.cc

@@ -25,7 +25,7 @@
 #include <dhcp/option_vendor_class.h>
 #include <dhcp/option_vendor_class.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt6.h>
-#include <dhcp6/dhcp6_dhcp4o6_ipc.h>
+#include <dhcp6/dhcp6to4_ipc.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcpsrv/callout_handle_store.h>
 #include <dhcpsrv/callout_handle_store.h>
@@ -213,7 +213,7 @@ Dhcpv6Srv::~Dhcpv6Srv() {
     }
     }
 
 
     try {
     try {
-        Dhcp4o6Ipc::instance().close();
+        Dhcp6to4Ipc::instance().close();
     } catch(const std::exception& ex) {
     } catch(const std::exception& ex) {
         // Highly unlikely, but lets Report it but go on
         // Highly unlikely, but lets Report it but go on
         // LOG_ERROR(dhcp6_logger, DHCP6_SRV_DHCP4O6_ERROR).arg(ex.what());
         // LOG_ERROR(dhcp6_logger, DHCP6_SRV_DHCP4O6_ERROR).arg(ex.what());

+ 75 - 0
src/bin/dhcp6/dhcp6to4_ipc.cc

@@ -0,0 +1,75 @@
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <util/buffer.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcp6/dhcp6to4_ipc.h>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+Dhcp6to4Ipc::Dhcp6to4Ipc() : Dhcp4o6IpcBase() {}
+
+Dhcp6to4Ipc& Dhcp6to4Ipc::instance() {
+    static Dhcp6to4Ipc dhcp6to4_ipc;
+    return (dhcp6to4_ipc);
+}
+
+void Dhcp6to4Ipc::open() {
+    uint32_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port();
+    if (port == 0) {
+        Dhcp4o6IpcBase::close();
+        return;
+    }
+    if (port > 65534) {
+        isc_throw(OutOfRange, "DHCP4o6 port " << port);
+    }
+
+    int old_fd = socket_fd_;
+    socket_fd_ = Dhcp4o6IpcBase::open(static_cast<uint16_t>(port),
+                                      ENDPOINT_TYPE_V6);
+    if ((old_fd == -1) && (socket_fd_ != old_fd)) {
+        IfaceMgr::instance().addExternalSocket(socket_fd_,
+                                               Dhcp6to4Ipc::handler);
+    }
+}
+
+void Dhcp6to4Ipc::handler() {
+    Dhcp6to4Ipc& ipc = Dhcp6to4Ipc::instance();
+
+    // Receive message from IPC.
+    Pkt6Ptr pkt = ipc.receive();
+    if (!pkt) {
+        return;
+    }
+
+    // The received message has been unpacked by the receive() function. This
+    // method could have modified the message so it's better to pack() it
+    // again because we'll be forwarding it to a client.
+    isc::util::OutputBuffer& buf = pkt->getBuffer();
+    buf.clear();
+    pkt->pack();
+
+    uint8_t msg_type = pkt->getType();
+    if ((msg_type == DHCPV6_RELAY_FORW) || (msg_type == DHCPV6_RELAY_REPL)) {
+        pkt->setRemotePort(DHCP6_SERVER_PORT);
+    } else {
+        pkt->setRemotePort(DHCP6_CLIENT_PORT);
+    }
+
+    // Forward packet to the client.
+    IfaceMgr::instance().send(pkt);
+    // processStatsSent(pkt);
+}
+
+};  // namespace dhcp
+
+};  // namespace isc

+ 53 - 0
src/bin/dhcp6/dhcp6to4_ipc.h

@@ -0,0 +1,53 @@
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef DHCP6TO4_IPC_H
+#define DHCP6TO4_IPC_H
+
+/// @file dhcp6to4_ipc.h Defines the Dhcp6to4Ipc class.
+/// This file defines the class Kea uses to act as the DHCPv6 server
+/// side of DHCPv4-over-DHCPv6 communication between servers.
+///
+#include <dhcpsrv/dhcp4o6_ipc.h>
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Handles DHCPv4-over-DHCPv6 IPC on the DHCPv6 server side
+class Dhcp6to4Ipc : public Dhcp4o6IpcBase {
+protected:
+    /// @brief Constructor
+    ///
+    /// Default constructor
+    Dhcp6to4Ipc();
+
+    /// @brief Destructor.
+    virtual ~Dhcp6to4Ipc() { }
+
+public:
+    /// @brief Returns pointer to the sole instance of Dhcp6to4Ipc
+    ///
+    /// Dhcp6to4Ipc is a singleton class
+    ///
+    /// @return the only existing instance of DHCP4o6 IPC
+    static Dhcp6to4Ipc& instance();
+
+    /// @brief Open communication socket
+    ///
+    /// Call base open method and sets the handler/callback when needed
+    virtual void open();
+
+    /// @brief On receive handler
+    ///
+    /// The handler sends the DHCPv6 packet back to the remote address
+    static void handler();
+};
+
+} // namespace isc
+} // namespace dhcp
+
+#endif

+ 1 - 1
src/bin/dhcp6/tests/Makefile.am

@@ -94,8 +94,8 @@ dhcp6_unittests_SOURCES += confirm_unittest.cc
 dhcp6_unittests_SOURCES += infrequest_unittest.cc
 dhcp6_unittests_SOURCES += infrequest_unittest.cc
 dhcp6_unittests_SOURCES += decline_unittest.cc
 dhcp6_unittests_SOURCES += decline_unittest.cc
 dhcp6_unittests_SOURCES += dhcp6_message_test.cc dhcp6_message_test.h
 dhcp6_unittests_SOURCES += dhcp6_message_test.cc dhcp6_message_test.h
-
 dhcp6_unittests_SOURCES += kea_controller_unittest.cc
 dhcp6_unittests_SOURCES += kea_controller_unittest.cc
+dhcp6_unittests_SOURCES += dhcp6to4_ipc_unittest.cc
 
 
 nodist_dhcp6_unittests_SOURCES  = marker_file.h test_libraries.h
 nodist_dhcp6_unittests_SOURCES  = marker_file.h test_libraries.h
 
 

+ 135 - 0
src/bin/dhcp6/tests/dhcp6to4_ipc_unittest.cc

@@ -0,0 +1,135 @@
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/io_address.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcp/tests/pkt_filter6_test_stub.h>
+#include <dhcp6/dhcp6to4_ipc.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/testutils/dhcp4o6_test_ipc.h>
+#include <gtest/gtest.h>
+#include <stdint.h>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace isc::util;
+
+namespace {
+
+/// @brief Port number used in tests.
+const uint16_t TEST_PORT = 32000;
+
+/// @brief Define short name for the test IPC.
+typedef Dhcp4o6TestIpc TestIpc;
+
+/// @brief Test fixture class for DHCPv4 endpoint of DHCPv4o6 IPC.
+class Dhcp6to4IpcTest : public ::testing::Test {
+public:
+
+    /// @brief Constructor
+    ///
+    /// Configures IPC to use a test port. It also provides a fake
+    /// configuration of interfaces and opens IPv6 sockets.
+    Dhcp6to4IpcTest()
+        : iface_mgr_test_config_(true) {
+        IfaceMgr::instance().openSockets6();
+        configurePort(TEST_PORT);
+    }
+
+    /// @brief Configure DHCP4o6 port.
+    ///
+    /// @param port New port.
+    void configurePort(const uint16_t port);
+
+    /// @brief Creates an instance of the DHCPv4o6 Message option.
+    ///
+    /// The option will contain an empty DHCPREQUEST message, with
+    /// just the Message Type option inside and nothing else.
+    ///
+    /// @return Pointer to the instance of the DHCPv4-query Message option.
+    OptionPtr createDHCPv4MsgOption() const;
+
+private:
+
+    /// @brief Provides fake configuration of interfaces.
+    IfaceMgrTestConfig iface_mgr_test_config_;
+};
+
+void
+Dhcp6to4IpcTest::configurePort(const uint16_t port) {
+    CfgMgr::instance().getStagingCfg()->setDhcp4o6Port(port);
+}
+
+OptionPtr
+Dhcp6to4IpcTest::createDHCPv4MsgOption() const {
+    // Create the DHCPv4 message.
+    Pkt4Ptr pkt(new Pkt4(DHCPREQUEST, 1234));
+    // Make a wire representation of the DHCPv4 message.
+    pkt->pack();
+    OutputBuffer& output_buffer = pkt->getBuffer();
+    const uint8_t* data = static_cast<const uint8_t*>(output_buffer.getData());
+    OptionBuffer option_buffer(data, data + output_buffer.getLength());
+
+    // Create the DHCPv4 Message option holding the created message.
+    OptionPtr opt_msg(new Option(Option::V6, D6O_DHCPV4_MSG, option_buffer));
+    return (opt_msg);
+}
+
+// This test verifies that the IPC returns an error when trying to bind
+// to the out of range port.
+TEST_F(Dhcp6to4IpcTest, invalidPortError) {
+    // Create instance of the IPC endpoint under test with out-of-range port.
+    configurePort(65535);
+    Dhcp6to4Ipc& ipc = Dhcp6to4Ipc::instance();
+    EXPECT_THROW(ipc.open(), isc::OutOfRange);
+}
+
+// This test verifies that the DHCPv4 endpoint of the DHCPv4o6 IPC can
+// receive messages.
+TEST_F(Dhcp6to4IpcTest, receive) {
+    // Create instance of the IPC endpoint under test.
+    Dhcp6to4Ipc& ipc = Dhcp6to4Ipc::instance();
+    // Create instance of the IPC endpoint being used as a source of messages.
+    TestIpc src_ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V4);
+
+    // Open both endpoints.
+    ASSERT_NO_THROW(ipc.open());
+    ASSERT_NO_THROW(src_ipc.open());
+
+    // Create message to be sent over IPC.
+    Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_QUERY, 1234));
+    pkt->addOption(createDHCPv4MsgOption());
+    pkt->setIface("eth0");
+    pkt->setRemoteAddr(IOAddress("2001:db8:1::123"));
+    ASSERT_NO_THROW(pkt->pack());
+
+    // Send and wait up to 1 second to receive it.
+    ASSERT_NO_THROW(src_ipc.send(pkt));
+    ASSERT_NO_THROW(IfaceMgr::instance().receive6(1, 0));
+
+#if 0
+    // The stub packet filter exposes static function to retrieve messages
+    // sent over the fake sockets/interfaces. This is the message that the
+    // IPC endpoint should forward to the client after receiving it
+    // from the DHCPv4 server.
+    Pkt6Ptr forwarded = PktFilter6TestStub::popSent();
+    ASSERT_TRUE(forwarded);
+
+    // Verify the packet received.
+    EXPECT_EQ(DHCP6_CLIENT_PORT, forwarded->getRemotePort());
+    EXPECT_EQ(forwarded->getType(), pkt->getType());
+    EXPECT_TRUE(forwarded->getOption(D6O_DHCPV4_MSG));
+    EXPECT_EQ("eth0", forwarded->getIface());
+    EXPECT_EQ("2001:db8:1::123", forwarded->getRemoteAddr().toText());
+#endif
+}
+
+} // end of anonymous namespace

+ 112 - 95
src/lib/dhcpsrv/dhcp4o6_ipc.cc

@@ -1,27 +1,23 @@
 // Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 // Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
-// Permission to use, copy, modify, and/or distribute this software for any
+// This Source Code Form is subject to the terms of the Mozilla Public
-// purpose with or without fee is hereby granted, provided that the above
+// License, v. 2.0. If a copy of the MPL was not distributed with this
-// copyright notice and this permission notice appear in all copies.
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <config.h>
 #include <config.h>
 
 
 #include <dhcp/dhcp6.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/option6_addrlst.h>
 #include <dhcp/option6_addrlst.h>
+#include <dhcp/option_custom.h>
 #include <dhcp/option_string.h>
 #include <dhcp/option_string.h>
 #include <dhcp/option_vendor.h>
 #include <dhcp/option_vendor.h>
 #include <dhcpsrv/dhcp4o6_ipc.h>
 #include <dhcpsrv/dhcp4o6_ipc.h>
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/dhcpsrv_log.h>
 
 
+#include <boost/pointer_cast.hpp>
+
+#include <errno.h>
 #include <netinet/in.h>
 #include <netinet/in.h>
 #include <sys/fcntl.h>
 #include <sys/fcntl.h>
 #include <string>
 #include <string>
@@ -39,41 +35,26 @@ Dhcp4o6IpcBase::~Dhcp4o6IpcBase() {
     close();
     close();
 }
 }
 
 
-int Dhcp4o6IpcBase::open(uint16_t port, int side) {
+int Dhcp4o6IpcBase::open(uint16_t port, EndpointType endpoint_type) {
+    // Don't check if the value is greater than 65534 as it is done
+    // by callers before they cast the value to 16 bits.
+
     if (port == port_) {
     if (port == port_) {
         // No change: nothing to do
         // No change: nothing to do
         return (socket_fd_);
         return (socket_fd_);
     }
     }
 
 
-    // Port 0: closing
-    if (port == 0) {
-        port_ = 0;
-        if (socket_fd_ != -1) {
-            IfaceMgr::instance().deleteExternalSocket(socket_fd_);
-            ::close(socket_fd_);
-            socket_fd_ = -1;
-        }
-        return (socket_fd_);
-    }
-
     // Open socket
     // Open socket
     int sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
     int sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
     if (sock < 0) {
     if (sock < 0) {
-        isc_throw(Unexpected, "Failed to create DHCP4o6 socket.");
+        isc_throw(Dhcp4o6IpcError, "Failed to create DHCP4o6 socket.");
-    }
-
-    // Set reuse address
-    int flag = 1;
-    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
-                   (char *)&flag, sizeof(flag)) < 0) {
-        ::close(sock);
-        isc_throw(Unexpected, "Failed to set SO_REUSEADDR on DHCP4o6 socket.");
     }
     }
 
 
     // Set no blocking
     // Set no blocking
     if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
     if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
         ::close(sock);
         ::close(sock);
-        isc_throw(Unexpected, "Failed to set O_NONBLOCK on DHCP4o6 socket.");
+        isc_throw(Dhcp4o6IpcError,
+                  "Failed to set O_NONBLOCK on DHCP4o6 socket.");
     }
     }
 
 
     // Bind to the local address
     // Bind to the local address
@@ -83,7 +64,7 @@ int Dhcp4o6IpcBase::open(uint16_t port, int side) {
 #ifdef HAVE_SA_LEN
 #ifdef HAVE_SA_LEN
     local6.sin6_len = sizeof(local6);
     local6.sin6_len = sizeof(local6);
 #endif
 #endif
-    if (side == 6) {
+    if (endpoint_type == ENDPOINT_TYPE_V6) {
         local6.sin6_port = htons(port);
         local6.sin6_port = htons(port);
     } else {
     } else {
         local6.sin6_port = htons(port + 1);
         local6.sin6_port = htons(port + 1);
@@ -92,7 +73,7 @@ int Dhcp4o6IpcBase::open(uint16_t port, int side) {
     local6.sin6_addr.s6_addr[15] = 1;
     local6.sin6_addr.s6_addr[15] = 1;
     if (bind(sock, (struct sockaddr *)&local6, sizeof(local6)) < 0) {
     if (bind(sock, (struct sockaddr *)&local6, sizeof(local6)) < 0) {
         ::close(sock);
         ::close(sock);
-        isc_throw(Unexpected, "Failed to bind DHCP4o6 socket.");
+        isc_throw(Dhcp4o6IpcError, "Failed to bind DHCP4o6 socket.");
     }
     }
 
 
     // Connect to the remote address
     // Connect to the remote address
@@ -102,7 +83,7 @@ int Dhcp4o6IpcBase::open(uint16_t port, int side) {
 #ifdef HAVE_SA_LEN
 #ifdef HAVE_SA_LEN
     remote6.sin6_len = sizeof(remote6);
     remote6.sin6_len = sizeof(remote6);
 #endif
 #endif
-    if (side == 6) {
+    if (endpoint_type == ENDPOINT_TYPE_V6) {
         remote6.sin6_port = htons(port + 1);
         remote6.sin6_port = htons(port + 1);
     } else {
     } else {
         remote6.sin6_port = htons(port);
         remote6.sin6_port = htons(port);
@@ -113,13 +94,13 @@ int Dhcp4o6IpcBase::open(uint16_t port, int side) {
     if (connect(sock, reinterpret_cast<const struct sockaddr*>(&remote6),
     if (connect(sock, reinterpret_cast<const struct sockaddr*>(&remote6),
                 sizeof(remote6)) < 0) {
                 sizeof(remote6)) < 0) {
         ::close(sock);
         ::close(sock);
-        isc_throw(Unexpected, "Failed to connect DHCP4o6 socket.");
+        isc_throw(Dhcp4o6IpcError, "Failed to connect DHCP4o6 socket.");
     }
     }
 
 
     if (socket_fd_ != -1) {
     if (socket_fd_ != -1) {
         if (dup2(sock, socket_fd_) == -1) {
         if (dup2(sock, socket_fd_) == -1) {
             ::close(sock);
             ::close(sock);
-            isc_throw(Unexpected, "Failed to duplicate DHCP4o6 socket.");
+            isc_throw(Dhcp4o6IpcError, "Failed to duplicate DHCP4o6 socket.");
         }
         }
         if (sock != socket_fd_) {
         if (sock != socket_fd_) {
             ::close(sock);
             ::close(sock);
@@ -134,113 +115,149 @@ int Dhcp4o6IpcBase::open(uint16_t port, int side) {
 }
 }
 
 
 void Dhcp4o6IpcBase::close() {
 void Dhcp4o6IpcBase::close() {
-    static_cast<void>(open(0, 0));
+    port_ = 0;
+    if (socket_fd_ != -1) {
+        IfaceMgr::instance().deleteExternalSocket(socket_fd_);
+        ::close(socket_fd_);
+        socket_fd_ = -1;
+    }
 }
 }
 
 
 Pkt6Ptr Dhcp4o6IpcBase::receive() {
 Pkt6Ptr Dhcp4o6IpcBase::receive() {
     uint8_t buf[65536];
     uint8_t buf[65536];
     ssize_t cc = recv(socket_fd_, buf, sizeof(buf), 0);
     ssize_t cc = recv(socket_fd_, buf, sizeof(buf), 0);
     if (cc < 0) {
     if (cc < 0) {
-        isc_throw(Unexpected, "Failed to receive on DHCP4o6 socket.");
+        isc_throw(Dhcp4o6IpcError, "Failed to receive on DHCP4o6 socket.");
     }
     }
     Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(buf, cc));
     Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(buf, cc));
     pkt->updateTimestamp();
     pkt->updateTimestamp();
 
 
     // Get interface name and remote address
     // Get interface name and remote address
     pkt->unpack();
     pkt->unpack();
-    OptionVendorPtr vendor =
+
-        boost::dynamic_pointer_cast<OptionVendor>(pkt->getOption(D6O_VENDOR_OPTS));
+    // Vendor option is initially NULL. If we find the instance of the vendor
-    if (!vendor) {
+    // option with the ISC enterprise id this pointer will point to it.
-        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+    OptionVendorPtr option_vendor;
-                  DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
+
-            .arg("no vendor option");
+    // Get all vendor option and look for the one with the ISC enterprise id.
-        return (Pkt6Ptr());
+    OptionCollection vendor_options = pkt->getOptions(D6O_VENDOR_OPTS);
-    }
+    for (OptionCollection::const_iterator opt = vendor_options.begin();
-    if (vendor->getVendorId() != ENTERPRISE_ID_ISC) {
+         opt != vendor_options.end(); ++opt) {
-        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+        option_vendor = boost::dynamic_pointer_cast<OptionVendor>(opt->second);
-                  DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
+        if (option_vendor) {
-            .arg("vendor option enterprise ID is not ISC");
+            if (option_vendor->getVendorId() == ENTERPRISE_ID_ISC) {
-        return (Pkt6Ptr());
+                break;
-    }
+            }
-    OptionStringPtr ifname =
+            option_vendor.reset();
-        boost::dynamic_pointer_cast<OptionString>(vendor->getOption(ISC_V6_4O6_INTERFACE));
+        }
+    }
+         
+    // Vendor option must exist.
+    if (!option_vendor) {
+        LOG_WARN(dhcpsrv_logger, DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
+            .arg("no ISC vendor option");
+        isc_throw(Dhcp4o6IpcError, "option " << D6O_VENDOR_OPTS
+                  << " with ISC enterprise id is not present in the DHCP4o6"
+                  " message sent between the servers");
+    }
+
+    // The option carrying interface name is required.
+    OptionStringPtr ifname = boost::dynamic_pointer_cast<
+        OptionString>(option_vendor->getOption(ISC_V6_4O6_INTERFACE));
     if (!ifname) {
     if (!ifname) {
-        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+        LOG_WARN(dhcpsrv_logger, DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
-                  DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
             .arg("no interface suboption");
             .arg("no interface suboption");
-        return (Pkt6Ptr());
+        isc_throw(Dhcp4o6IpcError, "option " << D6O_VENDOR_OPTS
+                  << " doesn't contain the " << ISC_V6_4O6_INTERFACE
+                  << " option required in the DHCP4o6 message sent"
+                  " between Kea servers");
     }
     }
+
+    // Check if this interface is present in the system.
     IfacePtr iface = IfaceMgr::instance().getIface(ifname->getValue());
     IfacePtr iface = IfaceMgr::instance().getIface(ifname->getValue());
     if (!iface) {
     if (!iface) {
-        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+        LOG_WARN(dhcpsrv_logger, DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
-                  DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
             .arg("can't get interface " + ifname->getValue());
             .arg("can't get interface " + ifname->getValue());
-        return (Pkt6Ptr());
+        isc_throw(Dhcp4o6IpcError, "option " << ISC_V6_4O6_INTERFACE
+                  << " sent in the DHCP4o6 message contains non-existing"
+                  " interface name '" << ifname->getValue() << "'");
     }
     }
-    Option6AddrLstPtr srcs =
+
-        boost::dynamic_pointer_cast<Option6AddrLst>(vendor->getOption(ISC_V6_4O6_SRC_ADDRESS));
+    // Get the option holding source IPv6 address.
+    OptionCustomPtr srcs = boost::dynamic_pointer_cast<
+        OptionCustom>(option_vendor->getOption(ISC_V6_4O6_SRC_ADDRESS));
     if (!srcs) {
     if (!srcs) {
-        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+        LOG_WARN(dhcpsrv_logger, DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
-                  DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
             .arg("no source address suboption");
             .arg("no source address suboption");
-        return (Pkt6Ptr());
+        isc_throw(Dhcp4o6IpcError, "option " << D6O_VENDOR_OPTS
-    }
+                  << " doesn't contain the " << ISC_V6_4O6_SRC_ADDRESS
-    Option6AddrLst::AddressContainer addrs = srcs->getAddresses();
+                  << " option required in the DHCP4o6 message sent"
-    if (addrs.size() != 1) {
+                  " between Kea servers");
-        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-                  DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
-            .arg("bad source address suboption");
-        return (Pkt6Ptr());
     }
     }
 
 
-    // Update the packet and return it
+    // Update the packet.
-    static_cast<void>(pkt->delOption(D6O_VENDOR_OPTS));
+    pkt->setRemoteAddr(srcs->readAddress());
-    pkt->setRemoteAddr(addrs[0]);
     pkt->setIface(iface->getName());
     pkt->setIface(iface->getName());
     pkt->setIndex(iface->getIndex());
     pkt->setIndex(iface->getIndex());
+
+    // Remove options that have been added by the IPC sender.
+    static_cast<void>(option_vendor->delOption(ISC_V6_4O6_INTERFACE));
+    static_cast<void>(option_vendor->delOption(ISC_V6_4O6_SRC_ADDRESS));
+
+    // If there are no more options, the IPC sender has probably created the
+    // vendor option, in which case we should remove it here.
+    if (option_vendor->getOptions().empty()) {
+        static_cast<void>(pkt->delOption(D6O_VENDOR_OPTS));
+    }
+
     return (pkt);
     return (pkt);
 }
 }
 
 
-void Dhcp4o6IpcBase::send(Pkt6Ptr pkt) {
+void Dhcp4o6IpcBase::send(const Pkt6Ptr& pkt) {
-    // No packet: nothing to send
+    // This shouldn't happen, i.e. send() shouldn't be called if there is
+    // no message.
     if (!pkt) {
     if (!pkt) {
-        return;
+        isc_throw(Dhcp4o6IpcError, "DHCP4o6 message must not be NULL while"
+                  " trying to send it over the IPC");
     }
     }
 
 
     // Disabled: nowhere to send
     // Disabled: nowhere to send
     if (socket_fd_ == -1) {
     if (socket_fd_ == -1) {
-        return;
+        isc_throw(Dhcp4o6IpcError, "unable to send DHCP4o6 message because"
+                  " IPC socket is closed");
     }
     }
 
 
     // Check if vendor option exists.
     // Check if vendor option exists.
-    OptionVendorPtr vendor_opt = boost::dynamic_pointer_cast<
+    OptionVendorPtr option_vendor = boost::dynamic_pointer_cast<
         OptionVendor>(pkt->getOption(D6O_VENDOR_OPTS));
         OptionVendor>(pkt->getOption(D6O_VENDOR_OPTS));
 
 
     // If vendor option doesn't exist or its enterprise id is not ISC's
     // If vendor option doesn't exist or its enterprise id is not ISC's
     // enterprise id, let's create it.
     // enterprise id, let's create it.
-    if (!vendor_opt || (vendor_opt->getVendorId() != ENTERPRISE_ID_ISC)) {
+    if (!option_vendor ||
-        vendor_opt.reset(new OptionVendor(Option::V6, ENTERPRISE_ID_ISC));
+        (option_vendor->getVendorId() != ENTERPRISE_ID_ISC)) {
+        option_vendor.reset(new OptionVendor(Option::V6, ENTERPRISE_ID_ISC));
+        pkt->addOption(option_vendor);
 
 
     }
     }
 
 
     // Push interface name and source address in it
     // Push interface name and source address in it
-    vendor_opt->addOption(OptionPtr(new OptionString(Option::V6,
+    option_vendor->addOption(OptionStringPtr(new OptionString(Option::V6,
-                                                     ISC_V6_4O6_INTERFACE,
+                                                       ISC_V6_4O6_INTERFACE,
-                                                     pkt->getIface())));
+                                                       pkt->getIface())));
-    vendor_opt->addOption(OptionPtr(new Option6AddrLst(ISC_V6_4O6_SRC_ADDRESS,
+    option_vendor->addOption(Option6AddrLstPtr(new Option6AddrLst(
+                                                       ISC_V6_4O6_SRC_ADDRESS,
                                                        pkt->getRemoteAddr())));
                                                        pkt->getRemoteAddr())));
-    pkt->addOption(vendor_opt);
-
     // Get packet content
     // Get packet content
     OutputBuffer& buf = pkt->getBuffer();
     OutputBuffer& buf = pkt->getBuffer();
     buf.clear();
     buf.clear();
     pkt->pack();
     pkt->pack();
 
 
-    // Send
+    // Try to send the message.
-    if (::send(socket_fd_, buf.getData(), buf.getLength(), 0) < 0) {
+   if (::send(socket_fd_, buf.getData(), buf.getLength(), 0) < 0) {
-        isc_throw(Unexpected, "Failed to send over DHCP4o6 IPC socket");
+       isc_throw(Dhcp4o6IpcError,
-    }
+                 "failed to send DHCP4o6 message over the IPC: "
-    return;
+                 << strerror(errno));
+   }
 }
 }
 
 
 };  // namespace dhcp
 };  // namespace dhcp

+ 78 - 28
src/lib/dhcpsrv/dhcp4o6_ipc.h

@@ -1,16 +1,8 @@
 // Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 // Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
-// Permission to use, copy, modify, and/or distribute this software for any
+// This Source Code Form is subject to the terms of the Mozilla Public
-// purpose with or without fee is hereby granted, provided that the above
+// License, v. 2.0. If a copy of the MPL was not distributed with this
-// copyright notice and this permission notice appear in all copies.
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-//
-// 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 DHCP4O6_IPC_H
 #ifndef DHCP4O6_IPC_H
 #define DHCP4O6_IPC_H
 #define DHCP4O6_IPC_H
@@ -19,18 +11,62 @@
 /// This file defines the class Kea uses as a base for
 /// This file defines the class Kea uses as a base for
 /// DHCPv4-over-DHCPv6 communication between servers.
 /// DHCPv4-over-DHCPv6 communication between servers.
 ///
 ///
-#include <dhcp/pkt6.h>
 
 
+#include <exceptions/exceptions.h>
+#include <dhcp/pkt6.h>
 #include <boost/noncopyable.hpp>
 #include <boost/noncopyable.hpp>
-
 #include <stdint.h>
 #include <stdint.h>
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
-/// @brief 
+/// @brief Exception thrown when error occurs as a result of use of IPC.
+class Dhcp4o6IpcError : public Exception {
+public:
+    Dhcp4o6IpcError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief This class implements the communication between the DHCPv4
+/// and DHCPv6 servers to allow for transmission of the DHCPv4 query
+/// and DHCPv4 response messages.
+///
+/// When the DHCPv6 server receives the DHCPv4 query message it needs
+/// to forward it to the DHCPv4 server for processing. The DHCPv4
+/// server processes the message and answers with the DHCPv4 response
+/// message to the DHCPv6 server. The server forwards it back to the
+/// client. This class implements the communication between the DHCPv4
+/// and DHCPv6 servers to allow for transmission of the DHCPv4 query
+/// and DHCPv6 response messages.
 ///
 ///
+/// This class creates a socket (when @c open is called) and binds it
+/// to a port, depending on the configuration. The port number is
+/// explicitly specified in the server configuration. This explicit
+/// port value is used directly on the DHCPv6 server side. The DHCPv4
+/// server uses the port specified + 1.
+///
+/// The DHCPv4 and DHCPv6 servers use distinct instances of classes derived
+/// from this base class. Each of these instances is used to send and
+/// receive messages sent by the other server.
+///
+/// In order to make address allocation decisions, the DHCPv4 server
+/// requires information about the interface and the source address of
+/// the original DHCPv4 query message sent by the client. This
+/// information is known by the DHCPv6 server and needs to be conveyed
+/// to the DHCPv4 server. The IPC conveys it in the
+/// @c ISC_V6_4O6_INTERFACE and @c ISC_V6_4O6_SRC_ADDRESS options
+/// within the Vendor Specific Information option, with ISC
+/// enterprise id. These options are added by the IPC sender and removed
+/// by the IPC receiver.
 class Dhcp4o6IpcBase : public boost::noncopyable {
 class Dhcp4o6IpcBase : public boost::noncopyable {
+public:
+
+    /// @brief Endpoint type: DHCPv4 or DHCPv6 server.
+    enum EndpointType {
+        ENDPOINT_TYPE_V4 = 4,
+        ENDPOINT_TYPE_V6 = 6
+    };
+
 protected:
 protected:
     /// @brief Constructor
     /// @brief Constructor
     ///
     ///
@@ -40,38 +76,52 @@ protected:
     /// @brief Destructor.
     /// @brief Destructor.
     virtual ~Dhcp4o6IpcBase();
     virtual ~Dhcp4o6IpcBase();
 
 
-    /// @brief Open communication socket (from base class)
+    /// @brief Open communication socket (from base class).
     ///
     ///
-    /// @param port port number to use (0 for disabled)
+    /// @param port Port number to use. The socket is bound to this port
-    /// @param side side of the server (4 or 6)
+    /// if the endpoint type is DHCPv6 server, otherwise the port + 1
+    /// value is used.
+    /// @param endpoint_type Endpoint type (DHCPv4 or DHCPv6 server).
     ///
     ///
-    /// @return new socket descriptor
+    /// @return New socket descriptor.
-    int open(uint16_t port, int side);
+    /// @throw isc::dhcp::Dhcp4o6IpcError on system call errors.
+    int open(uint16_t port, EndpointType endpoint_type);
 
 
 public:
 public:
-    /// @brief Open communication socket (for derived classes)
+
+    /// @brief Open communication socket (for derived classes).
     virtual void open() = 0;
     virtual void open() = 0;
 
 
-    /// @brief Close communication socket
+    /// @brief Close communication socket.
     void close();
     void close();
 
 
-    /// @brief Receive IPC message
+    /// @brief Receive message over IPC.
     ///
     ///
     /// @return a pointer to a DHCPv6 message with interface and remote
     /// @return a pointer to a DHCPv6 message with interface and remote
     /// address set from the IPC message
     /// address set from the IPC message
+    /// @throw isc::dhcp::Dhcp4o6IpcError on system call error or
+    /// malformed packets.
     Pkt6Ptr receive();
     Pkt6Ptr receive();
 
 
-    /// @brief Send IPC message
+    /// @brief Send message over IPC.
     ///
     ///
-    /// @param a pointer to a DHCPv6 message with interface and remote
+    /// The IPC uses @c ISC_V6_4O6_INTERFACE and @c ISC_V6_4O6_SRC_ADDRESS
-    /// address set for the IPC message
+    /// options conveyed within the Vendor Specific Information option, with
-    void send(Pkt6Ptr pkt);
+    /// ISC enterprise id, to communicate the client remote address and the
+    /// interface on which the DHCPv4 query was received. These options will
+    /// be removed by the receiver.
+    ///
+    /// @param pkt Pointer to a DHCPv6 message with interface and remote
+    /// address.
+    /// @throw isc::dhcp::Dhcp4o6IpcError.
+    void send(const Pkt6Ptr& pkt);
 
 
 protected:
 protected:
-    /// @brief Port number
+
+    /// @brief Port number configured for IPC communication.
     uint16_t port_;
     uint16_t port_;
 
 
-    /// @brief Socket descriptor
+    /// @brief Socket descriptor.
     int socket_fd_;
     int socket_fd_;
 };
 };
 
 

+ 8 - 0
src/lib/dhcpsrv/pgsql_lease_mgr.cc

@@ -706,6 +706,14 @@ PgSqlLeaseMgr::PgSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
         isc_throw(DbOpenError, "Number of statements prepared: " << i
         isc_throw(DbOpenError, "Number of statements prepared: " << i
                   << " does not match expected count:" << NUM_STATEMENTS);
                   << " does not match expected count:" << NUM_STATEMENTS);
     }
     }
+
+    pair<uint32_t, uint32_t> code_version(PG_CURRENT_VERSION, PG_CURRENT_MINOR);
+    pair<uint32_t, uint32_t> db_version = getVersion();
+    if (code_version != db_version) {
+        isc_throw(DbOpenError, "Posgresql schema version mismatch: need version: "
+                  << code_version.first << "." << code_version.second
+                  << " found version:  " << db_version.first << "." << db_version.second);
+    }
 }
 }
 
 
 PgSqlLeaseMgr::~PgSqlLeaseMgr() {
 PgSqlLeaseMgr::~PgSqlLeaseMgr() {

+ 1 - 1
src/lib/dhcpsrv/pgsql_lease_mgr.h

@@ -26,7 +26,7 @@ class PgSqlLease4Exchange;
 class PgSqlLease6Exchange;
 class PgSqlLease6Exchange;
 
 
 /// Defines PostgreSQL backend version: 2.0
 /// Defines PostgreSQL backend version: 2.0
-const uint32_t PG_CURRENT_VERSION = 2;
+const uint32_t PG_CURRENT_VERSION = 3;
 const uint32_t PG_CURRENT_MINOR = 0;
 const uint32_t PG_CURRENT_MINOR = 0;
 
 
 /// @brief PostgreSQL Lease Manager
 /// @brief PostgreSQL Lease Manager

+ 1 - 0
src/lib/dhcpsrv/tests/Makefile.am

@@ -92,6 +92,7 @@ libdhcpsrv_unittests_SOURCES += d2_udp_unittest.cc
 libdhcpsrv_unittests_SOURCES += daemon_unittest.cc
 libdhcpsrv_unittests_SOURCES += daemon_unittest.cc
 libdhcpsrv_unittests_SOURCES += database_connection_unittest.cc
 libdhcpsrv_unittests_SOURCES += database_connection_unittest.cc
 libdhcpsrv_unittests_SOURCES += dbaccess_parser_unittest.cc
 libdhcpsrv_unittests_SOURCES += dbaccess_parser_unittest.cc
+libdhcpsrv_unittests_SOURCES += dhcp4o6_ipc_unittest.cc
 libdhcpsrv_unittests_SOURCES += duid_config_parser_unittest.cc
 libdhcpsrv_unittests_SOURCES += duid_config_parser_unittest.cc
 libdhcpsrv_unittests_SOURCES += expiration_config_parser_unittest.cc
 libdhcpsrv_unittests_SOURCES += expiration_config_parser_unittest.cc
 libdhcpsrv_unittests_SOURCES += host_mgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += host_mgr_unittest.cc

+ 36 - 65
src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc

@@ -1,16 +1,8 @@
 // Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 // Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
-// Permission to use, copy, modify, and/or distribute this software for any
+// This Source Code Form is subject to the terms of the Mozilla Public
-// purpose with or without fee is hereby granted, provided that the above
+// License, v. 2.0. If a copy of the MPL was not distributed with this
-// copyright notice and this permission notice appear in all copies.
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <config.h>
 #include <config.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/iface_mgr.h>
@@ -42,7 +34,7 @@ const uint16_t TEST_PORT = 12345;
 const uint16_t TEST_ITERATIONS = 10;
 const uint16_t TEST_ITERATIONS = 10;
 
 
 /// @brief Type definition for the function creating DHCP message.
 /// @brief Type definition for the function creating DHCP message.
-typedef boost::function<Pkt6Ptr(const uint16_t, const uint16_t)> CreateMsgFun;
+typedef boost::function<Pkt6Ptr(uint16_t, uint16_t)> CreateMsgFun;
 
 
 /// @brief Define short name for test IPC class.
 /// @brief Define short name for test IPC class.
 typedef Dhcp4o6TestIpc TestIpc;
 typedef Dhcp4o6TestIpc TestIpc;
@@ -65,7 +57,7 @@ protected:
     /// @param prefix Prefix.
     /// @param prefix Prefix.
     /// @param postfix Postfix.
     /// @param postfix Postfix.
     /// @return String representing concatenated prefix and postfix.
     /// @return String representing concatenated prefix and postfix.
-    static std::string concatenate(const std::string& prefix, const uint16_t postfix);
+    static std::string concatenate(const std::string& prefix, uint16_t postfix);
 
 
     /// @brief Creates an instance of the DHCPv4o6 message.
     /// @brief Creates an instance of the DHCPv4o6 message.
     ////
     ////
@@ -77,19 +69,20 @@ protected:
     /// the "eth0" will be used, for odd postfix values "eth1" will be used.
     /// the "eth0" will be used, for odd postfix values "eth1" will be used.
     ///
     ///
     /// @return Pointer to the created message.
     /// @return Pointer to the created message.
-    static Pkt6Ptr createDHCPv4o6Message(const uint16_t msg_type,
+    static Pkt6Ptr createDHCPv4o6Message(uint16_t msg_type,
-                                         const uint16_t postfix = 0);
+                                         uint16_t postfix = 0);
 
 
     /// @brief Creates an instance of the DHCPv4o6 message with vendor option.
     /// @brief Creates an instance of the DHCPv4o6 message with vendor option.
     ///
     ///
     /// @param msg_type Message type.
     /// @param msg_type Message type.
     /// @param postfix Postfix to be appended to the remote address. See the
     /// @param postfix Postfix to be appended to the remote address. See the
     /// documentation of @c createDHCPv4o6Message for details.
     /// documentation of @c createDHCPv4o6Message for details.
+    /// @param enterprise_id Enterprise ID for the vendor option.
     ///
     ///
     /// @return Pointer to the created message.
     /// @return Pointer to the created message.
-    static Pkt6Ptr createDHCPv4o6MsgWithVendorOption(const uint16_t msg_type,
+    static Pkt6Ptr createDHCPv4o6MsgWithVendorOption(uint16_t msg_type,
-                                                     const uint16_t postfix,
+                                                     uint16_t postfix,
-                                                     const uint32_t enterprise_id);
+                                                     uint32_t enterprise_id);
 
 
     /// @brief Creates an instance of the DHCPv4o6 message with ISC
     /// @brief Creates an instance of the DHCPv4o6 message with ISC
     /// vendor option.
     /// vendor option.
@@ -102,8 +95,8 @@ protected:
     /// documentation of @c createDHCPv4o6Message for details.
     /// documentation of @c createDHCPv4o6Message for details.
     ///
     ///
     /// @return Pointer to the created message.
     /// @return Pointer to the created message.
-    static Pkt6Ptr createDHCPv4o6MsgWithISCVendorOption(const uint16_t msg_type,
+    static Pkt6Ptr createDHCPv4o6MsgWithISCVendorOption(uint16_t msg_type,
-                                                        const uint16_t postfix);
+                                                        uint16_t postfix);
 
 
     /// @brief Creates an instance of the DHCPv4o6 message with vendor
     /// @brief Creates an instance of the DHCPv4o6 message with vendor
     /// option holding enterprise id of 32000.
     /// option holding enterprise id of 32000.
@@ -117,23 +110,24 @@ protected:
     /// documentation of @c createDHCPv4o6Message for details.
     /// documentation of @c createDHCPv4o6Message for details.
     ///
     ///
     /// @return Pointer to the created message.
     /// @return Pointer to the created message.
-    static Pkt6Ptr createDHCPv4o6MsgWithAnyVendorOption(const uint16_t msg_type,
+    static Pkt6Ptr createDHCPv4o6MsgWithAnyVendorOption(uint16_t msg_type,
-                                                        const uint16_t postfix);
+                                                        uint16_t postfix);
 
 
     /// @brief Creates an instance of the DHCPv4o6 Message option.
     /// @brief Creates an instance of the DHCPv4o6 Message option.
     ///
     ///
     /// @param src Type of the source endpoint. It can be 4 or 6.
     /// @param src Type of the source endpoint. It can be 4 or 6.
     /// @return Pointer to the instance of the option.
     /// @return Pointer to the instance of the option.
-    static OptionPtr createDHCPv4MsgOption(const TestIpc::EndpointType& src);
+    static OptionPtr createDHCPv4MsgOption(TestIpc::EndpointType src);
 
 
     /// @brief Tests sending and receiving packets over the IPC.
     /// @brief Tests sending and receiving packets over the IPC.
     ///
     ///
     /// @param iterations_num Number of packets to be sent over the IPC.
     /// @param iterations_num Number of packets to be sent over the IPC.
     /// @param src Type of the source IPC endpoint. It can be 4 or 6.
     /// @param src Type of the source IPC endpoint. It can be 4 or 6.
     /// @param dest Type of the destination IPC endpoint. It can be 4 or 6.
     /// @param dest Type of the destination IPC endpoint. It can be 4 or 6.
-    void testSendReceive(const uint16_t iterations_num,
+    /// @param create_msg_fun Function called to create the packet.
-                         const TestIpc::EndpointType& src,
+    void testSendReceive(uint16_t iterations_num,
-                         const TestIpc::EndpointType& dest,
+                         TestIpc::EndpointType src,
+                         TestIpc::EndpointType dest,
                          const CreateMsgFun& create_msg_fun);
                          const CreateMsgFun& create_msg_fun);
 
 
     /// @brief Tests that error is reported when invalid message is received.
     /// @brief Tests that error is reported when invalid message is received.
@@ -154,15 +148,15 @@ Dhcp4o6IpcBaseTest::Dhcp4o6IpcBaseTest()
 
 
 std::string
 std::string
 Dhcp4o6IpcBaseTest::concatenate(const std::string& prefix,
 Dhcp4o6IpcBaseTest::concatenate(const std::string& prefix,
-                                const uint16_t postfix) {
+                                uint16_t postfix) {
     std::ostringstream s;
     std::ostringstream s;
     s << prefix << postfix;
     s << prefix << postfix;
     return (s.str());
     return (s.str());
 }
 }
 
 
 Pkt6Ptr
 Pkt6Ptr
-Dhcp4o6IpcBaseTest::createDHCPv4o6Message(const uint16_t msg_type,
+Dhcp4o6IpcBaseTest::createDHCPv4o6Message(uint16_t msg_type,
-                                          const uint16_t postfix) {
+                                          uint16_t postfix) {
     // Create the DHCPv4o6 message.
     // Create the DHCPv4o6 message.
     Pkt6Ptr pkt(new Pkt6(msg_type, 0));
     Pkt6Ptr pkt(new Pkt6(msg_type, 0));
 
 
@@ -180,7 +174,7 @@ Dhcp4o6IpcBaseTest::createDHCPv4o6Message(const uint16_t msg_type,
     pkt->setRemoteAddr(IOAddress(concatenate("2001:db8:1::", postfix)));
     pkt->setRemoteAddr(IOAddress(concatenate("2001:db8:1::", postfix)));
 
 
     // Determine the endpoint type using the message type.
     // Determine the endpoint type using the message type.
-    const TestIpc::EndpointType src = (msg_type ==  DHCPV6_DHCPV4_QUERY) ?
+    TestIpc::EndpointType src = (msg_type ==  DHCPV6_DHCPV4_QUERY) ?
         TestIpc::ENDPOINT_TYPE_V6 : TestIpc::ENDPOINT_TYPE_V4;
         TestIpc::ENDPOINT_TYPE_V6 : TestIpc::ENDPOINT_TYPE_V4;
 
 
     // Add DHCPv4 Message option to make sure it is conveyed by the IPC.
     // Add DHCPv4 Message option to make sure it is conveyed by the IPC.
@@ -190,9 +184,9 @@ Dhcp4o6IpcBaseTest::createDHCPv4o6Message(const uint16_t msg_type,
 }
 }
 
 
 Pkt6Ptr
 Pkt6Ptr
-Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithVendorOption(const uint16_t msg_type,
+Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithVendorOption(uint16_t msg_type,
-                                                      const uint16_t postfix,
+                                                      uint16_t postfix,
-                                                      const uint32_t enterprise_id) {
+                                                      uint32_t enterprise_id) {
     Pkt6Ptr pkt = createDHCPv4o6Message(msg_type, postfix);
     Pkt6Ptr pkt = createDHCPv4o6Message(msg_type, postfix);
 
 
     // Create vendor option with ISC enterprise id.
     // Create vendor option with ISC enterprise id.
@@ -208,19 +202,19 @@ Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithVendorOption(const uint16_t msg_type,
 }
 }
 
 
 Pkt6Ptr
 Pkt6Ptr
-Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithISCVendorOption(const uint16_t msg_type,
+Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithISCVendorOption(uint16_t msg_type,
-                                                         const uint16_t postfix) {
+                                                         uint16_t postfix) {
     return (createDHCPv4o6MsgWithVendorOption(msg_type, postfix, ENTERPRISE_ID_ISC));
     return (createDHCPv4o6MsgWithVendorOption(msg_type, postfix, ENTERPRISE_ID_ISC));
 }
 }
 
 
 Pkt6Ptr
 Pkt6Ptr
-Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithAnyVendorOption(const uint16_t msg_type,
+Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithAnyVendorOption(uint16_t msg_type,
-                                                         const uint16_t postfix) {
+                                                         uint16_t postfix) {
     return (createDHCPv4o6MsgWithVendorOption(msg_type, postfix, 32000));
     return (createDHCPv4o6MsgWithVendorOption(msg_type, postfix, 32000));
 }
 }
 
 
 OptionPtr
 OptionPtr
-Dhcp4o6IpcBaseTest::createDHCPv4MsgOption(const TestIpc::EndpointType& src) {
+Dhcp4o6IpcBaseTest::createDHCPv4MsgOption(TestIpc::EndpointType src) {
     // Create the DHCPv4 message.
     // Create the DHCPv4 message.
     Pkt4Ptr pkt(new Pkt4(src == TestIpc::ENDPOINT_TYPE_V4 ? DHCPACK : DHCPREQUEST,
     Pkt4Ptr pkt(new Pkt4(src == TestIpc::ENDPOINT_TYPE_V4 ? DHCPACK : DHCPREQUEST,
                          1234));
                          1234));
@@ -236,9 +230,9 @@ Dhcp4o6IpcBaseTest::createDHCPv4MsgOption(const TestIpc::EndpointType& src) {
 }
 }
 
 
 void
 void
-Dhcp4o6IpcBaseTest::testSendReceive(const uint16_t iterations_num,
+Dhcp4o6IpcBaseTest::testSendReceive(uint16_t iterations_num,
-                                    const TestIpc::EndpointType& src,
+                                    TestIpc::EndpointType src,
-                                    const TestIpc::EndpointType& dest,
+                                    TestIpc::EndpointType dest,
                                     const CreateMsgFun& create_msg_fun) {
                                     const CreateMsgFun& create_msg_fun) {
     // Create IPC instances representing the source and destination endpoints.
     // Create IPC instances representing the source and destination endpoints.
     TestIpc ipc_src(TEST_PORT, src);
     TestIpc ipc_src(TEST_PORT, src);
@@ -248,7 +242,7 @@ Dhcp4o6IpcBaseTest::testSendReceive(const uint16_t iterations_num,
     ASSERT_NO_THROW(ipc_src.open());
     ASSERT_NO_THROW(ipc_src.open());
     ASSERT_NO_THROW(ipc_dest.open());
     ASSERT_NO_THROW(ipc_dest.open());
 
 
-    // Depnding if we're sending from DHCPv6 to DHCPv4 or the opposite
+    // Depending if we're sending from DHCPv6 to DHCPv4 or the opposite
     // direction we use different message type. This is not really required
     // direction we use different message type. This is not really required
     // for testing IPC, but it better simulates the real use case.
     // for testing IPC, but it better simulates the real use case.
     uint16_t msg_type = (src == TestIpc::ENDPOINT_TYPE_V6 ? DHCPV6_DHCPV4_QUERY :
     uint16_t msg_type = (src == TestIpc::ENDPOINT_TYPE_V6 ? DHCPV6_DHCPV4_QUERY :
@@ -437,40 +431,17 @@ TEST_F(Dhcp4o6IpcBaseTest, openError) {
     ASSERT_NE(-1, ipc_bound.getSocketFd());
     ASSERT_NE(-1, ipc_bound.getSocketFd());
 
 
     ipc.setDesiredPort(TEST_PORT + 10);
     ipc.setDesiredPort(TEST_PORT + 10);
-#if defined(OS_LINUX)
-    // Linux has a silly interpretation of SO_REUSEADDR so we use
-    // another way to raise an error.
-    ipc.setDesiredPort(65535);
-#endif
     ASSERT_THROW(ipc.open(), isc::dhcp::Dhcp4o6IpcError);
     ASSERT_THROW(ipc.open(), isc::dhcp::Dhcp4o6IpcError);
 
 
     EXPECT_EQ(sock_fd, ipc.getSocketFd());
     EXPECT_EQ(sock_fd, ipc.getSocketFd());
     EXPECT_EQ(TEST_PORT, ipc.getPort());
     EXPECT_EQ(TEST_PORT, ipc.getPort());
 
 
     ASSERT_NO_THROW(ipc_bound.close());
     ASSERT_NO_THROW(ipc_bound.close());
-#if defined(OS_LINUX)
-    // Restore the expected value
-    ipc.setDesiredPort(TEST_PORT + 10);
-#endif
     ASSERT_NO_THROW(ipc.open());
     ASSERT_NO_THROW(ipc.open());
     EXPECT_NE(-1, ipc.getSocketFd());
     EXPECT_NE(-1, ipc.getSocketFd());
     EXPECT_EQ(TEST_PORT + 10, ipc.getPort());
     EXPECT_EQ(TEST_PORT + 10, ipc.getPort());
 }
 }
 
 
-// This test verifies that the IPC returns an error when trying to bind
-// to the out of range port.
-TEST_F(Dhcp4o6IpcBaseTest, invalidPortError4) {
-    TestIpc ipc(65535, TestIpc::ENDPOINT_TYPE_V4);
-    EXPECT_THROW(ipc.open(), Dhcp4o6IpcError);
-}
-
-// This test verifies that the IPC returns an error when trying to bind
-// to the out of range port.
-TEST_F(Dhcp4o6IpcBaseTest, invalidPortError6) {
-    TestIpc ipc(65535, TestIpc::ENDPOINT_TYPE_V6);
-    EXPECT_THROW(ipc.open(), Dhcp4o6IpcError);
-}
-
 // This test verifies that receiving packet over the IPC fails when there
 // This test verifies that receiving packet over the IPC fails when there
 // is no vendor option present.
 // is no vendor option present.
 TEST_F(Dhcp4o6IpcBaseTest, receiveWithoutVendorOption) {
 TEST_F(Dhcp4o6IpcBaseTest, receiveWithoutVendorOption) {

+ 2 - 1
src/lib/dhcpsrv/testutils/Makefile.am

@@ -12,7 +12,8 @@ if HAVE_GTEST
 
 
 noinst_LTLIBRARIES = libdhcpsrvtest.la
 noinst_LTLIBRARIES = libdhcpsrvtest.la
 
 
-libdhcpsrvtest_la_SOURCES = config_result_check.cc config_result_check.h
+libdhcpsrvtest_la_SOURCES  = config_result_check.cc config_result_check.h
+libdhcpsrvtest_la_SOURCES += dhcp4o6_test_ipc.cc dhcp4o6_test_ipc.h
 
 
 if HAVE_MYSQL
 if HAVE_MYSQL
 libdhcpsrvtest_la_SOURCES += schema.cc schema.h
 libdhcpsrvtest_la_SOURCES += schema.cc schema.h

+ 41 - 0
src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.cc

@@ -0,0 +1,41 @@
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcpsrv/testutils/dhcp4o6_test_ipc.h>
+#include <boost/bind.hpp>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+Dhcp4o6TestIpc::Dhcp4o6TestIpc(uint16_t port, EndpointType endpoint_type)
+    : desired_port_(port), endpoint_type_(endpoint_type), pkt_received_() {
+}
+
+void
+Dhcp4o6TestIpc::open() {
+    // Use the base IPC to open the socket.
+    socket_fd_ = Dhcp4o6IpcBase::open(desired_port_, endpoint_type_);
+    // If the socket has been opened correctly, register it in the @c IfaceMgr.
+    // BTW if it has not an exception has been thrown so it is only
+    // a sanity / recommended check.
+    if (socket_fd_ != -1) {
+        IfaceMgr& iface_mgr = IfaceMgr::instance();
+        iface_mgr.addExternalSocket(socket_fd_,
+                boost::bind(&Dhcp4o6TestIpc::receiveHandler, this));
+    }
+}
+
+void
+Dhcp4o6TestIpc::receiveHandler() {
+    pkt_received_ = receive();
+}
+
+} // end of isc::dhcp::test namespace
+} // end of isc::dhcp namespace
+} // end of isc namespace

+ 90 - 0
src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.h

@@ -0,0 +1,90 @@
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef DHCP4O6_TEST_IPC_H
+#define DHCP4O6_TEST_IPC_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/pkt6.h>
+#include <dhcpsrv/dhcp4o6_ipc.h>
+#include <boost/noncopyable.hpp>
+#include <stdint.h>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @brief Implements a simple IPC for the test.
+class Dhcp4o6TestIpc : public  Dhcp4o6IpcBase {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// @param port Desired port.
+    /// @param endpoint_type Type of the IPC endpoint. It should be 4 or 6.
+    Dhcp4o6TestIpc(uint16_t port, EndpointType endpoint_type);
+
+    /// @brief Sets new port to be used with @c open.
+    ///
+    /// @param desired_port New desired port.
+    void setDesiredPort(uint16_t desired_port) {
+        desired_port_ = desired_port;
+    }
+
+    /// @brief Opens the IPC socket and registers it in @c IfaceMgr.
+    ///
+    /// This method opens the IPC socket and registers it as external
+    /// socket in the IfaceMgr. The @c TestIpc::receiveHandler is used as a
+    /// callback to be called by the @c IfaceMgr when the data is received
+    /// over the socket.
+    virtual void open();
+
+    /// @brief Retrieve port which socket is bound to.
+    uint16_t getPort() const {
+        return (port_);
+    }
+
+    /// @brief Retrieve socket descriptor.
+    int getSocketFd() const {
+        return (socket_fd_);
+    }
+
+    /// @brief Pops and returns a received message.
+    ///
+    /// @return Pointer to the received message over the IPC.
+    Pkt6Ptr popPktReceived() {
+        // Copy the received message.
+        Pkt6Ptr pkt_copy(pkt_received_);
+        // Set the received message to NULL (pop).
+        pkt_received_.reset();
+        // Return the copy.
+        return (pkt_copy);
+    }
+
+private:
+
+    /// @brief Callback for the IPC socket.
+    ///
+    /// This callback is called by the @c IfaceMgr when the data is received
+    /// over the IPC socket.
+    void receiveHandler();
+
+    /// @brief Port number.
+    uint16_t desired_port_;
+
+    /// @brief Endpoint type, i.e. 4 or 6.
+    EndpointType endpoint_type_;
+
+    /// @brief Pointer to the last received message.
+    Pkt6Ptr pkt_received_;
+};
+
+}; // end of isc::dhcp::test namespace
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // DHCP4O6_TEST_IPC_H

+ 1 - 0
src/share/database/scripts/pgsql/.gitignore

@@ -1 +1,2 @@
 upgrade_1.0_to_2.0.sh
 upgrade_1.0_to_2.0.sh
+upgrade_2.0_to_3.0.sh

+ 1 - 0
src/share/database/scripts/pgsql/Makefile.am

@@ -4,5 +4,6 @@ sqlscriptsdir = ${datarootdir}/${PACKAGE_NAME}/scripts/pgsql
 sqlscripts_DATA = dhcpdb_create.pgsql
 sqlscripts_DATA = dhcpdb_create.pgsql
 sqlscripts_DATA += dhcpdb_drop.pgsql
 sqlscripts_DATA += dhcpdb_drop.pgsql
 sqlscripts_DATA += upgrade_1.0_to_2.0.sh
 sqlscripts_DATA += upgrade_1.0_to_2.0.sh
+sqlscripts_DATA += upgrade_2.0_to_3.0.sh
 
 
 EXTRA_DIST = ${sqlscripts_DATA}
 EXTRA_DIST = ${sqlscripts_DATA}

+ 260 - 8
src/share/database/scripts/pgsql/dhcpdb_create.pgsql

@@ -19,6 +19,8 @@
 
 
 -- @dhcpdb_create.pgsql
 -- @dhcpdb_create.pgsql
 
 
+-- Start a single transaction for the Entire script
+START TRANSACTION;
 
 
 -- Holds the IPv4 leases.
 -- Holds the IPv4 leases.
 CREATE TABLE lease4 (
 CREATE TABLE lease4 (
@@ -71,11 +73,10 @@ CREATE TABLE lease6_types (
     lease_type SMALLINT PRIMARY KEY NOT NULL,   -- Lease type code.
     lease_type SMALLINT PRIMARY KEY NOT NULL,   -- Lease type code.
     name VARCHAR(5)                             -- Name of the lease type
     name VARCHAR(5)                             -- Name of the lease type
     );
     );
-START TRANSACTION;
+
 INSERT INTO lease6_types VALUES (0, 'IA_NA');   -- Non-temporary v6 addresses
 INSERT INTO lease6_types VALUES (0, 'IA_NA');   -- Non-temporary v6 addresses
 INSERT INTO lease6_types VALUES (1, 'IA_TA');   -- Temporary v6 addresses
 INSERT INTO lease6_types VALUES (1, 'IA_TA');   -- Temporary v6 addresses
 INSERT INTO lease6_types VALUES (2, 'IA_PD');   -- Prefix delegations
 INSERT INTO lease6_types VALUES (2, 'IA_PD');   -- Prefix delegations
-COMMIT;
 
 
 -- Finally, the version of the schema.  We start at 0.1 during development.
 -- Finally, the version of the schema.  We start at 0.1 during development.
 -- This table is only modified during schema upgrades.  For historical reasons
 -- This table is only modified during schema upgrades.  For historical reasons
@@ -90,9 +91,8 @@ CREATE TABLE schema_version (
     version INT PRIMARY KEY NOT NULL,       -- Major version number
     version INT PRIMARY KEY NOT NULL,       -- Major version number
     minor INT                               -- Minor version number
     minor INT                               -- Minor version number
     );
     );
-START TRANSACTION;
+
 INSERT INTO schema_version VALUES (1, 0);
 INSERT INTO schema_version VALUES (1, 0);
-COMMIT;
 
 
 --
 --
 -- Schema 2.0 specification starts here.
 -- Schema 2.0 specification starts here.
@@ -121,11 +121,9 @@ CREATE TABLE lease_state (
     name VARCHAR(64) NOT NULL);
     name VARCHAR(64) NOT NULL);
 
 
 -- Insert currently defined state names.
 -- Insert currently defined state names.
-START TRANSACTION;
 INSERT INTO lease_state VALUES (0, 'default');
 INSERT INTO lease_state VALUES (0, 'default');
 INSERT INTO lease_state VALUES (1, 'declined');
 INSERT INTO lease_state VALUES (1, 'declined');
 INSERT INTO lease_state VALUES (2, 'expired-reclaimed');
 INSERT INTO lease_state VALUES (2, 'expired-reclaimed');
-COMMIT;
 
 
 -- Add a constraint that any state value added to the lease4 must
 -- Add a constraint that any state value added to the lease4 must
 -- map to a value in the lease_state table.
 -- map to a value in the lease_state table.
@@ -146,7 +144,7 @@ ALTER TABLE lease6
     REFERENCES lease6_types (lease_type);
     REFERENCES lease6_types (lease_type);
 
 
 --
 --
+--  FUNCTION that returns a result set containing the column names for lease4 dumps.
 DROP FUNCTION IF EXISTS lease4DumpHeader();
 DROP FUNCTION IF EXISTS lease4DumpHeader();
 CREATE FUNCTION lease4DumpHeader() RETURNS text AS  $$
 CREATE FUNCTION lease4DumpHeader() RETURNS text AS  $$
     select cast('address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state' as text) as result;
     select cast('address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state' as text) as result;
@@ -154,7 +152,7 @@ $$ LANGUAGE SQL;
 --
 --
 
 
 --
 --
+--  FUNCTION that returns a result set containing the data for lease4 dumps.
 DROP FUNCTION IF EXISTS lease4DumpData();
 DROP FUNCTION IF EXISTS lease4DumpData();
 CREATE FUNCTION lease4DumpData() RETURNS
 CREATE FUNCTION lease4DumpData() RETURNS
     table (address inet,
     table (address inet,
@@ -184,7 +182,7 @@ $$ LANGUAGE SQL;
 --
 --
 
 
 --
 --
+--  FUNCTION that returns a result set containing the column names for lease6 dumps.
 DROP FUNCTION IF EXISTS lease6DumpHeader();
 DROP FUNCTION IF EXISTS lease6DumpHeader();
 CREATE FUNCTION lease6DumpHeader() RETURNS text AS  $$
 CREATE FUNCTION lease6DumpHeader() RETURNS text AS  $$
     select cast('address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,state' as text) as result;
     select cast('address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,state' as text) as result;
@@ -192,7 +190,7 @@ $$ LANGUAGE SQL;
 --
 --
 
 
 --
 --
+--  FUNCTION that returns a result set containing the data for lease6 dumps.
 DROP FUNCTION IF EXISTS lease6DumpData();
 DROP FUNCTION IF EXISTS lease6DumpData();
 CREATE FUNCTION lease6DumpData() RETURNS
 CREATE FUNCTION lease6DumpData() RETURNS
     TABLE (
     TABLE (
@@ -230,13 +228,263 @@ $$ LANGUAGE SQL;
 --
 --
 
 
 -- Set 2.0 schema version.
 -- Set 2.0 schema version.
-START TRANSACTION;
 UPDATE schema_version
 UPDATE schema_version
     SET version = '2', minor = '0';
     SET version = '2', minor = '0';
-COMMIT;
 
 
 -- Schema 2.0 specification ends here.
 -- Schema 2.0 specification ends here.
 
 
+-- Upgrade to schema 3.0 begins here:
+
+--
+-- Table structure for table host_identifier_type.
+--
+
+CREATE TABLE host_identifier_type (
+  type SMALLINT PRIMARY KEY NOT NULL,
+  name VARCHAR(32) DEFAULT NULL
+);
+
+INSERT INTO host_identifier_type VALUES (0, 'hw-address');
+INSERT INTO host_identifier_type VALUES (1, 'duid');
+INSERT INTO host_identifier_type VALUES (2, 'circuit-id');
+INSERT INTO host_identifier_type VALUES (3, 'client-id');
+
+--
+-- Table structure for table dhcp_option_scope.
+--
+
+CREATE TABLE dhcp_option_scope (
+  scope_id SMALLINT PRIMARY KEY NOT NULL,
+  scope_name varchar(32) DEFAULT NULL
+);
+
+INSERT INTO dhcp_option_scope VALUES (0, 'global');
+INSERT INTO dhcp_option_scope VALUES (1, 'subnet');
+INSERT INTO dhcp_option_scope VALUES (2, 'client-class');
+INSERT INTO dhcp_option_scope VALUES (3, 'host');
+
+--
+-- Table structure for table hosts.
+--
+-- Primary key and unique contraints automatically create indexes,
+-- foreign key constraints do not.
+CREATE TABLE hosts (
+  host_id SERIAL PRIMARY KEY NOT NULL,
+  dhcp_identifier BYTEA NOT NULL,
+  dhcp_identifier_type SMALLINT NOT NULL,
+  dhcp4_subnet_id INT DEFAULT NULL,
+  dhcp6_subnet_id INT DEFAULT NULL,
+  ipv4_address BIGINT DEFAULT NULL,
+  hostname VARCHAR(255) DEFAULT NULL,
+  dhcp4_client_classes VARCHAR(255) DEFAULT NULL,
+  dhcp6_client_classes VARCHAR(255) DEFAULT NULL,
+  CONSTRAINT key_dhcp4_ipv4_address_subnet_id UNIQUE (ipv4_address, dhcp4_subnet_id),
+  CONSTRAINT key_dhcp4_identifier_subnet_id UNIQUE (dhcp_identifier, dhcp_identifier_type, dhcp4_subnet_id),
+  CONSTRAINT key_dhcp6_identifier_subnet_id UNIQUE (dhcp_identifier, dhcp_identifier_type, dhcp6_subnet_id),
+  CONSTRAINT fk_host_identifier_type FOREIGN KEY (dhcp_identifier_type) REFERENCES host_identifier_type (type)
+  ON DELETE CASCADE
+);
+
+CREATE INDEX fk_host_identifier_type ON hosts (dhcp_identifier_type);
+
+--
+-- Table structure for table dhcp4_options.
+--
+
+CREATE TABLE dhcp4_options (
+  option_id SERIAL PRIMARY KEY NOT NULL,
+  code SMALLINT NOT NULL,
+  value BYTEA,
+  formatted_value TEXT,
+  space VARCHAR(128) DEFAULT NULL,
+  persistent BOOLEAN NOT NULL DEFAULT 'f',
+  dhcp_client_class VARCHAR(128) DEFAULT NULL,
+  dhcp4_subnet_id INT DEFAULT NULL,
+  host_id INT DEFAULT NULL,
+  scope_id SMALLINT NOT NULL,
+  CONSTRAINT fk_options_host1 FOREIGN KEY (host_id) REFERENCES hosts (host_id) ON DELETE CASCADE,
+  CONSTRAINT fk_dhcp4_option_scode FOREIGN KEY (scope_id) REFERENCES dhcp_option_scope (scope_id) ON DELETE CASCADE
+);
+
+CREATE INDEX fk_dhcp4_options_host1_idx ON dhcp4_options (host_id);
+CREATE INDEX fk_dhcp4_options_scope_idx ON dhcp4_options (scope_id);
+
+--
+-- Table structure for table dhcp6_options.
+--
+
+CREATE TABLE dhcp6_options (
+  option_id SERIAL PRIMARY KEY NOT NULL,
+  code INT NOT NULL,
+  value BYTEA,
+  formatted_value TEXT,
+  space VARCHAR(128) DEFAULT NULL,
+  persistent BOOLEAN NOT NULL DEFAULT 'f',
+  dhcp_client_class VARCHAR(128) DEFAULT NULL,
+  dhcp6_subnet_id INT DEFAULT NULL,
+  host_id INT DEFAULT NULL,
+  scope_id SMALLINT NOT NULL,
+  CONSTRAINT fk_options_host10 FOREIGN KEY (host_id) REFERENCES hosts (host_id) ON DELETE CASCADE,
+  CONSTRAINT fk_dhcp6_option_scode FOREIGN KEY (scope_id) REFERENCES dhcp_option_scope (scope_id) ON DELETE CASCADE
+);
+
+CREATE INDEX fk_dhcp6_options_host1_idx ON dhcp6_options (host_id);
+CREATE INDEX fk_dhcp6_options_scope_idx ON dhcp6_options (scope_id);
+
+--
+-- Table structure for table ipv6_reservations.
+--
+
+CREATE TABLE ipv6_reservations (
+  reservation_id SERIAL PRIMARY KEY NOT NULL,
+  address VARCHAR(39) NOT NULL,
+  prefix_len SMALLINT NOT NULL DEFAULT '128',
+  type SMALLINT NOT NULL DEFAULT '0',
+  dhcp6_iaid INT DEFAULT NULL,
+  host_id INT NOT NULL,
+  CONSTRAINT key_dhcp6_address_prefix_len UNIQUE (address, prefix_len),
+  CONSTRAINT fk_ipv6_reservations_host FOREIGN KEY (host_id) REFERENCES hosts (host_id) ON DELETE CASCADE
+);
+
+CREATE INDEX fk_ipv6_reservations_host_idx ON ipv6_reservations (host_id);
+
+--
+-- Table structure for table lease_hwaddr_source.
+--
+
+CREATE TABLE lease_hwaddr_source (
+  hwaddr_source INT PRIMARY KEY NOT NULL,
+  name VARCHAR(40) DEFAULT NULL
+);
+
+-- Hardware address obtained from raw sockets.
+INSERT INTO lease_hwaddr_source VALUES (1, 'HWADDR_SOURCE_RAW');
+
+-- Hardware address converted from IPv6 link-local address with EUI-64.
+INSERT INTO lease_hwaddr_source VALUES (2, 'HWADDR_SOURCE_IPV6_LINK_LOCAL');
+
+-- Hardware address extracted from client-id (duid).
+INSERT INTO lease_hwaddr_source VALUES (4, 'HWADDR_SOURCE_DUID');
+
+-- Hardware address extracted from client address relay option (RFC6939).
+INSERT INTO lease_hwaddr_source VALUES (8, 'HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION');
+
+-- Hardware address extracted from remote-id option (RFC4649).
+INSERT INTO lease_hwaddr_source VALUES (16, 'HWADDR_SOURCE_REMOTE_ID');
+
+-- Hardware address extracted from subscriber-id option (RFC4580).
+INSERT INTO lease_hwaddr_source VALUES (32, 'HWADDR_SOURCE_SUBSCRIBER_ID');
+
+-- Hardware address extracted from docsis options.
+INSERT INTO lease_hwaddr_source VALUES (64, 'HWADDR_SOURCE_DOCSIS_CMTS');
+
+INSERT INTO lease_hwaddr_source VALUES (128, 'HWADDR_SOURCE_DOCSIS_MODEM');
+
+-- In the event hardware address cannot be determined, we need to satisfy
+-- foreign key constraint between lease6 and lease_hardware_source.
+INSERT INTO lease_hwaddr_source VALUES (0, 'HWADDR_SOURCE_UNKNOWN');
+
+-- Adding ORDER BY clause to sort by lease address.
+--
+--  FUNCTION that returns a result set containing the data for lease4 dumps.
+DROP FUNCTION IF EXISTS lease4DumpData();
+CREATE FUNCTION lease4DumpData() RETURNS
+    table (address inet,
+           hwaddr text,
+           client_id text,
+           valid_lifetime bigint,
+           expire timestamp with time zone,
+           subnet_id bigint,
+           fqdn_fwd int,
+           fqdn_rev int,
+           hostname text,
+           state text
+    ) as $$
+    SELECT ('0.0.0.0'::inet + l.address),
+            encode(l.hwaddr,'hex'),
+            encode(l.client_id,'hex'),
+            l.valid_lifetime,
+            l.expire,
+            l.subnet_id,
+            l.fqdn_fwd::int,
+            l.fqdn_rev::int,
+            l.hostname,
+            s.name
+    FROM lease4 l
+         left outer join lease_state s on (l.state = s.state)
+    ORDER BY l.address;
+$$ LANGUAGE SQL;
+--
+
+-- Add new columns to lease6.
+ALTER TABLE lease6
+  ADD COLUMN hwaddr BYTEA DEFAULT NULL,
+  ADD COLUMN hwtype SMALLINT DEFAULT NULL,
+  ADD COLUMN hwaddr_source SMALLINT DEFAULT NULL;
+
+--
+--  FUNCTION that returns a result set containing the column names for lease6 dumps.
+DROP FUNCTION IF EXISTS lease6DumpHeader();
+CREATE FUNCTION lease6DumpHeader() RETURNS text AS  $$
+    select cast('address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,state,hwaddr,hwtype,hwaddr_source' as text) as result;
+$$ LANGUAGE SQL;
+--
+
+--
+--  FUNCTION that returns a result set containing the data for lease6 dumps.
+DROP FUNCTION IF EXISTS lease6DumpData();
+CREATE FUNCTION lease6DumpData() RETURNS
+    TABLE (
+           address text,
+           duid text,
+           valid_lifetime bigint,
+           expire timestamp with time zone,
+           subnet_id bigint,
+           pref_lifetime bigint,
+           name text,
+           iaid integer,
+           prefix_len smallint,
+           fqdn_fwd int,
+           fqdn_rev int,
+           hostname text,
+           state text,
+           hwaddr text,
+           hwtype smallint,
+           hwaddr_source text
+    ) AS $$
+    SELECT (l.address,
+            encode(l.duid,'hex'),
+            l.valid_lifetime,
+            l.expire,
+            l.subnet_id,
+            l.pref_lifetime,
+            t.name,
+            l.iaid,
+            l.prefix_len,
+            l.fqdn_fwd::int,
+            l.fqdn_rev::int,
+            l.hostname,
+            s.name,
+            encode(l.hwaddr,'hex'),
+            l.hwtype,
+            h.name
+     )
+     FROM lease6 l
+         left outer join lease6_types t on (l.lease_type = t.lease_type)
+         left outer join lease_state s on (l.state = s.state)
+         left outer join lease_hwaddr_source h on (l.hwaddr_source = h.hwaddr_source)
+     ORDER BY l.address;
+$$ LANGUAGE SQL;
+
+-- Set 3.0 schema version.
+UPDATE schema_version
+    SET version = '3', minor = '0';
+
+-- Schema 3.0 specification ends here.
+
+-- Commit the script transaction.
+COMMIT;
+
 -- Notes:
 -- Notes:
 
 
 -- Indexes
 -- Indexes

+ 7 - 0
src/share/database/scripts/pgsql/dhcpdb_drop.pgsql

@@ -9,6 +9,13 @@ DROP TABLE IF EXISTS lease6 CASCADE;
 DROP TABLE IF EXISTS lease6_types CASCADE;
 DROP TABLE IF EXISTS lease6_types CASCADE;
 DROP TABLE IF EXISTS schema_version CASCADE;
 DROP TABLE IF EXISTS schema_version CASCADE;
 DROP TABLE IF EXISTS lease_state CASCADE;
 DROP TABLE IF EXISTS lease_state CASCADE;
+DROP TABLE IF EXISTS dhcp4_options CASCADE;
+DROP TABLE IF EXISTS dhcp6_options CASCADE;
+DROP TABLE IF EXISTS ipv6_reservations CASCADE;
+DROP TABLE IF EXISTS lease_hwaddr_source CASCADE;
+DROP TABLE IF EXISTS host_identifier_type CASCADE;
+DROP TABLE IF EXISTS dhcp_option_scope CASCADE;
+DROP TABLE IF EXISTS hosts CASCADE;
 DROP FUNCTION IF EXISTS lease4DumpHeader();
 DROP FUNCTION IF EXISTS lease4DumpHeader();
 DROP FUNCTION IF EXISTS lease4DumpData();
 DROP FUNCTION IF EXISTS lease4DumpData();
 DROP FUNCTION IF EXISTS lease6DumpHeader();
 DROP FUNCTION IF EXISTS lease6DumpHeader();

+ 271 - 0
src/share/database/scripts/pgsql/upgrade_2.0_to_3.0.sh.in

@@ -0,0 +1,271 @@
+#!/bin/sh
+
+# Include utilities. Use installed version if available and
+# use build version if it isn't.
+if [ -e @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh ]; then
+    . @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh
+else
+    . @abs_top_builddir@/src/bin/admin/admin-utils.sh
+fi
+
+VERSION=`pgsql_version "$@"`
+
+if [ "$VERSION" != "2.0" ]; then
+    printf "This script upgrades 2.0 to 3.0. Reported version is $VERSION. Skipping upgrade.\n"
+    exit 0
+fi
+
+psql "$@" >/dev/null <<EOF
+
+START TRANSACTION;
+
+-- Upgrade to schema 3.0 begins here:
+
+--
+-- Table structure for table host_identifier_type
+--
+
+CREATE TABLE host_identifier_type (
+  type SMALLINT PRIMARY KEY NOT NULL,
+  name VARCHAR(32) DEFAULT NULL
+);
+
+INSERT INTO host_identifier_type VALUES (0, 'hw-address');
+INSERT INTO host_identifier_type VALUES (1, 'duid');
+INSERT INTO host_identifier_type VALUES (2, 'circuit-id');
+
+CREATE TABLE dhcp_option_scope (
+  scope_id SMALLINT PRIMARY KEY NOT NULL,
+  scope_name varchar(32) DEFAULT NULL
+);
+
+INSERT INTO dhcp_option_scope VALUES (0, 'global');
+INSERT INTO dhcp_option_scope VALUES (1, 'subnet');
+INSERT INTO dhcp_option_scope VALUES (2, 'client-class');
+INSERT INTO dhcp_option_scope VALUES (3, 'host');
+
+--
+-- Table structure for table hosts
+--
+-- Primary key and unique contraints automatically create indexes
+-- foreign key constraints do not
+CREATE TABLE hosts (
+  host_id SERIAL PRIMARY KEY NOT NULL,
+  dhcp_identifier BYTEA NOT NULL,
+  dhcp_identifier_type SMALLINT NOT NULL,
+  dhcp4_subnet_id INT DEFAULT NULL,
+  dhcp6_subnet_id INT DEFAULT NULL,
+  ipv4_address BIGINT DEFAULT NULL,
+  hostname VARCHAR(255) DEFAULT NULL,
+  dhcp4_client_classes VARCHAR(255) DEFAULT NULL,
+  dhcp6_client_classes VARCHAR(255) DEFAULT NULL,
+  CONSTRAINT key_dhcp4_ipv4_address_subnet_id UNIQUE (ipv4_address, dhcp4_subnet_id),
+  CONSTRAINT key_dhcp4_identifier_subnet_id UNIQUE (dhcp_identifier, dhcp_identifier_type, dhcp4_subnet_id),
+  CONSTRAINT key_dhcp6_identifier_subnet_id UNIQUE (dhcp_identifier, dhcp_identifier_type, dhcp6_subnet_id),
+  CONSTRAINT fk_host_identifier_type FOREIGN KEY (dhcp_identifier_type) REFERENCES host_identifier_type (type)
+  ON DELETE CASCADE
+);
+
+CREATE INDEX fk_host_identifier_type ON hosts (dhcp_identifier_type);
+
+--
+-- Table structure for table dhcp4_options
+--
+
+CREATE TABLE dhcp4_options (
+  option_id SERIAL PRIMARY KEY NOT NULL,
+  code SMALLINT NOT NULL,
+  value BYTEA,
+  formatted_value TEXT,
+  space VARCHAR(128) DEFAULT NULL,
+  persistent BOOLEAN NOT NULL DEFAULT 'f',
+  dhcp_client_class VARCHAR(128) DEFAULT NULL,
+  dhcp4_subnet_id INT DEFAULT NULL,
+  host_id INT DEFAULT NULL,
+  scope_id SMALLINT NOT NULL,
+  CONSTRAINT fk_options_host1 FOREIGN KEY (host_id) REFERENCES hosts (host_id) ON DELETE CASCADE,
+  CONSTRAINT fk_dhcp4_option_scode FOREIGN KEY (scope_id) REFERENCES dhcp_option_scope (scope_id) ON DELETE CASCADE
+);
+
+CREATE INDEX fk_dhcp4_options_host1_idx ON dhcp4_options (host_id);
+CREATE INDEX fk_dhcp4_options_scope_idx ON dhcp4_options (scope_id);
+
+--
+-- Table structure for table dhcp6_options
+--
+
+CREATE TABLE dhcp6_options (
+  option_id SERIAL PRIMARY KEY NOT NULL,
+  code INT NOT NULL,
+  value BYTEA,
+  formatted_value TEXT,
+  space VARCHAR(128) DEFAULT NULL,
+  persistent BOOLEAN NOT NULL DEFAULT 'f',
+  dhcp_client_class VARCHAR(128) DEFAULT NULL,
+  dhcp6_subnet_id INT DEFAULT NULL,
+  host_id INT DEFAULT NULL,
+  scope_id SMALLINT NOT NULL,
+  CONSTRAINT fk_options_host10 FOREIGN KEY (host_id) REFERENCES hosts (host_id) ON DELETE CASCADE,
+  CONSTRAINT fk_dhcp6_option_scode FOREIGN KEY (scope_id) REFERENCES dhcp_option_scope (scope_id) ON DELETE CASCADE
+);
+
+CREATE INDEX fk_dhcp6_options_host1_idx ON dhcp6_options (host_id);
+CREATE INDEX fk_dhcp6_options_scope_idx ON dhcp6_options (scope_id);
+
+--
+-- Table structure for table ipv6_reservations
+--
+
+CREATE TABLE ipv6_reservations (
+  reservation_id SERIAL PRIMARY KEY NOT NULL,
+  address VARCHAR(39) NOT NULL,
+  prefix_len SMALLINT NOT NULL DEFAULT '128',
+  type SMALLINT NOT NULL DEFAULT '0',
+  dhcp6_iaid INT DEFAULT NULL,
+  host_id INT NOT NULL,
+  CONSTRAINT key_dhcp6_address_prefix_len UNIQUE (address, prefix_len),
+  CONSTRAINT fk_ipv6_reservations_host FOREIGN KEY (host_id) REFERENCES hosts (host_id) ON DELETE CASCADE
+);
+
+CREATE INDEX fk_ipv6_reservations_host_idx ON ipv6_reservations (host_id);
+
+--
+-- Table structure for table lease_hwaddr_source
+--
+
+CREATE TABLE lease_hwaddr_source (
+  hwaddr_source INT PRIMARY KEY NOT NULL,
+  name VARCHAR(40) DEFAULT NULL
+);
+
+-- Hardware address obtained from raw sockets
+INSERT INTO lease_hwaddr_source VALUES (1, 'HWADDR_SOURCE_RAW');
+
+-- Hardware address converted from IPv6 link-local address with EUI-64
+INSERT INTO lease_hwaddr_source VALUES (2, 'HWADDR_SOURCE_IPV6_LINK_LOCAL');
+
+-- Hardware address extracted from client-id (duid)
+INSERT INTO lease_hwaddr_source VALUES (4, 'HWADDR_SOURCE_DUID');
+
+-- Hardware address extracted from client address relay option (RFC6939)
+INSERT INTO lease_hwaddr_source VALUES (8, 'HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION');
+
+-- Hardware address extracted from remote-id option (RFC4649)
+INSERT INTO lease_hwaddr_source VALUES (16, 'HWADDR_SOURCE_REMOTE_ID');
+
+-- Hardware address extracted from subscriber-id option (RFC4580)
+INSERT INTO lease_hwaddr_source VALUES (32, 'HWADDR_SOURCE_SUBSCRIBER_ID');
+
+-- Hardware address extracted from docsis options
+INSERT INTO lease_hwaddr_source VALUES (64, 'HWADDR_SOURCE_DOCSIS_CMTS');
+
+INSERT INTO lease_hwaddr_source VALUES (128, 'HWADDR_SOURCE_DOCSIS_MODEM');
+
+-- In the event hardware address cannot be determined, we need to satisfy
+-- foreign key constraint between lease6 and lease_hardware_source
+INSERT INTO lease_hwaddr_source VALUES (0, 'HWADDR_SOURCE_UNKNOWN');
+
+-- Adding ORDER BY clause to sort by lease address
+--
+--  FUNCTION that returns a result set containing the data for lease4 dumps
+DROP FUNCTION IF EXISTS lease4DumpData();
+CREATE FUNCTION lease4DumpData() RETURNS
+    table (address inet,
+           hwaddr text,
+           client_id text,
+           valid_lifetime bigint,
+           expire timestamp with time zone,
+           subnet_id bigint,
+           fqdn_fwd int,
+           fqdn_rev int,
+           hostname text,
+           state text
+    ) as \$\$
+    SELECT ('0.0.0.0'::inet + l.address),
+            encode(l.hwaddr,'hex'),
+            encode(l.client_id,'hex'),
+            l.valid_lifetime,
+            l.expire,
+            l.subnet_id,
+            l.fqdn_fwd::int,
+            l.fqdn_rev::int,
+            l.hostname,
+            s.name
+    FROM lease4 l
+         left outer join lease_state s on (l.state = s.state)
+    ORDER BY l.address;
+\$\$ LANGUAGE SQL;
+--
+
+-- Add new columns to lease6
+ALTER TABLE lease6
+  ADD COLUMN hwaddr BYTEA DEFAULT NULL,
+  ADD COLUMN hwtype SMALLINT DEFAULT NULL,
+  ADD COLUMN hwaddr_source SMALLINT DEFAULT NULL;
+
+--
+--  FUNCTION that returns a result set containing the column names for lease6 dumps
+DROP FUNCTION IF EXISTS lease6DumpHeader();
+CREATE FUNCTION lease6DumpHeader() RETURNS text AS  \$\$
+    select cast('address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,state,hwaddr,hwtype,hwaddr_source' as text) as result;
+\$\$ LANGUAGE SQL;
+--
+
+--
+--  FUNCTION that returns a result set containing the data for lease6 dumps
+DROP FUNCTION IF EXISTS lease6DumpData();
+CREATE FUNCTION lease6DumpData() RETURNS
+    TABLE (
+           address text,
+           duid text,
+           valid_lifetime bigint,
+           expire timestamp with time zone,
+           subnet_id bigint,
+           pref_lifetime bigint,
+           name text,
+           iaid integer,
+           prefix_len smallint,
+           fqdn_fwd int,
+           fqdn_rev int,
+           hostname text,
+           state text,
+           hwaddr text,
+           hwtype smallint,
+           hwaddr_source text
+    ) AS \$\$
+    SELECT (l.address,
+            encode(l.duid,'hex'),
+            l.valid_lifetime,
+            l.expire,
+            l.subnet_id,
+            l.pref_lifetime,
+            t.name,
+            l.iaid,
+            l.prefix_len,
+            l.fqdn_fwd::int,
+            l.fqdn_rev::int,
+            l.hostname,
+            s.name,
+            encode(l.hwaddr,'hex'),
+            l.hwtype,
+            h.name
+     )
+     FROM lease6 l
+         left outer join lease6_types t on (l.lease_type = t.lease_type)
+         left outer join lease_state s on (l.state = s.state)
+         left outer join lease_hwaddr_source h on (l.hwaddr_source = h.hwaddr_source)
+     ORDER BY l.address;
+\$\$ LANGUAGE SQL;
+
+-- Set 3.0 schema version.
+UPDATE schema_version
+    SET version = '3', minor = '0';
+
+-- Schema 3.0 specification ends here.
+
+-- Commit the script transaction
+COMMIT;
+
+EOF
+
+exit $RESULT