Commit b3c8a079 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac3336'

parents 83816e00 2f8ff64f
......@@ -21,8 +21,13 @@
# to start REBIND procedure (emergency renewal that allows switching
# to a different server).
"valid-lifetime": 4000,
"renew-timer": 1000,
"rebind-timer": 2000,
# Renew and rebind timers are commented out. This implies that options
# 58 and 59 will not be sent to the client. In this case it is up to
# the client to pick the timer values according to RFC2131. Uncomment the
# timers to send these options to the client.
# "renew-timer": 1000,
# "rebind-timer": 2000,
# The following list defines subnets. We have only one subnet
# here.
......
......@@ -2191,6 +2191,11 @@ Dhcp4/dhcp-ddns/qualifying-suffix "example.com" string
and apply to all defined subnets, unless they are overridden on a
per-subnet basis.
</para>
<para>
The renew-timer and rebind-timer are optional. If they are not specified,
the DHCPv4 options 58 and 59 are not sent in the server's response to the
client.
</para>
<section>
<title>Default storage for leases</title>
......
......@@ -32,13 +32,13 @@
{ "item_name": "renew-timer",
"item_type": "integer",
"item_optional": false,
"item_optional": true,
"item_default": 1000
},
{ "item_name": "rebind-timer",
"item_type": "integer",
"item_optional": false,
"item_optional": true,
"item_default": 2000
},
......
......@@ -1044,15 +1044,28 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
}
// IP Address Lease time (type 51)
opt = OptionPtr(new Option(Option::V4, DHO_DHCP_LEASE_TIME));
opt->setUint32(lease->valid_lft_);
opt.reset(new OptionUint32(Option::V4, DHO_DHCP_LEASE_TIME,
lease->valid_lft_));
answer->addOption(opt);
// Subnet mask (type 1)
answer->addOption(getNetmaskOption(subnet));
/// @todo: send renew timer option (T1, option 58)
/// @todo: send rebind timer option (T2, option 59)
// renewal-timer (type 58)
if (!subnet->getT1().unspecified()) {
OptionUint32Ptr t1(new OptionUint32(Option::V4,
DHO_DHCP_RENEWAL_TIME,
subnet->getT1()));
answer->addOption(t1);
}
// rebind timer (type 59)
if (!subnet->getT2().unspecified()) {
OptionUint32Ptr t2(new OptionUint32(Option::V4,
DHO_DHCP_REBINDING_TIME,
subnet->getT2()));
answer->addOption(t2);
}
// Create NameChangeRequests if DDNS is enabled and this is a
// real allocation.
......
......@@ -281,24 +281,32 @@ protected:
/// @param addr is IPv4 address of the subnet.
/// @param len is the prefix length
void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) {
// Get all 'time' parameters using inheritance.
// If the subnet-specific value is defined then use it, else
// use the global value. The global value must always be
// present. If it is not, it is an internal error and exception
// is thrown.
Triplet<uint32_t> t1 = getParam("renew-timer");
Triplet<uint32_t> t2 = getParam("rebind-timer");
// The renew-timer and rebind-timer are optional. If not set, the
// option 58 and 59 will not be sent to a client. In this case the
// client will use default values based on the valid-lifetime.
Triplet<uint32_t> t1 = getOptionalParam("renew-timer");
Triplet<uint32_t> t2 = getOptionalParam("rebind-timer");
// The valid-lifetime is mandatory. It may be specified for a
// particular subnet. If not, the global value should be present.
// If there is no global value, exception is thrown.
Triplet<uint32_t> valid = getParam("valid-lifetime");
// Subnet ID is optional. If it is not supplied the value of 0 is used,
// which means autogenerate.
SubnetID subnet_id =
static_cast<SubnetID>(uint32_values_->getOptionalParam("id", 0));
stringstream tmp;
tmp << addr << "/" << (int)len
<< " with params t1=" << t1 << ", t2=" << t2 << ", valid=" << valid;
stringstream s;
s << addr << "/" << static_cast<int>(len) << " with params: ";
// t1 and t2 are optional may be not specified.
if (!t1.unspecified()) {
s << "t1=" << t1 << ", ";
}
if (!t2.unspecified()) {
s << "t2=" << t2 << ", ";
}
s <<"valid-lifetime=" << valid;
LOG_INFO(dhcp4_logger, DHCP4_CONFIG_NEW_SUBNET).arg(tmp.str());
LOG_INFO(dhcp4_logger, DHCP4_CONFIG_NEW_SUBNET).arg(s.str());
Subnet4Ptr subnet4(new Subnet4(addr, len, t1, t2, valid, subnet_id));
subnet_ = subnet4;
......
......@@ -79,6 +79,8 @@ public:
// is sane.
srv_.reset(new Dhcpv4Srv(0));
CfgMgr::instance().deleteActiveIfaces();
// Create fresh context.
globalContext()->copyContext(ParserContext(Option::V4));
}
// Check that no hooks libraries are loaded. This is a pre-condition for
......@@ -510,6 +512,73 @@ TEST_F(Dhcp4ParserTest, emptySubnet) {
checkGlobalUint32("valid-lifetime", 4000);
}
/// Check that the renew-timer doesn't have to be specified, in which case
/// it is marked unspecified in the Subnet.
TEST_F(Dhcp4ParserTest, unspecifiedRenewTimer) {
ConstElementPtr status;
string config = "{ \"interfaces\": [ \"*\" ],"
"\"rebind-timer\": 2000, "
"\"subnet4\": [ { "
" \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
// returned value should be 0 (success)
checkResult(status, 0);
checkGlobalUint32("rebind-timer", 2000);
checkGlobalUint32("valid-lifetime", 4000);
Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"),
classify_);
ASSERT_TRUE(subnet);
EXPECT_TRUE(subnet->getT1().unspecified());
EXPECT_FALSE(subnet->getT2().unspecified());
EXPECT_EQ(2000, subnet->getT2());
EXPECT_EQ(4000, subnet->getValid());
// Check that subnet-id is 1
EXPECT_EQ(1, subnet->getID());
}
/// Check that the rebind-timer doesn't have to be specified, in which case
/// it is marked unspecified in the Subnet.
TEST_F(Dhcp4ParserTest, unspecifiedRebindTimer) {
ConstElementPtr status;
string config = "{ \"interfaces\": [ \"*\" ],"
"\"renew-timer\": 1000, "
"\"subnet4\": [ { "
" \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
// returned value should be 0 (success)
checkResult(status, 0);
checkGlobalUint32("renew-timer", 1000);
checkGlobalUint32("valid-lifetime", 4000);
Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"),
classify_);
ASSERT_TRUE(subnet);
EXPECT_FALSE(subnet->getT1().unspecified());
EXPECT_EQ(1000, subnet->getT1());
EXPECT_TRUE(subnet->getT2().unspecified());
EXPECT_EQ(4000, subnet->getValid());
// Check that subnet-id is 1
EXPECT_EQ(1, subnet->getID());
}
/// The goal of this test is to verify if defined subnet uses global
/// parameter timer definitions.
TEST_F(Dhcp4ParserTest, subnetGlobalDefaults) {
......@@ -1700,6 +1769,7 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) {
// belongs to the 'dhcp4' option space as it is the
// standard option.
string config = "{ \"interfaces\": [ \"*\" ],"
"\"valid-lifetime\": 4000,"
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"option-data\": [ {"
......@@ -1779,6 +1849,7 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
// Starting stage 1. Configure sub-options and their definitions.
string config = "{ \"interfaces\": [ \"*\" ],"
"\"valid-lifetime\": 4000,"
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"option-data\": [ {"
......@@ -2352,6 +2423,7 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
// that we will add to the base option.
// Let's create some dummy options: foo and foo2.
string config = "{ \"interfaces\": [ \"*\" ],"
"\"valid-lifetime\": 4000,"
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"option-data\": [ {"
......@@ -2512,6 +2584,7 @@ TEST_F(Dhcp4ParserTest, vendorOptionsHex) {
// sharing the code 1 and belonging to the different vendor spaces.
// (different vendor-id values).
string config = "{ \"interfaces\": [ \"*\" ],"
"\"valid-lifetime\": 4000,"
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"option-data\": [ {"
......@@ -2570,6 +2643,7 @@ TEST_F(Dhcp4ParserTest, vendorOptionsCsv) {
// sharing the code 1 and belonging to the different vendor spaces.
// (different vendor-id values).
string config = "{ \"interfaces\": [ \"*\" ],"
"\"valid-lifetime\": 4000,"
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"option-data\": [ {"
......@@ -2646,6 +2720,7 @@ buildHooksLibrariesConfig(const std::vector<std::string>& libraries) {
// Append the remainder of the configuration.
config += string(
"],"
"\"valid-lifetime\": 4000,"
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"option-data\": [ {"
......
......@@ -558,13 +558,51 @@ TEST_F(Dhcpv4SrvTest, DiscoverBasic) {
// Check that address was returned from proper range, that its lease
// lifetime is correct, that T1 and T2 are returned properly
checkAddressParams(offer, subnet_);
checkAddressParams(offer, subnet_, true, true);
// Check identifiers
checkServerId(offer, srv->getServerID());
checkClientId(offer, clientid);
}
// Check that option 58 and 59 are not included if they are not specified.
TEST_F(Dhcpv4SrvTest, DiscoverNoTimers) {
IfaceMgrTestConfig test_config(true);
IfaceMgr::instance().openSockets4();
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
dis->setRemoteAddr(IOAddress("192.0.2.1"));
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
dis->setIface("eth1");
// Recreate a subnet but set T1 and T2 to "unspecified".
subnet_.reset(new Subnet4(IOAddress("192.0.2.0"), 24,
Triplet<uint32_t>(),
Triplet<uint32_t>(),
3000));
pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"),
IOAddress("192.0.2.110")));
subnet_->addPool(pool_);
CfgMgr::instance().deleteSubnets4();
CfgMgr::instance().addSubnet4(subnet_);
// Pass it to the server and get an offer
Pkt4Ptr offer = srv->processDiscover(dis);
// Check if we get response at all
checkResponse(offer, DHCPOFFER, 1234);
// T1 and T2 timers must not be present.
checkAddressParams(offer, subnet_, false, false);
// Check identifiers
checkServerId(offer, srv->getServerID());
checkClientId(offer, clientid);
}
// This test verifies that incoming DISCOVER can be handled properly, that an
// OFFER is generated, that the response has an address and that address
......@@ -601,7 +639,7 @@ TEST_F(Dhcpv4SrvTest, DiscoverHint) {
// Check that address was returned from proper range, that its lease
// lifetime is correct, that T1 and T2 are returned properly
checkAddressParams(offer, subnet_);
checkAddressParams(offer, subnet_, true, true);
EXPECT_EQ(offer->getYiaddr(), hint);
......@@ -644,7 +682,7 @@ TEST_F(Dhcpv4SrvTest, DiscoverNoClientId) {
// Check that address was returned from proper range, that its lease
// lifetime is correct, that T1 and T2 are returned properly
checkAddressParams(offer, subnet_);
checkAddressParams(offer, subnet_, true, true);
EXPECT_EQ(offer->getYiaddr(), hint);
......@@ -687,7 +725,7 @@ TEST_F(Dhcpv4SrvTest, DiscoverInvalidHint) {
// Check that address was returned from proper range, that its lease
// lifetime is correct, that T1 and T2 are returned properly
checkAddressParams(offer, subnet_);
checkAddressParams(offer, subnet_, true, true);
EXPECT_NE(offer->getYiaddr(), hint);
......@@ -750,9 +788,9 @@ TEST_F(Dhcpv4SrvTest, ManyDiscovers) {
IOAddress addr3 = offer3->getYiaddr();
// Check that the assigned address is indeed from the configured pool
checkAddressParams(offer1, subnet_);
checkAddressParams(offer2, subnet_);
checkAddressParams(offer3, subnet_);
checkAddressParams(offer1, subnet_, true, true);
checkAddressParams(offer2, subnet_, true, true);
checkAddressParams(offer3, subnet_, true, true);
// Check server-ids
checkServerId(offer1, srv->getServerID());
......@@ -840,7 +878,7 @@ TEST_F(Dhcpv4SrvTest, RequestBasic) {
// Check that address was returned from proper range, that its lease
// lifetime is correct, that T1 and T2 are returned properly
checkAddressParams(ack, subnet_);
checkAddressParams(ack, subnet_, true, true);
// Check identifiers
checkServerId(ack, srv->getServerID());
......@@ -853,6 +891,45 @@ TEST_F(Dhcpv4SrvTest, RequestBasic) {
LeaseMgrFactory::instance().deleteLease(l->addr_);
}
// Check that option 58 and 59 are not included if they are not specified.
TEST_F(Dhcpv4SrvTest, RequestNoTimers) {
IfaceMgrTestConfig test_config(true);
IfaceMgr::instance().openSockets4();
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
req->setRemoteAddr(IOAddress("192.0.2.1"));
OptionPtr clientid = generateClientId();
req->addOption(clientid);
req->setIface("eth1");
// Recreate a subnet but set T1 and T2 to "unspecified".
subnet_.reset(new Subnet4(IOAddress("192.0.2.0"), 24,
Triplet<uint32_t>(),
Triplet<uint32_t>(),
3000));
pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"),
IOAddress("192.0.2.110")));
subnet_->addPool(pool_);
CfgMgr::instance().deleteSubnets4();
CfgMgr::instance().addSubnet4(subnet_);
// Pass it to the server and get an ACK.
Pkt4Ptr ack = srv->processRequest(req);
// Check if we get response at all
checkResponse(ack, DHCPACK, 1234);
// T1 and T2 timers must not be present.
checkAddressParams(ack, subnet_, false, false);
// Check identifiers
checkServerId(ack, srv->getServerID());
checkClientId(ack, clientid);
}
// This test verifies that incoming REQUEST can be handled properly, that an
// ACK is generated, that the response has an address and that address
// really belongs to the configured pool.
......@@ -927,9 +1004,9 @@ TEST_F(Dhcpv4SrvTest, ManyRequests) {
EXPECT_EQ(req_addr3, addr3);
// Check that the assigned address is indeed from the configured pool
checkAddressParams(ack1, subnet_);
checkAddressParams(ack2, subnet_);
checkAddressParams(ack3, subnet_);
checkAddressParams(ack1, subnet_, true, true);
checkAddressParams(ack2, subnet_, true, true);
checkAddressParams(ack3, subnet_, true, true);
// Check DUIDs
checkServerId(ack1, srv->getServerID());
......@@ -1049,7 +1126,7 @@ TEST_F(Dhcpv4SrvTest, RenewBasic) {
// Check that address was returned from proper range, that its lease
// lifetime is correct, that T1 and T2 are returned properly
checkAddressParams(ack, subnet_);
checkAddressParams(ack, subnet_, true, true);
// Check identifiers
checkServerId(ack, srv->getServerID());
......
......@@ -20,6 +20,7 @@
#include <dhcp4/json_config_parser.h>
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <dhcp/option4_addrlst.h>
#include <dhcp/option_int.h>
#include <dhcp/option_int_array.h>
#include <dhcp/option_custom.h>
#include <dhcp/iface_mgr.h>
......@@ -238,9 +239,10 @@ HWAddrPtr Dhcpv4SrvTest::generateHWAddr(size_t size /*= 6*/) {
return (HWAddrPtr(new HWAddr(mac, hw_type)));
}
void Dhcpv4SrvTest::checkAddressParams(const Pkt4Ptr& rsp, const SubnetPtr subnet,
bool t1_mandatory /*= false*/,
bool t2_mandatory /*= false*/) {
void Dhcpv4SrvTest::checkAddressParams(const Pkt4Ptr& rsp,
const SubnetPtr subnet,
bool t1_present,
bool t2_present) {
// Technically inPool implies inRange, but let's be on the safe
// side and check both.
......@@ -248,31 +250,35 @@ void Dhcpv4SrvTest::checkAddressParams(const Pkt4Ptr& rsp, const SubnetPtr subne
EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, rsp->getYiaddr()));
// Check lease time
OptionPtr opt = rsp->getOption(DHO_DHCP_LEASE_TIME);
OptionUint32Ptr opt = boost::dynamic_pointer_cast<
OptionUint32>(rsp->getOption(DHO_DHCP_LEASE_TIME));
if (!opt) {
ADD_FAILURE() << "Lease time option missing in response";
ADD_FAILURE() << "Lease time option missing in response or the"
" option has unexpected type";
} else {
EXPECT_EQ(opt->getUint32(), subnet->getValid());
EXPECT_EQ(opt->getValue(), subnet->getValid());
}
// Check T1 timer
opt = rsp->getOption(DHO_DHCP_RENEWAL_TIME);
if (opt) {
EXPECT_EQ(opt->getUint32(), subnet->getT1());
opt = boost::dynamic_pointer_cast<
OptionUint32>(rsp->getOption(DHO_DHCP_RENEWAL_TIME));
if (t1_present) {
ASSERT_TRUE(opt) << "Required T1 option missing or it has"
" an unexpected type";
EXPECT_EQ(opt->getValue(), subnet->getT1());
} else {
if (t1_mandatory) {
ADD_FAILURE() << "Required T1 option missing";
}
EXPECT_FALSE(opt);
}
// Check T2 timer
opt = rsp->getOption(DHO_DHCP_REBINDING_TIME);
if (opt) {
EXPECT_EQ(opt->getUint32(), subnet->getT2());
opt = boost::dynamic_pointer_cast<
OptionUint32>(rsp->getOption(DHO_DHCP_REBINDING_TIME));
if (t2_present) {
ASSERT_TRUE(opt) << "Required T2 option missing or it has"
" an unexpected type";
EXPECT_EQ(opt->getValue(), subnet->getT2());
} else {
if (t2_mandatory) {
ADD_FAILURE() << "Required T2 option missing";
}
EXPECT_FALSE(opt);
}
}
......
......@@ -290,11 +290,13 @@ public:
/// @param rsp response to be checked
/// @param subnet subnet that should be used to verify assigned address
/// and options
/// @param t1_mandatory is T1 mandatory?
/// @param t2_mandatory is T2 mandatory?
/// @param t1_present check that t1 must be present (true) or must not be
/// present (false)
/// @param t2_present check that t2 must be present (true) or must not be
/// present (false)
void checkAddressParams(const Pkt4Ptr& rsp, const SubnetPtr subnet,
bool t1_mandatory = false,
bool t2_mandatory = false);
bool t1_present = false,
bool t2_present = false);
/// @brief Basic checks for generated response (message type and trans-id).
///
......
......@@ -358,6 +358,8 @@ public:
// all interfaces before each test and later check that this setting
// has been overriden by the configuration used in the test.
CfgMgr::instance().deleteActiveIfaces();
// Create fresh context.
globalContext()->copyContext(ParserContext(Option::V6));
}
/// @brief Test invalid option parameter value.
......@@ -2002,6 +2004,8 @@ TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) {
// belongs to the 'dhcp6' option space as it is the
// standard option.
string config = "{ \"interfaces\": [ \"*\" ],"
"\"preferred-lifetime\": 3000,"
"\"valid-lifetime\": 4000,"
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"option-data\": [ {"
......@@ -2081,6 +2085,8 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
// Starting stage 1. Configure sub-options and their definitions.
string config = "{ \"interfaces\": [ \"*\" ],"
"\"preferred-lifetime\": 3000,"
"\"valid-lifetime\": 4000,"
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"option-data\": [ {"
......@@ -2540,6 +2546,8 @@ TEST_F(Dhcp6ParserTest, vendorOptionsHex) {
// sharing the code 1 and belonging to the different vendor spaces.
// (different vendor-id values).
string config = "{ \"interfaces\": [ \"*\" ],"
"\"preferred-lifetime\": 3000,"
"\"valid-lifetime\": 4000,"
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"option-data\": [ {"
......@@ -2598,6 +2606,8 @@ TEST_F(Dhcp6ParserTest, vendorOptionsCsv) {
// sharing the code 1 and belonging to the different vendor spaces.
// (different vendor-id values).
string config = "{ \"interfaces\": [ \"*\" ],"
"\"preferred-lifetime\": 3000,"
"\"valid-lifetime\": 4000,"
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"option-data\": [ {"
......@@ -2658,6 +2668,8 @@ TEST_F(Dhcp6ParserTest, stdOptionDataEncapsulate) {
// that we will add to the base option.
// Let's create some dummy options: foo and foo2.
string config = "{ \"interfaces\": [ \"*\" ],"
"\"preferred-lifetime\": 3000,"
"\"valid-lifetime\": 4000,"
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"option-data\": [ {"
......@@ -3157,6 +3169,8 @@ TEST_F(Dhcp6ParserTest, d2ClientConfig) {
ASSERT_FALSE(CfgMgr::instance().ddnsEnabled());
string config_str = "{ \"interfaces\": [ \"*\" ],"
"\"preferred-lifetime\": 3000,"
"\"valid-lifetime\": 4000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ { "
......
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2014 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
......@@ -25,6 +25,22 @@
namespace isc {
namespace dhcp {
template<typename T>
class OptionInt;
/// @defgroup option_int_array_defs Typedefs for OptionInt class.
///
/// @brief Classes that represent options comprising an integer.
///
/// @{
typedef OptionInt<uint8_t> OptionUint8;
typedef boost::shared_ptr<OptionUint8> OptionUint8Ptr;
typedef OptionInt<uint16_t> OptionUint16;
typedef boost::shared_ptr<OptionUint16> OptionUint16Ptr;
typedef OptionInt<uint32_t> OptionUint32;
typedef boost::shared_ptr<OptionUint32> OptionUint32Ptr;
/// @}
/// This template class represents DHCP option with single value.
/// This value is of integer type and can be any of the following:
/// - uint8_t,
......
......@@ -1336,6 +1336,16 @@ SubnetConfigParser::getParam(const std::string& name) {
return (Triplet<uint32_t>(value));