Commit f3928060 authored by Francis Dupont's avatar Francis Dupont
Browse files

[master] Merge trac4109a (DHCP4o6 v6 part

parents 208e7fb0 675ba26c
......@@ -3281,6 +3281,33 @@ should include options from the isc option space:
</entry>
</row>
<row>
<entry>pkt6-dhcpv4-query-received</entry>
<entry>integer</entry>
<entry>
Number of DHCPv4-QUERY packets received. This
statistic is expected to grow if there are devices
that are using DHCPv4-over-DHCPv6. DHCPv4-QUERY
messages are used by DHCPv4 clients on an IPv6 only
line so that encapsulate requests over DHCPv6.
</entry>
</row>
<row>
<entry>pkt6-dhcpv4-response-received</entry>
<entry>integer</entry>
<entry>
Number of DHCPv4-RESPONSE packets received. This
statistic is expected to remain zero at all times, as
DHCPv4-RESPONSE packets are sent by the server and the
server is never expected to receive them. A non-zero
value indicates an error. One likely cause would be a
misbehaving relay agent that incorrectly forwards
DHCPv4-RESPONSE message towards the server, rather
back to the clients.
</entry>
</row>
<row>
<entry>pkt6-unknown-received</entry>
<entry>integer</entry>
......@@ -3321,6 +3348,16 @@ should include options from the isc option space:
</entry>
</row>
<row>
<entry>pkt6-dhcpv4-response-sent</entry>
<entry>integer</entry>
<entry>Number of DHCPv4-RESPONSE packets sent. This
statistic is expected to grow in most cases after a
DHCPv4-QUERY is processed. There are certain cases where
there is no response.
</entry>
</row>
<row>
<entry>subnet[id].total-nas</entry>
<entry>integer</entry>
......
......@@ -237,6 +237,24 @@ received in Decline message. It's expected that the option will contain an
address that is being declined. Specific information will be printed in a
separate message.
% DHCP6_DHCP4O6_PACKET_RECEIVED received DHCPv4o6 packet from DHCPv4 server (type %1) for %2 on interface %3
This debug message is printed when the server is receiving a DHCPv4o6
from the DHCPv4 server over inter-process communication.
% DHCP6_DHCP4O6_RECEIVE_FAIL failed to receive DHCPv4o6: %1
This debug message indicates the inter-process communication with the
DHCPv4 server failed. The reason for the error is included in
the message.
% DHCP6_DHCP4O6_RECEIVING receiving DHCPv4o6 packet from DHCPv4 server
This debug message is printed when the server is receiving a DHCPv4o6
from the DHCPv4 server over inter-process communication socket.
% DHCP6_DHCP4O6_SEND_FAIL failed to send DHCPv4o6 packet: %1
This error is output if the IPv6 DHCP server fails to send an assembled
DHCPv4o6 message to a client. The reason for the error is included in the
message.
% DHCP6_DYNAMIC_RECONFIGURATION initiate server reconfiguration using file: %1, after receiving SIGHUP signal
This is the info message logged when the DHCPv6 server starts reconfiguration
as a result of receiving SIGHUP signal.
......
......@@ -634,6 +634,10 @@ Dhcpv6Srv::processPacket(Pkt6Ptr& query, Pkt6Ptr& rsp) {
rsp = processInfRequest(query);
break;
case DHCPV6_DHCPV4_QUERY:
processDhcp4Query(query);
break;
default:
// We received a packet type that we do not recognize.
LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_BASIC, DHCP6_UNKNOWN_MSG_RECEIVED)
......@@ -2787,6 +2791,26 @@ Dhcpv6Srv::processInfRequest(const Pkt6Ptr& inf_request) {
return (reply);
}
void
Dhcpv6Srv::processDhcp4Query(const Pkt6Ptr& dhcp4_query) {
sanityCheck(dhcp4_query, OPTIONAL, OPTIONAL);
// flags are in transid
// uint32_t flags = dhcp4_query->getTransid();
// do nothing with DHCPV4_QUERY_FLAGS_UNICAST
// Get the DHCPv4 message option
OptionPtr dhcp4_msg = dhcp4_query->getOption(D6O_DHCPV4_MSG);
if (dhcp4_msg) {
// Forward the whole message to the DHCPv4 server via IPC
Dhcp6to4Ipc::instance().send(dhcp4_query);
}
// This method does not return anything as we always sent back
// the response via Dhcp6To4Ipc.
}
void Dhcpv6Srv::classifyByVendor(const Pkt6Ptr& pkt, std::string& classes) {
OptionVendorClassPtr vclass = boost::dynamic_pointer_cast<
OptionVendorClass>(pkt->getOption(D6O_VENDOR_CLASS));
......@@ -3072,6 +3096,13 @@ void Dhcpv6Srv::processStatsReceived(const Pkt6Ptr& query) {
case DHCPV6_INFORMATION_REQUEST:
stat_name = "pkt6-infrequest-received";
break;
case DHCPV6_DHCPV4_QUERY:
stat_name = "pkt6-dhcpv4-query-received";
break;
case DHCPV6_DHCPV4_RESPONSE:
// Should not happen, but let's keep a counter for it
stat_name = "pkt6-dhcpv4-response-received";
break;
default:
; // do nothing
}
......@@ -3092,6 +3123,9 @@ void Dhcpv6Srv::processStatsSent(const Pkt6Ptr& response) {
case DHCPV6_REPLY:
stat_name = "pkt6-reply-sent";
break;
case DHCPV6_DHCPV4_RESPONSE:
stat_name = "pkt6-dhcpv4-response-sent";
break;
default:
// That should never happen
return;
......@@ -3100,5 +3134,9 @@ void Dhcpv6Srv::processStatsSent(const Pkt6Ptr& response) {
StatsMgr::instance().addValue(stat_name, static_cast<int64_t>(1));
}
int Dhcpv6Srv::getHookIndexBuffer6Send() {
return (Hooks.hook_index_buffer6_send_);
}
};
};
......@@ -289,6 +289,16 @@ protected:
/// @return Reply message to be sent to the client.
Pkt6Ptr processInfRequest(const Pkt6Ptr& inf_request);
/// @brief Processes incoming DHCPv4-query message.
///
/// It always returns NULL, as there is nothing to be sent back to the
/// client at this time. The message was sent to DHCPv4 server using
/// @ref isc::dhcp::Dhcp6to4Ipc::handler()). We will send back a response
/// to the client once we get back DHCP4-REPLY from the DHCPv4 server.
///
/// @param dhcp4_query message received from client
void processDhcp4Query(const Pkt6Ptr& dhcp4_query);
/// @brief Selects a subnet for a given client's packet.
///
/// @param question client's message
......@@ -781,12 +791,19 @@ private:
/// @param query packet received
static void processStatsReceived(const Pkt6Ptr& query);
/// UDP port number on which server listens.
uint16_t port_;
public:
/// @note used by DHCPv4-over-DHCPv6 so must be public and static
/// @brief Updates statistics for transmitted packets
/// @param query packet transmitted
/// @param response packet transmitted
static void processStatsSent(const Pkt6Ptr& response);
/// UDP port number on which server listens.
uint16_t port_;
/// @brief Returns the index of the buffer6_send hook
/// @return the index of the buffer6_send hook
static int getHookIndexBuffer6Send();
protected:
......
......@@ -8,10 +8,20 @@
#include <util/buffer.h>
#include <dhcp/iface_mgr.h>
#include <dhcp/pkt6.h>
#include <dhcpsrv/callout_handle_store.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcp6/dhcp6to4_ipc.h>
#include <dhcp6/dhcp6_log.h>
#include <dhcp6/dhcp6_srv.h>
#include <exceptions/exceptions.h>
#include <hooks/callout_handle.h>
#include <hooks/hooks_log.h>
#include <hooks/hooks_manager.h>
#include <stats/stats_mgr.h>
using namespace std;
using namespace isc::hooks;
namespace isc {
namespace dhcp {
......@@ -44,13 +54,30 @@ void Dhcp6to4Ipc::open() {
void Dhcp6to4Ipc::handler() {
Dhcp6to4Ipc& ipc = Dhcp6to4Ipc::instance();
Pkt6Ptr pkt;
try {
LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL, DHCP6_DHCP4O6_RECEIVING);
// Receive message from IPC.
pkt = ipc.receive();
if (pkt) {
LOG_DEBUG(packet6_logger, DBG_DHCP6_BASIC, DHCP6_DHCP4O6_PACKET_RECEIVED)
.arg(static_cast<int>(pkt->getType()))
.arg(pkt->getRemoteAddr().toText())
.arg(pkt->getIface());
}
} catch (const std::exception& e) {
LOG_DEBUG(packet6_logger,DBG_DHCP6_DETAIL, DHCP6_DHCP4O6_RECEIVE_FAIL)
.arg(e.what());
}
// Receive message from IPC.
Pkt6Ptr pkt = ipc.receive();
if (!pkt) {
return;
}
// Should we check it is a DHCPV6_DHCPV4_RESPONSE?
// 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.
......@@ -58,16 +85,62 @@ void Dhcp6to4Ipc::handler() {
buf.clear();
pkt->pack();
uint8_t msg_type = pkt->getType();
// Don't use getType(): get the message type from the buffer as we
// want to know if it is a relayed message (vs. internal message type).
// getType() always returns the type of internal message.
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);
}
// Forward packet to the client.
IfaceMgr::instance().send(pkt);
// processStatsSent(pkt);
// Can't call the pkt6_send callout because we don't have the query
// Copied from Dhcpv6Srv::run_one() sending part
try {
// Let's execute all callouts registered for buffer6_send
if (HooksManager::calloutsPresent(Dhcpv6Srv::getHookIndexBuffer6Send())) {
CalloutHandlePtr callout_handle = getCalloutHandle(pkt);
// Delete previously set arguments
callout_handle->deleteAllArguments();
// Pass incoming packet as argument
callout_handle->setArgument("response6", pkt);
// Call callouts
HooksManager::callCallouts(Dhcpv6Srv::getHookIndexBuffer6Send(),
*callout_handle);
// Callouts decided to skip the next processing step. The next
// processing step would to parse the packet, so skip at this
// stage means drop.
if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_BUFFER_SEND_SKIP)
.arg(pkt->getLabel());
return;
}
/// @todo: Add support for DROP status
callout_handle->getArgument("response6", pkt);
}
LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_RESPONSE_DATA)
.arg(static_cast<int>(pkt->getType())).arg(pkt->toText());
// Forward packet to the client.
IfaceMgr::instance().send(pkt);
// Update statistics accordingly for sent packet.
Dhcpv6Srv::processStatsSent(pkt);
} catch (const std::exception& e) {
LOG_ERROR(packet6_logger, DHCP6_DHCP4O6_SEND_FAIL).arg(e.what());
}
}
}; // namespace dhcp
......
......@@ -2585,6 +2585,13 @@ TEST_F(Dhcpv6SrvTest, receiveReplyStat) {
testReceiveStats(DHCPV6_REPLY, "pkt6-reply-received");
}
// Test checks if pkt6-dhcpv4-response-received is bumped up correctly.
// Note that in properly configured network the server never receives
// Dhcpv4-Response messages.
TEST_F(Dhcpv6SrvTest, receiveDhcpv4ResponseStat) {
testReceiveStats(DHCPV6_DHCPV4_RESPONSE, "pkt6-dhcpv4-response-received");
}
// Test checks if pkt6-unknown-received is bumped up correctly.
TEST_F(Dhcpv6SrvTest, receiveUnknownStat) {
testReceiveStats(123, "pkt6-unknown-received");
......@@ -2610,6 +2617,11 @@ TEST_F(Dhcpv6SrvTest, receiveDeclineStat) {
testReceiveStats(DHCPV6_DECLINE, "pkt6-decline-received");
}
// Test checks if pkt6-dhcpv4-query-received is bumped up correctly.
TEST_F(Dhcpv6SrvTest, receiveDhcpv4QueryStat) {
testReceiveStats(DHCPV6_DHCPV4_QUERY, "pkt6-dhcpv4-query-received");
}
// Test checks if reception of a malformed packet increases pkt-parse-failed
// and pkt6-receive-drop
TEST_F(Dhcpv6SrvTest, receiveParseFailedStat) {
......
......@@ -5,6 +5,7 @@
// 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>
......@@ -13,6 +14,11 @@
#include <dhcp6/dhcp6to4_ipc.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/testutils/dhcp4o6_test_ipc.h>
#include <stats/stats_mgr.h>
#include <hooks/callout_handle.h>
#include <hooks/hooks_manager.h>
#include <hooks/library_handle.h>
#include <gtest/gtest.h>
#include <stdint.h>
......@@ -20,6 +26,8 @@ using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
using namespace isc::stats;
using namespace isc::hooks;
using namespace isc::util;
namespace {
......@@ -42,6 +50,11 @@ public:
: iface_mgr_test_config_(true) {
IfaceMgr::instance().openSockets6();
configurePort(TEST_PORT);
// Install buffer6_send_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().
registerCallout("buffer6_send", buffer6_send_callout));
// Let's wipe all existing statistics.
StatsMgr::instance().removeAll();
}
/// @brief Configure DHCP4o6 port.
......@@ -57,12 +70,27 @@ public:
/// @return Pointer to the instance of the DHCPv4-query Message option.
OptionPtr createDHCPv4MsgOption() const;
/// @brief Handler for the buffer6_send hook
///
/// @param callout_handle handle passed by the hooks framework
/// @return always 0
static int
buffer6_send_callout(CalloutHandle& callout_handle) {
callout_handle.getArgument("response6", callback_pkt_);
return (0);
}
/// @brief Response Pkt6 shared pointer returned in the callout
static Pkt6Ptr callback_pkt_;
private:
/// @brief Provides fake configuration of interfaces.
IfaceMgrTestConfig iface_mgr_test_config_;
};
Pkt6Ptr Dhcp6to4IpcTest::callback_pkt_;
void
Dhcp6to4IpcTest::configurePort(const uint16_t port) {
CfgMgr::instance().getStagingCfg()->setDhcp4o6Port(port);
......@@ -104,23 +132,29 @@ TEST_F(Dhcp6to4IpcTest, receive) {
ASSERT_NO_THROW(ipc.open());
ASSERT_NO_THROW(src_ipc.open());
// Get statistics
StatsMgr& mgr = StatsMgr::instance();
ObservationPtr pkt6_snd = mgr.getObservation("pkt6-sent");
ObservationPtr d4_resp = mgr.getObservation("pkt6-dhcpv4-response-sent");
EXPECT_FALSE(pkt6_snd);
EXPECT_FALSE(d4_resp);
// Create message to be sent over IPC.
Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_QUERY, 1234));
Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_RESPONSE, 1234));
pkt->addOption(createDHCPv4MsgOption());
pkt->setIface("eth0");
pkt->setRemoteAddr(IOAddress("2001:db8:1::123"));
ASSERT_NO_THROW(pkt->pack());
// Reset the callout cached packet
Dhcp6to4IpcTest::callback_pkt_.reset();
// 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();
// Get the forwarded packet from the callout
Pkt6Ptr forwarded = Dhcp6to4IpcTest::callback_pkt_;
ASSERT_TRUE(forwarded);
// Verify the packet received.
......@@ -129,7 +163,75 @@ TEST_F(Dhcp6to4IpcTest, receive) {
EXPECT_TRUE(forwarded->getOption(D6O_DHCPV4_MSG));
EXPECT_EQ("eth0", forwarded->getIface());
EXPECT_EQ("2001:db8:1::123", forwarded->getRemoteAddr().toText());
#endif
// Verify statistics
pkt6_snd = mgr.getObservation("pkt6-sent");
d4_resp = mgr.getObservation("pkt6-dhcpv4-response-sent");
ASSERT_TRUE(pkt6_snd);
ASSERT_TRUE(d4_resp);
EXPECT_EQ(1, pkt6_snd->getInteger().first);
EXPECT_EQ(1, d4_resp->getInteger().first);
}
// This test verifies that the DHCPv4 endpoint of the DHCPv4o6 IPC can
// receive relayed messages.
// This is currently not supported: it is a known defect addressed by #4296.
TEST_F(Dhcp6to4IpcTest, DISABLED_receiveRelayed) {
// 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());
// Get statistics
StatsMgr& mgr = StatsMgr::instance();
ObservationPtr pkt6_snd = mgr.getObservation("pkt6-sent");
ObservationPtr d4_resp = mgr.getObservation("pkt6-dhcpv4-response-sent");
EXPECT_FALSE(pkt6_snd);
EXPECT_FALSE(d4_resp);
// Create relayed message to be sent over IPC.
Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_RESPONSE, 1234));
pkt->addOption(createDHCPv4MsgOption());
Pkt6::RelayInfo relay;
relay.linkaddr_ = IOAddress("3000:1::1");
relay.peeraddr_ = IOAddress("fe80::1");
relay.msg_type_ = DHCPV6_RELAY_FORW;
relay.hop_count_ = 1;
pkt->relay_info_.push_back(relay);
pkt->setIface("eth0");
pkt->setRemoteAddr(IOAddress("2001:db8:1::123"));
ASSERT_NO_THROW(pkt->pack());
// Reset the callout cached packet
Dhcp6to4IpcTest::callback_pkt_.reset();
// 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));
// Get the forwarded packet from the callout
Pkt6Ptr forwarded = Dhcp6to4IpcTest::callback_pkt_;
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());
EXPECT_EQ(DHCP6_CLIENT_PORT, forwarded->getRemotePort());
// Verify statistics
pkt6_snd = mgr.getObservation("pkt6-sent");
d4_resp = mgr.getObservation("pkt6-dhcpv4-response-sent");
ASSERT_TRUE(pkt6_snd);
ASSERT_TRUE(d4_resp);
EXPECT_EQ(1, pkt6_snd->getInteger().first);
EXPECT_EQ(1, d4_resp->getInteger().first);
}
} // end of anonymous namespace
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment