BIND 10 trac2417, updated. b966ebcc19a55c4d01825010ee1e707ba95293e8 [2417] Append configured DHCPv6 options to server's response.
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Nov 5 08:43:29 UTC 2012
The branch, trac2417 has been updated
via b966ebcc19a55c4d01825010ee1e707ba95293e8 (commit)
from 38ebe09ee1c3ed6906f6a439e5ac9088b665420e (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit b966ebcc19a55c4d01825010ee1e707ba95293e8
Author: Marcin Siodelski <marcin at isc.org>
Date: Mon Nov 5 09:43:20 2012 +0100
[2417] Append configured DHCPv6 options to server's response.
-----------------------------------------------------------------------
Summary of changes:
src/bin/dhcp6/config_parser.cc | 2 +-
src/bin/dhcp6/dhcp6_messages.mes | 4 ++
src/bin/dhcp6/dhcp6_srv.cc | 69 ++++++++++++++----
src/bin/dhcp6/dhcp6_srv.h | 2 -
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 109 ++++++++++++++++++++++++++---
src/lib/dhcp/libdhcp++.cc | 5 ++
6 files changed, 166 insertions(+), 25 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index e5a2be3..de60697 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -602,7 +602,7 @@ private:
<< " be equal to zero. Option code '0' is reserved in"
<< " DHCPv6.");
} else if (option_code > std::numeric_limits<uint16_t>::max()) {
- isc_throw(Dhcp6ConfigError, "Parser error: value of 'code' must not"ciwtezcowy
+ isc_throw(Dhcp6ConfigError, "Parser error: value of 'code' must not"
<< " exceed " << std::numeric_limits<uint16_t>::max());
}
// Check the option name has been specified, is non-empty and does not
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index ffca69a..071a3c7 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -110,6 +110,10 @@ This is a debug message issued during the IPv6 DHCP server startup.
It lists some information about the parameters with which the server
is running.
+% DHCP6_NO_SUBNET_FOR_ADDRESS fail to find subnet for address: %1
+This warning message indicates that server does not support subnet
+that received DHCPv6 packet comes from.
+
% DHCP6_CONFIG_LOAD_FAIL failed to load configuration: %1
This critical error message indicates that the initial DHCPv6
configuration has failed. The server will start, but nothing will be
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 79b5823..6a3174b 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -24,11 +24,16 @@
#include <dhcp/option6_addrlst.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_ia.h>
+#include <dhcp/option6_int_array.h>
#include <dhcp/pkt6.h>
+#include <dhcp/subnet.h>
+#include <dhcp/cfgmgr.h>
#include <exceptions/exceptions.h>
#include <util/io_utilities.h>
#include <util/range_utilities.h>
+#include <boost/foreach.hpp>
+
using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
@@ -289,23 +294,63 @@ void Dhcpv6Srv::copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
// TODO: Should throw if there is no client-id (except anonymous INF-REQUEST)
}
-void Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr& /*question*/, Pkt6Ptr& answer) {
- // TODO: question is currently unused, but we need it at least to know
- // message type we are answering
-
+void Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
// add server-id
answer->addOption(getServerID());
-}
+ // Get the subnet object. It holds options to be sent to the client
+ // that belongs to the particular subnet.
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(question->getRemoteAddr());
+ // Warn if subnet is not supported and quit.
+ if (!subnet) {
+ LOG_WARN(dhcp6_logger, DHCP6_NO_SUBNET_FOR_ADDRESS)
+ .arg(question->getRemoteAddr().toText());
+ return;
+ }
+ // Add DNS_SERVERS option. It should have been configured.
+ const Subnet::OptionContainer& options = subnet->getOptions();
+ const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
+ const Subnet::OptionContainerTypeRange range =
+ idx.equal_range(D6O_NAME_SERVERS);
+ // In theory we may have multiple options with the same
+ // option code. They are not differentiated right now
+ // until support for option spaces is implemented.
+ // Until that's the case, simply add the first found option.
+ if (std::distance(range.first, range.second) > 0) {
+ answer->addOption(range.first->option);
+ }
+}
-void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& /*question*/, Pkt6Ptr& answer) {
- // TODO: question is currently unused, but we need to extract ORO from it
- // and act on its content. Now we just send DNS-SERVERS option.
+void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
+ // Get the subnet for a particular address.
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(question->getRemoteAddr());
+ if (!subnet) {
+ LOG_WARN(dhcp6_logger, DHCP6_NO_SUBNET_FOR_ADDRESS)
+ .arg(question->getRemoteAddr().toText());
+ return;
+ }
- // add dns-servers option
- boost::shared_ptr<Option> dnsservers(new Option6AddrLst(D6O_NAME_SERVERS,
- IOAddress(HARDCODED_DNS_SERVER)));
- answer->addOption(dnsservers);
+ // Client requests some options using ORO option. Try to
+ // get this option from client's message.
+ boost::shared_ptr<Option6IntArray<uint16_t> > option_oro =
+ boost::dynamic_pointer_cast<Option6IntArray<uint16_t> >(question->getOption(D6O_ORO));
+ // Option ORO not found. Don't do anything then.
+ if (!option_oro) {
+ return;
+ }
+ // Get the list of options that client requested.
+ const std::vector<uint16_t>& requested_opts = option_oro->getValues();
+ // Get the list of options configured for a subnet.
+ const Subnet::OptionContainer& options = subnet->getOptions();
+ const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
+ // Try to match requested options with those configured for a subnet.
+ // If match is found, append configured option to the answer message.
+ BOOST_FOREACH(uint16_t opt, requested_opts) {
+ const Subnet::OptionContainerTypeRange& range = idx.equal_range(opt);
+ BOOST_FOREACH(Subnet::OptionDescriptor desc, range) {
+ answer->addOption(desc.option);
+ }
+ }
}
void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index c87d8f3..254ee7c 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -171,8 +171,6 @@ protected:
/// @brief Appends requested options to server's answer.
///
/// Appends options requested by client to the server's answer.
- /// TODO: This method is currently a stub. It just appends DNS-SERVERS
- /// option.
///
/// @param question client's message
/// @param answer server's message (options will be added here)
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 2028706..3477a3a 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -20,9 +20,13 @@
#include <arpa/inet.h>
#include <gtest/gtest.h>
+#include <dhcp6/config_parser.h>
+#include <dhcp6/dhcp6_srv.h>
#include <dhcp/dhcp6.h>
#include <dhcp/option6_ia.h>
-#include <dhcp6/dhcp6_srv.h>
+#include <dhcp/option6_addrlst.h>
+#include <dhcp/option6_int_array.h>
+#include <config/ccsession.h>
#include <util/buffer.h>
#include <util/range_utilities.h>
#include <boost/scoped_ptr.hpp>
@@ -31,6 +35,9 @@ using namespace std;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::util;
+using namespace isc::data;
+using namespace isc::config;
+using namespace isc::asiolink;
// namespace has to be named, because friends are defined in Dhcpv6Srv class
// Maybe it should be isc::test?
@@ -53,11 +60,14 @@ public:
class Dhcpv6SrvTest : public ::testing::Test {
public:
- // these are empty for now, but let's keep them around
- Dhcpv6SrvTest() {
+ Dhcpv6SrvTest()
+ : rcode_(-1) {
}
~Dhcpv6SrvTest() {
};
+
+ int rcode_;
+ ConstElementPtr comment_;
};
TEST_F(Dhcpv6SrvTest, basic) {
@@ -150,10 +160,40 @@ TEST_F(Dhcpv6SrvTest, DUID) {
}
}
-TEST_F(Dhcpv6SrvTest, Solicit_basic) {
+TEST_F(Dhcpv6SrvTest, solicitBasic) {
+ ConstElementPtr x;
+ string config = "{ \"interface\": [ \"all\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ { "
+ " \"pool\": [ \"2001:db8:1234::/80\" ],"
+ " \"subnet\": \"2001:db8:1234::/64\", "
+ " \"option-data\": [ {"
+ " \"name\": \"OPTION_DNS_SERVERS\","
+ " \"code\": 23,"
+ " \"data\": \"2001 0DB8 1234 FFFF 0000 0000 0000 0001"
+ "2001 0DB8 1234 FFFF 0000 0000 0000 0002\""
+ " },"
+ " {"
+ " \"name\": \"OPTION_FOO\","
+ " \"code\": 1000,"
+ " \"data\": \"1234\""
+ " } ]"
+ " } ],"
+ "\"valid-lifetime\": 4000 }";
+
+ ElementPtr json = Element::fromJSON(config);
+
boost::scoped_ptr<NakedDhcpv6Srv> srv;
ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv()) );
+ EXPECT_NO_THROW(x = configureDhcp6Server(*srv, json));
+ ASSERT_TRUE(x);
+ comment_ = parseAnswer(rcode_, x);
+
+ ASSERT_EQ(0, rcode_);
+
// a dummy content for client-id
OptionBuffer clntDuid(32);
for (int i = 0; i < 32; i++) {
@@ -169,10 +209,10 @@ TEST_F(Dhcpv6SrvTest, Solicit_basic) {
sol->addOption(ia);
// Let's not send address in solicit yet
- // boost::shared_ptr<Option6IAAddr> addr(new Option6IAAddr(D6O_IAADDR,
- // IOAddress("2001:db8:1234:ffff::ffff"), 5001, 7001));
- // ia->addOption(addr);
- // sol->addOption(ia);
+ /* boost::shared_ptr<Option6IAAddr>
+ addr(new Option6IAAddr(D6O_IAADDR, IOAddress("2001:db8:1234:ffff::ffff"), 5001, 7001));
+ ia->addOption(addr);
+ sol->addOption(ia); */
// constructed very simple SOLICIT message with:
// - client-id option (mandatory)
@@ -191,15 +231,38 @@ TEST_F(Dhcpv6SrvTest, Solicit_basic) {
boost::shared_ptr<Pkt6> reply = srv->processSolicit(sol);
// check if we get response at all
- ASSERT_TRUE( reply != boost::shared_ptr<Pkt6>() );
+ ASSERT_TRUE(reply);
EXPECT_EQ( DHCPV6_ADVERTISE, reply->getType() );
EXPECT_EQ( 1234, reply->getTransid() );
+ // We have not requested option with code 1000 so it should not
+ // be included in the response.
+ ASSERT_FALSE(reply->getOption(1000));
+
+ // Let's now request option with code 1000.
+ // We expect that server will include this option in its reply.
+ boost::shared_ptr<Option6IntArray<uint16_t> >
+ option_oro(new Option6IntArray<uint16_t>(D6O_ORO));
+ // Create vector with one code equal to 1000.
+ std::vector<uint16_t> codes(1, 1000);
+ // Pass this code to option.
+ option_oro->setValues(codes);
+ // Append ORO to SOLICIT message.
+ sol->addOption(option_oro);
+
+ // Need to process SOLICIT again after requesting new option.
+ reply = srv->processSolicit(sol);
+ ASSERT_TRUE(reply);
+
+ EXPECT_EQ(DHCPV6_ADVERTISE, reply->getType());
+
OptionPtr tmp = reply->getOption(D6O_IA_NA);
ASSERT_TRUE( tmp );
- Option6IA* reply_ia = dynamic_cast<Option6IA*>(tmp.get());
+ boost::shared_ptr<Option6IA> reply_ia =
+ boost::dynamic_pointer_cast<Option6IA>(tmp);
+ ASSERT_TRUE(reply_ia);
EXPECT_EQ( 234, reply_ia->getIAID() );
// check that there's an address included
@@ -219,6 +282,32 @@ TEST_F(Dhcpv6SrvTest, Solicit_basic) {
ASSERT_EQ(tmp->len(), srv->getServerID()->len() );
EXPECT_TRUE(tmp->getData() == srv->getServerID()->getData());
+
+ tmp = reply->getOption(D6O_NAME_SERVERS);
+ ASSERT_TRUE(tmp);
+
+ boost::shared_ptr<Option6AddrLst> reply_nameservers =
+ boost::dynamic_pointer_cast<Option6AddrLst>(tmp);
+ ASSERT_TRUE(reply_nameservers);
+
+ Option6AddrLst::AddressContainer addrs = reply_nameservers->getAddresses();
+ ASSERT_EQ(2, addrs.size());
+ EXPECT_TRUE(addrs[0] == IOAddress("2001:db8:1234:FFFF::1"));
+ EXPECT_TRUE(addrs[1] == IOAddress("2001:db8:1234:FFFF::2"));
+
+ // There is a dummy option with code 1000 we requested from a server.
+ // Expect that this option is in server's response.
+ tmp = reply->getOption(1000);
+ ASSERT_TRUE(tmp);
+
+ // Check that the option contains valid data (from configuration).
+ std::vector<uint8_t> data = tmp->getData();
+ ASSERT_EQ(2, data.size());
+
+ const uint8_t foo_expected[] = {
+ 0x12, 0x34
+ };
+ EXPECT_EQ(0, memcmp(&data[0], foo_expected, 2));
// more checks to be implemented
}
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index 30b1850..4f58c8f 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -106,6 +106,11 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
buf.begin() + offset,
buf.begin() + offset + opt_len));
break;
+ case D6O_ORO:
+ opt = OptionPtr(new Option6IntArray<uint16_t>(opt_type,
+ buf.begin() + offset,
+ buf.begin() + offset + opt_len));
+ break;
default:
opt = OptionPtr(new Option(Option::V6,
opt_type,
More information about the bind10-changes
mailing list