Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Sebastian Schrader
Kea
Commits
f73fba3c
Commit
f73fba3c
authored
Oct 11, 2013
by
Marcin Siodelski
Browse files
[master] Merge branch 'trac3180'
parents
e01ba204
36eabc93
Changes
33
Hide whitespace changes
Inline
Side-by-side
doc/devel/mainpage.dox
View file @
f73fba3c
...
...
@@ -53,12 +53,14 @@
* - @subpage dhcpv4Session
* - @subpage dhcpv4ConfigParser
* - @subpage dhcpv4ConfigInherit
* - @subpage dhcpv4OptionsParse
* - @subpage dhcpv4Other
* - @subpage dhcp6
* - @subpage dhcpv6Session
* - @subpage dhcpv6ConfigParser
* - @subpage dhcpv6ConfigInherit
* - @subpage dhcpv6DDNSIntegration
* - @subpage dhcpv6OptionsParse
* - @subpage dhcpv6Other
* - @subpage libdhcp
* - @subpage libdhcpIntro
...
...
src/bin/dhcp4/dhcp4.dox
View file @
f73fba3c
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012
-2013
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
...
...
@@ -79,6 +79,11 @@ See \ref dhcpv6ConfigParser.
Configuration inheritance in DHCPv4 follows exactly the same logic as its DHCPv6
counterpart. See \ref dhcpv6ConfigInherit.
@section dhcpv4OptionsParse Custom functions to parse message options
The DHCPv4 server uses the same logic to supply custom callback function to
parse message option as DHCPv6 server implementation. See \ref dhcpv6OptionsParse.
@section dhcpv4Other Other DHCPv4 topics
For hooks API support in DHCPv4, see @ref dhcpv4Hooks.
...
...
src/bin/dhcp4/dhcp4_srv.cc
View file @
f73fba3c
...
...
@@ -35,6 +35,7 @@
#include <hooks/hooks_manager.h>
#include <boost/algorithm/string/erase.hpp>
#include <boost/bind.hpp>
#include <iomanip>
#include <fstream>
...
...
@@ -197,6 +198,15 @@ Dhcpv4Srv::run() {
continue
;
}
// In order to parse the DHCP options, the server needs to use some
// configuration information such as: existing option spaces, option
// definitions etc. This is the kind of information which is not
// available in the libdhcp, so we need to supply our own implementation
// of the option parsing function here, which would rely on the
// configuration data.
query
->
setCallback
(
boost
::
bind
(
&
Dhcpv4Srv
::
unpackOptions
,
this
,
_1
,
_2
,
_3
));
bool
skip_unpack
=
false
;
// The packet has just been received so contains the uninterpreted wire
...
...
@@ -1161,6 +1171,95 @@ Dhcpv4Srv::openActiveSockets(const uint16_t port,
}
}
size_t
Dhcpv4Srv
::
unpackOptions
(
const
OptionBuffer
&
buf
,
const
std
::
string
&
option_space
,
isc
::
dhcp
::
OptionCollection
&
options
)
{
size_t
offset
=
0
;
OptionDefContainer
option_defs
;
if
(
option_space
==
"dhcp4"
)
{
// Get the list of stdandard option definitions.
option_defs
=
LibDHCP
::
getOptionDefs
(
Option
::
V4
);
}
else
if
(
!
option_space
.
empty
())
{
OptionDefContainerPtr
option_defs_ptr
=
CfgMgr
::
instance
().
getOptionDefs
(
option_space
);
if
(
option_defs_ptr
!=
NULL
)
{
option_defs
=
*
option_defs_ptr
;
}
}
// Get the search index #1. It allows to search for option definitions
// using option code.
const
OptionDefContainerTypeIndex
&
idx
=
option_defs
.
get
<
1
>
();
// The buffer being read comprises a set of options, each starting with
// a one-byte type code and a one-byte length field.
while
(
offset
+
1
<=
buf
.
size
())
{
uint8_t
opt_type
=
buf
[
offset
++
];
// DHO_END is a special, one octet long option
if
(
opt_type
==
DHO_END
)
return
(
offset
);
// just return. Don't need to add DHO_END option
// DHO_PAD is just a padding after DHO_END. Let's continue parsing
// in case we receive a message without DHO_END.
if
(
opt_type
==
DHO_PAD
)
continue
;
if
(
offset
+
1
>=
buf
.
size
())
{
// opt_type must be cast to integer so as it is not treated as
// unsigned char value (a number is presented in error message).
isc_throw
(
OutOfRange
,
"Attempt to parse truncated option "
<<
static_cast
<
int
>
(
opt_type
));
}
uint8_t
opt_len
=
buf
[
offset
++
];
if
(
offset
+
opt_len
>
buf
.
size
())
{
isc_throw
(
OutOfRange
,
"Option parse failed. Tried to parse "
<<
offset
+
opt_len
<<
" bytes from "
<<
buf
.
size
()
<<
"-byte long buffer."
);
}
// Get all definitions with the particular option code. Note that option code
// is non-unique within this container however at this point we expect
// to get one option definition with the particular code. If more are
// returned we report an error.
const
OptionDefContainerTypeRange
&
range
=
idx
.
equal_range
(
opt_type
);
// Get the number of returned option definitions for the option code.
size_t
num_defs
=
distance
(
range
.
first
,
range
.
second
);
OptionPtr
opt
;
if
(
num_defs
>
1
)
{
// Multiple options of the same code are not supported right now!
isc_throw
(
isc
::
Unexpected
,
"Internal error: multiple option definitions"
" for option type "
<<
static_cast
<
int
>
(
opt_type
)
<<
" returned. Currently it is not supported to initialize"
<<
" multiple option definitions for the same option code."
<<
" This will be supported once support for option spaces"
<<
" is implemented"
);
}
else
if
(
num_defs
==
0
)
{
opt
=
OptionPtr
(
new
Option
(
Option
::
V4
,
opt_type
,
buf
.
begin
()
+
offset
,
buf
.
begin
()
+
offset
+
opt_len
));
opt
->
setEncapsulatedSpace
(
"dhcp4"
);
}
else
{
// The option definition has been found. Use it to create
// the option instance from the provided buffer chunk.
const
OptionDefinitionPtr
&
def
=
*
(
range
.
first
);
assert
(
def
);
opt
=
def
->
optionFactory
(
Option
::
V4
,
opt_type
,
buf
.
begin
()
+
offset
,
buf
.
begin
()
+
offset
+
opt_len
,
boost
::
bind
(
&
Dhcpv4Srv
::
unpackOptions
,
this
,
_1
,
_2
,
_3
));
}
options
.
insert
(
std
::
make_pair
(
opt_type
,
opt
));
offset
+=
opt_len
;
}
return
(
offset
);
}
}
// namespace dhcp
}
// namespace isc
src/bin/dhcp4/dhcp4_srv.h
View file @
f73fba3c
...
...
@@ -349,6 +349,18 @@ protected:
/// simulates transmission of a packet. For that purpose it is protected.
virtual
void
sendPacket
(
const
Pkt4Ptr
&
pkt
);
/// @brief Implements a callback function to parse options in the message.
///
/// @param buf a A buffer holding options in on-wire format.
/// @param option_space A name of the option space which holds definitions
/// of to be used to parse options in the packets.
/// @param [out] options A reference to the collection where parsed options
/// will be stored.
/// @return An offset to the first byte after last parsed option.
size_t
unpackOptions
(
const
OptionBuffer
&
buf
,
const
std
::
string
&
option_space
,
isc
::
dhcp
::
OptionCollection
&
options
);
private:
/// @brief Constructs netmask option based on subnet4
...
...
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
View file @
f73fba3c
...
...
@@ -22,6 +22,7 @@
#include <dhcp/option.h>
#include <dhcp/option4_addrlst.h>
#include <dhcp/option_custom.h>
#include <dhcp/option_int.h>
#include <dhcp/option_int_array.h>
#include <dhcp/pkt_filter.h>
#include <dhcp/pkt_filter_inet.h>
...
...
@@ -147,6 +148,7 @@ public:
using
Dhcpv4Srv
::
writeServerID
;
using
Dhcpv4Srv
::
sanityCheck
;
using
Dhcpv4Srv
::
srvidToString
;
using
Dhcpv4Srv
::
unpackOptions
;
};
static
const
char
*
SRVID_FILE
=
"server-id-test.txt"
;
...
...
@@ -1658,21 +1660,94 @@ TEST_F(Dhcpv4SrvTest, Hooks) {
EXPECT_TRUE
(
hook_index_buffer4_send
>
0
);
}
// a dummy MAC address
const
uint8_t
dummyMacAddr
[]
=
{
0
,
1
,
2
,
3
,
4
,
5
};
// This test verifies that the following option structure can be parsed:
// - option (option space 'foobar')
// - sub option (option space 'foo')
// - sub option (option space 'bar')
// @todo Add more thorough unit tests for unpackOptions.
TEST_F
(
Dhcpv4SrvTest
,
unpackOptions
)
{
// Create option definition for each level of encapsulation. Each option
// definition is for the option code 1. Options may have the same
// option code because they belong to different option spaces.
// Top level option encapsulates options which belong to 'space-foo'.
OptionDefinitionPtr
opt_def
(
new
OptionDefinition
(
"option-foobar"
,
1
,
"uint32"
,
"space-foo"
));
\
// Middle option encapsulates options which belong to 'space-bar'
OptionDefinitionPtr
opt_def2
(
new
OptionDefinition
(
"option-foo"
,
1
,
"uint16"
,
"space-bar"
));
// Low level option doesn't encapsulate any option space.
OptionDefinitionPtr
opt_def3
(
new
OptionDefinition
(
"option-bar"
,
1
,
"uint8"
));
// Add option definitions to the Configuration Manager. Each goes under
// different option space.
CfgMgr
&
cfgmgr
=
CfgMgr
::
instance
();
ASSERT_NO_THROW
(
cfgmgr
.
addOptionDef
(
opt_def
,
"space-foobar"
));
ASSERT_NO_THROW
(
cfgmgr
.
addOptionDef
(
opt_def2
,
"space-foo"
));
ASSERT_NO_THROW
(
cfgmgr
.
addOptionDef
(
opt_def3
,
"space-bar"
));
// Create the buffer holding the structure of options.
const
char
raw_data
[]
=
{
// First option starts here.
0x01
,
// option code = 1
0x0B
,
// option length = 11
0x00
,
0x01
,
0x02
,
0x03
,
// This option carries uint32 value
// Sub option starts here.
0x01
,
// option code = 1
0x05
,
// option length = 5
0x01
,
0x02
,
// this option carries uint16 value
// Last option starts here.
0x01
,
// option code = 1
0x01
,
// option length = 1
0x00
// This option carries a single uint8
// value and has no sub options.
};
OptionBuffer
buf
(
raw_data
,
raw_data
+
sizeof
(
raw_data
));
// Parse options.
NakedDhcpv4Srv
srv
(
0
);
OptionCollection
options
;
ASSERT_NO_THROW
(
srv
.
unpackOptions
(
buf
,
"space-foobar"
,
options
));
// There should be one top level option.
ASSERT_EQ
(
1
,
options
.
size
());
boost
::
shared_ptr
<
OptionInt
<
uint32_t
>
>
option_foobar
=
boost
::
dynamic_pointer_cast
<
OptionInt
<
uint32_t
>
>
(
options
.
begin
()
->
second
);
ASSERT_TRUE
(
option_foobar
);
EXPECT_EQ
(
1
,
option_foobar
->
getType
());
EXPECT_EQ
(
0x00010203
,
option_foobar
->
getValue
());
// There should be a middle level option held in option_foobar.
boost
::
shared_ptr
<
OptionInt
<
uint16_t
>
>
option_foo
=
boost
::
dynamic_pointer_cast
<
OptionInt
<
uint16_t
>
>
(
option_foobar
->
getOption
(
1
));
ASSERT_TRUE
(
option_foo
);
EXPECT_EQ
(
1
,
option_foo
->
getType
());
EXPECT_EQ
(
0x0102
,
option_foo
->
getValue
());
// Finally, there should be a low level option under option_foo.
boost
::
shared_ptr
<
OptionInt
<
uint8_t
>
>
option_bar
=
boost
::
dynamic_pointer_cast
<
OptionInt
<
uint8_t
>
>
(
option_foo
->
getOption
(
1
));
ASSERT_TRUE
(
option_bar
);
EXPECT_EQ
(
1
,
option_bar
->
getType
());
EXPECT_EQ
(
0x0
,
option_bar
->
getValue
());
}
// a dummy MAC address
const
uint8_t
dummyMacAddr
[]
=
{
0
,
1
,
2
,
3
,
4
,
5
};
// A dummy MAC address, padded with 0s
const
uint8_t
dummyChaddr
[
16
]
=
{
0
,
1
,
2
,
3
,
4
,
5
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
};
// A dummy MAC address, padded with 0s
const
uint8_t
dummyChaddr
[
16
]
=
{
0
,
1
,
2
,
3
,
4
,
5
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
};
// Let's use some creative test content here (128 chars + \0)
const
uint8_t
dummyFile
[]
=
"Lorem ipsum dolor sit amet, consectetur "
"adipiscing elit. Proin mollis placerat metus, at "
"lacinia orci ornare vitae. Mauris amet."
;
// Let's use some creative test content here (128 chars + \0)
const
uint8_t
dummyFile
[]
=
"Lorem ipsum dolor sit amet, consectetur "
"adipiscing elit. Proin mollis placerat metus, at "
"lacinia orci ornare vitae. Mauris amet."
;
// Yet another type of test content (64 chars + \0)
const
uint8_t
dummySname
[]
=
"Lorem ipsum dolor sit amet, consectetur "
"adipiscing elit posuere."
;
// Yet another type of test content (64 chars + \0)
const
uint8_t
dummySname
[]
=
"Lorem ipsum dolor sit amet, consectetur "
"adipiscing elit posuere."
;
/// @brief a class dedicated to Hooks testing in DHCPv4 server
///
...
...
src/bin/dhcp6/dhcp6.dox
View file @
f73fba3c
...
...
@@ -167,6 +167,29 @@ Once the configuration is implemented, these constants will be removed.
@todo Add section about setting up options and their definitions with bindctl.
@section dhcpv6OptionsParse Custom functions to parse message options
The DHCPv6 server implementation provides a generic support to define option
formats and set option values. A number of options formats have been defined
for standard options in libdhcp++. However, the formats for vendor specific
options are dynamically configured by the server's administrator and thus can't
be stored in libdhcp++. Such option formats are stored in the
@c isc::dhcp::CfgMgr. The libdhcp++ provides functions for recursive parsing
of options which may be encapsulated by other options up to the any level of
encapsulation but these functions are unaware of the option formats defined
in the @c isc::dhcp::CfgMgr because they belong to a different library.
Therefore, the generic functions @c isc::dhcp::LibDHCP::unpackOptions4 and
@c isc::dhcp::LibDHCP::unpackOptions6 are only useful to parse standard
options which definitions are provided in the libdhcp++. In order to overcome
this problem a callback mechanism has been implemented in @c Option and @c Pkt6
classes. By installing a callback function on the instance of the @c Pkt6 the
server may provide a custom implementation of the options parsing algorithm.
This callback function will take precedence over the @c LibDHCP::unpackOptions6
and @c LibDHCP::unpackOptions4 functions. With this approach, the callback is
implemented within the context of the server and it has access to all objects
which define its configuration (including dynamically created option
definitions).
@section dhcpv6Other Other DHCPv6 topics
For hooks API support in DHCPv6, see @ref dhcpv6Hooks.
...
...
src/bin/dhcp6/dhcp6_srv.cc
View file @
f73fba3c
...
...
@@ -229,6 +229,15 @@ bool Dhcpv6Srv::run() {
continue
;
}
// In order to parse the DHCP options, the server needs to use some
// configuration information such as: existing option spaces, option
// definitions etc. This is the kind of information which is not
// available in the libdhcp, so we need to supply our own implementation
// of the option parsing function here, which would rely on the
// configuration data.
query
->
setCallback
(
boost
::
bind
(
&
Dhcpv6Srv
::
unpackOptions
,
this
,
_1
,
_2
,
_3
,
_4
,
_5
));
bool
skip_unpack
=
false
;
// The packet has just been received so contains the uninterpreted wire
...
...
@@ -703,7 +712,7 @@ Dhcpv6Srv::createStatusCode(uint16_t code, const std::string& text) {
void
Dhcpv6Srv
::
sanityCheck
(
const
Pkt6Ptr
&
pkt
,
RequirementLevel
clientid
,
RequirementLevel
serverid
)
{
Option
::
OptionCollection
client_ids
=
pkt
->
getOptions
(
D6O_CLIENTID
);
OptionCollection
client_ids
=
pkt
->
getOptions
(
D6O_CLIENTID
);
switch
(
clientid
)
{
case
MANDATORY
:
if
(
client_ids
.
size
()
!=
1
)
{
...
...
@@ -724,7 +733,7 @@ Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
break
;
}
Option
::
OptionCollection
server_ids
=
pkt
->
getOptions
(
D6O_SERVERID
);
OptionCollection
server_ids
=
pkt
->
getOptions
(
D6O_SERVERID
);
switch
(
serverid
)
{
case
FORBIDDEN
:
if
(
!
server_ids
.
empty
())
{
...
...
@@ -869,7 +878,7 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
//
// @todo: expand this to cover IA_PD and IA_TA once we implement support for
// prefix delegation and temporary addresses.
for
(
Option
::
OptionCollection
::
iterator
opt
=
question
->
options_
.
begin
();
for
(
OptionCollection
::
iterator
opt
=
question
->
options_
.
begin
();
opt
!=
question
->
options_
.
end
();
++
opt
)
{
switch
(
opt
->
second
->
getType
())
{
case
D6O_IA_NA
:
{
...
...
@@ -1059,8 +1068,8 @@ Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer,
// Get all IAs from the answer. For each IA, holding an address we will
// create a corresponding NameChangeRequest.
Option
::
OptionCollection
answer_ias
=
answer
->
getOptions
(
D6O_IA_NA
);
for
(
Option
::
OptionCollection
::
const_iterator
answer_ia
=
OptionCollection
answer_ias
=
answer
->
getOptions
(
D6O_IA_NA
);
for
(
OptionCollection
::
const_iterator
answer_ia
=
answer_ias
.
begin
();
answer_ia
!=
answer_ias
.
end
();
++
answer_ia
)
{
// @todo IA_NA may contain multiple addresses. We should process
// each address individually. Currently we get only one.
...
...
@@ -1702,7 +1711,7 @@ Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply,
}
DuidPtr
duid
(
new
DUID
(
opt_duid
->
getData
()));
for
(
Option
::
OptionCollection
::
iterator
opt
=
renew
->
options_
.
begin
();
for
(
OptionCollection
::
iterator
opt
=
renew
->
options_
.
begin
();
opt
!=
renew
->
options_
.
end
();
++
opt
)
{
switch
(
opt
->
second
->
getType
())
{
...
...
@@ -1769,7 +1778,7 @@ Dhcpv6Srv::releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply) {
// handled properly. Therefore the releaseIA_NA and releaseIA_PD options
// may turn the status code to some error, but can't turn it back to success.
int
general_status
=
STATUS_Success
;
for
(
Option
::
OptionCollection
::
iterator
opt
=
release
->
options_
.
begin
();
for
(
OptionCollection
::
iterator
opt
=
release
->
options_
.
begin
();
opt
!=
release
->
options_
.
end
();
++
opt
)
{
switch
(
opt
->
second
->
getType
())
{
case
D6O_IA_NA
:
{
...
...
@@ -2243,5 +2252,99 @@ Dhcpv6Srv::openActiveSockets(const uint16_t port) {
}
}
size_t
Dhcpv6Srv
::
unpackOptions
(
const
OptionBuffer
&
buf
,
const
std
::
string
&
option_space
,
isc
::
dhcp
::
OptionCollection
&
options
,
size_t
*
relay_msg_offset
,
size_t
*
relay_msg_len
)
{
size_t
offset
=
0
;
size_t
length
=
buf
.
size
();
OptionDefContainer
option_defs
;
if
(
option_space
==
"dhcp6"
)
{
// Get the list of stdandard option definitions.
option_defs
=
LibDHCP
::
getOptionDefs
(
Option
::
V6
);
}
else
if
(
!
option_space
.
empty
())
{
OptionDefContainerPtr
option_defs_ptr
=
CfgMgr
::
instance
().
getOptionDefs
(
option_space
);
if
(
option_defs_ptr
!=
NULL
)
{
option_defs
=
*
option_defs_ptr
;
}
}
// Get the search index #1. It allows to search for option definitions
// using option code.
const
OptionDefContainerTypeIndex
&
idx
=
option_defs
.
get
<
1
>
();
// The buffer being read comprises a set of options, each starting with
// a two-byte type code and a two-byte length field.
while
(
offset
+
4
<=
length
)
{
uint16_t
opt_type
=
isc
::
util
::
readUint16
(
&
buf
[
offset
]);
offset
+=
2
;
uint16_t
opt_len
=
isc
::
util
::
readUint16
(
&
buf
[
offset
]);
offset
+=
2
;
if
(
offset
+
opt_len
>
length
)
{
// @todo: consider throwing exception here.
return
(
offset
);
}
if
(
opt_type
==
D6O_RELAY_MSG
&&
relay_msg_offset
&&
relay_msg_len
)
{
// remember offset of the beginning of the relay-msg option
*
relay_msg_offset
=
offset
;
*
relay_msg_len
=
opt_len
;
// do not create that relay-msg option
offset
+=
opt_len
;
continue
;
}
// Get all definitions with the particular option code. Note that option
// code is non-unique within this container however at this point we
// expect to get one option definition with the particular code. If more
// are returned we report an error.
const
OptionDefContainerTypeRange
&
range
=
idx
.
equal_range
(
opt_type
);
// Get the number of returned option definitions for the option code.
size_t
num_defs
=
distance
(
range
.
first
,
range
.
second
);
OptionPtr
opt
;
if
(
num_defs
>
1
)
{
// Multiple options of the same code are not supported right now!
isc_throw
(
isc
::
Unexpected
,
"Internal error: multiple option definitions"
" for option type "
<<
opt_type
<<
" returned. Currently it is not"
" supported to initialize multiple option definitions"
" for the same option code. This will be supported once"
" support for option spaces is implemented"
);
}
else
if
(
num_defs
==
0
)
{
// @todo Don't crash if definition does not exist because only a few
// option definitions are initialized right now. In the future
// we will initialize definitions for all options and we will
// remove this elseif. For now, return generic option.
opt
=
OptionPtr
(
new
Option
(
Option
::
V6
,
opt_type
,
buf
.
begin
()
+
offset
,
buf
.
begin
()
+
offset
+
opt_len
));
opt
->
setEncapsulatedSpace
(
"dhcp6"
);
}
else
{
// The option definition has been found. Use it to create
// the option instance from the provided buffer chunk.
const
OptionDefinitionPtr
&
def
=
*
(
range
.
first
);
assert
(
def
);
opt
=
def
->
optionFactory
(
Option
::
V6
,
opt_type
,
buf
.
begin
()
+
offset
,
buf
.
begin
()
+
offset
+
opt_len
,
boost
::
bind
(
&
Dhcpv6Srv
::
unpackOptions
,
this
,
_1
,
_2
,
_3
,
_4
,
_5
));
}
// add option to options
options
.
insert
(
std
::
make_pair
(
opt_type
,
opt
));
offset
+=
opt_len
;
}
return
(
offset
);
}
};
};
src/bin/dhcp6/dhcp6_srv.h
View file @
f73fba3c
...
...
@@ -506,6 +506,24 @@ protected:
/// simulates transmission of a packet. For that purpose it is protected.
virtual
void
sendPacket
(
const
Pkt6Ptr
&
pkt
);
/// @brief Implements a callback function to parse options in the message.
///
/// @param buf a A buffer holding options in on-wire format.
/// @param option_space A name of the option space which holds definitions
/// of to be used to parse options in the packets.
/// @param [out] options A reference to the collection where parsed options
/// will be stored.
/// @param relay_msg_offset Reference to a size_t structure. If specified,
/// offset to beginning of relay_msg option will be stored in it.
/// @param relay_msg_len reference to a size_t structure. If specified,
/// length of the relay_msg option will be stored in it.
/// @return An offset to the first byte after last parsed option.
size_t
unpackOptions
(
const
OptionBuffer
&
buf
,
const
std
::
string
&
option_space
,
isc
::
dhcp
::
OptionCollection
&
options
,
size_t
*
relay_msg_offset
,
size_t
*
relay_msg_len
);
private:
/// @brief Allocation Engine.
/// Pointer to the allocation engine that we are currently using
...
...
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
View file @
f73fba3c
...
...
@@ -24,6 +24,7 @@
#include <dhcp/option6_client_fqdn.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option_int.h>
#include <dhcp/option_int_array.h>
#include <dhcp/iface_mgr.h>
#include <dhcp6/config_parser.h>
...
...
@@ -1994,7 +1995,7 @@ TEST_F(Dhcpv6SrvTest, portsRelayedTraffic) {
// Checks if server is able to handle a relayed traffic from DOCSIS3.0 modems
// @todo Uncomment this test as part of #3180 work.
// Kea code currently fails to handle docsis traffic.
TEST_F
(
Dhcpv6SrvTest
,
DISABLED_
docsisTraffic
)
{
TEST_F
(
Dhcpv6SrvTest
,
docsisTraffic
)
{
NakedDhcpv6Srv
srv
(
0
);
...
...
@@ -2018,6 +2019,78 @@ TEST_F(Dhcpv6SrvTest, DISABLED_docsisTraffic) {
/// that is relayed properly, etc.
}
// This test verifies that the following option structure can be parsed:
// - option (option space 'foobar')
// - sub option (option space 'foo')
// - sub option (option space 'bar')
TEST_F
(
Dhcpv6SrvTest
,
unpackOptions
)
{
// Create option definition for each level of encapsulation. Each option
// definition is for the option code 1. Options may have the same
// option code because they belong to different option spaces.
// Top level option encapsulates options which belong to 'space-foo'.
OptionDefinitionPtr
opt_def
(
new
OptionDefinition
(
"option-foobar"
,
1
,
"uint32"
,
"space-foo"
));
\
// Middle option encapsulates options which belong to 'space-bar'
OptionDefinitionPtr
opt_def2
(
new
OptionDefinition
(
"option-foo"
,
1
,
"uint16"
,
"space-bar"
));
// Low level option doesn't encapsulate any option space.
OptionDefinitionPtr
opt_def3
(
new
OptionDefinition
(
"option-bar"
,
1
,
"uint8"
));
// Add option definitions to the Configuration Manager. Each goes under
// different option space.
CfgMgr
&
cfgmgr
=
CfgMgr
::
instance
();
ASSERT_NO_THROW
(
cfgmgr
.
addOptionDef
(
opt_def
,
"space-foobar"
));
ASSERT_NO_THROW
(
cfgmgr
.
addOptionDef
(
opt_def2
,
"space-foo"
));
ASSERT_NO_THROW
(
cfgmgr
.
addOptionDef
(
opt_def3
,
"space-bar"
));
// Create the buffer holding the structure of options.
const
char
raw_data
[]
=
{
// First option starts here.
0x00
,
0x01
,
// option code = 1
0x00
,
0x0F
,
// option length = 15
0x00
,
0x01
,
0x02
,
0x03
,
// This option carries uint32 value
// Sub option starts here.
0x00
,
0x01
,
// option code = 1
0x00
,
0x07
,
// option length = 7
0x01
,
0x02
,
// this option carries uint16 value
// Last option starts here.
0x00
,
0x01
,
// option code = 1
0x00
,
0x01
,
// option length = 1
0x00
// This option carries a single uint8 value and has no sub options.
};
OptionBuffer
buf
(
raw_data
,
raw_data
+
sizeof
(
raw_data
));
// Parse options.
NakedDhcpv6Srv
srv
(
0
);
OptionCollection
options
;
ASSERT_NO_THROW
(
srv
.
unpackOptions
(
buf
,
"space-foobar"
,
options
,
0
,
0
));
// There should be one top level option.
ASSERT_EQ
(
1
,
options
.
size
());
boost
::
shared_ptr
<
OptionInt
<
uint32_t
>
>
option_foobar
=
boost
::
dynamic_pointer_cast
<
OptionInt
<
uint32_t
>
>
(
options
.
begin
()
->
second
);
ASSERT_TRUE
(
option_foobar
);
EXPECT_EQ
(
1
,
option_foobar
->
getType
());
EXPECT_EQ
(
0x00010203
,
option_foobar
->
getValue
());
// There should be a middle level option held in option_foobar.
boost
::
shared_ptr
<
OptionInt
<
uint16_t
>
>
option_foo
=
boost
::
dynamic_pointer_cast
<
OptionInt
<
uint16_t
>
>
(
option_foobar
->
getOption
(
1
));
ASSERT_TRUE
(
option_foo
);
EXPECT_EQ
(
1
,
option_foo
->
getType
());
EXPECT_EQ
(
0x0102
,
option_foo
->
getValue
());
// Finally, there should be a low level option under option_foo.
boost
::
shared_ptr
<
OptionInt
<
uint8_t
>
>
option_bar
=