summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/bin/dhcp4/ctrl_dhcp4_srv.cc2
-rw-r--r--src/bin/dhcp4/dhcp4_dhcp4o6_ipc.cc47
-rw-r--r--src/bin/dhcp4/dhcp4_dhcp4o6_ipc.h12
-rw-r--r--src/bin/dhcp4/tests/Makefile.am1
-rw-r--r--src/bin/dhcp4/tests/dhcp4_dhcp4o6_ipc_unittest.cc181
-rw-r--r--src/bin/dhcp6/ctrl_dhcp6_srv.cc2
-rw-r--r--src/bin/dhcp6/dhcp6_dhcp4o6_ipc.cc28
-rw-r--r--src/bin/dhcp6/dhcp6_dhcp4o6_ipc.h14
-rw-r--r--src/bin/dhcp6/dhcp6_srv.cc2
-rw-r--r--src/bin/dhcp6/tests/Makefile.am2
-rw-r--r--src/bin/dhcp6/tests/dhcp6_dhcp4o6_ipc_unittest.cc140
-rw-r--r--src/lib/dhcpsrv/dhcp4o6_ipc.cc194
-rw-r--r--src/lib/dhcpsrv/dhcp4o6_ipc.h87
-rw-r--r--src/lib/dhcpsrv/dhcpsrv_messages.mes3
-rwxr-xr-xsrc/lib/dhcpsrv/tests/Makefile.am1
-rw-r--r--src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc71
-rw-r--r--src/lib/dhcpsrv/testutils/Makefile.am3
-rw-r--r--src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.cc47
-rw-r--r--src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.h98
19 files changed, 749 insertions, 186 deletions
diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc
index 5f0f9c1a7d..78eeec3a94 100644
--- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc
+++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc
@@ -192,7 +192,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
// Setup DHCPv4-over-DHCPv6 IPC
try {
- Dhcp4o6Ipc::instance().open();
+ Dhcp4to6Ipc::instance().open();
} catch (const std::exception& ex) {
std::ostringstream err;
err << "error starting DHCPv4-over-DHCPv6 IPC "
diff --git a/src/bin/dhcp4/dhcp4_dhcp4o6_ipc.cc b/src/bin/dhcp4/dhcp4_dhcp4o6_ipc.cc
index 6daf6a6998..b429848c4f 100644
--- a/src/bin/dhcp4/dhcp4_dhcp4o6_ipc.cc
+++ b/src/bin/dhcp4/dhcp4_dhcp4o6_ipc.cc
@@ -24,14 +24,14 @@ using namespace std;
namespace isc {
namespace dhcp {
-Dhcp4o6Ipc::Dhcp4o6Ipc() : Dhcp4o6IpcBase() {}
+Dhcp4to6Ipc::Dhcp4to6Ipc() : Dhcp4o6IpcBase() {}
-Dhcp4o6Ipc& Dhcp4o6Ipc::instance() {
- static Dhcp4o6Ipc dhcp4o6_ipc;
- return (dhcp4o6_ipc);
+Dhcp4to6Ipc& Dhcp4to6Ipc::instance() {
+ static Dhcp4to6Ipc dhcp4to6_ipc;
+ return (dhcp4to6_ipc);
}
-void Dhcp4o6Ipc::open() {
+void Dhcp4to6Ipc::open() {
uint32_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port();
if (port == 0) {
Dhcp4o6IpcBase::close();
@@ -42,31 +42,48 @@ void Dhcp4o6Ipc::open() {
}
int old_fd = socket_fd_;
- socket_fd_ = Dhcp4o6IpcBase::open(static_cast<uint16_t>(port), 4);
+ 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_, Dhcp4o6Ipc::handler);
+ IfaceMgr::instance().addExternalSocket(socket_fd_,
+ Dhcp4to6Ipc::handler);
}
}
-void Dhcp4o6Ipc::handler() {
- Dhcp4o6Ipc& ipc = Dhcp4o6Ipc::instance();
+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.size() != 1) {
- return;
+ 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 = pkt->getOption(D6O_DHCPV4_MSG);
+
+ OptionPtr msg = msgs.begin()->second;
if (!msg) {
- isc_throw(Unexpected, "Can't get DHCPv4 message option");
+ isc_throw(Dhcp4o6IpcError, "null DHCPv4 message option in the"
+ " DHCPv4o6 message received by the DHCPv4 server");
}
- instance().received_.reset(new Pkt4o6(msg->getData(), pkt));
+
+ // Record this message.
+ ipc.received_.reset(new Pkt4o6(msg->getData(), pkt));
}
-Pkt4o6Ptr& Dhcp4o6Ipc::getReceived() {
+Pkt4o6Ptr& Dhcp4to6Ipc::getReceived() {
return (received_);
}
diff --git a/src/bin/dhcp4/dhcp4_dhcp4o6_ipc.h b/src/bin/dhcp4/dhcp4_dhcp4o6_ipc.h
index 8a0d98f492..47e5c69dc5 100644
--- a/src/bin/dhcp4/dhcp4_dhcp4o6_ipc.h
+++ b/src/bin/dhcp4/dhcp4_dhcp4o6_ipc.h
@@ -28,23 +28,23 @@ namespace isc {
namespace dhcp {
/// @brief Handles DHCPv4-over-DHCPv6 IPC on the DHCPv4 server side
-class Dhcp4o6Ipc : public Dhcp4o6IpcBase {
+class Dhcp4to6Ipc : public Dhcp4o6IpcBase {
protected:
/// @brief Constructor
///
/// Default constructor
- Dhcp4o6Ipc();
+ Dhcp4to6Ipc();
/// @brief Destructor.
- virtual ~Dhcp4o6Ipc() { }
+ virtual ~Dhcp4to6Ipc() { }
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
- static Dhcp4o6Ipc& instance();
+ static Dhcp4to6Ipc& instance();
/// @brief Open communication socket
///
diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
index a0856b21fd..302d9afc61 100644
--- a/src/bin/dhcp4/tests/Makefile.am
+++ b/src/bin/dhcp4/tests/Makefile.am
@@ -93,6 +93,7 @@ dhcp4_unittests_SOURCES += release_unittest.cc
dhcp4_unittests_SOURCES += out_of_range_unittest.cc
dhcp4_unittests_SOURCES += decline_unittest.cc
dhcp4_unittests_SOURCES += kea_controller_unittest.cc
+dhcp4_unittests_SOURCES += dhcp4_dhcp4o6_ipc_unittest.cc
nodist_dhcp4_unittests_SOURCES = marker_file.h test_libraries.h
diff --git a/src/bin/dhcp4/tests/dhcp4_dhcp4o6_ipc_unittest.cc b/src/bin/dhcp4/tests/dhcp4_dhcp4o6_ipc_unittest.cc
new file mode 100644
index 0000000000..7af25833d9
--- /dev/null
+++ b/src/bin/dhcp4/tests/dhcp4_dhcp4o6_ipc_unittest.cc
@@ -0,0 +1,181 @@
+// 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 <asiolink/io_address.h>
+#include <dhcp/pkt4o6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcp4/dhcp4_dhcp4o6_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.
+ ///
+ /// @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
diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
index 9b17b0124a..f546ad7c80 100644
--- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc
+++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
@@ -216,7 +216,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
// Setup DHCPv4-over-DHCPv6 IPC
try {
- Dhcp4o6Ipc::instance().open();
+ Dhcp6to4Ipc::instance().open();
} catch (const std::exception& ex) {
std::ostringstream err;
err << "error starting DHCPv4-over-DHCPv6 IPC "
diff --git a/src/bin/dhcp6/dhcp6_dhcp4o6_ipc.cc b/src/bin/dhcp6/dhcp6_dhcp4o6_ipc.cc
index 260db36d80..9cd203f404 100644
--- a/src/bin/dhcp6/dhcp6_dhcp4o6_ipc.cc
+++ b/src/bin/dhcp6/dhcp6_dhcp4o6_ipc.cc
@@ -24,14 +24,14 @@ using namespace std;
namespace isc {
namespace dhcp {
-Dhcp4o6Ipc::Dhcp4o6Ipc() : Dhcp4o6IpcBase() {}
+Dhcp6to4Ipc::Dhcp6to4Ipc() : Dhcp4o6IpcBase() {}
-Dhcp4o6Ipc& Dhcp4o6Ipc::instance() {
- static Dhcp4o6Ipc dhcp4o6_ipc;
- return (dhcp4o6_ipc);
+Dhcp6to4Ipc& Dhcp6to4Ipc::instance() {
+ static Dhcp6to4Ipc dhcp6to4_ipc;
+ return (dhcp6to4_ipc);
}
-void Dhcp4o6Ipc::open() {
+void Dhcp6to4Ipc::open() {
uint32_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port();
if (port == 0) {
Dhcp4o6IpcBase::close();
@@ -42,30 +42,38 @@ void Dhcp4o6Ipc::open() {
}
int old_fd = socket_fd_;
- socket_fd_ = Dhcp4o6IpcBase::open(static_cast<uint16_t>(port), 6);
+ 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_, Dhcp4o6Ipc::handler);
+ IfaceMgr::instance().addExternalSocket(socket_fd_,
+ Dhcp6to4Ipc::handler);
}
}
-void Dhcp4o6Ipc::handler() {
- Dhcp4o6Ipc& ipc = Dhcp4o6Ipc::instance();
+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 = buf[0];
+ 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);
}
diff --git a/src/bin/dhcp6/dhcp6_dhcp4o6_ipc.h b/src/bin/dhcp6/dhcp6_dhcp4o6_ipc.h
index 76c5b7e4ce..e8934e7227 100644
--- a/src/bin/dhcp6/dhcp6_dhcp4o6_ipc.h
+++ b/src/bin/dhcp6/dhcp6_dhcp4o6_ipc.h
@@ -15,7 +15,7 @@
#ifndef DHCP6_DHCP4O6_IPC_H
#define DHCP6_DHCP4O6_IPC_H
-/// @file dhcp6_dhcp4o6_ipc.h Defines the Dhcp4o6Ipc class.
+/// @file dhcp6_dhcp4o6_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.
///
@@ -26,23 +26,23 @@ namespace isc {
namespace dhcp {
/// @brief Handles DHCPv4-over-DHCPv6 IPC on the DHCPv6 server side
-class Dhcp4o6Ipc : public Dhcp4o6IpcBase {
+class Dhcp6to4Ipc : public Dhcp4o6IpcBase {
protected:
/// @brief Constructor
///
/// Default constructor
- Dhcp4o6Ipc();
+ Dhcp6to4Ipc();
/// @brief Destructor.
- virtual ~Dhcp4o6Ipc() { }
+ virtual ~Dhcp6to4Ipc() { }
public:
- /// @brief Returns pointer to the sole instance of Dhcp4o6Ipc
+ /// @brief Returns pointer to the sole instance of Dhcp6to4Ipc
///
- /// Dhcp4o6Ipc is a singleton class
+ /// Dhcp6to4Ipc is a singleton class
///
/// @return the only existing instance of DHCP4o6 IPC
- static Dhcp4o6Ipc& instance();
+ static Dhcp6to4Ipc& instance();
/// @brief Open communication socket
///
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 3faa0d8738..30f095ab22 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -213,7 +213,7 @@ Dhcpv6Srv::~Dhcpv6Srv() {
}
try {
- Dhcp4o6Ipc::instance().close();
+ Dhcp6to4Ipc::instance().close();
} catch(const std::exception& ex) {
// Highly unlikely, but lets Report it but go on
// LOG_ERROR(dhcp6_logger, DHCP6_SRV_DHCP4O6_ERROR).arg(ex.what());
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index 87dc69dd88..4076849cfa 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/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 += decline_unittest.cc
dhcp6_unittests_SOURCES += dhcp6_message_test.cc dhcp6_message_test.h
-
dhcp6_unittests_SOURCES += kea_controller_unittest.cc
+dhcp6_unittests_SOURCES += dhcp6_dhcp4o6_ipc_unittest.cc
nodist_dhcp6_unittests_SOURCES = marker_file.h test_libraries.h
diff --git a/src/bin/dhcp6/tests/dhcp6_dhcp4o6_ipc_unittest.cc b/src/bin/dhcp6/tests/dhcp6_dhcp4o6_ipc_unittest.cc
new file mode 100644
index 0000000000..b22dec1683
--- /dev/null
+++ b/src/bin/dhcp6/tests/dhcp6_dhcp4o6_ipc_unittest.cc
@@ -0,0 +1,140 @@
+// 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 <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/dhcp6_dhcp4o6_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.
+ ///
+ /// @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
diff --git a/src/lib/dhcpsrv/dhcp4o6_ipc.cc b/src/lib/dhcpsrv/dhcp4o6_ipc.cc
index bf3567a8e1..a4a5ea2d67 100644
--- a/src/lib/dhcpsrv/dhcp4o6_ipc.cc
+++ b/src/lib/dhcpsrv/dhcp4o6_ipc.cc
@@ -17,11 +17,12 @@
#include <dhcp/dhcp6.h>
#include <dhcp/iface_mgr.h>
#include <dhcp/option6_addrlst.h>
+#include <dhcp/option_custom.h>
#include <dhcp/option_string.h>
#include <dhcp/option_vendor.h>
#include <dhcpsrv/dhcp4o6_ipc.h>
-#include <dhcpsrv/dhcpsrv_log.h>
-
+#include <boost/pointer_cast.hpp>
+#include <errno.h>
#include <netinet/in.h>
#include <sys/fcntl.h>
#include <string>
@@ -39,41 +40,36 @@ Dhcp4o6IpcBase::~Dhcp4o6IpcBase() {
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_) {
// No change: nothing to do
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
int sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
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) {
+ static_cast<const void*>(&flag),
+ sizeof(flag)) < 0) {
::close(sock);
- isc_throw(Unexpected, "Failed to set SO_REUSEADDR on DHCP4o6 socket.");
+ isc_throw(Dhcp4o6IpcError,
+ "Failed to set SO_REUSEADDR on DHCP4o6 socket.");
}
// Set no blocking
if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
::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
@@ -83,7 +79,7 @@ int Dhcp4o6IpcBase::open(uint16_t port, int side) {
#ifdef HAVE_SA_LEN
local6.sin6_len = sizeof(local6);
#endif
- if (side == 6) {
+ if (endpoint_type == ENDPOINT_TYPE_V6) {
local6.sin6_port = htons(port);
} else {
local6.sin6_port = htons(port + 1);
@@ -92,7 +88,7 @@ int Dhcp4o6IpcBase::open(uint16_t port, int side) {
local6.sin6_addr.s6_addr[15] = 1;
if (bind(sock, (struct sockaddr *)&local6, sizeof(local6)) < 0) {
::close(sock);
- isc_throw(Unexpected, "Failed to bind DHCP4o6 socket.");
+ isc_throw(Dhcp4o6IpcError, "Failed to bind DHCP4o6 socket.");
}
// Connect to the remote address
@@ -102,7 +98,7 @@ int Dhcp4o6IpcBase::open(uint16_t port, int side) {
#ifdef HAVE_SA_LEN
remote6.sin6_len = sizeof(remote6);
#endif
- if (side == 6) {
+ if (endpoint_type == ENDPOINT_TYPE_V6) {
remote6.sin6_port = htons(port + 1);
} else {
remote6.sin6_port = htons(port);
@@ -113,13 +109,13 @@ int Dhcp4o6IpcBase::open(uint16_t port, int side) {
if (connect(sock, reinterpret_cast<const struct sockaddr*>(&remote6),
sizeof(remote6)) < 0) {
::close(sock);
- isc_throw(Unexpected, "Failed to connect DHCP4o6 socket.");
+ isc_throw(Dhcp4o6IpcError, "Failed to connect DHCP4o6 socket.");
}
if (socket_fd_ != -1) {
if (dup2(sock, socket_fd_) == -1) {
::close(sock);
- isc_throw(Unexpected, "Failed to duplicate DHCP4o6 socket.");
+ isc_throw(Dhcp4o6IpcError, "Failed to duplicate DHCP4o6 socket.");
}
if (sock != socket_fd_) {
::close(sock);
@@ -134,113 +130,141 @@ int Dhcp4o6IpcBase::open(uint16_t port, int side) {
}
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() {
uint8_t buf[65536];
ssize_t cc = recv(socket_fd_, buf, sizeof(buf), 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));
pkt->updateTimestamp();
// Get interface name and remote address
pkt->unpack();
- OptionVendorPtr vendor =
- boost::dynamic_pointer_cast<OptionVendor>(pkt->getOption(D6O_VENDOR_OPTS));
- if (!vendor) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
- DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
- .arg("no vendor option");
- return (Pkt6Ptr());
- }
- if (vendor->getVendorId() != ENTERPRISE_ID_ISC) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
- DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
- .arg("vendor option enterprise ID is not ISC");
- return (Pkt6Ptr());
- }
- OptionStringPtr ifname =
- boost::dynamic_pointer_cast<OptionString>(vendor->getOption(ISC_V6_4O6_INTERFACE));
+
+ // Vendor option is initially NULL. If we find the instance of the vendor
+ // option with the ISC enterprise id this pointer will point to it.
+ OptionVendorPtr option_vendor;
+
+ // Get all vendor option and look for the one with the ISC enterprise id.
+ OptionCollection vendor_options = pkt->getOptions(D6O_VENDOR_OPTS);
+ for (OptionCollection::const_iterator opt = vendor_options.begin();
+ opt != vendor_options.end(); ++opt) {
+ option_vendor = boost::dynamic_pointer_cast<OptionVendor>(opt->second);
+ if (option_vendor) {
+ if (option_vendor->getVendorId() == ENTERPRISE_ID_ISC) {
+ break;
+ }
+ option_vendor.reset();
+ }
+ }
+
+ // Vendor option must exist.
+ if (!option_vendor) {
+ 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) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
- DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
- .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());
if (!iface) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
- DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
- .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) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
- DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
- .arg("no source address suboption");
- return (Pkt6Ptr());
- }
- Option6AddrLst::AddressContainer addrs = srcs->getAddresses();
- if (addrs.size() != 1) {
- 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
- static_cast<void>(pkt->delOption(D6O_VENDOR_OPTS));
- pkt->setRemoteAddr(addrs[0]);
+ isc_throw(Dhcp4o6IpcError, "option " << D6O_VENDOR_OPTS
+ << " doesn't contain the " << ISC_V6_4O6_SRC_ADDRESS
+ << " option required in the DHCP4o6 message sent"
+ " between Kea servers");
+ }
+
+ // Update the packet.
+ pkt->setRemoteAddr(srcs->readAddress());
pkt->setIface(iface->getName());
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);
}
-void Dhcp4o6IpcBase::send(Pkt6Ptr pkt) {
- // No packet: nothing to send
+void Dhcp4o6IpcBase::send(const Pkt6Ptr& pkt) {
+ // This shouldn't happen, i.e. send() shouldn't be called if there is
+ // no message.
if (!pkt) {
- return;
+ isc_throw(Dhcp4o6IpcError, "DHCP4o6 message must not be NULL while"
+ " trying to send it over the IPC");
}
// Disabled: nowhere to send
if (socket_fd_ == -1) {
- return;
+ isc_throw(Dhcp4o6IpcError, "unable to send DHCP4o6 message because"
+ " IPC socket is closed");
}
// 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));
// If vendor option doesn't exist or its enterprise id is not ISC's
// enterprise id, let's create it.
- if (!vendor_opt || (vendor_opt->getVendorId() != ENTERPRISE_ID_ISC)) {
- vendor_opt.reset(new OptionVendor(Option::V6, ENTERPRISE_ID_ISC));
+ if (!option_vendor ||
+ (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
- vendor_opt->addOption(OptionPtr(new OptionString(Option::V6,
- ISC_V6_4O6_INTERFACE,
- pkt->getIface())));
- vendor_opt->addOption(OptionPtr(new Option6AddrLst(ISC_V6_4O6_SRC_ADDRESS,
+ option_vendor->addOption(OptionStringPtr(new OptionString(Option::V6,
+ ISC_V6_4O6_INTERFACE,
+ pkt->getIface())));
+ option_vendor->addOption(Option6AddrLstPtr(new Option6AddrLst(
+ ISC_V6_4O6_SRC_ADDRESS,
pkt->getRemoteAddr())));
- pkt->addOption(vendor_opt);
-
// Get packet content
OutputBuffer& buf = pkt->getBuffer();
buf.clear();
pkt->pack();
- // Send
- if (::send(socket_fd_, buf.getData(), buf.getLength(), 0) < 0) {
- isc_throw(Unexpected, "Failed to send over DHCP4o6 IPC socket");
- }
- return;
+ // Try to send the message.
+ if (::send(socket_fd_, buf.getData(), buf.getLength(), 0) < 0) {
+ isc_throw(Dhcp4o6IpcError,
+ "failed to send DHCP4o6 message over the IPC: "
+ << strerror(errno));
+ }
}
}; // namespace dhcp
diff --git a/src/lib/dhcpsrv/dhcp4o6_ipc.h b/src/lib/dhcpsrv/dhcp4o6_ipc.h
index 8097d2117e..21fa2ec526 100644
--- a/src/lib/dhcpsrv/dhcp4o6_ipc.h
+++ b/src/lib/dhcpsrv/dhcp4o6_ipc.h
@@ -19,18 +19,61 @@
/// This file defines the class Kea uses as a base for
/// DHCPv4-over-DHCPv6 communication between servers.
///
-#include <dhcp/pkt6.h>
+#include <exceptions/exceptions.h>
+#include <dhcp/pkt6.h>
#include <boost/noncopyable.hpp>
-
#include <stdint.h>
namespace isc {
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 Base class implementing transmission of the DHCPv4 over
+/// DHCPv6 messages (RFC 7341) between the Kea servers.
+///
+/// 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 distict 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 {
+public:
+
+ /// @brief Endpoint type: DHCPv4 or DHCPv6 server.
+ enum EndpointType {
+ ENDPOINT_TYPE_V4 = 4,
+ ENDPOINT_TYPE_V6 = 6
+ };
+
protected:
/// @brief Constructor
///
@@ -40,38 +83,48 @@ protected:
/// @brief Destructor.
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 side side of the server (4 or 6)
+ /// @param port Port number to use. The socket is bound to this port
+ /// 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
- int open(uint16_t port, int side);
+ /// @return New socket descriptor.
+ int open(uint16_t port, EndpointType endpoint_type);
public:
- /// @brief Open communication socket (for derived classes)
+
+ /// @brief Open communication socket (for derived classes).
virtual void open() = 0;
- /// @brief Close communication socket
+ /// @brief Close communication socket.
void close();
- /// @brief Receive IPC message
+ /// @brief Receive message over IPC.
///
/// @return a pointer to a DHCPv6 message with interface and remote
/// address set from the IPC message
Pkt6Ptr receive();
- /// @brief Send IPC message
+ /// @brief Send message over IPC.
+ ///
+ /// The IPC uses @c ISC_V6_4O6_INTERFACE and @c ISC_V6_4O6_SRC_ADDRESS
+ /// options conveyed within the Vendor Specific Information option, with
+ /// 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 a pointer to a DHCPv6 message with interface and remote
- /// address set for the IPC message
- void send(Pkt6Ptr pkt);
+ /// @param pkt Pointer to a DHCPv6 message with interface and remote
+ /// address.
+ void send(const Pkt6Ptr& pkt);
protected:
- /// @brief Port number
+
+ /// @brief Port number configured for IPC communication.
uint16_t port_;
- /// @brief Socket descriptor
+ /// @brief Socket descriptor.
int socket_fd_;
};
diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes
index 201761a4a7..4821a4342b 100644
--- a/src/lib/dhcpsrv/dhcpsrv_messages.mes
+++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes
@@ -151,9 +151,6 @@ the database access parameters are changed: in the latter case, the
server closes the currently open database, and opens a database using
the new parameters.
-% DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET received bad DHCPv4o6 packet: %1
-A bad DHCPv4o6 packet was received.
-
% DHCPSRV_DHCP_DDNS_ERROR_EXCEPTION error handler for DHCP_DDNS IO generated an expected exception: %1
This is an error message that occurs when an attempt to send a request to
kea-dhcp-ddns fails there registered error handler threw an uncaught exception.
diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am
index b8c766f497..64f793c456 100755
--- a/src/lib/dhcpsrv/tests/Makefile.am
+++ b/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 += database_connection_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 += expiration_config_parser_unittest.cc
libdhcpsrv_unittests_SOURCES += host_mgr_unittest.cc
diff --git a/src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc b/src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc
index c9e8db199a..1ab1c6461b 100644
--- a/src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc
+++ b/src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc
@@ -42,7 +42,7 @@ const uint16_t TEST_PORT = 12345;
const uint16_t TEST_ITERATIONS = 10;
/// @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.
typedef Dhcp4o6TestIpc TestIpc;
@@ -65,7 +65,7 @@ protected:
/// @param prefix Prefix.
/// @param postfix 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.
////
@@ -77,19 +77,20 @@ protected:
/// the "eth0" will be used, for odd postfix values "eth1" will be used.
///
/// @return Pointer to the created message.
- static Pkt6Ptr createDHCPv4o6Message(const uint16_t msg_type,
- const uint16_t postfix = 0);
+ static Pkt6Ptr createDHCPv4o6Message(uint16_t msg_type,
+ uint16_t postfix = 0);
/// @brief Creates an instance of the DHCPv4o6 message with vendor option.
///
/// @param msg_type Message type.
/// @param postfix Postfix to be appended to the remote address. See the
/// documentation of @c createDHCPv4o6Message for details.
+ /// @param enterprise_id Enterprise ID for the vendor option.
///
/// @return Pointer to the created message.
- static Pkt6Ptr createDHCPv4o6MsgWithVendorOption(const uint16_t msg_type,
- const uint16_t postfix,
- const uint32_t enterprise_id);
+ static Pkt6Ptr createDHCPv4o6MsgWithVendorOption(uint16_t msg_type,
+ uint16_t postfix,
+ uint32_t enterprise_id);
/// @brief Creates an instance of the DHCPv4o6 message with ISC
/// vendor option.
@@ -102,8 +103,8 @@ protected:
/// documentation of @c createDHCPv4o6Message for details.
///
/// @return Pointer to the created message.
- static Pkt6Ptr createDHCPv4o6MsgWithISCVendorOption(const uint16_t msg_type,
- const uint16_t postfix);
+ static Pkt6Ptr createDHCPv4o6MsgWithISCVendorOption(uint16_t msg_type,
+ uint16_t postfix);
/// @brief Creates an instance of the DHCPv4o6 message with vendor
/// option holding enterprise id of 32000.
@@ -117,23 +118,24 @@ protected:
/// documentation of @c createDHCPv4o6Message for details.
///
/// @return Pointer to the created message.
- static Pkt6Ptr createDHCPv4o6MsgWithAnyVendorOption(const uint16_t msg_type,
- const uint16_t postfix);
+ static Pkt6Ptr createDHCPv4o6MsgWithAnyVendorOption(uint16_t msg_type,
+ uint16_t postfix);
/// @brief Creates an instance of the DHCPv4o6 Message option.
///
/// @param src Type of the source endpoint. It can be 4 or 6.
/// @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.
///
/// @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 dest Type of the destination IPC endpoint. It can be 4 or 6.
- void testSendReceive(const uint16_t iterations_num,
- const TestIpc::EndpointType& src,
- const TestIpc::EndpointType& dest,
+ /// @param create_msg_fun Function called to create the packet.
+ void testSendReceive(uint16_t iterations_num,
+ TestIpc::EndpointType src,
+ TestIpc::EndpointType dest,
const CreateMsgFun& create_msg_fun);
/// @brief Tests that error is reported when invalid message is received.
@@ -154,15 +156,15 @@ Dhcp4o6IpcBaseTest::Dhcp4o6IpcBaseTest()
std::string
Dhcp4o6IpcBaseTest::concatenate(const std::string& prefix,
- const uint16_t postfix) {
+ uint16_t postfix) {
std::ostringstream s;
s << prefix << postfix;
return (s.str());
}
Pkt6Ptr
-Dhcp4o6IpcBaseTest::createDHCPv4o6Message(const uint16_t msg_type,
- const uint16_t postfix) {
+Dhcp4o6IpcBaseTest::createDHCPv4o6Message(uint16_t msg_type,
+ uint16_t postfix) {
// Create the DHCPv4o6 message.
Pkt6Ptr pkt(new Pkt6(msg_type, 0));
@@ -180,7 +182,7 @@ Dhcp4o6IpcBaseTest::createDHCPv4o6Message(const uint16_t msg_type,
pkt->setRemoteAddr(IOAddress(concatenate("2001:db8:1::", postfix)));
// 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;
// Add DHCPv4 Message option to make sure it is conveyed by the IPC.
@@ -190,9 +192,9 @@ Dhcp4o6IpcBaseTest::createDHCPv4o6Message(const uint16_t msg_type,
}
Pkt6Ptr
-Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithVendorOption(const uint16_t msg_type,
- const uint16_t postfix,
- const uint32_t enterprise_id) {
+Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithVendorOption(uint16_t msg_type,
+ uint16_t postfix,
+ uint32_t enterprise_id) {
Pkt6Ptr pkt = createDHCPv4o6Message(msg_type, postfix);
// Create vendor option with ISC enterprise id.
@@ -208,19 +210,19 @@ Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithVendorOption(const uint16_t msg_type,
}
Pkt6Ptr
-Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithISCVendorOption(const uint16_t msg_type,
- const uint16_t postfix) {
+Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithISCVendorOption(uint16_t msg_type,
+ uint16_t postfix) {
return (createDHCPv4o6MsgWithVendorOption(msg_type, postfix, ENTERPRISE_ID_ISC));
}
Pkt6Ptr
-Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithAnyVendorOption(const uint16_t msg_type,
- const uint16_t postfix) {
+Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithAnyVendorOption(uint16_t msg_type,
+ uint16_t postfix) {
return (createDHCPv4o6MsgWithVendorOption(msg_type, postfix, 32000));
}
OptionPtr
-Dhcp4o6IpcBaseTest::createDHCPv4MsgOption(const TestIpc::EndpointType& src) {
+Dhcp4o6IpcBaseTest::createDHCPv4MsgOption(TestIpc::EndpointType src) {
// Create the DHCPv4 message.
Pkt4Ptr pkt(new Pkt4(src == TestIpc::ENDPOINT_TYPE_V4 ? DHCPACK : DHCPREQUEST,
1234));
@@ -236,9 +238,9 @@ Dhcp4o6IpcBaseTest::createDHCPv4MsgOption(const TestIpc::EndpointType& src) {
}
void
-Dhcp4o6IpcBaseTest::testSendReceive(const uint16_t iterations_num,
- const TestIpc::EndpointType& src,
- const TestIpc::EndpointType& dest,
+Dhcp4o6IpcBaseTest::testSendReceive(uint16_t iterations_num,
+ TestIpc::EndpointType src,
+ TestIpc::EndpointType dest,
const CreateMsgFun& create_msg_fun) {
// Create IPC instances representing the source and destination endpoints.
TestIpc ipc_src(TEST_PORT, src);
@@ -248,7 +250,7 @@ Dhcp4o6IpcBaseTest::testSendReceive(const uint16_t iterations_num,
ASSERT_NO_THROW(ipc_src.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
// for testing IPC, but it better simulates the real use case.
uint16_t msg_type = (src == TestIpc::ENDPOINT_TYPE_V6 ? DHCPV6_DHCPV4_QUERY :
@@ -459,13 +461,6 @@ TEST_F(Dhcp4o6IpcBaseTest, openError) {
// 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);
diff --git a/src/lib/dhcpsrv/testutils/Makefile.am b/src/lib/dhcpsrv/testutils/Makefile.am
index f058c553a7..7ff34a859e 100644
--- a/src/lib/dhcpsrv/testutils/Makefile.am
+++ b/src/lib/dhcpsrv/testutils/Makefile.am
@@ -12,7 +12,8 @@ if HAVE_GTEST
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
libdhcpsrvtest_la_SOURCES += schema.cc schema.h
diff --git a/src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.cc b/src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.cc
new file mode 100644
index 0000000000..14d25432d9
--- /dev/null
+++ b/src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.cc
@@ -0,0 +1,47 @@
+// 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 <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.
+ 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
diff --git a/src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.h b/src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.h
new file mode 100644
index 0000000000..6cda8068a9
--- /dev/null
+++ b/src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.h
@@ -0,0 +1,98 @@
+// 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 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