Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Open sidebar
Sebastian Schrader
Kea
Commits
556f6591
Commit
556f6591
authored
Jul 03, 2017
by
Francis Dupont
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[5288] Added option-data in DHCPv4 pools
parent
f5e88bca
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
523 additions
and
61 deletions
+523
-61
doc/examples/kea4/multiple-options.json
doc/examples/kea4/multiple-options.json
+14
-4
doc/guide/dhcp4-srv.xml
doc/guide/dhcp4-srv.xml
+51
-0
src/bin/dhcp4/tests/config_parser_unittest.cc
src/bin/dhcp4/tests/config_parser_unittest.cc
+151
-0
src/bin/dhcp4/tests/get_config_unittest.cc
src/bin/dhcp4/tests/get_config_unittest.cc
+250
-48
src/bin/dhcp4/tests/get_config_unittest.cc.skel
src/bin/dhcp4/tests/get_config_unittest.cc.skel
+1
-1
src/lib/dhcpsrv/cfg_subnets4.cc
src/lib/dhcpsrv/cfg_subnets4.cc
+3
-1
src/lib/dhcpsrv/parsers/dhcp_parsers.cc
src/lib/dhcpsrv/parsers/dhcp_parsers.cc
+0
-6
src/lib/dhcpsrv/tests/pool_unittest.cc
src/lib/dhcpsrv/tests/pool_unittest.cc
+53
-1
No files found.
doc/examples/kea4/multiple-options.json
View file @
556f6591
...
...
@@ -32,9 +32,11 @@
//
clients
connected
to
this
subnet.
The
first
two
options
are
//
identified
by
the
name.
The
third
option
is
identified
by
the
//
option
code.
//
There
is
an
address
pool
defined
within
this
subnet.
Pool
//
specific
value
for
option
domain-name-servers
is
defined
//
for
the
pool.
"subnet4"
:
[
{
"pools"
:
[
{
"pool"
:
"192.0.2.10 - 192.0.2.200"
}
],
"subnet"
:
"192.0.2.0/24"
,
"interface"
:
"ethX"
,
"option-data"
:
[
...
...
@@ -124,9 +126,17 @@
"
name
": "
default-ip-ttl
",
"
data
": "
0
xf
0
"
}
]
}
]
],
"
pools
": [ {
"
pool
": "
192.0
.
2.10
-
192.0
.
2.200
",
"
option-data
": [
{
"
name
": "
domain-name-servers
",
"
data
": "
192.0
.
2.3
,
192.0
.
2.4
"
}
]
} ]
} ]
},
// The following configures logging. It assumes that messages with at
...
...
doc/guide/dhcp4-srv.xml
View file @
556f6591
...
...
@@ -1057,6 +1057,57 @@ temporarily override a list of interface names and listen on all interfaces.
</screen>
</para>
<para>
In some cases it is useful to associate some options with an
address pool from which a client is assigned a lease. Pool
specific option values override subnet specific and global
option values. If the client is assigned multiple leases from
different pools, the server will assign options from all pools
from which the leases have been obtained. However, if the
particular option is specified in multiple pools from which
the client obtains the leases, only one instance of this
option will be handed out to the client. The server's
administrator must not try to prioritize assignment of pool
specific options by trying to order pools declarations in the
server configuration. Future Kea releases may change the order
in which options are assigned from the pools without any
notice.
</para>
<para>
The following configuration snippet demonstrates how to specify the
DNS servers option, which will be assigned to a client only if the
client obtains an address from the given pool:
<screen>
"Dhcp4": {
"subnet4": [
{
"pools": [
{
"pool": "192.0.2.1 - 192.0.2.200",
<userinput>
"option-data": [
{
"name": "domain-name-servers",
"code": 6,
"space": "dhcp4",
"csv-format": true,
"data": "192.0.2.3"
},
...
]
</userinput>
,
...
},
...
],
...
},
...
],
...
}
</screen>
</para>
<para>
The currently supported standard DHCPv4 options are
listed in
<xref
linkend=
"dhcp4-std-options-list"
/>
.
...
...
src/bin/dhcp4/tests/config_parser_unittest.cc
View file @
556f6591
...
...
@@ -2739,6 +2739,157 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
testOption
(
*
range2
.
first
,
23
,
foo2_expected
,
sizeof
(
foo2_expected
));
}
// This test verifies that it is possible to specify options on
// pool levels.
TEST_F
(
Dhcp4ParserTest
,
optionDataSinglePool
)
{
ConstElementPtr
x
;
string
config
=
"{ "
+
genIfaceConfig
()
+
","
"
\"
rebind-timer
\"
: 2000, "
"
\"
renew-timer
\"
: 1000, "
"
\"
subnet4
\"
: [ { "
"
\"
pools
\"
: [ { "
"
\"
pool
\"
:
\"
192.0.2.1 - 192.0.2.100
\"
,"
"
\"
option-data
\"
: [ {"
"
\"
name
\"
:
\"
dhcp-message
\"
,"
"
\"
data
\"
:
\"
ABCDEF0105
\"
,"
"
\"
csv-format
\"
: false"
" },"
" {"
"
\"
name
\"
:
\"
default-ip-ttl
\"
,"
"
\"
data
\"
:
\"
01
\"
,"
"
\"
csv-format
\"
: false"
" } ]"
" } ],"
"
\"
subnet
\"
:
\"
192.0.2.0/24
\"
"
" } ],"
"
\"
valid-lifetime
\"
: 4000 }"
;
ConstElementPtr
json
;
ASSERT_NO_THROW
(
json
=
parseDHCP4
(
config
));
extractConfig
(
config
);
EXPECT_NO_THROW
(
x
=
configureDhcp4Server
(
*
srv_
,
json
));
checkResult
(
x
,
0
);
Subnet4Ptr
subnet
=
CfgMgr
::
instance
().
getStagingCfg
()
->
getCfgSubnets4
()
->
selectSubnet
(
IOAddress
(
"192.0.2.24"
),
classify_
);
ASSERT_TRUE
(
subnet
);
PoolPtr
pool
=
subnet
->
getPool
(
Lease
::
TYPE_V4
,
IOAddress
(
"192.0.2.24"
),
false
);
ASSERT_TRUE
(
pool
);
Pool4Ptr
pool4
=
boost
::
dynamic_pointer_cast
<
Pool4
>
(
pool
);
ASSERT_TRUE
(
pool4
);
OptionContainerPtr
options
=
pool4
->
getCfgOption
()
->
getAll
(
"dhcp4"
);
ASSERT_EQ
(
2
,
options
->
size
());
// Get the search index. Index #1 is to search using option code.
const
OptionContainerTypeIndex
&
idx
=
options
->
get
<
1
>
();
// Get the options for specified index. Expecting one option to be
// returned but in theory we may have multiple options with the same
// code so we get the range.
std
::
pair
<
OptionContainerTypeIndex
::
const_iterator
,
OptionContainerTypeIndex
::
const_iterator
>
range
=
idx
.
equal_range
(
56
);
// Expect a single option with the code equal to 100.
ASSERT_EQ
(
1
,
std
::
distance
(
range
.
first
,
range
.
second
));
const
uint8_t
foo_expected
[]
=
{
0xAB
,
0xCD
,
0xEF
,
0x01
,
0x05
};
// Check if option is valid in terms of code and carried data.
testOption
(
*
range
.
first
,
56
,
foo_expected
,
sizeof
(
foo_expected
));
range
=
idx
.
equal_range
(
23
);
ASSERT_EQ
(
1
,
std
::
distance
(
range
.
first
,
range
.
second
));
// Do another round of testing with second option.
const
uint8_t
foo2_expected
[]
=
{
0x01
};
testOption
(
*
range
.
first
,
23
,
foo2_expected
,
sizeof
(
foo2_expected
));
}
TEST_F
(
Dhcp4ParserTest
,
optionDataMultiplePools
)
{
ConstElementPtr
x
;
string
config
=
"{ "
+
genIfaceConfig
()
+
","
"
\"
rebind-timer
\"
: 2000, "
"
\"
renew-timer
\"
: 1000, "
"
\"
subnet4
\"
: [ { "
"
\"
pools
\"
: [ { "
"
\"
pool
\"
:
\"
192.0.2.1 - 192.0.2.100
\"
,"
"
\"
option-data
\"
: [ {"
"
\"
name
\"
:
\"
dhcp-message
\"
,"
"
\"
data
\"
:
\"
ABCDEF0105
\"
,"
"
\"
csv-format
\"
: false"
" } ]"
" },"
" {"
"
\"
pool
\"
:
\"
192.0.2.200 - 192.0.2.250
\"
,"
"
\"
option-data
\"
: [ {"
"
\"
name
\"
:
\"
default-ip-ttl
\"
,"
"
\"
data
\"
:
\"
01
\"
,"
"
\"
csv-format
\"
: false"
" } ]"
" } ],"
"
\"
subnet
\"
:
\"
192.0.2.0/24
\"
"
" } ],"
"
\"
valid-lifetime
\"
: 4000 }"
;
ConstElementPtr
json
;
ASSERT_NO_THROW
(
json
=
parseDHCP4
(
config
));
extractConfig
(
config
);
EXPECT_NO_THROW
(
x
=
configureDhcp4Server
(
*
srv_
,
json
));
checkResult
(
x
,
0
);
Subnet4Ptr
subnet
=
CfgMgr
::
instance
().
getStagingCfg
()
->
getCfgSubnets4
()
->
selectSubnet
(
IOAddress
(
"192.0.2.24"
),
classify_
);
ASSERT_TRUE
(
subnet
);
PoolPtr
pool1
=
subnet
->
getPool
(
Lease
::
TYPE_V4
,
IOAddress
(
"192.0.2.24"
),
false
);
ASSERT_TRUE
(
pool1
);
Pool4Ptr
pool41
=
boost
::
dynamic_pointer_cast
<
Pool4
>
(
pool1
);
ASSERT_TRUE
(
pool41
);
OptionContainerPtr
options1
=
pool41
->
getCfgOption
()
->
getAll
(
"dhcp4"
);
ASSERT_EQ
(
1
,
options1
->
size
());
// Get the search index. Index #1 is to search using option code.
const
OptionContainerTypeIndex
&
idx1
=
options1
->
get
<
1
>
();
// Get the options for specified index. Expecting one option to be
// returned but in theory we may have multiple options with the same
// code so we get the range.
std
::
pair
<
OptionContainerTypeIndex
::
const_iterator
,
OptionContainerTypeIndex
::
const_iterator
>
range1
=
idx1
.
equal_range
(
56
);
// Expect a single option with the code equal to 100.
ASSERT_EQ
(
1
,
std
::
distance
(
range1
.
first
,
range1
.
second
));
const
uint8_t
foo_expected
[]
=
{
0xAB
,
0xCD
,
0xEF
,
0x01
,
0x05
};
// Check if option is valid in terms of code and carried data.
testOption
(
*
range1
.
first
,
56
,
foo_expected
,
sizeof
(
foo_expected
));
// Test another pool in the same way.
PoolPtr
pool2
=
subnet
->
getPool
(
Lease
::
TYPE_V4
,
IOAddress
(
"192.0.2.240"
),
false
);
ASSERT_TRUE
(
pool2
);
Pool4Ptr
pool42
=
boost
::
dynamic_pointer_cast
<
Pool4
>
(
pool2
);
ASSERT_TRUE
(
pool42
);
OptionContainerPtr
options2
=
pool42
->
getCfgOption
()
->
getAll
(
"dhcp4"
);
ASSERT_EQ
(
1
,
options2
->
size
());
const
OptionContainerTypeIndex
&
idx2
=
options2
->
get
<
1
>
();
std
::
pair
<
OptionContainerTypeIndex
::
const_iterator
,
OptionContainerTypeIndex
::
const_iterator
>
range2
=
idx2
.
equal_range
(
23
);
ASSERT_EQ
(
1
,
std
::
distance
(
range2
.
first
,
range2
.
second
));
const
uint8_t
foo2_expected
[]
=
{
0x01
};
testOption
(
*
range2
.
first
,
23
,
foo2_expected
,
sizeof
(
foo2_expected
));
}
// Verify that empty option name is rejected in the configuration.
...
...
src/bin/dhcp4/tests/get_config_unittest.cc
View file @
556f6591
This diff is collapsed.
Click to expand it.
src/bin/dhcp4/tests/get_config_unittest.cc.skel
View file @
556f6591
...
...
@@ -340,6 +340,6 @@ TEST_P(Dhcp4GetConfigTest, run) {
/// Define the parametrized test loop
INSTANTIATE_TEST_CASE_P(Dhcp4GetConfigTest, Dhcp4GetConfigTest,
::testing::Range(
0UL
, max_config_counter));
::testing::Range(
static_cast<size_t>(0)
, max_config_counter));
};
src/lib/dhcpsrv/cfg_subnets4.cc
View file @
556f6591
...
...
@@ -335,7 +335,9 @@ CfgSubnets4::toElement() const {
if
(
!
isNull
(
context
))
{
pool_map
->
set
(
"user-context"
,
context
);
}
// Set pool options (not yet supported)
// Set pool options
ConstCfgOptionPtr
opts
=
(
*
pool
)
->
getCfgOption
();
pool_map
->
set
(
"option-data"
,
opts
->
toElement
());
// Push on the pool list
pool_list
->
add
(
pool_map
);
}
...
...
src/lib/dhcpsrv/parsers/dhcp_parsers.cc
View file @
556f6591
...
...
@@ -749,12 +749,6 @@ PoolParser::parse(PoolStoragePtr pools,
ConstElementPtr
option_data
=
pool_structure
->
get
(
"option-data"
);
if
(
option_data
)
{
try
{
// Currently we don't support specifying options for the DHCPv4 server.
if
(
address_family
==
AF_INET
)
{
isc_throw
(
DhcpConfigError
,
"option-data is not supported for DHCPv4"
" address pools"
);
}
CfgOptionPtr
cfg
=
pool
->
getCfgOption
();
OptionDataListParser
option_parser
(
address_family
);
option_parser
.
parse
(
cfg
,
option_data
);
...
...
src/lib/dhcpsrv/tests/pool_unittest.cc
View file @
556f6591
...
...
@@ -120,6 +120,58 @@ TEST(Pool4Test, toText) {
EXPECT_EQ
(
"type=V4, 192.0.2.128-192.0.2.143"
,
pool2
.
toText
());
}
// This test checks that it is possible to specify pool specific options.
TEST
(
Pool4Test
,
addOptions
)
{
// Create a pool to add options to it.
Pool4Ptr
pool
(
new
Pool4
(
IOAddress
(
"192.0.2.0"
),
IOAddress
(
"192.0.2.255"
)));
// Differentiate options by their codes (100-109)
for
(
uint16_t
code
=
100
;
code
<
110
;
++
code
)
{
OptionPtr
option
(
new
Option
(
Option
::
V4
,
code
,
OptionBuffer
(
10
,
0xFF
)));
ASSERT_NO_THROW
(
pool
->
getCfgOption
()
->
add
(
option
,
false
,
"dhcp4"
));
}
// Add 7 options to another option space. The option codes partially overlap
// with option codes that we have added to dhcp4 option space.
for
(
uint16_t
code
=
105
;
code
<
112
;
++
code
)
{
OptionPtr
option
(
new
Option
(
Option
::
V6
,
code
,
OptionBuffer
(
10
,
0xFF
)));
ASSERT_NO_THROW
(
pool
->
getCfgOption
()
->
add
(
option
,
false
,
"isc"
));
}
// Get options from the pool and check if all 10 are there.
OptionContainerPtr
options
=
pool
->
getCfgOption
()
->
getAll
(
"dhcp4"
);
ASSERT_TRUE
(
options
);
ASSERT_EQ
(
10
,
options
->
size
());
// Validate codes of options added to dhcp4 option space.
uint16_t
expected_code
=
100
;
for
(
OptionContainer
::
const_iterator
option_desc
=
options
->
begin
();
option_desc
!=
options
->
end
();
++
option_desc
)
{
ASSERT_TRUE
(
option_desc
->
option_
);
EXPECT_EQ
(
expected_code
,
option_desc
->
option_
->
getType
());
++
expected_code
;
}
options
=
pool
->
getCfgOption
()
->
getAll
(
"isc"
);
ASSERT_TRUE
(
options
);
ASSERT_EQ
(
7
,
options
->
size
());
// Validate codes of options added to isc option space.
expected_code
=
105
;
for
(
OptionContainer
::
const_iterator
option_desc
=
options
->
begin
();
option_desc
!=
options
->
end
();
++
option_desc
)
{
ASSERT_TRUE
(
option_desc
->
option_
);
EXPECT_EQ
(
expected_code
,
option_desc
->
option_
->
getType
());
++
expected_code
;
}
// Try to get options from a non-existing option space.
options
=
pool
->
getCfgOption
()
->
getAll
(
"abcd"
);
ASSERT_TRUE
(
options
);
EXPECT_TRUE
(
options
->
empty
());
}
TEST
(
Pool6Test
,
constructor_first_last
)
{
// let's construct 2001:db8:1:: - 2001:db8:1::ffff:ffff:ffff:ffff pool
...
...
@@ -329,7 +381,7 @@ TEST(Pool6Test, unique_id) {
}
// Simple check if toText returns reasonable values
TEST
(
Pool6Test
,
toText
)
{
TEST
(
Pool6Test
,
toText
)
{
Pool6
pool1
(
Lease
::
TYPE_NA
,
IOAddress
(
"2001:db8::1"
),
IOAddress
(
"2001:db8::2"
));
EXPECT_EQ
(
"type=IA_NA, 2001:db8::1-2001:db8::2, delegated_len=128"
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment